Projections

A sphere does not fit on a rectangle, and every way of forcing it lies about something: shape, area, or distance. mappo now lets you choose the lie. Four projections ship, any central meridian, and a seam for your own — and every one of them gets the whole toolkit for free: the dot field, contours, vector coastlines, borders, places, overlays, locate() and the graticule are one code path with a projection plugged in.

A prototype · mappo · more demos · other worlds

Two ways to flatten the world

Left, the equirectangular map every version of mappo has drawn: latitude and longitude as straight lines, areas stretched toward the poles (Greenland looks the size of Africa; it is a fourteenth). Right, Equal Earth (Šavrič, Patterson & Jenny, 2019): areas are true everywhere, the meridians curve, and the corners of the frame are simply not part of the world. Same body, same data, same attributes but one.

projection="equirectangular"

The default. The frame is 360° by the latitude band; the graticule is a grid of straight lines.

projection="equal-earth"

Equal-area. The frame is 2.05:1 for the whole sphere and the edge of the world is the curve the clip follows.

<mappo-world projection="equal-earth" lat-min="-90" lat-max="90"
             figure="solid outline" figure-source="vector" borders graticule></mappo-world>

The Pacific in the middle

A map centred on 0° cuts the Pacific in half and puts Auckland and Los Angeles at opposite ends of the page. center-lon moves the central meridian; the seam moves with it, and the coastlines are re-cut at the new seam rather than at the old one — the Chukchi coast and Fiji are whole, the Atlantic is split, and nothing is stroked along the edge.

center-lon="150"

Dots, places and the equator, all through the same central meridian.

<mappo-world center-lon="150" places="Tokyo, Sydney, Auckland, Los Angeles"></mappo-world>

The poles, honestly

On an equirectangular map the whole Artemis landing region — a few hundred kilometres around the lunar south pole — is smeared across the bottom row. Polar stereographic puts the pole at the centre and keeps shapes true (it is conformal; the scale grows toward the rim, 2× at the equator of a hemisphere). On a polar map lat-min/lat-max name the band you see: the far bound is the rim of the disc. Longitude 90°E is to the right in both aspects, so 0° points down on a north map and up on a south one, the way NASA and USGS print them. Sites arrive as ordinary data-lat/data-lon children.

<mappo-moon projection="stereographic-south" lat-max="-80">

The last ten degrees to the lunar south pole: highland almost entirely, so the disc is regolith, and what this map is for is where things are — Shackleton at the centre, the Artemis candidate regions around it, each an ordinary overlay child. On an equirectangular map all of this is one row of dots.

<mappo-world projection="stereographic-north" lat-min="45">

The Arctic as one ocean, the way it is: Greenland, Svalbard and the Siberian coast around it, borders included. Places come from the gazetteer or as { name, lat, lon } records.

<mappo-mars projection="stereographic-north" lat-min="55">

Mars from above the north pole: the lowlands of Vastitas Borealis as one basin, whole across the 180° meridian.

<mappo-moon projection="stereographic-south" lat-max="-80" figure="solid" figure-source="vector" graticule>
  <a class="pin" data-lat="-89.7" data-lon="129.2">Shackleton</a>
</mappo-moon>

// or in JavaScript — the same options, the same answers
const map = new Mappo(el, { body: "moon", projection: "stereographic-south", latMax: -80 });
map.projection.forward(-89.7, 129.2);   // → { x: 0.5..., y: 0.5... }  the pole is the centre
map.locate(45, 0);                        // → null: the far hemisphere has no place on this map

Bring your own

A projection is a small object: forward(lat, lon) to a point in the unit frame (or null), inverse(x, y) back to a coordinate (or null off the world), an aspect, and optionally an outline() for the clip. The dot field, the contours and the highlights come from the inverse alone. A custom projection is antimeridian-cut by default; set seam: false only when it has no cylindrical seam. Below, a sinusoidal projection written in eight lines. A d3-geo projection works unchanged: mappo uses its stream, including rotation, clipping and adaptive resampling.

a { forward, inverse } object — sinusoidal

Equal-area, straight parallels, meridians as sine curves. Nothing in mappo knows its name.

the code

const RAD = Math.PI / 180;
const sinusoidal = {
  id: "sinusoidal", aspect: 2,
  forward: (lat, lon) => ({ x: 0.5 + lon * Math.cos(lat * RAD) / 360, y: (90 - lat) / 180 }),
  inverse: (x, y) => {
    const lat = 90 - y * 180, lon = (x - 0.5) * 360 / Math.cos(lat * RAD);
    return Math.abs(lon) <= 180 ? { lat, lon } : null;      // null: off the world
  },
  outline: () => [ [ ...meridian(-180), ...meridian(180).reverse() ] ]
};
new Mappo(el, { projection: sinusoidal, latRange: [-90, 90] });

// a d3-geo projection is accepted as it is (.invert + .stream)
import { geoMollweide } from "d3-geo-projection";
new Mappo(el, { projection: geoMollweide(), latRange: [-90, 90] });