viewof context = {
  const context = DOM.context2d(width, height);
  context.canvas.style.display = "block";
  context.canvas.style.maxWidth = "100%";
  context.canvas.value = context;
  return context.canvas;
}
function render(projection) {
  const path = d3.geoPath(projection, context);
  context.clearRect(0, 0, width, height);
  context.save();
  context.beginPath(), path(outline), context.clip(), context.fillStyle = "#fff", context.fillRect(0, 0, width, height);
  context.beginPath(), path(graticule), context.strokeStyle = "#ccc", context.stroke();
  context.beginPath(), path(land), context.fillStyle = "#000", context.fill();
  context.restore();
  context.beginPath(), path(outline), context.strokeStyle = "#000", context.stroke();
}
update = {
  const r0 = mutable previousProjection;
  const r1 = projection;
  if (r0 === r1) return;
  mutable previousProjection = r1;
  const interpolate = interpolateProjection(r0, r1);
  for (let j = 1, m = 45; true; ++j) {
    const t = Math.min(1, ease(j / m));
    render(interpolate(t).rotate([performance.now() / 100, 0]));
    yield;
  }
}
mutable previousProjection = d3.geoEquirectangularRaw
function interpolateProjection(raw0, raw1) {
  const {scale: scale0, translate: translate0} = fit(raw0);
  const {scale: scale1, translate: translate1} = fit(raw1);
  return (t) => d3.geoProjection((x, y) => lerp2(raw0(x, y), raw1(x, y), t))
    .scale(lerp1(scale0, scale1, t))
    .translate(lerp2(translate0, translate1, t))
    .precision(0.1);
}
function lerp1(x0, x1, t) {
  return (1 - t) * x0 + t * x1;
}
function lerp2([x0, y0], [x1, y1], t) {
  return [(1 - t) * x0 + t * x1, (1 - t) * y0 + t * y1];
}
function fit(raw) {
  const p = d3.geoProjection(raw).fitExtent([[0.5, 0.5], [width - 0.5, height - 0.5]], outline);
  return {scale: p.scale(), translate: p.translate()};
}
ease = d3.easeCubicInOut
width = 954
height = 600
outline = ({type: "Sphere"})
graticule = d3.geoGraticule10()
land = topojson.feature(world, world.objects.land)
world = FileAttachment("data/land-110m.json").json()
topojson = require("topojson-client@3")
d3 = require("d3-geo@2", "d3-geo-projection@3", "d3-ease@2")
projections = [
  {name: "Aitoff", value: d3.geoAitoffRaw},
  {name: "American polyconic", value: d3.geoPolyconicRaw},
  {name: "August", value: d3.geoAugustRaw},
  {name: "Baker dinomic", value: d3.geoBakerRaw},
  {name: "Boggs’ eumorphic", value: d3.geoBoggsRaw},
  {name: "Bonne", value: d3.geoBonneRaw(Math.PI / 4)},
  {name: "Bottomley", value: d3.geoBottomleyRaw(0.5)},
  {name: "Bromley", value: d3.geoBromleyRaw},
  {name: "Collignon", value: d3.geoCollignonRaw},
  {name: "conic equal-area", value: d3.geoConicEqualAreaRaw(0, Math.PI / 3)},
  {name: "conic equidistant", value: d3.geoConicEquidistantRaw(0, Math.PI / 3)},
  {name: "Craster parabolic", value: d3.geoCrasterRaw},
  {name: "cylindrical equal-area", value: d3.geoCylindricalEqualAreaRaw(38.58 / 180 * Math.PI)},
  {name: "cylindrical stereographic", value: d3.geoCylindricalStereographicRaw(0)},
  {name: "Eckert I", value: d3.geoEckert1Raw},
  {name: "Eckert II", value: d3.geoEckert2Raw},
  {name: "Eckert III", value: d3.geoEckert3Raw},
  {name: "Eckert IV", value: d3.geoEckert4Raw},
  {name: "Eckert V", value: d3.geoEckert5Raw},
  {name: "Eckert VI", value: d3.geoEckert6Raw},
  {name: "Eisenlohr conformal", value: d3.geoEisenlohrRaw},
  {name: "Equal Earth", value: d3.geoEqualEarthRaw},
  {name: "Equirectangular (plate carrée)", value: d3.geoEquirectangularRaw},
  {name: "Fahey pseudocylindrical", value: d3.geoFaheyRaw},
  {name: "flat-polar parabolic", value: d3.geoMtFlatPolarParabolicRaw},
  {name: "flat-polar quartic", value: d3.geoMtFlatPolarQuarticRaw},
  {name: "flat-polar sinusoidal", value: d3.geoMtFlatPolarSinusoidalRaw},
  {name: "Foucaut’s stereographic equivalent", value: d3.geoFoucautRaw},
  {name: "Foucaut’s sinusoidal", value: d3.geoFoucautSinusoidalRaw(0.5)},
  {name: "Ginzburg V", value: d3.geoGinzburg5Raw},
  {name: "Ginzburg VI", value: d3.geoGinzburg6Raw},
  {name: "Ginzburg VIII", value: d3.geoGinzburg8Raw},
  {name: "Ginzburg IX", value: d3.geoGinzburg9Raw},
  {name: "Goode’s homolosine", value: d3.geoHomolosineRaw},
  {name: "Hammer", value: d3.geoHammerRaw(2)},
  {name: "Hill eucyclic", value: d3.geoHillRaw(1)},
  {name: "Hufnagel pseudocylindrical", value: d3.geoHufnagelRaw(1, 0, Math.PI / 4, 2)},
  {name: "Kavrayskiy VII", value: d3.geoKavrayskiy7Raw},
  {name: "Lagrange conformal", value: d3.geoLagrangeRaw(0.5)},
  {name: "Larrivée", value: d3.geoLarriveeRaw},
  {name: "Laskowski tri-optimal", value: d3.geoLaskowskiRaw},
  {name: "Loximuthal", value: d3.geoLoximuthalRaw(40 / 180 * Math.PI)},
  {name: "Miller cylindrical", value: d3.geoMillerRaw},
  {name: "Mollweide", value: d3.geoMollweideRaw},
  {name: "Natural Earth", value: d3.geoNaturalEarth1Raw},
  {name: "Natural Earth II", value: d3.geoNaturalEarth2Raw},
  {name: "Nell–Hammer", value: d3.geoNellHammerRaw},
  {name: "Nicolosi globular", value: d3.geoNicolosiRaw},
  {name: "Patterson cylindrical", value: d3.geoPattersonRaw},
  {name: "rectangular polyconic", value: d3.geoRectangularPolyconicRaw(0)},
  {name: "Robinson", value: d3.geoRobinsonRaw},
  {name: "sinusoidal", value: d3.geoSinusoidalRaw},
  {name: "sinu-Mollweide", value: d3.geoSinuMollweideRaw},
  {name: "Times", value: d3.geoTimesRaw},
  {name: "Tobler hyperelliptical", value: d3.geoHyperellipticalRaw(0, 2.5, 1.183136)},
  {name: "Van der Grinten", value: d3.geoVanDerGrintenRaw},
  {name: "Van der Grinten II", value: d3.geoVanDerGrinten2Raw},
  {name: "Van der Grinten III", value: d3.geoVanDerGrinten3Raw},
  {name: "Van der Grinten IV", value: d3.geoVanDerGrinten4Raw},
  {name: "Wagner IV", value: d3.geoWagner4Raw},
  {name: "Wagner VI", value: d3.geoWagner6Raw},
  {name: "Wagner VII", value: d3.geoWagnerRaw(65 / 180 * Math.PI, 60 / 180 * Math.PI, 0, 200)},
  {name: "Wagner VIII", value: d3.geoWagnerRaw(65 / 180 * Math.PI, 60 / 180 * Math.PI, 20, 200)},
  {name: "Werner", value: d3.geoBonneRaw(Math.PI / 2)},
  {name: "Winkel tripel", value: d3.geoWinkel3Raw}
]
function projectionInput({name = "", value} = {}) {
  const form = html`<form><select name=i>${projections.map((p) => {
    return Object.assign(html`<option>`, {
      textContent: p.name,
      selected: p.name === value
    });
  })}</select> <i style="font-size:smaller;">${name}</i>`;
  form.onchange = () => form.dispatchEvent(new CustomEvent("input")); // Safari
  form.oninput = (event) => {
    if (event && event.isTrusted) form.onchange = null;
    form.value = projections[form.i.selectedIndex].value;
  };
  form.oninput();
  return form;
}
✎ Suggest changes to this page