Force directed graphs nodes stick to the center

瘦欲@ 提交于 2019-12-11 15:44:41

问题


After following up from this question Insert text inside Circle in D3 chart

My nodes are sticking to the center. I am not sure which property is directing my nodes and their x and y coordinates. I recently chnaged my code to add a g layer to the circles so that i can append text along with shape.

DATA

https://api.myjson.com/bins/hwtj0

UPDATED CODE

    async function d3function() {
        d3.selectAll("svg > *").remove();

        const svg = d3.select("svg");
        file = document.getElementById("selectFile").value;
        console.log("File: " + file)
        var width = 900
        var height = 900 
        svg.style("width", width + 'px').style("height", height + 'px');

        data = (await fetch(file)).json()
        d3.json(file).then(function(data) {

            const links = data.links.map(d => Object.create(d));
            const nodes = data.nodes.map(d => Object.create(d));
            console.log(links.length);
            console.log(nodes.length);
            const simulation = forceSimulation(nodes, links).on("tick", ticked);

            var categorical = [
              { "name" : "schemeAccent", "n": 8},
              { "name" : "schemeDark2", "n": 8},
            ]
            // var colorScale = d3.scaleOrdinal(d3[categorical[6].name])

            var color = d3.scaleOrdinal(d3[categorical[1].name]);


            var drag = simulation => {

                  function dragstarted(d) {
                    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
                    d.fx = d.x;
                    d.fy = d.y;
                  }

                  function dragged(d) {
                    d.fx = d3.event.x;
                    d.fy = d3.event.y;
                  }

                  function dragended(d) {
                    if (!d3.event.active) simulation.alphaTarget(0);
                    d.fx = null;
                    d.fy = null;
                  }

                  return d3.drag()
                      .on("start", dragstarted)
                      .on("drag", dragged)
                      .on("end", dragended);
            }

            const link = svg.append("g")
                  .attr("stroke", "#999")
                  .attr("stroke-opacity", 0.6)
                .selectAll("line")
                .data(links)
                .enter().append("line")
                  .attr("stroke-width", d => Math.sqrt(d.value));

            // link.append("title").text(d => d.value);

            // var circles = svg.append("g")
            //       .attr("stroke", "#fff")
            //       .attr("stroke-width", 1.5)
            //     .selectAll(".circle")
            //     .data(nodes)

            // const node = circles.enter().append("circle")
            //       .attr("r", 5)
            //       .attr("fill", d => color(d.group))
            //       .call(drag(simulation));

            const node = svg.append("g")
                  .attr("stroke", "#fff")
                  .attr("stroke-width", 1.5)
                  .selectAll("circles")
                  .data(nodes)
                  .enter()
                .append("g")
                .classed('circles', true)
                .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');

                node.append("circle")
                .classed('circle', true)
                .attr("r", 5)
                .attr("fill", d => color(d.group))
                .call(drag(simulation));

                node
                  .append("text")
                  .classed('circleText', true)
                  .attr('dy', '0.35em')
                  .attr('dx', 5)
                  .text(d => "Node: " + d.id);

            node.append("title").text(d => "Node: " + d.id);

            function ticked() {
                link
                    .attr("x1", d => d.source.x)
                    .attr("y1", d => d.source.y)
                    .attr("x2", d => d.target.x)
                    .attr("y2", d => d.target.y);

                node
                    .attr("cx", d => d.x)
                    .attr("cy", d => d.y);
            }

        });

    }



    function forceSimulation(nodes, links) {
      return d3.forceSimulation(nodes)
          .force("link", d3.forceLink(links).id(d => d.id))
          .force("charge", d3.forceManyBody())
          .force("center", d3.forceCenter());
    }

UPDATED OUTPUT

EXPECTED OUTPUT

UPDATED HTML

<g stroke="#fff" stroke-width="1.5">
   <g class="circle" cx="-35.89111508769784" cy="131.13965804447696">
      <circle class="circle" r="5" fill="#1b9e77"></circle>
      <text class="circleText" dy="0.35em" dx="5">Node: 0</text>
      <title>Node: 0</title>
   </g>
   <g class="circle" cx="70.97799024729613" cy="-195.71408429254427">
      <circle class="circle" r="5" fill="#d95f02"></circle>
      <text class="circleText" dy="0.35em" dx="5">Node: 3</text>
      <title>Node: 3</title>
   </g>
   [....]
  </g>

回答1:


You have to adapt your code slightly as it currently assumes that you're working with circle elements, where you specify the centres using cx and cy, but you are now using g elements, which use standard x and y coordinates.

First, remove the transform from the g element (that's a leftover from my demo code):

const node = svg.append("g")
  .attr("stroke", "#fff")
  .attr("stroke-width", 1.5)
.selectAll(".circles")  // note - should be .circles!
  .data(nodes)
  .enter()
  .append("g")
  .classed('circles', true)

and in the ticked() function, change the node updating code into a transform that works on g elements (which don't have cx or cy):

node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')' )

Demo:

var json = {"nodes":[{"id":"0","group":0},{"id":"1","group":1},{"id":"2","group":2},{"id":"3","group":3},{"id":"4","group":4},{"id":"5","group":5},{"id":"6","group":6},{"id":"7","group":7},{"id":"8","group":8},{"id":"9","group":9},{"id":"10","group":10},{"id":"11","group":11},{"id":"12","group":12},{"id":"13","group":13},{"id":"14","group":14},{"id":"15","group":15},{"id":"16","group":16},{"id":"17","group":17},{"id":"18","group":18},{"id":"19","group":19}],"links":[{"source":"0","target":"1","value":1},{"source":"0","target":"18","value":1},{"source":"0","target":"10","value":1},{"source":"0","target":"12","value":1},{"source":"0","target":"5","value":1},{"source":"0","target":"8","value":1},{"source":"1","target":"0","value":1},{"source":"1","target":"9","value":1},{"source":"1","target":"4","value":1},{"source":"2","target":"4","value":1},{"source":"2","target":"17","value":1},{"source":"2","target":"13","value":1},{"source":"2","target":"15","value":1},{"source":"3","target":"6","value":1},{"source":"4","target":"14","value":1},{"source":"4","target":"2","value":1},{"source":"4","target":"5","value":1},{"source":"4","target":"19","value":1},{"source":"4","target":"1","value":1},{"source":"5","target":"4","value":1},{"source":"5","target":"0","value":1},{"source":"6","target":"3","value":1},{"source":"7","target":"18","value":1},{"source":"7","target":"16","value":1},{"source":"8","target":"0","value":1},{"source":"9","target":"1","value":1},{"source":"10","target":"0","value":1},{"source":"10","target":"15","value":1},{"source":"12","target":"0","value":1},{"source":"13","target":"15","value":1},{"source":"13","target":"2","value":1},{"source":"14","target":"4","value":1},{"source":"15","target":"13","value":1},{"source":"15","target":"10","value":1},{"source":"15","target":"2","value":1},{"source":"16","target":"7","value":1},{"source":"17","target":"2","value":1},{"source":"18","target":"0","value":1},{"source":"18","target":"7","value":1},{"source":"19","target":"4","value":1},{"source":"19","target":"4","value":1}]};


d3.selectAll("svg > *").remove();

const svg = d3.select("svg");
var width = 900
var height = 900
svg.style("width", width + 'px').style("height", height + 'px');

const links = json.links.map(d => Object.create(d));
const nodes = json.nodes.map(d => Object.create(d));
const simulation = forceSimulation(nodes, links).on("tick", ticked);

var categorical = [
{
  "name": "schemeAccent",
  "n": 8
},
{
  "name": "schemeDark2",
  "n": 8
}, ]

var color = d3.scaleOrdinal(d3[categorical[1].name]);


var drag = simulation => {

  function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }

  return d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended);
}

const link = svg.append("g")
  .attr("stroke", "#999")
  .attr("stroke-opacity", 0.6)
  .selectAll("line")
  .data(links)
  .enter().append("line")
  .attr("stroke-width", d => Math.sqrt(d.value));

const node = svg.append("g")
  .attr("stroke", "#fff")
  .attr("stroke-width", 1.5)
  .selectAll(".circles")
  .data(nodes)
  .enter()
  .append("g")
  .classed('circles', true)
  .call(drag(simulation))
//    .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');

const circle = node.append("circle")
  .classed('circle', true)
  .attr("r", 5)
  .attr("fill", d => color(d.group))

node
  .append("text")
  .classed('circleText', true)
  .attr('dy', '0.35em')
  .attr('dx', 5)
  .text(d => "Node: " + d.id);

node.append("title").text(d => "Node: " + d.id);

function ticked() {
  link
    .attr("x1", d => d.source.x)
    .attr("y1", d => d.source.y)
    .attr("x2", d => d.target.x)
    .attr("y2", d => d.target.y);

  node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')')
}

function forceSimulation(nodes, links) {
  return d3.forceSimulation(nodes)
    .force("link", d3.forceLink(links).id(d => d.id))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter());
}
.circleText { fill: black; stroke: none }
<script src="//d3js.org/d3.v5.js"></script>
<svg></svg>


来源:https://stackoverflow.com/questions/52804351/force-directed-graphs-nodes-stick-to-the-center

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!