d3.js V3 force directed graph and unlinked nodes

余生颓废 提交于 2021-02-04 21:43:39

问题


I'm doing my first data visualization project. It's more for practice, and to learn d3.js as data visualization interests me.

My first project is making a force directed graph. The data set is the 50 states linked to their bordering states.

The source is the state, the target is the bordering state(s).

Hawaii and Alaska have no bordering States.

If I leave the target State blank in the csv file, they attach to each other with a middle node that is an empty string.

If I make their targets empty string of different lengths, they obviously attach to nodes. That's almost okay, but now I have nodes with blank values on their tooltips.

It's just not 100% clean.

I'm not sure if this is possible, to have nodes with no link whatsoever.

csv snippet

Georgia,North Carolina
Georgia,South Carolina
Georgia,Tennessee
Hawaii,
Idaho,Montana
Idaho,Nevada
Idaho,Oregon

creating nodeList

var nodesList = {}

data.forEach((link) => {
  link.source = nodesList[link.source] ||
    (nodesList[link.source] = { name: link.source });
  link.target = nodesList[link.target] ||
    (nodesList[link.target] = { name: link.target });
});

I tried writing a conditional in there that if the source was Alaska or Hawaii to set name to null, but that didn't work either.

Thanks for any help!


回答1:


It's a little hard to tell what you're doing because we can't see all your code, but it seems like the basic problem is that you want Hawaii and Alaska in your nodelist, but not in your link list.

The easiest thing to do, since you know all the states is to just have a states.csv file. Then it's just a matter of making a links list from your csv which should be as simple as:

var links = []

data.forEach(link => {
    if (link[0] && link[1]) links.push({source: link[0], target: link[1]})
});

or if you prefer map & filter:

var links = data
  .filter(link => link[0] && link[1])
  .map(link => ({source: link[0], target: link[1]}))

Here's a version with a state list truncated to just the states in your links

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

var states = [
     {id:"Georgia",abbreviation:"GA"},
     {id:"Hawaii",abbreviation:"HI"},
     {id:"Idaho",abbreviation:"ID"},
     {id:"Montana",abbreviation:"MT"},
     {id:"Nevada",abbreviation:"NV"},
     {id:"North Carolina",abbreviation:"NC"},
     {id:"Oregon",abbreviation:"OR"},
     {id:"South Carolina",abbreviation:"SC"},
     {id:"Tennessee",abbreviation:"TN"},
]

var data = [
     ['Georgia','North Carolina'],
     ['Georgia','South Carolina'],
     ['Georgia','Tennessee'],
     ['Hawaii'],
     ['Idaho',  'Montana'],
     ['Idaho',  'Nevada'],
     ['Idaho',  'Oregon']
]


var links = data.filter(link => link[0] && link[1])
            .map(link => ({source: link[0], target: link[1]}))
  

  var link = svg.append("g")
      .attr("class", "links")
    .selectAll("line")
    .data(links)
    .enter().append("line")
      .attr("stroke-width", 1);

  var node = svg.append("g")
      .attr("class", "nodes")
    .selectAll("circle")
    .data(states)
    .enter().append("circle")
      .attr("r", 5)
    .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

  node.append("title")
      .text(function(d) { return d.abbreviation; });

  simulation
      .nodes(states)
      .on("tick", ticked);

  simulation.force("link")
      .links(links);

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

    node
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
  }

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;
}
.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="200"></svg>


来源:https://stackoverflow.com/questions/46134424/d3-js-v3-force-directed-graph-and-unlinked-nodes

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