Documentation menu
MapMap Nav
Early access. The Android app is available now as a direct
APK download (sideload, roughly 200 MB),
with full offline navigation once a territory is installed. An iOS build
is in development. MapMap Nav is a complete, twelve-screen turn-by-turn
navigation app for iOS and Android, built from one Expo (React Native)
codebase using only published MapMap SDKs. It's a reference, not a demo
shell: on-device routing, guidance and voice all run against the real
MapMapKit (iOS) and ai.mapmap:core (Android) SDKs. Download the
boilerplate, run it, and fork it as the starting point for your own
branded nav app.
Download the boilerplate (.zip): source-only, secrets stripped, runs on the JS mock with live MapMap online tiles out of the box. See how to run it below.
Early access: the Android app
The MapMap navigation app for Android is available now as an early-access build, installed directly rather than from the Play Store.
Download for Android (.apk) · Early access · Android · sideload APK · roughly 200 MB. An iOS build is in development.
Install (sideload)
- Download the APK on your phone using the link above.
- Open the downloaded file. Android will ask you to allow installs from your browser or file manager ("install unknown apps"); allow it for this install.
- Confirm the install, then open MapMap Nav.
Free accounts and the 8 GB allowance
- On first run the app issues you a personal API key with just an email address. Online maps, search and routing work immediately.
- Verifying the email unlocks offline territory downloads, with 8 GB per month included free.
- Territories are per-country offline map packages, signed and verified on the device before use (see Territory packages). Installing the UK territory downloads 3,077 MiB (about 3.0 GiB), which is 38% of a month's free allowance, so it fits comfortably with room for a refresh. Note that the download is what counts against the allowance; the package occupies roughly 5.8 GB once unpacked on the device.
- Only the large layer files are byte-metered against the allowance;
checking for updates is not. If a month's allowance is spent, the
gateway answers
402 E_TERRITORY_ALLOWANCEbefore streaming anything. - Offline search, offline routing and offline guidance need an installed territory; until one is installed the app runs in online mode.
What offline means here
With a territory installed, the app does full offline navigation: offline search, offline turn-by-turn routing and guidance with voice instructions, all on the device, with no signal at all.
Speed camera alerts
- 3,425 fixed safety cameras across the UK, shown along the route with the enforced limit where known.
- Audible alerts stay calm while you are within the limit and become more urgent when you are over it.
- Jurisdiction-aware: camera alerts run on a GB and Ireland allowlist and stay off elsewhere. The app flow is UK-first today; Ireland coverage is rolling out.
- Camera locations are derived from OpenStreetMap data, published under the ODbL. Attribution: © OpenStreetMap contributors.
Also in this build
- Overspeed warning: the speed readout turns red and a soft chime plays when you are over the posted limit.
- Faster-route suggestions while navigating: when a faster route is found the app offers it; switch with a tap or dismiss it and keep your chosen road.
- Full manoeuvre iconography from the canonical MapMap direction set: 88 glyphs including ferry, with per-angle roundabout exits derived from the route geometry.
- Zoom that keeps following: zoom in and out during guidance without losing puck-following; a pinch keeps following too, and re-centre resumes guidance at the zoom you chose.
What it is
- Twelve screens to a full nav-app spec: splash, onboarding, explore, search, place preview, route preview, guidance, arrived, settings, territories, about, and error states.
- A typed JS↔native bridge (
mapmap-react-native): no business logic in JavaScript beyond UI state; routing, guidance and territory management all run in the native SDK. - On-device routing and turn-by-turn guidance, including a truck/ADR profile (MapMap's differentiator), a replay-drive demo path for running guidance without a real drive, and EAS build profiles + store/privacy drafts.
- Built and verified against the public SDKs only, in the position of an outside RN shop building on MapMap with nothing beyond published artefacts and docs; see the verdict below.
Features shipped
- Search → preview → turn-by-turn. When the driver leaves the route the puck follows live GPS off the line and the app auto-reroutes from the current position.
- Multiple route alternatives with a per-route time/distance overview; tap any alternative line to pick it before starting.
- Posted speed-limit signs and an over-limit warning: the real road sign (UK mph, e.g. 30) for the current road, drawn only for standard signs in use, and the speed pill turns red when the driver exceeds the limit.
- Postcode and address search (UK plus a dozen other countries) and reverse-geocode tap-to-place: tapping a point resolves to a real street/address, not raw coordinates.
- A current-road name banner and on-road manoeuvre arrows at major turns, alongside a dimming "vanishing" route line for the travelled distance.
- A 3D chase camera with a pinned, smoothed location puck: the puck and camera glide between fixes instead of hopping on each guidance tick.
- Real GPS by default, with an explicit Simulate demo mode that replays a scripted drive for desk demos and testing; no real movement required.
- Tilt and 2D/3D toggles, plus puck-style options, on a layers control alongside 3D buildings, POIs and light/dark theme.
- Truck and ADR routing: vehicle-dimension presets, hazmat declaration, and ADR tunnel category B–E, all shaping the route in the costing itself.
- Reachable-area isochrones: 10/20-minute drive-time contours from a single tap.
- Offline, end to end: territory install/list/remove, a fully local map style, and offline search, routing and voice guidance against an installed territory. Packages arrive over the signed territory channel and are verified on the device before use (see the early-access section above).
Architecture
One Expo app (SDK 57, dev client, TypeScript) plus one local Expo
module, modules/mapmap-react-native, that wraps the native SDKs:
- iOS: Swift
MapMapKit+MapMapValhallafromMapmapai/mapmap-ios(pin0.2.1; see SDKs). - Android: Kotlin
ai.mapmap:corefrom GitHub Packages (see SDKs for the repository/credential setup). - Map rendering stays in JS via
@maplibre/maplibre-react-native, fed by a style JSON the native module returns; every source points at local territory tiles, so the map renders with the radios off. - App state is an explicit state machine:
Explore → Search → Preview → Guidance → Arrived, with Settings/Territories/About pushed as modal-ish stack screens on top (react-navigationnative stack).
All navigation logic (routing, guidance, territory verification) lives in the native SDKs. The bridge is a thin async facade plus an event stream; JS never reimplements routing or guidance state.
The bridge contract
The single source of truth is
modules/mapmap-react-native/src/MapmapNav.types.ts in the app repo; this
section summarises it. Native module name:
MapmapNav. Every method is Promise-based; continuous data (territory
downloads, guidance ticks, voice state) arrives as events. Everything
crossing the bridge is JSON-serialisable.
interface MapmapNavModule {
// Load the native core, remember the API key + gateway base. Idempotent.
init(apiKey: string, gatewayBaseUrl?: string): Promise<void>;
// Territories
listAvailableTerritories(): Promise<TerritorySummary[]>;
listInstalledTerritories(): Promise<TerritoryInfo[]>;
installTerritory(territoryId: string): Promise<void>; // emits territoryProgress
removeTerritory(territoryId: string): Promise<void>;
getTerritoryStyle(territoryId: string, theme: "light" | "dark"): Promise<string>;
// Search: on-device, against the active territory
search(query: string, near?: LatLng, limit?: number): Promise<SearchResult[]>;
// Routing: on-device
computeRoute(request: RouteRequest): Promise<RouteResult>;
// Guidance
startGuidance(options: GuidanceOptions): Promise<void>;
stopGuidance(): Promise<void>;
// Voice
setVoiceMuted(muted: boolean): Promise<void>;
setVoiceVolume(volume: number): Promise<void>;
}
Events (Expo Modules event emitter): territoryProgress, guidance,
voiceState, bridgeError. Guidance events fire at ≥1Hz during a session,
plus one per state transition (banner change, reroute start/end, arrival).
Routing: car, bike, walk, and truck with ADR
type RouteProfile = "car" | "bicycle" | "walking" | "truck";
type TruckOptions = {
heightM?: number;
weightT?: number;
lengthM?: number;
widthM?: number;
hazmat?: boolean; // carrying ADR dangerous goods
tunnelCode?: string; // ADR tunnel restriction category, "B".."E"
};
type RouteAvoid = {
tolls?: boolean;
motorways?: boolean;
ferries?: boolean;
};
type RouteRequest = {
profile: RouteProfile;
origin: LatLng;
destination: LatLng;
waypoints?: LatLng[]; // intermediate stops, visited in order
alternatives?: number; // extra full routes beyond the primary (0–3, default 2)
truck?: TruckOptions; // truck profile only
avoid?: RouteAvoid;
};
Guidance: the event stream and banner instructions
type GuidanceEvent = {
state: "navigating" | "rerouting" | "arrived";
location: LatLng & { bearingDegrees: number; speedMps: number };
banner?: BannerInstruction; // absent momentarily during reroute
distanceToManeuverMeters: number;
remainingDistanceMeters: number;
remainingDurationSeconds: number;
etaEpochMs: number;
isOffRoute: boolean;
};
type BannerInstruction = {
primary: string; // e.g. "Turn left onto High Street"
secondary?: string;
maneuverType: ManeuverType;
roundaboutExit?: number;
thenPrimary?: string; // the "Then ↰" chip
thenManeuverType?: ManeuverType;
lanes?: LaneInfo[]; // populated close to the turn (~150–300m)
};
type LaneInfo = {
directions: ("left" | "slight-left" | "through" | "slight-right" | "right" | "uturn")[];
active: boolean; // whether this lane keeps you on the upcoming manoeuvre
};
Errors
Every rejected promise carries a stable code plus a message; for
example E_TERRITORY_ALLOWANCE (402, download allowance exhausted),
E_NO_ROUTE (no legal route for the request), E_NOT_INITIALISED (a
method called before init() resolved), or E_GUIDANCE_ACTIVE
(startGuidance called while a session is already running). If the native
SDK can't honour a contract item, the rule the builders worked to was:
stop and report it; don't redesign the interface silently.
Features built
- Tap-to-route: tap or long-press anywhere on the map, not just a named vector feature, to drop a pin and route there; the pin is reverse-geocoded to a real street/address via the gateway.
- Search: place/POI names, category quick-search chips, and postcode + address search (UK via postcodes.io / MapMap geocode, plus a dozen other countries). Saved places (Home/Work/favourites) and recent searches.
- Layers control: 3D buildings, POIs, map theme (light/dark), zoom controls and camera tilt.
- Truck/ADR routing (vehicle dimension presets, hazmat declaration, ADR tunnel category B–E), MapMap's differentiator, verified end-to-end against the gateway.
- Route options (avoid tolls/motorways/ferries), route alternatives with a tap-to-select picker, waypoints plumbed through the contract.
- Guidance: current-road banner, lane-guidance bar, posted speed-limit
signs with an over-limit warning (limits fetched per-edge via
/trace_attributes), chase camera, off-route GPS-follow + auto-reroute, arrival, voice. - Reachable-area isochrones (10/20-minute drive-time contours).
- Offline: territory install/list/remove, fully local map style, offline search and routing against an installed territory, and replay-drive guidance for demoing without an actual drive.
Full feature-by-feature detail, including what's built versus what's still
on the roadmap (add-a-stop UI, search-along-route, ETA share), is in the
app repo's NAV-FEATURE-GAPS.md.
How to run it
Quick start: the mock, no native build
The fastest path, and the one the download is built for: the app ships with a pure-TypeScript mock of the MapMap native module, so the whole flow (search, route, turn-by-turn) runs against live MapMap online tiles without compiling any native code.
unzip mapmap-nav-boilerplate.zip && cd mapmap-nav-boilerplate
cp .env.example .env # paste your MapMap API key into both vars
npm install
npx expo start # press i / a for a simulator, or scan the QR
You'll need an snk_ API key for the online tiles; issue one card-free
from the quickstart.
| Capability | JS mock (npx expo start) | Real native SDK |
|---|---|---|
| App UI, navigation, screens | Yes | Yes |
| Online MapMap map tiles | Yes | Yes |
| Search, routing, route lines | Yes (simulated) | Yes (real Valhalla routing) |
| Guidance events + voice, Simulate demo drive | Yes (scripted) | Yes (real map-matching) |
| Offline territories / offline routing | No | Yes |
| On-device map-matching against the real core | No | Yes |
Native build
The app uses native modules (the local mapmap-react-native Expo module,
@maplibre/maplibre-react-native), which rules out Expo Go for a native
build: you need an Expo dev client and a prebuild:
npx expo prebuild
npm run ios # or: npm run android
You'll need the native SDKs available to the build; see honest caveats below before you start; both are real friction today, not a five-minute setup.
Build your own
This bridge became the official
@mapmap/react-native
module: the territory lifecycle, routing, guidance stream and voice surface
here are what that package now ships. Install it from npm rather than
copying the bridge; see SDKs.
Two other ways to build on this app:
- Fork the app. It's a complete, working starting point: swap the branding, trim the screens you don't need, and you have a nav app running on real MapMap SDKs from day one.
- Reuse the bridge.
modules/mapmap-react-nativeis a self-contained Expo module; lift it into your own app if you want a different UI layer on the same native surface.
With a working bridge in place, a competent React Native team can ship a branded nav app in about two weeks; most of the SDK-integration work is already done.
Honest caveats
This was built in the position of an outside RN shop with nothing beyond published artefacts and public docs, and it surfaced real friction that's worth knowing before you start. The mock/online-tiles quick start above sidesteps all of it; these only bite when you go for a native build:
- This bridge is no longer the only option. It was written before
there was an official module, so it is roughly 2,000 lines of Swift and
Kotlin you would otherwise own.
@mapmap/react-nativenow ships the same surface from npm; prefer it for new work. - The boilerplate's Gradle config still points at GitHub Packages.
ai.mapmap:core0.3.0 resolves frommavenCentral()with no account and no token, so that repository block and itsgpr.user/gpr.tokencredentials are no longer needed. See the Android section of the SDKs docs for the current package coordinates. - iOS needs the vendored
MapMapKit.xcframework. An Expo module is a CocoaPods pod, and a pod can't cleanly consume a Swift Package Manager product today, so the iOS bridge links a vendored, prebuilt xcframework rather than a plain SwiftPM dependency. Those binaries are not included in the boilerplate download (source-only, secrets stripped); request the xcframework from MapMap and drop it intomodules/mapmap-react-native/ios/Vendored/beforepod install. See the iOS section of the SDKs docs for the package coordinates and versioning. - Offline territory downloads need a verified account. The signed
territory channel is live and the early-access Android build uses it.
If you are building from the boilerplate, offline downloads require an
email-verified key with remaining monthly allowance (a
402 E_TERRITORY_ALLOWANCEmeans the allowance is exhausted).
None of this blocks the quick start: run the mock first, and only chase native-SDK access once you're ready for a real device build. None of it blocks building on MapMap today; it's exactly the kind of plumbing this reference app already fought through once so you don't have to fight through it cold.
Next steps
- SDKs & installation: iOS/Android/web SDK install coordinates and the signing-key model this app relies on.
- Territory packages & OTA updates: how the offline maps this app installs are packaged and verified.
- Quickstart: issue an
snk_key for online-maps mode. - Conventions: base URL, auth, error envelope, ADR tunnel codes referenced throughout the bridge contract above.