var canvasAny = d3.select("canvas#any"), ctxAny = canvasAny.node().getContext("2d"), canvasAnyWidth = +canvasAny.attr("width"), canvasAnyHeight = +canvasAny.attr("height"); var projectionAny = d3 .geoMercator() .rotate([-160, 0, -23.4]) .fitSize([canvasAnyWidth, canvasAnyHeight], { type: "Sphere" }); var geoPathGeneratorAny = d3 .geoPath() .projection(projectionAny) .context(ctxAny); var canvasOrtho = d3.select("canvas#ortho"), ctxOrtho = canvasOrtho.node().getContext("2d"), canvasOrthoWidth = +canvasOrtho.attr("width"), canvasOrthoHeight = +canvasOrtho.attr("height"); var projectionOrtho = d3 .geoOrthographic() .rotate([-160, 0, -23.4]) .fitSize([canvasOrthoWidth, canvasOrthoHeight], { type: "Sphere" }); var geoPathGeneratorOrtho = d3 .geoPath() .projection(projectionOrtho) .context(ctxOrtho); var selectElement = document.querySelector("select#projectionChoice"); selectElement.addEventListener("change", function(evt) { changeProjection(evt.target.value); }); var axialTilt = -23.4; var axialTiltElement = document.querySelector("input#axialTilt"); axialTiltElement.addEventListener("change", function(evt) { changeAxialTilt(evt.target.checked); }); var geoCircles = { type: "GeometryCollection", geometries: [] }; for (var longitude = -180; longitude < 180; longitude += 15) { for (var latitude = -90; latitude <= 90; latitude += 15) { geoCircles.geometries.push( d3 .geoCircle() .center([longitude, latitude]) .radius(1)() ); } } drawSphereAndCircles(geoCircles); d3 .json("https://unpkg.com/world-atlas@1.1.4/world/110m.json") .then(function(loadedTopoJson) { var topology = topojson.presimplify(loadedTopoJson); topology = topojson.simplify(topology, 1.1); var worldGeoJson = topojson.feature( topology, topology.objects.land ); d3.timer(function(elapsed) { geoPathGeneratorAny .projection() .rotate([elapsed * 0.005 - 160, 0, axialTilt]); geoPathGeneratorOrtho .projection() .rotate([elapsed * 0.005 - 160, 0, axialTilt]); ctxOrtho.clearRect(0, 0, canvasOrthoWidth, canvasOrthoHeight); ctxAny.clearRect(0, 0, canvasAnyWidth, canvasAnyHeight); drawWorld(worldGeoJson); drawSphereAndCircles(geoCircles); }); var totalOptions = selectElement.options.length; var intervalTimer = d3.interval(function() { if (selectElement.selectedIndex === totalOptions - 1) { selectElement.selectedIndex = 1; } else { selectElement.selectedIndex++; } selectElement.dispatchEvent(new Event("change")); }, 2500); selectElement.addEventListener("focus", function() { intervalTimer.stop(); }); }); function drawWorld(worldGeoJson) { ctxOrtho.beginPath(); ctxOrtho.fillStyle = "#b2df8a"; geoPathGeneratorOrtho(worldGeoJson); ctxOrtho.fill(); ctxOrtho.closePath(); ctxAny.beginPath(); ctxAny.fillStyle = "#b2df8a"; geoPathGeneratorAny(worldGeoJson); ctxAny.fill(); ctxAny.closePath(); } function drawSphereAndCircles(geoCircles) { ctxOrtho.beginPath(); ctxOrtho.fillStyle = "#1f78b4"; geoPathGeneratorOrtho(geoCircles); ctxOrtho.fill(); ctxOrtho.closePath(); ctxOrtho.beginPath(); ctxOrtho.strokeStyle = "#1f78b4"; geoPathGeneratorOrtho({ type: "Sphere" }); ctxOrtho.stroke(); ctxOrtho.closePath(); ctxAny.beginPath(); ctxAny.fillStyle = "#1f78b4"; geoPathGeneratorAny(geoCircles); ctxAny.fill(); ctxAny.closePath(); ctxAny.beginPath(); ctxAny.strokeStyle = "#1f78b4"; geoPathGeneratorAny({ type: "Sphere" }); ctxAny.stroke(); ctxAny.closePath(); } function changeProjection(d3ProjectionName) { var newProjection = d3[d3ProjectionName]().fitSize( [canvasAnyWidth, canvasAnyHeight], { type: "Sphere" } ); geoPathGeneratorAny.projection(newProjection); } function changeAxialTilt(checked) { var newAxialTilt = checked ? -23.4 : 0; d3 .select({}) .transition() .duration(1250) .tween(null, function() { var interpolator = d3.interpolate(axialTilt, newAxialTilt); return function(t) { axialTilt = interpolator(t); }; }); }