Published
Edited
Dec 3, 2020
16 forks
Importers
72 stars
Insert cell
Insert cell
Insert cell
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;
}
Insert cell
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();
}
Insert cell
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;
}
}
Insert cell
mutable previousProjection = d3.geoEquirectangularRaw
Insert cell
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);
}
Insert cell
function lerp1(x0, x1, t) {
return (1 - t) * x0 + t * x1;
}
Insert cell
function lerp2([x0, y0], [x1, y1], t) {
return [(1 - t) * x0 + t * x1, (1 - t) * y0 + t * y1];
}
Insert cell
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()};
}
Insert cell
ease = d3.easeCubicInOut
Insert cell
width = 954
Insert cell
height = 600
Insert cell
outline = ({type: "Sphere"})
Insert cell
graticule = d3.geoGraticule10()
Insert cell
land = topojson.feature(world, world.objects.land)
Insert cell
world = FileAttachment("land-110m.json").json()
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3-geo@2", "d3-geo-projection@3", "d3-ease@2")
Insert cell
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}
]
Insert cell
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;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more