A v4 update to mbostock's block: Projection Transitions
I also tweaked the transition so that it's constantly moving...
These projections are available in the geo.projection plugin.
| license: gpl-3.0 |
A v4 update to mbostock's block: Projection Transitions
I also tweaked the transition so that it's constantly moving...
These projections are available in the geo.projection plugin.
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| body { | |
| background: #fcfcfa; | |
| height: 500px; | |
| position: relative; | |
| width: 960px; | |
| } | |
| #projection-menu { | |
| position: absolute; | |
| right: 10px; | |
| top: 10px; | |
| } | |
| .stroke { | |
| fill: none; | |
| stroke: #000; | |
| stroke-width: 3px; | |
| } | |
| .fill { | |
| fill: #fff; | |
| } | |
| .graticule { | |
| fill: none; | |
| stroke: #777; | |
| stroke-width: .5px; | |
| stroke-opacity: .5; | |
| } | |
| .land { | |
| fill: #222; | |
| } | |
| .boundary { | |
| fill: none; | |
| stroke: #fff; | |
| stroke-width: .5px; | |
| } | |
| </style> | |
| <select id="projection-menu"></select> | |
| <script src="//d3js.org/d3.v4.min.js"></script> | |
| <script src="//d3js.org/d3-geo-projection.v1.min.js"></script> | |
| <script src="//d3js.org/topojson.v2.min.js"></script> | |
| <script> | |
| var width = 960, | |
| height = 500; | |
| var options = [ | |
| {name: "Aitoff", projection: d3.geoAitoff()}, | |
| {name: "Albers", projection: d3.geoAlbers().scale(145).parallels([20, 50])}, | |
| {name: "August", projection: d3.geoAugust().scale(60)}, | |
| {name: "Baker", projection: d3.geoBaker().scale(100)}, | |
| {name: "Boggs", projection: d3.geoBoggs()}, | |
| {name: "Bonne", projection: d3.geoBonne().scale(120)}, | |
| {name: "Bromley", projection: d3.geoBromley()}, | |
| {name: "Collignon", projection: d3.geoCollignon().scale(93)}, | |
| {name: "Craster Parabolic", projection: d3.geoCraster()}, | |
| {name: "Eckert I", projection: d3.geoEckert1().scale(165)}, | |
| {name: "Eckert II", projection: d3.geoEckert2().scale(165)}, | |
| {name: "Eckert III", projection: d3.geoEckert3().scale(180)}, | |
| {name: "Eckert IV", projection: d3.geoEckert4().scale(180)}, | |
| {name: "Eckert V", projection: d3.geoEckert5().scale(170)}, | |
| {name: "Eckert VI", projection: d3.geoEckert6().scale(170)}, | |
| {name: "Eisenlohr", projection: d3.geoEisenlohr().scale(60)}, | |
| {name: "Equirectangular (Plate Carrée)", projection: d3.geoEquirectangular()}, | |
| {name: "Hammer", projection: d3.geoHammer().scale(165)}, | |
| {name: "Hill", projection: d3.geoHill()}, | |
| {name: "Goode Homolosine", projection: d3.geoHomolosine()}, | |
| {name: "Kavrayskiy VII", projection: d3.geoKavrayskiy7()}, | |
| {name: "Lambert cylindrical equal-area", projection: d3.geoCylindricalEqualArea()}, | |
| {name: "Lagrange", projection: d3.geoLagrange().scale(120)}, | |
| {name: "Larrivée", projection: d3.geoLarrivee().scale(95)}, | |
| {name: "Laskowski", projection: d3.geoLaskowski().scale(120)}, | |
| {name: "Loximuthal", projection: d3.geoLoximuthal()}, | |
| // {name: "Mercator", projection: d3.geoMercator().scale(490 / 2 / Math.PI)}, | |
| {name: "Miller", projection: d3.geoMiller().scale(100)}, | |
| {name: "McBryde–Thomas Flat-Polar Parabolic", projection: d3.geoMtFlatPolarParabolic()}, | |
| {name: "McBryde–Thomas Flat-Polar Quartic", projection: d3.geoMtFlatPolarQuartic()}, | |
| {name: "McBryde–Thomas Flat-Polar Sinusoidal", projection: d3.geoMtFlatPolarSinusoidal()}, | |
| {name: "Mollweide", projection: d3.geoMollweide().scale(165)}, | |
| {name: "Natural Earth", projection: d3.geoNaturalEarth()}, | |
| {name: "Nell–Hammer", projection: d3.geoNellHammer()}, | |
| {name: "Polyconic", projection: d3.geoPolyconic().scale(100)}, | |
| {name: "Robinson", projection: d3.geoRobinson()}, | |
| {name: "Sinusoidal", projection: d3.geoSinusoidal()}, | |
| {name: "Sinu-Mollweide", projection: d3.geoSinuMollweide()}, | |
| {name: "van der Grinten", projection: d3.geoVanDerGrinten().scale(75)}, | |
| {name: "van der Grinten IV", projection: d3.geoVanDerGrinten4().scale(120)}, | |
| {name: "Wagner IV", projection: d3.geoWagner4()}, | |
| {name: "Wagner VI", projection: d3.geoWagner6()}, | |
| {name: "Wagner VII", projection: d3.geoWagner7()}, | |
| {name: "Winkel Tripel", projection: d3.geoWinkel3()} | |
| ]; | |
| options.forEach(function(o) { | |
| o.projection.rotate([0, 0]).center([0, 0]); | |
| }); | |
| var //interval = setInterval(loop, 750), | |
| i = 0, | |
| n = options.length - 1; | |
| var projection = options[i].projection; | |
| var path = d3.geoPath(projection); | |
| var graticule = d3.geoGraticule(); | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width) | |
| .attr("height", height); | |
| svg.append("defs").append("path") | |
| .datum({type: "Sphere"}) | |
| .attr("id", "sphere") | |
| .attr("d", path); | |
| svg.append("use") | |
| .attr("class", "stroke") | |
| .attr("xlink:href", "#sphere"); | |
| svg.append("use") | |
| .attr("class", "fill") | |
| .attr("xlink:href", "#sphere"); | |
| svg.append("path") | |
| .datum(graticule) | |
| .attr("class", "graticule") | |
| .attr("d", path); | |
| d3.json("world-110m.json", function(error, world) { | |
| if (error) throw error; | |
| svg.insert("path", ".graticule") | |
| .datum(topojson.feature(world, world.objects.land)) | |
| .attr("class", "land") | |
| .attr("d", path); | |
| }); | |
| var menu = d3.select("#projection-menu") | |
| .on("change", change); | |
| menu.selectAll("option") | |
| .data(options) | |
| .enter().append("option") | |
| .text(function(d) { return d.name; }); | |
| update(options[0]) | |
| function loop() { | |
| var j = Math.floor(Math.random() * n); | |
| menu.property("selectedIndex", i = j + (j >= i)); | |
| update(options[i]); | |
| } | |
| function change() { | |
| clearInterval(interval); | |
| update(options[this.selectedIndex]); | |
| } | |
| function update(option) { | |
| svg.selectAll("path").interrupt().transition() | |
| .duration(1000).ease(d3.easeLinear) | |
| .attrTween("d", projectionTween(projection, projection = option.projection)) | |
| d3.timeout(loop, 1000) | |
| } | |
| function projectionTween(projection0, projection1) { | |
| return function(d) { | |
| var t = 0; | |
| var projection = d3.geoProjection(project) | |
| .scale(1) | |
| .translate([width / 2, height / 2]); | |
| var path = d3.geoPath(projection); | |
| function project(λ, φ) { | |
| λ *= 180 / Math.PI, φ *= 180 / Math.PI; | |
| var p0 = projection0([λ, φ]), p1 = projection1([λ, φ]); | |
| return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]]; | |
| } | |
| return function(_) { | |
| t = _; | |
| return path(d); | |
| }; | |
| }; | |
| } | |
| </script> |