hprtree/
lib.rs

1mod hprtree;
2pub use crate::hprtree::*;
3mod hprtree_wrapping;
4pub use crate::hprtree_wrapping::*;
5
6/// A simple stuct representing a bounding box / envelope, intended for lat/lon coordinates with lat=y, lon=x
7///
8/// Used for querying the index and for the internal data structure of the HPRTree
9#[derive(Clone, Debug)]
10pub struct BBox {
11    pub minx: CoordinateType,
12    pub miny: CoordinateType,
13    pub maxx: CoordinateType,
14    pub maxy: CoordinateType,
15}
16
17impl Default for BBox {
18    /// The default of the bbox is min = f32::MAX and max = f32::MIN
19    ///
20    /// This is so that "expanding to include"ing such a bbox results in whatever was used to expand the bbox by
21    fn default() -> Self {
22        Self {
23            minx: CoordinateType::MAX,
24            miny: CoordinateType::MAX,
25            maxx: CoordinateType::MIN,
26            maxy: CoordinateType::MIN,
27        }
28    }
29}
30
31impl BBox {
32    pub fn new(
33        minx: CoordinateType,
34        miny: CoordinateType,
35        maxx: CoordinateType,
36        maxy: CoordinateType,
37    ) -> Self {
38        Self {
39            minx,
40            miny,
41            maxx,
42            maxy,
43        }
44    }
45
46    /// Returns the width of the bbox
47    pub fn width(&self) -> CoordinateType {
48        self.maxx - self.minx
49    }
50
51    /// Returns the height of the bbox
52    pub fn height(&self) -> CoordinateType {
53        self.maxy - self.miny
54    }
55
56    /// Expands the bbox to include another bbox
57    pub fn expand_to_include(&mut self, other: &Self) {
58        self.minx = self.minx.min(other.minx);
59        self.miny = self.miny.min(other.miny);
60        self.maxx = self.maxx.max(other.maxx);
61        self.maxy = self.maxy.max(other.maxy);
62    }
63
64    /// Expands the bbox to include a point
65    pub fn expand_to_include_point(&mut self, point: &Point) {
66        self.minx = self.minx.min(point.x);
67        self.miny = self.miny.min(point.y);
68        self.maxx = self.maxx.max(point.x);
69        self.maxy = self.maxy.max(point.y);
70    }
71
72    /// Expands the bbox to include a point
73    pub fn expand_to_include_spatially_indexable(&mut self, point: &impl SpatiallyIndexable) {
74        self.minx = self.minx.min(point.x());
75        self.miny = self.miny.min(point.y());
76        self.maxx = self.maxx.max(point.x());
77        self.maxy = self.maxy.max(point.y());
78    }
79
80    /// Checks if a given point is contained within the bounds of the bbox
81    pub fn contains(&self, other: &Point) -> bool {
82        !(other.x > self.maxx || other.x < self.minx || other.y > self.maxy || other.y < self.miny)
83    }
84
85    /// Checks if a given point is contained within the bounds of the bbox
86    pub fn contains_spatially_indexable(&self, other: &impl SpatiallyIndexable) -> bool {
87        !(other.x() > self.maxx
88            || other.x() < self.minx
89            || other.y() > self.maxy
90            || other.y() < self.miny)
91    }
92
93    /// Checks if a given bbox intersects the self bbox
94    pub fn intersects(&self, other: &Self) -> bool {
95        !(other.minx > self.maxx
96            || other.maxx < self.minx
97            || other.miny > self.maxy
98            || other.maxy < self.miny)
99    }
100}
101
102/// A simple point struct, intended for lat/lon coordinates with lat=y, lon=x
103#[derive(Clone, Debug)]
104pub struct Point {
105    pub x: CoordinateType,
106    pub y: CoordinateType,
107}
108
109impl SpatiallyIndexable for Point {
110    fn x(&self) -> CoordinateType {
111        self.x
112    }
113
114    fn y(&self) -> CoordinateType {
115        self.y
116    }
117}
118
119/// Trait that enables a struct to be spatially indexed
120pub trait SpatiallyIndexable {
121    fn x(&self) -> CoordinateType;
122    fn y(&self) -> CoordinateType;
123}
124
125const NODE_CAPACITY: usize = 16;
126const HILBERT_LEVEL: usize = 12;
127const H: usize = (1 << HILBERT_LEVEL) - 1;
128
129fn interleave(x: u32) -> u32 {
130    let x = (x | (x << 8)) & 0x00FF00FF;
131    let x = (x | (x << 4)) & 0x0F0F0F0F;
132    let x = (x | (x << 2)) & 0x33333333;
133    (x | (x << 1)) & 0x55555555
134}
135
136#[allow(non_snake_case)]
137fn hilbert_xy_to_index(x: u32, y: u32) -> u32 {
138    let x = x << (16 - HILBERT_LEVEL);
139    let y = y << (16 - HILBERT_LEVEL);
140
141    let mut A: u32;
142    let mut B: u32;
143    let mut C: u32;
144    let mut D: u32;
145
146    // Initial prefix scan round, prime with x and y
147    {
148        let a = x ^ y;
149        let b = 0xFFFF ^ a;
150        let c = 0xFFFF ^ (x | y);
151        let d = x & (y ^ 0xFFFF);
152
153        A = a | (b >> 1);
154        B = (a >> 1) ^ a;
155
156        C = ((c >> 1) ^ (b & (d >> 1))) ^ c;
157        D = ((a & (c >> 1)) ^ (d >> 1)) ^ d;
158    }
159
160    {
161        let a = A;
162        let b = B;
163        let c = C;
164        let d = D;
165
166        A = (a & (a >> 2)) ^ (b & (b >> 2));
167        B = (a & (b >> 2)) ^ (b & ((a ^ b) >> 2));
168
169        C ^= (a & (c >> 2)) ^ (b & (d >> 2));
170        D ^= (b & (c >> 2)) ^ ((a ^ b) & (d >> 2));
171    }
172
173    {
174        let a = A;
175        let b = B;
176        let c = C;
177        let d = D;
178
179        A = (a & (a >> 4)) ^ (b & (b >> 4));
180        B = (a & (b >> 4)) ^ (b & ((a ^ b) >> 4));
181
182        C ^= (a & (c >> 4)) ^ (b & (d >> 4));
183        D ^= (b & (c >> 4)) ^ ((a ^ b) & (d >> 4));
184    }
185
186    // Final round and projection
187    {
188        let a = A;
189        let b = B;
190        let c = C;
191        let d = D;
192
193        C ^= (a & (c >> 8)) ^ (b & (d >> 8));
194        D ^= (b & (c >> 8)) ^ ((a ^ b) & (d >> 8));
195    }
196
197    // Undo transformation prefix scan
198    let a = C ^ (C >> 1);
199    let b = D ^ (D >> 1);
200
201    // Recover index bits
202    let i0 = x ^ y;
203    let i1 = b | (0xFFFF ^ (i0 | a));
204
205    ((interleave(i1) << 1) | interleave(i0)) >> (32 - 2 * HILBERT_LEVEL)
206}
207
208fn get_layer_size(layer: usize, layer_start_index: &[usize]) -> usize {
209    layer_start_index[layer + 1] - layer_start_index[layer]
210}
211
212/// Internal type for coordinates
213pub type CoordinateType = f32;