rustial-engine 0.0.1

Framework-agnostic 2.5D map engine for rustial
Documentation
//! Concrete tile layer backed by a TileManager.

use crate::layer::{Layer, LayerId};
use crate::tile_cache::TileCacheStats;
use crate::tile_lifecycle::TileLifecycleDiagnostics;
use crate::tile_manager::{
    TileManager, TileManagerCounters, TileSelectionConfig, TileSelectionStats, VisibleTileSet,
    ZoomPrefetchDirection,
};
use crate::tile_source::TileSource;
use crate::tile_source::TileSourceDiagnostics;
use rustial_math::{FlatTileView, Frustum, GeoCoord, TileId, WorldBounds};
use std::any::Any;
use std::collections::HashSet;

/// A raster tile layer that fetches slippy-map tiles from a source.
pub struct TileLayer {
    id: LayerId,
    name: String,
    visible: bool,
    opacity: f32,
    manager: TileManager,
    last_visible_set: VisibleTileSet,
}

impl TileLayer {
    /// Create a new tile layer.
    pub fn new(
        name: impl Into<String>,
        source: Box<dyn TileSource>,
        cache_capacity: usize,
    ) -> Self {
        Self::new_with_selection_config(
            name,
            source,
            cache_capacity,
            TileSelectionConfig::default(),
        )
    }

    /// Create a new tile layer with an explicit tile-selection policy.
    pub fn new_with_selection_config(
        name: impl Into<String>,
        source: Box<dyn TileSource>,
        cache_capacity: usize,
        selection_config: TileSelectionConfig,
    ) -> Self {
        Self {
            id: LayerId::next(),
            name: name.into(),
            visible: true,
            opacity: 1.0,
            manager: TileManager::new_with_config(source, cache_capacity, selection_config),
            last_visible_set: VisibleTileSet::default(),
        }
    }

    /// Update tile fetching for the current frame.
    pub fn update(
        &mut self,
        viewport_bounds: &WorldBounds,
        zoom: u8,
        camera_world: (f64, f64),
        camera_distance: f64,
    ) {
        self.update_with_view(viewport_bounds, zoom, camera_world, camera_distance, None);
    }

    /// Update tile fetching for the current frame with optional
    /// footprint-aware flat-view selection parameters.
    pub fn update_with_view(
        &mut self,
        viewport_bounds: &WorldBounds,
        zoom: u8,
        camera_world: (f64, f64),
        camera_distance: f64,
        flat_view: Option<&FlatTileView>,
    ) {
        if self.visible {
            self.last_visible_set = self.manager.update_with_view(
                viewport_bounds,
                zoom,
                camera_world,
                camera_distance,
                flat_view,
            );
        }
    }

    /// Update tile fetching for the current frame using frustum-based
    /// quadtree traversal (MapLibre-equivalent `coveringTiles` path).
    pub fn update_with_frustum(&mut self, frustum: &Frustum, zoom: u8, camera_world: (f64, f64)) {
        if self.visible {
            self.last_visible_set = self
                .manager
                .update_with_frustum(frustum, zoom, camera_world);
        }
    }

    /// Update tile fetching using MapLibre-equivalent covering-tiles
    /// traversal with per-tile variable zoom heuristics.
    ///
    /// This is the preferred path for steep-pitch terrain views where
    /// distant tiles should use lower zoom levels than near tiles.
    pub fn update_with_covering(
        &mut self,
        frustum: &Frustum,
        cam: &rustial_math::CoveringCamera,
        opts: &rustial_math::CoveringTilesOptions,
        camera_world: (f64, f64),
    ) {
        if self.visible {
            self.last_visible_set =
                self.manager
                    .update_with_covering(frustum, cam, opts, camera_world);
        }
    }

    /// The set of tiles visible in the last update.
    pub fn visible_tiles(&self) -> &VisibleTileSet {
        &self.last_visible_set
    }

    /// The set of source tiles the manager last considered the desired view.
    pub fn desired_tiles(&self) -> &HashSet<TileId> {
        self.manager.desired_tiles()
    }

    /// Read-only access to the most recent tile-selection/update stats.
    pub fn last_selection_stats(&self) -> &TileSelectionStats {
        self.manager.last_selection_stats()
    }

    /// Read-only access to cumulative tile-manager counters.
    pub fn counters(&self) -> &TileManagerCounters {
        self.manager.counters()
    }

    /// Snapshot counts of the current tile-cache state.
    pub fn cache_stats(&self) -> TileCacheStats {
        self.manager.cache_stats()
    }

    /// Optional runtime diagnostics from the underlying tile source.
    pub fn source_diagnostics(&self) -> Option<TileSourceDiagnostics> {
        self.manager.source_diagnostics()
    }

    /// Snapshot of recent tile lifecycle diagnostics.
    pub fn lifecycle_diagnostics(&self) -> TileLifecycleDiagnostics {
        self.manager.lifecycle_diagnostics()
    }

    /// Read-only access to the tile-selection policy.
    pub fn selection_config(&self) -> &TileSelectionConfig {
        self.manager.selection_config()
    }

    /// Replace the tile-selection policy.
    pub fn set_selection_config(&mut self, config: TileSelectionConfig) {
        self.manager.set_selection_config(config);
    }

    /// Access the underlying tile manager.
    pub fn manager(&self) -> &TileManager {
        &self.manager
    }

    /// Promote externally decoded tiles into the tile manager's cache.
    ///
    /// See [`TileManager::promote_decoded`] for details.
    pub fn promote_decoded(
        &mut self,
        decoded: Vec<(rustial_math::TileId, crate::tile_source::TileResponse)>,
    ) {
        self.manager.promote_decoded(decoded);
    }

    /// Speculatively prefetch tiles for a predicted viewport without changing
    /// the current visible tile set.
    pub fn prefetch_with_view(
        &mut self,
        viewport_bounds: &WorldBounds,
        zoom: u8,
        camera_world: (f64, f64),
        flat_view: Option<&FlatTileView>,
        max_requests: usize,
    ) -> usize {
        self.manager.prefetch_with_view(
            viewport_bounds,
            zoom,
            camera_world,
            flat_view,
            max_requests,
        )
    }

    /// Speculatively prefetch tiles implied by the current desired set and a
    /// zoom direction.
    pub fn prefetch_zoom_direction(
        &mut self,
        camera_world: (f64, f64),
        direction: ZoomPrefetchDirection,
        max_requests: usize,
    ) -> usize {
        self.manager
            .prefetch_zoom_direction(camera_world, direction, max_requests)
    }

    /// Speculatively prefetch tiles along a geographic route polyline.
    ///
    /// See [`TileManager::prefetch_route`] for details.
    pub fn prefetch_route(
        &mut self,
        route: &[GeoCoord],
        zoom: u8,
        camera_world: (f64, f64),
        max_requests: usize,
    ) -> usize {
        self.manager
            .prefetch_route(route, zoom, camera_world, max_requests)
    }
}

impl Layer for TileLayer {
    fn id(&self) -> LayerId {
        self.id
    }

    fn kind(&self) -> crate::layer::LayerKind {
        crate::layer::LayerKind::Tile
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn visible(&self) -> bool {
        self.visible
    }

    fn set_visible(&mut self, visible: bool) {
        self.visible = visible;
    }

    fn opacity(&self) -> f32 {
        self.opacity
    }

    fn set_opacity(&mut self, opacity: f32) {
        self.opacity = opacity.clamp(0.0, 1.0);
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
}