Repository navigation
v0.5.0
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/vehiclesandmunim-maps/vehicles-glbthrow an error that points tomunim-maps-vehicles. The npm package no longer carries the 34 MB of models, and apps no longer bundle all 57: installmunim-maps-vehiclesandimport { VEHICLES } from 'munim-maps-vehicles'(remote, cached on the device) orimport car from 'munim-maps-vehicles/bundled/car-ev'(one model in the app). The Swift Package'sMunimMapsVehicleshas 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
cesiumto the app (npm install cesium@1.146.0, the version the engine is written for; other versions build with a warning) and turn oncesium: { bundled: true }(Expo),munimMaps.cesiumBundled=true(Gradle) orMUNIM_MAPS_CESIUM_BUNDLED=1(CocoaPods): the minified build, itsLICENSE.mdandThirdParty.jsonare copied from the app'scesiumpackage atpod install(into theMunimMapsCesiumresource bundle) and by a Gradle task (munimMapsCopyCesium, generated assets).MUNIM_MAPS_CESIUM_DIR/munimMaps.cesiumDirpoint at anothercesiumfolder. - Android with Mapbox: Mapbox's Maven repository comes from the Expo config plugin (or your
android/build.gradle), no longer from arootProject.allprojectshook in munim-maps' build file.
Added
Engines and setup
provideronMunimMapView:'mapkit'(default on iOS),'google','mapbox','maplibre'(default on Android without Google) or'cesium', withstyleUrland per-engine options ingoogle,mapbox,maplibre,cesiumandmapkitprops. Engines that are not built in show a placeholder and reportonError.configureMunimMaps({ googleMapsApiKey, mapboxAccessToken, cesiumIonToken, maplibreStyleUrl, mapboxStyleUrl, defaultProvider }),availableProviders(),installedProviders(),isProviderAvailable()andMAP_PROVIDERS.- Expo config plugin (
"plugins": [["munim-maps", { "providers": [...], ...keys }]]): turns engines on (CocoaPods subspecs throughPodfile.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,googleMaps3dandcesium: { bundled }. - Opt-in engines:
NitroMunimMaps/Google,/Mapbox,/MapLibreand/Cesiumsubspecs on iOS (the default install is still MapKit only),munimMaps.google,mapbox,maplibreandcesiumGradle properties on Android. - Swift: the
MunimMapEngineprotocol,MunimMapContainerView,MunimMapEngines,MunimMapsConfiguration, andMapCameraSource/MapCameraState, soMunimModelLayer.attach(to:)can draw over any engine's map.
Shared API on every engine
modelRendering: 'auto' | 'native' | 'overlay'onMunimMapView: who drawsmodels.autolets 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.modelRenderingandcesium.modelRenderingare aliases.- Engine-only methods and events through one channel:
ref.current.providerCommand(command, argsJson)andonProviderEvent({ provider, name, data }), withproviderCommand(ref, name, args),parseProviderEvent,callProvider/addProviderEventListenerfor 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, betweenonMarkerDragStartandonMarkerDragEnd, on every engine (MapKit reads the dragged view on a display link).- react-native-maps' region API:
initialRegion, a controlledregion,onRegionChangeStart,onRegionChangeComplete(region)andref.animateToRegion(region, ms);initialCamerais optional when a region is given. MarkerViewon Android, on every engine: the views are drawn into a bitmap (again on change withtracksViewChanges); Mapbox shows them as view annotations, Google, MapLibre and Cesium as image markers.selectableMapFeaturesreaches 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, andmunim-maps-vehicles/bundled/<name>/bundled/glb/<name>(one file per model).scripts/vehicles/write-catalogue.mjsgenerates its files from the modelsmake-vehicles.swiftwrites.
Android
- Android:
MunimMapViewandMapModelLayerare Kotlin Nitro views.MunimMapViewdraws 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),occludermodels, zones, 3D paths,occlusion="buildings"(vector tiles), the Earth hiding the globe's far side,altitudeReference: 'sea'andfollowTerrain(Terrarium terrain tiles), andflyCamera/stopFlightstepped on the layer's frame clock for any engine that can set its camera. groundElevation()on Android.- Android:
hasLookAround/openLookAroundgo to the engine (Google's Street View);MapModelLayercan draw over react-native-maps' GoogleMapViewwhen 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 andsetIndoorLevel, 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 withclusterStyles; 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 SDKplay-services-maps3d0.2.0 behindmunimMaps.googleMaps3d=true/ the Expo plugin'sgoogleMaps3d): 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;googleGeometryandencodePolyline/decodePolylinein 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 declarativemapbox={{ … }}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) andMapboxServices(Geocoding, Search Box, Directions, Matrix, Isochrone). See the checklist in docs/providers.md. - Mapbox draws glTF / GLB
modelsnatively in its model layer (mapbox={{ modelRendering: 'auto' | 'native' | 'overlay' }},autoby default): lit, shadowed and hidden by Mapbox's buildings and terrain, with motion, spin,screenSizeandtint; 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 withapiKey),styleJson, dark style, satellite tiles formapStyleimagery/hybrid, style-specsources(vector, raster, raster-dem, GeoJSON with clustering, image, PMTiles, MLT) andlayers(all ten layer types, expressions, filters),images,light,transition,hillshadeandcolorRelieffrom 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.openMapsServicesandconfigureOpenMapsServices: Nominatim geocoding, Photon search and OSRM / Valhalla routing with configurable endpoints (the public servers are for light use only), anddecodePolyline.
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 owncesiumpackage withcesium: { bundled: true }; 13 MB, minified build only) and run in a WebView the engine owns (WKWebViewwith amunim-cesium://scheme handler,android.webkit.WebViewservinghttps://appassets.androidplatform.net/), noreact-native-webview. OpenStreetMap imagery and an ellipsoid without a key;cesiumIonTokenadds 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,MarkerViewon 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) oroverlay(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), andonProviderEventwithparseCesiumEvent(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 fromexample/.env.localor~/.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_URLpoints 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/behindMunimMapEngine; the 3D renderer now reads only the camera state, notMKMapView. Behaviour is unchanged (the on-device self-test still passes 47 of 47). decodePolylineis 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.hasCoveragework (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 throwsIllegalAccessError;getNativeModelsexists on Android; overlay taps ignore off-screen projections; a location override set before following starts moves the follow camera. - MapLibre:
getFeatureState,setFeatureState,removeFeatureStateand the offscreensnapshotcommand 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
initialCamerathat arrives aftergoogle.mode: '3d'.getCamera,getCamera3dand relativeflyTo/setCamera3dread the camera set in code (the SDK reports only gestures), camera changes made in code reachonCameraChange, andmap3dReadyfires 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,getCameraread 3 to 30 km for a 1.1 km camera for several seconds after the map was ready).