bbox_tile_server/store/
mbtiles.rs

1use crate::config::MbtilesStoreCfg;
2use crate::mbtiles_ds::{Error as MbtilesDsError, MbtilesDatasource};
3use crate::store::{TileReader, TileStoreError, TileWriter};
4use async_trait::async_trait;
5use bbox_core::{Compression, TileResponse};
6use log::info;
7use martin_mbtiles::{CopyDuplicateMode, Metadata};
8use martin_tile_utils::{Encoding as TileEncoding, Format as TileFormat};
9use std::ffi::OsStr;
10use std::io::Cursor;
11use std::path::Path;
12use tile_grid::Xyz;
13
14#[derive(Clone, Debug)]
15pub struct MbtilesStore {
16    pub(crate) mbt: MbtilesDatasource,
17}
18
19impl MbtilesStore {
20    pub async fn from_config(cfg: &MbtilesStoreCfg) -> Result<Self, MbtilesDsError> {
21        info!("Creating connection pool for {}", &cfg.abs_path().display());
22        let mbt = MbtilesDatasource::from_config(cfg, None).await?;
23        //let opt = SqliteConnectOptions::new().filename(file).read_only(true);
24        Ok(MbtilesStore { mbt })
25    }
26    pub async fn from_config_writable(
27        cfg: &MbtilesStoreCfg,
28        metadata: Metadata,
29    ) -> Result<Self, MbtilesDsError> {
30        info!("Creating connection pool for {}", &cfg.abs_path().display());
31        let mbt = MbtilesDatasource::from_config(cfg, Some(metadata)).await?;
32        Ok(MbtilesStore { mbt })
33    }
34    pub fn config_from_cli_arg(file_or_url: &str) -> Option<MbtilesStoreCfg> {
35        match Path::new(file_or_url).extension().and_then(OsStr::to_str) {
36            Some("mbtiles") => {
37                let cfg = MbtilesStoreCfg {
38                    path: file_or_url.into(),
39                };
40                Some(cfg)
41            }
42            _ => None,
43        }
44    }
45}
46
47#[async_trait]
48impl TileWriter for MbtilesStore {
49    fn compression(&self) -> Compression {
50        match self.mbt.format_info.encoding {
51            TileEncoding::Gzip => Compression::Gzip,
52            _ => Compression::None,
53        }
54    }
55    async fn exists(&self, xyz: &Xyz) -> bool {
56        match self.mbt.get_tile(xyz.z, xyz.x as u32, xyz.y as u32).await {
57            Ok(None) | Err(_) => false,
58            Ok(_) => true,
59        }
60    }
61    async fn put_tile(&self, xyz: &Xyz, data: Vec<u8>) -> Result<(), TileStoreError> {
62        let mut conn = self.mbt.pool.acquire().await?;
63        self.mbt
64            .mbtiles
65            .insert_tiles(
66                &mut conn,
67                self.mbt.layout,
68                CopyDuplicateMode::Override,
69                &[(xyz.z, xyz.x as u32, xyz.y as u32, data)],
70            )
71            .await?;
72        Ok(())
73    }
74    async fn put_tiles(&mut self, tiles: &[(u8, u32, u32, Vec<u8>)]) -> Result<(), TileStoreError> {
75        let mut conn = self.mbt.pool.acquire().await?;
76        self.mbt
77            .mbtiles
78            .insert_tiles(
79                &mut conn,
80                self.mbt.layout,
81                CopyDuplicateMode::Override,
82                tiles,
83            )
84            .await?;
85        Ok(())
86    }
87}
88
89#[async_trait]
90impl TileReader for MbtilesStore {
91    async fn get_tile(&self, xyz: &Xyz) -> Result<Option<TileResponse>, TileStoreError> {
92        let resp =
93            if let Some(content) = self.mbt.get_tile(xyz.z, xyz.x as u32, xyz.y as u32).await? {
94                let mut response = TileResponse::new();
95                if self.mbt.format_info.format == TileFormat::Mvt {
96                    response.set_content_type("application/x-protobuf");
97                }
98                if let Some(encoding) = self.mbt.format_info.encoding.content_encoding() {
99                    response.insert_header(("Content-Encoding", encoding));
100                }
101                let body = Box::new(Cursor::new(content));
102                Some(response.with_body(body))
103            } else {
104                None
105            };
106        Ok(resp)
107    }
108}