Skip to content

Instantly share code, notes, and snippets.

@gabrielflorit
Last active June 21, 2017 20:52
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabrielflorit/f822f40b7f20e019f381e3a266280bde to your computer and use it in GitHub Desktop.
Save gabrielflorit/f822f40b7f20e019f381e3a266280bde to your computer and use it in GitHub Desktop.
Lat/lon histogram
license: mit
height: 720
border: no

Lat/lon histogram

Made with blockup.

Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
.buttons{margin:0 auto;text-align:center}.map svg{display:block;margin:0 auto}.map svg circle{fill:#8f092a;fill-opacity:.25;stroke:#8f092a}
var margin={top:10,right:10,bottom:10,left:10};d3.json("./boundary.topojson",function(t){var n=topojson.feature(t,t.objects.boundary),e=(d3.geoBounds(n),d3.geoCentroid(n),d3.geoAlbers()),r=d3.geoPath().projection(e),o=r.bounds(n),a=(o[1][0]-o[0][0])/(o[1][1]-o[0][1]),i=460,u=i*a,c=u-margin.left-margin.right,f=i-margin.top-margin.bottom,l=d3.select(".map svg").attrs({width:u,height:i}).append("g").attr("transform","translate("+margin.left+", "+margin.top+")");e.fitSize([c,f],n);var d=_(n.features).map("geometry").flatten().map("coordinates").flatten().sortBy("length").map(function(t){return turf.lineString(t)}).value(),s=_(d).map(function(t){return turf.lineDistance(t)}).sum(),m=1e3,g=m,p=_(d).map(function(t){var n=turf.lineDistance(t),e=Math.ceil(n*m/s),r=Math.min(g,e);g-=r;var o=n/r,a=d3.range(r).map(function(n){return turf.along(t,n*o)});return a}).flatten().map(function(t){return e(t.geometry.coordinates)}).map(function(t,n){return{index:n,x2:t[0],y2:t[1]}}).value(),y=5,x=_(p).groupBy(function(t){return Math.round(t.y2/y)*y}).map(function(t,n){return _(t).sortBy("x2").map(function(e,r){return{index:e.index,x1:c/2+4*r-4*t.length/2,y1:+n,x2:e.x2,y2:e.y2}}).value()}).flatten().value(),v=function(){var t=l.selectAll("circle").data(x,function(t){return t.index});t.enter().append("circle").attrs({cx:function(t){return t.x1},cy:function(t){return t.y1},r:1}).merge(t).transition("enter").duration(1e3).delay(function(t,n){return 10*n}).attrs({cx:function(t){return t.x2},cy:function(t){return t.y2},r:1})},h=function(){var t=l.selectAll("circle").data(x,function(t){return t.index});t.transition("exit").duration(250).delay(function(t,n){return 2*n}).attrs({cx:function(t){return t.x1},cy:function(t){return t.y1},r:1})};v(),document.querySelector("button.enter").addEventListener("click",v),document.querySelector("button.exit").addEventListener("click",h)});
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNjcmlwdC5qcyJdLCJuYW1lcyI6WyJjb25zdCIsIm1hcmdpbiIsInRvcCIsInJpZ2h0IiwiYm90dG9tIiwibGVmdCIsImQzIiwianNvbiIsImZlYXR1cmUiLCJ0b3BvanNvbiIsIm9iamVjdHMiLCJib3VuZGFyeSIsInByb2plY3Rpb24iLCJnZW9Cb3VuZHMiLCJnZW9DZW50cm9pZCIsImdlb0FsYmVycyIsInBhdGgiLCJnZW9QYXRoIiwiYiIsImJvdW5kcyIsImFzcGVjdCIsIm91dGVySGVpZ2h0Iiwib3V0ZXJXaWR0aCIsIndpZHRoIiwiaGVpZ2h0IiwiZyIsInNlbGVjdCIsImF0dHJzIiwiYXBwZW5kIiwiYXR0ciIsImZpdFNpemUiLCJsaW5lU3RyaW5ncyIsIl8iLCJmZWF0dXJlcyIsIm1hcCIsImZsYXR0ZW4iLCJzb3J0QnkiLCJkIiwidHVyZiIsImxpbmVTdHJpbmciLCJ2YWx1ZSIsInRvdGFsTGVuZ3RoIiwibGluZURpc3RhbmNlIiwic3VtIiwicG9pbnRzQ291bnQiLCJwb2ludHNSZW1haW5pbmciLCJwb2ludHMiLCJsaW5lIiwibGluZUxlbmd0aCIsInVwcGVyQ291bnQiLCJNYXRoIiwiY2VpbCIsImxpbmVQb2ludHNDb3VudCIsIm1pbiIsInN0ZXAiLCJsaW5lUG9pbnRzIiwicmFuZ2UiLCJhbG9uZyIsImdlb21ldHJ5IiwiY29vcmRpbmF0ZXMiLCJpbmRleCIsIngyIiwieTIiLCJ5U3RlcCIsImhpc3RvZ3JhbSIsImdyb3VwQnkiLCJyb3VuZCIsImtleSIsImkiLCJ4MSIsImxlbmd0aCIsInkxIiwiZW50ZXIiLCJjaXJjbGVzIiwic2VsZWN0QWxsIiwiZGF0YSIsImN4IiwiY3kiLCJyIiwibWVyZ2UiLCJ0cmFuc2l0aW9uIiwiZHVyYXRpb24iLCJkZWxheSIsImV4aXQiLCJkb2N1bWVudCIsInF1ZXJ5U2VsZWN0b3IiLCJhZGRFdmVudExpc3RlbmVyIl0sIm1hcHBpbmdzIjoiQUFDQUEsR0FBTUMsU0FBV0MsSUFBTyxHQUFFQyxNQUFTLEdB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LEdBQUEsTUFBQUEsR0FBQXdCLElBQ0pnQixHQUFJLFNBQUF4QyxHQUFBLE1BQUFBLEdBQUF5QixJQUNKZ0IsRUFBQyxLQUtDSyxFQUFPLFdBR1puRixHQUNHeUUsR0FBS2hELEVBQVNpRCxVQUFFLFVBQWhCQyxLQUFLWCxFQUFXLFNBQUEzQixHQUFFLE1BQUdBLEdBQUV1QixPQUcxQmEsR0FDRU8sV0FDVSxRQUNUQyxTQUFNLEtBQ05DLE1BQU0sU0FBQTdDLEVBQUErQixHQUFBLE1BQUEsR0FBQUEsSUFDTnpDLE9BQ0FpRCxHQUFJLFNBQUF2QyxHQUFBLE1BQUFBLEdBQUFnQyxJQUNKUSxHQUFJLFNBQUF4QyxHQUFBLE1BQUFBLEdBQUFrQyxJQUNKTyxFQUFDLElBS0xOLEtBSUFZLFNBQVNDLGNBQWMsZ0JBQWVDLGlCQUFpQixRQUFTZCxHQUFoRVksU0FBU0MsY0FBYyxlQUFlQyxpQkFBaUIsUUFBU0giLCJmaWxlIjoic2NyaXB0LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gU2V0dXAgY2hhcnQgZGltZW5zaW9ucy5cbmNvbnN0IG1hcmdpbiA9IHsgdG9wOiAxMCwgcmlnaHQ6IDEwLCBib3R0b206IDEwLCBsZWZ0OiAxMCB9XG5cbi8vIEdldCBHZW9KU09OLlxuZDMuanNvbignLi9ib3VuZGFyeS50b3BvanNvbicsIGpzb24gPT4ge1xuXG5cdGNvbnN0IGZlYXR1cmUgPSB0b3BvanNvbi5mZWF0dXJlKGpzb24sIGpzb24ub2JqZWN0cy5ib3VuZGFyeSlcblxuXHQvLyBHZXQgZmVhdHVyZSdzIGJvdW5kcywgY2VudHJvaWQsIGFuZCBwcm9qZWN0aW9uLlxuXHRjb25zdCBib3VuZHMgPSBkMy5nZW9Cb3VuZHMoZmVhdHVyZSlcblx0Y29uc3QgY2VudHJvaWQgPSBkMy5nZW9DZW50cm9pZChmZWF0dXJlKVxuXHRjb25zdCBwcm9qZWN0aW9uID0gZDMuZ2VvQWxiZXJzKClcblxuXHQvLyBHZXQgdGhlIHBhdGguXG5cdGNvbnN0IHBhdGggPSBkMy5nZW9QYXRoKCkucHJvamVjdGlvbihwcm9qZWN0aW9uKVxuXG5cdC8vIEdldCB0aGUgcGF0aCdzIGJvdW5kcyAoaS5lLiwgaW4gcGl4ZWxzKS5cblx0Y29uc3QgYiA9IHBhdGguYm91bmRzKGZlYXR1cmUpXG5cblx0Ly8gR2V0IGFzcGVjdCByYXRpby5cblx0Y29uc3QgYXNwZWN0ID0gKGJbMV1bMF0gLSBiWzBdWzBdKSAvIChiWzFdWzFdIC0gYlswXVsxXSlcblxuXHRjb25zdCBvdXRlckhlaWdodCA9IDQ2MFxuXHRjb25zdCBvdXRlcldpZHRoID0gb3V0ZXJIZWlnaHQgKiBhc3BlY3RcblxuXHRjb25zdCB3aWR0aCA9IG91dGVyV2lkdGggLSBtYXJnaW4ubGVmdCAtIG1hcmdpbi5yaWdodFxuXHRjb25zdCBoZWlnaHQgPSBvdXRlckhlaWdodCAtIG1hcmdpbi50b3AgLSBtYXJnaW4uYm90dG9tXG5cblx0Ly8gUHJlcGFyZSBzdmcuXG5cdGNvbnN0IGcgPSBkMy5zZWxlY3QoJy5tYXAgc3ZnJylcblx0XHRcdC5hdHRycyh7IHdpZHRoOiBvdXRlcldpZHRoLCBoZWlnaHQ6IG91dGVySGVpZ2h0IH0pXG5cdFx0LmFwcGVuZCgnZycpXG5cdFx0XHQuYXR0cigndHJhbnNmb3JtJywgYHRyYW5zbGF0ZSgke21hcmdpbi5sZWZ0fSwgJHttYXJnaW4udG9wfSlgKVxuXG5cdC8vIEZpdCB0aGUgZmVhdHVyZSB0byB0aGUgY29udGFpbmVyJ3Mgd2lkdGguXG5cdHByb2plY3Rpb24uZml0U2l6ZShbd2lkdGgsIGhlaWdodF0sIGZlYXR1cmUpXG5cblx0Ly8gR2V0IHRoZSBpbmRpdmlkdWFsIGxpbmUgc3RyaW5ncy5cblx0Y29uc3QgbGluZVN0cmluZ3MgPSBfKGZlYXR1cmUuZmVhdHVyZXMpXG5cdFx0Lm1hcCgnZ2VvbWV0cnknKVxuXHRcdC5mbGF0dGVuKClcblx0XHQubWFwKCdjb29yZGluYXRlcycpXG5cdFx0LmZsYXR0ZW4oKVxuXHRcdC5zb3J0QnkoJ2xlbmd0aCcpXG5cdFx0Lm1hcChkID0+IHR1cmYubGluZVN0cmluZyhkKSlcblx0XHQudmFsdWUoKVxuXG5cdC8vIENhbGN1bGF0ZSB0aGUgb3ZlcmFsbCBsaW5lIHN0cmluZyBsZW5ndGguXG5cdGNvbnN0IHRvdGFsTGVuZ3RoID0gXyhsaW5lU3RyaW5ncylcblx0XHQubWFwKGQgPT4gdHVyZi5saW5lRGlzdGFuY2UoZCkpXG5cdFx0LnN1bSgpXG5cblx0Ly8gRGVzaXJlZCBudW1iZXIgb2YgdG90YWwgcG9pbnRzLlxuXHRjb25zdCBwb2ludHNDb3VudCA9IDEwMDBcblx0bGV0IHBvaW50c1JlbWFpbmluZyA9IHBvaW50c0NvdW50XG5cblx0Y29uc3QgcG9pbnRzID0gXyhsaW5lU3RyaW5ncylcblx0XHQubWFwKGxpbmUgPT4ge1xuXG5cdFx0XHQvLyBIb3cgbWFueSBwb2ludHMgd2lsbCB0aGlzIGxpbmUgZ2V0P1xuXG5cdFx0XHQvLyBGaXJzdCwgY2FsY3VsYXRlIHRoaXMgbGluZSdzIGxlbmd0aC5cblx0XHRcdGNvbnN0IGxpbmVMZW5ndGggPSB0dXJmLmxpbmVEaXN0YW5jZShsaW5lKVxuXG5cdFx0XHQvLyBOZXh0LCBnZXQgdGhpcyBsaW5lJ3MgcG9pbnRzIHByb3BvcnRpb24sIHJvdW5kZWQgdXAuXG5cdFx0XHRjb25zdCB1cHBlckNvdW50ID0gTWF0aC5jZWlsKGxpbmVMZW5ndGggKiBwb2ludHNDb3VudCAvIHRvdGFsTGVuZ3RoKVxuXG5cdFx0XHQvLyBEb24ndCBnZXQgbW9yZSBwb2ludHMgdGhhdCBhcmUgYXZhaWxhYmxlLlxuXHRcdFx0Y29uc3QgbGluZVBvaW50c0NvdW50ID0gTWF0aC5taW4ocG9pbnRzUmVtYWluaW5nLCB1cHBlckNvdW50KVxuXG5cdFx0XHQvLyBNYWtlIHN1cmUgdG8gdXBkYXRlIHBvaW50cyByZW1haW5pbmcuXG5cdFx0XHRwb2ludHNSZW1haW5pbmcgLT0gbGluZVBvaW50c0NvdW50XG5cblx0XHRcdC8vIE5vdyB0aGF0IHdlIGtub3cgaG93IG1hbnkgcG9pbnRzIHRoaXMgbGluZSB3aWxsIGdldCxcblx0XHRcdC8vIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBwb2ludHMgLSB0aGUgc3RlcDpcblx0XHRcdGNvbnN0IHN0ZXAgPSBsaW5lTGVuZ3RoIC8gbGluZVBvaW50c0NvdW50XG5cblx0XHRcdGNvbnN0IGxpbmVQb2ludHMgPSBkMy5yYW5nZShsaW5lUG9pbnRzQ291bnQpXG5cdFx0XHRcdC5tYXAoZCA9PiB0dXJmLmFsb25nKGxpbmUsIGQgKiBzdGVwKSlcblxuXHRcdFx0cmV0dXJuIGxpbmVQb2ludHNcblxuXHRcdH0pXG5cdFx0LmZsYXR0ZW4oKVxuXHRcdC5tYXAoZCA9PiBwcm9qZWN0aW9uKGQuZ2VvbWV0cnkuY29vcmRpbmF0ZXMpKVxuXHRcdC5tYXAoKGQsIGluZGV4KSA9PiAoe1xuXHRcdFx0aW5kZXgsXG5cdFx0XHR4MjogZFswXSxcblx0XHRcdHkyOiBkWzFdLFxuXHRcdH0pKVxuXHRcdC52YWx1ZSgpXG5cblx0Y29uc3QgeVN0ZXAgPSA1XG5cblx0Y29uc3QgaGlzdG9ncmFtID0gXyhwb2ludHMpXG5cdFx0Lmdyb3VwQnkoZCA9PiBNYXRoLnJvdW5kKGQueTIgLyB5U3RlcCkgKiB5U3RlcClcblx0XHQubWFwKCh2YWx1ZSwga2V5KSA9PlxuXHRcdFx0Xyh2YWx1ZSlcblx0XHRcdFx0LnNvcnRCeSgneDInKVxuXHRcdFx0XHQubWFwKChkLCBpKSA9PiAoe1xuXHRcdFx0XHRcdGluZGV4OiBkLmluZGV4LFxuXHRcdFx0XHRcdHgxOiAoKHdpZHRoIC8gMikgKyAoaSAqIDQpKSAtICh2YWx1ZS5sZW5ndGggKiA0KS8yLFxuXHRcdFx0XHRcdHkxOiAra2V5LFxuXHRcdFx0XHRcdHgyOiBkLngyLFxuXHRcdFx0XHRcdHkyOiBkLnkyLFxuXHRcdFx0XHR9KSlcblx0XHRcdFx0LnZhbHVlKCkpXG5cdFx0LmZsYXR0ZW4oKVxuXHRcdC52YWx1ZSgpXG5cblx0Ly8gVGhpcyBmdW5jdGlvbiBhZGRzIHRoZSBjaXJjbGVzLlxuXHRjb25zdCBlbnRlciA9ICgpID0+IHtcblxuXHRcdC8vIEpPSU4gbmV3IGRhdGEgd2l0aCBvbGQgZWxlbWVudHMuXG5cdFx0Y29uc3QgY2lyY2xlcyA9IGcuc2VsZWN0QWxsKCdjaXJjbGUnKVxuXHRcdFx0LmRhdGEoaGlzdG9ncmFtLCBkID0+IGQuaW5kZXgpXG5cblx0XHQvLyBFTlRFUiBuZXcgZWxlbWVudHMgcHJlc2VudCBpbiBuZXcgZGF0YS5cblx0XHRjaXJjbGVzLmVudGVyKCkuYXBwZW5kKCdjaXJjbGUnKVxuXHRcdFx0XHQuYXR0cnMoe1xuXHRcdFx0XHRcdGN4OiBkID0+IGQueDEsXG5cdFx0XHRcdFx0Y3k6IGQgPT4gZC55MSxcblx0XHRcdFx0XHRyOiAxLFxuXHRcdFx0XHR9KVxuXHRcdFx0Lm1lcmdlKGNpcmNsZXMpXG5cdFx0XHQudHJhbnNpdGlvbignZW50ZXInKVxuXHRcdFx0XHQuZHVyYXRpb24oMTAwMClcblx0XHRcdFx0LmRlbGF5KChkLCBpKSA9PiBpICogMTApXG5cdFx0XHRcdC5hdHRycyh7XG5cdFx0XHRcdFx0Y3g6IGQgPT4gZC54Mixcblx0XHRcdFx0XHRjeTogZCA9PiBkLnkyLFxuXHRcdFx0XHRcdHI6IDEsXG5cdFx0XHRcdH0pXG5cblx0fVxuXG5cdC8vIFRoaXMgZnVuY3Rpb24gcmVtb3ZlcyB0aGUgY2lyY2xlcy5cblx0Y29uc3QgZXhpdCA9ICgpID0+IHtcblxuXHRcdC8vIEpPSU4gbmV3IGRhdGEgd2l0aCBvbGQgZWxlbWVudHMuXG5cdFx0Y29uc3QgY2lyY2xlcyA9IGcuc2VsZWN0QWxsKCdjaXJjbGUnKVxuXHRcdFx0XHQuZGF0YShoaXN0b2dyYW0sIGQgPT4gZC5pbmRleClcblxuXHRcdC8vIFVQREFURSBvbGQgZWxlbWVudHMgcHJlc2VudCBpbiBuZXcgZGF0YS5cblx0XHRjaXJjbGVzXG5cdFx0XHQudHJhbnNpdGlvbignZXhpdCcpXG5cdFx0XHRcdC5kdXJhdGlvbigyNTApXG5cdFx0XHRcdC5kZWxheSgoZCwgaSkgPT4gaSAqIDIpXG5cdFx0XHRcdC5hdHRycyh7XG5cdFx0XHRcdFx0Y3g6IGQgPT4gZC54MSxcblx0XHRcdFx0XHRjeTogZCA9PiBkLnkxLFxuXHRcdFx0XHRcdHI6IDEsXG5cdFx0XHRcdH0pXG5cblx0fVxuXG5cdC8vIEZpcmUgdGhlIGVudGVyIGZ1bmN0aW9uIG9uIHBhZ2UgbG9hZC5cblx0ZW50ZXIoKVxuXG5cdC8vIExpc3RlbiB0byBidXR0b24gY2xpY2tzLlxuXHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdidXR0b24uZW50ZXInKS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIGVudGVyKVxuXHRkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdidXR0b24uZXhpdCcpLmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgZXhpdClcblxufSlcbiJdfQ==
<!DOCTYPE html>
<title>Lat/lon histogram</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>
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, centroid, and projection.
const bounds = d3.geoBounds(feature)
const centroid = d3.geoCentroid(feature)
const projection = d3.geoAlbers()
// Get the path.
const path = d3.geoPath().projection(projection)
// 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', `translate(${margin.left}, ${margin.top})`)
// 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))
.map((d, index) => ({
index,
x2: d[0],
y2: d[1],
}))
.value()
const yStep = 5
const histogram = _(points)
.groupBy(d => Math.round(d.y2 / yStep) * yStep)
.map((value, key) =>
_(value)
.sortBy('x2')
.map((d, i) => ({
index: d.index,
x1: ((width / 2) + (i * 4)) - (value.length * 4)/2,
y1: +key,
x2: d.x2,
y2: d.y2,
}))
.value())
.flatten()
.value()
// This function adds the circles.
const enter = () => {
// JOIN new data with old elements.
const circles = g.selectAll('circle')
.data(histogram, d => d.index)
// ENTER new elements present in new data.
circles.enter().append('circle')
.attrs({
cx: d => d.x1,
cy: d => d.y1,
r: 1,
})
.merge(circles)
.transition('enter')
.duration(1000)
.delay((d, i) => i * 10)
.attrs({
cx: d => d.x2,
cy: d => d.y2,
r: 1,
})
}
// This function removes the circles.
const exit = () => {
// JOIN new data with old elements.
const circles = g.selectAll('circle')
.data(histogram, d => d.index)
// UPDATE old elements present in new data.
circles
.transition('exit')
.duration(250)
.delay((d, i) => i * 2)
.attrs({
cx: d => d.x1,
cy: d => d.y1,
r: 1,
})
}
// 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)
})
$red = #8f092a
.buttons
margin 0 auto
text-align center
.map
svg
display block
margin 0 auto
circle
fill $red
fill-opacity 0.25
stroke $red
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment