vms2_tile_db_reader/sources/
mod.rs1use 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 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}