Skip to content

Instantly share code, notes, and snippets.

Last active April 5, 2022 17:37
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
Projection Transitions v4
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">
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;
<select id="projection-menu"></select>
<script src="//"></script>
<script src="//"></script>
<script src="//"></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 ="body").append("svg")
.attr("width", width)
.attr("height", height);
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
.attr("class", "fill")
.attr("xlink:href", "#sphere");
.attr("class", "graticule")
.attr("d", path);
d3.json("world-110m.json", function(error, world) {
if (error) throw error;
svg.insert("path", ".graticule")
.attr("class", "land")
.attr("d", path);
var menu ="#projection-menu")
.on("change", change);
.text(function(d) { return; });
function loop() {
var j = Math.floor(Math.random() * n);"selectedIndex", i = j + (j >= i));
function change() {
function update(option) {
.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)
.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);
Display the source blob
Display the rendered blob
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment