block by fil 6e61f54f1a534a42220ea17adb5e1a7c

Country map + voronoi.find

Full Screen

The trick here is that we dissociate MultiPolygons before using centroids.

Built with blockbuilder.org

index.html

<!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","//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>