Skip to main content

rustial_engine/layers/
tile_layer.rs

1//! Concrete tile layer backed by a TileManager.
2
3use crate::layer::{Layer, LayerId};
4use crate::tile_cache::TileCacheStats;
5use crate::tile_lifecycle::TileLifecycleDiagnostics;
6use crate::tile_manager::{
7    TileManager, TileManagerCounters, TileSelectionConfig, TileSelectionStats, VisibleTileSet,
8    ZoomPrefetchDirection,
9};
10use crate::tile_source::TileSource;
11use crate::tile_source::TileSourceDiagnostics;
12use rustial_math::{FlatTileView, Frustum, GeoCoord, TileId, WorldBounds};
13use std::any::Any;
14use std::collections::HashSet;
15
16/// A raster tile layer that fetches slippy-map tiles from a source.
17pub struct TileLayer {
18    id: LayerId,
19    name: String,
20    visible: bool,
21    opacity: f32,
22    manager: TileManager,
23    last_visible_set: VisibleTileSet,
24}
25
26impl TileLayer {
27    /// Create a new tile layer.
28    pub fn new(
29        name: impl Into<String>,
30        source: Box<dyn TileSource>,
31        cache_capacity: usize,
32    ) -> Self {
33        Self::new_with_selection_config(
34            name,
35            source,
36            cache_capacity,
37            TileSelectionConfig::default(),
38        )
39    }
40
41    /// Create a new tile layer with an explicit tile-selection policy.
42    pub fn new_with_selection_config(
43        name: impl Into<String>,
44        source: Box<dyn TileSource>,
45        cache_capacity: usize,
46        selection_config: TileSelectionConfig,
47    ) -> Self {
48        Self {
49            id: LayerId::next(),
50            name: name.into(),
51            visible: true,
52            opacity: 1.0,
53            manager: TileManager::new_with_config(source, cache_capacity, selection_config),
54            last_visible_set: VisibleTileSet::default(),
55        }
56    }
57
58    /// Update tile fetching for the current frame.
59    pub fn update(
60        &mut self,
61        viewport_bounds: &WorldBounds,
62        zoom: u8,
63        camera_world: (f64, f64),
64        camera_distance: f64,
65    ) {
66        self.update_with_view(viewport_bounds, zoom, camera_world, camera_distance, None);
67    }
68
69    /// Update tile fetching for the current frame with optional
70    /// footprint-aware flat-view selection parameters.
71    pub fn update_with_view(
72        &mut self,
73        viewport_bounds: &WorldBounds,
74        zoom: u8,
75        camera_world: (f64, f64),
76        camera_distance: f64,
77        flat_view: Option<&FlatTileView>,
78    ) {
79        if self.visible {
80            self.last_visible_set = self.manager.update_with_view(
81                viewport_bounds,
82                zoom,
83                camera_world,
84                camera_distance,
85                flat_view,
86            );
87        }
88    }
89
90    /// Update tile fetching for the current frame using frustum-based
91    /// quadtree traversal (MapLibre-equivalent `coveringTiles` path).
92    pub fn update_with_frustum(&mut self, frustum: &Frustum, zoom: u8, camera_world: (f64, f64)) {
93        if self.visible {
94            self.last_visible_set = self
95                .manager
96                .update_with_frustum(frustum, zoom, camera_world);
97        }
98    }
99
100    /// Update tile fetching using MapLibre-equivalent covering-tiles
101    /// traversal with per-tile variable zoom heuristics.
102    ///
103    /// This is the preferred path for steep-pitch terrain views where
104    /// distant tiles should use lower zoom levels than near tiles.
105    pub fn update_with_covering(
106        &mut self,
107        frustum: &Frustum,
108        cam: &rustial_math::CoveringCamera,
109        opts: &rustial_math::CoveringTilesOptions,
110        camera_world: (f64, f64),
111    ) {
112        if self.visible {
113            self.last_visible_set =
114                self.manager
115                    .update_with_covering(frustum, cam, opts, camera_world);
116        }
117    }
118
119    /// The set of tiles visible in the last update.
120    pub fn visible_tiles(&self) -> &VisibleTileSet {
121        &self.last_visible_set
122    }
123
124    /// The set of source tiles the manager last considered the desired view.
125    pub fn desired_tiles(&self) -> &HashSet<TileId> {
126        self.manager.desired_tiles()
127    }
128
129    /// Read-only access to the most recent tile-selection/update stats.
130    pub fn last_selection_stats(&self) -> &TileSelectionStats {
131        self.manager.last_selection_stats()
132    }
133
134    /// Read-only access to cumulative tile-manager counters.
135    pub fn counters(&self) -> &TileManagerCounters {
136        self.manager.counters()
137    }
138
139    /// Snapshot counts of the current tile-cache state.
140    pub fn cache_stats(&self) -> TileCacheStats {
141        self.manager.cache_stats()
142    }
143
144    /// Optional runtime diagnostics from the underlying tile source.
145    pub fn source_diagnostics(&self) -> Option<TileSourceDiagnostics> {
146        self.manager.source_diagnostics()
147    }
148
149    /// Snapshot of recent tile lifecycle diagnostics.
150    pub fn lifecycle_diagnostics(&self) -> TileLifecycleDiagnostics {
151        self.manager.lifecycle_diagnostics()
152    }
153
154    /// Read-only access to the tile-selection policy.
155    pub fn selection_config(&self) -> &TileSelectionConfig {
156        self.manager.selection_config()
157    }
158
159    /// Replace the tile-selection policy.
160    pub fn set_selection_config(&mut self, config: TileSelectionConfig) {
161        self.manager.set_selection_config(config);
162    }
163
164    /// Access the underlying tile manager.
165    pub fn manager(&self) -> &TileManager {
166        &self.manager
167    }
168
169    /// Promote externally decoded tiles into the tile manager's cache.
170    ///
171    /// See [`TileManager::promote_decoded`] for details.
172    pub fn promote_decoded(
173        &mut self,
174        decoded: Vec<(rustial_math::TileId, crate::tile_source::TileResponse)>,
175    ) {
176        self.manager.promote_decoded(decoded);
177    }
178
179    /// Speculatively prefetch tiles for a predicted viewport without changing
180    /// the current visible tile set.
181    pub fn prefetch_with_view(
182        &mut self,
183        viewport_bounds: &WorldBounds,
184        zoom: u8,
185        camera_world: (f64, f64),
186        flat_view: Option<&FlatTileView>,
187        max_requests: usize,
188    ) -> usize {
189        self.manager.prefetch_with_view(
190            viewport_bounds,
191            zoom,
192            camera_world,
193            flat_view,
194            max_requests,
195        )
196    }
197
198    /// Speculatively prefetch tiles implied by the current desired set and a
199    /// zoom direction.
200    pub fn prefetch_zoom_direction(
201        &mut self,
202        camera_world: (f64, f64),
203        direction: ZoomPrefetchDirection,
204        max_requests: usize,
205    ) -> usize {
206        self.manager
207            .prefetch_zoom_direction(camera_world, direction, max_requests)
208    }
209
210    /// Speculatively prefetch tiles along a geographic route polyline.
211    ///
212    /// See [`TileManager::prefetch_route`] for details.
213    pub fn prefetch_route(
214        &mut self,
215        route: &[GeoCoord],
216        zoom: u8,
217        camera_world: (f64, f64),
218        max_requests: usize,
219    ) -> usize {
220        self.manager
221            .prefetch_route(route, zoom, camera_world, max_requests)
222    }
223}
224
225impl Layer for TileLayer {
226    fn id(&self) -> LayerId {
227        self.id
228    }
229
230    fn kind(&self) -> crate::layer::LayerKind {
231        crate::layer::LayerKind::Tile
232    }
233
234    fn name(&self) -> &str {
235        &self.name
236    }
237
238    fn visible(&self) -> bool {
239        self.visible
240    }
241
242    fn set_visible(&mut self, visible: bool) {
243        self.visible = visible;
244    }
245
246    fn opacity(&self) -> f32 {
247        self.opacity
248    }
249
250    fn set_opacity(&mut self, opacity: f32) {
251        self.opacity = opacity.clamp(0.0, 1.0);
252    }
253
254    fn as_any(&self) -> &dyn Any {
255        self
256    }
257
258    fn as_any_mut(&mut self) -> &mut dyn Any {
259        self
260    }
261}