This map shows, on a Bertin projection, a selection of the largest container ports in the world. Their Urquhart graph is computed by d3.geoVoronoi and displayed in red geodesics.
(Re: the obvious geographical bugs: the dataset is composed only of the coordinates of ports and contains no knowledge of the shape of continents -- the network thus happily crosses through land.)
by Philippe Rivière, visionscarto.net.
xxxxxxxxxx
<meta charset="utf-8">
<style>
.countries {
stroke: white;
stroke-width: 0.3;
opacity: 0.95;
fill: #aaa;
}
.polygons {
fill: rgba(220,220,220,0.3);
}
.links {
stroke: red;
stroke-opacity: 0.8;
stroke-width: 2;
fill: none;
}
</style>
<svg width="900" height="500"></svg>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="https://unpkg.com/d3-delaunay@4"></script>
<script src="https://unpkg.com/d3-geo-voronoi@1"></script>
<script src="https://unpkg.com/d3-geo-projection@2"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
svg = svg
.append('g');
d3.queue()
.defer(d3.csv, 'ports.csv')
.defer(d3.json, 'countries.topo.json')
.await(function (err, points, world) {
var features = topojson.feature(world, world.objects.countries).features,
sites = points
.map(function (e) {
return [+e.lon, +e.lat, e.name];
})
var v = d3.geoVoronoi()(sites),
polygons = v.polygons().features,
links = v.links().features;
var projection = d3.geoBertin1953();
path = d3.geoPath()
.projection(projection)
.pointRadius(1);
var g = svg.append('g')
.append('g')
.attr("class", "s");
var defs = g.append("defs");
defs.append("path")
.datum({
type: "Sphere"
})
.attr("id", "sphere")
.attr("d", path);
g.append("use")
.attr("xlink:href", "#sphere")
.attr("fill", "white");
defs.append("clipPath")
.attr("id", "clip")
.append("use")
.attr("xlink:href", "#sphere");
g.attr("clip-path", "url(#clip)")
// countries
countries = g.append('g')
.attr('class', 'countries')
.selectAll('path')
.data(features)
.enter()
.append('path')
.attr("d", path);
var poly = g.append("g")
.attr("class", "polygons")
.selectAll("path")
.data(polygons)
.enter().append("path")
.attr('opacity', 0.001)
.on('mouseover', function () {
d3.select(this)
.attr('opacity', 1);
})
.on('mouseout', function () {
d3.select(this)
.transition()
.attr('opacity', 0.001);
})
.attr("d", path);
poly
.append('title')
.text(function (d, i) {
return sites[i][2];
});
var link = g.append("g")
.attr("class", "links")
.selectAll("path")
.data(links.filter(function (d) {
return d.properties.urquhart;
}))
.enter().append("path")
.attr("d", path);
g.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere")
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "url(#grad1)")
.attr('pointer-events', 'none');
g.append("g")
.attr("class", "site")
.selectAll('path')
.data([null])
.enter()
.append('path')
.attr('d', function (d) {
return path({
type: "MultiPoint",
coordinates: sites
});
});
});
</script>
https://d3js.org/d3.v4.js
https://d3js.org/topojson.v2.min.js
https://unpkg.com/d3-delaunay@4
https://unpkg.com/d3-geo-voronoi@1
https://unpkg.com/d3-geo-projection@2