Built with blockbuilder.org
References: https://observablehq.com/@larsvers/making-a-tesselated-hexbin-map https://bl.ocks.org/veltman/38149d05ea247cbcebb1/a7e57c31b82bfaf7c46587635e8c119da5e45008 https://bl.ocks.org/mbostock/9885854/8967c821e056e2f8ed3a1432e7b43fa8293078b0 https://bl.ocks.org/veltman/c582a31d347e04dd75d5331b0074558e/123ef658b4db1eee28c662e7a5ee4d4d3258ba95
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="d3.v5.min.js"></script>
<script src="topojson.v2.min.js"></script>
<script src="d3-hexbin.v0.2.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width: 100%; height: 90%;}
.landshadow {
fill: none;
stroke: #ccc;
stroke-width: 2px;
stroke-linejoin: round;
}
.states {
fill: none;
stroke: #fff;
stroke-linejoin: round;
}
.counties {
fill: #ffffff;
}
svg.circle {
opacity: 0.2;
}
</style>
</head>
<body>
<script>
var svg = d3.select("body").append("svg");
var projection = d3.geoAlbersUsa().translate([400,250]).scale(800);
var path = d3.geoPath().projection(projection);
var url = "//enjalot.github.io/wwsd/data/world/world-110m.geojson";
var data_url = "//enjalot.github.io/wwsd/data/world/ne_50m_populated_places_simple.geojson";
var us_counties_shapes_url = "us.json";
var pvs_counties_data_url = "pvscounty_fips.tsv";
var pvs_points_url = "pvscoords.csv";
var hexbin = d3.hexbin()
.extent([[0, 0], [svg.width, svg.height]])
.radius(10);
hexbin.x(d => {
return projection([+d.lon,+d.lat]) ? projection([+d.lon,+d.lat])[0] : null;
});
hexbin.y(d => {
return projection([+d.lon,+d.lat]) ? projection([+d.lon,+d.lat])[1] : null;
});
function cmyk(d) {
if (d.PCTPOP == 0 && d.PCTSODA == 0 && d.PCTCOKE == 0 && d.PCTOTHER == 0) {
return "white";
} else {
return "rgb("
+ Math.round((d.PCTPOP + d.PCTCOKE) * 100) + "%,"
+ Math.round((d.PCTSODA + d.PCTPOP) * 100) + "%,"
+ Math.round((d.PCTCOKE + d.PCTSODA) * 100) + "%)";
}
}
Promise.all([d3.json(us_counties_shapes_url),d3.tsv(pvs_counties_data_url),d3.csv(pvs_points_url)]).then(function(data) {
var us = data[0];
var pvscounty_fips = data[1];
var points = data[2];
var pvscounty_lookup = {};
pvscounty_fips.forEach(function(d) {
pvscounty_lookup[d.id] = {
PCTPOP: +d.PCTPOP,
PCTSODA: +d.PCTSODA,
PCTCOKE: +d.PCTCOKE,
PCTOTHER: +d.PCTOTHER,
SUMPOP: +d.SUMPOP,
SUMSODA: +d.SUMSODA,
SUMCOKE: +d.SUMCOKE,
SUMOTHER: +d.SUMOTHER,
}
});
svg.append("path").datum(topojson.feature(us, us.objects.land))
.attr("class", "landshadow")
.attr("d", path);
var counties = svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("d", path);
/*
counties
.style("fill", function(d) {
//var threshold = 0.6;
var result = pvscounty_lookup[d.id];
if (result) {
//if (result.PCTPOP > threshold || result.PCTSODA > threshold || result.PCTCOKE > threshold)
return cmyk(result);
} else {
console.log("county id not found:", d.id);
//return 'red';
}
});
*/
points.forEach(d => {
d.SUMPOP = +d.pop;
d.SUMSODA = +d.soda;
d.SUMCOKE = +d.coke;
d.SUMOTHER = +d.other;
d.PCTPOP = d.SUMPOP/+d.count;
d.PCTSODA = d.SUMSODA/+d.count;
d.PCTCOKE = d.SUMCOKE/+d.count;
d.PCTOTHER = d.SUMOTHER/+d.count;
d.lat = +d.lat;
d.lon = +d.lon;
d.count = +d.count;
});
/*
svg.append("g").selectAll("circle")
.data(points
.filter(d => projection([+d.lon,+d.lat])) // filter anything where proj fails
.filter(d => d.zip != 92122 && d.zip != 77069 && d.zip != 82401) // filter some bad data
)
.enter()
.append("circle")
.attr("r", function(d) {
return d.count / 10;
})
.attr("fill", function(d) {
return cmyk(d);
})
.attr("cx", function(d) {
return projection([+d.lon,+d.lat])[0];
})
.attr("cy", function(d) {
return projection([+d.lon,+d.lat])[1];
})
//.on("mouseover", d => {
// console.log(d.zip);
//});
*/
svg.append("g")
.attr("class", "hexbins")
.attr("clip-path", "url(#us-clip)") // clip to the shape of the us
.selectAll("path")
.data(hexbin(points))
.enter().append("path")
.attr("class", "hexagon")
.attr("d", function(d) { return hexbin.hexagon(); })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("fill", function(d) {
var arrayvalues = Array.from(d);
d.PCTPOP = d3.sum(arrayvalues, i => i.SUMPOP)/d3.sum(arrayvalues, i => i.count);
d.PCTSODA = d3.sum(arrayvalues, i => i.SUMSODA)/d3.sum(arrayvalues, i => i.count);
d.PCTCOKE = d3.sum(arrayvalues, i => i.SUMCOKE)/d3.sum(arrayvalues, i => i.count);
d.PCTOTHER = d3.sum(arrayvalues, i => i.SUMOTHER)/d3.sum(arrayvalues, i => i.count);
return cmyk(d);
});
//var topology = topojson.topology();
// Add the us outline again just for clipping
svg.append("clipPath").datum(topojson.feature(us, us.objects.land))
.attr("id", "us-clip")
.append("path")
.attr("d", path);
// Add the shapes of states on top, to have outlines.
svg.append("path").datum(topojson.mesh(us, us.objects.states, function(a, b) {
return a.id !== b.id;
})).attr("class", "states").attr("d", path);
});
</script>
</body>
// https://github.com/d3/d3-hexbin Version 0.2.2. Copyright 2017 Mike Bostock.
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(n.d3=n.d3||{})}(this,function(n){"use strict";function t(n){return n[0]}function r(n){return n[1]}var e=Math.PI/3,u=[0,e,2*e,3*e,4*e,5*e],o=function(){function n(n){var t,r={},e=[],u=n.length;for(t=0;t<u;++t)if(!isNaN(i=+d.call(null,o=n[t],t,n))&&!isNaN(c=+p.call(null,o,t,n))){var o,i,c,s=Math.round(c/=f),h=Math.round(i=i/a-(1&s)/2),l=c-s;if(3*Math.abs(l)>1){var v=i-h,M=h+(i<h?-1:1)/2,x=s+(c<s?-1:1),g=i-M,m=c-x;v*v+l*l>g*g+m*m&&(h=M+(1&s?1:-1)/2,s=x)}var y=h+"-"+s,j=r[y];j?j.push(o):(e.push(j=r[y]=[o]),j.x=(h+(1&s)/2)*a,j.y=s*f)}return e}function o(n){var t=0,r=0;return u.map(function(e){var u=Math.sin(e)*n,o=-Math.cos(e)*n,i=u-t,a=o-r;return t=u,r=o,[i,a]})}var i,a,f,c=0,s=0,h=1,l=1,d=t,p=r;return n.hexagon=function(n){return"m"+o(null==n?i:+n).join("l")+"z"},n.centers=function(){for(var n=[],t=Math.round(s/f),r=Math.round(c/a),e=t*f;e<l+i;e+=f,++t)for(var u=r*a+(1&t)*a/2;u<h+a/2;u+=a)n.push([u,e]);return n},n.mesh=function(){var t=o(i).slice(0,4).join("l");return n.centers().map(function(n){return"M"+n+"m"+t}).join("")},n.x=function(t){return arguments.length?(d=t,n):d},n.y=function(t){return arguments.length?(p=t,n):p},n.radius=function(t){return arguments.length?(i=+t,a=2*i*Math.sin(e),f=1.5*i,n):i},n.size=function(t){return arguments.length?(c=s=0,h=+t[0],l=+t[1],n):[h-c,l-s]},n.extent=function(t){return arguments.length?(c=+t[0][0],s=+t[0][1],h=+t[1][0],l=+t[1][1],n):[[c,s],[h,l]]},n.radius(1)};n.hexbin=o,Object.defineProperty(n,"__esModule",{value:!0})});
topojson=function(){function t(t,e){function n(e){var n=t.arcs[e],r=n[0],o=[0,0];return n.forEach(function(t){o[0]+=t[0],o[1]+=t[1]}),[r,o]}var r={},o={},a={};e.forEach(function(t){var e=n(t);(r[e[0]]||(r[e[0]]=[])).push(t),(r[e[1]]||(r[e[1]]=[])).push(~t)}),e.forEach(function(t){var e,r,i=n(t),c=i[0],s=i[1];if(e=a[c])if(delete a[e.end],e.push(t),e.end=s,r=o[s]){delete o[r.start];var u=r===e?e:e.concat(r);o[u.start=e.start]=a[u.end=r.end]=u}else if(r=a[s]){delete o[r.start],delete a[r.end];var u=e.concat(r.map(function(t){return~t}).reverse());o[u.start=e.start]=a[u.end=r.start]=u}else o[e.start]=a[e.end]=e;else if(e=o[s])if(delete o[e.start],e.unshift(t),e.start=c,r=a[c]){delete a[r.end];var f=r===e?e:r.concat(e);o[f.start=r.start]=a[f.end=e.end]=f}else if(r=o[c]){delete o[r.start],delete a[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);o[f.start=r.end]=a[f.end=e.end]=f}else o[e.start]=a[e.end]=e;else if(e=o[c])if(delete o[e.start],e.unshift(~t),e.start=s,r=a[s]){delete a[r.end];var f=r===e?e:r.concat(e);o[f.start=r.start]=a[f.end=e.end]=f}else if(r=o[s]){delete o[r.start],delete a[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);o[f.start=r.end]=a[f.end=e.end]=f}else o[e.start]=a[e.end]=e;else if(e=a[s])if(delete a[e.end],e.push(~t),e.end=c,r=a[c]){delete o[r.start];var u=r===e?e:e.concat(r);o[u.start=e.start]=a[u.end=r.end]=u}else if(r=o[c]){delete o[r.start],delete a[r.end];var u=e.concat(r.map(function(t){return~t}).reverse());o[u.start=e.start]=a[u.end=r.start]=u}else o[e.start]=a[e.end]=e;else e=[t],o[e.start=c]=a[e.end=s]=e});var i=[];for(var c in a)i.push(a[c]);return i}function e(e,r,o){function a(t){0>t&&(t=~t),(l[t]||(l[t]=[])).push(f)}function i(t){t.forEach(a)}function c(t){t.forEach(i)}function s(t){"GeometryCollection"===t.type?t.geometries.forEach(s):t.type in d&&(f=t,d[t.type](t.arcs))}var u=[];if(arguments.length>1){var f,l=[],d={LineString:i,MultiLineString:c,Polygon:c,MultiPolygon:function(t){t.forEach(c)}};s(r),l.forEach(3>arguments.length?function(t,e){u.push([e])}:function(t,e){o(t[0],t[t.length-1])&&u.push([e])})}else for(var p=0,h=e.arcs.length;h>p;++p)u.push([p]);return n(e,{type:"MultiLineString",arcs:t(e,u)})}function n(t,e){function n(t,e){e.length&&e.pop();for(var n,o=h[0>t?~t:t],a=0,i=o.length,c=0,s=0;i>a;++a)e.push([(c+=(n=o[a])[0])*f+d,(s+=n[1])*l+p]);0>t&&r(e,i)}function o(t){return[t[0]*f+d,t[1]*l+p]}function a(t){for(var e=[],r=0,o=t.length;o>r;++r)n(t[r],e);return 2>e.length&&e.push(e[0]),e}function i(t){for(var e=a(t);4>e.length;)e.push(e[0]);return e}function c(t){return t.map(i)}function s(t){var e=t.type,n="GeometryCollection"===e?{type:e,geometries:t.geometries.map(s)}:e in v?{type:e,coordinates:v[e](t)}:{type:null};return"id"in t&&(n.id=t.id),"properties"in t&&(n.properties=t.properties),n}var u=t.transform,f=u.scale[0],l=u.scale[1],d=u.translate[0],p=u.translate[1],h=t.arcs,v={Point:function(t){return o(t.coordinates)},MultiPoint:function(t){return t.coordinates.map(o)},LineString:function(t){return a(t.arcs)},MultiLineString:function(t){return t.arcs.map(a)},Polygon:function(t){return c(t.arcs)},MultiPolygon:function(t){return t.arcs.map(c)}};return s(e)}function r(t,e){for(var n,r=t.length,o=r-e;--r>o;)n=t[o],t[o++]=t[r],t[r]=n}function o(t,e){for(var n=0,r=t.length;r>n;){var o=n+r>>>1;e>t[o]?n=o+1:r=o}return n}function a(t){function e(t,e){t.forEach(function(t){0>t&&(t=~t);var n=a[t]||(a[t]=[]);n[e]||(n.forEach(function(t){var n,r;r=o(n=i[e],t),n[r]!==t&&n.splice(r,0,t),r=o(n=i[t],e),n[r]!==e&&n.splice(r,0,e)}),n[e]=e)})}function n(t,n){t.forEach(function(t){e(t,n)})}function r(t,e){"GeometryCollection"===t.type?t.geometries.forEach(function(t){r(t,e)}):t.type in c&&c[t.type](t.arcs,e)}var a=[],i=t.map(function(){return[]}),c={LineString:e,MultiLineString:n,Polygon:n,MultiPolygon:function(t,e){t.forEach(function(t){n(t,e)})}};return t.forEach(r),i}return{version:"0.0.32",mesh:e,object:n,neighbors:a}}();