bbox_tile_server/store/
mbtiles.rs1use 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 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}