Skip to main content

maplibre_native/render/
query.rs

1use std::ptr;
2
3use maplibre_native_core as maplibre_core;
4use maplibre_native_sys as sys;
5
6use crate::Result;
7use crate::geojson::{Feature, FeatureNativeExt};
8use crate::json::{JsonValue, JsonValueNativeExt};
9
10pub use maplibre_core::query::{
11    FeatureExtensionResult, FeatureStateSelector, QueriedFeature, RenderedFeatureQueryOptions,
12    RenderedQueryGeometry, SourceFeatureQueryOptions,
13};
14pub(crate) use maplibre_core::query::{
15    FeatureStateSelectorNativeExt, NativeRenderedFeatureQueryOptions,
16    NativeSourceFeatureQueryOptions, RenderedFeatureQueryOptionsNativeExt,
17    RenderedQueryGeometryNativeExt, SourceFeatureQueryOptionsNativeExt,
18};
19
20impl super::RenderSessionHandle {
21    /// Sets per-feature state on a render source for this session.
22    pub fn set_feature_state(
23        &self,
24        selector: &FeatureStateSelector,
25        state: &JsonValue,
26    ) -> Result<()> {
27        self.inner.ensure_no_frame_acquired()?;
28        let session = self.inner.as_ptr()?;
29        let selector = selector.to_native();
30        let state = state.try_to_native()?;
31        // SAFETY: session is live. selector and state own all call-scoped
32        // descriptor storage until the native call returns.
33        maplibre_core::check(unsafe {
34            sys::mln_render_session_set_feature_state(session, selector.as_ptr(), state.as_ptr())
35        })
36    }
37
38    /// Copies per-feature state from a render source in this session.
39    pub fn get_feature_state(&self, selector: &FeatureStateSelector) -> Result<JsonValue> {
40        self.inner.ensure_no_frame_acquired()?;
41        let session = self.inner.as_ptr()?;
42        let selector = selector.to_native();
43        let mut out = maplibre_core::ptr::OutPtr::<sys::mln_json_snapshot>::new();
44        // SAFETY: session is live, selector owns call-scoped storage, and out
45        // is a null-initialized out-pointer owned by this call.
46        maplibre_core::check(unsafe {
47            sys::mln_render_session_get_feature_state(session, selector.as_ptr(), out.as_mut_ptr())
48        })?;
49        // SAFETY: On success, the C API returns either null or an owned JSON
50        // snapshot handle for this call; core copies and releases it.
51        Ok(
52            unsafe { maplibre_core::json::copy_json_snapshot(out.into_option()) }?
53                .unwrap_or_else(|| JsonValue::Object(Vec::new())),
54        )
55    }
56
57    /// Removes per-feature state selected for this session.
58    pub fn remove_feature_state(&self, selector: &FeatureStateSelector) -> Result<()> {
59        self.inner.ensure_no_frame_acquired()?;
60        let session = self.inner.as_ptr()?;
61        let selector = selector.to_native();
62        // SAFETY: session is live and selector owns all call-scoped storage
63        // until the native call returns.
64        maplibre_core::check(unsafe {
65            sys::mln_render_session_remove_feature_state(session, selector.as_ptr())
66        })
67    }
68
69    /// Queries rendered features from the latest render session state.
70    pub fn query_rendered_features(
71        &self,
72        geometry: &RenderedQueryGeometry,
73        options: Option<&RenderedFeatureQueryOptions>,
74    ) -> Result<Vec<QueriedFeature>> {
75        self.inner.ensure_no_frame_acquired()?;
76        let session = self.inner.as_ptr()?;
77        let geometry = geometry.to_native();
78        let options = options
79            .map(RenderedFeatureQueryOptions::to_native)
80            .transpose()?;
81        let mut out = maplibre_core::ptr::OutPtr::<sys::mln_feature_query_result>::new();
82        // SAFETY: session is live; geometry and options retain all borrowed
83        // descriptor storage for the call; out is a null-initialized owned
84        // out-pointer.
85        maplibre_core::check(unsafe {
86            sys::mln_render_session_query_rendered_features(
87                session,
88                geometry.as_ptr(),
89                options
90                    .as_ref()
91                    .map_or(ptr::null(), NativeRenderedFeatureQueryOptions::as_ptr),
92                out.as_mut_ptr(),
93            )
94        })?;
95        // SAFETY: On success, the C API returns an owned feature-query result
96        // handle; core copies and releases it.
97        unsafe {
98            maplibre_core::query::copy_feature_query_result(
99                out.into_non_null("mln_feature_query_result")?,
100            )
101        }
102    }
103
104    /// Queries source features from the latest render session state.
105    pub fn query_source_features(
106        &self,
107        source_id: &str,
108        options: Option<&SourceFeatureQueryOptions>,
109    ) -> Result<Vec<QueriedFeature>> {
110        self.inner.ensure_no_frame_acquired()?;
111        let session = self.inner.as_ptr()?;
112        let source_id = maplibre_core::string::string_view(source_id);
113        let options = options
114            .map(SourceFeatureQueryOptions::to_native)
115            .transpose()?;
116        let mut out = maplibre_core::ptr::OutPtr::<sys::mln_feature_query_result>::new();
117        // SAFETY: session is live; source_id and options retain all borrowed
118        // descriptor storage for the call; out is a null-initialized owned
119        // out-pointer.
120        maplibre_core::check(unsafe {
121            sys::mln_render_session_query_source_features(
122                session,
123                source_id.raw(),
124                options
125                    .as_ref()
126                    .map_or(ptr::null(), NativeSourceFeatureQueryOptions::as_ptr),
127                out.as_mut_ptr(),
128            )
129        })?;
130        // SAFETY: On success, the C API returns an owned feature-query result
131        // handle; core copies and releases it.
132        unsafe {
133            maplibre_core::query::copy_feature_query_result(
134                out.into_non_null("mln_feature_query_result")?,
135            )
136        }
137    }
138
139    /// Queries a feature extension from the latest render session state.
140    ///
141    /// The `supercluster` extension reads the `cluster_id` feature property and
142    /// the `limit` and `offset` arguments as [`JsonValue::UInt`]. Other numeric
143    /// types are treated as absent: a `cluster_id` that is not
144    /// [`JsonValue::UInt`] returns [`FeatureExtensionResult::Value`] holding
145    /// [`JsonValue::Null`] instead of a feature collection, and a `limit` or
146    /// `offset` that is not [`JsonValue::UInt`] leaves `leaves` at the native
147    /// defaults of ten leaves at offset zero. Queried feature properties keep
148    /// their JSON value type, so a queried cluster feature can be passed back
149    /// unmodified.
150    pub fn query_feature_extension(
151        &self,
152        source_id: &str,
153        feature: &Feature,
154        extension: &str,
155        extension_field: &str,
156        arguments: Option<&JsonValue>,
157    ) -> Result<FeatureExtensionResult> {
158        self.inner.ensure_no_frame_acquired()?;
159        let session = self.inner.as_ptr()?;
160        let source_id = maplibre_core::string::string_view(source_id);
161        let extension = maplibre_core::string::string_view(extension);
162        let extension_field = maplibre_core::string::string_view(extension_field);
163        let feature = feature.try_to_native(0)?;
164        if let Some(arguments) = arguments
165            && !matches!(arguments, JsonValue::Object(_))
166        {
167            return Err(crate::Error::invalid_argument(
168                "feature extension arguments must be a JSON object",
169            ));
170        }
171        let arguments = arguments.map(JsonValue::try_to_native).transpose()?;
172        let mut out = maplibre_core::ptr::OutPtr::<sys::mln_feature_extension_result>::new();
173        // SAFETY: session is live; all string, feature, and optional JSON
174        // descriptors retain borrowed storage for the call; out is a
175        // null-initialized owned out-pointer.
176        maplibre_core::check(unsafe {
177            sys::mln_render_session_query_feature_extensions(
178                session,
179                source_id.raw(),
180                feature.as_ptr(),
181                extension.raw(),
182                extension_field.raw(),
183                arguments
184                    .as_ref()
185                    .map_or(ptr::null(), crate::json::NativeJsonValue::as_ptr),
186                out.as_mut_ptr(),
187            )
188        })?;
189        // SAFETY: On success, the C API returns an owned feature-extension
190        // result handle; core copies and releases it.
191        unsafe {
192            maplibre_core::query::copy_feature_extension_result(
193                out.into_non_null("mln_feature_extension_result")?,
194            )
195        }
196    }
197}