The trick here is that we dissociate MultiPolygons before using centroids.
Built with blockbuilder.org
forked from Fil's block: Country map + voronoi.find
| license: mit |
The trick here is that we dissociate MultiPolygons before using centroids.
Built with blockbuilder.org
forked from Fil's block: Country map + voronoi.find
| <!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; | |
| } | |
| svg {border: 1px solid black} | |
| </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 = 600, | |
| height = 580; | |
| var color = d3.scaleThreshold() | |
| .domain(d3.range(9)) | |
| .range(d3.schemeCategory10); | |
| 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['caribis']).features, | |
| neighbors = topojson.neighbors(world.objects['caribis'].geometries); | |
| var borders = topojson.feature( | |
| world, | |
| world.objects['caribis'] | |
| ); | |
| // borders.features = borders.features.filter(d => d.properties.name != 'Antarctica'); | |
| var projection = d3.geoNaturalEarth2().fitSize([width, height], borders) | |
| var path = d3.geoPath() | |
| .projection(projection); | |
| function countryid(d){ | |
| switch (d.properties.CNTRY_NAME) { | |
| case 'Somaliland': | |
| return 'SM'; | |
| case 'Kosovo': | |
| return 'XK'; | |
| case 'N. Cyprus': | |
| return 'C0'; | |
| } | |
| return d.properties.CNTRY_NAME; | |
| } | |
| 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); | |
| voronoi.selectAll('.polygon') | |
| .data(vor.polygons()) | |
| .enter() | |
| .append('path') | |
| .attr('d', d => d ? "M" + d.join("L") + "Z" : null) | |
| .attr('stroke', '#dbdbdb') | |
| .attr('fill', 'none'); | |
| 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) | |
| }) | |
| svg.append('path') | |
| .datum({type:"Sphere"}) | |
| .attr('d', path) | |
| .attr('class', 'stroke') | |
| }); | |
| </script> |