Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active September 8, 2023 16:20
  • Star 4 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mbostock/1073373 to your computer and use it in GitHub Desktop.
Force-Directed States of America
license: gpl-3.0
redirect: https://observablehq.com/@d3/force-directed-states

Click and drag to move states around.

So, what happens when you use the Delaunay triangulation to compute links between the centroids of the lower 48 United States of America? And then you simulate the whole thing as a force-directed graph? This, it turns out! Built with D3.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ddd;
fill-opacity: .8;
stroke: #fff;
stroke-width: 1.5px;
}
line {
stroke: #999;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var path = d3.geo.path(),
force = d3.layout.force().size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("/mbostock/raw/4090846/us.json", function(error, us) {
if (error) throw error;
var states = topojson.feature(us, us.objects.states),
nodes = [],
links = [];
states.features.forEach(function(d, i) {
if (d.id === 2 || d.id === 15 || d.id === 72) return; // lower 48
var centroid = path.centroid(d);
if (centroid.some(isNaN)) return;
centroid.x = centroid[0];
centroid.y = centroid[1];
centroid.feature = d;
nodes.push(centroid);
});
d3.geom.voronoi().links(nodes).forEach(function(link) {
var dx = link.source.x - link.target.x,
dy = link.source.y - link.target.y;
link.distance = Math.sqrt(dx * dx + dy * dy);
links.push(link);
});
force
.gravity(0)
.nodes(nodes)
.links(links)
.linkDistance(function(d) { return d.distance; })
.start();
var link = svg.selectAll("line")
.data(links)
.enter().append("line")
.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; });
var node = svg.selectAll("g")
.data(nodes)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + -d.x + "," + -d.y + ")"; })
.call(force.drag)
.append("path")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("d", function(d) { return path(d.feature); });
force.on("tick", function(e) {
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("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
});
</script>
@heyjordn
Copy link

Hey there Mr Bostock, I tried to replicate this map with data of my own country and it's not working as it should, can you take a look at my code if it's not too much trouble?

Force Directed graph of Jamaica

Blocks preview

@jorgeluisyh
Copy link

I also tried to replicate it with data of Peru but is not showing, I'm having problems to assign "d" attribute with path(d.feature) to nodes data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment