focused
- focus indicator on the country, ortho
- projection indicator, speed
- rotation speed, start
- start of rotation, corr
- variable to save the rotation phase.endall(transition, callback)
function counts the number of elements to which the transition will be applied (animation), and, when everything is finished, performs the function that it has fed to ( callback
). In principle, it can be replaced with SetTimeout
, but it is better to use endall(transition, callback)
. //Starter for function AFTER All transitions function endall(transition, callback) { var n = 0; transition .each(function() { ++n; }) .each("end", function() { if (!--n) callback.apply(this, arguments); }); }
path
with the ortho
class. //Globe rotating via timer d3.timer(function() { var λ = speed * (Date.now() - start), φ = -5; projection.rotate([λ + corr, φ]); g.selectAll(".ortho").attr("d", path); });
corr
allows us to return to the same angle of rotation of the globe, which was before the change of the projection. The following picture from the wiki article Geographical coordinates will help you understand the longitude of λ
and the latitude φ
: //Transforming Globe to Map if (ortho === true) { corr = projection.rotate()[0]; // <- save last rotation angle g.selectAll(".ortho").classed("ortho", ortho = false); projection = projectionMap; path.projection(projection); g.selectAll("path").transition().duration(3000).attr("d", path); }
ortho
class, change the projection and turn on the built-in transition animation.ortho
class for all path
at the end of the transition and the resetting of the angle of rotation of the globe (the timer has ticked all this time). //Transforming Map to Globe projection = projectionGlobe; path.projection(projection); g.selectAll("path").transition() .duration(3000).attr("d", path) .call(endall, function() { g.selectAll("path").classed("ortho", ortho = true); start = Date.now(); // <- reset start for rotation });
λ
), so the map is cut by different values of longitude (meridians). To get the usual view of the map (the section on the anti-meridian), we need to turn the globe to the zero meridian before the transition. Also, instead of adjusting, you can change the projection parameters of a two-dimensional map, but I chose the first option. In the second example, anti-meridian cutting is implemented, and the globe rotates with a mouse (drag event). The defaultRotate()
function is responsible for defaultRotate()
: //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)); g.selectAll("path").attr("d", path); }; }) };
//Unreelling transformation function animation(interProj) { defaultRotate(); g.transition() .duration(7500) .tween("projection", function() { return function(_) { interProj.alpha(_); g.selectAll("path").attr("d", path); }; }) } 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 (ortho === true) {clip(180 - α * 90);} return projection; }; delete projection.scale; delete projection.translate; delete projection.center; return projection.alpha(0); }
interpolatedProjection(a, b)
, they are normalized (one scale, coordinates in radians), then a combined projection is created, which is the pairwise sum of parameters (center, offset) of the original projections with coefficient α
(interpolation step). And at each step we are given a combined projection depending on α
. With increasing step α
, the weight of the first projection decreases, and the weight of the second increases. Thus, we get a beautiful animation.ready(error, world, countryData)
, since they must directly manipulate the geodata, otherwise the logic of the work is the same. There is nothing to comment on, so here is the code on bl.ocks.org right away : Globe to Map IV .Source: https://habr.com/ru/post/198300/
All Articles