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