Reputation: 1437
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!
Upvotes: 1
Views: 321
Reputation: 92440
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>
Upvotes: 2