web-dev-qa-db-fra.com

d3 Node Étiquetage

J'ai utilisé l'exemple de code de ce projet d pour apprendre à afficher les graphiques d3 et je n'arrive pas à faire apparaître du texte au milieu des cercles (similaire à cet exemple et cet exemple ). J'ai regardé d'autres exemples et j'ai essayé d'ajouter

node.append("title").text("Node Name To Display")

et

node.append("text")
    .attr("text-anchor", "middle")
    .attr("dy", ".3em").text("Node Name To Display")

juste après la définition du nœud, mais le seul résultat que je vois est "Nom du nœud à afficher" s'affiche lorsque je survole chaque nœud. Il ne s'affiche pas sous forme de texte à l'intérieur du cercle. Dois-je écrire mon propre objet texte svg et déterminer les coordonnées sur lesquelles il doit être placé en fonction des coordonnées du rayon du cercle? D'après les deux autres exemples, il semblerait que d3 s'en occupe déjà d'une manière ou d'une autre. Je ne connais simplement pas le bon attribut à appeler/définir.

46
Josh Bradley

Il y a beaucoup d'exemples montrant comment ajouter des étiquettes aux visualisations de graphiques et d'arbres, mais je commencerais probablement par celui-ci comme le plus simple:

Vous n'avez pas publié de lien vers votre code, mais je suppose que node fait référence à une sélection d'éléments de cercle SVG. Vous ne pouvez pas ajouter d’éléments de texte aux éléments de cercle car les éléments de cercle ne sont pas conteneurs; l'ajout d'un élément de texte à un cercle sera ignoré.

En règle générale, vous utilisez un élément G pour regrouper un élément cercle (ou un élément image, comme ci-dessus) et un élément texte pour chaque nœud. La structure résultante ressemble à ceci:

<g class="node" transform="translate(130,492)">
  <circle r="4.5"/>
  <text dx="12" dy=".35em">Gavroche</text>
</g>

Utilisez un jointure de données pour créer les éléments G pour chaque nœud, puis utilisez selection.append pour ajouter un cercle et un élément de texte pour chacun. Quelque chose comme ça:

var node = svg.selectAll(".node")
    .data(nodes)
  .enter().append("g")
    .attr("class", "node")
    .call(force.drag);

node.append("circle")
    .attr("r", 4.5);

node.append("text")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name });

Un inconvénient de cette approche est que vous souhaiterez peut-être que les étiquettes soient dessinées au-dessus des cercles. Étant donné que SVG ne prend pas encore en charge z-index, les éléments sont dessinés dans l'ordre du document; ainsi, l'approche ci-dessus fait dessiner une étiquette au-dessus de son cercle, mais elle peut être dessinée sous d'autres cercles. Vous pouvez résoudre ce problème en utilisant deux jointures de données et en créant des groupes distincts pour les cercles et les étiquettes, comme ceci:

<g class="nodes">
  <circle transform="translate(130,492)" r="4.5"/>
  <circle transform="translate(110,249)" r="4.5"/>
  …
</g>
<g class="labels">
  <text transform="translate(130,492)" dx="12" dy=".35em">Gavroche</text>
  <text transform="translate(110,249)" dx="12" dy=".35em">Valjean</text>
  …
</g>

Et le JavaScript correspondant:

var circle = svg.append("g")
    .attr("class", "nodes")
  .selectAll("circle")
    .data(nodes)
  .enter().append("circle")
    .attr("r", 4.5)
    .call(force.drag);

var text = svg.append("g")
    .attr("class", "labels")
  .selectAll("text")
    .data(nodes)
  .enter().append("text")
    .attr("dx", 12)
    .attr("dy", ".35em")
    .text(function(d) { return d.name });

Cette technique est utilisée dans l'exemple Mobile Patent Suits (avec un élément de texte supplémentaire utilisé pour créer une ombre blanche).

86
mbostock

Si vous souhaitez agrandir les nœuds pour les adapter à de grandes étiquettes, vous pouvez utiliser la propriété getBBox d'un nœud de texte SVG après l'avoir dessiné. Voici comment je l'ai fait, pour une liste de nœuds avec des coordonnées fixes, et deux formes possibles:

nodes.forEach(function(v) {
  var nd;
  var cx = v.coord[0];
  var cy = v.coord[1];

  switch (v.shape) {
    case "circle":
      nd = svg.append("circle");
      break;
    case "rectangle":
      nd = svg.append("rect");
      break;
  }

  var w = 10;
  var h = 10;
  if (v.label != "") {
    var lText = svg.append("text");

    lText.attr("x", cx)
         .attr("y", cy + 5)
         .attr("class", "labelText")
         .text(v.label);

    var bbox = lText.node().getBBox();
    w = Math.max(w,bbox.width);
    h = Math.max(h,bbox.height);
  }

  var pad = 4;

  switch (v.shape) {
    case "circle":
      nd.attr("cx", cx)
        .attr("cy", cy)
        .attr("r", Math.sqrt(w*w + h*h)/2 + pad);
      break;
    case "rectangle":
      nd.attr("x", cx - w/2 - pad)
        .attr("y", cy - h/2 - pad)
        .attr("width", w + 2*pad)
        .attr("height", h + 2*pad);
      break;
  }

});

Notez que la forme est ajoutée, le texte est ajouté, puis la forme est positionnée, afin que le texte s'affiche en haut.

2
Aleks Kissinger

J'ai trouvé ce guide très utile pour essayer d'accomplir quelque chose de similaire:

https://www.dashingd3js.com/svg-text-element

Sur la base du lien ci-dessus, ce code générera des étiquettes de cercle:

<!DOCTYPE html>
<html>
  <head>
      <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
      <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
   </head>
<body style="overflow: hidden;">
<div id="canvas"  style="overflow: hidden;"></div>

<script type="text/javascript">

    var graph = {
        "nodes": [
            {name: "1", "group": 1, x: 100, y: 90, r: 10 , connected : "2"},
            {name: "2", "group": 1, x: 200, y: 50, r: 15, connected : "1"},
            {name: "3", "group": 2, x: 200, y: 130, r: 25, connected : "1"}
        ]
    }

    $( document ).ready(function() {

        var width = 2000;
        var height = 2000;

        var svg = d3.select("#canvas").append("svg")
                .attr("width", width)
                .attr("height", height)
                .append("g");

        var lines = svg.attr("class", "line")
                .selectAll("line").data(graph.nodes)
                .enter().append("line")
                .style("stroke", "gray") // <<<<< Add a color
                .attr("x1", function (d, i) {
                    return d.x
                })
                .attr("y1", function (d) {
                    return d.y
                })
                .attr("x2", function (d) {
                    return findAttribute(d.connected).x
                })
                .attr("y2", function (d) {
                    return findAttribute(d.connected).y
                })

        var circles = svg.selectAll("circle")
                .data(graph.nodes)
                .enter().append("circle")
                .style("stroke", "gray")
                .style("fill", "white")
                .attr("r", function (d, i) {
                    return d.r
                })
                .attr("cx", function (d, i) {
                    return d.x
                })
                .attr("cy", function (d, i) {
                    return d.y
                });

        var text = svg.selectAll("text")
                                .data(graph.nodes)
                                .enter()
                               .append("text");

        var textLabels = text
          .attr("x", function(d) { return d.x; })
          .attr("y", function(d) { return d.y; })
          .text( function (d) { return d.name })
          .attr("font-family", "sans-serif")
          .attr("font-size", "10px")
          .attr("fill", "red");

    });

    function findAttribute(name) {
        for (var i = 0, len = graph.nodes.length; i < len; i++) {
            if (graph.nodes[i].name === name)
                return graph.nodes[i]; // Return as soon as the object is found
        }
        return null; // The object was not found
    }


</script>
</body>
</html>
1
blue-sky