Skip to content

Instantly share code, notes, and snippets.

@danhammer
Last active July 26, 2017 05:19
Show Gist options
  • Select an option

  • Save danhammer/1b80a29f4e09e6277ee95e3c8adb437d to your computer and use it in GitHub Desktop.

Select an option

Save danhammer/1b80a29f4e09e6277ee95e3c8adb437d to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
path {
fill: #e3e3e3;
stroke-width: 1px;
stroke: #666;
}
circle {
stroke: none;
fill: #fc0;
}
.added {
fill: #f0f;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.20/topojson.min.js"></script>
<script>
var svg = d3.select("svg"),
path = svg.append("path"),
circles = svg.append("g");
d3.json("total.topojson", function(err, topo){
var states = topojson.feature(topo, topo.objects.collection).features.map(function(d){
return d.geometry.coordinates[0];
});
d3.shuffle(states);
draw();
function draw() {
var a = states[0].slice(0),
b = states[1].slice(0);
// Same number of points on each ring
if (a.length < b.length) {
addPoints(a, b.length - a.length);
} else if (b.length < a.length) {
addPoints(b, a.length - b.length);
}
// Pick optimal winding
a = wind(a, b);
path.attr("d", join(a));
// Redraw points
//circles.datum(a)
//.call(updateCircles);
// Morph
var t = d3.transition()
.duration(3000);
path.transition(t)
.on("end", function(){
states.push(states.shift());
setTimeout(draw, 100);
})
.attr("d", join(b));
circles.selectAll("circle").data(b)
.transition(t)
.attr("cx",function(d){
return d[0];
})
.attr("cy",function(d){
return d[1];
});
}
});
function updateCircles(sel) {
var circles = sel.selectAll("circle")
.data(function(d){ return d; });
var merged = circles.enter()
.append("circle")
.attr("r", 2)
.merge(circles);
merged.classed("added", function(d){
return d.added;
})
.attr("cx",function(d){
return d[0];
})
.attr("cy",function(d){
return d[1];
});
circles.exit().remove();
}
function addPoints(ring, numPoints) {
var desiredLength = ring.length + numPoints,
step = d3.polygonLength(ring) / numPoints;
var i = 0,
cursor = 0,
insertAt = step / 2;
do {
var a = ring[i],
b = ring[(i + 1) % ring.length];
var segment = distanceBetween(a, b);
if (insertAt <= cursor + segment) {
ring.splice(i + 1, 0, pointBetween(a, b, (insertAt - cursor) / segment));
insertAt += step;
continue;
}
cursor += segment;
i++;
} while (ring.length < desiredLength);
}
function pointBetween(a, b, pct) {
var point = [
a[0] + (b[0] - a[0]) * pct,
a[1] + (b[1] - a[1]) * pct
];
point.added = true;
return point;
}
function distanceBetween(a, b) {
return Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2));
}
function join(d) {
return "M" + d.join("L") + "Z";
}
function wind(ring, vs) {
var len = ring.length,
min = Infinity,
bestOffset,
sum;
for (var offset = 0, len = ring.length; offset < len; offset++) {
var sum = d3.sum(vs.map(function(p, i){
var distance = distanceBetween(ring[(offset + i) % len], p);
return distance * distance;
}));
if (sum < min) {
min = sum;
bestOffset = offset;
}
}
return ring.slice(bestOffset).concat(ring.slice(0, bestOffset));
}
</script>
Display the source blob
Display the rendered blob
Raw
{"type": "FeatureCollection", "features": [{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[111.916952,8.642059],[111.916866,8.642154],[111.917188,8.642504],[111.917392,8.642611],[111.917306,8.642717],[111.917467,8.642854],[111.917639,8.642833],[111.91795,8.643173],[111.917886,8.643258],[111.918175,8.643565],[111.91825,8.643979],[111.918154,8.644021],[111.918122,8.643915],[111.918036,8.643936],[111.918122,8.644308],[111.918186,8.644308],[111.918186,8.644255],[111.918358,8.644255],[111.918551,8.645008],[111.918465,8.644976],[111.918454,8.64523],[111.918422,8.645305],[111.918358,8.645315],[111.91839,8.6454],[111.918497,8.645549],[111.918508,8.645655],[111.918433,8.64574],[111.918486,8.645824],[111.918068,8.645803],[111.918068,8.645909],[111.918454,8.645941],[111.918508,8.646037],[111.918433,8.646068],[111.918433,8.646249],[111.918358,8.646249],[111.918293,8.6461],[111.917124,8.646334],[111.917038,8.646249],[111.916641,8.645368],[111.916673,8.645294],[111.91677,8.645305],[111.916791,8.6454],[111.916877,8.645347],[111.916695,8.644891],[111.916587,8.644923],[111.91663,8.64505],[111.916544,8.645124],[111.916448,8.644806],[111.916137,8.644233],[111.916137,8.643756],[111.916255,8.643756],[111.916255,8.643873],[111.916341,8.643873],[111.916351,8.643353],[111.916276,8.643353],[111.916265,8.643459],[111.916169,8.64347],[111.916169,8.643045],[111.916094,8.642992],[111.916029,8.642992],[111.915987,8.643077],[111.915976,8.644255],[111.916888,8.646387],[111.917381,8.646917],[111.918293,8.647299],[111.920278,8.648031],[111.923239,8.649176],[111.923207,8.649314],[111.923625,8.649473],[111.923625,8.649367],[111.924344,8.649664],[111.924977,8.649123],[111.924924,8.64854],[111.923175,8.646641],[111.921587,8.644456],[111.921126,8.643841],[111.92149,8.643533],[111.921619,8.643714],[111.921812,8.643576],[111.921523,8.643194],[111.921362,8.643332],[111.921426,8.643438],[111.921104,8.643745],[111.921072,8.643597],[111.920772,8.643332],[111.920418,8.643067],[111.920246,8.642961],[111.920139,8.642812],[111.919473,8.642207],[111.919398,8.642091],[111.919087,8.641995],[111.918969,8.642048],[111.918776,8.641868],[111.918036,8.641041],[111.918122,8.640956],[111.917757,8.640532],[111.917639,8.640606],[111.917435,8.640532],[111.917145,8.640447],[111.916963,8.640447],[111.916426,8.640882],[111.916373,8.641041],[111.916426,8.641476],[111.916952,8.642059]]]]},"properties":{"cartodb_id":1,"fid":0}},
{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[111.918395,8.644202],[111.918551,8.644891],[111.918637,8.645156],[111.918663,8.64548],[111.918728,8.645999],[111.919039,8.646318],[111.919034,8.646434],[111.919082,8.646519],[111.9192,8.646546],[111.919275,8.646509],[111.919312,8.646387],[111.919409,8.646355],[111.919763,8.646387],[111.920906,8.646721],[111.92126,8.646742],[111.921737,8.646657],[111.922215,8.64654],[111.92251,8.646286],[111.922601,8.645968],[111.92251,8.645713],[111.922038,8.644918],[111.921732,8.644599],[111.921673,8.644589],[111.921507,8.644355],[111.921319,8.644117],[111.921163,8.643894],[111.921126,8.643804],[111.92149,8.643475],[111.921533,8.643475],[111.921673,8.643639],[111.921748,8.64357],[111.921469,8.643258],[111.921394,8.643337],[111.921474,8.643427],[111.921115,8.643756],[111.92104,8.643767],[111.920879,8.643719],[111.920133,8.643093],[111.919752,8.642807],[111.919044,8.642372],[111.918781,8.642351],[111.918551,8.64226],[111.918406,8.642329],[111.918272,8.642489],[111.918261,8.642807],[111.918315,8.64365],[111.918352,8.643883],[111.918395,8.644202]]]]},"properties":{"cartodb_id":2,"fid":2}}
]}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment