Forked from Gabriel Florit's example
-
-
Save aerrity/3baaad32227b302b8662fb2316e4dd4c to your computer and use it in GitHub Desktop.
Equidistant map perimeter circles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.buttons { | |
margin: 0 auto; | |
text-align: center; | |
} | |
.map svg { | |
display: block; | |
margin: 0 auto; | |
} | |
.map svg circle { | |
fill: #008000; | |
fill-opacity: 0.25; | |
stroke: #008000; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var margin={top:10,right:10,bottom:10,left:10};d3.json("./boundary.topojson",function(t){var e=topojson.feature(t,t.objects.boundary),n=(d3.geoBounds(e),d3.geoCentroid(e),d3.geoAlbers()),r=d3.geoPath().projection(n),a=r.bounds(e),o=(a[1][0]-a[0][0])/(a[1][1]-a[0][1]),i=460,c=i*o,u=c-margin.left-margin.right,l=i-margin.top-margin.bottom,d=d3.select(".map svg").attrs({width:c,height:i}).append("g").attr("transform","rotate(45) translate("+100+", "+-170+")");n.fitSize([u,l],e);var s=_(e.features).map("geometry").flatten().map("coordinates").flatten().sortBy("length").map(function(t){return turf.lineString(t)}).value(),f=_(s).map(function(t){return turf.lineDistance(t)}).sum(),m=1e3,g=m,p=_(s).map(function(t){var e=turf.lineDistance(t),n=Math.ceil(e*m/f),r=Math.min(g,n);g-=r;var a=e/r,o=d3.range(r).map(function(e){return turf.along(t,e*a)});return o}).flatten().map(function(t){return n(t.geometry.coordinates)}).value(),v=function(){var t=d.selectAll("circle").data(p);t.enter().append("circle").attrs({cx:u/2,cy:l/2,r:0}).transition("enter").duration(1e3).delay(function(t,e){return 10*e}).attrs({cx:function(t){return t[0]},cy:function(t){return t[1]},r:1})},y=function(){var t=d.selectAll("circle").data(p);t.transition("exit").duration(250).delay(function(t,e){return 2*e}).attrs({cx:u/2,cy:l/2,r:0}).remove()};v(),document.querySelector("button.enter").addEventListener("click",v),document.querySelector("button.exit").addEventListener("click",y)}); | |
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNjcmlwdC5qcyJdLCJuYW1lcyI6WyJjb25zdCIsIm1hcmdpbiIsInRvcCIsInJpZ2h0IiwiYm90dG9tIiwibGVmdCIsImQzIiwianNvbiIsImZlYXR1cmUiLCJ0b3BvanNvbiIsIm9iamVjdHMiLCJib3VuZGFyeSIsInByb2plY3Rpb24iLCJnZW9Cb3VuZHMiLCJnZW9DZW50cm9pZCIsImdlb0FsYmVycyIsInBhdGgiLCJnZW9QYXRoIiwiYiIsImJvdW5kcyIsImFzcGVjdCIsIm91dGVySGVpZ2h0Iiwib3V0ZXJXaWR0aCIsIndpZHRoIiwiaGVpZ2h0IiwiZyIsInNlbGVjdCIsImF0dHJzIiwiYXBwZW5kIiwiYXR0ciIsImZpdFNpemUiLCJsaW5lU3RyaW5ncyIsIl8iLCJmZWF0dXJlcyIsIm1hcCIsImZsYXR0ZW4iLCJzb3J0QnkiLCJkIiwidHVyZiIsImxpbmVTdHJpbmciLCJ2YWx1ZSIsInRvdGFsTGVuZ3RoIiwibGluZURpc3RhbmNlIiwic3VtIiwicG9pbnRzQ291bnQiLCJwb2ludHNSZW1haW5pbmciLCJwb2ludHMiLCJsaW5lIiwibGluZUxlbmd0aCIsInVwcGVyQ291bnQiLCJNYXRoIiwiY2VpbCIsImxpbmVQb2ludHNDb3VudCIsIm1pbiIsInN0ZXAiLCJsaW5lUG9pbnRzIiwicmFuZ2UiLCJhbG9uZyIsImdlb21ldHJ5IiwiY29vcmRpbmF0ZXMiLCJlbnRlciIsImNpcmNsZXMiLCJzZWxlY3RBbGwiLCJkYXRhIiwiY3giLCJjeSIsInIiLCJ0cmFuc2l0aW9uIiwiZHVyYXRpb24iLCJkZWxheSIsImkiLCJleGl0IiwicmVtb3ZlIiwiZG9jdW1lbnQiLCJxdWVyeVNlbGVjdG9yIiwiYWRkRXZlbnRMaXN0ZW5lciJdLCJtYXBwaW5ncyI6IkFBQ0FBLEdBQU1DLFNBQVdDLElBQU8sR0FBRUMsTUFBUyxHQUFFQyxPQUFVLEdBQUVDLEtBQVEsR0FHekRDLElBQUdDLEtBQUssc0JBQXVCLFNBQUFBLEdBRTlCUCxHQUFNUSxHQUFVQyxTQUFTRCxRQUFRRCxFQUFNQSxFQUFLRyxRQUFRQyxVQVc5Q0MsR0FSV04sR0FBQ08sVUFBVUwsR0FDVEYsR0FBQ1EsWUFBWU4sR0FPWEYsR0FBQ1MsYUFHaEJDLEVBQVNWLEdBQUNXLFVBQVVMLFdBQVdBLEdBRzlCTSxFQUFHRixFQUFLRyxPQUFPWCxHQUdoQlksR0FBWUYsRUFBRSxHQUFHLEdBQUtBLEVBQUUsR0FBRyxLQUFPQSxFQUFFLEdBQUcsR0FBS0EsRUFBRSxHQUFHLElBRWpERyxFQUFjLElBQ2RDLEVBQWFELEVBQWNELEVBRTNCRyxFQUFRRCxFQUFhckIsT0FBT0ksS0FBT0osT0FBT0UsTUFDMUNxQixFQUFTSCxFQUFjcEIsT0FBT0MsSUFBTUQsT0FBT0csT0FHMUNxQixFQUFLbkIsR0FBQ29CLE9BQU8sWUFDakJDLE9BQVFKLE1BQU9ELEVBQVlFLE9BQVFILElBQ3BDTyxPQUFPLEtBQ05DLEtBQUssWUFBYSxhQUFXNUIsT0FBUyxLQUFBLEtBQUlBLE9BQUcsSUFBQSxJQUdoRFcsR0FBV2tCLFNBQVNQLEVBQU9DLEdBQVNoQixFQUdwQ1IsSUFBTStCLEdBQWdCQyxFQUFBeEIsRUFBUXlCLFVBQzVCQyxJQUFJLFlBQ0pDLFVBQ0FELElBQUksZUFDSkMsVUFDQUMsT0FBTyxVQUNQRixJQUFJLFNBQUFHLEdBQUEsTUFBQUMsTUFBQUMsV0FBRUYsS0FDTkcsUUFHSUMsRUFBZ0JULEVBQUFELEdBQ3BCRyxJQUFJLFNBQUFHLEdBQUEsTUFBQUMsTUFBQUksYUFBS0wsS0FDVE0sTUFHSUMsRUFBYyxJQUNoQkMsRUFBa0JELEVBRWhCRSxFQUFXZCxFQUFBRCxHQUNmRyxJQUFJLFNBQUFhLEdBS0ovQyxHQUFNZ0QsR0FBYVYsS0FBS0ksYUFBYUssR0FHL0JFLEVBQWFDLEtBQUtDLEtBQUtILEVBQWFKLEVBQWNILEdBR2xEVyxFQUFrQkYsS0FBS0csSUFBSVIsRUFBaUJJLEVBR2xESixJQUFtQk8sQ0FJbkJwRCxJQUFNc0QsR0FBT04sRUFBYUksRUFFcEJHLEVBQWVqRCxHQUFDa0QsTUFBTUosR0FDMUJsQixJQUFJLFNBQUFHLEdBQUEsTUFBQUMsTUFBQW1CLE1BQUVWLEVBQUdWLEVBQUlpQixJQUVmLE9BQU9DLEtBR1BwQixVQUNBRCxJQUFJLFNBQUFHLEdBQUEsTUFBQXpCLEdBQUV5QixFQUFBcUIsU0FBR0MsZUFDVG5CLFFBR0lvQixFQUFRLFdBR2I1RCxHQUFNNkQsR0FBWXBDLEVBQUFxQyxVQUFVLFVBQ3pCQyxLQUFLakIsRUFHUmUsR0FBUUQsUUFBUWhDLE9BQU8sVUFDcEJELE9BQ0FxQyxHQUFJekMsRUFBTSxFQUNWMEMsR0FBSXpDLEVBQU8sRUFDWDBDLEVBQUcsSUFFSkMsV0FBVyxTQUNWQyxTQUFTLEtBQ1RDLE1BQU0sU0FBQWhDLEVBQUFpQyxHQUFBLE1BQUssSUFBSkEsSUFDUDNDLE9BQ0FxQyxHQUFJLFNBQUEzQixHQUFBLE1BQUFBLEdBQUEsSUFDSjRCLEdBQUksU0FBQTVCLEdBQUEsTUFBQUEsR0FBQSxJQUNKNkIsRUFBRyxLQU1ESyxFQUFPLFdBR1p2RSxHQUFNNkQsR0FBWXBDLEVBQUFxQyxVQUFVLFVBQ3pCQyxLQUFLakIsRUFHUmUsR0FDRU0sV0FBVyxRQUNWQyxTQUFTLEtBQ1RDLE1BQU0sU0FBQWhDLEVBQUFpQyxHQUFBLE1BQUssR0FBSkEsSUFDUDNDLE9BQ0FxQyxHQUFJekMsRUFBTSxFQUNWMEMsR0FBSXpDLEVBQU8sRUFDWDBDLEVBQUcsSUFFSk0sU0FLSFosS0FHQWEsU0FBU0MsY0FBYyxnQkFBZ0JDLGlCQUFpQixRQUFTZixHQUNqRWEsU0FBU0MsY0FBYyxlQUFlQyxpQkFBaUIsUUFBU0oiLCJmaWxlIjoic2NyaXB0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gU2V0dXAgY2hhcnQgZGltZW5zaW9ucy5cbmNvbnN0IG1hcmdpbiA9IHsgdG9wOiAxMCwgcmlnaHQ6IDEwLCBib3R0b206IDEwLCBsZWZ0OiAxMCB9XG5cbi8vIEdldCBHZW9KU09OLlxuZDMuanNvbignLi9ib3VuZGFyeS50b3BvanNvbicsIGpzb24gPT4ge1xuXG5cdGNvbnN0IGZlYXR1cmUgPSB0b3BvanNvbi5mZWF0dXJlKGpzb24sIGpzb24ub2JqZWN0cy5ib3VuZGFyeSlcblxuXHQvLyBHZXQgZmVhdHVyZSdzIGJvdW5kcyBhbmQgY2VudHJvaWQuXG5cdGNvbnN0IGJvdW5kcyA9IGQzLmdlb0JvdW5kcyhmZWF0dXJlKVxuXHRjb25zdCBjZW50cm9pZCA9IGQzLmdlb0NlbnRyb2lkKGZlYXR1cmUpXG5cblx0Ly8gY29uc3QgcHJvamVjdGlvbiA9IGQzLmdlb0NvbmljQ29uZm9ybWFsKClcblx0Ly8gXHQucGFyYWxsZWxzKFtib3VuZHNbMF1bMV0sIGJvdW5kc1sxXVsxXV0pXG5cdC8vIFx0LnJvdGF0ZShbLWNlbnRyb2lkWzBdLCAwXSlcblx0Ly8gXHQuY2VudGVyKFswLCAtY2VudHJvaWRbMV1dKVxuXG5cdGNvbnN0IHByb2plY3Rpb24gPSBkMy5nZW9BbGJlcnMoKVxuXG5cdC8vIEdldCB0aGUgcGF0aC5cblx0Y29uc3QgcGF0aCA9IGQzLmdlb1BhdGgoKS5wcm9qZWN0aW9uKHByb2plY3Rpb24pXG5cblx0Ly8gR2V0IHRoZSBwYXRoJ3MgYm91bmRzIChpLmUuLCBpbiBwaXhlbHMpLlxuXHRjb25zdCBiID0gcGF0aC5ib3VuZHMoZmVhdHVyZSlcblxuXHQvLyBHZXQgYXNwZWN0IHJhdGlvLlxuXHRjb25zdCBhc3BlY3QgPSAoYlsxXVswXSAtIGJbMF1bMF0pIC8gKGJbMV1bMV0gLSBiWzBdWzFdKVxuXG5cdGNvbnN0IG91dGVySGVpZ2h0ID0gNDYwXG5cdGNvbnN0IG91dGVyV2lkdGggPSBvdXRlckhlaWdodCAqIGFzcGVjdFxuXG5cdGNvbnN0IHdpZHRoID0gb3V0ZXJXaWR0aCAtIG1hcmdpbi5sZWZ0IC0gbWFyZ2luLnJpZ2h0XG5cdGNvbnN0IGhlaWdodCA9IG91dGVySGVpZ2h0IC0gbWFyZ2luLnRvcCAtIG1hcmdpbi5ib3R0b21cblxuXHQvLyBQcmVwYXJlIHN2Zy5cblx0Y29uc3QgZyA9IGQzLnNlbGVjdCgnLm1hcCBzdmcnKVxuXHRcdFx0LmF0dHJzKHsgd2lkdGg6IG91dGVyV2lkdGgsIGhlaWdodDogb3V0ZXJIZWlnaHQgfSlcblx0XHQuYXBwZW5kKCdnJylcblx0XHRcdC5hdHRyKCd0cmFuc2Zvcm0nLCBgdHJhbnNsYXRlKCR7bWFyZ2luLmxlZnR9LCAke21hcmdpbi50b3B9KWApXG5cblx0Ly8gRml0IHRoZSBmZWF0dXJlIHRvIHRoZSBjb250YWluZXIncyB3aWR0aC5cblx0cHJvamVjdGlvbi5maXRTaXplKFt3aWR0aCwgaGVpZ2h0XSwgZmVhdHVyZSlcblxuXHQvLyBHZXQgdGhlIGluZGl2aWR1YWwgbGluZSBzdHJpbmdzLlxuXHRjb25zdCBsaW5lU3RyaW5ncyA9IF8oZmVhdHVyZS5mZWF0dXJlcylcblx0XHQubWFwKCdnZW9tZXRyeScpXG5cdFx0LmZsYXR0ZW4oKVxuXHRcdC5tYXAoJ2Nvb3JkaW5hdGVzJylcblx0XHQuZmxhdHRlbigpXG5cdFx0LnNvcnRCeSgnbGVuZ3RoJylcblx0XHQubWFwKGQgPT4gdHVyZi5saW5lU3RyaW5nKGQpKVxuXHRcdC52YWx1ZSgpXG5cblx0Ly8gQ2FsY3VsYXRlIHRoZSBvdmVyYWxsIGxpbmUgc3RyaW5nIGxlbmd0aC5cblx0Y29uc3QgdG90YWxMZW5ndGggPSBfKGxpbmVTdHJpbmdzKVxuXHRcdC5tYXAoZCA9PiB0dXJmLmxpbmVEaXN0YW5jZShkKSlcblx0XHQuc3VtKClcblxuXHQvLyBEZXNpcmVkIG51bWJlciBvZiB0b3RhbCBwb2ludHMuXG5cdGNvbnN0IHBvaW50c0NvdW50ID0gMTAwMFxuXHRsZXQgcG9pbnRzUmVtYWluaW5nID0gcG9pbnRzQ291bnRcblxuXHRjb25zdCBwb2ludHMgPSBfKGxpbmVTdHJpbmdzKVxuXHRcdC5tYXAobGluZSA9PiB7XG5cblx0XHRcdC8vIEhvdyBtYW55IHBvaW50cyB3aWxsIHRoaXMgbGluZSBnZXQ/XG5cblx0XHRcdC8vIEZpcnN0LCBjYWxjdWxhdGUgdGhpcyBsaW5lJ3MgbGVuZ3RoLlxuXHRcdFx0Y29uc3QgbGluZUxlbmd0aCA9IHR1cmYubGluZURpc3RhbmNlKGxpbmUpXG5cblx0XHRcdC8vIE5leHQsIGdldCB0aGlzIGxpbmUncyBwb2ludHMgcHJvcG9ydGlvbiwgcm91bmRlZCB1cC5cblx0XHRcdGNvbnN0IHVwcGVyQ291bnQgPSBNYXRoLmNlaWwobGluZUxlbmd0aCAqIHBvaW50c0NvdW50IC8gdG90YWxMZW5ndGgpXG5cblx0XHRcdC8vIERvbid0IGdldCBtb3JlIHBvaW50cyB0aGF0IGFyZSBhdmFpbGFibGUuXG5cdFx0XHRjb25zdCBsaW5lUG9pbnRzQ291bnQgPSBNYXRoLm1pbihwb2ludHNSZW1haW5pbmcsIHVwcGVyQ291bnQpXG5cblx0XHRcdC8vIE1ha2Ugc3VyZSB0byB1cGRhdGUgcG9pbnRzIHJlbWFpbmluZy5cblx0XHRcdHBvaW50c1JlbWFpbmluZyAtPSBsaW5lUG9pbnRzQ291bnRcblxuXHRcdFx0Ly8gTm93IHRoYXQgd2Uga25vdyBob3cgbWFueSBwb2ludHMgdGhpcyBsaW5lIHdpbGwgZ2V0LFxuXHRcdFx0Ly8gY2FsY3VsYXRlIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHBvaW50cyAtIHRoZSBzdGVwOlxuXHRcdFx0Y29uc3Qgc3RlcCA9IGxpbmVMZW5ndGggLyBsaW5lUG9pbnRzQ291bnRcblxuXHRcdFx0Y29uc3QgbGluZVBvaW50cyA9IGQzLnJhbmdlKGxpbmVQb2ludHNDb3VudClcblx0XHRcdFx0Lm1hcChkID0+IHR1cmYuYWxvbmcobGluZSwgZCAqIHN0ZXApKVxuXG5cdFx0XHRyZXR1cm4gbGluZVBvaW50c1xuXG5cdFx0fSlcblx0XHQuZmxhdHRlbigpXG5cdFx0Lm1hcChkID0+IHByb2plY3Rpb24oZC5nZW9tZXRyeS5jb29yZGluYXRlcykpXG5cdFx0LnZhbHVlKClcblxuXHQvLyBUaGlzIGZ1bmN0aW9uIGFkZHMgdGhlIGNpcmNsZXMuXG5cdGNvbnN0IGVudGVyID0gKCkgPT4ge1xuXG5cdFx0Ly8gSk9JTiBuZXcgZGF0YSB3aXRoIG9sZCBlbGVtZW50cy5cblx0XHRjb25zdCBjaXJjbGVzID0gZy5zZWxlY3RBbGwoJ2NpcmNsZScpXG5cdFx0XHRcdC5kYXRhKHBvaW50cylcblxuXHRcdC8vIEVOVEVSIG5ldyBlbGVtZW50cyBwcmVzZW50IGluIG5ldyBkYXRhLlxuXHRcdGNpcmNsZXMuZW50ZXIoKS5hcHBlbmQoJ2NpcmNsZScpXG5cdFx0XHRcdC5hdHRycyh7XG5cdFx0XHRcdFx0Y3g6IHdpZHRoLzIsXG5cdFx0XHRcdFx0Y3k6IGhlaWdodC8yLFxuXHRcdFx0XHRcdHI6IDAsXG5cdFx0XHRcdH0pXG5cdFx0XHQudHJhbnNpdGlvbignZW50ZXInKVxuXHRcdFx0XHQuZHVyYXRpb24oMTAwMClcblx0XHRcdFx0LmRlbGF5KChkLCBpKSA9PiBpICogMTApXG5cdFx0XHRcdC5hdHRycyh7XG5cdFx0XHRcdFx0Y3g6IGQgPT4gZFswXSxcblx0XHRcdFx0XHRjeTogZCA9PiBkWzFdLFxuXHRcdFx0XHRcdHI6IDEsXG5cdFx0XHRcdH0pXG5cblx0fVxuXG5cdC8vIFRoaXMgZnVuY3Rpb24gcmVtb3ZlcyB0aGUgY2lyY2xlcy5cblx0Y29uc3QgZXhpdCA9ICgpID0+IHtcblxuXHRcdC8vIEpPSU4gbmV3IGRhdGEgd2l0aCBvbGQgZWxlbWVudHMuXG5cdFx0Y29uc3QgY2lyY2xlcyA9IGcuc2VsZWN0QWxsKCdjaXJjbGUnKVxuXHRcdFx0XHQuZGF0YShwb2ludHMpXG5cblx0XHQvLyBVUERBVEUgb2xkIGVsZW1lbnRzIHByZXNlbnQgaW4gbmV3IGRhdGEuXG5cdFx0Y2lyY2xlc1xuXHRcdFx0LnRyYW5zaXRpb24oJ2V4aXQnKVxuXHRcdFx0XHQuZHVyYXRpb24oMjUwKVxuXHRcdFx0XHQuZGVsYXkoKGQsIGkpID0+IGkgKiAyKVxuXHRcdFx0XHQuYXR0cnMoe1xuXHRcdFx0XHRcdGN4OiB3aWR0aC8yLFxuXHRcdFx0XHRcdGN5OiBoZWlnaHQvMixcblx0XHRcdFx0XHRyOiAwLFxuXHRcdFx0XHR9KVxuXHRcdFx0LnJlbW92ZSgpXG5cblx0fVxuXG5cdC8vIEZpcmUgdGhlIGVudGVyIGZ1bmN0aW9uIG9uIHBhZ2UgbG9hZC5cblx0ZW50ZXIoKVxuXG5cdC8vIExpc3RlbiB0byBidXR0b24gY2xpY2tzLlxuXHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdidXR0b24uZW50ZXInKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGVudGVyKVxuXHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdidXR0b24uZXhpdCcpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXhpdClcblxufSlcbiJdfQ== |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<title>Equidistant outline circles</title> | |
<link href='dist.css' rel='stylesheet' /> | |
<body> | |
<div class='map'> | |
<svg></svg> | |
</div> | |
<div class='buttons'> | |
<button class='enter'>Enter</button> | |
<button class='exit'>Exit</button> | |
</div> | |
<script src='https://d3js.org/d3.v4.min.js'></script> | |
<script src='https://d3js.org/d3-selection-multi.v1.min.js'></script> | |
<script src='https://d3js.org/topojson.v2.min.js'></script> | |
<script src='https://npmcdn.com/@turf/[email protected]/turf.min.js'></script> | |
<script src='https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js'></script> | |
<script src='dist.js'></script> | |
</body> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Not used for Irish example. http://mapshaper.org/ used instead. | |
#all: | |
# | |
# rm boundary.topojson; | |
# mapshaper -i ~/Downloads/cb_2015_us_nation_5m/cb_2015_us_nation_5m.shp name=boundary -clip bbox=-126,23,-65,50 -filter-slivers min-area=700000000 -lines -simplify dp 5% -o format=topojson boundary.topojson; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Setup chart dimensions. | |
const margin = { top: 10, right: 10, bottom: 10, left: 10 } | |
// Get GeoJSON. | |
d3.json('./boundary.topojson', json => { | |
const feature = topojson.feature(json, json.objects.boundary) | |
// Get feature's bounds and centroid. | |
const bounds = d3.geoBounds(feature) | |
const centroid = d3.geoCentroid(feature) | |
// const projection = d3.geoConicConformal() | |
// .parallels([bounds[0][1], bounds[1][1]]) | |
// .rotate([-centroid[0], 0]) | |
// .center([0, -centroid[1]]) | |
const projection = d3.geoAlbers() | |
const rotate = [0,0,-45] | |
// Get the path. | |
const path = d3.geoPath().projection(projection).rotate(rotate) | |
// Get the path's bounds (i.e., in pixels). | |
const b = path.bounds(feature) | |
// Get aspect ratio. | |
const aspect = (b[1][0] - b[0][0]) / (b[1][1] - b[0][1]) | |
const outerHeight = 460 | |
const outerWidth = outerHeight * aspect | |
const width = outerWidth - margin.left - margin.right | |
const height = outerHeight - margin.top - margin.bottom | |
// Prepare svg. | |
const g = d3.select('.map svg') | |
.attrs({ width: outerWidth, height: outerHeight }) | |
.append('g') | |
.attr('transform', `rotate(45) translate("+100+", "+-170+"))`) | |
// Fit the feature to the container's width. | |
projection.fitSize([width, height], feature) | |
// Get the individual line strings. | |
const lineStrings = _(feature.features) | |
.map('geometry') | |
.flatten() | |
.map('coordinates') | |
.flatten() | |
.sortBy('length') | |
.map(d => turf.lineString(d)) | |
.value() | |
// Calculate the overall line string length. | |
const totalLength = _(lineStrings) | |
.map(d => turf.lineDistance(d)) | |
.sum() | |
// Desired number of total points. | |
const pointsCount = 1000 | |
let pointsRemaining = pointsCount | |
const points = _(lineStrings) | |
.map(line => { | |
// How many points will this line get? | |
// First, calculate this line's length. | |
const lineLength = turf.lineDistance(line) | |
// Next, get this line's points proportion, rounded up. | |
const upperCount = Math.ceil(lineLength * pointsCount / totalLength) | |
// Don't get more points that are available. | |
const linePointsCount = Math.min(pointsRemaining, upperCount) | |
// Make sure to update points remaining. | |
pointsRemaining -= linePointsCount | |
// Now that we know how many points this line will get, | |
// calculate the distance between points - the step: | |
const step = lineLength / linePointsCount | |
const linePoints = d3.range(linePointsCount) | |
.map(d => turf.along(line, d * step)) | |
return linePoints | |
}) | |
.flatten() | |
.map(d => projection(d.geometry.coordinates)) | |
.value() | |
// This function adds the circles. | |
const enter = () => { | |
// JOIN new data with old elements. | |
const circles = g.selectAll('circle') | |
.data(points) | |
// ENTER new elements present in new data. | |
circles.enter().append('circle') | |
.attrs({ | |
cx: width/2, | |
cy: height/2, | |
r: 0, | |
}) | |
.transition('enter') | |
.duration(1000) | |
.delay((d, i) => i * 10) | |
.attrs({ | |
cx: d => d[0], | |
cy: d => d[1], | |
r: 1, | |
}) | |
} | |
// This function removes the circles. | |
const exit = () => { | |
// JOIN new data with old elements. | |
const circles = g.selectAll('circle') | |
.data(points) | |
// UPDATE old elements present in new data. | |
circles | |
.transition('exit') | |
.duration(250) | |
.delay((d, i) => i * 2) | |
.attrs({ | |
cx: width/2, | |
cy: height/2, | |
r: 0, | |
}) | |
.remove() | |
} | |
// Fire the enter function on page load. | |
enter() | |
// Listen to button clicks. | |
document.querySelector('button.enter').addEventListener('click', enter) | |
document.querySelector('button.exit').addEventListener('click', exit) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$green = #008000 | |
.buttons | |
margin 0 auto | |
text-align center | |
.map | |
svg | |
display block | |
margin 0 auto | |
circle | |
fill $green | |
fill-opacity 0.25 | |
stroke $green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment