Skip to main content

maplibre_native_ffi/
map.rs

1use std::cell::RefCell;
2use std::fmt;
3use std::rc::Rc;
4
5use maplibre_native_ffi_core as maplibre_core;
6use maplibre_native_ffi_core::ptr::{const_ptr_or_null, mut_ptr_or_null, option_ptr};
7use maplibre_native_ffi_core::values::{
8    empty_lat_lng, empty_lat_lng_bounds as empty_bounds, empty_screen_point, lat_lngs_to_native,
9    screen_points_to_native,
10};
11use maplibre_native_ffi_sys as sys;
12
13#[cfg(test)]
14use crate::PremultipliedRgba8Image;
15use crate::camera::{
16    AnimationOptionsNativeExt, BoundOptionsNativeExt, CameraFitOptionsNativeExt,
17    CameraOptionsNativeExt, FreeCameraOptionsNativeExt, ProjectionModeNativeExt,
18};
19#[cfg(test)]
20use crate::custom_geometry::CanonicalTileId;
21use crate::events::MapId;
22use crate::handle::{ThreadAffineNativeHandle, closed_handle_error};
23use crate::options::{MapOptionsNativeExt, MapTileOptionsNativeExt, MapViewportOptionsNativeExt};
24use crate::render::{
25    MetalBorrowedTextureDescriptor, MetalOwnedTextureDescriptor, MetalSurfaceDescriptor,
26    OpenGLBorrowedTextureDescriptor, OpenGLOwnedTextureDescriptor, OpenGLSurfaceDescriptor,
27    RenderSessionHandle, VulkanBorrowedTextureDescriptor, VulkanOwnedTextureDescriptor,
28    VulkanSurfaceDescriptor, WebGpuBorrowedTextureDescriptor, WebGpuOwnedTextureDescriptor,
29    WebGpuSurfaceDescriptor,
30};
31use crate::runtime::{RuntimeHandle, RuntimeState};
32use crate::values::NativeValue;
33use crate::{
34    AnimationOptions, BoundOptions, CameraFitOptions, CameraOptions, Error, ErrorKind,
35    FreeCameraOptions, HandleOperationError, LatLng, LatLngBounds, MapDebugOptions, MapOptions,
36    MapProjectionHandle, MapTileOptions, MapViewportOptions, ProjectionMode, Result,
37    RuntimeEventMask, ScreenPoint,
38};
39
40mod style;
41pub use style::{
42    GeoJsonSourceOptions, ImageContent, ImageStretch, LocationIndicatorImageKind,
43    RasterDemEncoding, SourceInfo, SourceType, StyleImage, StyleImageInfo, StyleImageOptions,
44    StyleImageTextFit, StyleLayerVisibility, StyleTransitionOptions, TileJsonInfo, TileScheme,
45    TileSourceOptions, VectorTileEncoding,
46};
47
48#[derive(Debug)]
49pub(crate) struct MapState {
50    handle: ThreadAffineNativeHandle<sys::mln_map>,
51    runtime: RefCell<Option<Rc<RuntimeState>>>,
52    id: MapId,
53}
54
55impl MapState {
56    fn new(native: sys::mln_map, runtime: Rc<RuntimeState>, id: MapId) -> Result<Self> {
57        // SAFETY: native came from successful mln_map_create and is paired with
58        // the matching map destroy function.
59        let handle = unsafe {
60            ThreadAffineNativeHandle::from_handle(native, sys::mln_map_destroy, "mln_map")
61        }?;
62        Ok(Self {
63            handle,
64            runtime: RefCell::new(Some(runtime)),
65            id,
66        })
67    }
68
69    pub(crate) fn native(&self) -> Result<sys::mln_map> {
70        self.handle
71            .live_handle()
72            .ok_or_else(|| closed_handle_error("MapHandle"))
73    }
74
75    fn is_closed(&self) -> bool {
76        self.handle.is_closed()
77    }
78
79    fn close(&self) -> Result<()> {
80        // The destroy releases the callback state of this map's custom geometry
81        // sources before it returns.
82        self.handle.close()?;
83        self.runtime.borrow_mut().take();
84        Ok(())
85    }
86}
87
88impl Drop for MapState {
89    fn drop(&mut self) {
90        self.runtime.borrow_mut().take();
91        // A failed destroy, such as one with a render session still attached,
92        // is reported through the leak channel by the handle's own `Drop`.
93        let _ = self.handle.close();
94    }
95}
96
97/// Owner-thread map handle bound to a retained runtime.
98pub struct MapHandle {
99    pub(crate) inner: Rc<MapState>,
100}
101
102impl fmt::Debug for MapHandle {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.debug_struct("MapHandle")
105            .field("closed", &self.inner.is_closed())
106            .finish()
107    }
108}
109
110impl MapHandle {
111    /// Creates a map with explicit map options on the runtime owner thread.
112    pub fn with_options(runtime: &RuntimeHandle, options: &MapOptions) -> Result<Self> {
113        let runtime_ptr = runtime.inner.native()?;
114        let mut out = maplibre_core::ptr::OutHandle::<sys::mln_map>::new();
115        let raw_options = options.to_native()?;
116
117        // SAFETY: runtime_ptr is a live runtime handle. raw_options is a
118        // materialized map descriptor with size filled by the binding. out is a
119        // valid null-initialized out-pointer owned by this call.
120        maplibre_core::check(unsafe {
121            sys::mln_map_create(runtime_ptr, &raw_options, out.as_mut_ptr())
122        })?;
123        let native = out.get();
124        let id = MapId::new(native.0);
125        let state = Rc::new(MapState::new(native, Rc::clone(&runtime.inner), id)?);
126
127        Ok(Self { inner: state })
128    }
129
130    /// Returns this map's runtime-local event source identity.
131    pub fn id(&self) -> MapId {
132        self.inner.id
133    }
134
135    /// Explicitly destroys the map. A failed destroy leaves the native handle
136    /// live so child handles can keep retaining and closing the map.
137    ///
138    /// Closing discards this map's queued runtime events and its recorded
139    /// loading failure, with no flush and no terminal event. Dropping the
140    /// handle ends the event stream the same way.
141    pub fn close(self) -> std::result::Result<(), HandleOperationError<Self>> {
142        if self.inner.is_closed() {
143            return Ok(());
144        }
145        if Rc::strong_count(&self.inner) > 1 {
146            return Err(HandleOperationError::new(
147                Error::new(
148                    ErrorKind::InvalidState,
149                    None,
150                    "MapHandle cannot close while child handles are live",
151                ),
152                self,
153            ));
154        }
155        self.inner
156            .close()
157            .map_err(|error| HandleOperationError::new(error, self))
158    }
159
160    /// Requests a repaint for a continuous map.
161    pub fn request_repaint(&self) -> Result<()> {
162        let map = self.inner.native()?;
163        // SAFETY: map is a live map handle owned by this wrapper.
164        maplibre_core::check(unsafe { sys::mln_map_request_repaint(map) })
165    }
166
167    /// Requests one still image for a static or tile map.
168    pub fn request_still_image(&self) -> Result<()> {
169        let map = self.inner.native()?;
170        // SAFETY: map is a live map handle owned by this wrapper.
171        maplibre_core::check(unsafe { sys::mln_map_request_still_image(map) })
172    }
173
174    /// Selects which map-originated event types this map queues.
175    ///
176    /// A map reads the bits in
177    /// [`RuntimeEventMask::ALL_MAP_EVENTS`](crate::RuntimeEventMask::ALL_MAP_EVENTS),
178    /// so [`RuntimeEventMask::ALL`](crate::RuntimeEventMask::ALL) selects every
179    /// map-originated type. Narrowing gates later events and keeps queued ones.
180    /// A bit outside `ALL` is an invalid-argument error.
181    pub fn set_event_mask(&self, mask: RuntimeEventMask) -> Result<()> {
182        let map = self.inner.native()?;
183        // SAFETY: map is live. The C API validates unknown mask bits.
184        maplibre_core::check(unsafe { sys::mln_map_set_event_mask(map, mask.bits()) })
185    }
186
187    /// Reports which map-originated event types this map queues, starting from
188    /// the mask its creation options selected.
189    pub fn event_mask(&self) -> Result<RuntimeEventMask> {
190        let map = self.inner.native()?;
191        let mut raw = 0;
192        // SAFETY: map is live and out_mask points to writable u64 storage.
193        maplibre_core::check(unsafe { sys::mln_map_get_event_mask(map, &mut raw) })?;
194        Ok(RuntimeEventMask::from_bits_retain(raw))
195    }
196
197    /// Applies MapLibre debug overlay mask bits.
198    pub fn set_debug_options(&self, options: MapDebugOptions) -> Result<()> {
199        let map = self.inner.native()?;
200        // SAFETY: map is live. The C API validates unknown mask bits.
201        maplibre_core::check(unsafe { sys::mln_map_set_debug_options(map, options.bits()) })
202    }
203
204    /// Reads MapLibre debug overlay mask bits.
205    pub fn debug_options(&self) -> Result<MapDebugOptions> {
206        let map = self.inner.native()?;
207        let mut raw = 0;
208        // SAFETY: map is live and out_options points to writable u32 storage.
209        maplibre_core::check(unsafe { sys::mln_map_get_debug_options(map, &mut raw) })?;
210        Ok(MapDebugOptions::from_bits_retain(raw))
211    }
212
213    /// Enables or disables MapLibre's rendering stats overlay view.
214    pub fn set_rendering_stats_view_enabled(&self, enabled: bool) -> Result<()> {
215        let map = self.inner.native()?;
216        // SAFETY: map is live and enabled is passed by value.
217        maplibre_core::check(unsafe { sys::mln_map_set_rendering_stats_view_enabled(map, enabled) })
218    }
219
220    /// Reads whether MapLibre's rendering stats overlay view is enabled.
221    pub fn rendering_stats_view_enabled(&self) -> Result<bool> {
222        let map = self.inner.native()?;
223        let mut enabled = false;
224        // SAFETY: map is live and out_enabled points to writable bool storage.
225        maplibre_core::check(unsafe {
226            sys::mln_map_get_rendering_stats_view_enabled(map, &mut enabled)
227        })?;
228        Ok(enabled)
229    }
230
231    /// Reads whether MapLibre currently considers the map fully loaded.
232    pub fn is_fully_loaded(&self) -> Result<bool> {
233        let map = self.inner.native()?;
234        let mut loaded = false;
235        // SAFETY: map is live and out_loaded points to writable bool storage.
236        maplibre_core::check(unsafe { sys::mln_map_is_fully_loaded(map, &mut loaded) })?;
237        Ok(loaded)
238    }
239
240    /// Dumps map debug logs through MapLibre Native logging.
241    pub fn dump_debug_logs(&self) -> Result<()> {
242        let map = self.inner.native()?;
243        // SAFETY: map is live.
244        maplibre_core::check(unsafe { sys::mln_map_dump_debug_logs(map) })
245    }
246
247    /// Reads the map's logical viewport size in UI pixels and its pixel ratio.
248    /// The scale factor is fixed for the lifetime of the map and independent of
249    /// any render target's scale factor.
250    pub fn size(&self) -> Result<(u32, u32, f64)> {
251        let map = self.inner.native()?;
252        let mut width = 0u32;
253        let mut height = 0u32;
254        let mut scale_factor = 0f64;
255        // SAFETY: map is live and all three out pointers reference live locals
256        // for the duration of the call.
257        maplibre_core::check(unsafe {
258            sys::mln_map_get_size(map, &mut width, &mut height, &mut scale_factor)
259        })?;
260        Ok((width, height, scale_factor))
261    }
262
263    /// Reads live viewport and render-transform controls.
264    pub fn viewport_options(&self) -> Result<MapViewportOptions> {
265        let map = self.inner.native()?;
266        // SAFETY: Default constructor takes no arguments and initializes size.
267        let mut raw = unsafe { sys::mln_map_viewport_options_default() };
268        // SAFETY: map is live and raw has a valid size field for C to fill.
269        maplibre_core::check(unsafe { sys::mln_map_get_viewport_options(map, &mut raw) })?;
270        Ok(MapViewportOptions::from_native(raw))
271    }
272
273    /// Applies selected live viewport and render-transform controls.
274    pub fn set_viewport_options(&self, options: &MapViewportOptions) -> Result<()> {
275        let map = self.inner.native()?;
276        let raw = options.to_native();
277        // SAFETY: map is live and raw is a materialized descriptor valid for
278        // the duration of this call.
279        maplibre_core::check(unsafe { sys::mln_map_set_viewport_options(map, &raw) })
280    }
281
282    /// Reads tile prefetch and LOD tuning controls.
283    pub fn tile_options(&self) -> Result<MapTileOptions> {
284        let map = self.inner.native()?;
285        // SAFETY: Default constructor takes no arguments and initializes size.
286        let mut raw = unsafe { sys::mln_map_tile_options_default() };
287        // SAFETY: map is live and raw has a valid size field for C to fill.
288        maplibre_core::check(unsafe { sys::mln_map_get_tile_options(map, &mut raw) })?;
289        Ok(MapTileOptions::from_native(raw))
290    }
291
292    /// Applies selected tile prefetch and LOD tuning controls.
293    pub fn set_tile_options(&self, options: &MapTileOptions) -> Result<()> {
294        let map = self.inner.native()?;
295        let raw = options.to_native();
296        // SAFETY: map is live and raw is a materialized descriptor valid for
297        // the duration of this call.
298        maplibre_core::check(unsafe { sys::mln_map_set_tile_options(map, &raw) })
299    }
300
301    /// Reads the current camera snapshot.
302    pub fn camera(&self) -> Result<CameraOptions> {
303        let map = self.inner.native()?;
304        // SAFETY: Default constructor takes no arguments and initializes size.
305        let mut raw = unsafe { sys::mln_camera_options_default() };
306        // SAFETY: map is live and raw has a valid size field for C to fill.
307        maplibre_core::check(unsafe { sys::mln_map_get_camera(map, &mut raw) })?;
308        Ok(CameraOptions::from_native(raw))
309    }
310
311    /// Applies a camera jump command.
312    pub fn jump_to(&self, camera: &CameraOptions) -> Result<()> {
313        let map = self.inner.native()?;
314        let raw = camera.to_native();
315        // SAFETY: map is live and raw is a materialized descriptor valid for
316        // the duration of this call.
317        maplibre_core::check(unsafe { sys::mln_map_jump_to(map, &raw) })
318    }
319
320    /// Applies a camera ease transition command. An absent `animation`, or one
321    /// with no duration, reaches the target before this call returns.
322    pub fn ease_to(
323        &self,
324        camera: &CameraOptions,
325        animation: Option<&AnimationOptions>,
326    ) -> Result<()> {
327        let map = self.inner.native()?;
328        let raw_camera = camera.to_native();
329        let raw_animation = animation.map(AnimationOptions::to_native);
330        // SAFETY: map is live and descriptors are valid for this call. A null
331        // animation pointer requests native defaults.
332        maplibre_core::check(unsafe {
333            sys::mln_map_ease_to(map, &raw_camera, option_ptr(raw_animation.as_ref()))
334        })
335    }
336
337    /// Applies a camera fly transition command. Fly animates by default, so the
338    /// camera is still en route when this call returns and advances as the
339    /// runtime is pumped.
340    pub fn fly_to(
341        &self,
342        camera: &CameraOptions,
343        animation: Option<&AnimationOptions>,
344    ) -> Result<()> {
345        let map = self.inner.native()?;
346        let raw_camera = camera.to_native();
347        let raw_animation = animation.map(AnimationOptions::to_native);
348        // SAFETY: map is live and descriptors are valid for this call. A null
349        // animation pointer requests native defaults.
350        maplibre_core::check(unsafe {
351            sys::mln_map_fly_to(map, &raw_camera, option_ptr(raw_animation.as_ref()))
352        })
353    }
354
355    /// Applies a screen-space pan command.
356    pub fn move_by(&self, delta_x: f64, delta_y: f64) -> Result<()> {
357        let map = self.inner.native()?;
358        // SAFETY: map is live. The C API validates numeric values.
359        maplibre_core::check(unsafe { sys::mln_map_move_by(map, delta_x, delta_y) })
360    }
361
362    /// Applies an animated screen-space pan command.
363    /// An absent `animation`, or one with no duration, applies the pan
364    /// instantly.
365    pub fn move_by_animated(
366        &self,
367        delta_x: f64,
368        delta_y: f64,
369        animation: Option<&AnimationOptions>,
370    ) -> Result<()> {
371        let map = self.inner.native()?;
372        let raw_animation = animation.map(AnimationOptions::to_native);
373        // SAFETY: map is live and the optional animation descriptor is valid
374        // for this call. The C API validates numeric values.
375        maplibre_core::check(unsafe {
376            sys::mln_map_move_by_animated(map, delta_x, delta_y, option_ptr(raw_animation.as_ref()))
377        })
378    }
379
380    /// Applies a screen-space zoom command.
381    pub fn scale_by(&self, scale: f64, anchor: Option<ScreenPoint>) -> Result<()> {
382        let map = self.inner.native()?;
383        let raw_anchor = anchor.map(ScreenPoint::to_native);
384        // SAFETY: map is live and the optional anchor pointer is valid for this
385        // call. The C API validates numeric values.
386        maplibre_core::check(unsafe {
387            sys::mln_map_scale_by(map, scale, option_ptr(raw_anchor.as_ref()))
388        })
389    }
390
391    /// Applies an animated screen-space zoom command.
392    /// An absent `animation`, or one with no duration, applies the zoom
393    /// instantly.
394    pub fn scale_by_animated(
395        &self,
396        scale: f64,
397        anchor: Option<ScreenPoint>,
398        animation: Option<&AnimationOptions>,
399    ) -> Result<()> {
400        let map = self.inner.native()?;
401        let raw_anchor = anchor.map(ScreenPoint::to_native);
402        let raw_animation = animation.map(AnimationOptions::to_native);
403        // SAFETY: map is live and optional descriptors are valid for this call.
404        // The C API validates numeric values.
405        maplibre_core::check(unsafe {
406            sys::mln_map_scale_by_animated(
407                map,
408                scale,
409                option_ptr(raw_anchor.as_ref()),
410                option_ptr(raw_animation.as_ref()),
411            )
412        })
413    }
414
415    /// Applies a screen-space rotate command.
416    pub fn rotate_by(&self, first: ScreenPoint, second: ScreenPoint) -> Result<()> {
417        let map = self.inner.native()?;
418        // SAFETY: map is live. Points are passed by value and validated by C.
419        maplibre_core::check(unsafe {
420            sys::mln_map_rotate_by(map, first.to_native(), second.to_native())
421        })
422    }
423
424    /// Applies an animated screen-space rotate command.
425    /// An absent `animation`, or one with no duration, applies the rotation
426    /// instantly.
427    pub fn rotate_by_animated(
428        &self,
429        first: ScreenPoint,
430        second: ScreenPoint,
431        animation: Option<&AnimationOptions>,
432    ) -> Result<()> {
433        let map = self.inner.native()?;
434        let raw_animation = animation.map(AnimationOptions::to_native);
435        // SAFETY: map is live and optional animation descriptor is valid for
436        // this call. Points are passed by value and validated by C.
437        maplibre_core::check(unsafe {
438            sys::mln_map_rotate_by_animated(
439                map,
440                first.to_native(),
441                second.to_native(),
442                option_ptr(raw_animation.as_ref()),
443            )
444        })
445    }
446
447    /// Applies a pitch delta command.
448    pub fn pitch_by(&self, pitch: f64) -> Result<()> {
449        let map = self.inner.native()?;
450        // SAFETY: map is live. The C API validates numeric values.
451        maplibre_core::check(unsafe { sys::mln_map_pitch_by(map, pitch) })
452    }
453
454    /// Applies an animated pitch delta command.
455    /// An absent `animation`, or one with no duration, applies the pitch
456    /// instantly.
457    pub fn pitch_by_animated(
458        &self,
459        pitch: f64,
460        animation: Option<&AnimationOptions>,
461    ) -> Result<()> {
462        let map = self.inner.native()?;
463        let raw_animation = animation.map(AnimationOptions::to_native);
464        // SAFETY: map is live and optional animation descriptor is valid for
465        // this call. The C API validates numeric values.
466        maplibre_core::check(unsafe {
467            sys::mln_map_pitch_by_animated(map, pitch, option_ptr(raw_animation.as_ref()))
468        })
469    }
470
471    /// Cancels active camera transitions.
472    pub fn cancel_transitions(&self) -> Result<()> {
473        let map = self.inner.native()?;
474        // SAFETY: map is live.
475        maplibre_core::check(unsafe { sys::mln_map_cancel_transitions(map) })
476    }
477
478    /// Marks whether a host-driven gesture is in progress. The flag stays set
479    /// until the host clears it, so pair every `true` with a `false`.
480    pub fn set_gesture_in_progress(&self, in_progress: bool) -> Result<()> {
481        let map = self.inner.native()?;
482        // SAFETY: map is live and in_progress is passed by value.
483        maplibre_core::check(unsafe { sys::mln_map_set_gesture_in_progress(map, in_progress) })
484    }
485
486    /// Reads whether a host-driven gesture is currently in progress.
487    pub fn is_gesture_in_progress(&self) -> Result<bool> {
488        let map = self.inner.native()?;
489        let mut in_progress = false;
490        // SAFETY: map is live and out_in_progress points to writable bool
491        // storage.
492        maplibre_core::check(unsafe {
493            sys::mln_map_is_gesture_in_progress(map, &mut in_progress)
494        })?;
495        Ok(in_progress)
496    }
497
498    /// Computes a camera that fits geographic bounds in the current viewport.
499    pub fn camera_for_lat_lng_bounds(
500        &self,
501        bounds: LatLngBounds,
502        fit_options: Option<&CameraFitOptions>,
503    ) -> Result<CameraOptions> {
504        let map = self.inner.native()?;
505        let raw_fit = fit_options.map(CameraFitOptions::to_native);
506        // SAFETY: Default constructor takes no arguments and initializes size.
507        let mut raw_camera = unsafe { sys::mln_camera_options_default() };
508        // SAFETY: map is live, bounds is passed by value, optional fit options
509        // are valid for this call, and raw_camera is writable.
510        maplibre_core::check(unsafe {
511            sys::mln_map_camera_for_lat_lng_bounds(
512                map,
513                bounds.to_native(),
514                option_ptr(raw_fit.as_ref()),
515                &mut raw_camera,
516            )
517        })?;
518        Ok(CameraOptions::from_native(raw_camera))
519    }
520
521    /// Computes a camera that fits geographic coordinates in the current viewport.
522    pub fn camera_for_lat_lngs(
523        &self,
524        coordinates: &[LatLng],
525        fit_options: Option<&CameraFitOptions>,
526    ) -> Result<CameraOptions> {
527        let map = self.inner.native()?;
528        if coordinates.is_empty() {
529            return Err(Error::invalid_argument(
530                "camera_for_lat_lngs requires at least one coordinate",
531            ));
532        }
533        let raw_coordinates = lat_lngs_to_native(coordinates);
534        let raw_fit = fit_options.map(CameraFitOptions::to_native);
535        // SAFETY: Default constructor takes no arguments and initializes size.
536        let mut raw_camera = unsafe { sys::mln_camera_options_default() };
537        // SAFETY: map is live, arrays are valid for coordinate_count non-empty
538        // entries, optional fit options are valid, and raw_camera is writable.
539        maplibre_core::check(unsafe {
540            sys::mln_map_camera_for_lat_lngs(
541                map,
542                const_ptr_or_null(&raw_coordinates),
543                raw_coordinates.len(),
544                option_ptr(raw_fit.as_ref()),
545                &mut raw_camera,
546            )
547        })?;
548        Ok(CameraOptions::from_native(raw_camera))
549    }
550
551    /// Computes a camera that fits a geometry in the current viewport.
552    pub fn camera_for_geometry(
553        &self,
554        geometry: &[u8],
555        fit_options: Option<&CameraFitOptions>,
556    ) -> Result<CameraOptions> {
557        let map = self.inner.native()?;
558        let native_geometry = maplibre_core::string::buffer_view(geometry);
559        let raw_fit = fit_options.map(CameraFitOptions::to_native);
560        // SAFETY: Default constructor takes no arguments and initializes size.
561        let mut raw_camera = unsafe { sys::mln_camera_options_default() };
562        // SAFETY: map is live, native_geometry owns backing storage for the
563        // duration of this call, optional fit options are valid, and raw_camera
564        // is writable.
565        maplibre_core::check(unsafe {
566            sys::mln_map_camera_for_geometry(
567                map,
568                native_geometry,
569                option_ptr(raw_fit.as_ref()),
570                &mut raw_camera,
571            )
572        })?;
573        Ok(CameraOptions::from_native(raw_camera))
574    }
575
576    /// Computes geographic bounds for a camera from two viewport corners.
577    ///
578    /// The box is the hull of the top-left and bottom-right screen corners for
579    /// that camera in the current viewport. When bearing and pitch are zero, the
580    /// box equals the visible area. Those corners are the northwest and
581    /// southeast of the viewport. Longitudes stay in -180 to 180.
582    pub fn lat_lng_bounds_for_camera(&self, camera: &CameraOptions) -> Result<LatLngBounds> {
583        let map = self.inner.native()?;
584        let raw_camera = camera.to_native();
585        let mut raw_bounds = empty_bounds();
586        // SAFETY: map is live, raw_camera is a valid descriptor for this call,
587        // and raw_bounds points to writable storage.
588        maplibre_core::check(unsafe {
589            sys::mln_map_lat_lng_bounds_for_camera(map, &raw_camera, &mut raw_bounds)
590        })?;
591        Ok(LatLngBounds::from_native(raw_bounds))
592    }
593
594    /// Computes geographic bounds for a camera from the four viewport corners.
595    ///
596    /// The axis-aligned hull of all four screen corners and the center
597    /// encompasses the projected viewport. Longitudes unwrap onto the shortest
598    /// path through the center. A viewport that crosses the antimeridian reports
599    /// values outside -180 to 180.
600    pub fn lat_lng_bounds_for_camera_unwrapped(
601        &self,
602        camera: &CameraOptions,
603    ) -> Result<LatLngBounds> {
604        let map = self.inner.native()?;
605        let raw_camera = camera.to_native();
606        let mut raw_bounds = empty_bounds();
607        // SAFETY: map is live, raw_camera is a valid descriptor for this call,
608        // and raw_bounds points to writable storage.
609        maplibre_core::check(unsafe {
610            sys::mln_map_lat_lng_bounds_for_camera_unwrapped(map, &raw_camera, &mut raw_bounds)
611        })?;
612        Ok(LatLngBounds::from_native(raw_bounds))
613    }
614
615    /// Reads map camera constraint options.
616    pub fn bounds(&self) -> Result<BoundOptions> {
617        let map = self.inner.native()?;
618        // SAFETY: Default constructor takes no arguments and initializes size.
619        let mut raw = unsafe { sys::mln_bound_options_default() };
620        // SAFETY: map is live and raw has a valid size field for C to fill.
621        maplibre_core::check(unsafe { sys::mln_map_get_bounds(map, &mut raw) })?;
622        Ok(BoundOptions::from_native(raw))
623    }
624
625    /// Applies selected map camera constraint options.
626    pub fn set_bounds(&self, options: &BoundOptions) -> Result<()> {
627        let map = self.inner.native()?;
628        let raw = options.to_native();
629        // SAFETY: map is live and raw is a valid descriptor for this call.
630        maplibre_core::check(unsafe { sys::mln_map_set_bounds(map, &raw) })
631    }
632
633    /// Reads the current free camera position and orientation.
634    pub fn free_camera_options(&self) -> Result<FreeCameraOptions> {
635        let map = self.inner.native()?;
636        // SAFETY: Default constructor takes no arguments and initializes size.
637        let mut raw = unsafe { sys::mln_free_camera_options_default() };
638        // SAFETY: map is live and raw has a valid size field for C to fill.
639        maplibre_core::check(unsafe { sys::mln_map_get_free_camera_options(map, &mut raw) })?;
640        Ok(FreeCameraOptions::from_native(raw))
641    }
642
643    /// Applies selected free camera position and orientation fields.
644    pub fn set_free_camera_options(&self, options: &FreeCameraOptions) -> Result<()> {
645        let map = self.inner.native()?;
646        let raw = options.to_native();
647        // SAFETY: map is live and raw is a valid descriptor for this call.
648        maplibre_core::check(unsafe { sys::mln_map_set_free_camera_options(map, &raw) })
649    }
650
651    /// Reads current axonometric rendering options.
652    pub fn projection_mode(&self) -> Result<ProjectionMode> {
653        let map = self.inner.native()?;
654        // SAFETY: Default constructor takes no arguments and initializes size.
655        let mut raw = unsafe { sys::mln_projection_mode_default() };
656        // SAFETY: map is live and raw has a valid size field for C to fill.
657        maplibre_core::check(unsafe { sys::mln_map_get_projection_mode(map, &mut raw) })?;
658        Ok(ProjectionMode::from_native(raw))
659    }
660
661    /// Applies selected axonometric rendering option fields.
662    pub fn set_projection_mode(&self, mode: &ProjectionMode) -> Result<()> {
663        let map = self.inner.native()?;
664        let raw = mode.to_native();
665        // SAFETY: map is live and raw is a valid descriptor for this call.
666        maplibre_core::check(unsafe { sys::mln_map_set_projection_mode(map, &raw) })
667    }
668
669    /// Converts a geographic world coordinate to a screen point for the current map.
670    pub fn pixel_for_lat_lng(&self, coordinate: LatLng) -> Result<ScreenPoint> {
671        let map = self.inner.native()?;
672        let mut raw_point = empty_screen_point();
673        // SAFETY: map is live, coordinate is passed by value, and raw_point is
674        // writable storage for the output.
675        maplibre_core::check(unsafe {
676            sys::mln_map_pixel_for_lat_lng(map, coordinate.to_native(), &mut raw_point)
677        })?;
678        Ok(ScreenPoint::from_native(raw_point))
679    }
680
681    /// Converts a screen point to a geographic world coordinate for the current map.
682    ///
683    /// The longitude is wrapped to the range from -180 to 180 degrees.
684    pub fn lat_lng_for_pixel(&self, point: ScreenPoint) -> Result<LatLng> {
685        let map = self.inner.native()?;
686        let mut raw_coordinate = empty_lat_lng();
687        // SAFETY: map is live, point is passed by value, and raw_coordinate is
688        // writable storage for the output.
689        maplibre_core::check(unsafe {
690            sys::mln_map_lat_lng_for_pixel(map, point.to_native(), &mut raw_coordinate)
691        })?;
692        Ok(LatLng::from_native(raw_coordinate))
693    }
694
695    /// Converts a screen point to an unwrapped geographic coordinate.
696    ///
697    /// The longitude preserves the visible world copy and may fall outside
698    /// -180 to 180.
699    pub fn lat_lng_for_pixel_unwrapped(&self, point: ScreenPoint) -> Result<LatLng> {
700        let map = self.inner.native()?;
701        let mut raw_coordinate = empty_lat_lng();
702        // SAFETY: map is live, point is passed by value, and raw_coordinate is
703        // writable storage for the output.
704        maplibre_core::check(unsafe {
705            sys::mln_map_lat_lng_for_pixel_unwrapped(map, point.to_native(), &mut raw_coordinate)
706        })?;
707        Ok(LatLng::from_native(raw_coordinate))
708    }
709
710    /// Converts geographic world coordinates to screen points for the current map.
711    pub fn pixels_for_lat_lngs(&self, coordinates: &[LatLng]) -> Result<Vec<ScreenPoint>> {
712        let map = self.inner.native()?;
713        let raw_coordinates = lat_lngs_to_native(coordinates);
714        let mut raw_points = vec![empty_screen_point(); coordinates.len()];
715        // SAFETY: map is live. Input and output arrays are valid for len
716        // entries, or null when len is 0.
717        maplibre_core::check(unsafe {
718            sys::mln_map_pixels_for_lat_lngs(
719                map,
720                const_ptr_or_null(&raw_coordinates),
721                raw_coordinates.len(),
722                mut_ptr_or_null(&mut raw_points),
723            )
724        })?;
725        Ok(raw_points
726            .into_iter()
727            .map(ScreenPoint::from_native)
728            .collect())
729    }
730
731    /// Converts screen points to geographic world coordinates for the current map.
732    ///
733    /// Each longitude is wrapped to the range from -180 to 180 degrees.
734    pub fn lat_lngs_for_pixels(&self, points: &[ScreenPoint]) -> Result<Vec<LatLng>> {
735        let map = self.inner.native()?;
736        let raw_points = screen_points_to_native(points);
737        let mut raw_coordinates = vec![empty_lat_lng(); points.len()];
738        // SAFETY: map is live. Input and output arrays are valid for len
739        // entries, or null when len is 0.
740        maplibre_core::check(unsafe {
741            sys::mln_map_lat_lngs_for_pixels(
742                map,
743                const_ptr_or_null(&raw_points),
744                raw_points.len(),
745                mut_ptr_or_null(&mut raw_coordinates),
746            )
747        })?;
748        Ok(raw_coordinates
749            .into_iter()
750            .map(LatLng::from_native)
751            .collect())
752    }
753
754    /// Converts screen points to unwrapped geographic coordinates.
755    ///
756    /// Each longitude preserves its visible world copy and may fall outside
757    /// -180 to 180.
758    pub fn lat_lngs_for_pixels_unwrapped(&self, points: &[ScreenPoint]) -> Result<Vec<LatLng>> {
759        let map = self.inner.native()?;
760        let raw_points = screen_points_to_native(points);
761        let mut raw_coordinates = vec![empty_lat_lng(); points.len()];
762        // SAFETY: map is live. Input and output arrays are valid for len
763        // entries, or null when len is 0.
764        maplibre_core::check(unsafe {
765            sys::mln_map_lat_lngs_for_pixels_unwrapped(
766                map,
767                const_ptr_or_null(&raw_points),
768                raw_points.len(),
769                mut_ptr_or_null(&mut raw_coordinates),
770            )
771        })?;
772        Ok(raw_coordinates
773            .into_iter()
774            .map(LatLng::from_native)
775            .collect())
776    }
777
778    /// Creates a standalone projection snapshot from the current map transform.
779    pub fn create_projection(&self) -> Result<MapProjectionHandle> {
780        MapProjectionHandle::new(self)
781    }
782
783    /// Produces a [`Send`] reference to this map for attaching a render session.
784    /// A render session is owned by the thread that attaches it, which need not
785    /// be the map's owner thread.
786    pub fn attach_ref(&self) -> Result<MapAttachRef> {
787        Ok(MapAttachRef {
788            map: self.inner.native()?,
789        })
790    }
791}
792
793/// A reference to a map for the sole purpose of attaching a render session,
794/// produced by [`MapHandle::attach_ref`]. Attaching is the one map operation
795/// that runs on the render session's thread instead of the map's.
796///
797/// This is a copied handle value and carries no Rust retention of the map.
798/// Native destroys a map only once no session is attached, and rejects a
799/// reference that outlives its map rather than binding to a later one.
800///
801/// Close the session before the map: dropping a [`MapHandle`] with a session
802/// still attached leaks the native map, reports it through
803/// [`set_leak_reporter`](crate::set_leak_reporter), and leaves the runtime
804/// undestroyable.
805#[derive(Clone, Copy)]
806pub struct MapAttachRef {
807    map: sys::mln_map,
808}
809
810impl fmt::Debug for MapAttachRef {
811    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
812        f.debug_struct("MapAttachRef").finish()
813    }
814}
815
816impl MapAttachRef {
817    /// The handle of the map this reference names.
818    pub(crate) fn map(&self) -> sys::mln_map {
819        self.map
820    }
821
822    /// Attaches a Metal native surface render target to the map.
823    ///
824    /// The layer and optional device pointers are backend-native handles. They
825    /// must name valid Metal objects for this session and remain usable on the
826    /// owner thread until the session is detached or closed.
827    pub fn attach_metal_surface(
828        &self,
829        descriptor: &MetalSurfaceDescriptor,
830    ) -> Result<RenderSessionHandle> {
831        let raw = descriptor.to_native();
832        RenderSessionHandle::attach(self, |map, out| {
833            // SAFETY: map is live, raw is a materialized descriptor valid for
834            // this call, and out is a null-initialized out-pointer.
835            unsafe { sys::mln_metal_surface_attach(map, &raw, out) }
836        })
837    }
838
839    /// Attaches a Vulkan native surface render target to the map.
840    ///
841    /// Vulkan handles are borrowed. They must remain valid and externally
842    /// synchronized until the session is detached or closed.
843    pub fn attach_vulkan_surface(
844        &self,
845        descriptor: &VulkanSurfaceDescriptor,
846    ) -> Result<RenderSessionHandle> {
847        let raw = descriptor.to_native();
848        RenderSessionHandle::attach(self, |map, out| {
849            // SAFETY: map is live, raw is a materialized descriptor valid for
850            // this call, and out is a null-initialized out-pointer.
851            unsafe { sys::mln_vulkan_surface_attach(map, &raw, out) }
852        })
853    }
854
855    /// Attaches a WebGPU native surface render target to the map.
856    ///
857    /// The surface, device, and instance are borrowed. They must stay valid
858    /// until the session detaches or closes.
859    pub fn attach_webgpu_surface(
860        &self,
861        descriptor: &WebGpuSurfaceDescriptor,
862    ) -> Result<RenderSessionHandle> {
863        let raw = descriptor.to_native();
864        RenderSessionHandle::attach(self, |map, out| {
865            // SAFETY: map is live, raw is a materialized descriptor valid for
866            // this call, and out is a null-initialized out-pointer.
867            unsafe { sys::mln_webgpu_surface_attach(map, &raw, out) }
868        })
869    }
870
871    /// Attaches an OpenGL native surface render target to the map.
872    ///
873    /// OpenGL context provider and surface handles are borrowed. They must
874    /// remain valid and externally synchronized until the session is detached
875    /// or closed.
876    pub fn attach_opengl_surface(
877        &self,
878        descriptor: &OpenGLSurfaceDescriptor,
879    ) -> Result<RenderSessionHandle> {
880        let raw = descriptor.to_native();
881        RenderSessionHandle::attach(self, |map, out| {
882            // SAFETY: map is live, raw is a materialized descriptor valid for
883            // this call, and out is a null-initialized out-pointer.
884            unsafe { sys::mln_opengl_surface_attach(map, &raw, out) }
885        })
886    }
887
888    /// Attaches a Metal session-owned texture render target to the map.
889    ///
890    /// The device pointer must name a valid Metal device that remains usable on
891    /// the owner thread until the session is detached or closed.
892    pub fn attach_metal_owned_texture(
893        &self,
894        descriptor: &MetalOwnedTextureDescriptor,
895    ) -> Result<RenderSessionHandle> {
896        let raw = descriptor.to_native();
897        RenderSessionHandle::attach(self, |map, out| {
898            // SAFETY: map is live, raw is a materialized descriptor valid for
899            // this call, and out is a null-initialized out-pointer.
900            unsafe { sys::mln_metal_owned_texture_attach(map, &raw, out) }
901        })
902    }
903
904    /// Attaches a Metal caller-owned texture render target to the map.
905    ///
906    /// The texture pointer is borrowed. The caller owns the texture, keeps it
907    /// valid until detach or close, and synchronizes use outside this session.
908    pub fn attach_metal_borrowed_texture(
909        &self,
910        descriptor: &MetalBorrowedTextureDescriptor,
911    ) -> Result<RenderSessionHandle> {
912        let raw = descriptor.to_native();
913        RenderSessionHandle::attach(self, |map, out| {
914            // SAFETY: map is live, raw is a materialized descriptor valid for
915            // this call, and out is a null-initialized out-pointer.
916            unsafe { sys::mln_metal_borrowed_texture_attach(map, &raw, out) }
917        })
918    }
919
920    /// Attaches a Vulkan session-owned texture render target to the map.
921    ///
922    /// Vulkan device and queue handles are borrowed. They must remain valid and
923    /// externally synchronized until the session is detached or closed.
924    pub fn attach_vulkan_owned_texture(
925        &self,
926        descriptor: &VulkanOwnedTextureDescriptor,
927    ) -> Result<RenderSessionHandle> {
928        let raw = descriptor.to_native();
929        RenderSessionHandle::attach(self, |map, out| {
930            // SAFETY: map is live, raw is a materialized descriptor valid for
931            // this call, and out is a null-initialized out-pointer.
932            unsafe { sys::mln_vulkan_owned_texture_attach(map, &raw, out) }
933        })
934    }
935
936    /// Attaches a Vulkan caller-owned texture render target to the map.
937    ///
938    /// Vulkan handles, image, and image view are borrowed. The caller owns the
939    /// image resources, keeps them valid until detach or close, and handles
940    /// queue-family ownership and synchronization outside this session.
941    pub fn attach_vulkan_borrowed_texture(
942        &self,
943        descriptor: &VulkanBorrowedTextureDescriptor,
944    ) -> Result<RenderSessionHandle> {
945        let raw = descriptor.to_native();
946        RenderSessionHandle::attach(self, |map, out| {
947            // SAFETY: map is live, raw is a materialized descriptor valid for
948            // this call, and out is a null-initialized out-pointer.
949            unsafe { sys::mln_vulkan_borrowed_texture_attach(map, &raw, out) }
950        })
951    }
952
953    /// Attaches a WebGPU session-owned texture render target to the map.
954    ///
955    /// The WebGPU device and queue are borrowed. They must remain valid until
956    /// the session is detached or closed, and the session renders on the thread
957    /// that owns them.
958    pub fn attach_webgpu_owned_texture(
959        &self,
960        descriptor: &WebGpuOwnedTextureDescriptor,
961    ) -> Result<RenderSessionHandle> {
962        let raw = descriptor.to_native();
963        RenderSessionHandle::attach(self, |map, out| {
964            // SAFETY: map is live, raw is a materialized descriptor valid for
965            // this call, and out is a null-initialized out-pointer.
966            unsafe { sys::mln_webgpu_owned_texture_attach(map, &raw, out) }
967        })
968    }
969
970    /// Attaches a WebGPU caller-owned texture render target to the map.
971    ///
972    /// The texture and its view are borrowed. The caller owns them, keeps them
973    /// valid until detach or close, and creates both from the descriptor's
974    /// device.
975    pub fn attach_webgpu_borrowed_texture(
976        &self,
977        descriptor: &WebGpuBorrowedTextureDescriptor,
978    ) -> Result<RenderSessionHandle> {
979        let raw = descriptor.to_native();
980        RenderSessionHandle::attach(self, |map, out| {
981            // SAFETY: map is live, raw is a materialized descriptor valid for
982            // this call, and out is a null-initialized out-pointer.
983            unsafe { sys::mln_webgpu_borrowed_texture_attach(map, &raw, out) }
984        })
985    }
986
987    /// Attaches an OpenGL session-owned texture render target to the map.
988    ///
989    /// The context provider handles are borrowed. They must remain valid until
990    /// the session is detached or closed. Host sampling must use a context in
991    /// the same share group while the acquired frame remains open.
992    pub fn attach_opengl_owned_texture(
993        &self,
994        descriptor: &OpenGLOwnedTextureDescriptor,
995    ) -> Result<RenderSessionHandle> {
996        let raw = descriptor.to_native();
997        RenderSessionHandle::attach(self, |map, out| {
998            // SAFETY: map is live, raw is a materialized descriptor valid for
999            // this call, and out is a null-initialized out-pointer.
1000            unsafe { sys::mln_opengl_owned_texture_attach(map, &raw, out) }
1001        })
1002    }
1003
1004    /// Attaches an OpenGL caller-owned texture render target to the map.
1005    ///
1006    /// The context provider handles and texture object are borrowed. The caller
1007    /// owns the texture, keeps it valid until detach or close, and synchronizes
1008    /// use outside this session.
1009    pub fn attach_opengl_borrowed_texture(
1010        &self,
1011        descriptor: &OpenGLBorrowedTextureDescriptor,
1012    ) -> Result<RenderSessionHandle> {
1013        let raw = descriptor.to_native();
1014        RenderSessionHandle::attach(self, |map, out| {
1015            // SAFETY: map is live, raw is a materialized descriptor valid for
1016            // this call, and out is a null-initialized out-pointer.
1017            unsafe { sys::mln_opengl_borrowed_texture_attach(map, &raw, out) }
1018        })
1019    }
1020}
1021
1022#[cfg(test)]
1023mod tests;