title: Nodes Title around a Circle
body: <p>I created a force layout around a circle in d3js (<a href="https://stackoverflow.com/questions/22439832/d3-js-how-do-i-arrange-nodes-of-a-force-layout-to-be-on-a-circle">D3.js how do I arrange nodes of a force layout to be on a circle</a>) and now I want to put the labels around like this:</p>
  
  <p><a href="https://i.stack.imgur.com/4Hcw2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/4Hcw2.png" alt="enter image description here"></a></p>
  
  <p>but right now this is what I'm getting:
  You can take a look here too: <a href="https://bl.ocks.org/pierreee1/07eb3b07ba876011419168d60c587090" rel="nofollow noreferrer">https://bl.ocks.org/pierreee1/07eb3b07ba876011419168d60c587090</a></p>
  
  <p><a href="https://i.stack.imgur.com/1Oaq4.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/1Oaq4.png" alt="enter image description here"></a></p>
  
  <p>What could I do to get the results I want? I searched some questions but none of the solutions have helped me.</p>
  
  <p>Here's my code:</p>
  
  <pre><code>// width y height
  var w = 1350;
  var h = 600;
  
  // declarar la fuerza y la union de los nodos por id, ahora sin charge ni centro porque no se van a correr
  var fuerza = d3.forceSimulation()
      .force("link", d3.forceLink().id(function(d){
          return d.id;
      }))
  ;
  
  // insertar los datos y ponerlos en consola
  d3.json("actores_v5.json", function(error, data){
      if (error) throw error;
  
      //verificar los datos
      console.log("Número de Nodos: " + data.nodes.length)
      console.log(data.nodes)
      console.log("Número de Links: " + data.edges.length)
      console.log(data.edges)
  
      //svg en donde dibujar
      var svg = d3.selectAll("body")
          .append("svg")
          .attr('width', w)
          .attr('height', h)
      ;
  
      //circulo invisible para dibujar los nodos
      // it's actually two arcs so we can use the getPointAtLength() and getTotalLength() methods
      var dim = w/2;
      var circle = svg.append("circle")
          //.attr("d", "M 40, "+(dim/2+40)+" a "+dim/2+","+dim/2+" 0 1,0 "+dim+",0 a "+dim/2+","+dim/2+" 0 1,0 "+dim*-1+",0")
          .attr('cx', w/2)
          .attr('cy', h/2)
          .attr('r', 250)
          .style("fill", "#ffffff")
      ;
  
      //crea las lineas con un svg y los datos de "edges"
      var lineas = svg.append('g')
          .selectAll("line")
          .data(data.edges)
          .enter()
              .append("path")
              .attr("class", function(d) {
                  return "link " + d.tipo; 
              })          
      ;
  
      //crea los nodos de acuerdo a los nombres
      var nodos = svg.append("g")
          .selectAll("circle")
          .data(data.nodes)
          .enter()
              .append("circle")
              .attr('class', function(d){
                  if (d.categoria == "gobierno"){
                      return "nodos " + d.categoria;
                  }   
                  if (d.categoria == "patrimonio"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "planeacion"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "ong"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "gremios"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "academia"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "comunidad"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "privado"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "medios"){
                      return "nodos " + d.categoria;
                  }
                  if (d.categoria == "otros"){
                      return "nodos " + d.categoria;
                  }
              })
              .on("mouseover", mouseEncima)
              .on("mouseout", mouseAfuera)
              .attr('r', 5)
      ;
  
      nodos
          .filter(function(d){
              return d.categoria == "gobierno"
                  || d.categoria == "patrimonio"
                  || d.categoria == "planeacion"
                  || d.categoria == "ong"
                  || d.categoria == "gremios"
                  || d.categoria == "academia" 
                  || d.categoria == "comunidad"
                  || d.categoria == "privado"
                  || d.categoria == "medios"
                  || d.categoria == "otros"
              ;
          })
          .style("opacity", 1)
      ;
  
      //titulos de los nodos
      nodos.append("title")
          .text(function(d){
              return d.id;
          })
      ;
  
      var text = svg.append("g").selectAll("text")
          .data(data.nodes)
          .attr('class', "text")
          .enter().append("text")
          .attr("x", 8)
          .attr("y", ".31em")
          .text(function(d) {
              return d.id;
          })
      ;
  
      //define los nodos y los links de la simulacion
      fuerza.nodes(data.nodes);
      fuerza.force("link").links(data.edges);
  
      // calcula los espacios de los circulos en el circulo
      var circleCoord = function(node, index, num_nodes){
          var circumference = circle.node().getTotalLength();
          var pointAtLength = function(l){
              return circle.node().getPointAtLength(l)};
          var sectionLength = (circumference)/num_nodes;
          var position = sectionLength*index+sectionLength/2;
          return pointAtLength(circumference-position)
      }
  
      // define la posicion de los nodos segun el calculo anterior
      data.nodes.forEach(function(d, i) {
          var coord = circleCoord(d, i, data.nodes.length)
          d.fx = coord.x
          d.fy = coord.y
      });
  
      for (i = 0; i &lt; data.nodes.length; i++) {
          var angle = (i / (data.nodes.length / 2)) * Math.PI;
          //data.nodes.push({ 'angle': angle });
      }   
  
      //simulación y actualizacion de la posicion de los nodos en cada "tick"
      fuerza.on("tick", function(){
  
          lineas.attr("d", function(d) {
          var dx = d.target.x - d.source.x,
              dy = d.target.y - d.source.y,
              dr = Math.sqrt(dx * dx + dy * dy);
              return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
          });
  
          nodos.attr("cx", function(d){
                  return d.x = d.fx;
              })
              .attr('cy', function(d){
                  return dy = d.fy;
              })
          ;
  
          text.attr("x", function(d){
                  return dx = d.fx;
              })
              .attr('y', function(d){
                  return dy = d.fy;
              })
              .style("text-anchor", "start")
      });
  
      //saber si las conexiones se ven o no
      var toggle = 0;
  
      //Create an array logging what is connected to what
      var linkedByIndex = {};
          for (i = 0; i &lt; data.nodes.length; i++) {
              linkedByIndex[i + "," + i] = 1;
          };
  
      data.edges.forEach(function (d) {
          linkedByIndex[d.source.index + "," + d.target.index] = 1;
      });
  
      //This function looks up whether a pair are neighbours  
      function neighboring(a, b) {
          return linkedByIndex[a.index + "," + b.index];
      }
  
      function mouseEncima() {
  
          if (toggle == 0) {
  
              //Reduce the opacity of all but the neighbouring nodes
              d = d3.select(this).node().__data__;
              nodos
                  .transition()
                  .style("opacity", function (o) {
                      return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
                  })              
                  .attr('r', function(o){
                      return neighboring(d, o) | neighboring(o, d) ? 10 : 5;
                  })
              ;  
  
              lineas
                  .transition()
                  .style("stroke-opacity", function (o) {
                      return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
                  })
              ;
  
              // text
              //  .transition()
              //  .style("opacity", function (o) {
              //      return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
              //  })
              // ;
  
              //Reduce the opacity        
              toggle = 1;
          }
      }
  
      function mouseAfuera() {
  
              nodos
                  .filter(function(d){
                      return d.categoria == "gobierno"
                          || d.categoria == "patrimonio"
                          || d.categoria == "planeacion"
                          || d.categoria == "ong"
                          || d.categoria == "gremios"
                          || d.categoria == "academia" 
                          || d.categoria == "comunidad"
                          || d.categoria == "privado"
                          || d.categoria == "medios"
                          || d.categoria == "otros"
                      ;
                  })
                  .transition()
                  .style("opacity", 1)
                  .attr('r', 5)
              ;
  
              // y las lineas a 0        
              lineas
                  .transition()
                  .style("stroke-opacity", 0.1)
              ;
  
              // text
              //  .transition()
              //  .style("opacity", 0.1)
              // ;
  
              toggle = 0;
          }   
  });
  </code></pre>