Skip to content

Instantly share code, notes, and snippets.

@aerrity
Forked from gabrielflorit/Makefile
Last active March 8, 2017 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aerrity/3baaad32227b302b8662fb2316e4dd4c to your computer and use it in GitHub Desktop.
Save aerrity/3baaad32227b302b8662fb2316e4dd4c to your computer and use it in GitHub Desktop.
Equidistant map perimeter circles
Display the source blob
Display the rendered blob
Raw
{"type":"Topology","arcs":[[[1764,3879],[0,-1]],[[1884,4126],[1,-18],[-84,-121]],[[1732,3831],[43,69],[-2,66],[34,20],[71,109],[64,21],[57,-13],[39,67],[23,100],[23,-21],[101,-18],[21,37],[105,37],[27,31],[51,-21],[37,7],[62,-39],[52,25],[66,-78],[-9,-120],[46,-6],[-17,-67],[37,-24],[30,-69],[51,-69],[40,-1],[17,-85],[-30,-51],[-48,-21],[-39,-39],[12,-45],[78,48],[92,1],[32,-42],[31,-89],[21,-125],[-35,-133],[-47,71],[26,36],[-14,114],[-32,31],[-49,7],[11,-75],[29,-71],[-20,-81],[63,12],[11,-72],[-70,-99],[-41,31],[-62,-15],[-35,-40],[12,-45],[-20,-77],[-92,-87],[-82,87],[-52,16]],[[2451,3036],[104,-132],[-22,-22],[-54,6],[-46,31],[-41,-72],[10,-58],[74,-43],[-8,-118],[13,-113],[13,-30],[77,-90],[-33,-96],[13,-65],[-42,-61],[-10,-46],[68,-53],[-14,-20],[47,-204],[-12,-87],[24,-33],[-15,-82],[-64,-123],[0,-67],[-44,-85],[11,-126],[-98,-180],[-52,-65],[55,-88],[31,-14],[-28,-82],[-75,25],[-61,-27],[-39,48],[-41,5],[-89,-61],[-12,41],[-72,-72],[-54,41],[-32,-36],[-125,-7],[-124,-45],[35,-45],[-26,-64],[-63,-23],[-15,-37],[-71,2],[-86,-88],[-43,-63],[-114,-18],[-6,31],[38,73],[-70,1],[-22,-54],[45,-41],[-22,-80],[-40,-19],[-96,-76],[-63,-17],[-31,-69],[-94,18],[-29,-51],[-57,18],[-44,-11],[-69,-74],[-84,-19],[-52,71],[-159,-65],[-56,-30],[-17,41],[175,143],[-157,-71],[10,31],[52,40],[130,76],[-30,61],[-54,-57],[-165,-33],[-17,-35],[-55,-26],[-43,3],[46,83],[29,16],[-5,52],[90,60],[59,58],[-184,-66],[-39,-38],[-67,37],[-31,39],[-65,51],[62,81],[9,68],[27,2],[38,42],[27,-4],[82,47],[81,109],[-215,-57],[-54,18],[-100,-38],[-9,104],[57,-13],[9,68],[116,60],[10,-55],[71,30],[74,-39],[36,90],[0,77],[-48,21],[137,106],[15,88],[22,17],[84,-23],[26,23],[90,-6],[94,51],[61,-3],[53,77],[-40,90],[-91,-156],[-56,-1],[-45,-40],[-21,30],[-36,-13],[-76,37],[-37,-26],[-54,-6],[2,-34],[-58,-13],[7,64],[35,20],[73,120],[52,3],[42,108],[3,45],[48,51],[-48,44],[11,56],[54,80],[33,76],[65,-41],[27,48],[53,-18],[67,74],[-54,20],[-22,48],[-77,-25],[-34,5],[-138,-18],[-31,-14],[-41,27],[-46,154],[-47,-86],[-23,-9],[-65,76],[-72,19],[-33,66],[13,51],[-49,22],[-22,64],[83,9],[19,47],[49,-14],[20,41],[-28,63],[15,82],[74,21],[110,2],[6,131],[-56,-10],[-60,14],[-59,-42],[-36,7],[26,91],[27,54],[-77,114],[28,27],[-15,85],[-58,29],[-7,60],[45,29],[15,-33],[44,-9],[43,33],[7,57],[46,-21],[68,0],[95,-18],[57,16],[50,-17],[38,-76],[-12,-30],[53,-35],[44,127],[61,0],[64,-46],[47,19],[152,14],[-90,59],[-15,33],[73,37],[99,83],[58,12],[7,56],[86,99],[-91,41],[-52,-36],[-65,-11],[-100,19],[-79,41],[-8,48],[70,69],[81,30],[25,49],[71,27],[-36,55],[4,85],[52,94],[36,111],[76,-12],[83,31],[-2,27],[111,-41],[-26,47],[141,80],[17,-48],[-13,-34],[62,-75],[36,-1],[-50,90],[34,78],[36,-4],[56,35],[-5,74],[102,-48],[11,-46],[61,-18],[68,-58],[-165,-123],[-29,-54]],[[1801,3987],[-32,-8],[4,-78],[-41,-70]],[[125,2876],[40,34],[47,-4],[25,24],[79,-28],[6,-49],[-15,-92],[-64,52],[3,53],[-121,10]]],"transform":{"scale":[0.0017031223767726396,0.0008760662846581534],"translate":[-10.464093218949099,51.452799086782825]},"objects":{"boundary":{"type":"GeometryCollection","geometries":[{"type":"MultiLineString","arcs":[[0],[1],[2]],"properties":{"stroke":"#ff1b00","stroke-width":2,"stroke-opacity":1,"TYPE":0}},{"type":"MultiLineString","arcs":[[3],[4]],"properties":{"stroke":"#ffeb0f","stroke-width":2,"stroke-opacity":1,"TYPE":0}}]}}}
.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;
}
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==
<!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/turf@3.10.2/turf.min.js'></script>
<script src='https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js'></script>
<script src='dist.js'></script>
</body>
# 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;
// 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)
})
$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