1#[derive(Debug, Clone)]
2pub struct SpatialElement {
3 pub h3_index: u64,
4 pub bounds: (f64, f64, f64, f64), pub t0: u64,
6 pub t1: u64,
7}
8
9#[derive(Debug, Clone)]
10enum QuadTreeNode {
11 Leaf { elements: Vec<SpatialElement> },
12 Internal { children: Box<[QuadTreeNode; 4]> },
13}
14
15#[derive(Debug, Clone)]
16pub struct SpatiotemporalQuadTree {
17 pub root_bounds: (f64, f64, f64, f64),
18 root: QuadTreeNode,
19 max_elements: usize,
20 max_depth: u32,
21}
22
23impl SpatiotemporalQuadTree {
24 pub fn new(bounds: (f64, f64, f64, f64)) -> Self {
25 Self {
26 root_bounds: bounds,
27 root: QuadTreeNode::Leaf {
28 elements: Vec::new(),
29 },
30 max_elements: 16,
31 max_depth: 8,
32 }
33 }
34
35 pub fn insert(&mut self, element: SpatialElement) {
36 Self::insert_into(
37 &mut self.root,
38 self.root_bounds,
39 element,
40 0,
41 self.max_elements,
42 self.max_depth,
43 );
44 }
45
46 fn insert_into(
47 node: &mut QuadTreeNode,
48 bounds: (f64, f64, f64, f64),
49 element: SpatialElement,
50 depth: u32,
51 max_elements: usize,
52 max_depth: u32,
53 ) {
54 match node {
55 QuadTreeNode::Leaf { elements } => {
56 elements.push(element);
57 if elements.len() > max_elements && depth < max_depth {
58 let (min_x, min_y, max_x, max_y) = bounds;
60 let mid_x = (min_x + max_x) / 2.0;
61 let mid_y = (min_y + max_y) / 2.0;
62
63 let mut top_left = Vec::new();
64 let mut top_right = Vec::new();
65 let mut bottom_left = Vec::new();
66 let mut bottom_right = Vec::new();
67
68 for el in elements.drain(..) {
69 let (el_min_x, el_min_y, el_max_x, el_max_y) = el.bounds;
70 let left = el_min_x <= mid_x;
72 let right = el_max_x >= mid_x;
73 let bottom = el_min_y <= mid_y;
74 let top = el_max_y >= mid_y;
75
76 if left && top {
77 top_left.push(el.clone());
78 }
79 if right && top {
80 top_right.push(el.clone());
81 }
82 if left && bottom {
83 bottom_left.push(el.clone());
84 }
85 if right && bottom {
86 bottom_right.push(el.clone());
87 }
88 }
89
90 *node = QuadTreeNode::Internal {
91 children: Box::new([
92 QuadTreeNode::Leaf { elements: top_left },
93 QuadTreeNode::Leaf {
94 elements: top_right,
95 },
96 QuadTreeNode::Leaf {
97 elements: bottom_left,
98 },
99 QuadTreeNode::Leaf {
100 elements: bottom_right,
101 },
102 ]),
103 };
104 }
105 }
106 QuadTreeNode::Internal { children } => {
107 let (min_x, min_y, max_x, max_y) = bounds;
108 let mid_x = (min_x + max_x) / 2.0;
109 let mid_y = (min_y + max_y) / 2.0;
110
111 let (el_min_x, el_min_y, el_max_x, el_max_y) = element.bounds;
112 let left = el_min_x <= mid_x;
113 let right = el_max_x >= mid_x;
114 let bottom = el_min_y <= mid_y;
115 let top = el_max_y >= mid_y;
116
117 if left && top {
118 Self::insert_into(
119 &mut children[0],
120 (min_x, mid_y, mid_x, max_y),
121 element.clone(),
122 depth + 1,
123 max_elements,
124 max_depth,
125 );
126 }
127 if right && top {
128 Self::insert_into(
129 &mut children[1],
130 (mid_x, mid_y, max_x, max_y),
131 element.clone(),
132 depth + 1,
133 max_elements,
134 max_depth,
135 );
136 }
137 if left && bottom {
138 Self::insert_into(
139 &mut children[2],
140 (min_x, min_y, mid_x, mid_y),
141 element.clone(),
142 depth + 1,
143 max_elements,
144 max_depth,
145 );
146 }
147 if right && bottom {
148 Self::insert_into(
149 &mut children[3],
150 (mid_x, min_y, max_x, mid_y),
151 element.clone(),
152 depth + 1,
153 max_elements,
154 max_depth,
155 );
156 }
157 }
158 }
159 }
160
161 pub fn query_region(&self, x1: f64, y1: f64, x2: f64, y2: f64, t0: u64, t1: u64) -> Vec<u64> {
164 let mut results = Vec::new();
165 Self::query_node(
166 &self.root,
167 self.root_bounds,
168 (x1, y1, x2, y2),
169 t0,
170 t1,
171 &mut results,
172 );
173
174 results.sort_unstable();
176 results.dedup();
177 results
178 }
179
180 fn query_node(
181 node: &QuadTreeNode,
182 bounds: (f64, f64, f64, f64),
183 query_bounds: (f64, f64, f64, f64),
184 t0: u64,
185 t1: u64,
186 results: &mut Vec<u64>,
187 ) {
188 let (qx1, qy1, qx2, qy2) = query_bounds;
189 let (bx1, by1, bx2, by2) = bounds;
190
191 if qx1 > bx2 || qx2 < bx1 || qy1 > by2 || qy2 < by1 {
193 return;
194 }
195
196 match node {
197 QuadTreeNode::Leaf { elements } => {
198 for el in elements {
199 let overlap_x = el.bounds.0 <= qx2 && el.bounds.2 >= qx1;
200 let overlap_y = el.bounds.1 <= qy2 && el.bounds.3 >= qy1;
201 let overlap_t = el.t0 <= t1 && el.t1 >= t0;
202
203 if overlap_x && overlap_y && overlap_t {
204 results.push(el.h3_index);
205 }
206 }
207 }
208 QuadTreeNode::Internal { children } => {
209 let mid_x = (bx1 + bx2) / 2.0;
210 let mid_y = (by1 + by2) / 2.0;
211
212 Self::query_node(
213 &children[0],
214 (bx1, mid_y, mid_x, by2),
215 query_bounds,
216 t0,
217 t1,
218 results,
219 );
220 Self::query_node(
221 &children[1],
222 (mid_x, mid_y, bx2, by2),
223 query_bounds,
224 t0,
225 t1,
226 results,
227 );
228 Self::query_node(
229 &children[2],
230 (bx1, by1, mid_x, mid_y),
231 query_bounds,
232 t0,
233 t1,
234 results,
235 );
236 Self::query_node(
237 &children[3],
238 (mid_x, by1, bx2, mid_y),
239 query_bounds,
240 t0,
241 t1,
242 results,
243 );
244 }
245 }
246 }
247}
248
249pub fn embed_h3_context(index: u64, resolution: u8, base_cell: u8) -> u64 {
252 let res_bits = ((resolution & 0x0F) as u64) << 59;
254 let base_bits = ((base_cell & 0x7F) as u64) << 52;
255 let index_bits = index & 0x000F_FFFF_FFFF_FFFF;
256 res_bits | base_bits | index_bits
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_query_region() {
265 let mut qt = SpatiotemporalQuadTree::new((-180.0, -90.0, 180.0, 90.0));
266
267 qt.insert(SpatialElement {
268 h3_index: 101,
269 bounds: (10.0, 10.0, 20.0, 20.0),
270 t0: 1000,
271 t1: 2000,
272 });
273
274 qt.insert(SpatialElement {
275 h3_index: 202,
276 bounds: (50.0, 50.0, 60.0, 60.0),
277 t0: 1500,
278 t1: 2500,
279 });
280
281 let res1 = qt.query_region(15.0, 15.0, 25.0, 25.0, 1200, 1800);
283 assert_eq!(res1, vec![101]);
284
285 let res2 = qt.query_region(15.0, 15.0, 25.0, 25.0, 3000, 4000);
287 assert!(res2.is_empty());
288
289 let res3 = qt.query_region(40.0, 40.0, 55.0, 55.0, 1500, 2000);
291 assert_eq!(res3, vec![202]);
292 }
293
294 #[test]
295 fn test_quadtree_split() {
296 let mut qt = SpatiotemporalQuadTree::new((0.0, 0.0, 100.0, 100.0));
297 qt.max_elements = 2; for i in 0..5 {
300 qt.insert(SpatialElement {
301 h3_index: i,
302 bounds: (
303 10.0 + (i as f64),
304 10.0 + (i as f64),
305 15.0 + (i as f64),
306 15.0 + (i as f64),
307 ),
308 t0: 100,
309 t1: 200,
310 });
311 }
312
313 let res = qt.query_region(0.0, 0.0, 50.0, 50.0, 50, 250);
314 assert_eq!(res.len(), 5);
315 }
316}