1use crate::protocol::{CurrentLocation, LocationUpdate, Stats};
2use spatio::Spatio;
3use spatio_types::geo::{DistanceMetric, Point, Polygon};
4use spatio_types::point::Point3d;
5use std::sync::Arc;
6
7#[derive(Clone)]
8pub struct Reader {
9 db: Arc<Spatio>,
10}
11
12impl Reader {
13 pub fn new(db: Arc<Spatio>) -> Self {
14 Self { db }
15 }
16
17 pub fn get(&self, namespace: &str, id: &str) -> Result<Option<CurrentLocation>, String> {
18 match self.db.get(namespace, id) {
19 Ok(Some(loc)) => Ok(Some(CurrentLocation {
20 object_id: loc.object_id.clone(),
21 position: loc.position.clone(),
22 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
23 })),
24 Ok(None) => Ok(None),
25 Err(e) => Err(e.to_string()),
26 }
27 .map_err(|e| format!("Internal error: {}", e))
28 }
29
30 pub fn query_radius(
31 &self,
32 namespace: &str,
33 center: &Point3d,
34 radius: f64,
35 limit: usize,
36 ) -> Result<Vec<(CurrentLocation, f64)>, String> {
37 self.db
38 .query_radius(namespace, center, radius, limit)
39 .map(|results| {
40 results
41 .into_iter()
42 .map(|(loc, dist)| {
43 (
44 CurrentLocation {
45 object_id: loc.object_id.clone(),
46 position: loc.position.clone(),
47 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
48 },
49 dist,
50 )
51 })
52 .collect()
53 })
54 .map_err(|e| format!("Internal error: {}", e))
55 }
56
57 pub fn knn(
58 &self,
59 namespace: &str,
60 center: &Point3d,
61 k: usize,
62 ) -> Result<Vec<(CurrentLocation, f64)>, String> {
63 self.db
64 .knn(namespace, center, k)
65 .map(|results| {
66 results
67 .into_iter()
68 .map(|(loc, dist)| {
69 (
70 CurrentLocation {
71 object_id: loc.object_id.clone(),
72 position: loc.position.clone(),
73 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
74 },
75 dist,
76 )
77 })
78 .collect()
79 })
80 .map_err(|e| format!("Internal error: {}", e))
81 }
82
83 pub fn stats(&self) -> Stats {
84 let s = self.db.stats();
85 Stats {
86 object_count: s.hot_state_objects,
87 memory_usage_bytes: s.memory_usage_bytes,
88 }
89 }
90
91 pub fn query_bbox(
92 &self,
93 namespace: &str,
94 min_x: f64,
95 min_y: f64,
96 max_x: f64,
97 max_y: f64,
98 limit: usize,
99 ) -> Result<Vec<CurrentLocation>, String> {
100 self.db
101 .query_bbox(namespace, min_x, min_y, max_x, max_y, limit)
102 .map(|results| {
103 results
104 .into_iter()
105 .map(|loc| CurrentLocation {
106 object_id: loc.object_id.clone(),
107 position: loc.position.clone(),
108 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
109 })
110 .collect()
111 })
112 .map_err(|e| format!("Internal error: {}", e))
113 }
114
115 pub fn query_cylinder(
116 &self,
117 namespace: &str,
118 center: Point,
119 min_z: f64,
120 max_z: f64,
121 radius: f64,
122 limit: usize,
123 ) -> Result<Vec<(CurrentLocation, f64)>, String> {
124 self.db
125 .query_within_cylinder(namespace, center, min_z, max_z, radius, limit)
126 .map(|results| {
127 results
128 .into_iter()
129 .map(|(loc, dist)| {
130 (
131 CurrentLocation {
132 object_id: loc.object_id.clone(),
133 position: loc.position.clone(),
134 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
135 },
136 dist,
137 )
138 })
139 .collect()
140 })
141 .map_err(|e| format!("Internal error: {}", e))
142 }
143
144 pub fn query_trajectory(
145 &self,
146 namespace: &str,
147 id: &str,
148 start_time: Option<f64>,
149 end_time: Option<f64>,
150 limit: usize,
151 ) -> Result<Vec<LocationUpdate>, String> {
152 let start = start_time
153 .map(|t| std::time::UNIX_EPOCH + std::time::Duration::from_secs_f64(t))
154 .unwrap_or(std::time::UNIX_EPOCH);
155 let end = end_time
156 .map(|t| std::time::UNIX_EPOCH + std::time::Duration::from_secs_f64(t))
157 .unwrap_or_else(std::time::SystemTime::now);
158
159 self.db
160 .query_trajectory(namespace, id, start, end, limit)
161 .map(|results| {
162 results
163 .into_iter()
164 .map(|upd| {
165 let timestamp = upd
166 .timestamp
167 .duration_since(std::time::UNIX_EPOCH)
168 .unwrap_or_default()
169 .as_secs_f64();
170
171 LocationUpdate {
172 timestamp,
173 position: upd.position,
174 metadata: serde_json::to_vec(&upd.metadata).unwrap_or_default(),
175 }
176 })
177 .collect()
178 })
179 .map_err(|e| e.to_string())
180 }
181
182 #[allow(clippy::too_many_arguments)]
183 pub fn query_bbox_3d(
184 &self,
185 namespace: &str,
186 min_x: f64,
187 min_y: f64,
188 min_z: f64,
189 max_x: f64,
190 max_y: f64,
191 max_z: f64,
192 limit: usize,
193 ) -> Result<Vec<CurrentLocation>, String> {
194 self.db
195 .query_within_bbox_3d(namespace, min_x, min_y, min_z, max_x, max_y, max_z, limit)
196 .map(|results| {
197 results
198 .into_iter()
199 .map(|loc| CurrentLocation {
200 object_id: loc.object_id.clone(),
201 position: loc.position.clone(),
202 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
203 })
204 .collect()
205 })
206 .map_err(|e| format!("Internal error: {}", e))
207 }
208
209 pub fn query_near(
210 &self,
211 namespace: &str,
212 id: &str,
213 radius: f64,
214 limit: usize,
215 ) -> Result<Vec<(CurrentLocation, f64)>, String> {
216 self.db
217 .query_near(namespace, id, radius, limit)
218 .map(|results| {
219 results
220 .into_iter()
221 .map(|(loc, dist)| {
222 (
223 CurrentLocation {
224 object_id: loc.object_id.clone(),
225 position: loc.position.clone(),
226 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
227 },
228 dist,
229 )
230 })
231 .collect()
232 })
233 .map_err(|e| format!("Internal error: {}", e))
234 }
235
236 pub fn contains(
237 &self,
238 namespace: &str,
239 polygon: &Polygon,
240 limit: usize,
241 ) -> Result<Vec<CurrentLocation>, String> {
242 self.db
243 .query_polygon(namespace, polygon, limit)
244 .map(|results| {
245 results
246 .into_iter()
247 .map(|loc| CurrentLocation {
248 object_id: loc.object_id.clone(),
249 position: loc.position.clone(),
250 metadata: serde_json::to_vec(&loc.metadata).unwrap_or_default(),
251 })
252 .collect()
253 })
254 .map_err(|e| format!("Internal error: {}", e))
255 }
256
257 pub fn distance(
258 &self,
259 namespace: &str,
260 id1: &str,
261 id2: &str,
262 metric: Option<DistanceMetric>,
263 ) -> Result<Option<f64>, String> {
264 self.db
265 .distance_between(namespace, id1, id2, metric.unwrap_or_default())
266 .map_err(|e| format!("Internal error: {}", e))
267 }
268
269 pub fn distance_to(
270 &self,
271 namespace: &str,
272 id: &str,
273 point: &Point,
274 metric: Option<DistanceMetric>,
275 ) -> Result<Option<f64>, String> {
276 self.db
277 .distance_to(namespace, id, point, metric.unwrap_or_default())
278 .map_err(|e| format!("Internal error: {}", e))
279 }
280
281 pub fn convex_hull(&self, namespace: &str) -> Result<Option<Polygon>, String> {
282 self.db
283 .convex_hull(namespace)
284 .map_err(|e| format!("Internal error: {}", e))
285 }
286
287 pub fn bounding_box(
288 &self,
289 namespace: &str,
290 ) -> Result<Option<spatio_types::bbox::BoundingBox2D>, String> {
291 self.db
292 .bounding_box(namespace)
293 .map(|opt| opt.map(spatio_types::bbox::BoundingBox2D::from_rect))
294 .map_err(|e| format!("Internal error: {}", e))
295 }
296}