var mapWidth = 960, mapHeight = 500, ortho = true, clipMode = false, speed = -7e-3, start = Date.now(), corr = 0; var projectionGlobe = d3.geo.orthographic() .scale(240) .center([0, 0]) .translate([mapWidth / 2, mapHeight / 2]) .clipAngle(90); var projectionMap = d3.geo.equirectangular() .scale(145) .center([0, 0]) .translate([mapWidth / 2, mapHeight / 2]) var projection = projectionGlobe; var canvas = d3.select("div#map").append("canvas") .attr("overflow", "hidden") .attr("width", mapWidth) .attr("height", mapHeight); var context = canvas.node().getContext("2d"); var path = d3.geo.path() .projection(projection) .context(context); //Loading data queue() .defer(d3.json, "/d/5685937/world-110m.json") .defer(d3.tsv, "/d/5685937/world-110m-country-names.tsv") .await(ready); function ready(error, world, countryData) { var countryById = {}, land = topojson.feature(world, world.objects.land), borders = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }); var globe2map = interpolatedProjection(projectionGlobe, projectionMap), map2globe = interpolatedProjection(projectionMap, projectionGlobe); canvas.on("click", function(d) { //Transforming Globe to Map if (ortho === true) { ortho = false; defaultRotate(); setTimeout(function() { projection = globe2map; path.projection(projection); clipMode = false; animation(projection); } , 1600); } else { reset(); } }); //Globe rotating via timer d3.timer(function() { if (ortho === true) { var λ = speed * (Date.now() - start); projection.rotate([λ + corr, 0]); context.clearRect(0, 0, mapWidth, mapHeight); context.beginPath(); context.fillStyle = "#E6E6E6"; path(land); context.fill(); } }); function reset() { //Transforming Map to Globe projection = map2globe; path.projection(projection); clipMode = true; animation(projection); setTimeout(function() { start = Date.now(); ortho = true; } , 7600); } //Unreelling transformation function animation(interProj) { d3.transition() .duration(7500) .tween("projection", function() { return function(_) { interProj.alpha(_); context.clearRect(0, 0, mapWidth, mapHeight); context.beginPath(); path(land); context.fillStyle = "#E6E6E6"; context.fill(); context.beginPath(); path(borders); context.strokeStyle = "#ffffff"; context.lineWidth = .5 context.stroke(); }; }) } function interpolatedProjection(a, b) { var projection = d3.geo.projection(raw).scale(1), center = projection.center, translate = projection.translate, clip = projection.clipAngle, α; function raw(λ, φ) { var pa = a([λ *= 180 / Math.PI, φ *= 180 / Math.PI]), pb = b([λ, φ]); return [(1 - α) * pa[0] + α * pb[0], (α - 1) * pa[1] - α * pb[1]]; } projection.alpha = function(_) { if (!arguments.length) return α; α = +_; var ca = a.center(), cb = b.center(), ta = a.translate(), tb = b.translate(); center([(1 - α) * ca[0] + α * cb[0], (1 - α) * ca[1] + α * cb[1]]); translate([(1 - α) * ta[0] + α * tb[0], (1 - α) * ta[1] + α * tb[1]]); if (clipMode === true) {clip(180 - α * 90);} return projection; }; delete projection.scale; delete projection.translate; delete projection.center; return projection.alpha(0); } //Rotate to default before animation function defaultRotate() { d3.transition() .duration(1500) .tween("rotate", function() { var r = d3.interpolate(projection.rotate(), [0, 0]); return function(t) { projection.rotate(r(t)); context.clearRect(0, 0, mapWidth, mapHeight); context.beginPath(); path(land); context.fillStyle = "#E6E6E6"; context.fill(); }; }) }; };