Click and drag to move states around. Move slider to modify distance function
Fork of mbostock's force layout example (/mbostock/1073373) with a few experimental changes.
Create links between neighbouring states (inferred from topojson mesh) rather than from Voronoi triangles which add some unstable cross-country links like Maine-Washington. Parameterize distance function with a slider (0 = spatial similarity only, 1 = more variation based on type similarity).
xxxxxxxxxx
<html>
<head>
<title>Force-Directed States of America</title>
<script src=""></script>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<script src="d3.slider.js"></script>
<!-- <script type="text/javascript" src="https://d3js.org/d3.geom.js"></script>
<script type="text/javascript" src="https://d3js.org/d3.layout.js"></script>
-->
<link rel="stylesheet" href="d3.slider.css" />
<style type="text/css">
path {
fill: #ddd;
fill-opacity: .8;
stroke: #fff;
stroke-width: 1.5px;
}
line {
stroke: #999;
}
#slider {
width: 80%;
margin: auto;
}
</style>
</head>
<body>
<div id='slidervalue'></div>
<script type="text/javascript">
var w = 1024,
h = 600;
var projection = d3.geo.albersUsa().translate([w/2,h/2]),
path = d3.geo.path().projection(projection),
force = d3.layout.force().size([w, h]);
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
var slider = d3.slider()
.axis(true)
.min(0)
.max(1)
.step(0.01)
.on('slide', function(e,v) {
force.start();
});
d3.select("body")
.append("div")
.attr("id","slider")
.call(slider);
queue()
.defer(d3.json, "world-50m.json")
.defer(d3.csv, "similarity.csv")
.await(ready);
function linkDistance(d) {
var ds = (1. / d.ss - 1)*23,
tf = Math.exp(slider.value()*(1.-8*d.ts));
return ds * tf; //d.distance;
}
function ready(error, world, similarity) {
var nodes = [],
links = [],
nodesbyname = {};
function continentalus(o) {
return o.properties.iso_a2 == 'US'
&& !( o.properties.postal == 'HI' || o.properties.postal == 'AK');
}
var states = topojson.feature(world, world.objects.states).features
.filter(continentalus);
states.forEach(function(d, i) {
//if (d.id == "02" || d.id == "15" || d.id == "72" || d.id == "78") return; // excl AK,HI,PR
var centroid = path.centroid(d);
centroid.x = centroid[0];
centroid.y = centroid[1];
centroid.feature = d;
if (d.properties.name == 'Kansas') centroid.fixed = true;
nodes.push(centroid);
nodesbyname[d.properties.name] = centroid;
/*
for (var j=0; j<i; j++) {
links.push(edge(centroid,nodes[j]));
}
*/
});
var neighbours = { 'New York_Michigan' : true, 'Michigan_New York' : true };
/*
d3.geom.voronoi()
.links(nodes)
.forEach(function(lnk) {
var a = lnk.source.feature.properties.name,
b = lnk.target.feature.properties.name;
neighbours[a + '_' + b] = true;
neighbours[b + '_' + a] = true;
});
*/
topojson.mesh(world, world.objects.states, function (a, b) {
var aok = continentalus(a),
bok = continentalus(b);
if (aok && bok && a !== b) {
neighbours[a.properties.name + '_' + b.properties.name] = true;
neighbours[b.properties.name + '_' + a.properties.name] = true;
}
return false;
});
similarity.forEach(function(r){
if (!(r.a in nodesbyname && r.b in nodesbyname)) return;
if (!(r.a + '_' + r.b in neighbours)) return;
var e = {
source: nodesbyname[r.a],
target: nodesbyname[r.b],
ss: +r.spatialsimilarity,
ts: +r.typesimilarity
};
links.push(e);
});
// randomize position
/* nodes.forEach(function(d) {
var abbrev = d.feature.properties.postal;
if (abbrev == 'FL' || abbrev == 'WA' || abbrev == 'ME') {
d.fixed = true;
return;
}
d.x = w *(0.5 + 0.5* (Math.random()-0.5));
d.y = h *(0.5 + 0.5* (Math.random()-0.5));
});
*/
force
.size([w,h])
.gravity(0)
.nodes(nodes)
.links(links)
.linkDistance(linkDistance)
.start();
var link = svg.selectAll("line")
.data(links)
.enter().append("svg: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("svg:g")
// draw each region in a coord system centered at its centroid
.attr("transform", function(d) { return "translate(" + -d[0] + "," + -d[1] + ")"; })
.call(force.drag)
.append("svg:path")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("d", function(d) { return path(d.feature); });
node.append('title')
.text(function(d) {return d.feature.properties.name;});
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 + ")";
});
});
}
function edge(a, b) {
var dx = a[0] - b[0], dy = a[1] - b[1];
return {
source: a,
target: b,
distance: Math.sqrt(dx * dx + dy * dy)
};
}
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://d3js.org/topojson.v1.min.js to a secure url
Modified http://d3js.org/queue.v1.min.js to a secure url
Modified http://d3js.org/d3.geom.js to a secure url
Modified http://d3js.org/d3.layout.js to a secure url
https://d3js.org/d3.v3.min.js
https://d3js.org/topojson.v1.min.js
https://d3js.org/queue.v1.min.js
https://d3js.org/d3.geom.js
https://d3js.org/d3.layout.js