An interactive (spinning) globe showing the cities that I have travelled to around the world. Hover over place names to highlight the line to that location and to calculate the distance from London (where I currently live). Drag to stop the globe spinning (this uses Versor dragging).
forked from dwtkns's block: Faux-3d Shaded Globe
xxxxxxxxxx
<meta charset="utf-8">
<style>
.land {
fill: white;
opacity: .5;
stroke: #4f2291;
stroke-opacity: 1;
}
.countries path {
stroke: #4f2291;
stroke-linejoin: round;
stroke-width:.5;
fill: #4f2291;
opacity: .1;
pointer-events:none;
}
.lines path {
fill: none;
stroke: #4f2291;
stroke-opacity: 0.3;
stroke-dasharray: 5,5;
}
circle {
stroke: #4f2291;
}
.graticule {
fill: none;
stroke: #4f2291;
stroke-width:.5;
opacity:.2;
}
.labels {
font: 8px sans-serif;
fill: black;
opacity: 1;
cursor: pointer;
}
.noclicks {
pointer-events:none;
}
.point {
opacity:.6;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<script src="https://d3js.org/topojson.v0.min.js"></script>
<script src="versor.js"></script>
<script>
var width = 960,
height = 500;
var proj = d3.geoOrthographic()
.scale(230)
.translate([width / 2, height / 2])
// change this to 180 for transparent globe
.clipAngle(90);
var path = d3.geoPath().projection(proj).pointRadius(1.5);
var graticule = d3.geoGraticule();
var london = [-0.118667702475932, 51.5019405883275];
var time = Date.now();
var rotate = [39.666666666666664, -30];
var velocity = [.015, -0];
var lineToLondon = function(d) {
return path({"type": "LineString", "coordinates": [london, d.geometry.coordinates]});
}
function stripWhitespace(str) {
return str.replace(" ", "");
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
svg.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged));
queue()
.defer(d3.json, "world-110m.json")
.defer(d3.json, "destinations.json")
.await(ready);
function ready(error, world, places) {
svg.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", proj.scale())
.attr("class", "noclicks")
.attr("fill", "none");
svg.append("path")
.datum(topojson.object(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.append("path")
.datum(graticule)
.attr("class", "graticule noclicks")
.attr("d", path);
svg.append("g").attr("class","points")
.selectAll("text").data(places.features)
.enter().append("path")
.attr("class", "point")
.attr("d", path);
svg.append("g").attr("class","lines")
.selectAll(".lines").data(places.features)
.enter().append("path")
.attr("class", "lines")
.attr("id", d => stripWhitespace(d.properties.name))
.attr("d", d => lineToLondon(d));
svg.append("g").attr("class","labels")
.selectAll("text").data(places.features)
.enter().append("text")
.attr("class", "label")
.text(d => d.properties.name)
.on("mouseover", (d) => {
var distance = Math.round(d3.geoDistance(d.geometry.coordinates, london) * 6371);
d3.select("g.info").select("text.distance").text("Distance from London: ~" + distance + "km");
var name = stripWhitespace(d.properties.name);
d3.select("g.lines").select("#" + name).style("stroke-opacity", 1)
})
.on("mouseout", (d) => {
var name = stripWhitespace(d.properties.name);
d3.select("g.lines").select("#" + name).style("stroke-opacity", 0.3)
d3.select("g.info").select("text.distance").text("Distance from London: Hover Over A Location");
});
svg.append("g").attr("class","countries")
.selectAll("path")
.data(topojson.object(world, world.objects.countries).geometries)
.enter().append("path")
.attr("d", path);
position_labels();
svg.append("g").attr("class", "info")
.append("text")
.attr("class", "distance")
.attr("x", width / 20)
.attr("y", height * 0.9)
.attr("text-anchor", "start")
.style("font-size", "12px")
.text("Distance from London: Hover Over A Location");
refresh();
spin();
}
function position_labels() {
var centerPos = proj.invert([width/2,height/2]);
svg.selectAll(".label")
.attr("text-anchor", (d) => {
var x = proj(d.geometry.coordinates)[0];
return x < width/2-20 ? "end" :
x < width/2+20 ? "middle" :
"start"
})
.attr("transform", (d) => {
var loc = proj(d.geometry.coordinates),
x = loc[0],
y = loc[1];
var offset = x < width/2 ? -5 : 5;
return "translate(" + (x+offset) + "," + (y-2) + ")"
})
.style("display", (d) => {
var d = d3.geoDistance(d.geometry.coordinates, centerPos);
return (d > 1.57) ? 'none' : 'inline';
})
}
function refresh() {
svg.selectAll(".land").attr("d", path);
svg.selectAll(".countries path").attr("d", path);
svg.selectAll(".graticule").attr("d", path);
svg.selectAll(".point").attr("d", path);
svg.selectAll(".lines").attr("d", (d) => { if (d) { return lineToLondon(d); }});
position_labels();
}
var timer;
function spin() {
timer = d3.timer(function() {
var dt = Date.now() -time;
proj.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
refresh();
});
}
function dragstarted() {
timer.stop();
v0 = versor.cartesian(proj.invert(d3.mouse(this)));
r0 = proj.rotate();
q0 = versor(r0);
}
function dragged() {
var v1 = versor.cartesian(proj.rotate(r0).invert(d3.mouse(this))),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
proj.rotate(r1);
refresh();
}
</script>
Modified http://d3js.org/d3.v4.min.js to a secure url
Modified http://d3js.org/queue.v1.min.js to a secure url
Modified http://d3js.org/topojson.v0.min.js to a secure url
https://d3js.org/d3.v4.min.js
https://d3js.org/queue.v1.min.js
https://d3js.org/topojson.v0.min.js