block by alexmacy 6700d44240d2b6d3ec9767a5a5854e42

Projection Transitions v4

Full Screen

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.

index.html

<!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>