X-GIS

Concept

RTC + DSFUN precision.

Why a city street still renders sharply when the camera is zoomed to building-detail levels — even though the GPU vertex shader only handles 32-bit floats.

The problem

Web Mercator (EPSG:3857) coordinates measure in meters from the equator and prime meridian. A point in Seoul lands around (14130000, 4520000). Single-precision floats can represent that magnitude — but the precision drops to roughly 1.5 m per step at those values. Move the camera to a Seoul building (zoom 18), ask the GPU to subtract the camera position from each vertex, and you lose every sub-meter detail to f32 cancellation. The result on screen: jittering polygons and mis-aligned strokes.

X-GIS solves this with two coordinated tricks: RTC (relative-to-center coordinates) and DSFUN (double-single function math) — together they recover f64-equivalent precision inside an f32 GPU pipeline.

Coordinate spaces

Each stage of the X-GIS pipeline operates in a defined space. Crossing a stage boundary always means converting; staying inside means staying in the same units.

Code Space Units Stage
LLWGS84 lon/latdegreesGeoJSON input, bbox-reject
MMGlobal Web Mercatormeters (~±2×10⁷)All clipping, simplification, line arc length
DLMDSFUN tile-local Mercatormeters, hi/lo f32 pairOutput vertices, GPU consumption
SPScreen / NDCpixels / clip-spaceCamera projection only

DSFUN: f64-equivalent in f32 hardware

Each tile vertex is stored as two f32 values per axis — high and low — such that h + l reproduces the original f64 within ~1 µm. The vertex shader subtracts the camera position the same way:

// CPU side, once per tile:
let camRel = camera_merc - tile_origin_merc       // f64 subtraction
let cam_h = f32(camRel)
let cam_l = f32(camRel - cam_h)                    // residual

// GPU vertex shader, once per vertex:
let pos_relative_to_camera =
  (pos_h - cam_h) + (pos_l - cam_l)                // f32 cancellation

The (pos_h - cam_h) subtraction cancels the city-magnitude component to a small residual. The (pos_l - cam_l) term carries the sub-meter detail. The sum is small enough to live comfortably in f32 — every downstream calculation stays precise.

Cross-path invariants

Because polygons and lines flow through different code paths — triangulation vs SDF segment generation — X-GIS enforces explicit invariants between them, all checked in tile-cross-path-invariants.test.ts:

  1. Polygon fill and stroke clip in the same space (MM). Pre-2026-04-20 the stroke ran in MM and the fill in LL — endpoints diverged up to 27 km at z=8 boundary tiles.
  2. Fill triangulation boundary == stroke outline endpoints, within 1 m in DLM units.
  3. Batch compileGeoJSONToTiles and on-demand compileSingleTile produce geometrically equivalent tiles.
  4. Sub-tile area conservation: four DSFUN children sum to the parent's triangle-area.
  5. DSFUN reconstruction is exact: h + l recovers the original f64 within 1 µm.

Source

The full coordinate convention with the developer-facing invariants and stage-by-stage table lives in docs/COORDINATES.md in the repo. New contributors should read it before adding any clip / simplify / sub-tile logic.

Was this page helpful?

Tell us what's missing