Étiquette à l'extérieur de l'arc (graphique circulaire) d3.js

je suis nouveau en d3.js et moi essayons de faire un diagramme avec. J'ai un problème: je ne peux pas obtenir mes étiquettes en dehors de mes arcs... Les étiquettes sont positionnées avec arc.centre de gravité

arcs.append("svg:text")
    .attr("transform", function(d) {
        return "translate(" + arc.centroid(d) + ")";
    })
    .attr("text-anchor", "middle")

Qui peut m'aider avec ça?

40
demandé sur peller 2011-11-08 20:06:26

10 réponses

je peux résoudre ce problème - avec trigonométrie :).

voir violon: http://jsfiddle.net/nrabinowitz/GQDUS/

en gros, appeler arc.centroid(d) renvoie un tableau [x,y] . Vous pouvez utiliser le théorème de Pythagore pour calculer l'hypoténuse, qui est la longueur de la ligne du centre de la tarte au centroïde de l'arc. Vous pouvez alors utiliser les calculs x/h * desiredLabelRadius et y/h * desiredLabelRadius pour calculer souhaité x,y pour votre ancrage d'étiquette:

.attr("transform", function(d) {
    var c = arc.centroid(d),
        x = c[0],
        y = c[1],
        // pythagorean theorem for hypotenuse
        h = Math.sqrt(x*x + y*y);
    return "translate(" + (x/h * labelr) +  ',' +
       (y/h * labelr) +  ")"; 
})

le seul inconvénient ici est que text-anchor: middle n'est plus un bon choix - vous feriez mieux de mettre le text-anchor basé sur quel côté du gâteau nous sommes:

.attr("text-anchor", function(d) {
    // are we past the center?
    return (d.endAngle + d.startAngle)/2 > Math.PI ?
        "end" : "start";
})
58
répondu nrabinowitz 2011-11-19 23:43:17

Merci!

j'ai trouvé une autre façon de résoudre ce problème, mais la vôtre semble meilleure: -)

j'ai créé un second arc avec un plus grand rayon et je l'ai utilisé pour positionner mes étiquettes.

///// Arc Labels ///// 
// Calculate position 
var pos = d3.svg.arc().innerRadius(r + 20).outerRadius(r + 20); 

// Place Labels 
arcs.append("svg:text") 
       .attr("transform", function(d) { return "translate(" + 
    pos.centroid(d) + ")"; }) 
       .attr("dy", 5) 
       .attr("text-anchor", "middle") 
       .attr("fill", function(d, i) { return colorL(i); }) //Colorarray Labels
       .attr("display", function(d) { return d.value >= 2 ? null : "none"; })  
       .text(function(d, i) { return d.value.toFixed(0) + "%"});
15
répondu Ibe Vanmeenen 2011-11-25 14:43:25

spécifiquement pour les diagrammes à secteurs, la fonction d3.layout.pie() formatera les données avec les attributs startAngle et endAngle . Le rayon peut être ce que vous désirez (à quelle distance du centre, vous souhaitez placer l'étiquette).

la combinaison de ces éléments d'information avec un couple fonctions trigonométriques vous permet de déterminer les coordonnées x et y pour les étiquettes.

gist / bloc .

concernant le positionnement x/y du texte, la magie est dans cette ligne (formatée pour la lisibilité):

.attr("transform", function(d) {
  return "translate(" + 
    ( (radius - 12) * Math.sin( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
    ", " +
    ( -1 * (radius - 12) * Math.cos( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
  ")";
 })
  • ((d.endAngle - d.startAngle) / 2) + d.startAngle nous donne notre angle (thêta) en radians.
  • (radius - 12) est le rayon arbitraire que j'ai choisi pour la position du texte.
  • -1 * l'axe des y est inversé (voir ci-dessous).

les fonctions de trigo utilisées sont: cos = adjacent / hypotenuse et sin = opposite / hypotenuse . Mais il y a certaines choses que nous devons considérer pour que cela fonctionne avec nos étiquettes.

  1. 0 angle est à 12 heures.
  2. l'angle augmente encore dans le sens des aiguilles d'une montre.
  3. l'axe y est inversé par rapport au système de coordonnées cartésien standard. Positif y est dans la direction de 6 heures.
  4. X positif est toujours dans la direction de 3 heures - droite.

qui gâche les choses un peu et a essentiellement l'effet d'échanger sin et cos . Nos fonctions trig deviennent alors: sin = adjacent / hypotenuse et cos = opposite / hypotenuse .

substituant des noms de variables nous avons sin(radians) = x / r et cos(radians) = y / r . Après quelques manipulations algébriques nous pouvons obtenir les deux fonctions en termes de x et y respectivement r * sin(radians) = x et r * cos(radians) = y . À partir de là, il suffit de les brancher dans l'attribut transform/translate.

qui mettra les étiquettes à la bonne place, pour les faire paraître fantaisistes, vous avez besoin d'une certaine logique de style comme celle-ci:

.style("text-anchor", function(d) {
    var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
    if ( (rads > 7 * Math.PI / 4 && rads < Math.PI / 4) || (rads > 3 * Math.PI / 4 && rads < 5 * Math.PI / 4) ) {
      return "middle";
    } else if (rads >= Math.PI / 4 && rads <= 3 * Math.PI / 4) {
      return "start";
    } else if (rads >= 5 * Math.PI / 4 && rads <= 7 * Math.PI / 4) {
      return "end";
    } else {
      return "middle";
    }
  })

cela fera les étiquettes de 10: 30 heures à 1: 30 heures et de 4: 30 heures à 7:30 heures ancre au milieu (ils sont au-dessus et en dessous), les étiquettes de 1:30 heures à 4:30 heures ancre sur la gauche (ils sont à la droite), et les étiquettes de 7:30 heures à 10:30 heures ancre sur la droite (ils sont à gauche).

les mêmes formules peuvent être utilisées pour n'importe quel graphique radial D3, la seule différence est la façon dont vous déterminez l'angle.

j'espère que cela aidera quiconque trébucher à travers elle!

14
répondu clayzermk1 2012-12-17 20:10:30

Je ne sais pas si cela aide, mais j'ai pu créer des arcs où je place du texte, les deux, sur l'arc et juste en dehors. Dans un cas, où je place la grandeur de l'arc dans les arcs, je tourne le texte sur l'arc pour correspondre à l'angle de l'arc. Dans l'autre, où je place le texte en dehors de l'arc, il est simplement horizontal. Le code est situé à: http://bl.ocks.org/2295263

De Mon Mieux

Frank

5
répondu Information Technology 2012-04-19 03:28:04

oui bébé, c'est SOHCAHTOA

function coordinates_on_circle(hyp, angle){
  var radian= angle * Math.PI / 180 //trig uses radians
  return {
    x: Math.cos(radian) * hyp, //adj = cos(r) * hyp
    y: Math.sin(radian) * hyp //opp = sin(r) * hyp
  }
}
var radius=100
var angle=45
coordinates_on_circle(radius, angle)
2
répondu spencercooly 2015-05-30 20:14:09

le Coffee-script suivant a fonctionné pour moi pour obtenir des étiquettes encore à l'intérieur des tranches de tarte, mais vers le bord extérieur:

attr 'transform', (d) ->
  radius = width / 2 # radius of whole pie chart
  d.innerRadius = radius * 0.5
  d.outerRadius = radius * 1.5
  'translate(' + arc.centroid(d) + ')'
1
répondu Sarah Vessels 2013-01-29 22:54:35

cette fonction a calculé le point central de la tranche de tarte pour un graphe à tarte. J'ajoute une fonction pour obtenir le point central de l'arc. Voici une image basée sur ma nouvelle fonction . voir le lien: https://github.com/mbostock/d3/issues/1124

1
répondu Sean Wang 2013-03-07 08:10:48

C'était la réponse à faible coût qui me plaisait. Il repousse toutes les étiquettes horizontalement (c'est là que j'ai eu l'espace supplémentaire):

g.append("text")
  .attr("transform", function(d) { 
      var pos = arc.centroid(d); 
      return "translate(" + (pos[0] + (.5 - (pos[0] < 0)) * radius) + "," + (pos[1]*2) + ")"; 
  })
  .attr("dy", ".35em")
  .style("text-anchor", function(d) { 
      return arc.centroid(d)[0] > 0 ? "start" : "end";
   })
  .text(function(d) { return d.label; });
1
répondu cheepychappy 2013-09-18 04:19:17

Donc, si vous voulez aller avec une très jolie légende plutôt que de texte aléatoire traîner. J'ai trouvé une bonne solution pour les étiquettes. comment ajouter une légende à un diagramme à secteurs en utilisant D3js? Et comment centraliser le camembert?

1
répondu DeadlyChambers 2017-05-23 10:30:47

j'ai obtenu le même pourcentage de dessin que les étiquettes à l'extérieur du graphique à secteurs, voici le code http://bl.ocks.org/farazshuja/e2cb52828c080ba85da5458e2304a61f

g.append("text")
        .attr("transform", function(d) {
        var _d = arc.centroid(d);
        _d[0] *= 2.2;   //multiply by a constant factor
        _d[1] *= 2.2;   //multiply by a constant factor
        return "translate(" + _d + ")";
      })
      .attr("dy", ".50em")
      .style("text-anchor", "middle")
      .text(function(d) {
        if(d.data.percentage < 8) {
          return '';
        }
        return d.data.percentage + '%';
      });
1
répondu FarazShuja 2016-07-22 06:31:30