# MapMapKit (iOS) reference

> Generated from the published **MapMapKit 0.8.0** sources (github.com/Mapmapai/mapmap-ios, tag 0.8.0) by `website/scripts/sdk-reference-native.mjs`. Do not edit by hand: change the doc comments in the SDK, or the examples in `website/content/sdk-reference/ios.examples.md`, and regenerate.

Every public declaration of the Swift package at release 0.8.0: the `MapMapKit`, `MapMapValhalla` and `MapMapCarPlay` products. The guide is [iOS (Swift) in SDKs & installation](/docs/sdks#ios-swift), with [Mobile maps with MapLibre Native](/docs/mobile) for the map itself.

```swift
// Package.swift
.package(url: "https://github.com/Mapmapai/mapmap-ios.git", exact: "0.8.0")
```

Other SDK references: [@mapmap/maps](/docs/maps-reference), [@mapmap/points](/docs/points-reference), [@mapmap/core](/docs/core-reference), [@mapmap/tile](/docs/tile-reference), [@mapmap/react-native](/docs/react-native-reference), [@mapmap/voice](/docs/voice-reference), [ai.mapmap:core](/docs/android-reference).

236 public declarations across 3 modules: 50 written by hand, 186 in the generated Rust bindings.

## How to read this page

Each entry gives the declaration as written in the release source (in Kotlin without identifier backticks or the `kotlin.` package prefix), its doc comment, and its public members. The **Default** column shows a value only where the source writes one (an initialiser, a parameter's default, or a doc comment that states it); otherwise it is blank. Types and classes written by hand carry a minimal example. The generated bindings are the Rust core's types as UniFFI exposes them; their descriptions are the Rust doc comments.

## Contents

- [MapMapKit](#mapmapkit): 219 declarations
- [MapMapValhalla](#mapmapvalhalla): 1 declarations
- [MapMapCarPlay](#mapmapcarplay): 16 declarations

## MapMapKit

### Classes (MapMapKit)

[`CoreLocationProvider`](#corelocationprovider), [`HTTPChannelFetcher`](#httpchannelfetcher), [`URLSessionProbeUploader`](#urlsessionprobeuploader), [`VoiceGuidance`](#voiceguidance)

#### `CoreLocationProvider`

*Class* · `Sources/MapMapKit/Navigation/CoreLocationProvider.swift`

```swift
public final class CoreLocationProvider: LocationProvider
```

Live `LocationProvider` on Core Location.

The SDK's Info.plist declares **no** usage strings. Apps must add
`NSLocationWhenInUseUsageDescription` (and
`NSLocationAlwaysAndWhenInUseUsageDescription` plus the `location`
background mode for background guidance) and obtain authorisation
*before* the stream is consumed, see ios/README.md
("Location authorisation"). If authorisation is denied while
streaming, the stream finishes by throwing `CLError.denied`.

Samples map platform sentinels to `nil`: negative
`horizontalAccuracy` (invalid fix channels), negative `speed` and
negative `course` are all reported as absent rather than passed
through.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var locations` | `AsyncThrowingStream<LocationSample, Error>` |  |  |

**Initialisers and methods**

- `public init(configuration: Configuration = Configuration())`

Nested: [`CoreLocationProvider.Configuration`](#corelocationproviderconfiguration).

**Example**

```swift
import MapMapKit

let gps = CoreLocationProvider(configuration: .init(allowsBackgroundLocationUpdates: true))
for try await fix in gps.locations {
    print(fix.lat, fix.lon, fix.horizontalAccuracyM ?? -1)
}
```

#### `HTTPChannelFetcher`

*Class* · `Sources/MapMapKit/Territory/ChannelSource.swift`

```swift
public final class HTTPChannelFetcher: LayerFetcher, @unchecked Sendable
```

A blocking HTTPS `LayerFetcher` for a `ChannelSource`, on
`URLSession`. `TerritoryStore` calls it off the cooperative pool.

`apiKey` is sent as `Authorization: Bearer <apiKey>` (the hosted gateway
meters layer downloads against the key's allowance); `nil` for an open
mirror. `cancel` is polled while a request is in flight, so a tripped
token aborts the transfer, not only the next one. `configuration`
defaults to an ephemeral session (pass your own for a proxy or a test
`URLProtocol`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let source` | `ChannelSource` |  |  |
| `var bytesReceived` | `Int64` |  | Total bytes received so far. |
| `var lastFailedStatus` | `Int?` |  | HTTP status of the last failed request: 401/403 auth, 402 allowance, 404 not found. |

**Initialisers and methods**

- `public init(source: ChannelSource, apiKey: String? = nil, cancel: CancelToken? = nil, onBytes: (@Sendable (Int64) -> Void)? = nil, configuration: URLSessionConfiguration? = nil)`
- `public func fetchIndex() throws -> SignedChannelIndex`: GET the signed index and its signature, exact bytes.
- `public func fetch(relPath: String, expectedBlake3: String, destPath: String) throws`

**Example**

```swift
import MapMapKit

// The fetcher TerritoryStore.sync(territoryId:source:apiKey:) builds for you; use it directly
// when you drive installFromChannel or checkForUpdate yourself.
let fetcher = HTTPChannelFetcher(source: .gateway(), apiKey: "snk_…")
let index = try fetcher.fetchIndex()
print(index.json.count, fetcher.bytesReceived)
```

#### `URLSessionProbeUploader`

*Class* · `Sources/MapMapKit/Navigation/ProbeContribution.swift`

```swift
public final class URLSessionProbeUploader: ProbeUploader, @unchecked Sendable
```

Default `ProbeUploader`: POSTs each body to `{baseURL}/v1/probe` with a
bearer key via `URLSession`. Transient failures (transport or `5xx`) are
retried with bounded backoff; permanent ones (`400`/`403`/`501`) are
dropped. No third-party dependency.

**Initialisers and methods**

- `public init(baseURL: String, apiKey: String, session: URLSession = .shared, maxAttempts: Int = 4, backoff: @escaping @Sendable (Int) -> TimeInterval = { TimeInterval($0) }, onResult: @escaping @Sendable (ProbeUploadOutcome) -> Void = { _ in })`: - Parameters: - baseURL: gateway origin, e.g. `https://api.mapmap.ai` (a trailing slash is tolerated). - maxAttempts: total tries including the first. - backoff: attempt number → delay before the next try.
- `public func enqueue(_ body: String)`

**Example**

```swift
import MapMapKit

// Opt-in aggregate probe upload (POST /v1/probe), retried with backoff.
let uploader = URLSessionProbeUploader(baseURL: "https://api.mapmap.ai", apiKey: "snk_…") { outcome in
    print("probe upload:", outcome)
}
let routeJson = "{}" // a Valhalla route response
let engine = NavigationEngine(routeResponseJson: routeJson)
let states = contributingProbe(
    await engine.navigate(provider: CoreLocationProvider()),
    config: ProbeConfig(enabled: true),
    uploader: uploader
)
for try await state in states { if state.isArrived { break } }
```

#### `VoiceGuidance`

*Class* · `Sources/MapMapKit/Navigation/VoiceGuidance.swift`

```swift
public final class VoiceGuidance: NSObject, AVSpeechSynthesizerDelegate
```

Spoken turn-by-turn guidance over `AVSpeechSynthesizer` (offline, the
platform voices synthesise on device).

Feed every `NavigationState` (or raw `GuidanceUpdate`) from
`NavigationEngine/navigate(provider:)`:

``swift
let voice = VoiceGuidance()
for try await state in await engine.navigate(provider: provider) {
    voice.handle(state)
    render(state)
}
``

The core schedules each spoken prompt by its trigger distance and
repeats it on every update until it is superseded, so the speaking
rule is: announce each `utteranceId` exactly once, the first time it
appears. Severity maps to prosody and earcons via `VoiceProsody`;
critical prompts stop pending speech. Off-route plays the warning
earcon once per episode. Observe `isSpeaking` /
`onSpeakingChanged` to reflect live speaking state in UI (e.g. a
pulsing voice icon). On iOS the audio session ducks other audio
around announcements (`.duckOthers` + `.voicePrompt`), so music dims
rather than stops, and is deactivated again once the last utterance
finishes, so music comes back to full volume instead of staying
ducked forever.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var isSpeaking` | `Bool` |  | Whether an utterance is currently being spoken or queued. Mirrors the transitions delivered to `onSpeakingChanged`. |
| `var onSpeakingChanged` | `((Bool) -> Void)?` |  | Called on every speaking-state transition: `true` when the first utterance of a burst starts, `false` once the last one finishes, is cancelled or `stop()` is called. May be invoked on an arbitrary queue (the synthesiser's delegate queue), hop to the main actor before touching UI. |
| `var muted` | `Bool` | `false` | Whether announcements are muted. Toggle freely mid-drive; muting also stops anything mid-utterance. |
| `var volume` | `Float` | `1.0` | Announcement volume, 0–1 (applied to speech and earcons). |

**Initialisers and methods**

- `public init(language: String? = nil, baseRate: Float = AVSpeechUtteranceDefaultSpeechRate)`: - Parameters: - language: BCP 47 tag for the voice, e.g. `en-GB`. `nil` keeps the device default voice. - baseRate: base `AVSpeechUtterance` rate (platform default when omitted).
- `public func handle(_ update: GuidanceUpdate)`: Consume one guidance update. Cheap; call it for every emission of `NavigationEngine/navigate(provider:)`.
- `public func handle(_ state: NavigationState)`: Convenience overload for `NavigationEngine/navigate(provider:)` emissions.
- `public func stop()`: Stop speech and release the earcon player. Deactivates the audio session (un-ducking whatever else plays) when anything was live.
- `public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance)`
- `public func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance)`

**Example**

```swift
import MapMapKit

let voice = VoiceGuidance(language: "en-GB")
voice.volume = 0.8
voice.onSpeakingChanged = { speaking in print("speaking:", speaking) }
// For every emission of NavigationEngine.navigate(provider:):
// voice.handle(state.update)
voice.muted = true
```

### Actors (MapMapKit)

[`NavigationEngine`](#navigationengine), [`TerritoryStore`](#territorystore)

#### `NavigationEngine`

*Actor* · `Sources/MapMapKit/Navigation/NavigationEngine.swift`

```swift
public actor NavigationEngine
```

Turn-by-turn guidance over one computed route.

The Rust core's `GuidanceSession` (Ferrostar-based) is the navigation
state machine; this engine owns its lifecycle and adapts it to Swift
concurrency: stream `LocationSample`s in from any
`LocationProvider`, receive `NavigationState`s out. The stream
completes after `arrived`. An engine is bound to a single route, on
`offRoute`, compute a new route and build a new engine (see
`NavigationState/needsReroute`).

Ferrostar UI note: the Stadia Maps Ferrostar iOS packages
(https://github.com/stadiamaps/ferrostar, SwiftPM products
`FerrostarCore`, `FerrostarMapLibreUI`, `FerrostarSwiftUI`,
`FerrostarCarPlayUI`) can render on top of this state, but our
`GuidanceSession` remains the state machine, see ios/README.md,
"Ferrostar boundary".

**Initialisers and methods**

- `public init(routeResponseJson: String, config: GuidanceConfig = GuidanceConfig())`
- `public init(route: RouteResult, config: GuidanceConfig = GuidanceConfig())`: Build an engine from a route computed by `OfflineRouting/route(router:waypoints:costing:adr:)`.
- `public func totalSteps() throws -> UInt32`: Total number of steps in the route (including arrival), for progress UIs. Opens a short-lived native session. - Throws: `NavCoreError` if the route response cannot start a session (e.g. malformed JSON).
- `public func navigate(provider: some LocationProvider) -> AsyncThrowingStream< NavigationState, Error >`: Run guidance: feed every fix from `provider` into a fresh native session and emit the resulting states. Each call starts a new session at the first route step; a previous run on the same engine is stopped first. The stream completes after arrival, or fails with `NavCoreError` if the route response cannot start a session, or with the provider's error if the fix stream fails.
- `public func advanceToNextStep() throws`: Manually advance to the next step, the tunnel/no-GNSS escape hatch (Ferrostar exposes this on its navigator; the core forwards it). Ignored before the first fix of the active run, mirroring the Android SDK.
- `public func stop()`: Stop the active run, if any: the stream finishes without error and the native session is released.

**Example**

```swift
import MapMapKit

// A Valhalla route response (hosted /route, or OfflineRouting.route on the device).
let routeJson: String = try String(contentsOf: Bundle.main.url(forResource: "route", withExtension: "json")!)
let engine = NavigationEngine(routeResponseJson: routeJson)
let voice = VoiceGuidance(language: "en-GB")
for try await state in await engine.navigate(provider: CoreLocationProvider()) {
    voice.handle(state.update)
    if state.isArrived { break }
    if state.needsReroute { /* request a new route and build a new engine */ }
}
```

#### `TerritoryStore`

*Actor* · `Sources/MapMapKit/Territory/TerritoryStore.swift`

```swift
public actor TerritoryStore
```

Actor-isolated store of signed offline territory packages.

Wraps the Rust `TerritoryManager`: every package is ed25519-signature
and BLAKE3-layer-hash verified before promotion, and nothing is
trusted until verification passes. Verification is fail-closed, a
package that does not verify is never promoted and leaves nothing on
disk.

All methods are `async` by actor isolation; the disk-heavy calls
(installs read whole archives) additionally hop onto a detached
utility task so they never occupy a cooperative-pool thread for the
duration of an install.

`territories` caches the installed set; it refreshes after every
mutating call and on `refresh()`. Observe changes with
`territoryUpdates()`.

Installs are cancellable. The `cancel:` overloads take a `CancelToken`
(from the core) that can be tripped from any thread, and they also
trip it when the calling Swift `Task` is cancelled, so `task.cancel()`
works the way callers expect. A cancelled install returns
`InstallOutcome.cancelled` / `UpdateOutcome.cancelled` rather than
throwing: nothing was installed, staging is removed, the previous
version is untouched. Cancelling after completion changes nothing.

Custom regions (`region-` ids, cut by `POST /v1/jobs/territory`) are
signed by a separate region key. Pass its public half as `regionKeyHex`
(`TerritoryKeys/hostedRegionKeyHex` for the hosted gateway) to accept
them; the core then verifies `region-` ids against that key only and
every other id against the factory key only. Without it every `region-`
package is refused as a bad signature.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var territories` | `[TerritoryInfo]` | `[]` | The installed territories as of the last refresh or mutation. |
| `var acceptsCustomRegions` | `Bool` |  | Whether this store was given a region key, and so can install custom regions. |

**Initialisers and methods**

- `public init(rootDir: URL, verifyingKeyHex: String, regionKeyHex: String? = nil)`: - Parameters: - rootDir: directory the store owns; created on first use. - verifyingKeyHex: the factory signing key's public half, 64 hex characters (`sn_package::VerifyingKey::to_hex`). Baked into the app at build time, treat it like a pin. - regionKeyHex: the region signing key's public half, 64 hex characters, or `nil` to refuse custom regions. Pinned like the factory key; never read from the network.
- `public func refresh() async throws -> [TerritoryInfo]`: Re-read the installed set from disk, cache and broadcast it.
- `public func install(snpkg: URL) async throws -> TerritoryInfo`: Install a packed `.snpkg` archive. Verify-then-promote: a package that fails verification leaves no trace on disk. - Throws: `NavCoreError/Verification(message:)` if the signature or any layer hash does not verify.
- `public func install(snpkg: URL, cancel: CancelToken) async throws -> InstallOutcome`: `install(snpkg:)` with a cancellation handle. The core polls `cancel` between archive entries, before verification and before promotion; a tripped token (or a cancelled calling task) yields `.cancelled` with staging removed and nothing installed.
- `public func installDirectory(_ packageDir: URL) async throws -> TerritoryInfo`: Install an unpacked package directory (same verification rules).
- `public func installDirectory(_ packageDir: URL, cancel: CancelToken) async throws -> InstallOutcome`: `installDirectory(_:)` with a cancellation handle; see `install(snpkg:cancel:)`.
- `public func installFromChannel(territoryId: String, indexJson: String, indexSigBase64: String, fetcher: LayerFetcher, omit: [MapLayerKind] = [], cancel: CancelToken = CancelToken()) async throws -> ChannelInstallOutcome`: Install `territoryId` **from its channel**: the first-install counterpart of `applyUpdate(territoryId:manifestJson:manifestSigBase64:fetcher:)`. `indexJson` / `indexSigBase64` are the exact served bytes of the channel index and its detached signature; the channel's latest version is installed, fetching only the layers not in `omit`. Nothing is trusted but the pinned keys: the index signature, the manifest signature and every layer hash are checked, and the assembled package goes through the full verification pass before it is promoted. Interrupted installs resume. A tripped `cancel` (or a cancelled calling task) yields `.cancelled` with nothing installed. - Throws: `NavCoreError/Verification(message:)` if a signature or hash does not verify, including a `region-` id on a store without a region key.
- `public func sync(territoryId: String, indexJson: String, indexSigBase64: String, fetcher: LayerFetcher, omit: [MapLayerKind] = [], cancel: CancelToken = CancelToken()) async throws -> ChannelSync`: Bring `territoryId` to the channel's latest version, whichever way that takes: a first install from the channel when it is not installed, a signed differential update when an older version is, nothing when it is already current. Updates go through `checkForUpdate(territoryId:indexJson:indexSigBase64:fetcher:)`, so a channel can never move an installed territory backwards.
- `public func sync(territoryId: String, source: ChannelSource, apiKey: String? = nil, omit: [MapLayerKind] = [], cancel: CancelToken = CancelToken(), onBytes: (@Sendable (Int64) -> Void)? = nil) async throws -> ChannelSync`: `sync(territoryId:indexJson:indexSigBase64:fetcher:omit:cancel:)` over HTTPS: fetch the signed index from `source` and every layer relative to it with an `HTTPChannelFetcher`. `cancel` also stops a transfer in progress.
- `public func installCustomRegion(territoryId: String, indexURL: URL, indexSigURL: URL, apiKey: String? = nil, omit: [MapLayerKind] = [], cancel: CancelToken = CancelToken(), onBytes: (@Sendable (Int64) -> Void)? = nil) async throws -> ChannelSync`: Install, or update, a custom region from the `territory_id`, `index_url` and `index_sig_url` of a `POST /v1/jobs/territory` result. A re-cut of the same area after the country is republished is the same id at a newer version, so calling this again is the update. - Throws: `ChannelSourceError/noRegionKey` if this store has no region key; `ChannelSourceError` for a malformed id or URL.
- `public func verify(territoryId: String) async throws -> TerritoryInfo`: Re-verify an installed territory from disk: the manifest signature (against the key its id is partitioned to), the hash of every layer it holds and the absence of every layer it omitted. The on-demand integrity check for after a restore or a crash. - Throws: `NavCoreError/Verification(message:)` when the install must be repaired or reinstalled.
- `public func setActive(_ territoryId: String) async throws`: Mark `territoryId` active; persisted atomically by the core.
- `public func active() async throws -> String?`: The active territory id, if one is set and still installed.
- `public func activeTerritory() async throws -> TerritoryInfo?`: The active territory's full info, if any.
- `public func layerPath(territoryId: String, kind: MapLayerKind) async throws -> URL`: Absolute path of one layer of an installed territory, e.g. `MapLayerKind/valhallaTiles` for the on-device router, `MapLayerKind/pmtiles` for MapLibre.
- `public func territoryStyle(territoryId: String, theme: StyleTheme = .light) async throws -> String`: A complete MapLibre style JSON for an installed territory with **local** (offline) references, mirroring the web SDK's `buildStyle()`. Load it straight into MapLibre, the territory tile source points at the installed PMTiles layer via a `pmtiles://<absolute path>` URL, so the map renders with radios off. Packages without a baked style layer get the default MapMap theme compiled on the fly.
- `public func openSearch(territoryId: String) async throws -> TerritorySearch`: Open the installed territory's bundled geocode index for fully offline on-device search. The returned handle holds the index open (mmap-backed), create it once per territory and reuse it: ``swift let search = try await store.openSearch(territoryId: "uk") let places = try search.search( query: "watford gap", near: nil, limit: 10) ``
- `public func remove(_ territoryId: String) async throws`: Remove an installed territory (clears the active marker if needed).
- `public func planUpdate(newManifestJson: String) async throws -> UpdatePlan`: Plan a differential update against a newly served (signed) manifest JSON: which layers must be downloaded, and how many bytes.
- `public func checkForUpdate(territoryId: String, indexJson: String, indexSigBase64: String, fetcher: LayerFetcher) async throws -> AvailableUpdate?`: Check the signed channel index for an update to an installed territory. `indexJson` must be the exact bytes served as `index.json` and `indexSigBase64` the content of `index.json.sig`; the remote manifest is fetched and verified through `fetcher` (the app's download transport, URLSession, an air-gapped mirror, …, the core decides *what* to fetch and cryptographically verifies everything itself). Returns `nil` when the device is already on the channel's latest version or the channel does not list the territory.
- `public func applyUpdate(territoryId: String, manifestJson: String, manifestSigBase64: String, fetcher: LayerFetcher) async throws -> UpdateResult`: Apply a differential update: stage, fetch only the changed layers through `fetcher`, verify everything and swap atomically. Any failure leaves the installed territory untouched; the active flag is preserved.
- `public func applyUpdate(territoryId: String, update: AvailableUpdate, fetcher: LayerFetcher) async throws -> UpdateResult`: Apply an update discovered by `checkForUpdate(territoryId:indexJson:indexSigBase64:fetcher:)`, passing its manifest and signature through verbatim.
- `public func applyUpdate(territoryId: String, manifestJson: String, manifestSigBase64: String, fetcher: LayerFetcher, cancel: CancelToken) async throws -> UpdateOutcome`: `applyUpdate(territoryId:manifestJson:manifestSigBase64:fetcher:)` with a cancellation handle. The core polls `cancel` before every layer download, between archive entries while unpacking, before each hash pass and before promotion; a tripped token (or a cancelled calling task) yields `.cancelled`. The installed version is untouched and staging removed; layers already fetched *and verified* stay in the resumable cache, so a later attempt with the same manifest re-fetches only what is missing. The token cannot interrupt `fetcher` while it is inside a transfer: give the fetcher the same token and have it poll `isCancelled()` between chunks (or cancel its `URLSessionTask`). Whatever the fetcher then throws, a tripped token reports `.cancelled` rather than a transport error.
- `public func applyUpdate(territoryId: String, update: AvailableUpdate, fetcher: LayerFetcher, cancel: CancelToken) async throws -> UpdateOutcome`: `applyUpdate(territoryId:update:fetcher:)` with a cancellation handle; see `applyUpdate(territoryId:manifestJson:manifestSigBase64:fetcher:cancel:)`.
- `public func territoryUpdates() -> AsyncStream<[TerritoryInfo]>`: A stream of the installed set: yields the current value immediately, then every subsequent change. Each call returns an independent stream; it finishes when the store is deallocated.

**Example**

```swift
import MapMapKit

let root = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
    .appendingPathComponent("territories")
let store = TerritoryStore(
    rootDir: root,
    verifyingKeyHex: TerritoryKeys.hostedFactoryKeyHex,
    regionKeyHex: TerritoryKeys.hostedRegionKeyHex
)
// First install or update, whichever is needed, from the hosted channel.
let result = try await store.sync(territoryId: "uk", source: .gateway(), apiKey: "snk_…")
try await store.setActive("uk")
let styleJson = try await store.territoryStyle(territoryId: "uk", theme: .dark)
print(result, styleJson.count)
```

### Structs (MapMapKit)

[`ChannelSource`](#channelsource), [`CoreLocationProvider.Configuration`](#corelocationproviderconfiguration), [`CorpusFormatError`](#corpusformaterror), [`DriveCorpus`](#drivecorpus), [`DriveFix`](#drivefix), [`DriveHeader`](#driveheader), [`LocationSample`](#locationsample), [`NavigationCameraInterpolator`](#navigationcamerainterpolator), [`NavigationState`](#navigationstate), [`ReplayLocationProvider`](#replaylocationprovider), [`SignedChannelIndex`](#signedchannelindex), [`SpeechSessionRefCount`](#speechsessionrefcount), [`VoiceProsody`](#voiceprosody)

#### `ChannelSource`

*Struct* · `Sources/MapMapKit/Territory/ChannelSource.swift`

```swift
public struct ChannelSource: Equatable, Sendable
```

Where a signed territory channel lives over HTTPS: its index, the
index's detached signature, and the base every channel-relative layer
path (`territories/<id>/<version>/...`) is resolved against.

Nothing fetched from here is trusted: the core verifies the index
signature, the manifest signature and every layer hash against the keys
pinned in the `TerritoryStore`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let indexURL` | `URL` |  |  |
| `let indexSigURL` | `URL` |  |  |
| `let layerBaseURL` | `URL` |  | Layer paths are appended to this. |
| `static let defaultGateway` |  |  | The hosted gateway. |

**Initialisers and methods**

- `public init(indexURL: URL, indexSigURL: URL, layerBaseURL: URL)`
- `public static func gateway(_ base: URL = defaultGateway) -> ChannelSource`: A gateway's country channel: `GET /territories` (the signed index) and `GET /territories/index.sig`.
- `public static func customRegion(territoryId: String, indexURL: URL, indexSigURL: URL) throws -> ChannelSource`: A custom region's own one-territory channel, from the `index_url` / `index_sig_url` of a `POST /v1/jobs/territory` result. The gateway serves the index at `<base>/v1/territories/<id>/index.json` and the layers under `<base>/territories/<id>/...`, so the layer base is `<base>`. Any other `.../index.json` (a mirror of the region directory, laid out as `SN_REGION_DIR/<id>/`) resolves layers next to the index. - Throws: `ChannelSourceError` if `territoryId` is not a custom-region id or `indexURL` does not end in `index.json`.
- `public static func isCustomRegionId(_ territoryId: String) -> Bool`: Whether `territoryId` has the shape the gateway gives custom regions: `region-` plus 16 lowercase hex characters.

#### `CoreLocationProvider.Configuration`

*Struct* · `Sources/MapMapKit/Navigation/CoreLocationProvider.swift`

```swift
public struct Configuration: Sendable
```

Configuration forwarded to `CLLocationManager`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var desiredAccuracy` | `CLLocationAccuracy` |  | Desired accuracy; vehicular guidance wants `kCLLocationAccuracyBestForNavigation`. |
| `var distanceFilter` | `CLLocationDistance` |  | Distance filter in metres (`kCLDistanceFilterNone` = every fix). |
| `var activityType` | `CLActivityType` |  | Activity type hint for the platform's fusion. |
| `var allowsBackgroundLocationUpdates` | `Bool` |  | Whether to request background updates (requires the app's `location` background mode). |

**Initialisers and methods**

- `public init(desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBestForNavigation, distanceFilter: CLLocationDistance = kCLDistanceFilterNone, activityType: CLActivityType = .automotiveNavigation, allowsBackgroundLocationUpdates: Bool = false)`

#### `CorpusFormatError`

*Struct* · `Sources/MapMapKit/Replay/DriveCorpus.swift`

```swift
public struct CorpusFormatError: Error, Equatable, LocalizedError
```

A `.drive.jsonl` document violated the corpus format.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let message` | `String` |  |  |
| `var errorDescription` | `String?` |  |  |

**Initialisers and methods**

- `public init(_ message: String)`

#### `DriveCorpus`

*Struct* · `Sources/MapMapKit/Replay/DriveCorpus.swift`

```swift
public struct DriveCorpus: Equatable, Sendable
```

Parser for the `.drive.jsonl` replay corpus format produced by the
workspace's `sn-replay` crate (see `crates/sn-replay/src/corpus.rs`).

Line 1 is a JSON header (`format_version`, `name`, `description`,
`route_fixture`, `seed`, optional `generator`); every following
non-blank line is one GPS fix (`t_ms`, `lat`, `lon`, optional
`speed_mps`, `bearing_deg`, `horizontal_accuracy_m`). The Swift parser
enforces the same invariants as the Rust one (and the Kotlin one in
`android/`): supported format version, non-empty name, at least one
fix, strictly increasing `t_ms`, and sane coordinate/channel ranges.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let formatVersion` |  |  | The corpus format version this parser accepts. |
| `let header` | `DriveHeader` |  |  |
| `let fixes` | `[DriveFix]` |  |  |

**Initialisers and methods**

- `public static func parse(_ jsonl: String) throws -> DriveCorpus`: Parse and validate a corpus from JSON-lines text. - Throws: `CorpusFormatError` describing the first violation, with its 1-based line number where applicable.
- `public static func parse(contentsOf url: URL) throws -> DriveCorpus`: Read and parse a corpus file.

#### `DriveFix`

*Struct* · `Sources/MapMapKit/Replay/DriveCorpus.swift`

```swift
public struct DriveFix: Equatable, Sendable
```

One GPS fix line of a corpus. `tMs` is milliseconds since drive start.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let tMs` | `Int64` |  |  |
| `let lat` | `Double` |  |  |
| `let lon` | `Double` |  |  |
| `let speedMps` | `Double?` |  |  |
| `let bearingDeg` | `Double?` |  |  |
| `let horizontalAccuracyM` | `Double?` |  |  |

#### `DriveHeader`

*Struct* · `Sources/MapMapKit/Replay/DriveCorpus.swift`

```swift
public struct DriveHeader: Equatable, Sendable
```

Header line of a `.drive.jsonl` corpus.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let formatVersion` | `Int` |  |  |
| `let name` | `String` |  |  |
| `let description` | `String` |  |  |
| `let routeFixture` | `String` |  |  |
| `let seed` | `Int64` |  |  |

#### `LocationSample`

*Struct* · `Sources/MapMapKit/Navigation/LocationProvider.swift`

```swift
public struct LocationSample: Equatable, Sendable
```

One position sample fed into guidance.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var lat` | `Double` |  | Latitude in decimal degrees (WGS84). |
| `var lon` | `Double` |  | Longitude in decimal degrees (WGS84). |
| `var timestampMs` | `Int64` |  | Milliseconds since the Unix epoch. |
| `var speedMps` | `Double?` |  | Speed over ground in metres per second, when the source reports it. |
| `var bearingDeg` | `Double?` |  | Course over ground, degrees clockwise from true north, `[0, 360)`. |
| `var horizontalAccuracyM` | `Double?` |  | Estimated 1-sigma horizontal accuracy in metres. Pass the platform value whenever available, the core uses it to gate step advance and deviation detection. |

**Initialisers and methods**

- `public init(lat: Double, lon: Double, timestampMs: Int64, speedMps: Double? = nil, bearingDeg: Double? = nil, horizontalAccuracyM: Double? = nil)`

#### `NavigationCameraInterpolator`

*Struct* · `Sources/MapMapKit/Navigation/NavigationCameraInterpolator.swift`

```swift
public struct NavigationCameraInterpolator: Sendable
```

Turns a ~1 Hz stream of guidance fixes into a value that can be sampled
on every display frame, so a follow camera moves continuously instead of
stepping once per fix.

## Why this type exists

Guidance fixes arrive at roughly 1 Hz (the replay corpora and Core
Location both). Driving a map camera straight off them looks jerky in two
distinct ways, and only one of them is fixed by easing:

1. **Per-tick easing.** Kicking off one animation per fix (MapLibre's
   `easeTo`/`setCamera(_:withDuration:)`) hands the camera a new target
   while the previous animation is still running. Each new animation
   restarts from wherever the last one had got to, with its own easing
   curve, so the velocity resets 1x/second, visible as a pulse.
2. **Two clocks.** Animating a *map-anchored* puck while the camera is
   itself animating gives the scene two independent clocks that drift
   against each other. That desync is the "stutters back and forth,
   worse at low zoom" bug already diagnosed and fixed in the MapMapNav
   app (see `src/screens/guidance/ChaseLayer.tsx`, whose fix, camera
   only, puck as a fixed screen overlay, this iOS work mirrors).

This type addresses (1): the host samples it from a `CADisplayLink` and
applies the result to the map with **no** animation, so the frame clock
is the only clock. (2) is addressed by the host drawing the puck as a
fixed overlay at the camera focal point.

There is a third way for this to look wrong, and `setRoute(coordinates:)`
is the answer to it: interpolating in a straight line between two fixes
a second apart **cuts corners**, so through a bend the vehicle drifts
off the road and back on. Given the route, the interpolation runs in arc
length along it and every position reported is a point of the route.

## Presentation lag (deliberate)

`state(at:)` reports where the vehicle was one fix-interval ago. That
lag is what lets it *interpolate between two known fixes* rather than
guess ahead of the newest one, and it is what makes the motion
continuous: the sample reaches fix *N* exactly as fix *N+1* lands, and
the next segment starts from that same point.

This is not a new trade-off. The approved MapMapNav chase camera has the
identical lag by construction, `ChaseLayer` eases to each fix over a
duration equal to the gap since the previous one, so its camera also
arrives at fix *N* as fix *N+1* appears. The difference here is only that
a frame clock replaces one animation per tick.

Guidance text, distances and ETA are *not* lagged: they come straight
from the engine tick. Only the camera geometry is interpolated.

## Clock

The type is clock-agnostic: `timestamp` is whatever monotonic seconds the
host feeds, and `state(at:)` must be queried on the same clock. Hosts
should feed the **arrival** time of each fix (`CACurrentMediaTime()`),
not the fix's own `timestampMs`: arrival time is what the extrapolation
cap needs to reason about, and it stays correct when a replay runs at a
time scale (`ReplayLocationProvider(timeScale:)`) or when fixes are
delivered late.

Pure value type: no UIKit, no MapLibre, no CarPlay, so it builds and is
tested on the macOS host path (`MAPMAP_HOST_CORE=1 swift test`) as well
as on iOS.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let bearingSmoothAlpha` | `Double` | `0.35` | Per-fix low-pass factor for the camera bearing (0…1; lower is smoother and laggier). 0.35, taken verbatim from `BEARING_SMOOTH_ALPHA` in MapMapNav's `src/screens/guidance/GuidanceScreen.tsx`, where it is the value proven on device: enough damping that a noisy heading or a sharp corner does not whip the map, tight enough that the camera still tracks the corner. |
| `static let standstillSpeedMps` | `Double` | `0.5` | Below this speed the reported course is noise, so the smoothed bearing is frozen rather than followed. 0.5 m/s is the same standstill threshold MapMapNav uses to decide a fix is not moving (`speedMps > 0.5` in `state/guidance/routeProgress.ts` and `state/guidance/fasterRoute.ts`). A stationary vehicle's course wanders over the whole compass; following it spins the map on the spot. |
| `static let extrapolationCap` | `Double` | `1.5` | Ceiling on the interpolation parameter: 1.0 is the newest fix, so 1.5 is **half an interval past** it, and beyond that the value holds still. A late fix should not freeze the camera dead, but neither should a stalled feed send it gliding away, the further it runs on a guess, the bigger the snap back when the truth lands. Half an interval absorbs ordinary jitter without ever overshooting far enough to see. Same value and same semantics as the Android side's `EXTRAPOLATION_CAP`. |
| `static let minFixInterval` | `TimeInterval` | `0.10` | The measured inter-fix interval is clamped to this range before it paces anything, so one absurd gap (a suspended app, a debugger pause) cannot make the camera crawl or bolt. Ceiling mirrors the 1400 ms clamp MapMapNav's `ChaseLayer` puts on its per-fix ease duration; the floor sits at 100 ms (below MapMapNav's 140 ms) so the demo's 8x replay cadence of 125 ms interpolates instead of binding on the clamp, which left a residual fix-interval stutter. The Android side mirrors both bounds. |
| `static let maxFixInterval` | `TimeInterval` | `1.4` |  |
| `static let nominalFixInterval` | `TimeInterval` | `1.0` | Assumed interval before two fixes have been seen (fixes are ~1 Hz), and the value the pacing window is seeded with. Seeding matters: with an empty window a single burst gap would *be* the median and collapse the pace to the floor. |
| `static let intervalWindow` | `Int` | `3` | How many recent inter-fix gaps the **pacing interval** is the median of, the Android side's `INTERVAL_WINDOW`. Three is the smallest window with a median at all, and it is enough: one anomalous gap (a burst of two fixes 40 ms apart, a single late one) is outvoted by its two neighbours, while a genuine change of cadence, the demo's 8x replay, a provider switching rate, is adopted after two fixes rather than being damped away. Larger windows only add lag to that adoption. |
| `static let onRouteToleranceM` | `Double` | `25` | How far off the route a fix may project and still be treated as on it, in metres, the Android side's `OFF_ROUTE_LATERAL_M`. The fix a follow camera should be fed is already route-snapped (`GuidanceDisplay.snappedFix`), so on-route projections land within centimetres; this only has to be wide enough for a raw fix on the carriageway and tight enough that a genuine excursion (a wrong turn, a parallel service road) stops pretending to be on the route. |
| `static let tangentWindowM` | `Double` | `15` | Half-width, in metres, of the window the route tangent is measured over, the Android side's `TANGENT_WINDOW_M`. Taking the tangent from a single polyline segment makes a right-angle corner flip the bearing over a couple of metres, which on a course-up camera is a snap. Measuring it as the chord across ±15 m spreads that turn over 30 m of driving instead. Note this smoothing is **spatial, not temporal**: a route-derived bearing gets no exponential filter on top of it. The wobble that filter exists for, noisy per-fix course, is not in the route geometry, and a distance-based window makes the corner look the same at any speed, which is what lets the two platforms match. |
| `var hasFix` | `Bool` |  | True once at least one fix has been fed. |
| `var latestBearingDeg` | `Double?` |  | The low-passed bearing as of the newest fix, in degrees `[0, 360)`. `nil` before the first fix. Exposed for tests and diagnostics; rendering should use `state(at:)`, which interpolates towards it. |
| `var fixInterval` | `TimeInterval` |  | The raw span between the two newest fixes, or `nominalFixInterval` before two have been seen. Diagnostics only: what actually paces the camera is `pacingInterval`, which is clamped and median-filtered. |
| `var pacingInterval` | `TimeInterval` |  | The interval the camera is actually **paced** by: the median of the last `intervalWindow` clamped inter-fix gaps, seeded with `nominalFixInterval`. A median rather than the single last gap because one anomalous gap must not redefine the pace. Two fixes 40 ms apart followed by ordinary 1 Hz delivery would otherwise set the interval to the 100 ms floor, run the interpolation parameter to `extrapolationCap` within 190 ms, freeze the pose for the rest of the second, and then snap it *backwards* when the next fix rolled the segment over. Mirrors the Android side's `pacingIntervalMs`. Distinct from `fixInterval`, which is the raw span between the two newest fixes and is reported for diagnostics only. |
| `var isFollowingRoute` | `Bool` |  | True when a route is set and both of the two newest fixes projected onto it, i.e. `state(at:)` is following the road, not a chord across it. |

**Initialisers and methods**

- `public init()`
- `public mutating func setRoute(coordinates: [CLLocationCoordinate2D])`: Give the interpolator the route the vehicle is driving, so that it interpolates **along the road** instead of straight between fixes. Why this matters: fixes arrive about a second apart, which at urban speed is 10–15 m. Interpolating in a straight line between two of them cuts every corner, through a bend the interpolated point leaves the route line, and the puck visibly drifts off the road and back on again. Interpolating in **arc length** along the route and emitting the point *on* the polyline cannot do that: every position it reports is a point of the route. The cumulative-distance table is built **once, here**. Rebuilding it per tick is the mistake that froze MapMapNav's JS thread (`src/state/guidance/routeProgress.ts` documents the same precompute); this type projects a fix onto the route once per fix (~1 Hz), and the per-frame query is a binary search and a lerp. Pass the route geometry in order. Fewer than two points clears it. Both stored fixes are **re-snapped** onto the new geometry here. A sample's `alongM` is an arc length measured against whatever polyline was current when the fix arrived, and `state(at:)` evaluates it against whatever polyline is current now, so carrying the old numbers across a reroute reads "2 km along route A" as "2 km along route B" and puts the camera wherever that happens to land, which on a reroute starting elsewhere is a jump of degrees of latitude (reproduced on the Android side). The re-snap honours `onRouteToleranceM` exactly as `feed(coordinate:courseDeg:speedMps:timestamp:)` does, so a pose the new route does not pass near loses its match and falls back to point-to-point rather than being dragged onto a line it was never on.
- `public mutating func feed(coordinate: CLLocationCoordinate2D, courseDeg: Double?, speedMps: Double?, timestamp: TimeInterval)`: Feed one guidance fix. - Parameters: - coordinate: the position to follow. Use the **route-snapped** fix (`GuidanceDisplay.snappedFix`), never the raw one: raw GPS drifts off the carriageway, and on a course-up camera that reads as the whole world shaking. - courseDeg: course over ground, degrees clockwise from true north. Values outside `[0, 360)` are wrapped. - speedMps: speed over ground. Below `standstillSpeedMps` the bearing is frozen (see the property). - timestamp: monotonic seconds on the host's clock, the fix's *arrival* time (see the type's "Clock" note). Fixes that are out of order, simultaneous with the newest one, or non-finite are ignored: replaying them would send the camera backwards.
- `public func state(at timestamp: TimeInterval) -> (coordinate: CLLocationCoordinate2D, bearingDeg: Double)?`: The camera position and bearing to render at `timestamp`. `nil` before the first fix, the host should leave the camera where it framed the route. Behaviour, in the order the cases arise: - **first fix**: snaps to it (nothing to interpolate between); - **healthy feed**: linear interpolation between the two newest fixes, one interval behind live (see the type's "Presentation lag" note); - **late fix**: keeps going along the last segment, but only as far as `extrapolationCap`, half an interval past the newest fix; - **stale feed**: holds at that capped point. With a route set (`setRoute(coordinates:)`) and both fixes on it, the interpolation runs in **arc length along the route** and the position returned is a point *on* the route line, with the bearing taken from the route's own direction there. Without a route, or once a fix projects further than `onRouteToleranceM` off it, it falls back to interpolating the two fixes directly. The fallback interpolates linearly in latitude/longitude. Over one fix interval of driving (tens of metres) the difference from a great-circle path is far below a pixel, so the cheap form is the honest one. It does not handle a segment crossing the antimeridian; no MapMap route fixture does.
- `public static func wrap360(_ degrees: Double) -> Double`: Normalise to `[0, 360)`.
- `public static func shortestArcDelta(from: Double, to: Double) -> Double`: Signed shortest-arc difference `from -> to`, in `[-180, 180)`. Positive is clockwise. The half-open range matches `shortestArcDeltaDeg` on the Android side down to the boundary: an exact 180 degree reversal resolves anticlockwise on both platforms rather than one each way.

#### `NavigationState`

*Struct* · `Sources/MapMapKit/Navigation/NavigationState.swift`

```swift
public struct NavigationState: Equatable, Sendable
```

One guidance emission: the fix that produced it plus the core's
verdict.

`GuidanceUpdate` is the Rust core's state, `navigating`, `arrived`
or `offRoute`, and the single source of truth. This wrapper only adds
the triggering sample and display conveniences.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let fix` | `LocationSample` |  |  |
| `let update` | `GuidanceUpdate` |  |  |
| `var isArrived` | `Bool` |  | True once the core reports arrival at the destination. |
| `var needsReroute` | `Bool` |  | True when the user has left the route entirely and the app should request a recalculation (build a new route + `NavigationEngine`). |

**Initialisers and methods**

- `public init(fix: LocationSample, update: GuidanceUpdate)`

#### `ReplayLocationProvider`

*Struct* · `Sources/MapMapKit/Navigation/ReplayLocationProvider.swift`

```swift
public struct ReplayLocationProvider: LocationProvider
```

Replays a `.drive.jsonl` corpus (the `sn-replay` QA format) as a
`LocationProvider`.

Fix timestamps in the corpus are relative to drive start; they are
rebased onto `epochStartMs` so guidance sees monotonic
wall-clock-like times.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var locations` | `AsyncThrowingStream<LocationSample, Error>` |  |  |

**Initialisers and methods**

- `public init(corpus: DriveCorpus, timeScale: Double = 1.0, epochStartMs: Int64 = 0)`: - Parameters: - corpus: a parsed corpus (see `DriveCorpus/parse(_:)`). - timeScale: playback speed: `1.0` = real time, `2.0` = twice as fast, `0.0` = emit every fix immediately (unit tests). - epochStartMs: epoch milliseconds assigned to the first fix.

#### `SignedChannelIndex`

*Struct* · `Sources/MapMapKit/Territory/ChannelSource.swift`

```swift
public struct SignedChannelIndex: Equatable, Sendable
```

The exact served bytes of a channel index and its detached signature.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let json` | `String` |  |  |
| `let signatureBase64` | `String` |  |  |

#### `SpeechSessionRefCount`

*Struct* · `Sources/MapMapKit/Navigation/VoiceGuidance.swift`

```swift
public struct SpeechSessionRefCount: Equatable, Sendable
```

Ref-count of in-flight utterances, deciding when the shared audio
session may be deactivated (so ducked music comes back up), pure and
unit-testable anywhere, like `VoiceProsody`. The platform wiring in
`VoiceGuidance` mutates it under a lock: `AVSpeechSynthesizer`
delegate callbacks arrive on an arbitrary queue.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var outstanding` | `Int` | `0` | Utterances handed to the synthesiser that have not yet finished or been cancelled. |

**Initialisers and methods**

- `public init()`
- `public mutating func willSpeak()`: Record one utterance handed to the synthesiser.
- `public mutating func didEnd() -> Bool`: Record one utterance finishing or being cancelled. Returns `true` when that was the last one, nothing is speaking or queued, so the audio session should be deactivated. Spurious callbacks after a `reset()` never trigger another deactivation.
- `public mutating func reset() -> Bool`: Clear everything (`stop()`). Returns `true` when there was anything outstanding, i.e. the session should be deactivated.

#### `VoiceProsody`

*Struct* · `Sources/MapMapKit/Navigation/VoiceGuidance.swift`

```swift
public struct VoiceProsody: Equatable, Sendable
```

Prosody and earcon policy per `InstructionSeverity`, pure and
unit-testable anywhere (the platform wiring lives in `VoiceGuidance`).

The core derives severity (see `InstructionSeverity` in
`crates/sn-nav-core`): `.info` for routine manoeuvres, `.warning` when
approaching a restriction (toll, ferry, and off-route), `.critical`
for prohibitions (ADR tunnel block, gate).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let rate` | `Float` | `` `AVSpeechUtterance` rate (0–1 scale; the platform default is `AVSpeechUtteranceDefaultSpeechRate`) `` | `AVSpeechUtterance` rate (0–1 scale; the platform default is `AVSpeechUtteranceDefaultSpeechRate`). |
| `let pitchMultiplier` | `Float` |  | Utterance pitch multiplier (1.0 = neutral). |
| `let interrupt` | `Bool` |  | Whether pending speech is stopped before this prompt. |

**Initialisers and methods**

- `public static func prosody(for severity: InstructionSeverity, baseRate: Float = AVSpeechUtteranceDefaultSpeechRate) -> VoiceProsody`: Prosody for `severity` at `baseRate` (defaults to the platform's default speech rate). Critical prompts speak slightly faster, clamped to the platform maximum.
- `public static func earconName(for severity: InstructionSeverity) -> String?`: The earcon resource name for `severity`, or `nil` when the severity plays none by default (info stays silent so a chime does not precede every routine manoeuvre). Assets are generated by `scripts/generate-earcons.py`.
- `public static func earconURL(for severity: InstructionSeverity) -> URL?`: URL of the earcon WAV inside MapMapKit's resource bundle, or `nil` for severities that play none.

### Enums (MapMapKit)

[`AdrCompliance`](#adrcompliance), [`ChannelSourceError`](#channelsourceerror), [`ChannelSync`](#channelsync), [`GuidanceDisplay`](#guidancedisplay), [`MapMap`](#mapmap), [`MapMapKitError`](#mapmapkiterror), [`OfflineRouting`](#offlinerouting), [`ProbeUploadOutcome`](#probeuploadoutcome), [`TerritoryKeys`](#territorykeys), [`ValhallaConfigWriter`](#valhallaconfigwriter)

#### `AdrCompliance`

*Enum* · `Sources/MapMapKit/Adr/AdrCompliance.swift`

```swift
public enum AdrCompliance
```

On-device ADR 8.6.4 dangerous-goods enforcement.

Two enforcement points, both running the same Rust domain model
(`sn-adr`, conservative ADR 8.6.4 reading):

1. **Pre-route**, pass the `AdrProfile` to
   `OfflineRouting/route(router:waypoints:costing:adr:)`; the core
   merges the restrictions into Valhalla truck costing, so forbidden
   tunnels and undersized roads are excluded before a route exists.
2. **En-route**, call `checkTunnel(profile:category:)` against a
   tunnel's category (e.g. from map data ahead of the vehicle) to gate
   entry while driving, and `forbiddenCategories(profile:)` to badge
   the whole profile in the UI.

**Initialisers and methods**

- `public static func checkTunnel(profile: AdrProfile, category: AdrTunnelCategory) -> AdrDecision`: May the vehicle described by `profile` pass through a tunnel of `category`? Blocked decisions carry a human-readable reason citing ADR 8.6.4.
- `public static func forbiddenCategories(profile: AdrProfile) -> [AdrTunnelCategory]`: All tunnel categories the vehicle is forbidden from, ascending. Empty when unrestricted.
- `public static func isTunnelRestricted(profile: AdrProfile) -> Bool`: True when the profile is barred from at least one tunnel category.
- `public static func validateProfile(_ profile: AdrProfile) -> [String]`: Validate a profile's physical plausibility before it goes anywhere near routing. Returns a list of human-readable problems; empty means valid. Pure Swift, safe to call from previews and tests without the native library.

#### `ChannelSourceError`

*Enum* · `Sources/MapMapKit/Territory/ChannelSource.swift`

```swift
public enum ChannelSourceError: Error, Equatable, CustomStringConvertible
```

Why a `ChannelSource` could not be built.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var description` | `String` |  |  |

| Case | Description |
| --- | --- |
| `notACustomRegion(String)` |  |
| `notAnIndexURL(String)` |  |
| `noRegionKey` | The store was built without a region key, so it refuses every custom region. |

#### `ChannelSync`

*Enum* · `Sources/MapMapKit/Territory/TerritoryStore.swift`

```swift
public enum ChannelSync: Sendable
```

What `TerritoryStore/sync(territoryId:indexJson:indexSigBase64:fetcher:omit:cancel:)` did.

| Case | Description |
| --- | --- |
| `installed(ChannelInstallResult)` | The territory was not installed; the channel's latest version now is. |
| `updated(UpdateResult)` | An older version was installed; it was updated in place. |
| `upToDate(TerritoryInfo)` | Already on the channel's latest version; nothing was downloaded. |
| `cancelled` | The `CancelToken` was tripped; nothing changed on disk. |

#### `GuidanceDisplay`

*Enum* · `Sources/MapMapKit/Navigation/NavigationState.swift`

```swift
public enum GuidanceDisplay
```

Pure display mappers for `GuidanceUpdate`. Kept free of native calls
so they are unit-testable anywhere.

**Initialisers and methods**

- `public static func instruction(_ update: GuidanceUpdate) -> String?`: The instruction to show for the current update, if any.
- `public static func visualBanner(_ update: GuidanceUpdate) -> VisualBanner?`: The visual banner for the upcoming manoeuvre, when one is active at the current distance (primary/secondary/sub lines with lane diagrams; render `primary.text` with the arrow named by `primary.maneuverType`/`maneuverModifier`).
- `public static func spokenPrompt(_ update: GuidanceUpdate) -> SpokenPrompt?`: The spoken prompt currently due. Hand `ssml ?? text` to `AVSpeechSynthesizer`, and de-duplicate by `utteranceId`, the same prompt is reported on every update until it is superseded.
- `public static func severity(_ update: GuidanceUpdate) -> InstructionSeverity`: How urgently the update should be surfaced (see the core's `InstructionSeverity` for the derivation). Off-route is a `.warning`, the driver has left the route and a recalculation is due; arrival is routine.
- `public static func snappedFix(_ update: GuidanceUpdate) -> SnappedFix?`: The route-snapped position and course for the current update, if any. Drive follow-cameras from this, e.g. feed it into a MapLibre course-tracking camera (`MapViewCamera.automotiveNavigation()` style: course-up, zoom 16, pitch 45), never the raw fix, which jitters and drifts off the carriageway.
- `public static func speedLimit(_ update: GuidanceUpdate) -> SpeedLimit?`: The posted speed limit for the current edge, when the route carries a `maxspeed` annotation at the current position. Render a speed-limit badge from `.known(kmh:)`, and compare the fix's speed against it for an over-limit warning. `nil` with stock map data (no annotation); `.unlimited`/`.unknown` distinguish an explicit no-limit from an explicitly-unvalued annotation.
- `public static func formatDistance(_ metres: Double) -> String`: Format a distance in metres for guidance display: metres under 1 km (10 m steps under 100 m, 50 m steps above), kilometres with one decimal from 1 km.
- `public static func summarise(_ update: GuidanceUpdate) -> String`: One-line status suitable for a debug HUD or live activity.

#### `MapMap`

*Enum* · `Sources/MapMapKit/MapMapKit.swift`

```swift
public enum MapMap
```

Entry point for the MapMap iOS SDK.

The SDK is a thin, idiomatic Swift layer over the Rust navigation core
(`sn-nav-core`, linked as a static library inside
`MapMapFFI.xcframework`). The raw UniFFI bindings, the
`mapmap` component and the `ferrostar` component, which is
compiled into the same library, live in `Generated/`; everything else
in this module is the supported surface:

- `TerritoryStore`, signed offline territory packages
  (install / verify / activate / update planning), actor-isolated.
- `OfflineRouting`, on-device Valhalla routing with ADR
  dangerous-goods enforcement merged into every request.
- `AdrCompliance`, pre-route and en-route ADR 8.6.4 tunnel checks.
- `NavigationEngine`, turn-by-turn guidance over a computed route,
  fed by any `LocationProvider` (`CoreLocationProvider` live,
  `ReplayLocationProvider` for QA corpora).

**Initialisers and methods**

- `public static func ensureLoaded()`: Eagerly verify that the bindings match the linked native core. Calling this is optional, the first native call performs the same UniFFI contract check lazily, but doing it at app start surfaces packaging problems (mismatched binding checksums, wrong framework slice) as an immediate, attributable crash instead of a mid-drive one.
- `public static func defaultGuidanceConfig() -> GuidanceConfig`: A `GuidanceConfig` with the core's documented defaults.
- `public static func defaultAdrProfile() -> AdrProfile`: An `AdrProfile` with the core's documented defaults: EU maximum authorised dimensions (Council Directive 96/53/EC), no dangerous goods.

#### `MapMapKitError`

*Enum* · `Sources/MapMapKit/MapMapKit.swift`

```swift
public enum MapMapKitError: Error, Equatable, LocalizedError
```

Errors raised by the Swift layer itself, before a call ever crosses
into the Rust core (which throws `NavCoreError`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `incompatibleCosting(costing: CostingModel)` | An ADR profile was supplied with a costing model other than `CostingModel/truck`. |
| `invalidArgument(String)` | A caller-supplied argument failed Swift-side validation. |

#### `OfflineRouting`

*Enum* · `Sources/MapMapKit/Routing/OfflineRouting.swift`

```swift
public enum OfflineRouting
```

On-device route computation.

The Rust core builds the Valhalla request (merging ADR dangerous-goods
and dimensional costing options, rejecting conflicts), the injected
`OfflineRouter` executes it against local tiles, and the core parses
the response into a `RouteResult` whose `valhallaResponseJson` feeds
`NavigationEngine`.

**Initialisers and methods**

- `public static func requireCompatibleCosting(costing: CostingModel, adr: AdrProfile?) throws`: ADR profiles ride on Valhalla truck costing options; requesting any other costing model with an ADR profile is a contract violation the Rust core rejects. Checked here too so the failure is a fast, clearly-attributed `MapMapKitError/incompatibleCosting(costing:)` rather than a routing error after the FFI hop.
- `public static func route(router: LocalRouter, waypoints: [RoutePoint], costing: CostingModel = .auto, adr: AdrProfile? = nil) async throws -> RouteResult`: Compute a route on-device. Routing is CPU-bound native work; it runs on a detached utility task, never on the caller's executor. - Parameters: - router: the on-device engine, usually a `ValhallaMobileRouter`. - waypoints: at least two `RoutePoint`s, in visit order. - costing: the Valhalla costing model; must be `CostingModel/truck` when `adr` is supplied. - adr: optional dangerous-goods/dimensional profile, when set, the request is built with the restrictions merged in, so illegal roads and tunnels are excluded before the route exists. - Throws: `NavCoreError/Routing(message:)` when the engine returns a Valhalla error body (e.g. no route, no tiles for the area).
- `public static func buildRequest(waypoints: [RoutePoint], costing: CostingModel = .auto, adr: AdrProfile? = nil) throws -> String`: Build the raw Valhalla request JSON without executing it, for callers that drive the engine themselves or log requests for audit.
- `public static func parseResponse(_ responseJson: String) throws -> RouteResult`: Parse a raw engine response into a `RouteResult`.

#### `ProbeUploadOutcome`

*Enum* · `Sources/MapMapKit/Navigation/ProbeContribution.swift`

```swift
public enum ProbeUploadOutcome: Sendable, Equatable
```

The outcome of one upload attempt, surfaced through
`URLSessionProbeUploader`'s result callback for observability and tests.

| Case | Description |
| --- | --- |
| `accepted` | `202`, accepted and stored. |
| `refused` | `403`, the key is not opted in; dropped, not retried. |
| `notEnabled` | `501`, server collection is not enabled (`SN_PROBE_DIR` unset); dropped. |
| `rejected` | `400`/other 4xx, malformed or unacceptable; dropped, not retried. |
| `gaveUp` | Transport error or `5xx` after exhausting retries; given up on. |

#### `TerritoryKeys`

*Enum* · `Sources/MapMapKit/Territory/TerritoryKeys.swift`

```swift
public enum TerritoryKeys
```

The public halves of the keys that sign MapMap's hosted territory
packages, for pinning in an app. Both are ed25519 public keys as 64 hex
characters, the form `TerritoryStore` takes.

Pin them at build time; never read either from the network. An app that
runs its own factory or region signer passes its own keys instead.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let hostedFactoryKeyHex` |  |  | The factory key: verifies every country package on the hosted channel (`https://api.mapmap.ai/territories`). Also published at mapmap.ai/docs/territories. |
| `static let hostedRegionKeyHex` |  |  | The region key: verifies custom regions (`region-` ids) cut by the hosted gateway (`POST /v1/jobs/territory`). The core accepts it for `region-` ids only, and never the factory key for them, so a leaked region key cannot forge a country package. Pass it as `TerritoryStore(rootDir:verifyingKeyHex:regionKeyHex:)`. |

#### `ValhallaConfigWriter`

*Enum* · `Sources/MapMapKit/Routing/OfflineRouting.swift`

```swift
public enum ValhallaConfigWriter
```

Writes the `valhalla.json` the on-device Valhalla engine needs.

The document is Valhalla's own complete default config
(`valhalla_build_config`, MIT,
https://github.com/valhalla/valhalla/blob/master/scripts/valhalla_build_config),
bundled as `Resources/valhalla-defaults.json`, with the tile source and
a mobile-sized cache set on top. The engine reads many keys (every
`service_limits` section, `loki`, `thor`, `meili`) with no compiled-in
fallback: a config carrying only `mjolnir` makes every route fail with
"No such node (service_limits.isochrone.max_contours)", which is what
this writer produced before (found on an Android device; the Android
writer bundles the same file).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let defaultMaxCacheBytes` | `Int64` | `268_435_456` | Mobile default for `mjolnir.max_cache_size` (bytes): 256 MiB. |

**Initialisers and methods**

- `public static func configJSON(tileDir: URL, maxCacheBytes: Int64 = defaultMaxCacheBytes) throws -> String`: Render the config document for a routing-tile directory (the territory's `MapLayerKind/valhallaTiles` layer).

### Protocols (MapMapKit)

[`LocationProvider`](#locationprovider), [`OfflineRouter`](#offlinerouter), [`ProbeUploader`](#probeuploader)

#### `LocationProvider`

*Protocol* · `Sources/MapMapKit/Navigation/LocationProvider.swift`

```swift
public protocol LocationProvider: Sendable
```

A source of location fixes for `NavigationEngine`.

Implementations: `CoreLocationProvider` (Core Location) for live
navigation, `ReplayLocationProvider` (a `.drive.jsonl` corpus) for
QA and demos. Any stream of samples in ascending timestamp order
qualifies.

The stream is cold: every access to `locations` starts a fresh
sequence, and it is consumed once per navigation session.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var locations` | `AsyncThrowingStream<LocationSample, Error>` |  | A fresh stream of fixes. Finishes when the source is exhausted (replay) or throws on unrecoverable platform errors (live). |

#### `OfflineRouter`

*Protocol* · `Sources/MapMapKit/Routing/OfflineRouting.swift`

```swift
public protocol OfflineRouter: LocalRouter
```

An on-device routing engine.

This is the Rust core's `LocalRouter` callback interface with a Swift
name: implementations execute a Valhalla turn-by-turn request JSON
synchronously and return the raw response JSON (either a route
response or a Valhalla error body, the core parses and classifies
both).

The production implementation is `ValhallaMobileRouter` in the
`MapMapValhalla` product (iOS only, it wraps the
valhalla-mobile engine); tests inject fakes that return canned
responses.

#### `ProbeUploader`

*Protocol* · `Sources/MapMapKit/Navigation/ProbeContribution.swift`

```swift
public protocol ProbeUploader: Sendable
```

Where a completed probe batch goes. Called off the guidance stream, so
`enqueue(_:)` must not block and must never throw.

**Initialisers and methods**

- `func enqueue(_ body: String)`: Hand one probe body, the string from `ProbeCollector.finishJson`, off to be POSTed to `/v1/probe`. Fire-and-forget.

### Functions (MapMapKit)

[`contributingProbe`](#contributingprobe)

#### `contributingProbe`

*Function* · `Sources/MapMapKit/Navigation/ProbeContribution.swift`

```swift
public func contributingProbe(_ upstream: AsyncThrowingStream<NavigationState, Error>, config: ProbeConfig, uploader: ProbeUploader, routeRef: String? = nil, territory: String? = nil, vehicleClass: String? = nil, now: @escaping @Sendable () -> Int64 = { Int64(Date().timeIntervalSince1970 * 1000) }) -> AsyncThrowingStream<NavigationState, Error>
```

Accumulate an aggregate probe batch from a guidance stream and upload it
when the trip ends. **A transparent pass-through when `config.enabled` is
false** (the shipped default): no collector is constructed and nothing is
uploaded, so leaving collection off costs nothing.

Wrap it around `NavigationEngine/navigate(provider:)`'s stream:
``swift
let stream = contributingProbe(
    await engine.navigate(provider: provider),
    config: ProbeConfig(enabled: consentGranted),
    uploader: URLSessionProbeUploader(baseURL: "https://api.mapmap.ai", apiKey: key),
    routeRef: servedRouteToken, territory: "uk", vehicleClass: "truck_40t"
)
for try await state in stream { render(state) }
``

Consent is the caller's responsibility: only pass `enabled: true` for a key
whose operator has set `probe_opt_in` and, for a consumer app, with the
user's opt-in consent. See `docs/PROBE-SDK-INTEGRATION.md`.

Only aggregates leave the device: the collector cannot emit a trajectory,
and the first/last stretch of every trip is discarded on-device.

- Parameters:
  - routeRef: opaque served-route token so the server can resolve segment
    indices.
  - vehicleClass: a costing class only, never a registration, VIN or
    driver id.
  - now: injectable clock (Unix millis) for deterministic tests.

### MapMap core bindings (generated)

#### Classes (MapMap core bindings (generated))

[`CancelToken`](#canceltoken), [`GuidanceSession`](#guidancesession), [`LayerFetcherImpl`](#layerfetcherimpl), [`LocalRouterImpl`](#localrouterimpl), [`ProbeCollector`](#probecollector), [`TerritoryManager`](#territorymanager), [`TerritorySearch`](#territorysearch)

##### `CancelToken`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class CancelToken: CancelTokenProtocol, @unchecked Sendable
```

A cancellation handle for an in-flight territory install or update.

The install calls on [`TerritoryManager`](#territorymanager) block the calling thread for
as long as a multi-gigabyte package takes to download, unpack and
verify. Create a token, pass it to one of the `_cancellable` variants
running on a background thread, and call [`CancelToken::cancel`] from
the UI thread when the user taps Cancel. The install polls it at safe
points and returns [`InstallOutcome::Cancelled`] /
[`UpdateOutcome::Cancelled`] with its staging directory removed: no
partial package is left on disk, the previously installed version is
untouched, and layers that had already been verified stay in the
resumable cache for the next attempt. Cancelling after the call has
returned changes nothing.

The token cannot interrupt a [`LayerFetcher`](#layerfetcher) blocked in a transfer:
give the fetcher the same token and have it poll
[`CancelToken::is_cancelled`] between chunks (or abort its request when
the app cancels). Whatever the fetcher then returns, the install
reports `Cancelled`, not a transport error.

**Initialisers and methods**

- `public convenience init()`: A fresh, uncancelled token.
- `open func cancel()`: Request cancellation. Idempotent and safe from any thread.
- `open func isCancelled() -> Bool`: Whether [`Self::cancel`] has been called.

##### `GuidanceSession`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class GuidanceSession: GuidanceSessionProtocol, @unchecked Sendable
```

A turn-by-turn guidance session (see [`crate::guidance`]).

Thread-safe: the mutable Ferrostar navigation state is guarded by a
mutex, matching UniFFI's shared-object concurrency model.

**Initialisers and methods**

- `public convenience init(routeResponseJson: String, config: GuidanceConfig) throws`: Create a session from a Valhalla route response JSON (as returned by `route_offline` in `valhalla_response_json`).
- `open func advanceToNextStep() throws -> GuidanceUpdate`: Manually advance to the next step (e.g. in a tunnel with no GNSS).
- `open func totalSteps() -> UInt32`: Number of steps in the route, including the arrival step.
- `open func updateLocation(lat: Double, lon: Double, timestampMs: Int64, speedMps: Double?, courseDeg: Double?, horizontalAccuracyM: Double?) throws -> GuidanceUpdate`: Feed a location fix; returns the updated guidance state. `horizontal_accuracy_m` defaults to `0.0` (trusted exactly) when absent; pass the platform's reported accuracy when available so step advance and deviation gating behave correctly.

##### `LayerFetcherImpl`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class LayerFetcherImpl: LayerFetcher, @unchecked Sendable
```

The platform's download transport for over-the-air territory updates,
exported as a UniFFI callback interface like
[`crate::routing::LocalRouter`]: the core decides *what* to fetch and
cryptographically verifies everything; Kotlin/Swift decide *how* bytes
move (OkHttp, URLSession, an air-gapped mirror, …).

**Initialisers and methods**

- `open func fetch(relPath: String, expectedBlake3: String, destPath: String) throws`: Download the channel-relative `rel_path` (e.g. `territories/uk/2.0.0/layers/<addr>/valhalla.tar.zst`) to the local file `dest_path`. `expected_blake3` is advisory (empty for signed metadata files); the core re-verifies all content itself.

##### `LocalRouterImpl`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class LocalRouterImpl: LocalRouter, @unchecked Sendable
```

The injection point for the platform's on-device Valhalla binding.

valhalla-mobile (<https://github.com/Rallista/valhalla-mobile>) exposes
a `route(request_json) -> response_json` actor on both Android and iOS;
Kotlin/Swift implement this interface with that actor and pass it to
[`crate::ffi::route_offline`]. Exported as a UniFFI callback interface
(`with_foreign`) so the implementation lives in foreign code.

**Initialisers and methods**

- `open func route(requestJson: String) -> String`: Execute a Valhalla turn-by-turn request and return the raw response JSON (either a route response or a Valhalla error body).

##### `ProbeCollector`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class ProbeCollector: ProbeCollectorProtocol, @unchecked Sendable
```

On-device probe collector. Feed it every guidance update with the fix's
position, speed and timestamp; call [`finish`](ProbeCollector::finish) at
arrival or trip abandonment to get the [`ProbeBatch`](#probebatch) to upload.

Interior mutability (a `Mutex`) lets the FFI methods take `&self`, matching
the [`crate::guidance::GuidanceSession`] object shape.

**Initialisers and methods**

- `public convenience init(config: ProbeConfig)`: Create a collector with the given configuration.
- `open func finish(arrived: Bool, timestampMs: Int64) -> ProbeBatch`: Close the trip and return the batch to upload. `arrived` distinguishes a completed trip from an abandoned one (both apply end suppression).
- `open func finishJson(arrived: Bool, timestampMs: Int64) -> String?`: Close the trip and return the exact JSON body to `POST /v1/probe`, or `None` when the batch carries nothing worth uploading. This is the single canonical serialiser for the probe wire format, platform code should upload this verbatim rather than re-encoding the record, so the wire form can never drift from what the gateway ingests.
- `open func observe(update: GuidanceUpdate, fixLat: Double, fixLon: Double, speedMps: Double?, timestampMs: Int64)`: Feed one guidance update and its fix. `speed_mps` is the device's reported ground speed when available (preferred); pass `None` to let the collector estimate it from successive positions.
- `open func setRouteRef(routeRef: String)`: Set the opaque served-route token so the server can resolve segments.
- `open func setTerritory(territory: String)`: Set the territory id stamped into the batch.
- `open func setVehicleClass(vehicleClass: String)`: Set the vehicle costing class stamped into the batch (a class only, never a vehicle or driver identifier).

##### `TerritoryManager`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class TerritoryManager: TerritoryManagerProtocol, @unchecked Sendable
```

Manages signed offline territory packages (see [`crate::territory`]).

**Initialisers and methods**

- `public convenience init(rootDir: String, verifyingKeyHex: String) throws`: Open a manager rooted at `root_dir`, trusting packages signed by the ed25519 public key given as 64 hex characters (the factory signing key's public half, [`sn_package::VerifyingKey::to_hex`]).
- `public static func withRegionKey(rootDir: String, verifyingKeyHex: String, regionKeyHex: String) throws -> TerritoryManager`: [`Self::new`], also trusting custom regions (`region-…` ids, cut by `POST /v1/jobs/territory`) signed by `region_key_hex`, the public half of the gateway's region signing key. The keys are partitioned by id: the region key verifies `region-` ids only and the factory key everything else. Pin both in the app; never read either from the network.
- `open func active() throws -> String?`: The active territory id, if one is set and still installed.
- `open func applyUpdate(territoryId: String, newManifestJson: String, newManifestSigBase64: String, fetcher: LayerFetcher) throws -> UpdateResult`: Apply a differential update: stage, fetch only the changed layers through `fetcher`, verify everything and swap atomically. Any failure leaves the installed territory untouched; the active flag is preserved. Blocks until done and cannot be interrupted; see [`Self::apply_update_cancellable`].
- `open func applyUpdateCancellable(territoryId: String, newManifestJson: String, newManifestSigBase64: String, fetcher: LayerFetcher, cancel: CancelToken) throws -> UpdateOutcome`: [`Self::apply_update`] with a [`CancelToken`](#canceltoken). Polled before every layer download, between archive entries while unpacking, before each hash pass and before promotion. A tripped token yields [`UpdateOutcome::Cancelled`]: the installed version is untouched, staging is removed, and layers that had already been fetched and verified stay in the resumable cache, so calling again with the same manifest re-fetches only what is missing. Give `fetcher` the same token so it can stop an in-flight transfer (the token cannot interrupt foreign code on its own); whatever the fetcher then returns, a tripped token reports `Cancelled` rather than a transport error.
- `open func cachedLayerBytes() throws -> UInt64`: Bytes parked in the resumable layer cache: downloads a failed install already paid for, which a retry will not pay for again. Surface this next to a "resume" affordance and in storage settings.
- `open func checkForUpdate(territoryId: String, indexJson: String, indexSigBase64: String, fetcher: LayerFetcher) throws -> AvailableUpdate?`: Check the signed channel index for an update to an installed territory. `index_json` must be the exact bytes served as `index.json` and `index_sig_base64` the content of `index.json.sig`; the remote manifest is fetched and verified through `fetcher`. Returns `None` when the device is already on the channel's latest version (or the channel does not list the territory).
- `open func clearLayerCache() throws`: Discard every parked layer. The bytes are lost and a later install re-downloads (and on a metered channel re-pays for) them, so offer this only as a deliberate "free up space" action.
- `open func installDir(packageDir: String) throws -> TerritoryInfo`: Install an unpacked package directory. Blocks until done and cannot be interrupted; see [`Self::install_dir_cancellable`].
- `open func installDirCancellable(packageDir: String, cancel: CancelToken) throws -> InstallOutcome`: [`Self::install_dir`] with a [`CancelToken`](#canceltoken). Polled between copied files, before verification and before promotion.
- `open func installDirPartial(packageDir: String, omit: [MapLayerKind], cancel: CancelToken) throws -> InstallOutcome`: Install an unpacked package directory, holding only the layers this app can use: `omit` names the kinds to leave out (e.g. `Pmtiles` and `Extra("style")` for an app with no map view). The install records what it left out, and every later verification requires those layers to be **absent** rather than exempting them from hashing, so the record cannot be forged into admitting tampered bytes. Asking for an omitted layer answers [`NavCoreError::LayerNotInstalled`], which is not an error state: it is "you chose not to download this". The selection is sticky: updates carry it forward, so an app that skipped the render layers never downloads them on an update either.
- `open func installFromChannel(territoryId: String, indexJson: String, indexSigBase64: String, omit: [MapLayerKind], fetcher: LayerFetcher, cancel: CancelToken) throws -> ChannelInstallOutcome`: Install a territory **from the channel**, downloading only the layers this app can use. The first-install counterpart of [`Self::apply_update`], and the only path on which omitting layers saves bandwidth: a layer in `omit` is never requested, so it is never metered and never stored. `index_json` / `index_sig_base64` are the exact served bytes of `index.json` and `index.json.sig`; the channel's latest version of the territory is installed. Nothing is trusted but the pinned key: the index signature, the manifest signature and every blob's BLAKE3 are checked, and the assembled directory goes through the same full verification pass as any other install before it is promoted. Interrupted installs resume: verified layers stay parked in the layer cache. Calling it again for a territory that is already installed is how a selection is widened later. Layers already on the device at the same content hash are linked, not re-downloaded, so adding the map to a router-only install costs the map alone.
- `open func installSnpkg(archivePath: String) throws -> TerritoryInfo`: Install a packed `.snpkg` archive (verify-then-promote; rejected packages leave no trace). Blocks until done and cannot be interrupted; see [`Self::install_snpkg_cancellable`].
- `open func installSnpkgCancellable(archivePath: String, cancel: CancelToken) throws -> InstallOutcome`: [`Self::install_snpkg`] with a [`CancelToken`](#canceltoken). Polled between archive entries, before verification and before promotion; a tripped token yields [`InstallOutcome::Cancelled`] with staging removed and nothing installed.
- `open func installSnpkgPartial(archivePath: String, omit: [MapLayerKind], cancel: CancelToken) throws -> InstallOutcome`: [`Self::install_dir_partial`] from a packed `.snpkg` archive. The archive carries every layer, so this saves storage, not bandwidth: the omitted layers are unpacked and then dropped before verification. For the bandwidth saving use [`Self::install_from_channel`], which never fetches a blob it does not want.
- `open func layerPath(territoryId: String, kind: MapLayerKind) throws -> String`: Absolute path of a layer inside an installed territory (e.g. the Valhalla tile tree for valhalla-mobile, the PMTiles file for MapLibre).
- `open func list() throws -> [TerritoryInfo]`: List installed territories in territory-id order.
- `open func openSearch(territoryId: String) throws -> TerritorySearch`: Open the installed territory's bundled geocode index for on-device search. The returned [`TerritorySearch`](#territorysearch) holds the index open (mmap-backed), create it once per territory and reuse it across queries.
- `open func planUpdate(newManifestJson: String) throws -> UpdatePlan`: Plan a differential update from the installed manifest to `new_manifest_json` (a signed manifest's JSON body, as served by the update channel).
- `open func remove(territoryId: String) throws`: Remove an installed territory (clears the active marker if needed).
- `open func setActive(territoryId: String) throws`: Mark a territory as active (persisted atomically).
- `open func territoryStyle(territoryId: String, theme: StyleTheme) throws -> String`: A complete MapLibre style JSON for an installed territory with **local** (offline) references, mirroring the web SDK's `buildStyle()`. Load it straight into MapLibre, the territory tile source points at the installed PMTiles layer via a `pmtiles://<absolute path>` URL, so the map renders with radios off. Packages without a baked style layer get the default MapMap theme compiled on the fly over the package's PMTiles layer. The `glyphs` endpoint stays hosted (MapLibre caches fetched glyph ranges; no offline glyph bundle ships in packages today).
- `open func verifyTerritory(territoryId: String) throws -> TerritoryInfo`: Re-verify an installed territory from disk: the manifest signature, the BLAKE3 of every layer this install holds, and the absence of every layer it recorded as omitted. The ordinary calls do not re-hash gigabytes; this is the on-demand integrity check for after a restore or a crash. A failure arrives as [`NavCoreError::Verification`] and means the install must be repaired or reinstalled, not retried.

##### `TerritorySearch`

*Class* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
open class TerritorySearch: TerritorySearchProtocol, @unchecked Sendable
```

On-device search over an installed territory's bundled geocode index
(see [`TerritoryManager::open_search`]). Fully offline; queries are
synchronous and mmap-backed, call from a background thread/dispatcher
like any other disk-bound SDK call.

**Initialisers and methods**

- `open func reverse(lat: Double, lon: Double, limit: UInt32) throws -> [Place]`: Reverse geocode: the nearest indexed places to a coordinate, nearest first.
- `open func search(query: String, near: RoutePoint?, limit: UInt32) throws -> [Place]`: Free-text search (addresses, streets, places, POIs, UK postcodes; prefix-tokenised, so it works for search-as-you-type). `near` biases ranking towards the given point and fills each result's `distance_m`; `limit` caps the number of hits.

#### Structs (MapMap core bindings (generated))

[`AdrProfile`](#adrprofile), [`AvailableUpdate`](#availableupdate), [`BannerLine`](#bannerline), [`ChangedLayer`](#changedlayer), [`ChannelInstallResult`](#channelinstallresult), [`FasterRouteCandidate`](#fasterroutecandidate), [`FasterRouteConfig`](#fasterrouteconfig), [`FasterRouteRecommendation`](#fasterrouterecommendation), [`GuidanceConfig`](#guidanceconfig), [`LaneGuidance`](#laneguidance), [`OdRecord`](#odrecord), [`Place`](#place), [`ProbeBatch`](#probebatch), [`ProbeConfig`](#probeconfig), [`RerouteAnchor`](#rerouteanchor), [`RerouteConfig`](#rerouteconfig), [`RerouteDecision`](#reroutedecision), [`RerouteWaypoint`](#reroutewaypoint), [`RouteManeuver`](#routemaneuver), [`RoutePoint`](#routepoint), [`RouteResult`](#routeresult), [`RouteSummary`](#routesummary), [`SegmentAggregate`](#segmentaggregate), [`SnappedFix`](#snappedfix), [`SpokenPrompt`](#spokenprompt), [`TerritoryInfo`](#territoryinfo), [`UpdatePlan`](#updateplan), [`UpdateResult`](#updateresult), [`VisualBanner`](#visualbanner)

##### `AdrProfile`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct AdrProfile: Equatable, Hashable
```

Vehicle profile for ADR and dimensional enforcement, mirroring
[`sn_adr::AdrVehicleProfile`]. Defaults are the EU maximum authorised
dimensions (Council Directive 96/53/EC) with no dangerous goods.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var heightM` | `Double` |  | Vehicle height in metres. |
| `var widthM` | `Double` |  | Vehicle width in metres. |
| `var lengthM` | `Double` |  | Vehicle length in metres. |
| `var grossWeightT` | `Double` |  | Gross combination weight in metric tonnes. |
| `var axleLoadT` | `Double?` |  | Heaviest single-axle load in tonnes, if known. |
| `var axleCount` | `UInt32?` |  | Number of axles, if known. |
| `var hazmat` | `Bool` |  | Whether the vehicle carries dangerous goods at all. |
| `var tunnelCode` | `AdrTunnelCode?` |  | Declared ADR tunnel restriction code. `None` with `hazmat = true` means "dangerous goods of unknown code" and is treated as code B (most restrictive non-quantity code). |

**Initialisers and methods**

- `public init(heightM: Double = Double(4.0), widthM: Double = Double(2.55), lengthM: Double = Double(16.5), grossWeightT: Double = Double(40.0), axleLoadT: Double? = nil, axleCount: UInt32? = nil, hazmat: Bool = false, tunnelCode: AdrTunnelCode? = nil)`

##### `AvailableUpdate`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct AvailableUpdate: Equatable, Hashable
```

An update the channel offers for an installed territory.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var version` | `String` |  | The channel's latest version for the territory. |
| `var changedLayers` | `[ChangedLayer]` |  | Layers that must be downloaded. |
| `var installedBytes` | `UInt64` |  | **Uncompressed** total of the changed layers, on-device disk, not metered wire bytes. Same warning as [`UpdatePlan::installed_bytes`]. |
| `var manifestJson` | `String` |  | The remote manifest's exact JSON text; pass verbatim to [`TerritoryManager::apply_update`]. |
| `var manifestSig` | `String` |  | The remote manifest's detached base64 ed25519 signature. |

**Initialisers and methods**

- `public init(version: String, changedLayers: [ChangedLayer], installedBytes: UInt64, manifestJson: String, manifestSig: String)`

##### `BannerLine`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct BannerLine: Equatable, Hashable
```

One line of a visual banner (primary/secondary/sub).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var text` | `String` |  | Text to display. |
| `var maneuverType` | `String?` |  | Standardised maneuver type for the arrow glyph (e.g. `turn`). |
| `var maneuverModifier` | `String?` |  | Standardised maneuver modifier (e.g. `right`). |
| `var roundaboutExitDegrees` | `UInt16?` |  | Degrees travelled around a roundabout before exiting, when known. |
| `var exitNumbers` | `[String]` |  | Exit numbers (e.g. `8B`), when signposted. |
| `var lanes` | `[LaneGuidance]` |  | Lane diagram entries (usually only on sub banners). |

**Initialisers and methods**

- `public init(text: String, maneuverType: String?, maneuverModifier: String?, roundaboutExitDegrees: UInt16?, exitNumbers: [String], lanes: [LaneGuidance])`

##### `ChangedLayer`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct ChangedLayer: Equatable, Hashable
```

One layer that a territory update must download.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var kind` | `String` |  | The layer kind's kebab-case string. |
| `var path` | `String` |  | Path of the layer relative to the package root. |
| `var bytes` | `UInt64` |  | Layer size in bytes. |

**Initialisers and methods**

- `public init(kind: String, path: String, bytes: UInt64)`

##### `ChannelInstallResult`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct ChannelInstallResult: Equatable, Hashable
```

What [`TerritoryManager::install_from_channel`] did.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var territory` | `TerritoryInfo` |  | The installed territory. |
| `var layersFetched` | `UInt32` |  | How many layer blobs were downloaded on this attempt. |
| `var layersReused` | `UInt32` |  | How many layers cost nothing: already on the device at the same content hash, or parked by an attempt that failed before it could promote. |
| `var bytesDownloaded` | `UInt64` |  | Bytes downloaded on this attempt. Compressed blob bytes on the wire, so directly comparable with a metering allowance, unlike `installedBytes`, which is uncompressed disk. |

**Initialisers and methods**

- `public init(territory: TerritoryInfo, layersFetched: UInt32, layersReused: UInt32, bytesDownloaded: UInt64)`

##### `FasterRouteCandidate`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct FasterRouteCandidate: Equatable, Hashable
```

One candidate route for the remaining journey, summarised from the
router's response (a primary route or an alternate).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var id` | `String?` |  | Opaque identifier for the candidate (e.g. the response `id` or an alternate index), echoed back so the platform can find the route to switch to. Not interpreted by the evaluator. |
| `var durationS` | `Double` |  | The candidate's estimated duration in seconds for the remaining journey, from the same response baseline as the current-route remaining ETA (recompute both from one response to avoid drift). |

**Initialisers and methods**

- `public init(id: String?, durationS: Double)`

##### `FasterRouteConfig`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct FasterRouteConfig: Equatable, Hashable
```

Faster-route evaluator configuration, flattened for FFI like
[`crate::reroute::RerouteConfig`].

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var checkIntervalS` | `Double` |  | Seconds between checks. A check becomes due once this much time has elapsed since the last evaluation (or since the first observation). |
| `var minSavingS` | `UInt32` |  | Minimum absolute saving in seconds. Candidates saving less are never recommended. |
| `var minSavingFrac` | `Double` |  | Minimum saving as a fraction of the current remaining time (0..1). Both this and `min_saving_s` must pass. |
| `var dismissSuppressS` | `Double` |  | Seconds a dismissal suppresses further recommendations. Within the window only a saving at least double the dismissed one re-prompts. |
| `var maneuverBlackoutS` | `Double` |  | Seconds-to-next-manoeuvre under which a due check is deferred; the check fires on the first observation clear of the blackout. |
| `var enabled` | `Bool` |  | Whether the evaluator is active at all. When `false`, checks are never due and evaluations never recommend. |

**Initialisers and methods**

- `public init(checkIntervalS: Double = Double(120.0), minSavingS: UInt32 = UInt32(180), minSavingFrac: Double = Double(0.08), dismissSuppressS: Double = Double(600.0), maneuverBlackoutS: Double = Double(60.0), enabled: Bool = true)`

##### `FasterRouteRecommendation`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct FasterRouteRecommendation: Equatable, Hashable
```

A recommendation to switch to a faster candidate route.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var savesS` | `UInt32` |  | Estimated saving in seconds versus staying on the current route. |
| `var candidateIdx` | `UInt32` |  | Index into the candidate slice passed to [`FasterRouteEvaluator::evaluate`] of the winning candidate. |

**Initialisers and methods**

- `public init(savesS: UInt32, candidateIdx: UInt32)`

##### `GuidanceConfig`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct GuidanceConfig: Equatable, Hashable
```

Guidance behaviour configuration, flattened for FFI.

Maps onto Ferrostar's `NavigationControllerConfig`: a
distance-to-end-of-step advance condition, a static-threshold route
deviation tracker and course snapping.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var stepAdvanceDistanceM` | `UInt16` |  | Advance to the next step when the user is within this many metres of the end of the current step. |
| `var minimumHorizontalAccuracyM` | `UInt16` |  | Location fixes with worse horizontal accuracy than this (metres) can neither advance a step nor flag a deviation. |
| `var routeDeviationThresholdM` | `Double` |  | The user is off route when further than this many metres from the route line. |
| `var snapCourseToRoute` | `Bool` |  | Snap the reported course to the route line (`true`, recommended for vehicular navigation) or report the raw course (`false`). |
| `var waypointAdvanceRangeM` | `Double` |  | A waypoint is considered visited within this many metres. |

**Initialisers and methods**

- `public init(stepAdvanceDistanceM: UInt16 = UInt16(20), minimumHorizontalAccuracyM: UInt16 = UInt16(25), routeDeviationThresholdM: Double = Double(25.0), snapCourseToRoute: Bool = true, waypointAdvanceRangeM: Double = Double(100.0))`

##### `LaneGuidance`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct LaneGuidance: Equatable, Hashable
```

One lane at the upcoming maneuver, for lane-diagram UIs.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var directions` | `[String]` |  | Turn directions this lane allows (OSRM indication strings, e.g. `left`, `straight`, `slight right`). |
| `var active` | `Bool` |  | Whether guidance recommends this lane for the maneuver. |
| `var activeDirection` | `String?` |  | Which of `directions` to highlight when the lane serves several. |

**Initialisers and methods**

- `public init(directions: [String], active: Bool, activeDirection: String?)`

##### `OdRecord`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct OdRecord: Equatable, Hashable
```

Coarse origin/destination for a trip. Both cells are geohashes of
*suppressed* (mid-trip) positions, so they are already
[`ProbeConfig::trip_end_suppression_m`] away from the true ends.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var originCell` | `String` |  | Geohash cell of the first committed position. |
| `var destCell` | `String` |  | Geohash cell of the last committed position. |
| `var departBucket` | `String` |  | Coarse departure bucket, e.g. `tue_am_peak`. |

**Initialisers and methods**

- `public init(originCell: String, destCell: String, departBucket: String)`

##### `Place`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct Place: Equatable, Hashable
```

One on-device search result: the matched document's display and
context fields plus its position.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var id` | `String` |  | Stable identifier (e.g. an OSM-derived id). |
| `var kind` | `PlaceKind` |  | What kind of entity this is. |
| `var name` | `String` |  | Primary display name. |
| `var housenumber` | `String?` |  | House number, for [`PlaceKind::Address`] results. |
| `var street` | `String?` |  | Containing street name, for addresses and POIs. |
| `var locality` | `String?` |  | Containing town or city. |
| `var region` | `String?` |  | Containing county / region. |
| `var postcode` | `String?` |  | Postcode, formatted as displayed (e.g. `"SW1A 2AA"`). |
| `var countryCode` | `String` |  | ISO 3166-1 alpha-2 country code (e.g. `"GB"`). |
| `var lat` | `Double` |  | Latitude in decimal degrees (WGS84). |
| `var lon` | `Double` |  | Longitude in decimal degrees (WGS84). |
| `var categories` | `[String]` |  | POI categories (e.g. `"fuel"`, `"truck_stop"`). Empty for non-POIs. |
| `var importance` | `Float` |  | Rank boost in [0, 1]; larger means more prominent. |
| `var distanceM` | `Double?` |  | Haversine distance in metres from the query's `near` point, when one was given. |

**Initialisers and methods**

- `public init(id: String, kind: PlaceKind, name: String, housenumber: String?, street: String?, locality: String?, region: String?, postcode: String?, countryCode: String, lat: Double, lon: Double, categories: [String], importance: Float, distanceM: Double?)`

##### `ProbeBatch`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct ProbeBatch: Equatable, Hashable
```

A completed trip's aggregates, ready for the platform layer to POST to the
probe ingest endpoint. Contains no trajectory.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var probeFormat` | `UInt32` |  | Wire-format version (`PROBE_FORMAT`). |
| `var territory` | `String?` |  | Territory id the trip was navigated in, when the caller supplies it. |
| `var vehicleClass` | `String?` |  | Vehicle costing class (e.g. `truck_40t`), a class only, never a registration, VIN or driver identifier. |
| `var routeRef` | `String?` |  | Opaque token for the served route, so the server can resolve `step_index` to stable edge / OSM way IDs. |
| `var segments` | `[SegmentAggregate]` |  | Per-segment aggregates that cleared suppression and the observation threshold. |
| `var events` | `[ProbeEvent]` |  | Coarse, time-bucketed events. |
| `var od` | `OdRecord?` |  | Coarse origin/destination, when [`ProbeConfig::collect_od`] is set and the trip was long enough to have committed positions. |

**Initialisers and methods**

- `public init(probeFormat: UInt32, territory: String?, vehicleClass: String?, routeRef: String?, segments: [SegmentAggregate], events: [ProbeEvent], od: OdRecord?)`

##### `ProbeConfig`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct ProbeConfig: Equatable, Hashable
```

Probe collector configuration, flattened for FFI like
[`crate::guidance::GuidanceConfig`].

Collection is **off by default**: `enabled` must be set explicitly (and, in
deployment, gated on the fleet customer's opt-in). Everything a batch
contains follows from these knobs.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var enabled` | `Bool` |  | Whether the collector aggregates at all. When `false`, [`ProbeCollector`](#probecollector) ignores every fix and [`ProbeCollector::finish`] returns an empty batch. |
| `var tripEndSuppressionM` | `Double` |  | Radius in metres around both trip ends within which observations are discarded (see the module's trip-end-suppression note). |
| `var minSegmentObservations` | `UInt32` |  | Minimum observation count for a segment cell to appear in the batch. |
| `var timeBucketMinutes` | `UInt32` |  | Width of the time-of-day bucket in minutes (the day-of-week prefix is always present); 5 gives the `tue_0745`-style buckets that line up with the factory's weekly historical-speed profiles. |
| `var odCellPrecision` | `UInt8` |  | Geohash precision (characters) for the origin/destination cells; 5 is roughly a 5 km cell. |
| `var collectOd` | `Bool` |  | Whether to emit the coarse origin/destination record at all. |

**Initialisers and methods**

- `public init(enabled: Bool = false, tripEndSuppressionM: Double = Double(500.0), minSegmentObservations: UInt32 = UInt32(1), timeBucketMinutes: UInt32 = UInt32(5), odCellPrecision: UInt8 = UInt8(5), collectOd: Bool = true)`

##### `RerouteAnchor`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RerouteAnchor: Equatable, Hashable
```

The point and course a recalculated route should depart from.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var lat` | `Double` |  | Latitude in decimal degrees (WGS84). |
| `var lon` | `Double` |  | Longitude in decimal degrees (WGS84). |
| `var courseDeg` | `Double?` |  | Course over ground in degrees clockwise from true north, when known. Threaded into the request as the anchor's Valhalla `heading`. |

**Initialisers and methods**

- `public init(lat: Double, lon: Double, courseDeg: Double?)`

##### `RerouteConfig`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RerouteConfig: Equatable, Hashable
```

Reroute controller configuration, flattened for FFI like
[`crate::guidance::GuidanceConfig`].

Both the count gate and the duration gate must pass for a reroute to be
confirmed: a burst of fixes arriving in a fraction of a second should not
out-vote the duration requirement, and a slow trickle of fixes should not
out-vote the count requirement.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var minConsecutiveOffrouteFixes` | `UInt32` |  | Minimum number of consecutive off-route fixes before a reroute is confirmed. A single off-route fix that recovers on the next update (a GPS blip) never reaches this count. |
| `var minOffrouteDurationS` | `Double` |  | Minimum time in seconds the vehicle must have been continuously off route before a reroute is confirmed. |
| `var minIntervalBetweenReroutesS` | `Double` |  | Minimum time in seconds between successive reroutes. While inside this window the controller reports [`RerouteState::CoolingDown`] instead of firing again, the platform is still applying the previous reroute. |
| `var enabled` | `Bool` |  | Whether the controller is active at all. When `false` it always reports [`RerouteState::Monitoring`] and never confirms a reroute. |

**Initialisers and methods**

- `public init(minConsecutiveOffrouteFixes: UInt32 = UInt32(3), minOffrouteDurationS: Double = Double(5.0), minIntervalBetweenReroutesS: Double = Double(10.0), enabled: Bool = true)`

##### `RerouteDecision`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RerouteDecision: Equatable, Hashable
```

A confirmed reroute: where to route from, where to, and why.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var reason` | `RerouteReason` |  | Why the reroute was requested. |
| `var anchor` | `RerouteAnchor` |  | The current position and course to route from. |
| `var waypointsRemaining` | `[RerouteWaypoint]` |  | The not-yet-visited waypoints, in visiting order (destination last). |

**Initialisers and methods**

- `public init(reason: RerouteReason, anchor: RerouteAnchor, waypointsRemaining: [RerouteWaypoint])`

##### `RerouteWaypoint`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RerouteWaypoint: Equatable, Hashable
```

One onward waypoint of the active route.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var lat` | `Double` |  | Latitude in decimal degrees (WGS84). |
| `var lon` | `Double` |  | Longitude in decimal degrees (WGS84). |
| `var isBreak` | `Bool` |  | Whether this is a `break` (leg boundary, with arrival/departure narration) rather than a pass-through `via`. |

**Initialisers and methods**

- `public init(lat: Double, lon: Double, isBreak: Bool)`

##### `RouteManeuver`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RouteManeuver: Equatable, Hashable
```

One maneuver of the primary route, in SI units with shape indices into
[`RouteResult::geometry`]. Parsed once by the core so integrators never
re-parse the raw Valhalla JSON themselves.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var instruction` | `String` |  | Written instruction, e.g. `Turn right onto Main Street`. |
| `var maneuverType` | `UInt32` |  | Numeric Valhalla maneuver type (0–43, exactly as tabulated in the public turn-by-turn API reference: 1–3 start, 4–6 destination, 9–16 turns, 26–27 roundabouts, …). |
| `var verbalPreTransitionInstruction` | `String?` |  | Spoken instruction immediately before the transition, when present. |
| `var verbalPostTransitionInstruction` | `String?` |  | Spoken instruction immediately after the transition, when present. |
| `var streetNames` | `[String]` |  | Street names along the maneuver (empty when unnamed). |
| `var distanceM` | `Double` |  | Maneuver length in metres. |
| `var durationS` | `Double` |  | Estimated maneuver time in seconds. |
| `var beginShapeIndex` | `UInt32` |  | Index into [`RouteResult::geometry`] where the maneuver starts. |
| `var endShapeIndex` | `UInt32` |  | Index into [`RouteResult::geometry`] where the maneuver ends. |
| `var roundaboutExitCount` | `UInt32?` |  | Roundabout exit ordinal, for roundabout maneuvers. |

**Initialisers and methods**

- `public init(instruction: String, maneuverType: UInt32, verbalPreTransitionInstruction: String?, verbalPostTransitionInstruction: String?, streetNames: [String], distanceM: Double, durationS: Double, beginShapeIndex: UInt32, endShapeIndex: UInt32, roundaboutExitCount: UInt32?)`

##### `RoutePoint`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RoutePoint: Equatable, Hashable
```

A route waypoint.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var lat` | `Double` |  | Latitude in decimal degrees (WGS84). |
| `var lon` | `Double` |  | Longitude in decimal degrees (WGS84). |

**Initialisers and methods**

- `public init(lat: Double, lon: Double)`

##### `RouteResult`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RouteResult: Equatable, Hashable
```

Result of an offline route computation.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var summary` | `RouteSummary` |  | SI summary of the primary route. |
| `var geometry` | `[RoutePoint]` |  | The primary route's full geometry (decoded polyline, ordered), ready for a MapLibre LineString. (Defaulted so foreign code that hand-builds records, e.g. test fixtures, stays source-compatible.) |
| `var maneuvers` | `[RouteManeuver]` |  | The primary route's maneuvers, flattened across legs, with shape indices into [`Self::geometry`]. |
| `var valhallaResponseJson` | `String` |  | The raw Valhalla response JSON (feed this to [`GuidanceSession::new`]). |
| `var osrmResponseJson` | `String` |  | OSRM v1 translation at polyline6 precision, for OSRM-speaking consumers (e.g. map matching debug tooling). |

**Initialisers and methods**

- `public init(summary: RouteSummary, geometry: [RoutePoint] = [], maneuvers: [RouteManeuver] = [], valhallaResponseJson: String, osrmResponseJson: String)`

##### `RouteSummary`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct RouteSummary: Equatable, Hashable
```

SI summary of a parsed route.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var distanceM` | `Double` |  | Total distance in metres. |
| `var durationS` | `Double` |  | Total travel time in seconds. |
| `var hasToll` | `Bool` |  | Whether the route crosses a toll. |
| `var hasHighway` | `Bool` |  | Whether the route uses a highway. |
| `var hasFerry` | `Bool` |  | Whether the route uses a ferry. |

**Initialisers and methods**

- `public init(distanceM: Double, durationS: Double, hasToll: Bool, hasHighway: Bool, hasFerry: Bool)`

##### `SegmentAggregate`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct SegmentAggregate: Equatable, Hashable
```

One aggregated road segment: statistics for a `step_index` within the
served route, in one day-of-week × time-of-day bucket.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var stepIndex` | `UInt32` |  | Zero-based step index within the served route (`route_ref`). |
| `var todBucket` | `String` |  | Day-of-week × time-of-day bucket, e.g. `tue_0745`. |
| `var observations` | `UInt32` |  | Number of fixes folded into this cell. |
| `var meanSpeedKmh` | `Double` |  | Mean speed over the cell, in km/h. |
| `var p85SpeedKmh` | `Double` |  | 85th-percentile speed over the cell, in km/h (exact up to `MAX_SPEED_SAMPLE` observations, reservoir-estimated beyond). |
| `var stoppedS` | `Double` |  | Seconds spent at or below `STOPPED_SPEED_MPS` in this cell. |
| `var limitDeltaKmh` | `Double?` |  | Mean signed delta between observed speed and the posted limit, in km/h (positive = over the limit). `None` when no fix in the cell carried a known [`SpeedLimit::Known`] limit. |

**Initialisers and methods**

- `public init(stepIndex: UInt32, todBucket: String, observations: UInt32, meanSpeedKmh: Double, p85SpeedKmh: Double, stoppedS: Double, limitDeltaKmh: Double?)`

##### `SnappedFix`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct SnappedFix: Equatable, Hashable
```

The route-snapped position for the current update.

This is Ferrostar's `snapped_user_location`: the raw fix projected
onto the route line, with the course snapped to the route direction
when [`GuidanceConfig::snap_course_to_route`] is on (the default).
Drive follow-cameras (course-up / chase views) from this, never the
raw fix, the raw GPS trace jitters and drifts off the carriageway.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var lat` | `Double` |  | Route-snapped latitude in decimal degrees (WGS84). |
| `var lon` | `Double` |  | Route-snapped longitude in decimal degrees (WGS84). |
| `var courseDeg` | `Double?` |  | Course over ground in degrees clockwise from true north, snapped to the route line when course snapping is enabled; `None` when Ferrostar reports no course for this update. |

**Initialisers and methods**

- `public init(lat: Double, lon: Double, courseDeg: Double?)`

##### `SpokenPrompt`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct SpokenPrompt: Equatable, Hashable
```

The spoken prompt to synthesise for the upcoming maneuver.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var text` | `String` |  | Plain text for the platform text-to-speech engine. |
| `var ssml` | `String?` |  | SSML form, preferred by TTS engines that support it. |
| `var triggerDistanceM` | `Double` |  | Metres before the maneuver at which to start speaking. |
| `var utteranceId` | `String` |  | Unique utterance id so platform layers can de-duplicate; the same prompt is reported on every update until it is superseded. |

**Initialisers and methods**

- `public init(text: String, ssml: String?, triggerDistanceM: Double, utteranceId: String)`

##### `TerritoryInfo`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct TerritoryInfo: Equatable, Hashable
```

Summary of one installed territory.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var territoryId` | `String` |  | Stable territory identifier, e.g. `"uk"`. |
| `var displayName` | `String` |  | Human-readable territory name. |
| `var version` | `String` |  | Package semver. |
| `var dataTimestamp` | `String` |  | OSM snapshot timestamp, RFC 3339. |
| `var west` | `Double` |  | Bounding box west edge (degrees). |
| `var south` | `Double` |  | Bounding box south edge (degrees). |
| `var east` | `Double` |  | Bounding box east edge (degrees). |
| `var north` | `Double` |  | Bounding box north edge (degrees). |
| `var totalBytes` | `UInt64` |  | Total size of all layers in the **published package**, in bytes, whether or not this install holds them. |
| `var installedBytes` | `UInt64` |  | Size of the layers this install actually holds, in bytes. Equal to [`Self::total_bytes`] for a complete install; smaller when layers were deliberately left out. This is the disk figure to show in a storage list. |
| `var omittedKinds` | `[MapLayerKind]` |  | Layer kinds the package has and this install deliberately does **not** hold. Empty for an ordinary complete install. Asking for one of these yields [`NavCoreError::LayerNotInstalled`]. |
| `var path` | `String` |  | Absolute path of the installed package directory. |
| `var active` | `Bool` |  | Whether this territory is the active one. |

**Initialisers and methods**

- `public init(territoryId: String, displayName: String, version: String, dataTimestamp: String, west: Double, south: Double, east: Double, north: Double, totalBytes: UInt64, installedBytes: UInt64, omittedKinds: [MapLayerKind], path: String, active: Bool)`

##### `UpdatePlan`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct UpdatePlan: Equatable, Hashable
```

A differential update plan.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var changed` | `[ChangedLayer]` |  | Layers that must be downloaded. |
| `var installedBytes` | `UInt64` |  | **Uncompressed** total of the changed layers: the disk the update adds on the device, not the bytes that cross the wire. Layers travel `tar + zstd`, so this overstates metered transfer for everything except PMTiles (already internally gzipped), do not build a download-cost prompt from it. |

**Initialisers and methods**

- `public init(changed: [ChangedLayer], installedBytes: UInt64)`

##### `UpdateResult`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct UpdateResult: Equatable, Hashable
```

What [`TerritoryManager::apply_update`] did.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var territory` | `TerritoryInfo` |  | The territory after the update. |
| `var previousVersion` | `String` |  | The version that was installed before the update. |
| `var layersFetched` | `UInt32` |  | How many layer blobs were downloaded on this attempt. |
| `var layersReused` | `UInt32` |  | How many changed layers were served from the resumable layer cache, having been fetched and verified by an earlier attempt that failed before it could promote. |
| `var bytesDownloaded` | `UInt64` |  | Bytes downloaded on this attempt (compressed blob bytes on the wire, so directly comparable with a metered allowance). |

**Initialisers and methods**

- `public init(territory: TerritoryInfo, previousVersion: String, layersFetched: UInt32, layersReused: UInt32, bytesDownloaded: UInt64)`

##### `VisualBanner`

*Struct* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public struct VisualBanner: Equatable, Hashable
```

The visual banner to display for the upcoming maneuver.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var primary` | `BannerLine` |  | Main line: what to do / where you're going. |
| `var secondary` | `BannerLine?` |  | Optional second line. |
| `var sub` | `BannerLine?` |  | Optional sub banner (typically the lane diagram). |
| `var triggerDistanceM` | `Double` |  | Metres before the maneuver at which to start displaying. |
| `var drivingSide` | `String?` |  | The side of the road traffic drives on at the manoeuvre this banner describes: `"left"` (UK-style left-hand traffic, where roundabouts circulate clockwise) or `"right"`. `None` when the engine does not establish it for that manoeuvre: the route translation sets it on roundabout enter/exit and u-turn steps only (see `sn_valhalla::translate`). Icon mappings read it to pick the roundabout circulation and the u-turn arm, and must draw a plain glyph rather than guess when it is `None`. |
| `var roadRef` | `String?` |  | The route number(s) of the road the manoeuvre this banner describes leads onto, exactly as the engine reports them on that manoeuvre (`is_route_number` names only, `"; "` separated, e.g. `"A27"` or `"I 5; US 101"`; see `sn_valhalla::valhalla::Maneuver::ref_`). `None` when the road carries none or the route came without the gateway's road fields: a road-number badge is drawn only from this, never inferred from a street name. |
| `var country` | `String?` |  | ISO 3166-1 alpha-2 country of that road (e.g. `"GB"`), from the engine's admin data; picks the country's road-number badge forms. |
| `var subdivision` | `String?` |  | ISO 3166-2 subdivision of that road (e.g. `"US-CA"`, `"CA-QC"`) where the map carries one; some badge tiers apply in one subdivision only. |
| `var roadClass` | `String?` |  | Engine road class of that road (`motorway`, `trunk`, `primary`, ...); the GB primary-route badge (green) needs `trunk`. |

**Initialisers and methods**

- `public init(primary: BannerLine, secondary: BannerLine?, sub: BannerLine?, triggerDistanceM: Double, drivingSide: String? = nil, roadRef: String? = nil, country: String? = nil, subdivision: String? = nil, roadClass: String? = nil)`

#### Enums (MapMap core bindings (generated))

[`AdrDecision`](#adrdecision), [`AdrTunnelCategory`](#adrtunnelcategory), [`AdrTunnelCode`](#adrtunnelcode), [`ChannelInstallOutcome`](#channelinstalloutcome), [`CostingModel`](#costingmodel), [`FetchError`](#fetcherror), [`GuidanceUpdate`](#guidanceupdate), [`InstallOutcome`](#installoutcome), [`InstructionSeverity`](#instructionseverity), [`MapLayerKind`](#maplayerkind), [`NavCoreError`](#navcoreerror), [`PlaceKind`](#placekind), [`ProbeEvent`](#probeevent), [`RerouteReason`](#reroutereason), [`RerouteState`](#reroutestate), [`SpeedLimit`](#speedlimit), [`StyleTheme`](#styletheme), [`UpdateOutcome`](#updateoutcome)

##### `AdrDecision`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum AdrDecision: Equatable, Hashable
```

Outcome of a tunnel-entry check, mirroring [`sn_adr::Decision`].

| Case | Description |
| --- | --- |
| `allowed` | Passage is permitted. |
| `blocked(reason: String)` | Passage is forbidden. |

##### `AdrTunnelCategory`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum AdrTunnelCategory: Equatable, Hashable
```

ADR tunnel category A–E (ascending restrictiveness), mirroring
[`sn_adr::TunnelCategory`].

| Case | Description |
| --- | --- |
| `a` | No dangerous-goods restriction. |
| `b` | Restriction for goods which may lead to a very large explosion. |
| `c` | … or a large explosion or large toxic release. |
| `d` | … or a large fire. |
| `e` | Restriction for nearly all dangerous goods. |

##### `AdrTunnelCode`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum AdrTunnelCode: Equatable, Hashable
```

ADR 8.6.4 tunnel restriction code of a load, mirroring
[`sn_adr::TunnelRestrictionCode`]. `NoCode` is ADR's `(, )`: explicitly
unrestricted.

| Case | Description |
| --- | --- |
| `noCode` | ADR `(, )`, passage permitted through all tunnels. |
| `b` | Code B. |
| `b1000c` | Code B1000C. |
| `bd` | Code B/D. |
| `be` | Code B/E. |
| `c` | Code C. |
| `c5000d` | Code C5000D. |
| `cd` | Code C/D. |
| `ce` | Code C/E. |
| `d` | Code D. |
| `de` | Code D/E. |
| `e` | Code E. |

##### `ChannelInstallOutcome`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum ChannelInstallOutcome: Equatable, Hashable
```

How a channel install ended.

| Case | Description |
| --- | --- |
| `installed(result: ChannelInstallResult)` | The package verified and was promoted. |
| `cancelled` | The [`CancelToken`](#canceltoken) was tripped before promotion. Nothing was installed; verified layers stay parked for the next attempt. |

##### `CostingModel`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum CostingModel: Equatable, Hashable
```

Valhalla costing model, mirroring [`sn_valhalla::valhalla::Costing`].

| Case | Description |
| --- | --- |
| `auto` | Standard car costing. |
| `truck` | Truck costing (required when an ADR profile is supplied). |
| `bus` | Bus costing. |
| `motorScooter` | Motor scooter costing. |
| `motorcycle` | Motorcycle costing. |
| `bicycle` | Bicycle costing. |
| `pedestrian` | Pedestrian costing. |

##### `FetchError`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum FetchError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError
```

A transport failure thrown by a foreign [`LayerFetcher`](#layerfetcher)
implementation.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  | Human-readable description of the failure. |

| Case | Description |
| --- | --- |
| `Transport(message: String)` | The download failed (network, storage, cancelled, …). |

##### `GuidanceUpdate`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum GuidanceUpdate: Equatable, Hashable
```

The guidance state reported after each location update, flattened for
FFI from Ferrostar's `TripState`.

The `Navigating` variant is deliberately larger than the others: it is
the steady-state payload and the FFI shape (UniFFI enums cannot box),
and one allocation per location fix is irrelevant next to the fix
cadence.

| Case | Description |
| --- | --- |
| `navigating(stepIndex: UInt32, distanceToNextManeuverM: Double, distanceRemainingM: Double, durationRemainingS: Double, currentInstruction: String, deviationM: Double?, visual: VisualBanner?, spoken: SpokenPrompt?, snapped: SnappedFix?, severity: InstructionSeverity, speedLimit: SpeedLimit?)` | Actively navigating. |
| `arrived` | The user has arrived at the destination. |
| `offRoute(deviationM: Double)` | The user has left the route entirely (beyond the configured deviation threshold from every remaining step). The platform layer should recalculate. |

##### `InstallOutcome`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum InstallOutcome: Equatable, Hashable
```

How a cancellable install ended.

| Case | Description |
| --- | --- |
| `installed(territory: TerritoryInfo)` | The package verified and was promoted. |
| `cancelled` | The [`CancelToken`](#canceltoken) was tripped before promotion. Nothing was installed; staging was removed. |

##### `InstructionSeverity`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum InstructionSeverity: Equatable, Hashable
```

How urgently a guidance update should be surfaced to the driver.

Voice layers map this to prosody (pitch/rate) and earcons; visual
layers to banner styling. Ordered: `Info < Warning < Critical`.

Derivation (single source of truth for every SDK):

- **Critical**, restriction prohibitions: an ADR tunnel prohibition
([`InstructionSeverity::for_adr_decision`] on a `Blocked` decision,
the hook platform layers use when `check_tunnel` fires off-route)
and manoeuvres crossing a **gate** (a physical barrier the vehicle
must not run).
- **Warning**, approaching a restriction or interruption of the
drive: toll booths and ferry boardings/landings.
- **Info**, every normal manoeuvre.

Off-route updates carry no instruction; platform voice layers treat
[`GuidanceUpdate::OffRoute`] itself as `Warning`.

| Case | Description |
| --- | --- |
| `info` | A normal manoeuvre announcement. |
| `warning` | Approaching a restriction (toll booth, ferry boarding/landing). |
| `critical` | A prohibition: ADR tunnel block or a gate across the carriageway. |

##### `MapLayerKind`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum MapLayerKind: Equatable, Hashable
```

Map layer kind, mirroring [`sn_package::LayerKind`].

| Case | Description |
| --- | --- |
| `valhallaTiles` | Valhalla routing tile tree. |
| `pmtiles` | PMTiles render layer. |
| `geocodeIndex` | Tantivy geocoding index. |
| `poiSidecar` | Proprietary POI sidecar database. |
| `extra(name: String)` | Any other layer kind, by its manifest string. |

##### `NavCoreError`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum NavCoreError: Swift.Error, Equatable, Hashable, Foundation.LocalizedError
```

The flat error type crossing the FFI boundary.

Rich typed errors live on the Rust side (`TerritoryError`,
`RoutingError`, `GuidanceError`); foreign code gets a stable
category plus a human-readable message.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  | Human-readable description. |

| Case | Description |
| --- | --- |
| `Territory(message: String)` | Territory storage or lifecycle failure. |
| `Verification(message: String)` | A package failed cryptographic or integrity verification. |
| `Routing(message: String)` | Route request building or response parsing failure (including Valhalla error bodies from the on-device engine). |
| `Guidance(message: String)` | Guidance session failure. |
| `InvalidArgument(message: String)` | A caller-supplied argument was invalid. |
| `Search(message: String)` | On-device geocode search failure (index missing, unreadable or of an unsupported schema version). |
| `LayerNotInstalled(territoryId: String, kind: String, detail: String)` | The territory has a layer of that kind, but **this install left it out** (see [`TerritoryManager::install_from_channel`]). Its own case on purpose: it is not `Territory` (nothing failed), not a corrupt install and not an unknown territory. The app knows what it chose not to download, so this is the signal to offer the download, not to report an error. The human-readable sentence is `detail`, not `message`: a UniFFI error variant with several fields, one of them named `message`, generates a Kotlin class whose constructor property collides with `Throwable.message` and does not compile. Single-field variants (every other case here) are fine. |

##### `PlaceKind`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum PlaceKind: Equatable, Hashable
```

The kind of entity a [`Place`](#place) describes, mirroring
[`sn_geocode::DocKind`].

| Case | Description |
| --- | --- |
| `address` | A single addressable building or unit (housenumber + street). |
| `street` | A named street or road. |
| `locality` | A town, city, village or suburb. |
| `poi` | A point of interest (fuel station, truck stop, museum, …). |
| `postcode` | A postcode centroid (full or outward code). |

##### `ProbeEvent`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum ProbeEvent: Equatable, Hashable
```

A coarse, time-bucketed event within a trip.

| Case | Description |
| --- | --- |
| `offRoute(todBucket: String, deviationBandM: UInt32)` | The vehicle left the route (a confirmed off-route condition from guidance). Carries only the time bucket and a coarse deviation band. |
| `etaOutcome(predictedS: Double, actualS: Double, routeLenKmBucket: String)` | The realised trip duration against the engine's first estimate. Feeds ETA-accuracy calibration; carries no location. |

##### `RerouteReason`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum RerouteReason: Equatable, Hashable
```

Why a reroute was requested.

| Case | Description |
| --- | --- |
| `offRoute(deviationM: Double)` | The vehicle left the route line; `deviation_m` is the distance from the route at the confirming fix. |
| `fasterRoute(savesS: UInt32)` | A periodic re-evaluation found a faster route for the remaining journey (see [`crate::faster_route::FasterRouteEvaluator`]); the driver accepted the switch. `saves_s` is the estimated saving in seconds versus staying on the current route. |

##### `RerouteState`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum RerouteState: Equatable, Hashable
```

The controller's state after the most recent update, for UI and telemetry.

| Case | Description |
| --- | --- |
| `monitoring` | On route (or recovered): nothing to do. |
| `offRouteSuspected(count: UInt32)` | Off route, but not yet confirmed, the count and/or duration gate has not been met. `count` is the run of consecutive off-route fixes so far. |
| `rerouteNeeded` | A reroute is confirmed for this update; the accompanying [`RerouteDecision`](#reroutedecision) should be routed. Reported exactly once per confirmed departure. |
| `coolingDown` | Off route and confirmed again, but suppressed because a reroute fired within [`RerouteConfig::min_interval_between_reroutes_s`]. |

##### `SpeedLimit`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum SpeedLimit: Equatable, Hashable
```

The posted speed limit for the current edge, for a speed-limit badge and
over-limit warnings.

This is surfaced from the OSRM `annotation.maxspeed` that Ferrostar threads
through its navigation state (`TripState::Navigating.annotation_json`) at
the current geometry index, in the Mapbox shape
(<https://docs.mapbox.com/api/navigation/directions/#route-leg-object>). It
appears only when the route actually carries speed-limit data: on-device
that means the Valhalla graph was built with OSM `maxspeed` (and admin data
for implicit limits) and the engine emitted per-maneuver limits, which the
translator forwarded (see [`sn_valhalla::valhalla::SpeedLimit`]). With
stock data there is no annotation and [`GuidanceUpdate::Navigating`] reports
no limit at all, values are never invented.

| Case | Description |
| --- | --- |
| `known(kmh: Double)` | A known posted limit, normalised to kilometres per hour regardless of the unit the route expressed it in. |
| `unlimited` | The road is de-restricted (Mapbox `maxspeed` `none`; OSM `maxspeed=none`, e.g. a German autobahn), show a "no limit" badge, do not warn. |
| `unknown` | A limit applies but is unknown for the current segment (Mapbox `maxspeed` `unknown`), the data source annotates this position but has no value for it. Distinct from the field being absent entirely. |

##### `StyleTheme`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum StyleTheme: Equatable, Hashable
```

Which packaged style variant [`TerritoryManager::territory_style`]
returns, mirroring [`crate::territory::StyleTheme`] (and the web SDK's
`buildStyle({ theme })` names).

| Case | Description |
| --- | --- |
| `light` | The light (day) style, also the package default. |
| `dark` | The dark (night) style. |

##### `UpdateOutcome`

*Enum* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public enum UpdateOutcome: Equatable, Hashable
```

How a cancellable update ended.

| Case | Description |
| --- | --- |
| `applied(result: UpdateResult)` | The update verified and was swapped in. |
| `cancelled` | The [`CancelToken`](#canceltoken) was tripped before promotion. The installed version is untouched; verified layers stay parked for a retry. |

#### Protocols (MapMap core bindings (generated))

[`CancelTokenProtocol`](#canceltokenprotocol), [`GuidanceSessionProtocol`](#guidancesessionprotocol), [`LayerFetcher`](#layerfetcher), [`LocalRouter`](#localrouter), [`ProbeCollectorProtocol`](#probecollectorprotocol), [`TerritoryManagerProtocol`](#territorymanagerprotocol), [`TerritorySearchProtocol`](#territorysearchprotocol)

##### `CancelTokenProtocol`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol CancelTokenProtocol: AnyObject, Sendable
```

A cancellation handle for an in-flight territory install or update.

The install calls on [`TerritoryManager`](#territorymanager) block the calling thread for
as long as a multi-gigabyte package takes to download, unpack and
verify. Create a token, pass it to one of the `_cancellable` variants
running on a background thread, and call [`CancelToken::cancel`] from
the UI thread when the user taps Cancel. The install polls it at safe
points and returns [`InstallOutcome::Cancelled`] /
[`UpdateOutcome::Cancelled`] with its staging directory removed: no
partial package is left on disk, the previously installed version is
untouched, and layers that had already been verified stay in the
resumable cache for the next attempt. Cancelling after the call has
returned changes nothing.

The token cannot interrupt a [`LayerFetcher`](#layerfetcher) blocked in a transfer:
give the fetcher the same token and have it poll
[`CancelToken::is_cancelled`] between chunks (or abort its request when
the app cancels). Whatever the fetcher then returns, the install
reports `Cancelled`, not a transport error.

**Initialisers and methods**

- `func cancel()`: Request cancellation. Idempotent and safe from any thread.
- `func isCancelled() -> Bool`: Whether [`Self::cancel`] has been called.

##### `GuidanceSessionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol GuidanceSessionProtocol: AnyObject, Sendable
```

A turn-by-turn guidance session (see [`crate::guidance`]).

Thread-safe: the mutable Ferrostar navigation state is guarded by a
mutex, matching UniFFI's shared-object concurrency model.

**Initialisers and methods**

- `func advanceToNextStep() throws -> GuidanceUpdate`: Manually advance to the next step (e.g. in a tunnel with no GNSS).
- `func totalSteps() -> UInt32`: Number of steps in the route, including the arrival step.
- `func updateLocation(lat: Double, lon: Double, timestampMs: Int64, speedMps: Double?, courseDeg: Double?, horizontalAccuracyM: Double?) throws -> GuidanceUpdate`: Feed a location fix; returns the updated guidance state. `horizontal_accuracy_m` defaults to `0.0` (trusted exactly) when absent; pass the platform's reported accuracy when available so step advance and deviation gating behave correctly.

##### `LayerFetcher`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol LayerFetcher: AnyObject, Sendable
```

The platform's download transport for over-the-air territory updates,
exported as a UniFFI callback interface like
[`crate::routing::LocalRouter`]: the core decides *what* to fetch and
cryptographically verifies everything; Kotlin/Swift decide *how* bytes
move (OkHttp, URLSession, an air-gapped mirror, …).

**Initialisers and methods**

- `func fetch(relPath: String, expectedBlake3: String, destPath: String) throws`: Download the channel-relative `rel_path` (e.g. `territories/uk/2.0.0/layers/<addr>/valhalla.tar.zst`) to the local file `dest_path`. `expected_blake3` is advisory (empty for signed metadata files); the core re-verifies all content itself.

##### `LocalRouter`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol LocalRouter: AnyObject, Sendable
```

The injection point for the platform's on-device Valhalla binding.

valhalla-mobile (<https://github.com/Rallista/valhalla-mobile>) exposes
a `route(request_json) -> response_json` actor on both Android and iOS;
Kotlin/Swift implement this interface with that actor and pass it to
[`crate::ffi::route_offline`]. Exported as a UniFFI callback interface
(`with_foreign`) so the implementation lives in foreign code.

**Initialisers and methods**

- `func route(requestJson: String) -> String`: Execute a Valhalla turn-by-turn request and return the raw response JSON (either a route response or a Valhalla error body).

##### `ProbeCollectorProtocol`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol ProbeCollectorProtocol: AnyObject, Sendable
```

On-device probe collector. Feed it every guidance update with the fix's
position, speed and timestamp; call [`finish`](ProbeCollector::finish) at
arrival or trip abandonment to get the [`ProbeBatch`](#probebatch) to upload.

Interior mutability (a `Mutex`) lets the FFI methods take `&self`, matching
the [`crate::guidance::GuidanceSession`] object shape.

**Initialisers and methods**

- `func finish(arrived: Bool, timestampMs: Int64) -> ProbeBatch`: Close the trip and return the batch to upload. `arrived` distinguishes a completed trip from an abandoned one (both apply end suppression).
- `func finishJson(arrived: Bool, timestampMs: Int64) -> String?`: Close the trip and return the exact JSON body to `POST /v1/probe`, or `None` when the batch carries nothing worth uploading. This is the single canonical serialiser for the probe wire format, platform code should upload this verbatim rather than re-encoding the record, so the wire form can never drift from what the gateway ingests.
- `func observe(update: GuidanceUpdate, fixLat: Double, fixLon: Double, speedMps: Double?, timestampMs: Int64)`: Feed one guidance update and its fix. `speed_mps` is the device's reported ground speed when available (preferred); pass `None` to let the collector estimate it from successive positions.
- `func setRouteRef(routeRef: String)`: Set the opaque served-route token so the server can resolve segments.
- `func setTerritory(territory: String)`: Set the territory id stamped into the batch.
- `func setVehicleClass(vehicleClass: String)`: Set the vehicle costing class stamped into the batch (a class only, never a vehicle or driver identifier).

##### `TerritoryManagerProtocol`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol TerritoryManagerProtocol: AnyObject, Sendable
```

Manages signed offline territory packages (see [`crate::territory`]).

**Initialisers and methods**

- `func active() throws -> String?`: The active territory id, if one is set and still installed.
- `func applyUpdate(territoryId: String, newManifestJson: String, newManifestSigBase64: String, fetcher: LayerFetcher) throws -> UpdateResult`: Apply a differential update: stage, fetch only the changed layers through `fetcher`, verify everything and swap atomically. Any failure leaves the installed territory untouched; the active flag is preserved. Blocks until done and cannot be interrupted; see [`Self::apply_update_cancellable`].
- `func applyUpdateCancellable(territoryId: String, newManifestJson: String, newManifestSigBase64: String, fetcher: LayerFetcher, cancel: CancelToken) throws -> UpdateOutcome`: [`Self::apply_update`] with a [`CancelToken`](#canceltoken). Polled before every layer download, between archive entries while unpacking, before each hash pass and before promotion. A tripped token yields [`UpdateOutcome::Cancelled`]: the installed version is untouched, staging is removed, and layers that had already been fetched and verified stay in the resumable cache, so calling again with the same manifest re-fetches only what is missing. Give `fetcher` the same token so it can stop an in-flight transfer (the token cannot interrupt foreign code on its own); whatever the fetcher then returns, a tripped token reports `Cancelled` rather than a transport error.
- `func cachedLayerBytes() throws -> UInt64`: Bytes parked in the resumable layer cache: downloads a failed install already paid for, which a retry will not pay for again. Surface this next to a "resume" affordance and in storage settings.
- `func checkForUpdate(territoryId: String, indexJson: String, indexSigBase64: String, fetcher: LayerFetcher) throws -> AvailableUpdate?`: Check the signed channel index for an update to an installed territory. `index_json` must be the exact bytes served as `index.json` and `index_sig_base64` the content of `index.json.sig`; the remote manifest is fetched and verified through `fetcher`. Returns `None` when the device is already on the channel's latest version (or the channel does not list the territory).
- `func clearLayerCache() throws`: Discard every parked layer. The bytes are lost and a later install re-downloads (and on a metered channel re-pays for) them, so offer this only as a deliberate "free up space" action.
- `func installDir(packageDir: String) throws -> TerritoryInfo`: Install an unpacked package directory. Blocks until done and cannot be interrupted; see [`Self::install_dir_cancellable`].
- `func installDirCancellable(packageDir: String, cancel: CancelToken) throws -> InstallOutcome`: [`Self::install_dir`] with a [`CancelToken`](#canceltoken). Polled between copied files, before verification and before promotion.
- `func installDirPartial(packageDir: String, omit: [MapLayerKind], cancel: CancelToken) throws -> InstallOutcome`: Install an unpacked package directory, holding only the layers this app can use: `omit` names the kinds to leave out (e.g. `Pmtiles` and `Extra("style")` for an app with no map view). The install records what it left out, and every later verification requires those layers to be **absent** rather than exempting them from hashing, so the record cannot be forged into admitting tampered bytes. Asking for an omitted layer answers [`NavCoreError::LayerNotInstalled`], which is not an error state: it is "you chose not to download this". The selection is sticky: updates carry it forward, so an app that skipped the render layers never downloads them on an update either.
- `func installFromChannel(territoryId: String, indexJson: String, indexSigBase64: String, omit: [MapLayerKind], fetcher: LayerFetcher, cancel: CancelToken) throws -> ChannelInstallOutcome`: Install a territory **from the channel**, downloading only the layers this app can use. The first-install counterpart of [`Self::apply_update`], and the only path on which omitting layers saves bandwidth: a layer in `omit` is never requested, so it is never metered and never stored. `index_json` / `index_sig_base64` are the exact served bytes of `index.json` and `index.json.sig`; the channel's latest version of the territory is installed. Nothing is trusted but the pinned key: the index signature, the manifest signature and every blob's BLAKE3 are checked, and the assembled directory goes through the same full verification pass as any other install before it is promoted. Interrupted installs resume: verified layers stay parked in the layer cache. Calling it again for a territory that is already installed is how a selection is widened later. Layers already on the device at the same content hash are linked, not re-downloaded, so adding the map to a router-only install costs the map alone.
- `func installSnpkg(archivePath: String) throws -> TerritoryInfo`: Install a packed `.snpkg` archive (verify-then-promote; rejected packages leave no trace). Blocks until done and cannot be interrupted; see [`Self::install_snpkg_cancellable`].
- `func installSnpkgCancellable(archivePath: String, cancel: CancelToken) throws -> InstallOutcome`: [`Self::install_snpkg`] with a [`CancelToken`](#canceltoken). Polled between archive entries, before verification and before promotion; a tripped token yields [`InstallOutcome::Cancelled`] with staging removed and nothing installed.
- `func installSnpkgPartial(archivePath: String, omit: [MapLayerKind], cancel: CancelToken) throws -> InstallOutcome`: [`Self::install_dir_partial`] from a packed `.snpkg` archive. The archive carries every layer, so this saves storage, not bandwidth: the omitted layers are unpacked and then dropped before verification. For the bandwidth saving use [`Self::install_from_channel`], which never fetches a blob it does not want.
- `func layerPath(territoryId: String, kind: MapLayerKind) throws -> String`: Absolute path of a layer inside an installed territory (e.g. the Valhalla tile tree for valhalla-mobile, the PMTiles file for MapLibre).
- `func list() throws -> [TerritoryInfo]`: List installed territories in territory-id order.
- `func openSearch(territoryId: String) throws -> TerritorySearch`: Open the installed territory's bundled geocode index for on-device search. The returned [`TerritorySearch`](#territorysearch) holds the index open (mmap-backed), create it once per territory and reuse it across queries.
- `func planUpdate(newManifestJson: String) throws -> UpdatePlan`: Plan a differential update from the installed manifest to `new_manifest_json` (a signed manifest's JSON body, as served by the update channel).
- `func remove(territoryId: String) throws`: Remove an installed territory (clears the active marker if needed).
- `func setActive(territoryId: String) throws`: Mark a territory as active (persisted atomically).
- `func territoryStyle(territoryId: String, theme: StyleTheme) throws -> String`: A complete MapLibre style JSON for an installed territory with **local** (offline) references, mirroring the web SDK's `buildStyle()`. Load it straight into MapLibre, the territory tile source points at the installed PMTiles layer via a `pmtiles://<absolute path>` URL, so the map renders with radios off. Packages without a baked style layer get the default MapMap theme compiled on the fly over the package's PMTiles layer. The `glyphs` endpoint stays hosted (MapLibre caches fetched glyph ranges; no offline glyph bundle ships in packages today).
- `func verifyTerritory(territoryId: String) throws -> TerritoryInfo`: Re-verify an installed territory from disk: the manifest signature, the BLAKE3 of every layer this install holds, and the absence of every layer it recorded as omitted. The ordinary calls do not re-hash gigabytes; this is the on-demand integrity check for after a restore or a crash. A failure arrives as [`NavCoreError::Verification`] and means the install must be repaired or reinstalled, not retried.

##### `TerritorySearchProtocol`

*Protocol* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public protocol TerritorySearchProtocol: AnyObject, Sendable
```

On-device search over an installed territory's bundled geocode index
(see [`TerritoryManager::open_search`]). Fully offline; queries are
synchronous and mmap-backed, call from a background thread/dispatcher
like any other disk-bound SDK call.

**Initialisers and methods**

- `func reverse(lat: Double, lon: Double, limit: UInt32) throws -> [Place]`: Reverse geocode: the nearest indexed places to a coordinate, nearest first.
- `func search(query: String, near: RoutePoint?, limit: UInt32) throws -> [Place]`: Free-text search (addresses, streets, places, POIs, UK postcodes; prefix-tokenised, so it works for search-as-you-type). `near` biases ranking towards the given point and fills each result's `distance_m`; `limit` caps the number of hits.

#### Functions (MapMap core bindings (generated))

[`buildOfflineRouteRequest`](#buildofflinerouterequest), [`checkTunnel`](#checktunnel), [`forbiddenCategories`](#forbiddencategories), [`parseRouteResult`](#parserouteresult), [`routeOffline`](#routeoffline), [`styleForNavigation`](#stylefornavigation), [`styleWithBuildings3d`](#stylewithbuildings3d)

##### `buildOfflineRouteRequest`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func buildOfflineRouteRequest(locations: [RoutePoint], costing: CostingModel, adr: AdrProfile?) throws -> String
```

Build the Valhalla request JSON an on-device engine accepts, with ADR
costing options merged (conflicts rejected). Useful when foreign code
wants to drive the engine itself.

##### `checkTunnel`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func checkTunnel(profile: AdrProfile, category: AdrTunnelCategory) -> AdrDecision
```

Decide whether the vehicle may pass through a tunnel of `category`
(conservative ADR 8.6.4 reading; see [`crate::adr::check_tunnel`]).

##### `forbiddenCategories`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func forbiddenCategories(profile: AdrProfile) -> [AdrTunnelCategory]
```

All tunnel categories the vehicle is forbidden from (ascending). Empty
when unrestricted.

##### `parseRouteResult`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func parseRouteResult(responseJson: String) throws -> RouteResult
```

Parse a raw on-device route response into a [`RouteResult`](#routeresult), including
the typed [`RouteResult::geometry`] and [`RouteResult::maneuvers`].

##### `routeOffline`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func routeOffline(router: LocalRouter, locations: [RoutePoint], costing: CostingModel, adr: AdrProfile?) throws -> RouteResult
```

Full offline routing orchestration: build the request, run it through
the injected [`LocalRouter`](#localrouter) (the platform's valhalla-mobile binding)
and parse the response.

##### `styleForNavigation`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func styleForNavigation(styleJson: String, keepBuildings3d: Bool = true) throws -> String
```

Prepares a style for a turn-by-turn navigation view. Call it on
whatever style the app otherwise uses, right before loading the
navigation map; it is where nav-view safeguards live.

By default (`keep_buildings_3d = true`) the style's 3D building state
is left exactly as the app configured it: MapLibre Native's historical
fill-extrusion memory blow-up (maplibre-native#4107) is fixed by the
instancing rework (#4256; Android >= 13.2.0, iOS >= 6.26.1), and a
navigation-loop soak (`qa/expo-3d-soak`, 17 Jul 2026) measured a
bounded ~170 MB for city-wide 3D at nav zoom, flat, no growth.

Pass `keep_buildings_3d = false` to strip the `building-3d` layer, the pre-fix behaviour, when targeting MapLibre pins older than the
fix or low-memory devices you have not measured.

##### `styleWithBuildings3d`

*Function* · `Sources/MapMapKit/Generated/mapmap.swift`

```swift
public func styleWithBuildings3d(styleJson: String, enabled: Bool) throws -> String
```

Toggle 3D building extrusions on a MapMap `style.json` document (a
territory package's baked style or a hosted style body), returning the
adjusted document to load into MapLibre. Idempotent; errors with
[`NavCoreError::InvalidArgument`] on non-style input.

Safe on MapLibre Native releases carrying the fill-extrusion
instancing fix (maplibre-native#4256, merged 2026-05-05: Android
>= 13.2.0, iOS >= 6.26.1), the historical street-zoom memory blow-up
(#4107, 900 MB+ at zoom 17) is fixed there. Measured 17 Jul 2026 on a
Pixel-class device (`qa/expo-3d-soak`): city-wide extrusions at
zoom 16.5/pitch 60 cost a bounded ~170 MB over an identical no-3D
control, flat over the soak. On pins older than the fix, keep this
off outside measured map-browsing views.

### Ferrostar bindings (generated)

#### Classes (Ferrostar bindings (generated))

[`AndAdvanceConditions`](#andadvanceconditions), [`DistanceEntryAndExitCondition`](#distanceentryandexitcondition), [`DistanceEntryAndSnappedExitCondition`](#distanceentryandsnappedexitcondition), [`DistanceFromStepCondition`](#distancefromstepcondition), [`DistanceToEndOfStepCondition`](#distancetoendofstepcondition), [`ManualStepCondition`](#manualstepcondition), [`NavigationCacheImpl`](#navigationcacheimpl), [`NavigationController`](#navigationcontroller), [`NavigationObserverImpl`](#navigationobserverimpl), [`NavigationRecorder`](#navigationrecorder), [`NavigationReplay`](#navigationreplay), [`NavigationSession`](#navigationsession), [`NavigationSessionCache`](#navigationsessioncache), [`Navigator`](#navigator), [`OrAdvanceConditions`](#oradvanceconditions), [`RouteAdapter`](#routeadapter), [`RouteDeviationDetectorImpl`](#routedeviationdetectorimpl), [`RouteRequestGeneratorImpl`](#routerequestgeneratorimpl), [`RouteResponseParserImpl`](#routeresponseparserimpl), [`StepAdvanceCondition`](#stepadvancecondition)

##### `AndAdvanceConditions`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class AndAdvanceConditions: AndAdvanceConditionsProtocol, @unchecked Sendable
```

Advance if all of the conditions are met (AND).

##### `DistanceEntryAndExitCondition`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class DistanceEntryAndExitCondition: DistanceEntryAndExitConditionProtocol, @unchecked Sendable
```

A stateful condition that requires the user to reach the end of the step then proceed past it to advance.

##### `DistanceEntryAndSnappedExitCondition`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class DistanceEntryAndSnappedExitCondition: DistanceEntryAndSnappedExitConditionProtocol, @unchecked Sendable
```

A stateful condition that requires the user to reach the end of the step then proceed past it to advance.

This variant uses route snapping (snapping to the combined current+next step geometry) for the exit check,
making it more robust for pedestrian/hiking navigation where users may walk on the opposite side of the street
or wander around the optimal path. The route-snapped exit check prevents premature advancement while still
allowing natural pedestrian movement patterns.
The exit distance is measured from the current step to the route-snapped position.

##### `DistanceFromStepCondition`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class DistanceFromStepCondition: DistanceFromStepConditionProtocol, @unchecked Sendable
```

Advances once the user is at least [`Self::distance`] meters from the current step's polyline.

This results in *delayed* advance,
but is more robust to spurious / unwanted step changes in scenarios including
self-intersecting routes (sudden jumps to the next step)
and pauses at intersections (advancing too soon before the maneuver is complete).

NOTE! This may be less robust to things like short steps, out-and-backs, and U-turns,
where this may eagerly exit a current step before the user has traversed it
if the start of the step is within range of the end.

##### `DistanceToEndOfStepCondition`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class DistanceToEndOfStepCondition: DistanceToEndOfStepConditionProtocol, @unchecked Sendable
```

Automatically advances when the user's location is close enough to the end of the step.

This results in an eager advance where the user will jump to the next step when the
condition is met.

##### `ManualStepCondition`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class ManualStepCondition: ManualStepConditionProtocol, @unchecked Sendable
```

Never advances to the next step automatically;
requires calling [`NavigationController::advance_to_next_step`](super::NavigationController::advance_to_next_step).

You can use this to implement custom behaviors in external code.

##### `NavigationCacheImpl`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationCacheImpl: NavigationCache, @unchecked Sendable
```

**Initialisers and methods**

- `open func save(record: Data)`
- `open func load() -> Data?`
- `open func delete()`

##### `NavigationController`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationController: NavigationControllerProtocol, @unchecked Sendable
```

Manages the navigation lifecycle through a route,
returning an updated state given inputs like user location.

Notes for implementing a new platform:
- A controller is bound to a single route; if you want recalculation, create a new instance.
- This is a pure type (no interior mutability), so a core function of your platform code is responsibly managing mutable state.

**Initialisers and methods**

- `public convenience init(route: Route, config: NavigationControllerConfig)`: Create a navigation controller for a route and configuration.

##### `NavigationObserverImpl`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationObserverImpl: NavigationObserver, @unchecked Sendable
```

**Initialisers and methods**

- `open func onGetInitialState(state: NavState)`
- `open func onUserLocationUpdate(location: UserLocation, state: NavState)`
- `open func onAdvanceToNextStep(state: NavState)`
- `open func onRouteAvailable(route: Route)`

##### `NavigationRecorder`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationRecorder: NavigationRecorderProtocol, @unchecked Sendable
```

**Initialisers and methods**

- `public convenience init(route: Route, config: NavigationControllerConfig)`
- `open func getEvents() -> [NavigationRecordingEvent]`
- `open func getRecordingJson() throws -> String`
- `open func onAdvanceToNextStep(state: NavState)`
- `open func onGetInitialState(state: NavState)`
- `open func onRouteAvailable(route: Route)`
- `open func onUserLocationUpdate(location: UserLocation, state: NavState)`

##### `NavigationReplay`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationReplay: NavigationReplayProtocol, @unchecked Sendable
```

A wrapper around `NavigationRecording` to facilitate replaying the event stream.

##### `NavigationSession`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationSession: NavigationSessionProtocol, @unchecked Sendable
```

**Initialisers and methods**

- `public convenience init(controller: Navigator, observers: [NavigationObserver])`
- `open func advanceToNextStep(state: NavState) -> NavState`
- `open func getInitialState(location: UserLocation) -> NavState`
- `open func route() -> Route`
- `open func updateUserLocation(location: UserLocation, state: NavState) -> NavState`

##### `NavigationSessionCache`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class NavigationSessionCache: NavigationSessionCacheProtocol, @unchecked Sendable
```

**Initialisers and methods**

- `public convenience init(config: NavigationCachingConfig, cache: NavigationCache)`
- `open func canResume() -> Bool`: Check if the navigation session can be resumed.
- `open func load() -> NavigationSessionSnapshot?`: Load the navigation session record from the cache if it exists and is not stale.
- `open func onAdvanceToNextStep(state: NavState)`
- `open func onGetInitialState(state: NavState)`
- `open func onRouteAvailable(route: Route)`
- `open func onUserLocationUpdate(location: UserLocation, state: NavState)`

##### `Navigator`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class Navigator: NavigatorProtocol, @unchecked Sendable
```

Core interface for navigation functionalities.

This trait defines the essential operations for a navigation state manager.
This lets us build additional layers (e.g. event logging)
around [`NavigationController`](#navigationcontroller) in a composable manner.

**Initialisers and methods**

- `open func route() -> Route`
- `open func getInitialState(location: UserLocation) -> NavState`
- `open func advanceToNextStep(state: NavState) -> NavState`
- `open func updateUserLocation(location: UserLocation, state: NavState) -> NavState`

##### `OrAdvanceConditions`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class OrAdvanceConditions: OrAdvanceConditionsProtocol, @unchecked Sendable
```

Advance if any of the conditions are met (OR).

This is ideal for short circuit type advance conditions.

E.g. you may have:
1. A short circuit detecting if the user has exceeded a large distance from the current step.
2. A default advance behavior.

##### `RouteAdapter`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class RouteAdapter: RouteAdapterProtocol, @unchecked Sendable
```

The route adapter bridges between the common core and a routing backend where interaction takes place
over a generic request/response flow (typically over a network;
local/offline routers **do not use this object** as the interaction patterns are different).

This is essentially the composite of the [`RouteRequestGenerator`](#routerequestgenerator) and [`RouteResponseParser`](#routeresponseparser)
traits, but it provides one further level of abstraction which is helpful to consumers.
As there is no way to signal compatibility between request generators and response parsers,
the [`RouteAdapter`](#routeadapter) provides convenience constructors which take the guesswork out of it,
while still leaving consumers free to implement one or both halves.

In the future, we may provide additional methods or conveniences, and this
indirection leaves the design open to such changes without necessarily breaking source
compatibility.
One such possible extension would be the ability to fetch more detailed attributes in real time.
This is supported by the Valhalla stack, among others.

Ideas  welcome re: how to signal compatibility between request generators and response parsers.
I don't think we can do this in the type system, since one of the reasons for the split design
is modularity, including the possibility of user-provided implementations, and these will not
always be of a "known" type to the Rust side.

**Initialisers and methods**

- `public convenience init(requestGenerator: RouteRequestGenerator, responseParser: RouteResponseParser)`: Creates a route adapter from any request generator and response parser. This constructor offers unlimited flexibility, but if you're using a major routing vendor API, [`RouteAdapter::from_well_known_route_provider`] may be a more convenient interface.
- `public static func fromWellKnownRouteProvider(wellKnownRouteProvider: WellKnownRouteProvider) throws -> RouteAdapter`: Creates a route adapter from a well-known provider configuration.
- `open func generateRequest(userLocation: UserLocation, waypoints: [Waypoint]) throws -> RouteRequest`
- `open func parseResponse(response: Data) throws -> [Route]`

##### `RouteDeviationDetectorImpl`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class RouteDeviationDetectorImpl: RouteDeviationDetector, @unchecked Sendable
```

A custom deviation detector (for extending the behavior of [`RouteDeviationTracking`](#routedeviationtracking)).

This allows for arbitrarily complex implementations when the provided ones are not enough.
For example, detecting that the user is proceeding the wrong direction by keeping a ring buffer
of recent locations, or perform local map matching.

**Initialisers and methods**

- `open func checkRouteDeviation(route: Route, tripState: TripState) -> RouteDeviation`: Determines whether the user is following the route correctly or not. NOTE: This function has a single responsibility. Side-effects like whether to recalculate a route are left to higher levels, and implementations should only be concerned with determining the facts. IMPORTANT: If you are short circuiting [`StepAdvanceCondition`](#stepadvancecondition)'s to allow skipping steps, you must always fall back to checking the deviation from the full route line.

##### `RouteRequestGeneratorImpl`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class RouteRequestGeneratorImpl: RouteRequestGenerator, @unchecked Sendable
```

A trait describing any object capable of generating [`RouteRequest`](#routerequest)s.

The interface is intentionally generic. Every routing backend has its own set of
parameters, including a "profile," max travel speed, units of speed and distance, and more.
It is assumed that these properties will be set at construction time or otherwise configured
before use, so that we can keep the public interface as generic as possible.

Implementations may be either in Rust (most popular engines should eventually have Rust
glue code) or foreign code.

**Initialisers and methods**

- `open func generateRequest(userLocation: UserLocation, waypoints: [Waypoint]) throws -> RouteRequest`: Generates a routing backend request given the set of locations. While most implementations will treat the locations as an ordered sequence, this is not guaranteed (ex: an optimized router).

##### `RouteResponseParserImpl`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class RouteResponseParserImpl: RouteResponseParser, @unchecked Sendable
```

A generic interface describing any object capable of parsing a response from a routing
backend into one or more [`Route`](#route)s.

**Initialisers and methods**

- `open func parseResponse(response: Data) throws -> [Route]`: Parses a raw response from the routing backend into a route. We use a sequence of octets as a common interchange format. as this works for all currently conceivable formats (JSON, PBF, etc.).

##### `StepAdvanceCondition`

*Class* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
open class StepAdvanceCondition: StepAdvanceConditionProtocol, @unchecked Sendable
```

When implementing custom step advance logic, this trait allows you to define
whether the condition should advance to the next condition, the next step or not.

At the moment, these must be implemented in Rust.

**Initialisers and methods**

- `open func shouldAdvanceStep(tripState: TripState) -> StepAdvanceResult`: This callback method is used by a step advance condition to receive step updates. The step advance condition can choose based on its outcome and internal state whether to advance to the next step or not.
- `open func newInstance() -> StepAdvanceCondition`: Creates a clean instance of this condition with the same configuration but reset state. This is used by composite conditions (Or/And) to ensure proper state isolation when any condition triggers advancement. **Implementation Requirements:** - **Stateless conditions**: Return a copy/clone of self - **Stateful conditions**: Return a new instance with initial state but preserve configuration parameters - **Composite conditions**: Recursively create fresh instances of all nested conditions This method prevents the state leakage bugs that can cause rapid step advancement and jumping behavior in navigation.

#### Structs (Ferrostar bindings (generated))

[`BoundingBox`](#boundingbox), [`Congestion`](#congestion), [`CourseOverGround`](#courseoverground), [`GeographicCoordinate`](#geographiccoordinate), [`Heading`](#heading), [`Incident`](#incident), [`LaneInfo`](#laneinfo), [`LocationSimulationState`](#locationsimulationstate), [`NavigationCachingConfig`](#navigationcachingconfig), [`NavigationControllerConfig`](#navigationcontrollerconfig), [`NavigationRecordingEvent`](#navigationrecordingevent), [`NavigationSessionSnapshot`](#navigationsessionsnapshot), [`NavState`](#navstate), [`OsrmWaypointProperties`](#osrmwaypointproperties), [`Route`](#route), [`RouteStep`](#routestep), [`SerializableNavState`](#serializablenavstate), [`Speed`](#speed), [`SpokenInstruction`](#spokeninstruction), [`StepAdvanceResult`](#stepadvanceresult), [`TripProgress`](#tripprogress), [`TripSummary`](#tripsummary), [`UserLocation`](#userlocation), [`ValhallaLocationSearchFilter`](#valhallalocationsearchfilter), [`ValhallaWaypointProperties`](#valhallawaypointproperties), [`VisualInstruction`](#visualinstruction), [`VisualInstructionContent`](#visualinstructioncontent), [`Waypoint`](#waypoint)

##### `BoundingBox`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct BoundingBox: Equatable, Hashable, Codable
```

A geographic bounding box defined by its corners.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var sw` | `GeographicCoordinate` |  | The southwest corner of the bounding box. |
| `var ne` | `GeographicCoordinate` |  | The northeast corner of the bounding box. |

**Initialisers and methods**

- `public init(sw: GeographicCoordinate, ne: GeographicCoordinate)`

##### `Congestion`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct Congestion: Equatable, Hashable, Codable
```

Details about congestion for an incident.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var value` | `UInt8` |  | The level of congestion caused by the incident. 0 = no congestion 100 = road closed Other values mean no congestion was calculated |

**Initialisers and methods**

- `public init(value: UInt8)`

##### `CourseOverGround`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct CourseOverGround: Equatable, Hashable, Codable
```

The direction in which the user/device is observed to be traveling.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var degrees` | `UInt16` |  | The direction in which the user's device is traveling, measured in clockwise degrees from true north (N = 0, E = 90, S = 180, W = 270). |
| `var accuracy` | `UInt16?` |  | The accuracy of the course value, measured in degrees. |

**Initialisers and methods**

- `public init(degrees: UInt16, accuracy: UInt16?)`

##### `GeographicCoordinate`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct GeographicCoordinate: Equatable, Hashable, Codable
```

A geographic coordinate in WGS84.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var lat` | `Double` |  | The latitude (in degrees). |
| `var lng` | `Double` |  | The Longitude (in degrees). |

**Initialisers and methods**

- `public init(lat: Double, lng: Double)`

##### `Heading`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct Heading: Equatable, Hashable, Codable
```

The heading of the user/device.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var trueHeading` | `UInt16` |  | The heading in degrees relative to true north. |
| `var accuracy` | `UInt16` |  | The platform specific accuracy of the heading value. |
| `var timestamp` | `Date` |  | The time at which the heading was recorded. |

**Initialisers and methods**

- `public init(trueHeading: UInt16, accuracy: UInt16, timestamp: Date)`

##### `Incident`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct Incident: Equatable, Hashable, Codable
```

An incident affecting the free flow of traffic,
such as constructions, accidents, and congestion.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var id` | `String` |  | A unique identifier for the incident. |
| `var incidentType` | `IncidentType` |  | The type of incident. |
| `var description` | `String?` |  | A short description of the incident. |
| `var longDescription` | `String?` |  | A longer description of the incident. |
| `var creationTime` | `UtcDateTime?` |  | The time at which the incident was *last* created. NB: This can change throughout the life of the incident. |
| `var startTime` | `UtcDateTime?` |  | The time at which the incident started or is expected to start (ex: planned closure). |
| `var endTime` | `UtcDateTime?` |  | The time at which the incident ended or is expected to end. |
| `var impact` | `Impact?` |  | The level of impact to traffic. |
| `var lanesBlocked` | `[BlockedLane]` |  | Lanes which are blocked by the incident. |
| `var congestion` | `Congestion?` |  | Info about the amount of congestion on the road around the incident. |
| `var closed` | `Bool?` |  | Is the road completely closed? |
| `var geometryIndexStart` | `UInt64` |  | The index into the [`RouteStep`](#routestep) geometry where the incident starts. |
| `var geometryIndexEnd` | `UInt64?` |  | The index into the [`RouteStep`](#routestep) geometry where the incident ends. |
| `var subType` | `String?` |  | Optional additional information about the type of incident (free-form text). |
| `var subTypeDescription` | `String?` |  | Optional descriptions about the type of incident (free-form text). |
| `var iso31661Alpha2` | `String?` |  | The ISO 3166-1 alpha-2 code of the country in which the incident occurs. |
| `var iso31661Alpha3` | `String?` |  | The ISO 3166-1 alpha-3 code of the country in which the incident occurs. |
| `var affectedRoadNames` | `[String]` |  | A list of road names affected by the incident. |
| `var bbox` | `BoundingBox?` |  | The bounding box over which the incident occurs. |

**Initialisers and methods**

- `public init(id: String, incidentType: IncidentType, description: String?, longDescription: String?, creationTime: UtcDateTime?, startTime: UtcDateTime?, endTime: UtcDateTime?, impact: Impact?, lanesBlocked: [BlockedLane], congestion: Congestion?, closed: Bool?, geometryIndexStart: UInt64, geometryIndexEnd: UInt64?, subType: String?, subTypeDescription: String?, iso31661Alpha2: String?, iso31661Alpha3: String?, affectedRoadNames: [String], bbox: BoundingBox?)`

##### `LaneInfo`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct LaneInfo: Equatable, Hashable, Codable
```

The content of a visual instruction.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var active` | `Bool` |  |  |
| `var directions` | `[String]` |  |  |
| `var activeDirection` | `String?` |  |  |

**Initialisers and methods**

- `public init(active: Bool, directions: [String], activeDirection: String?)`

##### `LocationSimulationState`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct LocationSimulationState: Equatable, Hashable, Codable
```

The current state of the simulation.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var currentLocation` | `UserLocation` |  |  |
| `var remainingLocations` | `[GeographicCoordinate]` |  |  |
| `var bias` | `LocationBias` |  |  |

**Initialisers and methods**

- `public init(currentLocation: UserLocation, remainingLocations: [GeographicCoordinate], bias: LocationBias)`

##### `NavigationCachingConfig`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct NavigationCachingConfig: Equatable, Hashable, Codable
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var cacheIntervalSeconds` | `Int64` |  |  |
| `var maxAgeSeconds` | `Int64` |  |  |

**Initialisers and methods**

- `public init(cacheIntervalSeconds: Int64, maxAgeSeconds: Int64)`

##### `NavigationControllerConfig`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct NavigationControllerConfig
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var waypointAdvance` | `WaypointAdvanceMode` |  | Configures when navigation advances to the next waypoint in the route. |
| `var stepAdvanceCondition` | `StepAdvanceCondition` |  | Configures when navigation advances to the next step in the route. |
| `var arrivalStepAdvanceCondition` | `StepAdvanceCondition` |  | A special advance condition used for the final 2 route steps (last and arrival). This exists because several of our step advance conditions require entry and exit from a step's geometry. The end of the route/arrival doesn't always accommodate the expected location updates for the core step advance condition. |
| `var routeDeviationTracking` | `RouteDeviationTracking` |  | Configures when the user is deemed to be off course. NOTE: This is distinct from the action that is taken. It is only the determination that the user has deviated from the expected route. |
| `var snappedLocationCourseFiltering` | `CourseFiltering` |  | Configures how the heading component of the snapped location is reported in [`TripState`](#tripstate). |

**Initialisers and methods**

- `public init(waypointAdvance: WaypointAdvanceMode, stepAdvanceCondition: StepAdvanceCondition, arrivalStepAdvanceCondition: StepAdvanceCondition, routeDeviationTracking: RouteDeviationTracking, snappedLocationCourseFiltering: CourseFiltering)`

##### `NavigationRecordingEvent`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct NavigationRecordingEvent: Equatable, Hashable, Codable
```

An event that occurs during navigation.

This is used for the optional session recording / telemetry.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var timestamp` | `Int64` |  | The timestamp of the event in milliseconds since Jan 1, 1970 UTC. |
| `var eventData` | `NavigationRecordingEventData` |  | Data associated with the event. |

**Initialisers and methods**

- `public init(timestamp: Int64, eventData: NavigationRecordingEventData)`

##### `NavigationSessionSnapshot`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct NavigationSessionSnapshot: Equatable, Hashable, Codable
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var savedAt` | `UtcDateTime` |  |  |
| `var route` | `Route` |  |  |
| `var tripState` | `TripState?` |  |  |

**Initialisers and methods**

- `public init(savedAt: UtcDateTime, route: Route, tripState: TripState?)`

##### `NavState`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct NavState
```

The navigation state.

This is typically created from an initial trip state
and conditions for advancing navigation to the next step.
Any internal navigation state is packed in here so that
the navigation controller can remain functionally pure.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var tripState` | `TripState` |  |  |
| `var stepAdvanceCondition` | `StepAdvanceCondition` |  |  |

**Initialisers and methods**

- `public init(tripState: TripState, stepAdvanceCondition: StepAdvanceCondition)`

##### `OsrmWaypointProperties`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct OsrmWaypointProperties: Equatable, Hashable, Codable
```

Waypoint properties parsed from an OSRM-compatible server response.

NOTE: Some servers (such as Valhalla) may support additional parameters at request time
which are _not_ echoed back in the response time.
This is unfortunate; PRs upstream would likely be welcomed!
Similarly, if your server is OSRM-compatible and returns additional attributes,
feel free to open a PR to include these as optional properties.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var name` | `String?` |  | The name of the street that the waypoint snapped to. |
| `var distance` | `Double?` |  | The distance (in meters) between the snapped point and the input coordinate. |

**Initialisers and methods**

- `public init(name: String?, distance: Double?)`

##### `Route`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct Route: Equatable, Hashable, Codable
```

Information describing the series of steps needed to travel between two or more points.

NOTE: This type is unstable and is still under active development and should be
considered unstable.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var geometry` | `[GeographicCoordinate]` |  |  |
| `var bbox` | `BoundingBox` |  |  |
| `var distance` | `Double` |  | The total route distance, in meters. |
| `var waypoints` | `[Waypoint]` |  | The ordered list of waypoints to visit, including the starting point. Note that this is distinct from the *geometry* which includes all points visited. A waypoint represents a start/end point for a route leg. |
| `var steps` | `[RouteStep]` |  |  |

**Initialisers and methods**

- `public init(geometry: [GeographicCoordinate], bbox: BoundingBox, distance: Double, waypoints: [Waypoint], steps: [RouteStep])`

##### `RouteStep`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct RouteStep: Equatable, Hashable, Codable
```

A maneuver (such as a turn or merge) followed by travel of a certain distance until reaching
the next step.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var geometry` | `[GeographicCoordinate]` |  | The full route geometry for this step. |
| `var distance` | `Double` |  | The distance, in meters, to travel along the route after the maneuver to reach the next step. |
| `var duration` | `Double` |  | The estimated duration, in seconds, that it will take to complete this step. |
| `var roadName` | `String?` |  | The name of the road being traveled on (useful for certain UI styles). |
| `var exits` | `[String]` |  | A list of exits (name or number). |
| `var instruction` | `String` |  | A description of the maneuver (ex: "Turn wright onto main street"). Note for UI implementers: the context this appears in (or doesn't) depends somewhat on your use case and routing engine. For example, this field is useful as a written instruction in Valhalla. |
| `var visualInstructions` | `[VisualInstruction]` |  | A list of instructions for visual display (usually as banners) at specific points along the step. |
| `var spokenInstructions` | `[SpokenInstruction]` |  | A list of prompts to announce (via speech synthesis) at specific points along the step. |
| `var annotations` | `[String]?` |  | A list of json encoded strings representing annotations between each coordinate along the step. |
| `var incidents` | `[Incident]` |  | A list of incidents that occur along the step. |
| `var drivingSide` | `DrivingSide?` |  | Which side of the road traffic drives on for this step. This is relevant for roundabouts: left-hand traffic (e.g. UK) uses clockwise roundabouts, while right-hand traffic uses counterclockwise roundabouts. |
| `var roundaboutExitNumber` | `UInt8?` |  | The exit number when entering a roundabout (1 = first exit, 2 = second, etc.). |

**Initialisers and methods**

- `public init(geometry: [GeographicCoordinate], distance: Double, duration: Double, roadName: String?, exits: [String], instruction: String, visualInstructions: [VisualInstruction], spokenInstructions: [SpokenInstruction], annotations: [String]?, incidents: [Incident], drivingSide: DrivingSide?, roundaboutExitNumber: UInt8?)`

##### `SerializableNavState`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct SerializableNavState: Equatable, Hashable, Codable
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var tripState` | `TripState` |  |  |
| `var stepAdvanceCondition` | `SerializableStepAdvanceCondition` |  |  |

**Initialisers and methods**

- `public init(tripState: TripState, stepAdvanceCondition: SerializableStepAdvanceCondition)`

##### `Speed`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct Speed: Equatable, Hashable, Codable
```

The speed of the user from the location provider.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var value` | `Double` |  | The user's speed in meters per second. |
| `var accuracy` | `Double?` |  | The accuracy of the speed value, measured in meters per second. |

**Initialisers and methods**

- `public init(value: Double, accuracy: Double?)`

##### `SpokenInstruction`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct SpokenInstruction: Equatable, Hashable, Codable
```

An instruction that can be synthesized using a TTS engine to announce an upcoming maneuver.

Note that these do not have any locale information attached.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var text` | `String` |  | Plain-text instruction which can be synthesized with a TTS engine. |
| `var ssml` | `String?` |  | Speech Synthesis Markup Language, which should be preferred by clients capable of understanding it. |
| `var triggerDistanceBeforeManeuver` | `Double` |  | How far (in meters) from the upcoming maneuver the instruction should start being spoken. |
| `var utteranceId` | `Uuid` |  | A unique identifier for this instruction. This is provided so that platform-layer integrations can easily disambiguate between distinct utterances, which may have the same textual content. UUIDs conveniently fill this purpose. NOTE: While it is possible to deterministically create UUIDs, we do not do so at this time. This should be theoretically possible though if someone cares to write up a proposal and a PR. |

**Initialisers and methods**

- `public init(text: String, ssml: String?, triggerDistanceBeforeManeuver: Double, utteranceId: Uuid)`

##### `StepAdvanceResult`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct StepAdvanceResult
```

The step advance result is produced on every iteration of the navigation state machine and
used by the navigation to build a new [`NavState`](super::NavState) instance for that update.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var shouldAdvance` | `Bool` |  | The step should be advanced. |
| `var nextIteration` | `StepAdvanceCondition` |  | The next iteration of the step advance condition. This is what the navigation controller passes to the next instance of [`NavState`](super::NavState) on the completion of an update (e.g. a user location update). Use the helper methods on [`StepAdvanceResult`](#stepadvanceresult) to ensure proper state handling: - [`StepAdvanceResult::next()`] - Unified method that takes a boolean and handles state appropriately - [`StepAdvanceResult::advance()`] - Creates an advancing result with automatically reset state - [`StepAdvanceResult::no_advance()`] - Creates a non-advancing result preserving current state The trait method [`StepAdvanceCondition::new_instance()`] ensures that composite conditions (Or/And) properly reset all nested conditions when any condition triggers advancement. **CRITICAL**: When advancing, this must be a clean/reset state to prevent state leakage between steps. |

**Initialisers and methods**

- `public init(shouldAdvance: Bool, nextIteration: StepAdvanceCondition)`

##### `TripProgress`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct TripProgress: Equatable, Hashable, Codable
```

High-level state describing progress through a route.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var distanceToNextManeuver` | `Double` |  | The distance to the next maneuver, in meters. |
| `var distanceRemaining` | `Double` |  | The total distance remaining in the trip, in meters. This is the sum of the distance remaining in the current step and the distance remaining in all subsequent steps. |
| `var durationRemaining` | `Double` |  | The total duration remaining in the trip, in seconds. |

**Initialisers and methods**

- `public init(distanceToNextManeuver: Double, distanceRemaining: Double, durationRemaining: Double)`

##### `TripSummary`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct TripSummary: Equatable, Hashable, Codable
```

Information pertaining to the user's full navigation trip. This includes
simple stats like total duration and distance.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var distanceTraveled` | `Double` |  | The total raw distance traveled in the trip, in meters. |
| `var snappedDistanceTraveled` | `Double` |  | The total snapped distance traveled in the trip, in meters. |
| `var startedAt` | `UtcDateTime` |  | When the trip was started. |
| `var endedAt` | `UtcDateTime?` |  | When the trip was completed or canceled. |

**Initialisers and methods**

- `public init(distanceTraveled: Double, snappedDistanceTraveled: Double, startedAt: UtcDateTime, endedAt: UtcDateTime?)`

##### `UserLocation`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct UserLocation: Equatable, Hashable, Codable
```

The location of the user that is navigating.

In addition to coordinates, this includes estimated accuracy and course information,
which can influence navigation logic and UI.

NOTE: Heading is absent on purpose.
Heading updates are not related to a change in the user's location.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var coordinates` | `GeographicCoordinate` |  |  |
| `var horizontalAccuracy` | `Double` |  | The estimated accuracy of the coordinate (in meters) |
| `var courseOverGround` | `CourseOverGround?` |  |  |
| `var timestamp` | `Date` |  |  |
| `var speed` | `Speed?` |  |  |

**Initialisers and methods**

- `public init(coordinates: GeographicCoordinate, horizontalAccuracy: Double, courseOverGround: CourseOverGround?, timestamp: Date, speed: Speed?)`

##### `ValhallaLocationSearchFilter`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct ValhallaLocationSearchFilter: Equatable, Hashable, Codable
```

A set of optional filters to exclude candidate edges based on their attributes.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var excludeTunnel` | `Bool?` |  | Whether to exclude roads marked as tunnels. |
| `var excludeBridge` | `Bool?` |  | Whether to exclude roads marked as bridges. |
| `var excludeTolls` | `Bool?` |  | Whether to exclude roads with tolls. |
| `var excludeFerry` | `Bool?` |  | Whether to exclude ferries. |
| `var excludeRamp` | `Bool?` |  | Whether to exclude roads marked as ramps. |
| `var excludeClosures` | `Bool?` |  | Whether to exclude roads marked as closed due to a live traffic closure. |
| `var minRoadClass` | `ValhallaRoadClass?` |  | The lowest road class allowed. |
| `var maxRoadClass` | `ValhallaRoadClass?` |  | The highest road class allowed. |
| `var level` | `Float?` |  | If specified, will only consider edges that are on or traverse the passed floor level. It will set `search_cutoff` to a default value of 300 meters if no cutoff value is passed. Additionally, if a `search_cutoff` is passed, it will be clamped to 1000 meters. |

**Initialisers and methods**

- `public init(excludeTunnel: Bool? = nil, excludeBridge: Bool? = nil, excludeTolls: Bool? = nil, excludeFerry: Bool? = nil, excludeRamp: Bool? = nil, excludeClosures: Bool? = nil, minRoadClass: ValhallaRoadClass? = nil, maxRoadClass: ValhallaRoadClass? = nil, level: Float? = nil)`

##### `ValhallaWaypointProperties`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct ValhallaWaypointProperties: Equatable, Hashable, Codable
```

Waypoint properties supported by Valhalla servers.

Our docstrings are short here, since Valhalla is the final authority.
Refer to <https://valhalla.github.io/valhalla/api/turn-by-turn/api-reference/#locations>
for more details, including default values.
Other Valhalla-based APIs such as Stadia Maps or Mapbox may have slightly different defaults.
Refer to your vendor's documentation when in doubt.

NOTE: Waypoint properties will NOT currently be echoed back in OSRM format,
so these are sent to the server one time.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var heading` | `UInt16?` |  | Preferred direction of travel for the start from the location. The heading is indicated in degrees from north in a clockwise direction, where north is 0°, east is 90°, south is 180°, and west is 270°. Avoid specifying this unless you really know what you're doing. Most use cases for this are better served by `preferred_side`. |
| `var headingTolerance` | `UInt16?` |  | How close in degrees a given street's angle must be in order for it to be considered as in the same direction of the heading parameter. |
| `var minimumReachability` | `UInt16?` |  | Minimum number of nodes (intersections) reachable for a given edge (road between intersections) to consider that edge as belonging to a connected region. Disconnected edges are ignored. |
| `var radius` | `UInt16?` |  | The number of meters about this input location within which edges will be considered as candidates for said location. If there are no candidates within this distance, it will return the closest candidate within reason. This allows the routing engine to match another edge which is NOT the closest to your location, but in within this range. This can be useful if you have other constraints and want to disambiguate, but beware that this can lead to very strange results, particularly if you have specified other parameters like `heading`. This is an advanced feature and should only be used with extreme care. |
| `var preferredSide` | `ValhallaWaypointPreferredSide?` |  | Determines whether the location should be visited from the same, opposite or either side of the road, with respect to the side of the road the given locale drives on. NOTE: If the location is not offset from the road centerline or is very close to an intersection, this option has no effect! |
| `var displayCoordinate` | `GeographicCoordinate?` |  | Latitude of the map location in degrees. If provided, the waypoint location will still be used for routing, but these coordinates will determine the side of the street. |
| `var searchCutoff` | `UInt32?` |  | The cutoff at which we will assume the input is too far away from civilization to be worth correlating to the nearest graph elements. |
| `var nodeSnapTolerance` | `UInt16?` |  | During edge correlation, this is the tolerance used to determine whether to snap to the intersection rather than along the street. If the snap location is within this distance from the intersection, the intersection is used instead. |
| `var streetSideTolerance` | `UInt16?` |  | A tolerance in meters from the edge centerline used for determining the side of the street that the location is on. If the distance to the centerline is less than this tolerance, no side will be inferred. Otherwise, the left or right side will be selected depending on the direction of travel. |
| `var streetSideMaxDistance` | `UInt16?` |  | The max distance in meters that the input coordinates or display lat/lon can be from the edge centerline for them to be used for determining the side of the street. Beyond this distance, no street side is inferred. |
| `var streetSideCutoff` | `ValhallaRoadClass?` |  | Disables the `preferred_side` when set to `same` or `opposite` if the edge has a road class less than that provided by `street_side_cutoff`. |
| `var searchFilter` | `ValhallaLocationSearchFilter?` |  | A set of optional filters to exclude candidate edges based on their attributes. |
| `var allowUturns` | `Bool?` | `` `true` `` | Determines whether U-Turns are allowed at the waypoint (if it is an intermediate waypoint, not the first or last). Defaults to `true`. This has the effect of converting a [`WaypointKind::Break`] into a Valhalla `break_through`, and a [`WaypointKind::Via`] into a `through`. |
| `var name` | `String?` |  | Location or business name. The name may be used in the route narration directions, such as "You have arrived at &lt;business name&gt;." WARNING: this param is not echoed back in the response, so this can disappear when rerouting! NOTE: Serialized as `waypoint_name` in the waypoint properties blob so it cannot collide with the `name` of [`OsrmWaypointProperties`](crate::routing_adapters::osrm::models::OsrmWaypointProperties) (the snapped street name), which occupies the same blob on waypoints rebuilt from a route response (e.g. the remaining waypoints used for reroute requests). It is still sent to Valhalla as `name`. |

**Initialisers and methods**

- `public init(heading: UInt16? = nil, headingTolerance: UInt16? = nil, minimumReachability: UInt16? = nil, radius: UInt16? = nil, preferredSide: ValhallaWaypointPreferredSide? = nil, displayCoordinate: GeographicCoordinate? = nil, searchCutoff: UInt32? = nil, nodeSnapTolerance: UInt16? = nil, streetSideTolerance: UInt16? = nil, streetSideMaxDistance: UInt16? = nil, streetSideCutoff: ValhallaRoadClass? = nil, searchFilter: ValhallaLocationSearchFilter? = nil, allowUturns: Bool? = nil, name: String? = nil)`

##### `VisualInstruction`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct VisualInstruction: Equatable, Hashable, Codable
```

An instruction for visual display (usually as banners) at a specific point along a [`RouteStep`](#routestep).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var primaryContent` | `VisualInstructionContent` |  | The primary instruction content. This is usually given more visual weight. |
| `var secondaryContent` | `VisualInstructionContent?` |  | Optional secondary instruction content. |
| `var subContent` | `VisualInstructionContent?` |  | Optional sub-maneuver instruction content. |
| `var triggerDistanceBeforeManeuver` | `Double` |  | How far (in meters) from the upcoming maneuver the instruction should start being displayed |

**Initialisers and methods**

- `public init(primaryContent: VisualInstructionContent, secondaryContent: VisualInstructionContent?, subContent: VisualInstructionContent?, triggerDistanceBeforeManeuver: Double)`

##### `VisualInstructionContent`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct VisualInstructionContent: Equatable, Hashable, Codable
```

The content of a visual instruction.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var text` | `String` |  | The text to display. |
| `var maneuverType` | `ManeuverType?` |  | A standardized maneuver type (if any). |
| `var maneuverModifier` | `ManeuverModifier?` |  | A standardized maneuver modifier (if any). |
| `var roundaboutExitDegrees` | `UInt16?` |  | If applicable, the number of degrees you need to go around the roundabout before exiting. For example, entering and exiting the roundabout in the same direction of travel (as if you had gone straight, apart from the detour) would be an exit angle of 180 degrees. |
| `var laneInfo` | `[LaneInfo]?` |  | Detailed information about the lanes. This is typically only present in sub-maneuver instructions. |
| `var exitNumbers` | `[String]` |  | The exit number (or similar identifier like "8B"). |

**Initialisers and methods**

- `public init(text: String, maneuverType: ManeuverType?, maneuverModifier: ManeuverModifier?, roundaboutExitDegrees: UInt16?, laneInfo: [LaneInfo]?, exitNumbers: [String])`

##### `Waypoint`

*Struct* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public struct Waypoint: Equatable, Hashable, Codable
```

A waypoint along a route.

Within the context of Ferrostar, a route request consists of exactly one [`UserLocation`](#userlocation)
and at least one [`Waypoint`](#waypoint). The route starts from the user's location (which may
contain other useful information like their current course for the [`crate::routing_adapters::RouteRequestGenerator`]
to use) and proceeds through one or more waypoints.

Waypoints are used during route calculation, are tracked throughout the lifecycle of a trip,
and are used for recalculating when the user deviates from the expected route.

Note that support for properties beyond basic geographic coordinates varies by routing engine.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var coordinate` | `GeographicCoordinate` |  |  |
| `var kind` | `WaypointKind` |  |  |
| `var properties` | `Data?` |  | Optional additional properties that will be passed on to the [`crate::routing_adapters::RouteRequestGenerator`]. Most users should prefer convenience functions like [`Waypoint::new_with_valhalla_properties`] (or, on platforms like iOS and Android with `UniFFI` bindings, [`crate::routing_adapters::valhalla::create_waypoint_with_valhalla_properties`]). # Format guidelines This MAY be any format agreed upon by both the request generator and response parser. However, to promote interoperability, all implementations in the Ferrostar codebase MUST use JSON. We selected JSON is selected not because it is good, but because generics (i.e., becoming `Waypoint<T>`, where an example `T` is `ValhallaProperties`) would be way too painful, particularly for foreign code. Especially JavaScript. In any case, [`crate::routing_adapters::RouteRequestGenerator`] and [`crate::routing_adapters::RouteResponseParser`] implementations SHOULD document their level support for this, ideally with an exportable record type. |

**Initialisers and methods**

- `public init(coordinate: GeographicCoordinate, kind: WaypointKind, properties: Data? = nil)`

#### Enums (Ferrostar bindings (generated))

[`BlockedLane`](#blockedlane), [`CourseFiltering`](#coursefiltering), [`DeviationCalculationPolicy`](#deviationcalculationpolicy), [`DeviationKind`](#deviationkind), [`DrivingSide`](#drivingside), [`GraphHopperVoiceUnits`](#graphhoppervoiceunits), [`Impact`](#impact), [`IncidentType`](#incidenttype), [`InstantiationError`](#instantiationerror), [`LocationBias`](#locationbias), [`ManeuverModifier`](#maneuvermodifier), [`ManeuverType`](#maneuvertype), [`ModelError`](#modelerror), [`NavigationRecordingEventData`](#navigationrecordingeventdata), [`ParsingError`](#parsingerror), [`RecordingError`](#recordingerror), [`RouteDeviation`](#routedeviation), [`RouteDeviationTracking`](#routedeviationtracking), [`RouteRequest`](#routerequest), [`RoutingRequestGenerationError`](#routingrequestgenerationerror), [`SerializableStepAdvanceCondition`](#serializablestepadvancecondition), [`SimulationError`](#simulationerror), [`TripState`](#tripstate), [`ValhallaRoadClass`](#valhallaroadclass), [`ValhallaWaypointPreferredSide`](#valhallawaypointpreferredside), [`WaypointAdvanceMode`](#waypointadvancemode), [`WaypointKind`](#waypointkind), [`WellKnownRouteProvider`](#wellknownrouteprovider)

##### `BlockedLane`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum BlockedLane: Equatable, Hashable, Codable
```

The lane type blocked by the incident.

| Case | Description |
| --- | --- |
| `left` |  |
| `leftCenter` |  |
| `leftTurnLane` |  |
| `center` |  |
| `right` |  |
| `rightCenter` |  |
| `rightTurnLane` |  |
| `hov` |  |

##### `CourseFiltering`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum CourseFiltering: Equatable, Hashable, Codable
```

Controls filtering/post-processing of user course by the [`NavigationController`](#navigationcontroller).

| Case | Description |
| --- | --- |
| `snapToRoute` | Snap the user's course to the current step's linestring using the next index in the step's geometry. |
| `raw` | Use the raw course as reported by the location provider with no processing. |

##### `DeviationCalculationPolicy`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum DeviationCalculationPolicy: Equatable, Hashable, Codable
```

Controls when a deviation-aware step-advance condition is allowed to evaluate,
based on the user's current
[`RouteDeviation`](crate::deviation_detection::RouteDeviation) status.

| Case | Description |
| --- | --- |
| `always` | Always evaluate, regardless of deviation status. |
| `whileOnRoute` | Evaluate while the user is still somewhere on the route polyline (current step or any future step), but suspend if the user is completely off the route. |
| `whileOnCurrentStep` | Evaluate only while the user is within an acceptable deviation of the current step's polyline. Suspend on any deviation (whether off-step-but-on-route, or completely off-route). |

##### `DeviationKind`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum DeviationKind: Equatable, Hashable, Codable
```

The kind of deviation from the expected route.

| Case | Description |
| --- | --- |
| `offStepOnRoute(deviationFromStepLine: Double)` | The user is off the current step, but still on the route polyline (on a future step). This can happen when the user takes a shortcut, GPS noise places them on a future step, or the route self-intersects. Step advance conditions can use this to limit advancement. |
| `completelyOffRoute(deviationFromRouteLine: Double)` | The user is off the expected route entirely (not within threshold of any remaining step, neither the current one nor any future one). |

##### `DrivingSide`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum DrivingSide: Equatable, Hashable, Codable
```

Which side of the road traffic drives on.

This is needed by consumers like Android Auto to determine whether
a roundabout should be rendered as clockwise (right-hand traffic)
or counterclockwise (left-hand traffic).

| Case | Description |
| --- | --- |
| `left` |  |
| `right` |  |

##### `GraphHopperVoiceUnits`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum GraphHopperVoiceUnits: Equatable, Hashable, Codable
```

| Case | Description |
| --- | --- |
| `metric` |  |
| `imperial` |  |

##### `Impact`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum Impact: Equatable, Hashable, Codable
```

The impact of the incident that has occurred.

| Case | Description |
| --- | --- |
| `unknown` |  |
| `critical` |  |
| `major` |  |
| `minor` |  |
| `low` |  |

##### `IncidentType`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum IncidentType: Equatable, Hashable, Codable
```

The type of incident that has occurred.

| Case | Description |
| --- | --- |
| `accident` |  |
| `congestion` |  |
| `construction` |  |
| `disabledVehicle` |  |
| `laneRestriction` |  |
| `massTransit` |  |
| `miscellaneous` |  |
| `otherNews` |  |
| `plannedEvent` |  |
| `roadClosure` |  |
| `roadHazard` |  |
| `weather` |  |

##### `InstantiationError`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum InstantiationError: Swift.Error, Equatable, Hashable, Codable, Foundation.LocalizedError
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `OptionsJsonParseError` |  |

##### `LocationBias`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum LocationBias: Equatable, Hashable, Codable
```

Controls how simulated locations deviate from the actual route line.
This simulates real-world GPS behavior where readings often have systematic bias.

| Case | Description |
| --- | --- |
| `left(Double)` | Simulates GPS bias by offsetting locations to the left of the route direction. The f64 parameter specifies the offset distance in meters. |
| `right(Double)` | Simulates GPS bias by offsetting locations to the right of the route direction. The f64 parameter specifies the offset distance in meters. |
| `random(Double)` | Simulates GPS bias by randomly choosing left or right offset on initialization and maintaining that bias throughout the route. The f64 parameter specifies the offset distance in meters. This mimics real-world GPS behavior where bias direction is random but typically remains consistent during a trip. |
| `none` | No position bias - locations follow the route line exactly. This provides "perfect" GPS behavior, useful for testing basic route following without position uncertainty. |

##### `ManeuverModifier`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum ManeuverModifier: Equatable, Hashable, Codable
```

Additional information to further specify a [`ManeuverType`](#maneuvertype).

| Case | Description |
| --- | --- |
| `uTurn` |  |
| `sharpRight` |  |
| `right` |  |
| `slightRight` |  |
| `straight` |  |
| `slightLeft` |  |
| `left` |  |
| `sharpLeft` |  |

##### `ManeuverType`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum ManeuverType: Equatable, Hashable, Codable
```

The broad class of maneuver to perform.

This is usually combined with [`ManeuverModifier`](#maneuvermodifier) in [`VisualInstructionContent`](#visualinstructioncontent).

| Case | Description |
| --- | --- |
| `turn` |  |
| `newName` |  |
| `depart` |  |
| `arrive` |  |
| `merge` |  |
| `onRamp` |  |
| `offRamp` |  |
| `fork` |  |
| `endOfRoad` |  |
| `` `continue` `` |  |
| `roundabout` |  |
| `rotary` |  |
| `roundaboutTurn` |  |
| `notification` |  |
| `exitRoundabout` |  |
| `exitRotary` |  |

##### `ModelError`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum ModelError: Swift.Error, Equatable, Hashable, Codable, Foundation.LocalizedError
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `PolylineGenerationError(error: String)` |  |

##### `NavigationRecordingEventData`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum NavigationRecordingEventData: Equatable, Hashable, Codable
```

The event type.

For full replayability, we record things like rerouting, and not just location updates.

| Case | Description |
| --- | --- |
| `stateUpdate(tripState: TripState, stepAdvanceCondition: SerializableStepAdvanceCondition)` |  |
| `routeUpdate(route: Route)` |  |

##### `ParsingError`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum ParsingError: Swift.Error, Equatable, Hashable, Codable, Foundation.LocalizedError
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `InvalidRouteObject(error: String)` |  |
| `InvalidGeometry(error: String)` |  |
| `MalformedAnnotations(error: String)` |  |
| `InvalidStatusCode(code: String, description: String?)` |  |
| `UnknownParsingError` |  |

##### `RecordingError`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum RecordingError: Swift.Error, Equatable, Hashable, Codable, Foundation.LocalizedError
```

A session recording error.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `SerializationError(error: String)` | Error during serialization. |
| `RecordingNotEnabled` | Recording is not enabled for this controller. |
| `DeserializationError(error: String)` | Error during deserialization. |

##### `RouteDeviation`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum RouteDeviation: Equatable, Hashable, Codable
```

Status information that describes whether the user is proceeding according to the route or not.

Note that the name is intentionally a bit generic to allow for expansion of other states.
For example, we could conceivably add a "wrong way" status in the future.

| Case | Description |
| --- | --- |
| `noDeviation` | The user is proceeding on course within the expected tolerances; everything is normal. |
| `deviation(kind: DeviationKind)` | The user has deviated from the expected route. Check the [`DeviationKind`](#deviationkind) to determine if the user is still on the route polyline (off the current step but on a future step) or off the route entirely. |

##### `RouteDeviationTracking`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum RouteDeviationTracking
```

Determines if the user has deviated from the expected route.

| Case | Description |
| --- | --- |
| `none` | No checks will be done, and we assume the user is always following the route. |
| `staticThreshold(minimumHorizontalAccuracy: UInt16, maxAcceptableDeviation: Double)` | Detects deviation from the route using a configurable static distance threshold from the route line. |
| `custom(detector: RouteDeviationDetector)` | An arbitrary user-defined implementation. You decide with your own [`RouteDeviationDetector`](#routedeviationdetector) implementation! |

##### `RouteRequest`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum RouteRequest: Equatable, Hashable, Codable
```

A route request generated by a [`RouteRequestGenerator`](#routerequestgenerator).

| Case | Description |
| --- | --- |
| `httpPost(url: String, headers: [String: String], body: Data)` |  |
| `httpGet(url: String, headers: [String: String])` |  |

##### `RoutingRequestGenerationError`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum RoutingRequestGenerationError: Swift.Error, Equatable, Hashable, Codable, Foundation.LocalizedError
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `NotEnoughWaypoints` |  |
| `JsonError` |  |
| `UnknownRequestGenerationError` |  |

##### `SerializableStepAdvanceCondition`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum SerializableStepAdvanceCondition: Equatable, Hashable, Codable
```

| Case | Description |
| --- | --- |
| `manual` |  |
| `distanceToEndOfStep(distance: UInt16, minimumHorizontalAccuracy: UInt16)` |  |
| `distanceFromStep(distance: UInt16, minimumHorizontalAccuracy: UInt16, calculationPolicy: DeviationCalculationPolicy)` |  |
| `distanceEntryExit(distanceToEndOfStep: UInt16, distanceAfterEndStep: UInt16, minimumHorizontalAccuracy: UInt16, hasReachedEndOfCurrentStep: Bool)` |  |
| `distanceEntryAndSnappedExit(distanceToEndOfStep: UInt16, distanceAfterEndStep: UInt16, minimumHorizontalAccuracy: UInt16, hasReachedEndOfCurrentStep: Bool)` |  |
| `orAdvanceConditions(conditions: [SerializableStepAdvanceCondition])` |  |
| `andAdvanceConditions(conditions: [SerializableStepAdvanceCondition])` |  |

##### `SimulationError`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum SimulationError: Swift.Error, Equatable, Hashable, Codable, Foundation.LocalizedError
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var errorDescription` | `String?` |  |  |

| Case | Description |
| --- | --- |
| `PolylineError(error: String)` | Errors decoding the polyline string. |
| `NotEnoughPoints` | Not enough points in the input. |

##### `TripState`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum TripState: Equatable, Hashable, Codable
```

The state of a navigation session.

This is produced by [`NavigationController`](super::NavigationController) methods
including [`get_initial_state`](super::NavigationController::get_initial_state)
and [`update_user_location`](super::NavigationController::update_user_location).

| Case | Description |
| --- | --- |
| `idle(userLocation: UserLocation?)` | The navigation controller is idle and there is no active trip. |
| `navigating(currentStepGeometryIndex: UInt64?, userLocation: UserLocation, snappedUserLocation: UserLocation, remainingSteps: [RouteStep], remainingWaypoints: [Waypoint], progress: TripProgress, summary: TripSummary, deviation: RouteDeviation, visualInstruction: VisualInstruction?, spokenInstruction: SpokenInstruction?, annotationJson: String?)` | The navigation controller is actively navigating a trip. |
| `complete(userLocation: UserLocation, summary: TripSummary)` | The navigation controller has reached the end of the trip. |

##### `ValhallaRoadClass`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum ValhallaRoadClass: Equatable, Hashable, Codable
```

A road class in the Valhalla taxonomy.

These are ordered from highest (fastest travel speed) to lowest.

| Case | Description |
| --- | --- |
| `motorway` |  |
| `trunk` |  |
| `primary` |  |
| `secondary` |  |
| `tertiary` |  |
| `unclassified` |  |
| `residential` |  |
| `serviceOther` |  |

##### `ValhallaWaypointPreferredSide`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum ValhallaWaypointPreferredSide: Equatable, Hashable, Codable
```

Specifies a preferred side for departing from / arriving at a location.

Examples:
- Germany drives on the right side of the road. A value of `same` will only allow leaving
or arriving at a location such that it is on your right.
- Australia drives on the left side of the road. Passing a value of `same` will only allow
leaving or arriving at a location such that it is on your left.

| Case | Description |
| --- | --- |
| `same` | You must depart from or arrive at the location on the _same_ side as you drive. |
| `opposite` | You must depart from or arrive at the location on the _opposite_ side as you drive. |
| `either` | No preference; you can depart or arrive from any direction. |

##### `WaypointAdvanceMode`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum WaypointAdvanceMode: Equatable, Hashable, Codable
```

Controls when a waypoint should be marked as complete.

While a route may consist of thousands of points, waypoints are special.
A simple trip will have only one waypoint: the final destination.
A more complex trip may have several intermediate stops.
Just as the navigation state keeps track of which steps remain in the route,
it also tracks which waypoints are still remaining.

Tracking waypoints enables Ferrostar to reroute users when they stray off the route line.
The waypoint advance mode specifies how the framework decides
that a waypoint has been visited (and is removed from the list).

NOTE: Advancing to the next *step* and advancing to the next *waypoint*
are separate processes.
This will not normally cause any issues, but keep in mind that
manually advancing to the next step does not *necessarily* imply
that the waypoint will be marked as complete!

| Case | Description |
| --- | --- |
| `waypointWithinRange(Double)` | Advance when the waypoint is within a certain range of meters from the user's location. This condition is potentially more rigorous, requiring the user to actually visit within a range of every waypoint regardless of step advance. |
| `waypointAlongAdvancingStep(Double)` | Advance when a waypoint is within a certain range of meters of any point on the advancing step. This condition considers the step being advanced, not the user's location. As a result, it can recover when your step advance conditions allow the user to skip forward on the route. |

##### `WaypointKind`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum WaypointKind: Equatable, Hashable, Codable
```

Describes characteristics of the waypoint for routing purposes.

| Case | Description |
| --- | --- |
| `` `break` `` | Starts or ends a leg of the trip. Most routing engines will generate arrival and departure instructions. Some routing engines do not support multi-leg routes, so an intermediate break may behave like a [`WaypointKind::Via`]. |
| `via` | A waypoint that is simply passed through, but will not have any arrival or departure instructions. |

##### `WellKnownRouteProvider`

*Enum* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public enum WellKnownRouteProvider: Equatable, Hashable, Codable
```

Configurations for built-in route providers.

| Case | Description |
| --- | --- |
| `valhalla(endpointUrl: String, profile: String, optionsJson: String? = nil)` | A Valhalla-based routing API. [Stadia Maps](https://docs.stadiamaps.com/) offers a commercially supported API. You can also substitute any URL to use a self-hosted server. |
| `graphHopper(endpointUrl: String, profile: String, locale: String, voiceUnits: GraphHopperVoiceUnits, optionsJson: String? = nil)` | A GraphHopper-based routing API. [GraphHopper](https://www.graphhopper.com/) offers a commercially supported API. You can also substitute any other URL to use a self-hosted server. |

#### Protocols (Ferrostar bindings (generated))

[`AndAdvanceConditionsProtocol`](#andadvanceconditionsprotocol), [`DistanceEntryAndExitConditionProtocol`](#distanceentryandexitconditionprotocol), [`DistanceEntryAndSnappedExitConditionProtocol`](#distanceentryandsnappedexitconditionprotocol), [`DistanceFromStepConditionProtocol`](#distancefromstepconditionprotocol), [`DistanceToEndOfStepConditionProtocol`](#distancetoendofstepconditionprotocol), [`ManualStepConditionProtocol`](#manualstepconditionprotocol), [`NavigationCache`](#navigationcache), [`NavigationControllerProtocol`](#navigationcontrollerprotocol), [`NavigationObserver`](#navigationobserver), [`NavigationRecorderProtocol`](#navigationrecorderprotocol), [`NavigationReplayProtocol`](#navigationreplayprotocol), [`NavigationSessionCacheProtocol`](#navigationsessioncacheprotocol), [`NavigationSessionProtocol`](#navigationsessionprotocol), [`NavigatorProtocol`](#navigatorprotocol), [`OrAdvanceConditionsProtocol`](#oradvanceconditionsprotocol), [`RouteAdapterProtocol`](#routeadapterprotocol), [`RouteDeviationDetector`](#routedeviationdetector), [`RouteRequestGenerator`](#routerequestgenerator), [`RouteResponseParser`](#routeresponseparser), [`StepAdvanceConditionProtocol`](#stepadvanceconditionprotocol)

##### `AndAdvanceConditionsProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol AndAdvanceConditionsProtocol: AnyObject, Sendable
```

Advance if all of the conditions are met (AND).

##### `DistanceEntryAndExitConditionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol DistanceEntryAndExitConditionProtocol: AnyObject, Sendable
```

A stateful condition that requires the user to reach the end of the step then proceed past it to advance.

##### `DistanceEntryAndSnappedExitConditionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol DistanceEntryAndSnappedExitConditionProtocol: AnyObject, Sendable
```

A stateful condition that requires the user to reach the end of the step then proceed past it to advance.

This variant uses route snapping (snapping to the combined current+next step geometry) for the exit check,
making it more robust for pedestrian/hiking navigation where users may walk on the opposite side of the street
or wander around the optimal path. The route-snapped exit check prevents premature advancement while still
allowing natural pedestrian movement patterns.
The exit distance is measured from the current step to the route-snapped position.

##### `DistanceFromStepConditionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol DistanceFromStepConditionProtocol: AnyObject, Sendable
```

Advances once the user is at least [`Self::distance`] meters from the current step's polyline.

This results in *delayed* advance,
but is more robust to spurious / unwanted step changes in scenarios including
self-intersecting routes (sudden jumps to the next step)
and pauses at intersections (advancing too soon before the maneuver is complete).

NOTE! This may be less robust to things like short steps, out-and-backs, and U-turns,
where this may eagerly exit a current step before the user has traversed it
if the start of the step is within range of the end.

##### `DistanceToEndOfStepConditionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol DistanceToEndOfStepConditionProtocol: AnyObject, Sendable
```

Automatically advances when the user's location is close enough to the end of the step.

This results in an eager advance where the user will jump to the next step when the
condition is met.

##### `ManualStepConditionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol ManualStepConditionProtocol: AnyObject, Sendable
```

Never advances to the next step automatically;
requires calling [`NavigationController::advance_to_next_step`](super::NavigationController::advance_to_next_step).

You can use this to implement custom behaviors in external code.

##### `NavigationCache`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationCache: AnyObject, Sendable
```

**Initialisers and methods**

- `func save(record: Data)`
- `func load() -> Data?`
- `func delete()`

##### `NavigationControllerProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationControllerProtocol: AnyObject, Sendable
```

Manages the navigation lifecycle through a route,
returning an updated state given inputs like user location.

Notes for implementing a new platform:
- A controller is bound to a single route; if you want recalculation, create a new instance.
- This is a pure type (no interior mutability), so a core function of your platform code is responsibly managing mutable state.

##### `NavigationObserver`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationObserver: AnyObject, Sendable
```

**Initialisers and methods**

- `func onGetInitialState(state: NavState)`
- `func onUserLocationUpdate(location: UserLocation, state: NavState)`
- `func onAdvanceToNextStep(state: NavState)`
- `func onRouteAvailable(route: Route)`

##### `NavigationRecorderProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationRecorderProtocol: AnyObject, Sendable
```

**Initialisers and methods**

- `func getEvents() -> [NavigationRecordingEvent]`
- `func getRecordingJson() throws -> String`
- `func onAdvanceToNextStep(state: NavState)`
- `func onGetInitialState(state: NavState)`
- `func onRouteAvailable(route: Route)`
- `func onUserLocationUpdate(location: UserLocation, state: NavState)`

##### `NavigationReplayProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationReplayProtocol: AnyObject, Sendable
```

A wrapper around `NavigationRecording` to facilitate replaying the event stream.

##### `NavigationSessionCacheProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationSessionCacheProtocol: AnyObject, Sendable
```

**Initialisers and methods**

- `func canResume() -> Bool`: Check if the navigation session can be resumed.
- `func load() -> NavigationSessionSnapshot?`: Load the navigation session record from the cache if it exists and is not stale.
- `func onAdvanceToNextStep(state: NavState)`
- `func onGetInitialState(state: NavState)`
- `func onRouteAvailable(route: Route)`
- `func onUserLocationUpdate(location: UserLocation, state: NavState)`

##### `NavigationSessionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigationSessionProtocol: AnyObject, Sendable
```

**Initialisers and methods**

- `func advanceToNextStep(state: NavState) -> NavState`
- `func getInitialState(location: UserLocation) -> NavState`
- `func route() -> Route`
- `func updateUserLocation(location: UserLocation, state: NavState) -> NavState`

##### `NavigatorProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol NavigatorProtocol: AnyObject, Sendable
```

Core interface for navigation functionalities.

This trait defines the essential operations for a navigation state manager.
This lets us build additional layers (e.g. event logging)
around [`NavigationController`](#navigationcontroller) in a composable manner.

**Initialisers and methods**

- `func route() -> Route`
- `func getInitialState(location: UserLocation) -> NavState`
- `func advanceToNextStep(state: NavState) -> NavState`
- `func updateUserLocation(location: UserLocation, state: NavState) -> NavState`

##### `OrAdvanceConditionsProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol OrAdvanceConditionsProtocol: AnyObject, Sendable
```

Advance if any of the conditions are met (OR).

This is ideal for short circuit type advance conditions.

E.g. you may have:
1. A short circuit detecting if the user has exceeded a large distance from the current step.
2. A default advance behavior.

##### `RouteAdapterProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol RouteAdapterProtocol: AnyObject, Sendable
```

The route adapter bridges between the common core and a routing backend where interaction takes place
over a generic request/response flow (typically over a network;
local/offline routers **do not use this object** as the interaction patterns are different).

This is essentially the composite of the [`RouteRequestGenerator`](#routerequestgenerator) and [`RouteResponseParser`](#routeresponseparser)
traits, but it provides one further level of abstraction which is helpful to consumers.
As there is no way to signal compatibility between request generators and response parsers,
the [`RouteAdapter`](#routeadapter) provides convenience constructors which take the guesswork out of it,
while still leaving consumers free to implement one or both halves.

In the future, we may provide additional methods or conveniences, and this
indirection leaves the design open to such changes without necessarily breaking source
compatibility.
One such possible extension would be the ability to fetch more detailed attributes in real time.
This is supported by the Valhalla stack, among others.

Ideas  welcome re: how to signal compatibility between request generators and response parsers.
I don't think we can do this in the type system, since one of the reasons for the split design
is modularity, including the possibility of user-provided implementations, and these will not
always be of a "known" type to the Rust side.

**Initialisers and methods**

- `func generateRequest(userLocation: UserLocation, waypoints: [Waypoint]) throws -> RouteRequest`
- `func parseResponse(response: Data) throws -> [Route]`

##### `RouteDeviationDetector`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol RouteDeviationDetector: AnyObject, Sendable
```

A custom deviation detector (for extending the behavior of [`RouteDeviationTracking`](#routedeviationtracking)).

This allows for arbitrarily complex implementations when the provided ones are not enough.
For example, detecting that the user is proceeding the wrong direction by keeping a ring buffer
of recent locations, or perform local map matching.

**Initialisers and methods**

- `func checkRouteDeviation(route: Route, tripState: TripState) -> RouteDeviation`: Determines whether the user is following the route correctly or not. NOTE: This function has a single responsibility. Side-effects like whether to recalculate a route are left to higher levels, and implementations should only be concerned with determining the facts. IMPORTANT: If you are short circuiting [`StepAdvanceCondition`](#stepadvancecondition)'s to allow skipping steps, you must always fall back to checking the deviation from the full route line.

##### `RouteRequestGenerator`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol RouteRequestGenerator: AnyObject, Sendable
```

A trait describing any object capable of generating [`RouteRequest`](#routerequest)s.

The interface is intentionally generic. Every routing backend has its own set of
parameters, including a "profile," max travel speed, units of speed and distance, and more.
It is assumed that these properties will be set at construction time or otherwise configured
before use, so that we can keep the public interface as generic as possible.

Implementations may be either in Rust (most popular engines should eventually have Rust
glue code) or foreign code.

**Initialisers and methods**

- `func generateRequest(userLocation: UserLocation, waypoints: [Waypoint]) throws -> RouteRequest`: Generates a routing backend request given the set of locations. While most implementations will treat the locations as an ordered sequence, this is not guaranteed (ex: an optimized router).

##### `RouteResponseParser`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol RouteResponseParser: AnyObject, Sendable
```

A generic interface describing any object capable of parsing a response from a routing
backend into one or more [`Route`](#route)s.

**Initialisers and methods**

- `func parseResponse(response: Data) throws -> [Route]`: Parses a raw response from the routing backend into a route. We use a sequence of octets as a common interchange format. as this works for all currently conceivable formats (JSON, PBF, etc.).

##### `StepAdvanceConditionProtocol`

*Protocol* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public protocol StepAdvanceConditionProtocol: AnyObject, Sendable
```

When implementing custom step advance logic, this trait allows you to define
whether the condition should advance to the next condition, the next step or not.

At the moment, these must be implemented in Rust.

**Initialisers and methods**

- `func shouldAdvanceStep(tripState: TripState) -> StepAdvanceResult`: This callback method is used by a step advance condition to receive step updates. The step advance condition can choose based on its outcome and internal state whether to advance to the next step or not.
- `func newInstance() -> StepAdvanceCondition`: Creates a clean instance of this condition with the same configuration but reset state. This is used by composite conditions (Or/And) to ensure proper state isolation when any condition triggers advancement. **Implementation Requirements:** - **Stateless conditions**: Return a copy/clone of self - **Stateful conditions**: Return a new instance with initial state but preserve configuration parameters - **Composite conditions**: Recursively create fresh instances of all nested conditions This method prevents the state leakage bugs that can cause rapid step advancement and jumping behavior in navigation.

#### Functions (Ferrostar bindings (generated))

[`advanceLocationSimulation`](#advancelocationsimulation), [`createFerrostarLogger`](#createferrostarlogger), [`createNavigationSession`](#createnavigationsession), [`createNavigator`](#createnavigator), [`createOsrmResponseParser`](#createosrmresponseparser), [`createRouteFromOsrm`](#createroutefromosrm), [`createRouteFromOsrmRoute`](#createroutefromosrmroute), [`createValhallaRequestGenerator`](#createvalhallarequestgenerator), [`createWaypointWithValhallaProperties`](#createwaypointwithvalhallaproperties), [`getRoutePolyline`](#getroutepolyline), [`locationSimulationFromCoordinates`](#locationsimulationfromcoordinates), [`locationSimulationFromPolyline`](#locationsimulationfrompolyline), [`locationSimulationFromRoute`](#locationsimulationfromroute), [`stepAdvanceAnd`](#stepadvanceand), [`stepAdvanceDistanceEntryAndExit`](#stepadvancedistanceentryandexit), [`stepAdvanceDistanceEntryAndSnappedExit`](#stepadvancedistanceentryandsnappedexit), [`stepAdvanceDistanceFromStep`](#stepadvancedistancefromstep), [`stepAdvanceDistanceToEndOfStep`](#stepadvancedistancetoendofstep), [`stepAdvanceManual`](#stepadvancemanual), [`stepAdvanceOr`](#stepadvanceor)

##### `advanceLocationSimulation`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func advanceLocationSimulation(state: LocationSimulationState) -> LocationSimulationState
```

Returns the next simulation state based on the desired strategy.
Results of this can be thought of like a stream from a generator function.

This function is intended to be called once/second.
However, the caller may vary speed to purposefully replay at a faster rate
(ex: calling 3x per second will be a triple speed simulation).

When there are now more locations to visit, returns the same state forever.

##### `createFerrostarLogger`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createFerrostarLogger()
```

##### `createNavigationSession`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createNavigationSession(route: Route, config: NavigationControllerConfig, observers: [NavigationObserver]) -> NavigationSession
```

Creates a new navigation session for the given route and configuration.

It returns an Arc-wrapped trait object implementing `Navigator`.
If `should_record` is true, it creates a controller with event recording enabled.

##### `createNavigator`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createNavigator(route: Route, config: NavigationControllerConfig, shouldRecord: Bool) -> Navigator
```

Creates a new navigation controller for the given route and configuration.

It returns an Arc-wrapped trait object implementing `Navigator`.
If `should_record` is true, it creates a controller with event recording enabled.

##### `createOsrmResponseParser`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createOsrmResponseParser(polylinePrecision: UInt32) -> RouteResponseParser
```

Creates a [`RouteResponseParser`](#routeresponseparser) capable of parsing OSRM responses.

This response parser is designed to be fairly flexible,
supporting both vanilla OSRM and enhanced Valhalla (ex: from Stadia Maps and Mapbox) outputs
which contain richer information like banners and voice instructions for navigation.

##### `createRouteFromOsrm`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createRouteFromOsrm(routeData: Data, waypointData: Data, polylinePrecision: UInt32) throws -> Route
```

Creates a [`Route`](#route) from OSRM data.

This uses the same logic as the `OsrmResponseParser` and is designed to be fairly flexible,
supporting both vanilla OSRM and enhanced Valhalla (ex: from Stadia Maps and Mapbox) outputs
which contain richer information like banners and voice instructions for navigation.

##### `createRouteFromOsrmRoute`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createRouteFromOsrmRoute(routeData: Data, waypoints: [Waypoint], polylinePrecision: UInt32) throws -> Route
```

Creates a [`Route`](#route) from OSRM route data and ferrostar waypoints.

This uses the same logic as the `OsrmResponseParser` and is designed to be fairly flexible,
supporting both vanilla OSRM and enhanced Valhalla (ex: from Stadia Maps and Mapbox) outputs
which contain richer information like banners and voice instructions for navigation.

##### `createValhallaRequestGenerator`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createValhallaRequestGenerator(endpointUrl: String, profile: String, optionsJson: String?) throws -> RouteRequestGenerator
```

Creates a [`RouteRequestGenerator`](#routerequestgenerator)
which generates requests to an arbitrary Valhalla server (using the OSRM response format).

This is provided as a convenience for use from foreign code when creating your own [`routing_adapters::RouteAdapter`].

##### `createWaypointWithValhallaProperties`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func createWaypointWithValhallaProperties(coordinate: GeographicCoordinate, kind: WaypointKind, properties: ValhallaWaypointProperties) -> Waypoint
```

A convenience helper for creating waypoints with Valhalla rich location properties.

Regrettably this must live as a top-level function unless constructors for record types lands
in `UniFFI`:
<https://github.com/mozilla/uniffi-rs/issues/1935>.

##### `getRoutePolyline`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func getRoutePolyline(route: Route, precision: UInt32) throws -> String
```

Helper function for getting the route as an encoded polyline.

Mostly used for debugging.

##### `locationSimulationFromCoordinates`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func locationSimulationFromCoordinates(coordinates: [GeographicCoordinate], resampleDistance: Double?, bias: LocationBias) throws -> LocationSimulationState
```

Creates a location simulation from a set of coordinates.

Optionally resamples the input line so that there is a maximum distance between points.

##### `locationSimulationFromPolyline`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func locationSimulationFromPolyline(polyline: String, precision: UInt32, resampleDistance: Double?, bias: LocationBias) throws -> LocationSimulationState
```

Creates a location simulation from a polyline.

Optionally resamples the input line so that there is no more than the specified maximum distance between points.

##### `locationSimulationFromRoute`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func locationSimulationFromRoute(route: Route, resampleDistance: Double?, bias: LocationBias) throws -> LocationSimulationState
```

Creates a location simulation from a route.

Optionally resamples the route geometry so that there is no more than the specified maximum distance between points.

##### `stepAdvanceAnd`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceAnd(conditions: [StepAdvanceCondition]) -> StepAdvanceCondition
```

Convenience function for creating an [`AndAdvanceConditions`](#andadvanceconditions).

This composes multiple conditions together and advances to the next step if ALL of them trigger.

##### `stepAdvanceDistanceEntryAndExit`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceDistanceEntryAndExit(distanceToEndOfStep: UInt16, distanceAfterEndOfStep: UInt16, minimumHorizontalAccuracy: UInt16) -> StepAdvanceCondition
```

Convenience function for creating a [`DistanceEntryAndExitCondition`](#distanceentryandexitcondition).

Requires the user to first travel within `distance_to_end_of_step` meters of the end of the step,
and then travel at least `distance_after_end_of_step` meters away from the step geometry.
This ensures the user completes the maneuver before advancing to the next step.

##### `stepAdvanceDistanceEntryAndSnappedExit`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceDistanceEntryAndSnappedExit(distanceToEndOfStep: UInt16, distanceAfterEndOfStep: UInt16, minimumHorizontalAccuracy: UInt16) -> StepAdvanceCondition
```

Convenience function for creating a [`DistanceEntryAndSnappedExitCondition`](#distanceentryandsnappedexitcondition).

This variant uses route snapping for better handling of pedestrian/hiking navigation scenarios
where users may walk on the opposite side of the street or wander around the optimal path.
Requires the user to first travel within `distance_to_end_of_step` meters of the end of the step,
and then the route-snapped position moves `distance_after_end_of_step` meters from the current step.
The snapping to the combined route (current+next steps) prevents premature advancement.

The exit distance is automatically capped to the next step's length to prevent getting stuck on short steps.

Recommended values for pedestrian navigation: entry 20m, exit 2-5m.

##### `stepAdvanceDistanceFromStep`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceDistanceFromStep(distance: UInt16, minimumHorizontalAccuracy: UInt16, calculationPolicy: DeviationCalculationPolicy) -> StepAdvanceCondition
```

Convenience function for creating a [`DistanceFromStepCondition`](#distancefromstepcondition).

This advances to the next step when the user is at least `distance` meters away from any point
on the current route step geometry.
Does not advance unless the reported location accuracy is `minimum_horizontal_accuracy`
meters or better.

`calculation_policy` controls when this condition is permitted to evaluate
based on the user's current route deviation status.

##### `stepAdvanceDistanceToEndOfStep`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceDistanceToEndOfStep(distance: UInt16, minimumHorizontalAccuracy: UInt16) -> StepAdvanceCondition
```

Convenience function for creating a [`DistanceToEndOfStepCondition`](#distancetoendofstepcondition).

This advances to the next step when the user is within `distance` meters of the last point in the current route step.
Does not advance unless the reported location accuracy is `minimum_horizontal_accuracy` meters or better.

##### `stepAdvanceManual`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceManual() -> StepAdvanceCondition
```

Convenience function for creating a [`ManualStepCondition`](#manualstepcondition).

This never advances to the next step automatically.
You must manually advance to the next step programmatically using a `FerrostarCore`
platform wrapper or by calling [`super::Navigator::advance_to_next_step`] manually.

##### `stepAdvanceOr`

*Function* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public func stepAdvanceOr(conditions: [StepAdvanceCondition]) -> StepAdvanceCondition
```

Convenience function for creating an [`OrAdvanceConditions`](#oradvanceconditions).

This composes multiple conditions together and advances to the next step if ANY of them trigger.

#### Type aliases (Ferrostar bindings (generated))

[`UtcDateTime`](#utcdatetime), [`Uuid`](#uuid)

##### `UtcDateTime`

*Type alias* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public typealias UtcDateTime = Date
```

Typealias from the type name used in the UDL file to the custom type.  This
is needed because the UDL type name is used in function/method signatures.

##### `Uuid`

*Type alias* · `Sources/MapMapKit/Generated/ferrostar.swift`

```swift
public typealias Uuid = UUID
```

Typealias from the type name used in the UDL file to the custom type.  This
is needed because the UDL type name is used in function/method signatures.

## MapMapValhalla

### Classes (MapMapValhalla)

[`ValhallaMobileRouter`](#valhallamobilerouter)

#### `ValhallaMobileRouter`

*Class* · `Sources/MapMapValhalla/ValhallaMobileRouter.swift`

```swift
public final class ValhallaMobileRouter: OfflineRouter, @unchecked Sendable
```

`OfflineRouter` backed by the on-device Valhalla engine:
valhalla-mobile's wrapper (https://github.com/Rallista/valhalla-mobile,
MIT, SwiftPM product "Valhalla", iOS 16.4+) compiled against the
Valhalla release and patch series the factory builds tiles with
(tools/mobile-engine/engine.pin; `mapMapValhallaEngineVersion` in
Package.swift). An engine older than the tiles answers 171 "No suitable
edges" on every territory, which is why the version is pinned and
guarded.

valhalla-mobile's `Valhalla` class takes a raw Valhalla request JSON and
returns the raw response JSON. From 0.6 it throws Valhalla's errors
instead of returning them; this router turns a thrown error back into
the engine's `{"code", "message"}` envelope, which the Rust core parses
and classifies exactly as Android's engine returns it.

This class lives in the separate `MapMapValhalla` product so
apps opt in to the engine (which embeds a prebuilt
`valhalla-wrapper.xcframework`), the SwiftPM equivalent of the
Android SDK's `compileOnly` seam. Routing is CPU-bound native work, call it through
`MapMapKit/OfflineRouting/route(router:waypoints:costing:adr:)`,
which hops off the caller's executor.

**Initialisers and methods**

- `public init(configPath: String) throws`: Create a router from an absolute path of a `valhalla.json` whose `mjolnir.tile_dir` points at the territory's Valhalla tile layer.
- `public static func fromConfig(_ configFile: URL) throws -> ValhallaMobileRouter`: Create a router from an existing `valhalla.json` config file.
- `public static func fromTileDir(_ tileDir: URL, scratchDir: URL) throws -> ValhallaMobileRouter`: Create a router for a territory's Valhalla tile directory, writing a config file under `scratchDir` (e.g. the app's caches directory). ``swift let tiles = try await store.layerPath(territoryId: id, kind: .valhallaTiles) let router = try ValhallaMobileRouter.fromTileDir(tiles, scratchDir: cachesDir) ``
- `public func route(requestJson: String) -> String`
- `public func close()`: Release the native engine and the mapped tiles. Routing after this returns an error envelope.

**Example**

```swift
import MapMapKit
import MapMapValhalla

// On-device routing over an installed territory's Valhalla tiles.
let store = TerritoryStore(rootDir: URL.applicationSupportDirectory, verifyingKeyHex: TerritoryKeys.hostedFactoryKeyHex)
let tiles = try await store.layerPath(territoryId: "uk", kind: .valhallaTiles)
let router = try ValhallaMobileRouter.fromTileDir(tiles, scratchDir: URL.cachesDirectory)
let route = try await OfflineRouting.route(
    router: router,
    waypoints: [RoutePoint(lat: 51.5072, lon: -0.1276), RoutePoint(lat: 51.4545, lon: -2.5879)],
    costing: .truck,
    adr: MapMap.defaultAdrProfile()
)
let engine = NavigationEngine(route: route)
print(try await engine.totalSteps())
router.close()
```

## MapMapCarPlay

### Classes (MapMapCarPlay)

[`CarPlayManager`](#carplaymanager), [`ExampleCarPlaySceneDelegate`](#examplecarplayscenedelegate)

#### `CarPlayManager`

*Class* · `Sources/MapMapCarPlay/CarPlayManager.swift`

```swift
public final class CarPlayManager: NSObject
```

Orchestrates one CarPlay connection end-to-end: the `CPMapTemplate`
root and its delegate, the browse → preview → navigate activity states,
and the active `CPNavigationSession`'s lifecycle.

This is the class Mapbox's Navigation SDK ships
(`CarPlayManager`/`CarPlayManagerDelegate`) and that Ferrostar's own
docs point to as a gap in `FerrostarCarPlayUI`, Ferrostar leaves this
wiring to each app's demo code (see `DemoCarPlayModel.swift`). MapMap
ships it as SDK surface instead: host apps forward their
`CPTemplateApplicationSceneDelegate` callbacks to
`sceneConnected(interfaceController:window:)`/`sceneDisconnected(interfaceController:)`
and implement `CarPlayManagerDelegate` for the parts that are
necessarily app-specific (the map view, route selection, trip start).

Not a port of any Ferrostar source, this orchestration layer does not
exist in `FerrostarCarPlayUI`. See `ExampleCarPlaySceneDelegate.swift`
for the exact scene-delegate wiring.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `var isWaitingForConfiguration` |  |  | `true` from init until `sceneConnected(interfaceController:window:)` has run once. CarPlay scene callbacks are not guaranteed to arrive in the order you'd expect, iOS 18.5 has been observed delivering `sceneWillEnterForeground` on the CarPlay scene *before* `didConnectInterfaceController:toWindow:`, so anything that needs `interfaceController`/`window`/`mapTemplate` must check this (or simply rely on those properties being `nil`) rather than assume connection has completed just because *some* scene callback fired. This mirrors OsmAnd's CarPlay `isWaitingForConfiguration` pattern. |
| `var interfaceController` | `CPInterfaceController?` |  |  |
| `var carWindow` | `CPWindow?` |  |  |
| `var mapTemplate` | `CPMapTemplate?` |  |  |
| `var scaleAdjuster` | `CarPlayWindowScaleAdjuster?` |  |  |
| `var activity` | `CarPlayActivity` | `.browsing` |  |
| `var navigationSession` | `CPNavigationSession?` |  | The active navigation session, when `activity` is `.navigating`. Held here (not duplicated by the delegate/host) so there is exactly one owner of the CarPlay-side trip lifecycle, per the platform note that duplicate references to the same session are a common source of "which copy is authoritative" bugs. |
| `var sessionConfiguration` | `CPSessionConfiguration?` |  | The CarPlay-suggested UI limits (keyboard/list availability) and content style (light/dark), kept live via `CPSessionConfigurationDelegate`. Consult `sessionConfiguration.limitedUserInterfaces` before presenting anything keyboard-driven (e.g. a `CPSearchTemplate`), CarPlay screens without a keyboard input (many aftermarket and built-in units) will otherwise dead-end the driver. This SDK does not ship a search template itself; if you add one, gate it on this. |
| `var delegate` | `CarPlayManagerDelegate?` |  |  |

**Initialisers and methods**

- `public override init()`
- `public func sceneConnected(interfaceController: CPInterfaceController, window: CPWindow)`: Forward `templateApplicationScene(_:didConnectInterfaceController:toWindow:)` here. Creates the root `CPMapTemplate`, sets it as the scene's root template, and, only once `window` is actually attached to its scene, asks the delegate for a map view controller. Creating a MapLibre view before the window attaches is a known blank-map/crash bug class (the view has no real drawable size yet); this ordering is why the delegate callback happens here and not in `init`.
- `public func sceneDisconnected()`: Forward `templateApplicationScene(_:didDisconnectInterfaceController:fromWindow:)` (or the window-less variant) here. Ends any active trip and releases everything scoped to the connection.
- `public func previewRoutes(for trip: CPTrip, textConfiguration: CPTripPreviewTextConfiguration? = nil)`: Show a route-choice preview for `trip` (`CPMapTemplate.showRouteChoicesPreview`). Call once route(s) have been computed for a chosen destination.
- `public func startTrip(_ trip: CPTrip, using routeChoice: CPRouteChoice) -> CPNavigationSession?`: Start guidance for `routeChoice`. Ends any previous session first, there is only ever one active `navigationSession`.
- `public func updateNavigationState(_ state: NavigationState, units: CarPlayDistanceUnits = .localeDefault, locale: Locale = .current)`: Push the latest `NavigationState` from your `NavigationEngine` onto the active session. No-op if there is no active trip.
- `public func stopTrip(cancelled: Bool)`: End the active trip, if any. - Parameter cancelled: `true` for a driver/system cancellation (`cancelTrip()`); `false` for a normal finish, e.g. on arrival (`finishTrip()`).
- `public func refreshChrome()`: Re-apply the current activity's chrome (buttons), call after something that affects them changes (e.g. a mute toggle), since `CarPlayManagerDelegate` is asked fresh each time.

**Example**

```swift
import CarPlay
import MapMapCarPlay

final class SceneDelegate: NSObject, CPTemplateApplicationSceneDelegate {
    let carPlay = CarPlayManager()

    func templateApplicationScene(_ scene: CPTemplateApplicationScene,
                                  didConnect interfaceController: CPInterfaceController,
                                  to window: CPWindow) {
        carPlay.sceneConnected(interfaceController: interfaceController, window: window)
    }

    func templateApplicationScene(_ scene: CPTemplateApplicationScene,
                                  didDisconnect interfaceController: CPInterfaceController,
                                  from window: CPWindow) {
        carPlay.sceneDisconnected()
    }
}
// While navigating: carPlay.updateNavigationState(state) for every NavigationState.
```

#### `ExampleCarPlaySceneDelegate`

*Class* · `Sources/MapMapCarPlay/ExampleCarPlaySceneDelegate.swift`

```swift
public final class ExampleCarPlaySceneDelegate: NSObject, CPTemplateApplicationSceneDelegate
```

Example `CPTemplateApplicationSceneDelegate`: forwards scene connect/
disconnect straight to a `CarPlayManager`. This is the entire
contract, everything else (template stack, activity states,
navigation session lifecycle) lives in the manager.

**Initialisers and methods**

- `public func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didConnect interfaceController: CPInterfaceController, to window: CPWindow)`
- `public func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didDisconnect interfaceController: CPInterfaceController, from window: CPWindow)`

**Example**

```swift
// Info.plist, UIApplicationSceneManifest > UISceneConfigurations >
// CPTemplateApplicationSceneSessionRoleApplication:
//   UISceneDelegateClassName = MapMapCarPlay.ExampleCarPlaySceneDelegate
// It forwards connect and disconnect to a CarPlayManager, as in the CarPlayManager example.
import MapMapCarPlay

let delegate = ExampleCarPlaySceneDelegate()
print(delegate)
```

### Structs (MapMapCarPlay)

[`CarPlayMeasurementLength`](#carplaymeasurementlength), [`CarPlayWindowScaleAdjuster`](#carplaywindowscaleadjuster), [`RoadRef`](#roadref)

#### `CarPlayMeasurementLength`

*Struct* · `Sources/MapMapCarPlay/Models/CarPlayDistanceUnits.swift`

```swift
public struct CarPlayMeasurementLength: Sendable
```

A CarPlay-ready distance measurement: picks short vs. long units from a
raw metre value, and rounds it to the step sizes CarPlay's estimate
cards expect (never a distractingly precise number while driving).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let measurement` | `Measurement<UnitLength>` |  |  |

**Initialisers and methods**

- `public init(units: CarPlayDistanceUnits, distanceM: Double, locale: Locale = .current)`
- `public func rounded() -> Measurement<UnitLength>`: Round to the step CarPlay's own trip-estimate cards use: 5 m/ft/yd under 50, 10 under 100, 50 under 500, 100 above; for kilometres or miles, whole numbers above 10, one decimal below.

#### `CarPlayWindowScaleAdjuster`

*Struct* · `Sources/MapMapCarPlay/CarPlayWindowScaleAdjuster.swift`

```swift
public struct CarPlayWindowScaleAdjuster: Sendable
```

Mitigation for a known MapLibre Native iOS bug class: the renderer
computes its display scale from `UIScreen.main` rather than the screen
the map view is actually attached to
(https://github.com/maplibre/maplibre-native/issues/3214). On CarPlay
that produces the wrong pixel density, usually oversized or blurry
labels/icons, because `CPWindow.screen` is very rarely the same
`UIScreen` as the phone's.

This mirrors the approach Organic Maps' CarPlay integration uses
(`CarPlayWindowScaleAdjuster`, compare the two screens' native scales
and adjust the visual scale the map is told to render at): MapMap
is headless, so it cannot reach into the host's MapLibre view directly,
but this computes the correction factor and hands it to the host to
apply to their own map view (e.g. as a style zoom-level offset, or by
overriding the scale MapLibre reads if their integration exposes that
hook).

Usage: create this **after** the `CPWindow` has attached (see
`CarPlayManager`'s connect flow, never before, the window has no
`screen` until then), and again on `contentStyleDidChange`/on every
reattach, since a CarPlay unit can be swapped or reconnected with a
different physical display mid-session.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let carScreenNativeScale` | `CGFloat` |  | The native scale (points → pixels) of the screen the CarPlay window is actually presented on. |
| `let phoneScreenNativeScale` | `CGFloat` |  | The native scale of the phone's own main screen, what MapLibre Native (pre-fix) actually renders at regardless of which screen the view is attached to. |
| `var correctionFactor` | `CGFloat` |  | Multiply MapLibre's reported/assumed scale by this to get the scale the CarPlay screen actually needs. `1.0` when the two screens happen to share a native scale (nothing to correct) or when the phone's scale could not be determined. |

**Initialisers and methods**

- `public init(carWindow: CPWindow, phoneScreen: UIScreen = .main)`: - Parameters: - carWindow: the `CPWindow` supplied by `CPTemplateApplicationSceneDelegate.templateApplicationScene(_:didConnectInterfaceController:toWindow:)`. Must already be attached to its scene (has a non-default `screen`). - phoneScreen: the phone's own main screen. Defaults to `UIScreen.main`, the same value MapLibre Native reads internally, which is exactly what needs correcting for.
- `public func applyToContentScale(of view: UIView)`: Apply the correction to a view's `contentScaleFactor` directly. Suitable for plain `UIView`/`CALayer`-backed renderers; MapLibre's `MLNMapView` (or a MapLibre-Metal view) may need the factor applied through its own API instead if it manages scale itself, check your MapLibre version's CarPlay notes before relying on this shortcut.

#### `RoadRef`

*Struct* · `Sources/MapMapCarPlay/Models/RoadRefs.swift`

```swift
public struct RoadRef: Equatable, Hashable, Sendable
```

One road-number badge to draw (SPEC 4.5): the text on the shield, its tier, the country whose
signs it follows and the shield template. A mirror of the canonical `RoadRef` in
`web-sdk/src/road-signs.ts`, plus `source`, the raw ref the text came from.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `let text` | `String` |  | Text on the shield, the country's display prefix stripped where the sign does (`"I 95"` draws `95`; GB, IE, FR, DE and NL keep the full text). |
| `let tier` | `String` |  | Tier id (`gb-motorway`, `us-interstate`, ...), `"generic"` for a ref with no national tier, `"text"` for a ref longer than six characters, which is never a badge. |
| `let country` | `String` |  | ISO 3166-1 alpha-2 of the profile that resolved it, `""` when unknown. |
| `let a11yLabel` | `String` |  | Spoken label (`"Motorway M25"`, `"Interstate 95"`); English only for now, like the canonical table. |
| `let template` | `String?` |  | Shield template id; `nil` for a text-only ref. |
| `let source` | `String` |  | The raw ref as the engine reported it (`"I 95"`), the text a badge stands in for. |

**Initialisers and methods**

- `public init(text: String, tier: String, country: String, a11yLabel: String, template: String?, source: String)`

### Enums (MapMapCarPlay)

[`BadgeSurface`](#badgesurface), [`CarPlayActivity`](#carplayactivity), [`CarPlayAudioSession`](#carplayaudiosession), [`CarPlayDistanceUnits`](#carplaydistanceunits), [`CarPlayMapButtons`](#carplaymapbuttons), [`CPLaneGuidanceConverter`](#cplaneguidanceconverter), [`ManeuverGlyph`](#maneuverglyph), [`MapMapCarPlay`](#mapmapcarplay-1), [`RoadRefImage`](#roadrefimage), [`RoadRefResolver`](#roadrefresolver)

#### `BadgeSurface`

*Enum* · `Sources/MapMapCarPlay/Models/RoadRefBadgeLayout.swift`

```swift
public enum BadgeSurface: Sendable
```

The surface a badge sits on: on `dark` it gets the white halo of SPEC 3.6.

| Case | Description |
| --- | --- |
| `light` |  |
| `dark` |  |

#### `CarPlayActivity`

*Enum* · `Sources/MapMapCarPlay/CarPlayManagerDelegate.swift`

```swift
public enum CarPlayActivity: Equatable, Sendable
```

Where `CarPlayManager` currently is in the browse → preview →
navigate flow. Exposed so host apps can drive their own UI (e.g. a
phone-side "navigating on CarPlay" banner) off the same state machine.

| Case | Description |
| --- | --- |
| `browsing` | Connected, no destination chosen yet, the map template is showing free browse (pan/zoom) with the host's custom buttons, if any. |
| `previewingRoutes` | One or more `RouteResult`s are being previewed (`showRouteChoicesPreview`) before the driver commits to one. |
| `navigating(trip: CPTrip)` | A `CPNavigationSession` is active for the given trip. |

#### `CarPlayAudioSession`

*Enum* · `Sources/MapMapCarPlay/Audio/CarPlayAudioSession.swift`

```swift
public enum CarPlayAudioSession
```

`AVAudioSession` configuration for voice-prompt playback on CarPlay.

MapMapKit ships no TTS of its own, apps synthesise
`SpokenPrompt/text`/`SpokenPrompt/ssml` themselves (typically with
`AVSpeechSynthesizer`), same as on the phone. What CarPlay adds is a
specific audio-session recipe so prompts duck the car's current audio
source (music, phone calls) instead of fighting with it or going
silent: category `.playback`, mode `.voicePrompt`, options
`[.duckOthers, .interruptSpokenAudioAndMixWithOthers]`, the
combination settled on by mapbox-navigation-ios (#1864/#1933) after
exactly this category/mode/options set produced the fewest "no audio
on CarPlay" and "prompt cut off other audio entirely" reports.

**Initialisers and methods**

- `public static func configureForVoicePrompts() throws`: Configure the shared `AVAudioSession` for CarPlay voice prompts. Call once when navigation starts (or when your CarPlay scene connects, if you want to be ready before the first prompt) and again if you've deactivated the session in between (e.g. after a phone call interruption). - Throws: whatever `AVAudioSession.setCategory`/`setActive` throw (e.g. another app holding an incompatible session category).
- `public static func deactivate() throws`: Deactivate the session, e.g. when navigation ends. Notifies other apps so they can resume ducked audio.

#### `CarPlayDistanceUnits`

*Enum* · `Sources/MapMapCarPlay/Models/CarPlayDistanceUnits.swift`

```swift
public enum CarPlayDistanceUnits: Sendable
```

Which distance units CarPlay should display: short/long unit pairs
mirroring `MKDistanceFormatter.Units`, exposed independently so this
module has no requirement on `MKDistanceFormatter` itself.

| Case | Description |
| --- | --- |
| `localeDefault` | Follow the given locale's measurement system (the default: feet → miles in the US, yards → miles in the UK, metres → kilometres elsewhere). |
| `imperial` | Feet, then miles. |
| `imperialWithYards` | Yards, then miles (UK road-signage convention). |
| `metric` | Metres, then kilometres. |

#### `CarPlayMapButtons`

*Enum* · `Sources/MapMapCarPlay/CarPlayMapButtons.swift`

```swift
public enum CarPlayMapButtons
```

Ready-made `CPMapButton`s for the common navigation-app map controls.
Entirely optional, `CarPlayManagerDelegate` lets host apps supply
their own `mapButtons` array instead.

`CPMapButton` (like `CPMapTemplate`/`CPNavigationSession`) is
main-actor-isolated by the CarPlay framework itself
(`CARPLAY_TEMPLATE_UI_ACTOR` in `CPTemplate.h`), so this whole type is
`@MainActor`.

**Initialisers and methods**

- `public static func zoomIn(action: @escaping () -> Void) -> CPMapButton`
- `public static func zoomOut(action: @escaping () -> Void) -> CPMapButton`
- `public static func recenter(action: @escaping () -> Void) -> CPMapButton`
- `public static func toggleMute(isMuted: Bool, action: @escaping () -> Void) -> CPMapButton`
- `public static func pan(action: @escaping () -> Void) -> CPMapButton`

#### `CPLaneGuidanceConverter`

*Enum* · `Sources/MapMapCarPlay/Models/CPLaneGuidance+MapMap.swift`

```swift
public enum CPLaneGuidanceConverter
```

Converts MapMap's OSRM-style `LaneGuidance` (direction strings, e.g.
`"left"`, `"straight"`, `"slight right"`) into CarPlay's angle-based
`CPLane`/`CPLaneGuidance` (iOS 17.4+, and the non-deprecated `CPLane`
initializer only from iOS 18.0).

This is a best-effort approximation, not a port: OSRM lane directions
are discrete buckets, not measured angles, so each bucket is mapped to
a representative angle (straight ahead = 0°, clockwise positive,
mirroring `CPTrafficSide.right`'s convention in `CPManeuver.h`). Host
apps with more precise lane geometry (e.g. from their own map data) can
bypass this and build `CPLaneGuidance` directly.

**Initialisers and methods**

- `public static func laneGuidance(for bannerLine: BannerLine) -> CPLaneGuidance?`: Build a `CPLaneGuidance` from a sub `BannerLine`'s lane diagram. Returns `nil` below iOS 18 (no non-deprecated `CPLane` initializer) or when there are no lanes to show.

#### `ManeuverGlyph`

*Enum* · `Sources/MapMapCarPlay/Models/ManeuverGlyph.swift`

```swift
public enum ManeuverGlyph
```

Maps the OSRM-style `maneuverType`/`maneuverModifier` strings carried on
`BannerLine` (see `crates/sn-valhalla/src/translate.rs`,
`map_maneuver_type`: types `depart`, `arrive`, `turn`, `new name`,
`continue`, `on ramp`, `off ramp`, `fork`, `merge`, `roundabout`,
`rotary`, `exit roundabout`, `exit rotary`, `notification`; modifiers
`straight`, `slight left`/`left`/`sharp left`, `slight right`/`right`/
`sharp right`, `uturn`), plus the manoeuvre's driving side
(`VisualBanner/drivingSide`), onto MapMap's own direction icons for
`CPManeuver.symbolImage` and onto CarPlay's `CPManeuverType`.

The icons are the approved MapMap set (`map-assets/icons/directions/`),
shipped in this module's `DirectionIcons.xcassets` as vector PDF template
images (generated by `map-assets/icons/directions/generate-native.mjs`).
Every transform, including the left-hand-traffic mirror, is baked into
the geometry, so CarPlay's image snapshot cannot lose a mirroring flag.
Secondary road furniture carries 0.35 alpha, which a template image
keeps: CarPlay tints the glyph for its light or dark card and the
untravelled part of a roundabout stays the same colour, fainter.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let symbolPointSize` | `CGFloat` | `50` | Point size the maneuver symbol is rendered at: the CarPlay Developer Guide's maximum for a first maneuver symbol (50 x 50 pt). |

**Initialisers and methods**

- `public static func drivingSide(_ value: String?) -> String?`: Normalises an engine-supplied driving side: `"left"` (UK-style left-hand traffic, roundabouts clockwise) or `"right"`. Anything else, including `nil`, is unknown and returns `nil`: never a guess.
- `public static func iconName(maneuverType: String?, maneuverModifier: String?, drivingSide: String?) -> String`: The MapMap direction icon name (`turn_left`, `rotary_lht_slight_left`, ...) for a manoeuvre. A mirror of the canonical `iconNameForManeuver(type, modifier, drivingSide)` in `map-assets/icons/directions/generate.mjs`; `ManeuverGlyphTests` checks it against a golden table generated from the canonical function, so the two cannot drift silently. There is no default driving side. A roundabout or rotary with a known side draws its ring in that circulation; with the side unknown it draws the plain turn glyph for its exit direction, and a u-turn the plain `continue_uturn`, never a guessed ring. Always returns a name with a bundled image.
- `public static func vectorImage(named name: String) -> UIImage?`: The bundled vector icon `name`, unsized, as a template image.
- `public static func image(named name: String, pointSize: CGFloat = symbolPointSize) -> UIImage?`: The icon `name` rendered from its vector data at `pointSize` (3x, so it is sharp on every CarPlay screen), as a template image: CarPlay supplies the colour, the alpha (including the 0.35 secondary) is the icon's own.
- `public static func symbolImage(maneuverType: String?, maneuverModifier: String?, drivingSide: String?) -> UIImage?`: The symbol image for a manoeuvre (see `iconName(maneuverType:maneuverModifier:drivingSide:)`).
- `public static func cpManeuverType(maneuverType: String?, maneuverModifier: String?) -> CPManeuverType`: The nearest `CPManeuverType`, for apps that also want the system's own presentation styling (`CPManeuver.maneuverType`, iOS 17.4+). Several MapMap/OSRM types have no exact CarPlay counterpart (`merge`, `notification`) and fall back to `.followRoad`.

#### `MapMapCarPlay`

*Enum* · `Sources/MapMapCarPlay/MapMapCarPlay.swift`

```swift
public enum MapMapCarPlay
```

CarPlay turn-by-turn support for the MapMap iOS SDK.

`MapMapCarPlay` is a thin adapter layer: it converts `NavigationEngine`
output (`NavigationState`/`GuidanceUpdate`, `VisualBanner`,
`SpokenPrompt`, `SpeedLimit`) into CarPlay's own vocabulary
(`CPTrip`, `CPManeuver`, `CPTravelEstimates`, `CPNavigationSession`), and
provides `CarPlayManager`, an orchestrator that owns the `CPMapTemplate`
root and the scene lifecycle so host apps don't have to reimplement it.

The SDK stays headless here too: MapMapCarPlay never creates a map
renderer. The host app supplies its own MapLibre view (or any other
`UIViewController`) through `CarPlayManagerDelegate/carPlayManager(_:mapViewControllerFor:)`,
and `CarPlayManager` only handles the CarPlay-specific scaffolding
around it (window attachment timing, screen-scale correction, template
stack, navigation-session lifecycle).

## Layout

- `CarPlayManager`, the `CPTemplateApplicationSceneDelegate`-facing
  orchestrator: browse → preview → navigate → stop activity states, the
  `CPMapTemplate` + its delegate, and the active `CPNavigationSession`.
- `CarPlayManagerDelegate`, host hooks: supply the map view
  controller, customise map buttons, observe camera gestures.
- `CPTrip`, `CPManeuver`, `CPTravelEstimates` converters (this module's
  `Models/` directory), pure value conversions from MapMap's FFI types.
- `CarPlayWindowScaleAdjuster`, mitigation for the CarPlay-screen
  scale bug class (MapLibre Native computes scale from
  `UIScreen.main`, not the CarPlay screen, maplibre-native#3214).
- `CarPlayAudioSession`, the exact `AVAudioSession` configuration
  voice prompts need on CarPlay.
- `ExampleCarPlaySceneDelegate.swift`, a compiled, heavily-commented
  integration example: Info.plist scene manifest keys, the
  `com.apple.developer.carplay-maps` entitlement, and the connect flow.

## Attribution

Several converters in this module are ported from Ferrostar's
`FerrostarCarPlayUI` (https://github.com/stadiamaps/ferrostar/tree/main/apple/Sources/FerrostarCarPlayUI,
© Stadia Maps, Inc., BSD-3-Clause, the same licence already recorded
for the pinned Ferrostar Rust crate in `THIRD-PARTY-NOTICES.md`), individual files carry a header noting what was ported. **No Ferrostar
Swift package is linked**: everything here binds against MapMapKit's
own generated types, per the "Ferrostar boundary" in ios/README.md, linking `FerrostarCarPlayUI` directly would pull in Ferrostar's own
copy of the Rust core, duplicating `sn_nav_core`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let requiredEntitlement` |  |  | The CarPlay entitlement identifier apps must request from Apple (see "Requesting CarPlay Entitlements", https://developer.apple.com/documentation/carplay/requesting-carplay-entitlements) before this module's scenes can connect on a real head unit or in the CarPlay Simulator. Exposed as a constant so host apps can reference it (e.g. in onboarding copy) without retyping it. |

#### `RoadRefImage`

*Enum* · `Sources/MapMapCarPlay/Models/RoadRefImage.swift`

```swift
public enum RoadRefImage
```

MapMap road-number badges (SPEC 4.5 and 5.5) as `UIImage`s, composed from the sign vectors
in this module's `Signs.xcassets` (lane 3's native assets, copied by
`map-assets/signs/generate-car.mjs`): the shield blank drawn in horizontal slices (its
stretch regions widen to fit the text, everything else keeps its shape), the halo on dark
surfaces, and the text as glyph vectors placed on the cap grid. No font is involved
anywhere, and the images keep their true colours (never template rendering).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `static let carPlayMaxSize` |  |  | CarPlay's limit for a text attachment in a manoeuvre instruction (`CPManeuver.attributedInstructionVariants`: "The maximum text attachment image size is 64x25 points"). |
| `static let carPlayHeight` | `CGFloat` | `23` | The badge height on CarPlay: 23 pt plus its 1 pt halo on each side fills the 25 pt attachment limit. SPEC 5.5 asks for 32; CarPlay cannot show more than 25. |

**Initialisers and methods**

- `public static func image(for ref: RoadRef, height: CGFloat = 32, surface: BadgeSurface = .dark, scale: CGFloat = 3) -> UIImage?`: The badge for `ref` at `height` points on `surface`, rendered at `scale` pixels per point; `nil` when nothing draws (a text-only ref, an unknown template or a character with no glyph).
- `public static func carPlayImage(for ref: RoadRef, scale: CGFloat = 3) -> UIImage?`: The badge for a CarPlay manoeuvre instruction: within `carPlayMaxSize` (a long ref is drawn smaller rather than cropped), with a light variant (no halo) and a dark variant (white halo) registered for `UIUserInterfaceStyle`, both on the same canvas so the instruction does not shift when the panel changes appearance.
- `public static func attributedInstruction(_ text: String, refs: [RoadRef], badge: (RoadRef) -> UIImage? = { carPlayImage(for: $0) }) -> NSAttributedString?`: `text` with a road-number badge for each of `refs` that has one, as CarPlay text attachments; `nil` when none of them draws. A badge replaces the ref where the text already names it as a whole word (the banner's primary text is often the ref itself, e.g. `"A27"`), so the instruction does not say it twice; otherwise the badge goes in front of the text. Refs with no badge (text-only, longer than six characters) are left as they are.

#### `RoadRefResolver`

*Enum* · `Sources/MapMapCarPlay/Models/RoadRefs.swift`

```swift
public enum RoadRefResolver
```

Picks the road-number badges for a step's road (SPEC 4.5) from what the car SDK knows about
it: the raw `ref`, its country, subdivision and road class.

A mirror of the canonical `resolveRoadRef` for those inputs. Natively there is no route
relation `network` and no tile network, so a tier is chosen by its ref-prefix pattern only
(the spec's third rule); `RoadRefResolverTests` checks this against a golden table computed
by the canonical resolver itself, so the two cannot drift silently.

Never invents a ref: no ref, no badge. Refs over six characters come back as tier `"text"`
with no template (shown as text, never a badge). At most three concurrent routes.

**Initialisers and methods**

- `public static func resolve(ref: String?, country: String?, subdivision: String?, roadClass: String?) -> [RoadRef]`
- `public static func label(text: String, tier: String) -> String`: `"Motorway M25"`, `"A road A259"`, `"Interstate 95"`: the canonical `roadRefLabel`.

### Protocols (MapMapCarPlay)

[`CarPlayManagerDelegate`](#carplaymanagerdelegate)

#### `CarPlayManagerDelegate`

*Protocol* · `Sources/MapMapCarPlay/CarPlayManagerDelegate.swift`

```swift
public protocol CarPlayManagerDelegate: AnyObject
```

Host app hooks for `CarPlayManager`.

MapMapKit is headless everywhere, including here: `CarPlayManager`
owns the CarPlay *chrome* (template stack, navigation session
lifecycle, screen-scale correction) but never renders a map itself.
The one piece of UI it cannot supply on the host's behalf is the map
view, that comes from `carPlayManager(_:mapViewControllerFor:)`.

**Initialisers and methods**

- `func carPlayManager(_ manager: CarPlayManager, mapViewControllerFor window: CPWindow, scaleAdjuster: CarPlayWindowScaleAdjuster) -> UIViewController`: Supply the view controller to place behind CarPlay's map template chrome, typically a thin wrapper around your own `MLNMapView` (MapLibre) instance, styled with `styleForNavigation(styleJson:)`. Called exactly once per connection, **after** `window` has already been attached to its `CPTemplateApplicationScene` (`window.screen` and `window.mapButtonSafeAreaLayoutGuide` are valid), this is the point at which it is safe to create a MapLibre view. Creating one any earlier is a known crash/blank-map bug class in MapLibre Native iOS tied to the window not yet reporting a real drawable size. Use `scaleAdjuster` to correct for CarPlay's screen-scale mismatch (maplibre-native#3214) before or as you configure your map view's layer.
- `func carPlayManager(_ manager: CarPlayManager, mapButtonsFor activity: CarPlayActivity) -> [CPMapButton]?`: Customise the map template's `mapButtons` for the current `CarPlayActivity`. Return `nil` to keep whatever `CarPlayManager` last set (its default is empty, most apps will want to provide at least a mute toggle via `CarPlayMapButtons`).
- `func carPlayManager(_ manager: CarPlayManager, leadingNavigationBarButtonsFor activity: CarPlayActivity) -> [CPBarButton]?`: The trailing/leading navigation-bar buttons (e.g. "Cancel", "Go") for the current activity. Return `nil` to keep CarPlay's defaults (none).
- `func carPlayManager(_ manager: CarPlayManager, trailingNavigationBarButtonsFor activity: CarPlayActivity) -> [CPBarButton]?`
- `func carPlayManager(_ manager: CarPlayManager, didSelectRouteChoice routeChoice: CPRouteChoice, for trip: CPTrip)`: The driver picked a route to preview/select. Most apps will store `routeChoice.mapMapRouteResult` and, once the driver taps "Go" (`carPlayManager(_:didStartTrip:using:)`), build a `NavigationEngine` from it.
- `func carPlayManager(_ manager: CarPlayManager, didStartTrip trip: CPTrip, using routeChoice: CPRouteChoice)`: The driver committed to navigating `routeChoice`. Start your `NavigationEngine` here and feed its states back through `CarPlayManager/updateNavigationState(_:)` on every tick.
- `func carPlayManager(_ manager: CarPlayManager, didEndTrip trip: CPTrip, cancelled: Bool)`: The trip ended, either the driver/system cancelled it (`cancelled == true`, e.g. `mapTemplateDidCancelNavigation`) or your own code called `CarPlayManager/stopTrip(cancelled:)` (e.g. on arrival). Stop your `NavigationEngine` here.
- `func carPlayManager(_ manager: CarPlayManager, didUpdateZoomWithScale scale: CGFloat, velocity: CGFloat)`
- `func carPlayManager(_ manager: CarPlayManager, didUpdateRotationWithRadians radians: CGFloat, velocity: CGFloat)`
- `func carPlayManager(_ manager: CarPlayManager, didUpdatePitchAtCenter center: CGPoint)`
