<!DOCTYPE html> <meta charset="utf-8"> <body> <script src="https://cdn.jsdelivr.net/npm/d3@5"></script> <script src="https://cdn.jsdelivr.net/npm/topojson@3"></script> <script src="https://cdn.jsdelivr.net/npm/versor@0.1"></script> <script src="https://cdn.jsdelivr.net/npm/d3-inertia@0.1"></script> <script> var width = 960, height = 500; var projection = d3.geoOrthographic(); var canvas = d3.select("body").append("canvas") .attr("width", width) .attr("height", height); var context = canvas.node().getContext("2d"); var path = d3.geoPath() .projection(projection) .context(context); // rendering function to be populated when the features are loaded var render = function(){}; // inertia versor dragging var inertia = d3.geoInertiaDrag(canvas, function() { render(); }, projection); d3.json("https://cdn.jsdelivr.net/npm/world-atlas@1/world/110m.json").then(function(world) { var land = topojson.feature(world, world.objects.land); render = function() { context.clearRect(0, 0, width, height); context.beginPath(); path(land); context.fill(); context.strokeStyle = 'black'; context.beginPath(); path({type:"Sphere"}); context.lineWidth = 2.5; context.stroke(); // draw a red line showing current inertia if (typeof inertia == 'object') { context.beginPath(); context.moveTo( inertia.position[0] + inertia.velocity[0] / 10, inertia.position[1] + inertia.velocity[1] / 10 ); context.lineTo( inertia.position[0] + inertia.velocity[0] * inertia.t / 10, inertia.position[1] + inertia.velocity[1] * inertia.t / 10 ); context.strokeStyle = "red"; context.stroke(); } var p = projection.rotate().map(d => Math.floor(10*d)/10); context.fillText(`λ = ${p[0]}, φ = ${p[1]}, γ = ${p[2]}`, 10, 10 ) }; render(); }); d3.select(self.frameElement).style("height", height + "px"); </script>