The trick here is that we dissociate MultiPolygons before using centroids.
Built with blockbuilder.org
license: mit |
The trick here is that we dissociate MultiPolygons before using centroids.
Built with blockbuilder.org
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
background: white; | |
} | |
.stroke { | |
fill: none; | |
stroke: #000; | |
stroke-width: 1.5px; | |
} | |
.fill { | |
fill: #fff; | |
} | |
.graticule { | |
fill: none; | |
stroke: #777; | |
stroke-width: .5px; | |
stroke-opacity: .5; | |
} | |
.land { | |
fill: #222; | |
} | |
.boundary { | |
fill: none; | |
stroke: #fff; | |
stroke-width: .5px; | |
} | |
</style> | |
<body> | |
<script src="https://unpkg.com/d3@4"></script> | |
<script src="https://unpkg.com/d3-geo-projection@2"></script> | |
<script src="https://unpkg.com/topojson-client@3"></script> | |
<script src="https://unpkg.com/d3-scale-chromatic"></script> | |
<script> | |
var width = 960, | |
height = 580; | |
var color = d3.scaleThreshold() | |
.domain(d3.range(9)) | |
.range(d3.schemeCategory10); | |
var projection = d3.geoNaturalEarth2().rotate([-11.5,0]); | |
var path = d3.geoPath() | |
.projection(projection); | |
var svg = d3.select("body").append("svg") | |
.attr("xmlns:xlink","http://www.w3.org/1999/xlink") | |
.attr("width", width) | |
.attr("height", height); | |
var map = svg.append('g'); | |
var voronoi = svg.append('g'); | |
var vor = null; | |
d3.json("110m.json", function(error, world) { | |
if (error) throw error; | |
var countries = topojson.feature(world, world.objects['countries']).features, | |
neighbors = topojson.neighbors(world.objects['countries'].geometries); | |
var borders = topojson.feature( | |
world, | |
world.objects['countries'] | |
); | |
borders.features = borders.features.filter(d => d.properties.name != 'Antarctica'); | |
function countryid(d){ | |
switch (d.properties.name) { | |
case 'Somaliland': | |
return 'SM'; | |
case 'Kosovo': | |
return 'XK'; | |
case 'N. Cyprus': | |
return 'C0'; | |
} | |
return d.properties.iso_a3; | |
} | |
map.selectAll('.border') | |
.data(borders.features) | |
.enter() | |
.append('a') | |
.attr('target', '_parent') | |
.append('path') | |
.attr('d', path) | |
.attr('stroke', 'black') | |
.attr('stroke-width', 0.25) | |
.attr('id', d => countryid(d)) | |
.attr('fill', d => color( Math.random() * 10 )); | |
var centroids = d3.merge( | |
borders.features.map(d => { | |
if (d.geometry.type == 'MultiPolygon') | |
return d.geometry.coordinates.map(e => ({ | |
type: "Point", | |
properties: d.properties, | |
coordinates: d3.geoCentroid({ type:"Polygon", coordinates: e }) | |
})); | |
else return [ { | |
type: "Point", | |
properties: d.properties, | |
coordinates: d3.geoCentroid(d) | |
} ]; | |
}) | |
); | |
centroids.forEach(d => { | |
var p = projection(d.coordinates); | |
d.x = p[0]; | |
d.y = p[1]; | |
}) | |
vor = d3.voronoi().x(d => d.x).y(d => d.y).extent([[0,0],[width, height]])(centroids); | |
if (0) voronoi.selectAll('.polygon') | |
.data(vor.polygons()) | |
.enter() | |
.append('path') | |
.attr('d', d => d ? "M" + d.join("L") + "Z" : null) | |
.attr('stroke', 'gray') | |
.attr('fill', 'none'); | |
if( 0) voronoi.selectAll('.centroid') | |
.data(centroids) | |
.enter() | |
.append('circle') | |
.attr('transform', d => `translate(${[d.x,d.y]})`) | |
.attr('r', 2) | |
.attr('fill', 'lime') | |
svg.on('mousemove click', function() { | |
var m = d3.mouse(this); | |
var site = vor.find(m[0], m[1], 1050); | |
var country = site ? site.data.properties : null; | |
map.selectAll('path') | |
.style('fill', d => d.properties == country ? 'white' : null) | |
}) | |
if(0) svg.append('path') | |
.datum({type:"Sphere"}) | |
.attr('d', path) | |
.attr('class', 'stroke') | |
}); | |
if (0) d3.timer(e => { | |
projection.rotate([e/150]) | |
svg.selectAll('path').attr('d', path) | |
}, 100) | |
</script> |