vms2_tile_db_reader/sources/
mod.rs

1use crate::data_type::DataType;
2use crate::error::Error;
3
4use byteorder::{LittleEndian, WriteBytesExt};
5use rusqlite::types::ValueRef;
6use rusqlite::{params, Connection, OpenFlags, Result, ToSql};
7use std::io::{Cursor, Write};
8use std::path::Path;
9
10pub trait Source {
11    fn get_raw_data(
12        &self,
13        x: u32,
14        y: u32,
15        z: u8,
16        key: String,
17        value: Option<String>,
18        data_type: Option<DataType>,
19    ) -> Result<Vec<u8>, Error>;
20}
21
22pub struct SQLite {
23    pub conn: Connection,
24}
25
26impl SQLite {
27    pub fn new(db_path: &Path) -> Result<Self, Error> {
28        let conn = Connection::open_with_flags(db_path, OpenFlags::SQLITE_OPEN_READ_ONLY).map_err(
29            |e| Error::DbError {
30                message: format!("Failed to open {:?}", db_path),
31                source: Some(Box::new(e)),
32            },
33        )?;
34        Ok(Self { conn })
35    }
36
37    fn get_detail_zoom(z: u8, value: &String, data_type: &DataType) -> u8 {
38        let mut detail_zooms = vec![0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14];
39
40        match value.as_str() {
41            "terrain" | "depth" => {
42                detail_zooms = vec![0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 12];
43            }
44            "bathymetry" | "blue_marble" | "elevation" => {
45                detail_zooms = vec![0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 10, 10, 10];
46            }
47            _ => {}
48        }
49
50        let clamped_z = z.clamp(0, 14);
51        let mut detail_zoom = detail_zooms[clamped_z as usize];
52
53        if let DataType::Points = data_type {
54            detail_zoom = 14;
55        }
56
57        detail_zoom
58    }
59
60    fn query_data(
61        &self,
62        query: &str,
63        query_params: &[&dyn ToSql],
64    ) -> Result<Vec<(u32, u32, u8, Vec<u8>)>, Error> {
65        let mut stmt = self.conn.prepare(query).map_err(|e| Error::DbError {
66            message: format!("Failed to prepare statement for query: {}", query),
67            source: Some(Box::new(e)),
68        })?;
69        let mut rows = stmt.query(query_params).map_err(|e| Error::DbError {
70            message: format!("Failed to execute the prepared statement with params"),
71            source: Some(Box::new(e)),
72        })?;
73
74        let mut result = Vec::new();
75        while let Some(row) = rows.next().map_err(|e| Error::DbError {
76            message: format!("Failed to attempt to get the next row from the query"),
77            source: Some(Box::new(e)),
78        })? {
79            let result_data = row.get_ref(3);
80            let mut data = Vec::new();
81            if let Ok(unwrapped_result_data) = result_data {
82                data = match unwrapped_result_data {
83                    ValueRef::Text(text) => text.to_vec(),
84                    ValueRef::Blob(blob) => blob.to_vec(),
85                    _ => Vec::new(),
86                }
87            }
88
89            result.push((
90                row.get_unwrap(0),
91                row.get_unwrap(1),
92                row.get_unwrap(2),
93                data,
94            ));
95        }
96
97        Ok(result)
98    }
99}
100
101impl Source for SQLite {
102    /// Get raw data from the SQLite database.
103    ///
104    /// # Example
105    /// ```
106    /// use std::path::Path;
107    /// use vms2_tile_db_reader::data_type::DataType;
108    /// use vms2_tile_db_reader::sources::{SQLite, Source};
109    ///
110    /// let tile_db = SQLite::new(Path::new("./tests/data/braunschweig.sqlite")).unwrap();
111    /// let tile_data = tile_db
112    ///     .get_raw_data(
113    ///         34686,
114    ///         21566,
115    ///         16,
116    ///         String::from("building"),
117    ///         Some(String::from("*")),
118    ///         Some(DataType::Polygons),
119    ///     ).unwrap();
120    ///
121    /// assert!(tile_data.len() >= 4);
122    /// ```
123    fn get_raw_data(
124        &self,
125        x: u32,
126        y: u32,
127        z: u8,
128        mut key: String,
129        mut value: Option<String>,
130        mut data_type: Option<DataType>,
131    ) -> Result<Vec<u8>, Error> {
132        match key.as_str() {
133            "land" | "terrain" | "blue_marble" | "elevation" | "bathymetry" | "depth" => {
134                value = Some(key);
135                key = String::from("locr");
136                data_type = Some(DataType::Polygons);
137            }
138            _ => {}
139        }
140
141        let data_type = data_type.unwrap_or(DataType::Polygons);
142        let value = value.unwrap_or(String::new());
143
144        let detail_zoom = Self::get_detail_zoom(z, &value, &data_type);
145
146        let mut data_buffer = Cursor::new(Vec::new());
147        let data_type_int = data_type as u32;
148        let max_tile_zoom = 16_u8;
149        let mut number_of_tiles = 0_u32;
150        let mut tile_weight = 0_u64;
151
152        let single_tile_query = "SELECT x, y, z, data \
153            FROM tiles \
154            WHERE detail_zoom = ?1 \
155                AND object_type = ?2 \
156                AND osm_key = ?3 \
157                AND osm_value = ?4 \
158                AND x = ?5 \
159                AND y = ?6 \
160                AND z = ?7";
161        let multi_tile_query = "SELECT x, y, z, data \
162            FROM tiles \
163            WHERE detail_zoom = ?1 \
164                AND object_type = ?2 \
165                AND osm_key = ?3 \
166                AND osm_value = ?4 \
167                AND x >= ?5 \
168                AND x < ?6 \
169                AND y >= ?7 \
170                AND y < ?8 \
171                AND z = ?9";
172
173        for query_z in 0..=max_tile_zoom {
174            let queried_data: Vec<(u32, u32, u8, Vec<u8>)>;
175
176            if query_z <= z {
177                let query_x = x >> (z - query_z);
178                let query_y = y >> (z - query_z);
179
180                let query_params = params![
181                    detail_zoom,
182                    data_type_int,
183                    key,
184                    &value,
185                    query_x,
186                    query_y,
187                    query_z
188                ];
189                queried_data = self.query_data(single_tile_query, query_params)?;
190            } else {
191                let query_left_x = x << (query_z - z);
192                let query_top_y = y << (query_z - z);
193
194                let query_right_x = query_left_x + (1 << (query_z - z));
195                let query_bottom_y = query_top_y + (1 << (query_z - z));
196
197                let query_params = params![
198                    detail_zoom,
199                    data_type_int,
200                    key,
201                    &value,
202                    query_left_x,
203                    query_right_x,
204                    query_top_y,
205                    query_bottom_y,
206                    query_z
207                ];
208                queried_data = self.query_data(multi_tile_query, query_params)?;
209            }
210
211            if queried_data.len() > 0 {
212                number_of_tiles += queried_data.len() as u32;
213                tile_weight += 4_u64.pow((max_tile_zoom - query_z) as u32);
214
215                for (tile_x, tile_y, tile_z, tile_data) in queried_data {
216                    let _ = data_buffer.write_u32::<LittleEndian>(tile_x);
217                    let _ = data_buffer.write_u32::<LittleEndian>(tile_y);
218                    let _ = data_buffer.write_u32::<LittleEndian>(tile_z as u32);
219                    let _ = data_buffer.write_u32::<LittleEndian>(detail_zoom as u32);
220                    let _ = data_buffer.write_u32::<LittleEndian>(tile_data.len() as u32);
221
222                    if tile_data.len() > 0 {
223                        let _ = data_buffer.write_all(&tile_data);
224                    }
225                }
226            }
227
228            if tile_weight as f64 >= 4_f64.powi(max_tile_zoom as i32 - z as i32) {
229                break;
230            }
231        }
232
233        let mut buffer = Cursor::new(Vec::new());
234        let _ = buffer.write_u32::<LittleEndian>(number_of_tiles);
235        let _ = buffer.write_all(data_buffer.get_ref());
236
237        Ok(buffer.into_inner())
238    }
239}