Skip to content

Releases: TechIdiots-LLC/MaplibreNativeMAUI

v4.5.1

Choose a tag to compare

@github-actions github-actions released this 28 Aug 13:00
79e26ea

🐞 Bug fixes

  • Android: the 64-bit libmln-cabi.so was not 16 KB page aligned, so Google Play rejected apps that shipped it — Android 15 allows devices with 16 KB memory pages, and Play now blocks uploads whose native libraries are linked for 4 KB ("Your app is not compatible with 16 KB memory page sizes"). The release CI builds with NDK r27, whose linker still defaults max-page-size to 4 KB; r28's picks 16 KB on its own, which is why a local build looked fine while every published arm64-v8a and x86_64 .so — 4.5.0 included — carried 0x1000-aligned LOAD segments. The Android link now passes -Wl,-z,max-page-size=16384 on the two 64-bit ABIs whatever the NDK version, and both Android native workflows fail the build if the linked .so comes out below 16 KB. armeabi-v7a is untouched — the NDK applies the flag to 64-bit ABIs only, and 32-bit Android has no 16 KB devices. Windows and Apple need nothing: the Windows build emits a PE via MSVC, where -Wl,-z has no meaning and the loader wants 4 KB sections, and on Apple mln-cabi is a static archive with no LOAD segments of its own — the app's own arm64 link already aligns to 16 KB. Reported in #32.

v4.5.0

Choose a tag to compare

@github-actions github-actions released this 16 Jul 04:12

✨ Features and improvements

  • GpsFollowZoomMode / GpsFollowZoom — configurable zoom when GPS Follow engages — new properties on the MAUI MapLibreMap control (and matching dependency properties on the WPF MlnMapImage) controlling the camera zoom applied when the GPS control enters Follow mode (via the on-map button, or the first fix while following). KeepCurrent (default) preserves the old behaviour (only zooms to 14 when further out than 8); Fixed always eases to the GpsFollowZoom level (default 16, the vistumbler-android behaviour); Accuracy computes the zoom from the fix's reported accuracy so the accuracy circle spans about a third of the viewport — a sharp fix lands at street level (clamped to 17), a coarse cell-grade fix stays zoomed out to cover its uncertainty (clamped to 10). Later fixes never change the zoom, so a manual pinch/scroll zoom sticks until Follow is re-entered. Entering Follow via the button now also applies the entry zoom on Android/iOS (previously only Windows/WPF re-eased the camera there).

v4.4.0

Choose a tag to compare

@github-actions github-actions released this 13 Jul 06:08
717776b

✨ Features and improvements

  • GPS control reworked: independent tracking and bearing buttons — on all platforms (Android, iOS/MacCatalyst, WinUI, WPF) the on-map GPS control's two buttons now have separate jobs, mirroring maplibre-gl-js's GeolocateControl and the Vistumbler Android app. The top button cycles the tracking mode Off ○ → Show ⊙ → Follow ◎ → Off (the old combined FollowBearing state is gone), and the bottom button — previously a plain reset-to-north — cycles the camera bearing mode Free ↺ → North-up N → GPS bearing ➤ → Free. The two combine: Follow + GPS bearing re-centres and rotates with each fix; Show + GPS bearing rotates the camera with the fix bearing without re-centring. Manually panning the map while in Follow drops the control back to Show (one click on the button re-enters Follow), and manually rotating (two-finger twist on Android, nav d-pad on any platform) drops the bearing mode back to Free; the CameraTrackingDismissed event fires when a pan dismisses Follow. The location dot now always points in the direction of travel regardless of bearing mode.

v4.2.2

Choose a tag to compare

@github-actions github-actions released this 12 Jul 01:13
d0ab8e7

🐞 Bug fixes

  • Android: fixed a native crash (SIGSEGV in OnlineFileSource) under heavy tile traffic — when an HTTP response arrived, the provider removed the request from its pending table before posting the callback onto the RunLoop. A request destroyed in that window (routine with many vector sources loading while panning/zooming or backgrounding the app, where superseded requests are constantly cancelled) couldn't be flagged as cancelled — its destructor found nothing to mark — so the already-posted callback ran against the freed OnlineFileRequest and crashed in mbgl::util::Timer. The pending entry now stays registered until the callback actually executes, and the posted closure re-checks the cancelled flag (it runs on the same RunLoop thread as the request's destructor, making the check decisive).
  • Manually opened attribution stays open long enough to read — tapping the collapsed ⓘ chip now pins the banner on every platform: on Android/iOS camera motion no longer collapses it instantly (with GPS-follow active the camera eases on every fix, which swatted the banner shut the moment it opened), and on all platforms (Android, iOS/MacCatalyst, WinUI, WPF) a manually opened banner gets a longer 10 s auto-collapse instead of the standard 5 s. Automatic expansions (style load, attribution content changes) keep the existing behaviour.

v4.2.1

Choose a tag to compare

@github-actions github-actions released this 11 Jul 20:25
bda514b

🐞 Bug fixes

  • Attribution banner no longer re-expands on every source refreshonSourceChanged fires for every runtime source mutation, including an app updating a GeoJSON source on a timer, and the attribution handler rebuilt the banner and re-expanded it each time — so an app refreshing a live overlay every few seconds had the attribution popup permanently reopening. The Android, iOS/MacCatalyst, and WPF controls now track the attribution content they last applied and only rewrite/re-expand the banner when that content actually changed (a new style load still shows it once, and newly added sources still surface their attribution). The WinUI control already only refreshed while empty and is unchanged.

v4.2.0

Choose a tag to compare

@github-actions github-actions released this 10 Jul 01:44

✨ Features and improvements

  • Android: two-finger pinch-zoom, rotate and tilt — the two-finger gesture handling was reworked, porting maplibre-gl-js's two_fingers_touch model (MapLibreMapController.Android.cs). Each of zoom / rotate / tilt now activates independently off its own threshold (zoom: cumulative log2-zoom delta; rotate: pixels along the touch-circle circumference, scaled by the smallest finger separation seen; tilt: both fingers moving vertically in the same direction), so an ordinary pinch no longer also spins or tilts the map. The map also claims the touch stream from any ancestor ScrollView/ViewPager2 via requestDisallowInterceptTouchEvent, so gestures work when the map is hosted inside a scrolling/paged container.
  • Sample app: map pages are one continuous scroll — the demo map pages previously split the screen into a map area and a separate fixed control panel (two disconnected surfaces). Each map page is now a single ScrollView with the map at the top (bounded height) and the controls flowing directly beneath, so dragging the map pans it while dragging the controls scrolls the page.

🐞 Bug fixes

  • Android: app crashed (native stack overflow) as soon as the map opened — the render callback ran Render() + RunLoop.RunOnce() synchronously from inside mbgl's update() dispatch, so a burst of tile-load events during the initial style load recursed on the same native stack until the (very stack-hungry) Adreno GL driver overflowed the thread. The callback now only flags a pending frame; a separate PostOnAnimation loop drives Render()/RunOnce(), mirroring the Windows controller.
  • Android: touch gestures did nothing (only the on-screen buttons worked)RotateGesturesEnabled / ScrollGesturesEnabled / TiltGesturesEnabled / ZoomGesturesEnabled were declared without a default, so they defaulted to false and MAUI's initial property sync disabled all gesture input at startup. They now default to true.
  • Android: panning was far slower than the finger, and pinch anchored to the wrong point — touch screen-coordinates (pan delta, pinch focus, tap position, rotate pivot) were divided by the display's pixel ratio before being handed to the native map, which uses the same raw device-pixel space as SetSize. On a high-density/scaled screen a full-width drag panned only a fraction of the map. The redundant division was removed.
  • Android: polygon fills rendered as a checkerboard and tiles showed white seams — the EGL config requested no depth or stencil buffer, but mbgl-core's fill-layer renderer needs the stencil buffer for polygon tessellation and tile-boundary clipping (Windows already requests a depth/stencil pixel format). EGL_DEPTH_SIZE, 24 and EGL_STENCIL_SIZE, 8 are now requested.
  • Android: map content stretched/squished (or blanked) after rotating the deviceRendererBackend::assumeViewport() only updates mbgl-core's cached viewport; it never calls glViewport(). Since the GL viewport is context state (not surface state) it never reset on resize, so the hardware viewport stayed frozen at its first-frame value and content kept rasterizing through the stale rectangle. The Android backend now issues a real glViewport() in updateAssumedState() (and recreates the EGL surface on size changes), keeping the viewport honest across rotations and tab switches.
  • Android: tiles stopped refreshing when zooming in (stuck on lower-zoom content) — the C# HttpClient tile provider never cancelled requests mbgl superseded, so they ran to completion and starved the connection pool of slots for the tiles actually needed at the new zoom. The provider protocol gained a cancel callback (mbgl_set_http_cancel_provider) so superseded fetches are aborted and their connections freed. Only affected Android (Windows uses mbgl-core's built-in native HTTP).

v4.1.3

Choose a tag to compare

@github-actions github-actions released this 09 Jul 03:47
b181c2e

🐞 Bug fixes

  • Runtime vector-source layers now relayout via the upstream source-layer fix instead of a local workaroundmbgl::style::Layer::setSourceLayer() (and setSourceID()) now call observer->onLayerChanged() upstream (maplibre/maplibre-native#4372), so a circle/symbol/etc. layer whose source-layer is set via SetSourceLayer after it is added to the style correctly triggers a relayout of already-loaded tiles. The dependencies/maplibre-native submodule is bumped to include that fix, and the visibility-toggle workaround previously carried in mbgl_layer_set_source_layer (native/src/mln_cabi.cpp) has been removed. Runtime behaviour is unchanged — the workaround is simply no longer needed. See the 4.1.0-era source-layer bug fix below for the original symptom.

v4.1.2

Choose a tag to compare

@github-actions github-actions released this 07 Jul 13:38
9699444

🐞 Bug fixes

  • MAUI Windows: double-clicking the nav/GPS/d-pad overlay buttons leaked through to the map — On WinUI the second click of a fast double-click is raised as DoubleTapped (not a second Tapped), so the overlay buttons, which only handled Tapped, dropped every second press and let the unhandled DoubleTapped bubble past the button — zooming/panning the map "behind" it. Fixed by also handling DoubleTapped on the zoom (+/−) buttons, GPS buttons, and rotate/pitch d-pad arrows (running the same action and marking the event handled), and swallowing DoubleTapped on the attribution chip.

v4.1.0

Choose a tag to compare

@github-actions github-actions released this 05 Jul 18:50
2114ab8

🐞 Bug fixes

  • WPF: add AddLineLayer/AddFillLayer/AddRasterLayer wrappers to MlnMapI… (#16) (@acalcutt)

v4.0.0

Choose a tag to compare

@github-actions github-actions released this 05 Jul 13:57

⚠️ Breaking changes

  • WPF: MlnMapHost removed — The old HwndHost+Popup renderer has been deleted. MlnMapImage is now the only WPF map control. Migration: replace <wpf:MlnMapHost …> with <wpf:MlnMapImage …> in XAML; the public API surface (properties, events, methods) is identical, including the MapClicked event and its MlnMapClickEventArgs args.
  • MAUI Windows: WS_POPUP GL window renderer removed — The floating WS_POPUP renderer is gone; MapImageView (in-tree Image+WriteableBitmap) is now the only Windows path.
  • MAUI Windows: SwapChainMapView renamed to MapImageView — The class stopped using a swap chain when the renderer moved to Image+WriteableBitmap; the name now matches the implementation. It is created internally by the handler, so most apps are unaffected.
  • MapLibreMap.RotateGestureEnabled renamed to RotateGesturesEnabled — Now consistent with ScrollGesturesEnabled / TiltGesturesEnabled / ZoomGesturesEnabled (and with the README, which already documented the plural name).

✨ Features and improvements

  • Airspace-free map rendering — WPF (MlnMapImage) — The map is a real WPF Image element; nav/GPS/attribution controls are ordinary WPF children with correct z-order, clipping, DPI, and hit-testing. No more floating Popup windows, no HwndHost airspace, no per-tick overlay realignment. Powered by glReadPixels(GL_BGRA) into a WriteableBitmap after each frame — see docs/design/in-tree-map-surface.md.
  • Airspace-free map rendering — MAUI Windows (MapImageView) — The map is a real WinUI Image element; nav/GPS/attribution are real XAML children. The floating WS_POPUP window, PopupWndProc, and per-tick realignment are gone. Same glReadPixels+WriteableBitmap approach as WPF, with pixels written via IBufferByteAccess.
  • Android: TextureView replaces SurfaceViewTextureView is an ordinary in-tree View with no compositing hole, so MAUI content layers reliably above the map on Android. SurfaceCallback/ISurfaceHolderCallback replaced by TextureSurfaceListener/ISurfaceTextureListener.
  • Declarative over-the-map overlay elements — New Pin, Polyline, Polygon, and Circle overlay types modelled after Microsoft.Maui.Controls.Maps. Declared as direct children of MapLibreMap (or produced via ItemsSource/ItemTemplate); property changes sync to MapLibre style layers automatically. Each compiles to a GeoJSON source + style layer: PolylineLineLayer, PolygonFillLayer+LineLayer, PinSymbolLayer, Circle→circumference polygon+FillLayer.
  • Data-bound overlays: ItemsSource / ItemTemplate / ItemTemplateSelectorMapLibreMap now supports MVVM-style data binding of overlay elements, modelled after Microsoft.Maui.Controls.Maps.Map. Bind ItemsSource to a collection and supply an ItemTemplate (or ItemTemplateSelector) whose DataTemplate produces an overlay element (Pin, Polyline, Polygon, or Circle); each item's element gets the item as its BindingContext. Collections implementing INotifyCollectionChanged sync add/remove/replace/reset automatically. The WPF MlnMapImage gains an equivalent ItemsSource that binds an IEnumerable of the new MlnMapMarker model (lat/lon + optional label/colour) and renders them as a managed GeoJSON circle + label layer, with live INotifyCollectionChanged/INotifyPropertyChanged sync.
  • Pin upgrade: SymbolLayer + SDF spritePin now renders via a SymbolLayer with an SDF sprite (mln_marker) instead of a CircleLayer. Supports icon-color tinting, configurable text labels, and all standard symbol layout/paint properties.
  • AddSpriteImage / RemoveSpriteImage — New methods on IMapLibreMapController (and MlnMapImage) for adding and removing named SDF or raster sprites at runtime, on all platforms.
  • MAUI Windows nav d-pad — The MAUI Windows navigation panel now has a full 4-way rotate/pitch/compass d-pad (▲/▼ pitch, ◀/▶ rotate, centre reset-north + live compass tick), matching the WPF renderer added in 3.3.0.
  • SymbolLayerProperties — New properties class covering the full symbol layer paint and layout property set.
  • MapSpan camera overloads — New JumpTo / EaseTo / FlyTo overloads on IMapLibreMapController that take a MapSpan, for fitting a geographic region into view.
  • MapLibreMap.VisibleRegion read-back — New VisibleRegion property (a MapSpan?) exposing the region currently visible on screen, refreshed whenever the camera becomes idle and raising PropertyChanged for data binding. A GetVisibleRegion() method reads it on demand. Backed by a new GetVisibleBounds() method on IMapLibreMapController (all platforms) that returns the actual visible lat/lng bounding box. The WPF MlnMapImage exposes the same VisibleRegion (a read-only DependencyProperty) and GetVisibleRegion().
  • Sample: ShapesPage — New sample page demonstrating Polyline, Polygon, and Circle overlay elements. MarkersPage converted to declarative Pin elements.
  • Vortice D3D packages removedVortice.Direct3D9 (WPF), Vortice.Direct3D11, and Vortice.DXGI (MAUI handlers) are no longer dependencies.
  • Offline regions + ambient cache (cabi 2.2.0) — New mbgl_offline_* C ABI family wrapping mbgl::DatabaseFileSource, and a new MbglOfflineManager (Task-based async API in MapLibreNative.Maui): create tile-pyramid or GeoJSON-geometry offline regions, start/pause downloads with progress + error observers (RegionProgress/RegionError events), list regions, query download status, round-trip opaque binary region metadata, delete/invalidate regions, merge (side-load) a secondary cache database, set the Mapbox tile-count limit, and ambient-cache maintenance (SetMaximumAmbientCacheSizeAsync, ClearAmbientCacheAsync, InvalidateAmbientCacheAsync, PackDatabaseAsync, ResetDatabaseAsync). Callbacks fire on MapLibre's database thread; the C# wrapper surfaces them as Tasks and events. Exercised end-to-end by the WPF sample's --autotest (download → progress → list → metadata → delete → ambient clear/pack).
  • Persistent tile cache by default — mbgl's default cache is :memory:, and none of the map views passed a cache path, so nothing survived an app restart and offline regions could never be served to the map. All map surfaces (MAUI Windows/Android/iOS + WPF MlnMapImage) now default to the new MbglCache.DefaultPath ({LocalApplicationData}/MapLibreNative.Maui/{processName}/cache.db), which MbglOfflineManager also uses by default — so offline regions downloaded by the manager are rendered by the map automatically, including with the network forced offline via MbglNetwork.Online = false.
  • MAUI sample: Offline page — New tab demonstrating the offline workflow: download the visible region (with live progress from the observer), list regions with size + metadata, delete all, and a network offline/online toggle.
  • GeoJSON source options / clustering — New mbgl_style_add_geojson_source_options C ABI function, MbglStyle.AddGeoJsonSourceOptions(sourceId, optionsJson), and an AddGeoJsonSource(sourceName, source, optionsJson) overload on IMapLibreMapController (all platforms) and WPF MlnMapImage. Accepts the style-spec GeoJSON source options (cluster, clusterRadius, clusterMaxZoom, clusterMinPoints, clusterProperties, maxzoom, buffer, tolerance, lineMetrics), enabling point clustering for typed (non-JSON-spec) sources.
  • Cluster expansion queries — New mbgl_map_query_feature_extensions C ABI function plus QueryFeatureExtensions (on MbglMap) and convenience helpers GetClusterExpansionZoom, GetClusterChildren, and GetClusterLeaves (on MbglMap, IMapLibreMapController, and MlnMapImage) for drilling into supercluster clusters (tap-to-expand, list cluster members). Exercised end-to-end by the WPF sample's --autotest harness.
  • Source-feature queries — New mbgl_map_query_source_features C ABI function and QuerySourceFeatures(sourceId, sourceLayerIds, filterJson) on MbglMap, IMapLibreMapController, and MlnMapImage: query all features in a source's data (with optional style-spec filter), independent of what is currently rendered.
  • Camera edge padding — New mbgl_map_jump_to_padded / mbgl_map_ease_to_padded / mbgl_map_fly_to_padded / mbgl_map_get_camera C ABI functions and matching padded JumpTo/EaseTo/FlyTo overloads on MbglMap, IMapLibreMapController, and MlnMapImage (plus MbglMap.GetCamera). Padding (top/left/bottom/right, screen px) centres the target in the unobscured part of the viewport — useful when panels overlap the map. NaN zoom/bearing/pitch means "keep current value" in the padded variants. MlnMapImage also gains plain full-camera JumpTo/EaseTo/FlyTo.
  • ScaleBy — New mbgl_map_scale_by C ABI function and ScaleBy(scale, anchorX, anchorY, durationMs) on MbglMap, IMapLibreMapController, and MlnMapImage for anchored zoom (2.0 = one zoom level in).
  • Offline mode toggle — New mbgl_network_status_set / mbgl_network_status_get C ABI functions and static MbglNetwork.Online property: force MapLibre offline (serve only cached resources) and back online at runtime.
  • API key + cache size at map creation — New mbgl_map_create2 C ABI function; MbglMap's constructor gains optional apiKey and maxCacheSizeBytes parameters (ResourceOptions::withApiKey / withMaximumCacheSize).
  • Transform state read-back — New mbgl_map_is_gesture_in_progress / mbgl_map_is_rotating / mbgl_map_is_scaling / mbgl_map_is_panning C ABI functions and matching MbglMap properties.

🐞 Bug fixes

  • Blank map on WPF and MAUI WindowsWGLRenderableResource::bind() in platform_frontend_windows.cpp unconditionally calls `glBindFramebuffer(0...
Read more