RFC-012 — Mars Surface Map · technical strategy
Orrery · Closed RFC · v0.1 · May 2026
Status: Closed (v0.4.0 / v0.5.0) Author: product Closed by: ADR-037 (shared surface-site type, deferred component), ADR-038 (per-body 2D projection: Mars equirectangular, Moon orthographic dual-disc), ADR-039 (bidirectional cross-link contract). Shared
<SurfaceMap>component refactor deferred to v0.6.0 (issue #42). Slice gate: v0.4.0 (Mars Surface Map shipped) Why this is an RFC (historical context): PRD-009 specified a Mars peer of/moon— same panel pattern, same site catalogue shape. The technical questions were about abstraction (do /mars and /moon share a generic SurfaceMap, or stay independent?), projection (the Moon's near/far-disc 2D view doesn't translate to Mars cleanly — Mars has no permanent far side), and cross-linking (every site is also a mission card; how do we keep the two views from drifting?).
Context
/moon shipped in v0.1 with:
- 3D sphere with rotation
- 2D toggle: dual-disc near-side / far-side
- Site markers, agency-coloured
- Detail panel: OVERVIEW · GALLERY · LEARN
- Site-to-mission cross-link via shared
id
Mars adds these constraints:
- ~16 confirmed surface artefacts (vs Moon's 16 — comparable count)
- No "near side / far side" — Mars rotates fully every 24h 39 min and is observed from all sides
- Failed landings are part of the Mars story (Schiaparelli, Beagle 2, Mars 2/3/6) — Moon's catalogue is mostly successful
- Latitude clustering is a story (most successes near the equator due to EDL physics) — invisible on a 3D globe but obvious on a 2D map
- Mission cross-link is much stronger than for Moon (every Mars site has a mission card; many Moon sites do too but the linkage was retrofitted)
Open Questions
OQ-1 — Generic SurfaceMap component, or independent /mars + /moon?
| Option | Code reuse | Customisation cost | Risk |
|---|---|---|---|
A. Independent — /mars/+page.svelte is a fork of /moon/+page.svelte, edited for Mars | 0% reuse | Lowest startup cost | Drift over time; bug fixes have to be made twice |
B. Generic component — <SurfaceMap body={...} sites={...} /> consumes both Mars + Moon | ~70% reuse | Initial refactor of /moon (~1 day) | Coupling: a change to one body's UI risks the other |
C. Shared schema only — same surface-site.ts type, separate page components | 0% UI reuse, 100% data reuse | No refactor | Drift in UI; aligned in data |
Recommendation: B. The two pages are doing the same thing. A generic component avoids the fork-and-drift problem. The Moon-specific bits (near-side / far-side dual disc) become props (projection: 'orthographic-pair' | 'equirectangular') on the generic component. /mars passes 'equirectangular'; /moon keeps the dual-disc.
The refactor is a one-time cost. Subsequent feature work (e.g. adding rover traverses in V1.5) ships once for both.
OQ-2 — 2D projection for Mars
The Moon's 2D view (orthographic pair: near-side disc + far-side disc) works because Earth-locked tidal rotation makes "near" and "far" semantically meaningful. For Mars, every longitude is equally observable.
| Option | Pros | Cons |
|---|---|---|
| A. Equirectangular (rectangular projection: lat 0…180°, lon 0…360°) | Simplest; reads like a world map; latitude clustering instantly visible | Polar distortion (Mars's poles aren't rover targets — low cost) |
| B. Mollweide (equal-area elliptical) | Beautiful; fair representation | Most users don't know how to read it; pole detail lost |
| C. Orthographic pair (eastern hemisphere + western hemisphere) | Mirrors /moon's dual-disc visual rhythm | Nothing about Mars's geography respects a hemisphere split |
| D. Polar-projection toggle (south-pole disc; north-pole disc) | Highlights polar landers (Phoenix near 68°N) | Equatorial sites get crowded near the disc edge |
Recommendation: A — Equirectangular. The latitude-clustering story is the educational payload. Mollweide is prettier but the audience reads world-maps as rectangles. Polar-projection (D) can ship as V1.5 if Phoenix / planned ExoMars-Vikram-class polar work warrants it.
ADR-038 to lock equirectangular for Mars and reaffirm orthographic-pair for Moon (the projection decision is per-body, not global).
OQ-3 — Failed-attempt visual encoding
Mars catalogue has confirmed-success, partial-success, and confirmed-failure landers, all with known surface positions. Today /moon doesn't carry this distinction (most Moon entries are successes; the few failures are buried in the description).
| Option | Encoding |
|---|---|
| A. Same dot, dashed outline for failure | Dashed white border instead of solid |
| B. Same dot, opacity reduction for failure | 60% opacity on the agency colour |
| C. Different shape | Cross or X for failures, disc for success |
| D. Two separate legend categories per agency | "USA · success" + "USA · failure" rows in the legend |
Recommendation: A — Dashed outline. Carries information without inventing a second visual vocabulary. Legend explains "dashed = mission ended before / during landing." Applies to /moon too — Beresheet, Vikram-on-Chandrayaan-2, etc., become better-encoded.
OQ-4 — /mars ↔ /missions cross-link UX
Every Mars surface site corresponds to a mission card. From the site panel, the user should be able to "open the full mission card." From the mission card, the user should be able to "see this on the surface."
| Option | UX |
|---|---|
| A. Site panel → "FULL MISSION CARD" button → /missions?id=curiosity | Existing pattern (FLY-button family) |
| B. Site panel → embedded mission summary inline | No navigation; less discoverability of the card itself |
| C. Both directions — site → card AND card → site | Symmetric but adds a button to /missions cards |
Recommendation: C. Add a "ON THE SURFACE" chip to mission cards whose mission has a corresponding /mars site. Click navigates to /mars?site=[id]. The reverse direction is the existing FLY/mission-link pattern. Both are URL-addressable and survive deep-link sharing.
OQ-5 — When does /mars get added to the nav?
The current nav (post v0.4 reorder) is: EXPLORE → MISSIONS → PLAN → FLY → EARTH → MOON
Adding MARS:
- After MOON (preserves "by destination" cluster after the simulator screens) — new tail position
- Before EARTH (preserves rough "size order: Earth → Moon → Mars" but breaks the existing v0.4 order)
- Between MOON and FLY (breaks the simulator cluster)
Recommendation: After MOON. Final order: EXPLORE → MISSIONS → PLAN → FLY → EARTH → MOON → MARS. Story arc: simulators first, then the destination catalogue (Moon prologue → Mars main act).
OQ-6 — Rover traverses in V1 (resolved)
Decision (2026-05-05): C — V1 ships traverses for all four successful rovers as a static snapshot vendored at build. Sojourner (~100 m) and Zhurong (~2 km) tracks too tiny to plot meaningfully — defer to V1.5.
PRD-009 originally deferred this; PM revisited the trade after seeing how strong the visual payoff is for the educational story ("you can see Curiosity wandered, Perseverance went straight, Opportunity beat them both") and confirmed traverses ship in V1.
Vendoring policy: snapshot at each release, no live refresh. NASA publishes daily traverse data freely (CC-0 / NASA-OSA); we capture a mars-traverses/[rover].json polyline per rover, refreshed by hand at release time. Active rovers move ~50 m/sol on average, so a per-release snapshot is "fresh enough" — formal real-time plumbing deferred to a later RFC.
| Rover | Track length (snapshot) | Source |
|---|---|---|
| Curiosity | ~33 km | NASA MSL Analyst's Notebook |
| Perseverance | ~30 km | NASA Mars 2020 traverse data |
| Opportunity | 45.16 km (final) | NASA archive |
| Spirit | 7.73 km (final) | NASA archive |
Implementation: traverses load on-demand only when zoom passes a threshold (no global-view perf hit); each traverse simplified at build time to ≤ 500 waypoints via Douglas-Peucker.
OQ-7 — Orbital layer for /mars (and /moon parity)
/moon has rendering code for type: 'orbiter' site shapes (a hovering torus above the surface — src/routes/moon/+page.svelte:239) but moon-sites.json carries zero orbiter entries today. The path is dead code. Mars without active orbiters renders as visually inert — no MAVEN, no MRO, no Mars Express, no TGO, no Tianwen-1 orbiter, no Hope. That's understating ~30 years of orbital imagery and atmospheric science.
Resolution path:
| Option | What we ship in V1 |
|---|---|
| A. Surface only (PRD's original V1) | Same shape as today's /moon; orbiters stay as cards in /missions |
| B. Surface + dimmed-icon orbiter pin on the surface beneath each orbiter | Hacky; doesn't represent "actually in orbit" |
| C. Surface + orbital ring layer | Each orbiter is a small dot moving on its own faint inclined ring around the body. Click → panel. |
Recommendation (decided 2026-05-05): C. A <SurfaceMap> renders three layers: surface markers · orbital rings + dots · traverses. Each layer is independently toggleable from the new chip rail (matches /explore's PLANETS / DWARFS / COMETS / ISM pattern).
Animation policy (V1). Orbiter dots move at a perception-scaled rate, not the orbit's true period. A real ~2 h orbit at 1× sim time would be a blur. Each dot completes one ring per ~30 s of real time so the user can see motion without it being distracting. Inactive / ended orbiters render dimmed, non-moving. No real-time positions or live ephemeris in V1 — the orbital phase is decorative, not predictive. Live ephemeris deferred to a later RFC if there's appetite.
Moon parity (decided 2026-05-05). Same <SurfaceMap> runs both Mars and Moon. moon-sites.json is backfilled with at least 5 lunar orbiter entries to validate the orbital layer doesn't drift between bodies:
- LRO (NASA · 2009 · ACTIVE)
- Clementine (NASA-DoD · 1994 · ENDED)
- Chandrayaan-1 (ISRO · 2008 · ENDED 2009)
- Chang'e 1 (CNSA · 2007 · ENDED 2009)
- Chang'e 2 (CNSA · 2010 · ENDED 2014)
- Lunar Prospector (NASA · 1998 · ENDED 1999)
- SMART-1 (ESA · 2003 · ENDED 2006)
The Moon backfill is part of the same generic-component refactor (OQ-1).
Decision criteria
We accept a recommendation when:
- The generic SurfaceMap refactor doesn't regress /moon (e2e suite passes).
- The Mars 3D + 2D toggle works at 375 px width with no horizontal scroll.
- Cross-link round-trip works:
/mars?site=curiosity→ click "FULL MISSION CARD" →/missions?id=curiosity→ click "ON THE SURFACE" → back to/mars?site=curiosity. - Failed-attempt encoding reads correctly without colour-only cues (accessibility).
- Schema validation (ADR-019) covers
mars-sites.jsonfrom day one.
Closure path
- ADR-037 — Generic SurfaceMap component + shared
surface-site.tstype withkind: 'surface' | 'orbiter'discriminator. Refactor /moon onto it. - ADR-038 — Per-body 2D projection: Mars equirectangular, Moon orthographic-pair (status quo). Polar-projection toggle deferred.
- ADR-039 — Bidirectional cross-link contract between site panels and mission cards.
- ADR-? — Orbital layer animation policy (perception-scaled, not ephemeris-correct in V1).
Traverses (OQ-6) and orbital layer (OQ-7) both ship in V1 per the 2026-05-05 PM revision. Live ephemeris + live rover positions explicitly deferred to a later RFC.
Orrery · RFC-012 · v0.1 · Open.