martin_core/tiles/
source.rs

1use std::collections::HashMap;
2use std::fmt::Debug;
3
4use async_trait::async_trait;
5use martin_tile_utils::{TileCoord, TileData, TileInfo};
6use tilejson::TileJSON;
7
8use crate::tiles::MartinCoreResult;
9use crate::tiles::catalog::CatalogSourceEntry;
10
11/// URL query parameters for dynamic tile generation.
12pub type UrlQuery = HashMap<String, String>;
13
14/// Core trait for tile sources providing data to Martin
15///
16/// Implementors can serve tiles from databases, files, or other backends.
17#[async_trait]
18pub trait Source: Send + Debug {
19    /// Unique source identifier used in URLs.
20    fn get_id(&self) -> &str;
21
22    /// `TileJSON` specification served to clients.
23    fn get_tilejson(&self) -> &TileJSON;
24
25    /// Technical tile information (format, encoding, etc.).
26    fn get_tile_info(&self) -> TileInfo;
27
28    /// Creates a boxed clone for trait object storage.
29    fn clone_source(&self) -> BoxedSource;
30
31    /// A version string for this source, if available. Default: None.
32    /// If available, this string is appended to tile URLs as a query parameter,
33    /// invalidating caches.
34    fn get_version(&self) -> Option<String> {
35        None
36    }
37
38    /// Whether this source accepts URL query parameters. Default: false.
39    fn support_url_query(&self) -> bool {
40        false
41    }
42
43    /// Whether martin-cp should use concurrent scraping. Default: false.
44    fn benefits_from_concurrent_scraping(&self) -> bool {
45        false
46    }
47
48    /// Retrieves tile data for the given coordinates.
49    ///
50    /// # Arguments
51    /// * `xyz` - Tile coordinates (x, y, zoom)
52    /// * `url_query` - Optional query parameters for dynamic tiles
53    async fn get_tile(
54        &self,
55        xyz: TileCoord,
56        url_query: Option<&UrlQuery>,
57    ) -> MartinCoreResult<TileData>;
58
59    /// Validates zoom level against `TileJSON` min/max zoom constraints.
60    fn is_valid_zoom(&self, zoom: u8) -> bool {
61        let tj = self.get_tilejson();
62        tj.minzoom.is_none_or(|minzoom| zoom >= minzoom)
63            && tj.maxzoom.is_none_or(|maxzoom| zoom <= maxzoom)
64    }
65
66    /// Generates catalog entry for this source.
67    fn get_catalog_entry(&self) -> CatalogSourceEntry {
68        let id = self.get_id();
69        let tilejson = self.get_tilejson();
70        let info = self.get_tile_info();
71        CatalogSourceEntry {
72            content_type: info.format.content_type().to_string(),
73            content_encoding: info.encoding.content_encoding().map(ToString::to_string),
74            name: tilejson.name.as_ref().filter(|v| *v != id).cloned(),
75            description: tilejson.description.clone(),
76            attribution: tilejson.attribution.clone(),
77        }
78    }
79}
80
81/// Boxed tile source trait object for storage in collections.
82pub type BoxedSource = Box<dyn Source>;
83
84impl Clone for BoxedSource {
85    fn clone(&self) -> Self {
86        self.clone_source()
87    }
88}