海底ケーブルを地球儀風に表示。
データ元 Submarine Cable Map
<!DOCTYPE html>
<html xmlns="//www.w3.org/1999/xhtml">
<head>
<title>Submarine Cable</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css"/>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
background-color: black;
}
#warpper {
width: 100%;
height: 100%;
}
svg {
width: 100%;
height: 100%;
}
.tooltip {
border: 1px solid black;
background-color: white;
padding: 5px 8px 4px 8px;
border-radius: 4px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
}
</style>
</head>
<body>
<div id="warpper"><svg></svg></div>
<script src="//unpkg.com/d3@5.0.0/dist/d3.min.js"></script>
<script src="//npmcdn.com/@turf/turf/turf.min.js"></script>
<script>
const tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.on("click", function(){
d3.select(this)
.style("visibility", "hidden")
})
const warpper = d3.select("#warpper").node(); //firefox hacks
const svg = d3.select("svg");
const stage = svg.append("g");
const seaLayer = stage.append("g").attr("class", "seaLayer");
const landLayer = stage.append("g").attr("class", "landLayer");
const cableLayer = stage.append("g").attr("class", "cableLayer");
const pointLayer = stage.append("g").attr("class", "pointLayer");
const projection = d3.geoOrthographic();
const path = d3.geoPath();
const initialScale = projection.scale();
const sensitivity = 58*2;
const loadGeobuf = (file, callback) => {
const oReq = new XMLHttpRequest();
oReq.open("GET", file, true);
oReq.responseType = "arraybuffer";
oReq.onload = (oEvent) => {
const pd = new Pbf(new Uint8Array(oReq.response));
const geojson = geobuf.decode(pd);
console.log(geojson)
callback(geojson);
};
oReq.send();
};
const p1 = d3.json("cable.geojson");
const p2 = d3.json("countries.geojson");
const p3 = d3.csv("fusion-landing-points-201803141423.csv");
Promise.all([p1, p2, p3]).then(init);
function init(data){
const cable = data[0];
const countries = data[1];
const point = data[2];
const parser = new DOMParser();
const pointGeoJSON = point.map(d => {
d.coordinates = parser.parseFromString(d.coordinates, 'text/xml');
const lnglat = d.coordinates.firstChild.textContent.split(",");
d.lng = +lnglat [0];
d.lat = +lnglat[1];
delete d.coordinates;
return turf.point([d.lng, d.lat], {name:d.name})
})
path.projection(projection);
seaLayer.append("path")
.datum({type: "Sphere"})
.attr("fill", "#eee")
landLayer
.selectAll("path")
.data(countries.features)
.enter()
.append("path")
.attr("fill", "rgb(200,200,203)")
.attr("stroke", "rgb(127, 135, 143)")
cableLayer
.selectAll("path")
.data(cable.features)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.attr("stroke-opacity", 0.6)
.attr("stroke", "rgb(0,65,255)")
.on("mouseout", function(d){
d3.select(this).attr("stroke", "rgb(0,65,255)")
.attr("stroke-opacity", 0.6)
})
.on("mouseover", function(d){
d3.select(this).attr("stroke", "rgb(255,0,0)")
.attr("stroke-opacity", 1)
const dp = d.properties;
const content = `${dp.Name}<br>${dp.length}`;
tooltip
.style("top", (d3.event.pageY-100)+"px")
.style("left",(d3.event.pageX-100)+"px")
.style("visibility", "visible")
.style("background-color", "rgb(180,235,250)")
.html(content);
});
pointLayer.selectAll("path")
.data(pointGeoJSON)
.enter()
.append("path")
.attr("fill", "white")
.attr("stroke", "black")
.on("mouseout", function(d){
d3.select(this).attr("fill", "white")
})
.on("mouseover", function(d){
d3.select(this).attr("fill", "red")
const dp = d.properties;
const content = `${dp.name}`;
tooltip
.style("top", (d3.event.pageY-100)+"px")
.style("left",(d3.event.pageX-100)+"px")
.style("visibility", "visible")
.style("background-color", "rgb(250,250,250)")
.html(content);
});
render();
}
function render(){
const w = warpper.clientWidth || warpper.offsetWidth;
const h = warpper.clientHeight || warpper.offsetHeight;
projection
.scale(d3.min([w, h]) / 2)
.translate([w/2, h/2])
//.preclip(d3.geoClipCircle(90))
path.projection(projection);
seaLayer.select("path").attr("d", path);
landLayer.selectAll("path").attr("d", path);
cableLayer.selectAll("path").attr("d", path);
pointLayer.selectAll("path").attr("d", path);
}
var drag = d3.drag().on("drag", dragged);
var zoom = d3.zoom()
.scaleExtent([1 / 2, 12])
.on("zoom", zoomed)
svg
.call(zoom)
.on("mousedown.zoom", null)
svg.call(drag)
function zoomed() {
stage.attr("transform", d3.event.transform);
}
function dragged(){
const rotate = projection.rotate();
const k = sensitivity / projection.scale();
projection.rotate([
rotate[0] + d3.event.dx * k,
rotate[1] - d3.event.dy * k,
])
render();
}
window.addEventListener("resize", this.resize());
function resize() {
let t;
return event => {
if (t !== false) {
clearTimeout(t);
}
t = setTimeout(() => {
render();
}, 100);
};
}
</script>
</body>
</html>