Public
Edited
Oct 2, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
map = html`<svg viewBox="0 0 ${width} ${height}" style="display: block;">
<defs>
<path id="outline" d="${path(outline)}" />
<clipPath id="clip"><use xlink:href="${new URL("#outline", location)}" /></clipPath>
</defs>
<g transform-origin="${width/2} ${height/2}" transform="scale(${zoom})">
<g clip-path="url(${new URL("#clip", location)})">
<use xlink:href="${new URL("#outline", location)}" fill="#fff" />
<path d="${path(graticule)}" stroke="#ccc" fill="none"></path>
<path d="${path(land)}" stroke="${landStroke}" stroke-width="${landStrokeWidth}" stroke-linejoin="bevel" fill="${landColor}"></path>
<path d="${tissot ? path(circles) : ""}" stroke="#000000" stroke-width="0.2" stroke-linejoin="bevel" fill="rgba(230,150,150,0.7)"></path>
</g>
<use xlink:href="${new URL("#outline", location)}" fill="none" stroke="#000" />
</g>
</svg>`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
circles = {
const step = 20;
const circle = d3.geoCircle().center(d => d).radius(step / 6).precision(10);
const coordinates = [];
for (let y = -80; y <= 80; y += step) {
for (let x = -180; x < 180; x += step) {
// draw only every second circle near poles
if (Math.abs(y) < 70 || ((x / step) % 2 == 0)) {
coordinates.push(circle([x, y]).coordinates);
}
}
}
return {type: "MultiPolygon", coordinates};
}
Insert cell
projection = {
let proj = selectedProjection;
if (proj.rotate) proj.rotate([-lambda0,-phi0,gamma0]);
if (proj.angle) proj.angle(angle);
// for conic projections
if (proj.parallels) proj.parallels([phi1, phi2]);
if (proj.parallel) proj.parallel(phi1);
try {
if (proj.lobes) proj.lobes(lobes);
}
catch (e) {
// happens for Bogg's interrupted :\
}
return proj;
}
Insert cell
height = {
const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, outline)).bounds(outline);
const dy = Math.ceil(y1 - y0), l = Math.min(Math.ceil(x1 - x0), dy);
projection.scale(projection.scale() * (l - 1) / l).precision(0.2);
return dy;
}
Insert cell
projections = [
{
category: "Azimuthal Projections",
projections: []
},
{name: "Airy’s minimum error", value: d3.geoAiry},
{name: "Azimuthal equidistant", value: d3.geoAzimuthalEquidistant, highlight: true},
{name: "General perspective", value: d3.geoSatellite, highlight: true},
{name: "Gnomonic", value: d3.geoGnomonic},
{name: "Lambert Azimuthal equal-area", value: d3.geoAzimuthalEqualArea, highlight: true},
{name: "Orthographic", value: d3.geoOrthographic, highlight: true},
{name: "Stereographic", value: d3.geoStereographic},
{category: "Conic Projections"},
{name: "Conic equal-area", value: d3.geoConicEqualArea, highlight: true},
{name: "Conic equidistant", value: d3.geoConicEquidistant},
//{name: "conic conformal", value: d3.geoConicConformal}, // Not suitable for world maps.
{category: "Cylindrical Projections"},
{name: "Equirectangular (plate carrée)", value: d3.geoEquirectangular, highlight: true},
{name: "Cylindrical equal-area", value: d3.geoCylindricalEqualArea, highlight: true},
{name: "Cylindrical stereographic", value: d3.geoCylindricalStereographic},
{name: "Collignon", value: d3.geoCollignon},
{name: "Mercator", value: d3.geoMercator, highlight: true},
{name: "Transverse Mercator", value: d3.geoTransverseMercator, highlight: true},
{name: "Miller cylindrical", value: d3.geoMiller},
{name: "Patterson cylindrical", value: d3.geoPatterson},
{category: "Interrupted / Polyhedral Projections"},
{name: "Berghaus’ star", value: d3.geoBerghaus},
{name: "Boggs’ eumorphic (interrupted)", value: d3.geoInterruptedBoggs},
{name: "Butterfly (gnomonic)", value: d3.geoPolyhedralButterfly},
{name: "Butterfly (Collignon)", value: d3.geoPolyhedralCollignon},
{name: "Butterfly (Waterman)", value: d3.geoPolyhedralWaterman},
{name: "Gingery", value: d3.geoGingery},
{name: "Goode’s homolosine (interrupted)", value: d3.geoInterruptedHomolosine},
{name: "HEALPix", value: d3.geoHealpix},
{name: "Mollweide (Goode’s interrupted)", value: d3.geoInterruptedMollweide},
{name: "Mollweide (interrupted hemispheres)", value: d3.geoInterruptedMollweideHemispheres},
{name: "sinusoidal (interrupted)", value: d3.geoInterruptedSinusoidal},
{name: "sinu-Mollweide (interrupted)", value: d3.geoInterruptedSinuMollweide},
{category: "Hybrid / Compromise / Other Projections"},
{name: "Aitoff", value: d3.geoAitoff},
{name: "American polyconic", value: d3.geoPolyconic},
{name: "Armadillo", value: d3.geoArmadillo},
{name: "August", value: d3.geoAugust},
{name: "Baker dinomic", value: d3.geoBaker},
{name: "Bertin’s 1953", value: d3.geoBertin1953},
{name: "Boggs’ eumorphic", value: d3.geoBoggs},
{name: "Bonne", value: d3.geoBonne, highlight: true},
{name: "Bottomley", value: d3.geoBottomley},
{name: "Bromley", value: d3.geoBromley},
{name: "Craig retroazimuthal", value: d3.geoCraig},
{name: "Craster parabolic", value: d3.geoCraster},
{name: "Eckert I", value: d3.geoEckert1},
{name: "Eckert II", value: d3.geoEckert2},
{name: "Eckert III", value: d3.geoEckert3},
{name: "Eckert IV", value: d3.geoEckert4},
{name: "Eckert V", value: d3.geoEckert5},
{name: "Eckert VI", value: d3.geoEckert6},
{name: "Eisenlohr conformal", value: d3.geoEisenlohr},
{name: "Equal Earth", value: d3.geoEqualEarth, highlight: true},
{name: "Fahey pseudocylindrical", value: d3.geoFahey},
{name: "Foucaut’s stereographic equivalent", value: d3.geoFoucaut},
{name: "Foucaut’s sinusoidal", value: d3.geoFoucautSinusoidal},
{name: "Gilbert’s two-world", value: d3.geoGilbert},
{name: "Ginzburg V", value: d3.geoGinzburg5},
{name: "Ginzburg VI", value: d3.geoGinzburg6},
{name: "Ginzburg VIII", value: d3.geoGinzburg8},
{name: "Ginzburg IX", value: d3.geoGinzburg9},
{name: "Gringorten quincuncial", value: d3.geoGringortenQuincuncial},
{name: "Gringorten square", value: d3.geoGringorten},
{name: "Guyou square", value: d3.geoGuyou},
{name: "Goode’s homolosine", value: d3.geoHomolosine},
{name: "Hammer", value: d3.geoHammer},
{name: "Hammer retroazimuthal", value: d3.geoHammerRetroazimuthal},
{name: "Hill eucyclic", value: d3.geoHill},
{name: "Hufnagel pseudocylindrical", value: d3.geoHufnagel},
{name: "Kavrayskiy VII", value: d3.geoKavrayskiy7},
{name: "Lagrange conformal", value: d3.geoLagrange},
{name: "Larrivée", value: d3.geoLarrivee},
{name: "Laskowski tri-optimal", value: d3.geoLaskowski},
// {name: "Littrow retroazimuthal", value: d3.geoLittrow}, // Not suitable for world maps.
{name: "Loximuthal", value: d3.geoLoximuthal},
{name: "Mollweide", value: d3.geoMollweide},
{name: "McBryde–Thomas flat-polar parabolic", value: d3.geoMtFlatPolarParabolic},
{name: "McBryde–Thomas flat-polar quartic", value: d3.geoMtFlatPolarQuartic},
{name: "McBryde–Thomas flat-polar sinusoidal", value: d3.geoMtFlatPolarSinusoidal},
{name: "Natural Earth", value: d3.geoNaturalEarth1},
{name: "Natural Earth II", value: d3.geoNaturalEarth2},
{name: "Nell–Hammer", value: d3.geoNellHammer},
{name: "Nicolosi globular", value: d3.geoNicolosi},
{name: "Peirce quincuncial", value: d3.geoPeirceQuincuncial},
{name: "rectangular polyconic", value: d3.geoRectangularPolyconic},
{name: "Robinson", value: d3.geoRobinson, highlight: true},
{name: "sinusoidal", value: d3.geoSinusoidal},
{name: "sinu-Mollweide", value: d3.geoSinuMollweide},
{name: "Times", value: d3.geoTimes},
{name: "Tobler hyperelliptical", value: d3.geoHyperelliptical},
{name: "Van der Grinten", value: d3.geoVanDerGrinten},
{name: "Van der Grinten II", value: d3.geoVanDerGrinten2},
{name: "Van der Grinten III", value: d3.geoVanDerGrinten3},
{name: "Van der Grinten IV", value: d3.geoVanDerGrinten4},
{name: "Wagner IV", value: d3.geoWagner4},
{name: "Wagner VI", value: d3.geoWagner6},
{name: "Wagner VII", value: d3.geoWagner7},
{name: "Werner", value: () => d3.geoBonne().parallel(90)},
{name: "Wiechel", value: d3.geoWiechel},
{name: "Winkel tripel", value: d3.geoWinkel3}
]
Insert cell
outline = ({type: "Sphere"})
Insert cell
graticule = d3.geoGraticule10()
Insert cell
land = topojson.feature(world, world.objects.land)
Insert cell
world = FileAttachment("land-50m.json").json()
Insert cell
function projectionInput({ name = "", value } = {}) {
const form = html`<form><select name=i>${projections.map((p) => {
if (p.category) {
return Object.assign(html`<option>`, {
textContent: "====== " + p.category + " ======",
style: "font-style: italic; font-weight: bold;"
});
}
return Object.assign(html`<option>`, {
textContent: p.name,
style: p.highlight ? "font-weight: bold;" : "",
selected: p.name === value
});
})}</select> <i style="font-size:smaller;">${name}</i>`;
form.onchange = () => form.dispatchEvent(new CustomEvent("input"));
//form.i.selectedIndex = 2;
form.oninput = () => {
let proj = projections[form.i.selectedIndex];
let nextProj = projections[form.i.selectedIndex + 1];
if (typeof proj.value === "function") {
proj = proj.value();
} else if (typeof nextProj.value === "function") {
proj = nextProj.value();
} else {
proj = d3.geoEquirectangular;
}
form.value = proj;
return proj;
};
form.oninput();
return form;
}
Insert cell
path = d3.geoPath(projection)
Insert cell
topojson = require("topojson-client@3")
Insert cell
d3 = require("d3-geo@2", "d3-geo-projection@3")
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