Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

How it works

The SDK is one Rust core behind a frozen interface. This page explains the concepts that interface exposes, so the tutorials’ calls read as applications of them rather than recipes to copy. Every concept below is part of the frozen FFI contract.

Coordinates and the camera

A position is a GeographicCoordinate: latitude and longitude in decimal degrees on the WGS 84 ellipsoid. A region is a BoundingBox between a south-west and a north-east corner, and a box never spans the antimeridian.

The camera is a MapCamera value: an anchored pose of center, zoom, bearing, and pitch. The ground point under the center sits at the viewport center, zoom and pitch are clamped, and bearing wraps into the range from 0 up to but not including 360 degrees.

Camera attributeWhat it means
centerThe coordinate anchored at the viewport center
zoom_levelScale; a continuous level, not necessarily a whole number
bearing_degreesRotation clockwise from north
pitch_degreesTilt from top-down (0) toward the horizon

A camera carries no setters. A new pose is a new constructor call, and the attribute getters read the state back as it was built. The second constructor, fit_bounds, frames a bounding box inside the inset region you give it: top-down, north-up, zoomed to the largest level that keeps the whole box visible, and anchored on the inset region’s center. Insets are the pixels your own overlays keep clear, so route cards and attribution never hide the destination.

The camera answers screen mappings without drawing. project_to_screen and screen_to_ground_coordinate convert between a coordinate and viewport pixels, visible_tile_keys lists the tiles the view covers, and ground_meters_per_screen_pixel sizes a scale bar.

Zoom levels and tiles

A tile is one payload at a fixed zoom, addressed by a TileKeyRecord: a column, a row counted from the north, and the zoom level. The addressing is the web-mercator XYZ scheme. At zoom 0 the world is one tile, and each level doubles the columns and rows.

The camera’s zoom is continuous, so it answers two different questions. tile_zoom_level gives the nearest whole level a tile fetch should use. visible_tile_keys gives every tile at a chosen level that intersects the visible ground, sorted row-major from the north.

A raster tile is a pre-rendered image. The contract carries it as RasterTileData with its encoded bytes, an image format (PNG or JPEG), and a resolution: Standard, or Retina for the doubled-texel @2x form. A vector tile carries geometry and style separately from the pixels and is rendered on the device. The shipped renderer draws raster imagery, and vector rendering is a later path.

The provider registry

Provider-backed capability reaches you through MapSdk, an object with no providers at construction. You register implementations, then call the contract methods: forward and reverse geocoding, routing, the distance matrix, place search, traffic incidents, traffic flow, and raster tiles. Every call resolves through a registry. A call into a contract with no registration throws ProviderNotConfigured naming the contract, and the elevation contract has no registered implementation today.

There are two registration paths:

  • register_mapquest_providers installs the whole map-platform provider set from one API key, with the SDK defaults for rate limiting and retries.
  • register_geocoding_provider_from_foreign and register_raster_tile_provider_from_foreign install an implementation your own Kotlin or Swift code supplies through the GeocodingProvider and RasterTileProvider interfaces.

Registration is per object, and re-registration replaces every slot. A second MapSdk object needs its own registration.

The cache path

register_mapquest_providers_with_tile_cache installs the same provider set and wraps the raster-tile contract in a disk response cache: a SQLite database at a path you own, under an absolute byte budget. A cache hit answers from disk, and a cache miss goes to the network and back-fills.

Cached tile responses are retained under the tiles category’s time-to-live, 30 calendar days by default. That default is also the imagery provider’s terms ceiling. set_raster_tile_cache_time_to_live tunes the value, an override above the ceiling is accepted and clamped, and raster_tile_cache_terms reads back three values: the effective time-to-live, the provider’s declared ceiling, and the category default. The byte budget is fixed when the store opens, so retuning it means registering again, and a fresh registration starts at the category default.

Raster imagery is the layer the cache holds. Offline routing is a separate layer with its own archive (see below).

The frozen contract and four platforms

One UniFFI UDL file defines the surface, and it is the single source of truth. The Kotlin, Swift, and React Native bindings are generated from it, and the WebAssembly TypeScript mirror is regression-checked against it. The contract changes addition-only: existing members keep their signature and meaning, and new capability arrives as new members.

PlatformHow you call the contract
Android (Kotlin)Suspend functions and typed records; failures are the contract’s SdkError variants
iOS (Swift)Async throwing methods and records
React NativePromise-returning TurboModule methods with lower camel case field names
WebAssemblyA hand-reviewed TypeScript mirror over wasm-bindgen, covering the camera, rendering, and offline-routing slice

Plain data crosses as records and enums. The error model crosses as the SdkError variants the failing call can produce, with verbatim detail text. get_sdk_compiled_version on every surface answers the compiled SDK version, so a diagnostics screen can compare the loaded core with the artifacts your application shipped.

Routing returns a RouteData: legs between consecutive waypoints, and steps inside each leg, each carrying its distance, duration, maneuver label, and instruction texts. Navigation consumes that value rather than extending it.

MapNavigationController holds a trip. You start one with start_trip(route, unit_system, step_advance_policy, deviation_policy) and deliver one position fix at a time with provide_location_update. The controller publishes four topics: banner instructions, speech triggers, deviation status, and phase changes. A subscription is cancelled explicitly through its handle.

A trip has three phases: Navigating, OffRoute, and Arrived. Rerouting is not a phase; a trip moves between navigating and off route until it arrives. Deviation has two grades:

  • FullyOffRoute freezes progress until a graded fix recovers or a reroute replaces the route.
  • OffStepOnRoute means the fix is off the current step but still on a later part of the route, and the trip keeps navigating.

Guidance uses two policies you choose at trip start. StepAdvancePolicy decides when the trip moves to the next step, and DeviationPolicy decides when a fix counts as off route. A fix without a reported accuracy never advances a step and is never judged for deviation, so a simulated source must carry accuracy like a real one.

Instruction text has two channels. The banner channel carries the display text, and the speech channel carries the text-to-speech text through SpeechInstruction. The SDK decides when a line is newsworthy and supplies the text verbatim. Your application owns playback and synthesis, and utterance_id is a stable identity for deduplication.

A deviation asks for a replacement route through a small gate. begin_reroute_calculation answers true when the request passes the in-flight, cooldown, and movement checks, provide_reroute applies the replacement, and cancel_reroute_calculation abandons it. The trip state’s is_calculating_new_route reports the in-flight fact.

Offline routing

Offline routing answers from a compiled archive rather than a network round trip. The archive is built from an OpenStreetMap extract and is self-describing. It answers routes with an A* search over a memory-mapped graph and honors recorded turn restrictions. Maneuver text derives from the geometry where the extract carries no street names.

The archive’s direct route call joins the native contract in a later slice. Today it answers the automatic fallback when the online provider fails and the WebAssembly surface’s offline route seam, and one compiled archive serves every target.

Where to go next

The tutorials apply these concepts in order: Your first map, Search and routing, Turn-by-turn navigation, and Offline maps.