Force Directed Graph, Teil 2

Nachdem Strukturieren des Codes möchte ich den Graphen erweitern. Mir sind Gewichtungen der Nodes wichtig und eine bessere Orientierung mit Textlabels.

Der Graph mit Gewichtungen und Beschriftung

Ich möchte den Kreisradius nach der Anzahl der Beziehungen der jeweiligen Protagonisten vergrößern. Je mehr Links ein Node besitzt, desto größer soll der Node dargestellt sein.

Nesting Data – eine einfache Möglichkeit, JSON-Daten zu sortieren und zu sammeln.

Die Daten aus miserables.json sammeln zwar die Anzahl der Verbindungen pro Knoten, sind aber nicht gewichtet oder sortiert. Die d3.nest() Funktion kann JSON Daten nach Kriterien einsammeln und dabei eine Auswertung vornehmen. Das Nesting nach Sources gruppiert Links Einträge nach Source.

d3.json('assets/data/miserables.json')
    .then(function (graph) {
  // - - - - - - - - - -
  // nesting data by source
  // - - - - - - - - - -
  // sort links by source (name)
  var graphLinksBySource = d3.nest()
    .key(function (d) {
      return d.source;
    })
    .entries(graph.links);
}
// graphLinksBySource
[{
    "key": "Napoleon",
    "values": ["..."]
  },
  {
    "key": "Mlle.Baptistine",
    "values": ["..."]
  },
  {
    "key": "Mme.Magloire",
    "values": [{
        "source": { "id": "Mme.Magloire", "group": 1, "index": 3, "..." },
        "target": { "id": "Myriel", "group": 1, "index": 0, "..." },
        "value": 10,
        "index": 2
      },
      {
        "source": { "id": "Mme.Magloire", "group": 1, "index": 3, "..." },
        "target": { "id": "Mlle.Baptistine", "group": 1, "index": 2, "..." },
        "value": 6,
        "index": 3
      }
    ]
}]

Für den Radius brauche ich aber lediglich die Anzahl der Links. Dazu ist die Rollup-Funktion hilfreich. Damit kann ich die nach Source sortierten Einträge weiter auswerten. In diesem Fall interessiert mich die Anzahl der targets:

// count links per name
      // and save as object
      var graphLinksCount = d3.nest()
        .key(function (d) {
          return d.source;
        })
        .rollup(function (v) {
          return v.length;
        })
        .object(graph.links); //.entries(graph.links);

      console.log(graphLinksCount);
// graphLinksCount
{
  "Anzelma": "3",
  "Babet": "7",
  "Bahorel": "8",
  "Bamatabois": "3",
  "BaronessT": "2",
  ...
  "Mme.Magloire": "2",  
  ...
}

Damit kann ich den Radius der Knoten nach der Anzahl der Begegnungen dynamisch rechnen:

      // building the nodes by circles
      groupData = svg.selectAll()
        .data(graph.nodes)

      group = groupData.enter()
        .append('g')
        .attr('class', 'nodes')
        .attr('transform', 'translate(' + (0) + ',' + (0) + ') rotate(0)');

      node = group
        .append('circle')
        .attr('r', function (d) {
          let r;
          if (graphLinksCount[d.id]) {
            r = graphLinksCount[d.id] * 3;
          } else {
            r = 0;
          }
          if (r < 5) r = 5;
          return r;
        })
        .attr('stroke', 'white')
        .attr('stroke-width', '2')
        .attr('fill', function (d) {
          return color(d.group);
        })
        .call(d3.drag()
          .on('start', dragstarted)
          .on('drag', dragged)
          .on('end', dragended));

Die Ausgabe des Namens geht dann mit einem zusätzlichen text-Element:

// building text attributes
     
      group.append('text')
        .attr('transform', 'translate(5, 0) rotate(0)')
        .text(function (d, i) {
          return d.id;
        })