Skip to content

v0.5.0

Choose a tag to compare

@sheehanmunim sheehanmunim released this 07 Oct 22:06
· 21 commits to main since this release

munim-maps is one API over five map engines (MapKit, Google Maps, Mapbox, MapLibre and Cesium) on iOS and Android, and ships no 3D models any more: the vehicle catalogue is the separate munim-maps-vehicles package. See docs/providers.md.

Breaking

  • munim-maps/vehicles and munim-maps/vehicles-glb throw an error that points to munim-maps-vehicles. The npm package no longer carries the 34 MB of models, and apps no longer bundle all 57: install munim-maps-vehicles and import { VEHICLES } from 'munim-maps-vehicles' (remote, cached on the device) or import car from 'munim-maps-vehicles/bundled/car-ev' (one model in the app). The Swift Package's MunimMapsVehicles has the names and remote URLs (MunimVehicles.url(_:), glbURL(_:), baseURL) and no resources.
  • The Cesium engine loads CesiumJS from jsDelivr by default, and the npm package no longer carries CesiumJS (13 MB). To bundle it, add cesium to the app (npm install cesium@1.146.0, the version the engine is written for; other versions build with a warning) and turn on cesium: { bundled: true } (Expo), munimMaps.cesiumBundled=true (Gradle) or MUNIM_MAPS_CESIUM_BUNDLED=1 (CocoaPods): the minified build, its LICENSE.md and ThirdParty.json are copied from the app's cesium package at pod install (into the MunimMapsCesium resource bundle) and by a Gradle task (munimMapsCopyCesium, generated assets). MUNIM_MAPS_CESIUM_DIR / munimMaps.cesiumDir point at another cesium folder.
  • Android with Mapbox: Mapbox's Maven repository comes from the Expo config plugin (or your android/build.gradle), no longer from a rootProject.allprojects hook in munim-maps' build file.

Added

Engines and setup

  • provider on MunimMapView: 'mapkit' (default on iOS), 'google', 'mapbox', 'maplibre' (default on Android without Google) or 'cesium', with styleUrl and per-engine options in google, mapbox, maplibre, cesium and mapkit props. Engines that are not built in show a placeholder and report onError.
  • configureMunimMaps({ googleMapsApiKey, mapboxAccessToken, cesiumIonToken, maplibreStyleUrl, mapboxStyleUrl, defaultProvider }), availableProviders(), installedProviders(), isProviderAvailable() and MAP_PROVIDERS.
  • Expo config plugin ("plugins": [["munim-maps", { "providers": [...], ...keys }]]): turns engines on (CocoaPods subspecs through Podfile.properties.json, Gradle properties) and writes their keys into Info.plist, the manifest and strings.xml. It also adds Mapbox's Maven repository on Android, googleMaps3d and cesium: { bundled }.
  • Opt-in engines: NitroMunimMaps/Google, /Mapbox, /MapLibre and /Cesium subspecs on iOS (the default install is still MapKit only), munimMaps.google, mapbox, maplibre and cesium Gradle properties on Android.
  • Swift: the MunimMapEngine protocol, MunimMapContainerView, MunimMapEngines, MunimMapsConfiguration, and MapCameraSource / MapCameraState, so MunimModelLayer.attach(to:) can draw over any engine's map.

Shared API on every engine

  • modelRendering: 'auto' | 'native' | 'overlay' on MunimMapView: who draws models. auto lets Mapbox (its model layer), Cesium (entities) and Google's photorealistic 3D map (Android) draw them, and uses munim-maps' 3D layer on MapKit, MapLibre and the Google 2D map; whatever an engine cannot draw stays on the 3D layer, except on Cesium. google.modelRendering, mapbox.modelRendering and cesium.modelRendering are aliases.
  • Engine-only methods and events through one channel: ref.current.providerCommand(command, argsJson) and onProviderEvent({ provider, name, data }), with providerCommand(ref, name, args), parseProviderEvent, callProvider / addProviderEventListener for engine-level commands that need no map, and typed wrappers per engine (googleMap, googleEvent, mapboxMap, maplibreCommands, cesiumCommands, parseCesiumEvent).
  • onMarkerDrag: a marker's position while it is dragged, between onMarkerDragStart and onMarkerDragEnd, on every engine (MapKit reads the dragged view on a display link).
  • react-native-maps' region API: initialRegion, a controlled region, onRegionChangeStart, onRegionChangeComplete(region) and ref.animateToRegion(region, ms); initialCamera is optional when a region is given.
  • MarkerView on Android, on every engine: the views are drawn into a bitmap (again on change with tracksViewChanges); Mapbox shows them as view annotations, Google, MapLibre and Cesium as image markers.
  • selectableMapFeatures reaches every Android engine (Mapbox: Standard's POI, landmark and place-label featuresets).
  • Remote models (http(s)://, such as munim-maps-vehicles') are cached on disk on Android too, and Mapbox's model layer and Cesium read the cached files, so maps work offline after the first load. Two-format sources ({ uri, usdz, glb }) let munim-maps pick USDZ or GLB per engine and platform.

Vehicle catalogue package

  • munim-maps-vehicles: the 57 models as USDZ and GLB; VEHICLES[name] (jsDelivr URLs at the package's version), VEHICLE_NAMES, VehicleName, vehicleSource(name), configureMunimMapsVehicles({ baseUrl }) for self-hosting, and munim-maps-vehicles/bundled/<name> / bundled/glb/<name> (one file per model). scripts/vehicles/write-catalogue.mjs generates its files from the models make-vehicles.swift writes.

Android

  • Android: MunimMapView and MapModelLayer are Kotlin Nitro views. MunimMapView draws MapLibre Native with OpenFreeMap (no key) and GLB / glTF models through a Filament 3D layer driven by the same camera model as iOS (position, altitude, heading, scale, screenSize, tint, spin, motion, onModelPress, measureAlignment), with the camera API, regions, conversions, gestures and map events.
  • Android 3D layer at parity with iOS, on every engine (it reads only MapCameraState): built-in shapes, pictures (avatars with rings and badges), labels, stems, lift, ground shadows, embedded glTF animations, effects (exhaust, smoke, contrail), occluder models, zones, 3D paths, occlusion="buildings" (vector tiles), the Earth hiding the globe's far side, altitudeReference: 'sea' and followTerrain (Terrarium terrain tiles), and flyCamera / stopFlight stepped on the layer's frame clock for any engine that can set its camera.
  • groundElevation() on Android.
  • Android: hasLookAround / openLookAround go to the engine (Google's Street View); MapModelLayer can draw over react-native-maps' Google MapView when the Google engine is built in.

Google Maps

  • Google Maps engine (provider="google") on iOS (Maps SDK for iOS 10 + Google Maps Utils 7, NitroMunimMaps/Google) and Android (Maps SDK 20 + android-maps-utils 3.20, munimMaps.google=true): map types, cloud styling (google.mapId) and JSON styles, dark mode, buildings, traffic, transit, indoor maps with level events and setIndoorLevel, lite mode (Android), every gesture and UI control (compass, my-location button, zoom controls and map toolbar on Android), padding, zoom limits and camera target bounds; markers of every style with info windows, dragging, flat / rotated markers, advanced markers with pins and collision behaviour, MarkerView, clustering with clusterStyles; polylines with gradients, spans, patterns, caps, joints and texture / sprite stamps, polygons with holes, circles, overlay taps, tile overlays ({x}{y}{z}, {-y}, {quadkey}, file://), ground overlays, heatmaps, KML and GeoJSON layers, data-driven styling of boundaries and datasets; Street View (openLookAround, hasLookAround, googleMap(ref).streetView); snapshots, reverse geocoding, POI taps, camera-move reasons; and the 3D layer on Google's camera, with Google's field of view measured from its projection.
  • Google photorealistic 3D mode on Android (google={{ mode: '3d' }}, Maps 3D SDK play-services-maps3d 0.2.0 behind munimMaps.googleMaps3d=true / the Expo plugin's googleMaps3d): models as native Google glTF models (modelRendering: 'auto' | 'native' | 'overlay'), native polylines, polygons and markers, flyTo / flyAround. Needs the Map Tiles API and the Maps 3D SDK for Android on the key.
  • googleMapsServices({ apiKey }): Places API (New) autocomplete, details, text and nearby search and photos, the Geocoding API and the Routes API (routes with decoded lines, route matrices), for keys with those APIs on; googleGeometry and encodePolyline / decodePolyline in JavaScript.

Mapbox

  • Mapbox engine (provider="mapbox", Mapbox Maps SDK 11.32 on iOS and Android): Mapbox Standard and Standard Satellite with light presets, themes and the rest of their config, globe, terrain, atmosphere, lights, snow and rain, style imports and colour themes; every style-spec source and layer type, images and glTF models from the declarative mapbox={{ … }} options (MapboxMapOptions); markers as point annotations with clustering and dragging, shapes and tile overlays as layers in Standard's slots, MarkerViews as view annotations; the location puck (2D / 3D, heading, pulsing), follow and follow-with-heading through Mapbox's viewport; featureset taps; Mapbox map events; munim-maps' 3D layer aligned to Mapbox's camera. mapboxMap(ref) methods (queries, feature state, clusters, runtime styling, Mapbox's camera, free camera, viewport, Snapshotter, elevation, location override, tile cover, statistics), MapboxOffline (style packs, tile regions) and MapboxServices (Geocoding, Search Box, Directions, Matrix, Isochrone). See the checklist in docs/providers.md.
  • Mapbox draws glTF / GLB models natively in its model layer (mapbox={{ modelRendering: 'auto' | 'native' | 'overlay' }}, auto by default): lit, shadowed and hidden by Mapbox's buildings and terrain, with motion, spin, screenSize and tint; avatars, labels, stems, effects, zones and paths stay on munim-maps' 3D layer.

MapLibre (open maps)

  • MapLibre engine (provider="maplibre", open maps: OpenStreetMap data from OpenFreeMap, no key) on iOS (NitroMunimMaps/MapLibre, MapLibre Native 6.30+) and Android (built in, MapLibre Native 13.6): the shared API (camera, flights, regions, conversions, gestures, limits, padding, markers of every style with callouts, dragging and clustering, polylines with gradients and dashes, polygons, circles, tile overlays, overlay taps, tappable base-map places, user location and tracking, compass, scale bar, tracking button, snapshots, addressForCoordinate) and the munim 3D layer on MapLibre's camera.
  • maplibre={{ … }} (MapLibreMapOptions): style presets (OpenFreeMap Liberty, Bright, Positron, Dark, Fiord; MapLibre demo tiles; MapTiler and Stadia with apiKey), styleJson, dark style, satellite tiles for mapStyle imagery/hybrid, style-spec sources (vector, raster, raster-dem, GeoJSON with clustering, image, PMTiles, MLT) and layers (all ten layer types, expressions, filters), images, light, transition, hillshade and colorRelief from keyless AWS Terrain Tiles, labelLanguage, ornaments, gesture tuning, zoom/pitch limits and roll, rendering options (fps, prefetch, tile cache and LOD, frustum offset, debug overlays), location puck options, HTTP headers, log level.
  • maplibreCommands(ref): typed MapLibre commands: rendered and source feature queries, cluster leaves / children / expansion zoom, runtime styling (add / remove / move sources and layers, paint and layout properties, filters, zoom ranges, images, light), feature state, flyTo, resetNorth, an offscreen snapshotter, offline packs (create, list, resume, suspend, delete, invalidate, progress events, ambient cache, database merge) and connectivity. Events: styleLoaded, mapLoadFailed, cameraMoveStarted, idle, renderedMap, sourceChanged, styleImageMissing, renderError, offlineProgress, offlineError.
  • openMapsServices and configureOpenMapsServices: Nominatim geocoding, Photon search and OSRM / Valhalla routing with configurable endpoints (the public servers are for light use only), and decodePolyline.

Cesium

  • Cesium engine (provider="cesium", iOS and Android): CesiumJS 1.146.0 (Apache-2.0), loaded from jsDelivr at that version and kept on the device (or bundled in the app from its own cesium package with cesium: { bundled: true }; 13 MB, minified build only) and run in a WebView the engine owns (WKWebView with a munim-cesium:// scheme handler, android.webkit.WebView serving https://appassets.androidplatform.net/), no react-native-webview. OpenStreetMap imagery and an ellipsoid without a key; cesiumIonToken adds Cesium World Terrain, Bing imagery, OSM Buildings and ion assets. The shared API works on it: camera (set, animate, fly through keyframes, regions, fit, conversions, limits, boundary, padding, gestures), events, markers (pin, balloon, image, avatar, label, dot, badges, callouts with accessories, dragging, clustering, MarkerView on iOS), polylines (dashes, gradients, trimming), polygons with holes, circles, tile overlays, user location and tracking, compass / scale / tracking / 2D-3D controls, snapshots, reverse geocoding, and the 3D layer: GLB / glTF models (the vehicle catalogue) drawn by Cesium with heading, altitude, screenSize, tint, spin, animations, motion, labels, stems, lift, ground shadows, effects (exhaust, smoke, contrail), zones and paths, hidden by terrain and 3D Tiles; other files (USDZ on iOS) on the native layer over Cesium's camera (measureAlignment); cesium.modelRendering: auto / native (Cesium entities and primitives) or overlay (munim-maps' native 3D layer over the WebView). cesium={{ … }} (CesiumMapOptions) reaches the rest of CesiumJS: scene modes, every imagery and terrain provider, 3D Tiles (OSM Buildings, Google Photorealistic, I3S, voxels, vector tiles, iTwin), CZML entities, CZML / GeoJSON / KML / GPX data sources, the clock, lighting, atmosphere, shadows, fog, clouds, post-processing and Viewer widgets.
  • cesiumCommands(ref): Cesium's own methods (flights, lookAt, orbit, picking, pickPosition, measuring, terrain heights, screenshots, entities, data sources, tilesets and styles, imagery layers, terrain, clock, particle systems, panoramas, model animations and styles, evaluate), and onProviderEvent with parseCesiumEvent (pick, tilesetLoaded, dataSourceLoaded, morphComplete…).

Example

  • One engine picker (munimmapsexample://providers[/<provider>], the start screen on Android) shows the same models on each engine with the 3D layer's measured alignment and opens every other screen: each engine's own screen, the 3D layer on the picked engine, and on iOS the MapKit, react-native-maps and expo-maps examples. Development keys are read at build time from example/.env.local or ~/.config/munim-maps/keys.env.
  • Each engine's screen with every feature group and on-device checks behind a button or a deep link: munimmapsexample://google[/checks], mapbox[/checks], maplibre[/check], cesium[/checks], layer3d[/<provider>][/check]. Checks never run on their own, except the MapKit self-test on a plain iOS launch.
  • Models come from munim-maps-vehicles (EXPO_PUBLIC_MUNIM_MAPS_VEHICLES_BASE_URL points it at a local server before publishing); the engine picker's balloon is a bundled model.

Changed

  • The MapKit map and its features moved to ios/Engines/MapKit/ behind MunimMapEngine; the 3D renderer now reads only the camera state, not MKMapView. Behaviour is unchanged (the on-device self-test still passes 47 of 47).
  • decodePolyline is one implementation (Google's services and the OpenStreetMap services share it).
  • Cesium draws at most 2 device pixels per CSS pixel (it drew 2 x the device resolution: 5.25 pixels per CSS pixel on a 420 dpi phone, 4 x the pixels on an iPad).

Fixed

Found running every engine's checks on Android:

  • Google: apps no longer stop when a Google map is created on devices with older Google Play services (org.apache.http.legacy, not required, in the library manifest); hasLookAround / streetView.hasCoverage work (the probe panorama was never started).
  • Mapbox: clustered markers, MarkerView (added twice before its first layout; plain layout params in Mapbox's FrameLayout) and callouts no longer crash; setViewport({ state: 'overview' }) no longer throws IllegalAccessError; getNativeModels exists on Android; overlay taps ignore off-screen projections; a location override set before following starts moves the follow camera.
  • MapLibre: getFeatureState, setFeatureState, removeFeatureState and the offscreen snapshot command answer JSON.

Found running Google's photorealistic 3D map on a phone (Maps 3D SDK 0.2.0):

  • Google 3D (Android): the initial camera, models, markers, polylines and polygons were given to the SDK before its map was ready and were lost (the map stayed on the whole Earth, with no models); they are applied once the map is ready, including an initialCamera that arrives after google.mode: '3d'. getCamera, getCamera3d and relative flyTo / setCamera3d read the camera set in code (the SDK reports only gestures), camera changes made in code reach onCameraChange, and map3dReady fires once instead of on every loading-progress update.
  • Cesium: without terrain, the camera centre and distance (getCamera, getVisibleRegion, pointForCoordinate, cameraChange) are measured on the ellipsoid, not on the globe's loading tiles, which sag up to tens of kilometres below it until finer tiles arrive (on a slow phone, getCamera read 3 to 30 km for a 1.1 km camera for several seconds after the map was ready).