ADR-0030: Offline vector map pack for the location picker
Context
ADR-0026 shipped a tile-free canvas grid picker because ADR-0015 forbids the
INTERNET permission — an online tile map or geocoder is unavailable by
construction. The canvas works but gives no geographic context: no streets,
no neighborhoods, no coastlines. Users placing or reading a transaction
location deserve a real basemap, still with zero network.
Decision
The location picker renders offline vector tiles from a single-file PMTiles archive, with a three-tier fallback:
- Bundled overview (always). A z≤9 Malaysia extract (~5.3 MB) ships in
APK assets under
assets/maps/and is copied once tofilesDir/maps/on first use —pmtiles://asset://does not work because PMTiles needs byte-range reads thatAssetManagerFileSourcecannot provide. - Imported full pack (opt-in). Settings › Data gains "Offline map pack":
the user picks any
.pmtilesdocument; the header is validated (Pmtiles.isV3Header) while streaming into place atomically. Provenance (display name) lives in the encrypted settings table (KEY_MAP_PACK_NAME). We publish dated whole-Malaysia packs as GitHub release artifacts cut from Protomaps' public daily builds (scripts/cut-map-pack.sh, z0–13 ≈ 114 MB measured). - Canvas fallback (ADR-0026 unchanged) when even the overview is unavailable.
Rendering uses MapLibre Native (via the official maplibre-compose
wrapper, which bundles MapLibre Android ≥ 13 with native pmtiles://file://
support). The pin stays fixed at screen center — pan/zoom moves the target,
exactly the ADR-0026 drag interaction translated to a real map. Style JSON,
glyphs (Noto Sans Regular, OFL), and attribution ship in assets; nothing
loads over the network.
Consequences
- The app keeps no INTERNET permission; map data reaches devices only via the APK (overview) or user-initiated file import (full pack).
- Attribution "© OpenStreetMap contributors" is embedded in the style's source and always rendered by MapLibre's overlay (ODbL compliance).
- Full-pack zoom is capped at z14 client-side; the published pack targets z0–13 (~114 MB measured for the MY bbox; z14 ≈ 226 MB, z15 ≈ 456 MB).
.pmtilescountry packs are never committed to git; only the small bundled overview asset is.maplibre-composeis pre-1.0; upgrades may require code motion. The Vulkan renderer needs a physical device or Vulkan-capable emulator image.- The canvas picker remains reachable (Loading / CanvasFallback states), so the feature degrades gracefully on any device.
Alternatives considered
- Online tiles behind an opt-in permission — rejected: reverses ADR-0015's core guarantee and reopens the Play-review surface.
- Raster MBTiles + osmdroid — rejected: ~340 MB at z12 alone, ~22 GB at z15 for the same bbox, fixed style, blurry between zooms.
- Mapsforge prebuilt maps — viable renderer but weaker Compose story and a second ecosystem to maintain; MapLibre + PMTiles won on format (single-file, cloud-friendly) and active maintenance.
- Bundle the full pack in the APK / Play Asset Delivery — rejected: couples map data to app releases, bloats every install by hundreds of MB, and adds CI complexity for a closed-testing-scale app.