# Maps & routing Fully open source, no API key, no account, no card. | Concern | Technology | Cost | | --- | --- | --- | | Rendering | [MapLibre GL](https://maplibre.org) (BSD) | free | | Tiles | [OpenFreeMap](https://openfreemap.org) — OSM data, MIT | free, no key, no limits | | Routing | [OSRM](https://project-osrm.org) (BSD) | free, self-hostable | There is no dependency on Google Maps: no Maps SDK, no Directions API, no `com.google.android.geo.API_KEY` in the manifest, and no credential of any kind shipped in the app. ## Why Google's mobile Maps *SDK* is free and unlimited — the bill came from the **Directions API** at $5 per 1,000 requests above a 10,000/month free tier. Two things made it worse: the multi-stop screen issued one Directions call *per route segment*, and the API key was hardcoded in five places (including `AndroidManifest.xml`), unrestricted because the routing calls went out from the device as web-service requests. Commercial alternatives were considered and rejected because they all want an account and a payment method. The open-source stack has neither. ## Configuration Nothing is required to run the app — the defaults work out of the box: ```sh flutter run ``` Both endpoints are overridable at build time, so production can point at self-hosted infrastructure without a code change: ```sh flutter build apk \ --dart-define=OSRM_BASE_URL=https://osrm.your-domain.in \ --dart-define=MAP_STYLE_URL=https://tiles.your-domain.in/styles/liberty ``` > **Before production**, stand up your own OSRM. The default > (`routing.openstreetmap.de`) is the FOSSGIS community server — free and > keyless, but donation-funded with a fair-use policy that a delivery fleet > would abuse. See [`deploy/osrm/README.md`](../deploy/osrm/README.md); the > Coimbatore region needs only a 0.52 GB extract. > > Tiles do **not** need self-hosting — OpenFreeMap's public instance has no key > and no request limits, and permits commercial use under MIT with attribution > (which MapLibre renders automatically). ## Layout | File | Role | | --- | --- | | `lib/services/maps/map_config.dart` | Tile style + routing endpoints | | `lib/services/maps/routing_service.dart` | OSRM client, polyline decoding, response cache | | `lib/services/maps/map_marker_icons.dart` | Canvas-drawn marker bitmaps (MapLibre has no built-in pins) | | `lib/services/maps/nearle_map.dart` | The `NearleMap` widget — declarative markers/polylines over MapLibre | Provider choice is confined to the first two files. The four map screens talk only to `NearleMap` and `RoutingService`, so swapping backends again would not touch UI code. ### OSRM notes - Coordinates go in the URL path as **`lon,lat`** — the reverse of most APIs. - `overview=full&geometries=polyline` returns an encoded polyline at **precision 5**. OSRM's `polyline6` option is precision 6; pass `precision: 6` to `decodePolyline` if you ever switch. - `_parse()` is deliberately tolerant — plain-number *or* `{"value": n}` distances, several geometry key names, GeoJSON `LineString` — so a proxy or an OSRM-compatible alternative (Valhalla, GraphHopper) drops in without changes. ## Cost controls These mattered when routing was billed and still cut latency and load: - **One call per route, not per leg.** The multi-stop screen passes intermediate stops as additional coordinates in a single OSRM request. It previously issued one request per consecutive pair, so an N-stop route cost N calls every time the screen opened. - **Response cache.** `RoutingService` caches by origin/destination rounded to ~11 m for 30 minutes, so repeated quotes for the same delivery reuse one call. - **Rendering makes no routing calls at all.** ## Known gaps vs. the old Google implementation - **Info windows.** MapLibre has no info-window concept. Marker taps still fire (`NearleMapMarker.onTap` drives the customer sheet on the multi-stop screen), but the old tap-to-show-title bubbles are gone. - **Traffic and 3D buildings.** `trafficEnabled` / `buildingsEnabled` on the navigation screen had no MapLibre equivalent and were dropped. OSRM has no live-traffic model either — ETAs are free-flow estimates. - **Turn-by-turn navigation is unchanged.** It still hands off to whatever navigation app is installed via a `google.navigation:` / maps URL intent. That is an OS intent, not an API — no key, no SDK, no billing.