Skip to main content

rustial_engine/map_state/
heavy_layers.rs

1use super::*;
2
3impl MapState {
4    pub(super) fn refresh_streamed_vector_layer_features(&mut self) -> HashMap<String, bool> {
5        let streamed_layers: Vec<StreamedVectorLayerRefreshSpec> = self
6            .layers
7            .iter()
8            .filter_map(|layer| {
9                let vector_layer = layer
10                    .as_any()
11                    .downcast_ref::<crate::layers::VectorLayer>()?;
12                let source_id = vector_layer.query_source_id.as_ref()?;
13                let runtime = self.streamed_vector_sources.get(source_id)?;
14                let layer_key = vector_layer
15                    .query_layer_id
16                    .as_deref()
17                    .unwrap_or(vector_layer.name())
18                    .to_owned();
19                Some(StreamedVectorLayerRefreshSpec {
20                    runtime_id: layer.id(),
21                    layer_key,
22                    source_id: source_id.clone(),
23                    source_layer: vector_layer.query_source_layer.clone(),
24                    visible_tiles: runtime.visible_tiles().tiles.clone(),
25                })
26            })
27            .collect();
28
29        let resolved = resolve_streamed_vector_layer_refresh(streamed_layers);
30
31        let mut changed = HashMap::new();
32        let active_keys: HashSet<String> = resolved
33            .iter()
34            .map(|entry| entry.layer_key.clone())
35            .collect();
36        self.streamed_vector_layer_fingerprints
37            .retain(|key, _| active_keys.contains(key));
38        self.streamed_vector_query_payloads
39            .retain(|key, _| active_keys.contains(key));
40
41        for resolved_layer in resolved {
42            let was_changed = self
43                .streamed_vector_layer_fingerprints
44                .get(&resolved_layer.layer_key)
45                .copied()
46                != Some(resolved_layer.fingerprint);
47            self.streamed_vector_layer_fingerprints
48                .insert(resolved_layer.layer_key.clone(), resolved_layer.fingerprint);
49            let (features, provenance) = resolved_layer.rebuild_feature_inputs();
50            self.streamed_vector_query_payloads.insert(
51                resolved_layer.layer_key.clone(),
52                resolved_layer.tile_payloads,
53            );
54
55            if let Some(layer) = self
56                .layers
57                .iter_mut()
58                .find(|layer| layer.id() == resolved_layer.runtime_id)
59            {
60                if let Some(vector_layer) = layer
61                    .as_any_mut()
62                    .downcast_mut::<crate::layers::VectorLayer>()
63                {
64                    vector_layer.set_features_with_provenance(features, provenance);
65                }
66            }
67
68            changed.insert(resolved_layer.layer_key, was_changed);
69        }
70
71        changed
72    }
73
74    pub(super) fn rebuild_symbol_query_payloads(&mut self) {
75        let view = VisiblePlacedSymbolView::new(&self.placed_symbols);
76        self.streamed_symbol_query_payloads = view.rebuild_query_payloads();
77        self.streamed_symbol_dependency_payloads = view.rebuild_dependency_payloads();
78    }
79
80    /// Look up the retained tile-owned payload view for a streamed vector layer.
81    ///
82    /// This keeps the optional-map lookup and empty fallback in one place so
83    /// query, pick, and regeneration paths all read payloads the same way.
84    pub(super) fn streamed_payload_view_for(&self, layer_id: &str) -> StreamedPayloadView<'_> {
85        StreamedPayloadView::from_optional(
86            self.streamed_vector_query_payloads
87                .get(layer_id)
88                .map(Vec::as_slice),
89        )
90    }
91
92    /// Look up retained tile-owned symbol query payloads for a layer.
93    ///
94    /// Symbol query and pick paths both read from the same retained placement
95    /// output, so this keeps the map lookup and empty-slice fallback in one
96    /// place instead of repeating it at each call site.
97    pub(super) fn streamed_symbol_query_payloads_for(
98        &self,
99        layer_id: &str,
100    ) -> &[SymbolQueryPayload] {
101        symbol_query_payloads_from_optional(
102            self.streamed_symbol_query_payloads
103                .get(layer_id)
104                .map(Vec::as_slice),
105        )
106    }
107
108    pub(super) fn invalidate_symbol_dependency_tiles(
109        &mut self,
110        matches: impl Fn(&crate::symbols::SymbolAssetDependencies) -> bool,
111    ) -> usize {
112        let affected: HashSet<StreamedSymbolPayloadKey> =
113            collect_affected_symbol_payloads(&self.streamed_symbol_dependency_payloads, matches);
114
115        if affected.is_empty() {
116            return 0;
117        }
118
119        for (layer_id, tile) in &affected {
120            if let Some(tile) = tile {
121                self.dirty_streamed_symbol_tiles
122                    .entry(layer_id.clone())
123                    .or_default()
124                    .insert(*tile);
125            } else {
126                self.dirty_streamed_symbol_layers.insert(layer_id.clone());
127            }
128        }
129        self.data_update_elapsed = self.data_update_interval;
130
131        self.placed_symbols = Arc::new(prune_affected_symbol_payloads(
132            &affected,
133            &self.placed_symbols,
134            &mut self.streamed_symbol_query_payloads,
135            &mut self.streamed_symbol_dependency_payloads,
136        ));
137        self.symbol_assets
138            .rebuild_from_symbols(&self.placed_symbols);
139        affected.len()
140    }
141
142    /// Heavy per-frame update for terrain, vectors, symbols, and models.
143    ///
144    /// Throttled during animation to avoid stutter from synchronous CPU
145    /// work.  Tile layer updates are handled separately by
146    /// [`update_tile_layers`](Self::update_tile_layers).
147    pub(super) fn update_heavy_layers(&mut self, dt_seconds: f64) {
148        use crate::layer::LayerKind;
149        use crate::layers::{VectorLayer, VectorRenderMode};
150
151        // Use covering-tiles with per-tile variable zoom when terrain is
152        // enabled and the camera is pitched in perspective mode.
153        // Terrain: when covering-tiles is active, use the tile layer's
154        // visible tile targets so terrain meshes are guaranteed to have
155        // matching raster textures available.  Otherwise fall back to
156        // the terrain manager's own viewport-based selection.
157        if let Some(terrain_tiles) = self.desired_terrain_tiles() {
158            self.update_terrain_with_tiles(&terrain_tiles);
159        } else {
160            self.update_terrain();
161        }
162
163        // Vector layers -- uses the sync tessellation cache to skip
164        // re-tessellation when neither the style, feature data, nor the
165        // projection have changed since the previous frame.
166        let camera_projection = self.camera.projection();
167        let mut all_vectors: Vec<VectorMeshData> = Vec::new();
168        let mut symbol_candidates = Vec::new();
169        let mut had_symbol_layer = false;
170        let mut active_keys: HashSet<SyncVectorCacheKey> = HashSet::new();
171
172        self.refresh_streamed_vector_layer_features();
173
174        for layer in self.layers.iter() {
175            if !layer.visible() || layer.kind() != LayerKind::Vector {
176                continue;
177            }
178            if let Some(vector_layer) = layer.as_any().downcast_ref::<VectorLayer>() {
179                let layer_id = vector_layer
180                    .query_layer_id
181                    .as_deref()
182                    .unwrap_or(vector_layer.name());
183                let is_streamed_layer = vector_layer
184                    .query_source_id
185                    .as_ref()
186                    .is_some_and(|source_id| self.streamed_vector_sources.contains_key(source_id));
187                had_symbol_layer =
188                    had_symbol_layer || vector_layer.style.render_mode == VectorRenderMode::Symbol;
189
190                // Build the cache key from stable layer identity, style
191                // fingerprint, data generation, and current projection.
192                let cache_key = SyncVectorCacheKey {
193                    layer_id: vector_layer.id(),
194                    style_fingerprint: vector_layer.style.tessellation_fingerprint(),
195                    data_generation: vector_layer.data_generation(),
196                    projection: camera_projection,
197                };
198                active_keys.insert(cache_key);
199
200                // Cache hit ? reuse the previously tessellated mesh.
201                if let Some(entry) = self.sync_vector_cache.get(&cache_key) {
202                    if !entry.mesh.is_empty() {
203                        all_vectors.push(entry.mesh.clone());
204                    }
205                } else {
206                    // Cache miss ? tessellate and store.
207                    let mesh = vector_layer.tessellate(camera_projection);
208                    if !mesh.is_empty() {
209                        all_vectors.push(mesh.clone());
210                    }
211                    self.sync_vector_cache
212                        .insert(cache_key, SyncVectorCacheEntry { mesh });
213                }
214
215                if is_streamed_layer {
216                    let streamed_payloads = self.streamed_payload_view_for(layer_id);
217                    let (features, provenance) = streamed_payloads.rebuild_feature_inputs();
218                    symbol_candidates.extend(
219                        vector_layer.symbol_candidates_for_features(&features, &provenance),
220                    );
221                } else {
222                    symbol_candidates.extend(vector_layer.symbol_candidates());
223                }
224            }
225        }
226
227        // Prune cache entries for layers that are no longer active or whose
228        // key has changed (style/data/projection changed).
229        self.sync_vector_cache
230            .retain(|key, _| active_keys.contains(key));
231        self.vector_meshes = Arc::new(all_vectors);
232
233        // Symbol placement (main thread).
234        if had_symbol_layer && !symbol_candidates.is_empty() {
235            let meters_per_pixel = self.camera.meters_per_pixel();
236            let placed = self.symbol_placement.place_candidates(
237                &symbol_candidates,
238                camera_projection,
239                meters_per_pixel,
240                dt_seconds,
241                Some(&self.scene_viewport_bounds),
242            );
243            self.symbol_assets.rebuild_from_symbols(&placed);
244            self.placed_symbols = Arc::new(placed);
245            self.rebuild_symbol_query_payloads();
246        } else if !had_symbol_layer {
247            self.placed_symbols = Arc::new(Vec::new());
248            self.streamed_symbol_query_payloads.clear();
249            self.streamed_symbol_dependency_payloads.clear();
250            self.dirty_streamed_symbol_tiles.clear();
251        }
252
253        // Model layers.
254        let mut models = Vec::new();
255        for layer in self.layers.iter() {
256            if !layer.visible() || layer.kind() != LayerKind::Model {
257                continue;
258            }
259            if let Some(model_layer) = layer.as_any().downcast_ref::<crate::layers::ModelLayer>() {
260                models.extend(model_layer.instances.iter().cloned());
261            }
262        }
263        self.model_instances = Arc::new(models);
264
265        // Visualization layers.
266        self.collect_visualization_overlays();
267
268        // Image overlay layers.
269        self.collect_image_overlays();
270
271        self.dirty_streamed_symbol_layers.clear();
272        self.dirty_streamed_symbol_tiles.clear();
273    }
274
275    pub(super) fn apply_pending_frame_overrides(&mut self) {
276        if let Some(meshes) = self.pending_terrain_meshes.take() {
277            self.terrain_meshes = meshes;
278        }
279
280        if let Some(meshes) = self.pending_vector_meshes.take() {
281            self.vector_meshes = meshes;
282        }
283
284        if let Some(instances) = self.pending_model_instances.take() {
285            self.model_instances = instances;
286        }
287    }
288
289    /// Collect visualization overlays from the layer stack.
290    pub(super) fn collect_visualization_overlays(&mut self) {
291        use crate::visualization::{
292            GridExtrusionLayer, GridScalarLayer, InstancedColumnLayer, PointCloudLayer,
293            VisualizationOverlay,
294        };
295
296        let mut overlays = Vec::new();
297        for layer in self.layers.iter() {
298            if !layer.visible() {
299                continue;
300            }
301            if layer.kind() != crate::layer::LayerKind::Visualization {
302                continue;
303            }
304            if let Some(grid_layer) = layer.as_any().downcast_ref::<GridScalarLayer>() {
305                overlays.push(VisualizationOverlay::GridScalar {
306                    layer_id: grid_layer.id(),
307                    grid: grid_layer.grid.clone(),
308                    field: grid_layer.field.clone(),
309                    ramp: grid_layer.ramp.clone(),
310                });
311            } else if let Some(grid_layer) = layer.as_any().downcast_ref::<GridExtrusionLayer>() {
312                overlays.push(VisualizationOverlay::GridExtrusion {
313                    layer_id: grid_layer.id(),
314                    grid: grid_layer.grid.clone(),
315                    field: grid_layer.field.clone(),
316                    ramp: grid_layer.ramp.clone(),
317                    params: grid_layer.params.clone(),
318                });
319            } else if let Some(col_layer) = layer.as_any().downcast_ref::<InstancedColumnLayer>() {
320                overlays.push(VisualizationOverlay::Columns {
321                    layer_id: col_layer.id(),
322                    columns: col_layer.columns.clone(),
323                    ramp: col_layer.ramp.clone(),
324                });
325            } else if let Some(point_layer) = layer.as_any().downcast_ref::<PointCloudLayer>() {
326                overlays.push(VisualizationOverlay::Points {
327                    layer_id: point_layer.id(),
328                    points: point_layer.points.clone(),
329                    ramp: point_layer.ramp.clone(),
330                });
331            }
332        }
333        self.visualization_overlays = Arc::new(overlays);
334    }
335
336    /// Collect image overlays from the layer stack.
337    pub(super) fn collect_image_overlays(&mut self) {
338        use crate::layers::DynamicImageOverlayLayer;
339        use crate::layers::ImageOverlayLayer;
340
341        let projection = self.camera.projection();
342        let mut overlays = Vec::new();
343        for layer in self.layers.iter_mut() {
344            if !layer.visible() {
345                continue;
346            }
347            if let Some(img_layer) = layer.as_any().downcast_ref::<ImageOverlayLayer>() {
348                overlays.push(img_layer.to_overlay_data(projection));
349            } else if let Some(dyn_layer) = layer
350                .as_any_mut()
351                .downcast_mut::<DynamicImageOverlayLayer>()
352            {
353                dyn_layer.poll_frame();
354                if let Some(data) = dyn_layer.to_overlay_data(projection) {
355                    overlays.push(data);
356                }
357            }
358        }
359        self.image_overlays = Arc::new(overlays);
360    }
361
362    /// Rebuild the layer stack from a style document.
363    pub(super) fn apply_style_document(
364        &mut self,
365        document: &StyleDocument,
366    ) -> Result<(), StyleError> {
367        let layers = document.to_runtime_layers()?;
368        self.layers = LayerStack::new();
369        for layer in layers {
370            self.layers.push(layer);
371        }
372        self.streamed_vector_sources = build_streamed_vector_sources(document);
373        self.streamed_vector_layer_fingerprints.clear();
374        self.streamed_vector_query_payloads.clear();
375        self.streamed_symbol_query_payloads.clear();
376        self.streamed_symbol_dependency_payloads.clear();
377        self.dirty_streamed_symbol_layers.clear();
378        self.dirty_streamed_symbol_tiles.clear();
379        Ok(())
380    }
381}
382
383impl MapState {
384    /// Return `true` if the attached style currently uses the given source id.
385    pub fn style_source_is_used(&self, source_id: &str) -> bool {
386        match self.style_document() {
387            Some(document) => document.source_is_used(source_id),
388            None => false,
389        }
390    }
391
392    /// Return the ordered style layer ids that currently reference the given source id.
393    pub fn style_layer_ids_using_source(&self, source_id: &str) -> Vec<&str> {
394        match self.style_document() {
395            Some(document) => document.layer_ids_using_source(source_id),
396            None => Vec::new(),
397        }
398    }
399
400    /// Replace a source in the attached style document and re-apply the style.
401    pub fn reload_style_source(
402        &mut self,
403        source_id: impl Into<String>,
404        source: crate::style::StyleSource,
405    ) -> Result<bool, StyleError> {
406        let Some(mut style): Option<MapStyle> = self.style.take() else {
407            return Ok(false);
408        };
409        style.document_mut().set_source(source_id.into(), source);
410        self.apply_style_document(style.document())?;
411        self.style = Some(style);
412        Ok(true)
413    }
414
415    /// Remove a source from the attached style document and re-apply the style.
416    pub fn clear_style_source(
417        &mut self,
418        source_id: &str,
419    ) -> Result<Option<crate::style::StyleSource>, StyleError> {
420        let Some(mut style): Option<MapStyle> = self.style.take() else {
421            return Ok(None);
422        };
423        let removed = style.document_mut().remove_source(source_id);
424        self.apply_style_document(style.document())?;
425        self.style = Some(style);
426        Ok(removed)
427    }
428}
429
430fn build_streamed_vector_sources(
431    document: &StyleDocument,
432) -> HashMap<String, crate::layers::TileLayer> {
433    document
434        .sources()
435        .filter_map(|(source_id, source)| match source {
436            crate::style::StyleSource::VectorTile(vector_source) => {
437                vector_source.make_tile_source().map(|tile_source| {
438                    (
439                        source_id.to_owned(),
440                        crate::layers::TileLayer::new_with_selection_config(
441                            format!("__vector_source::{source_id}"),
442                            tile_source,
443                            vector_source.cache_capacity,
444                            vector_source.selection.clone(),
445                        ),
446                    )
447                })
448            }
449            _ => None,
450        })
451        .collect()
452}
453
454pub(super) fn wrapped_world_delta(delta: f64) -> f64 {
455    let half_world = WGS84_CIRCUMFERENCE * 0.5;
456    if delta > half_world {
457        delta - WGS84_CIRCUMFERENCE
458    } else if delta < -half_world {
459        delta + WGS84_CIRCUMFERENCE
460    } else {
461        delta
462    }
463}
464
465pub(super) fn translated_world_bounds(bounds: &WorldBounds, delta: glam::DVec2) -> WorldBounds {
466    WorldBounds::new(
467        WorldCoord::new(
468            bounds.min.position.x + delta.x,
469            bounds.min.position.y + delta.y,
470            bounds.min.position.z,
471        ),
472        WorldCoord::new(
473            bounds.max.position.x + delta.x,
474            bounds.max.position.y + delta.y,
475            bounds.max.position.z,
476        ),
477    )
478}