Stress testing resizing hexagon bins on the fly with d3-hexbin and canvas
. 41k random 311 service request locations.
Currently relies on Path2D()
so that probably excludes a bunch of browsers.
See also: Dynamic Hexbin
xxxxxxxxxx
<meta charset="utf-8">
<style>
canvas {
position: absolute;
left: 0;
top: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script src="hexbin.js"></script>
<script>
var canvas = d3.select("body").append("canvas")
context = d3.select("canvas").node().getContext("2d");
// NY state plane
var projection = d3.geo.conicConformal()
.parallels([40 + 2 / 3, 41 + 1 / 30])
.rotate([74, 40 + 1 / 6]);
var path = d3.geo.path()
.projection(projection);
// ColorBrewer purples
var color = d3.scale.quantile()
.range(["#feebe2","#fbb4b9","#f768a1","#c51b8a","#7a0177"]);
// How long before switching direction, in ms
var interval = 2000;
// Scale for hexagon radius
var radius = d3.scale.linear().domain([0,interval]).range([2,30]);
var clip;
var hexbinner = d3.hexbin();
// Get data
queue()
.defer(d3.json,"nyc.geojson")
.defer(d3.csv,"service-requests-311.csv") // 41k random 311 service request locations
.await(ready);
function ready(err,nyc,points) {
points.forEach(function(p){
p.lng = +p.lng;
p.lat = +p.lat;
});
// Keep canvas responsive
window.onresize = resize;
resize();
// Redraw canvas
function update() {
var bins = hexbinner(points.map(function(p){
return projection([p.lng,p.lat]);
}));
color.domain(bins.map(function(b){
return b.length;
}));
context.fillStyle = "#fff";
context.fill(clip);
context.globalCompositeOperation = "source-atop";
var hex = new Path2D(hexbinner.hexagon());
bins.forEach(function(bin){
context.translate(bin.x,bin.y);
context.fillStyle = color(bin.length);
context.fill(hex);
context.setTransform(1, 0, 0, 1, 0, 0);
});
context.stroke(clip);
}
// Update the hex radius based on time and redraw
// Reverse scale every other interval
function animate(t) {
var progress = t % interval,
cycle = Math.floor(t/interval);
if (cycle % 2) {
progress = interval - progress;
}
hexbinner.radius(radius(progress));
update();
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
// Get new window size, update all dimensions + projection
function resize() {
var width = window.innerWidth,
height = window.innerHeight;
hexbinner.size([width, height]);
canvas.attr("width",width)
.attr("height",height);
context.clearRect(0,0,width,height);
projection.scale(1)
.translate([0,0]);
var b = path.bounds(nyc),
s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
projection
.scale(s)
.translate(t);
clip = new Path2D(path(nyc));
}
}
</script>
https://d3js.org/d3.v3.min.js
https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js