|
<!DOCTYPE html> |
|
<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> |