X-GIS

Concept

Orbit the globe, pan the plane.

One set of gestures, interpreted by the active projection. On the globe a drag is a real 3D orbit around the planet; on a flat map the same drag pans a plane. The cursor stays anchored across a zoom, a flick glides with inertia, and a Mapbox-parity API drives the camera from code.

The model

The camera is projection-aware: a drag, a scroll, a pinch mean the same thing to the user, but the camera resolves them differently depending on what is on screen. The 3D globe is a true orbit camera around the planet; the seven flat projections move a 2D plane. The input layer reads the active projection each frame and routes every gesture down the matching path, so the point you grab stays under your finger whether the Earth is a sphere or a Mercator sheet.

Everything below is true of the engine today, with the one not-yet-built part — an animated fly-to — marked as roadmap.

Orbit the globe, pan the plane

Pan splits on the projection family: a flat map slides in projected metres, the globe rolls in surface degrees.

01

Pan the plane

On the cylindrical and pseudo-cylindrical projections, a drag moves the camera centre in Web Mercator metres, rotated by the current bearing so a drag tracks the screen even on a turned map. The centre wraps across the antimeridian and clamps to the Mercator latitude limit (±85.051129°) so it can't run off the world.
02

Orbit the globe

In globe mode the view is a real orbit camera, not a panned plane. A drag rotates the planet under the cursor: the grabbed point is found by intersecting the pointer ray with the sphere, and the centre rotates so it returns under the finger. Because the globe tracks its true centre latitude, the orbit rolls all the way to the pole instead of stopping at the Mercator limit.

Zoom toward the cursor

03

The point under the cursor stays put

Zoom is anchored to the cursor: the geographic point under the pointer is unprojected before the zoom, the zoom is applied, and the centre is shifted so that same point lands back under the cursor. Each projection family uses its own inverse to do it — an exact Mercator-metre shift for Web Mercator, a fixed-point solve for the other flat forms, the disc inverse for the azimuthal discs, and a ray↔sphere solve on the globe — so the spot you point at is the spot you zoom into.
04

Smooth wheel zoom

Wheel and trackpad zoom is eased, not instant: each frame the zoom moves a fraction of the way toward a target level, with no spring overshoot, anchored at the cursor the whole time. Reversing the wheel mid-glide drops the pending overshoot so the direction flips immediately.

Rotate and tilt

05

Bearing and pitch, by drag or pinch

A right-drag turns the map: horizontal motion changes bearing, vertical motion changes pitch (clamped 0–85°). A two-finger gesture rotates, pinch-zooms, and tilts at once, and the bearing snaps to the nearest 15° on release. On the flat azimuthal discs — orthographic, azimuthal-equidistant, stereographic — pitch is locked to 0, because a 2D disc has no meaningful tilt; only the true 3D globe takes a real camera tilt.

Inertia

06

Flick to glide

Release a fast drag and the map keeps moving. The release velocity is the cursor's screen-pixel speed, capped so a hard flick can't launch the view, and it decays each frame until it falls below a threshold and stops. A flick only starts a glide above a small speed, so a deliberate slow drag ends exactly where you let go. The same screen-pixel velocity drives the glide for the flat plane, the discs, and the globe.

Projection-aware unproject

07

Screen pixel → geographic point

Turning a screen pixel back into a lon/lat is also projection-aware. For the flat projections the pixel is inverted through that projection's own inverse — Web Mercator, equirect­angular, Natural Earth, and oblique Mercator each recover the correct point on Earth, not a one-size Mercator guess. On the globe the pixel is unprojected by intersecting the pointer ray with the sphere, returning null off the limb. This is what keeps drag and zoom anchored under the cursor across projections.

Programmatic control

08

A Mapbox-parity camera API

Drive the camera from code with the familiar methods — setCenter, setZoom, jumpTo, panBy, getBounds, fitBounds — matching MapLibre GL JS's signatures. fitBounds fits a lon/lat box by choosing the more constraining of the horizontal and vertical fit zooms. Every input is finite-checked and clamped (zoom to its min/max, pitch to 0–85°), and setMaxBounds / setMinZoom / setMaxZoom constrain interactive gestures too. Drag and panBy are device-pixel-ratio invariant — one CSS pixel of input is the same world distance on any display.

Where it is going

Roadmap · not yet shipped

These are planned, not built. They are on the roadmap, not in the engine today:

  • Animated fly-to — smooth, eased camera flights between views. Today easeTo and flyTo exist for API parity but jump instantly to the destination; there is no transition interpolation yet.
  • Azimuthal-disc unproject — screen→lon/lat for the orthographic / azimuthal-equidistant / stereographic discs is deferred (their limb is singular). Drag and pinch on those discs are already anchored via a dedicated disc inverse; the general-purpose unproject returns null for them.

Tracked in the repo ROADMAP.md ↗.

See it live

The camera is best felt, not read. Drag, scroll, and pinch the globe in the home page hero, or open the playground to orbit, pan, and zoom a real map and switch projections under the same gestures.

Specifications

See also