Cesium Basemap Migration: When Changing Tile URLs Moves Every Marker 400 Meters
Any Cesium deployment that serves users in China will hit this coordinate split, and the cost of getting it wrong is a map where every asset lands in the wrong block. The article's patterns—override the tile scheme, not the data; load-once-and-toggle layers; treat preferences as live state—apply directly to any multi-provider basemap architecture, including Western apps that switch between WGS84-compliant providers but need to support GCJ-02 data for Chinese users.
A real-world migration from Tianditu to AMap in a Cesium application starts with a simple URL change but quickly exposes a deeper problem: every icon and 3D model on the map shifts southeast by hundreds of meters. The cause is the coordinate datum difference—Tianditu uses WGS84, AMap uses GCJ-02, a non-linear offset applied to public coordinates in China—and no constant correction fixes it.
The fix is to leave business data in WGS84 and override the Cesium TilingScheme's two key methods: converting lat/lng to tile numbers and tile numbers back to lat/lng rectangles, inserting a WGS84↔GCJ-02 conversion at that boundary. The forward transform has an analytic expression; the inverse does not, so the author uses four iterations of residual-feedback to get sub-millimeter accuracy. The second problem is that switching basemaps by rebuilding the imagery provider causes a white flash and camera reset; the solution is to load all four layers (two imagery + two label layers) once and just toggle `.show`. The third problem is state: the preference must persist, propagate to live instances (via `WeakMap` and `WeakRef`), and be re-read from storage on every entry point, avoiding the module-level snapshot trap.
The author's decision to correct at the tile scheme rather than the data is the correct architectural call: it confines the coordinate-system difference to one layer, making future provider additions a matter of adding another parameter table entry instead of a data migration.
The iterative inverse is a pragmatic engineering tradeoff; since GCJ-02 has no closed-form inverse, accepting four rounds of feedback gets the error below tile seams, which is far cheaper than trying to derive a perfect transform.
The symptom list—offset points, zoom-time seams, and the 'refresh fixes it' bug—is a reusable diagnostic guide for any map app: coordinate datum mismatches show up as non-constant offsets, and stale module state always persists until a manual reload.
The article's pre-launch checklist is a reminder that the real cost of this migration is testing: most failures only appear under specific zoom levels or specific operation sequences, so a static screenshot check is never enough.
Coordinate transformation should be decoupled from business data.