1use std::fmt::Debug;
2
3use geo_traits::CoordTrait;
4use num_traits::{Bounded, Num, NumCast};
5
6use crate::kdtree::constants::KDBUSH_MAGIC;
7use crate::GeoIndexError;
8
9pub trait IndexableNum:
16 private::Sealed + Num + NumCast + PartialOrd + Debug + Send + Sync + bytemuck::Pod + Bounded
17{
18 const TYPE_INDEX: u8;
20 const BYTES_PER_ELEMENT: usize;
22
23 fn to_f64(self) -> Option<f64> {
25 NumCast::from(self)
26 }
27
28 fn from_f64(value: f64) -> Option<Self> {
30 NumCast::from(value)
31 }
32
33 fn sqrt(self) -> Option<Self> {
35 self.to_f64()
36 .and_then(|value| {
37 if value >= 0.0 {
38 Some(value.sqrt())
39 } else {
40 None
41 }
42 })
43 .and_then(NumCast::from)
44 }
45}
46
47impl IndexableNum for i8 {
48 const TYPE_INDEX: u8 = 0;
49 const BYTES_PER_ELEMENT: usize = 1;
50}
51
52impl IndexableNum for u8 {
53 const TYPE_INDEX: u8 = 1;
54 const BYTES_PER_ELEMENT: usize = 1;
55}
56
57impl IndexableNum for i16 {
58 const TYPE_INDEX: u8 = 3;
59 const BYTES_PER_ELEMENT: usize = 2;
60}
61
62impl IndexableNum for u16 {
63 const TYPE_INDEX: u8 = 4;
64 const BYTES_PER_ELEMENT: usize = 2;
65}
66
67impl IndexableNum for i32 {
68 const TYPE_INDEX: u8 = 5;
69 const BYTES_PER_ELEMENT: usize = 4;
70}
71
72impl IndexableNum for u32 {
73 const TYPE_INDEX: u8 = 6;
74 const BYTES_PER_ELEMENT: usize = 4;
75}
76
77impl IndexableNum for f32 {
78 const TYPE_INDEX: u8 = 7;
79 const BYTES_PER_ELEMENT: usize = 4;
80}
81
82impl IndexableNum for f64 {
83 const TYPE_INDEX: u8 = 8;
84 const BYTES_PER_ELEMENT: usize = 8;
85}
86
87const U8_CLAMPED_TYPE_INDEX: u8 = 2;
89
90#[allow(missing_docs)]
94#[derive(Debug, Clone, Copy, PartialEq, Hash)]
95pub enum CoordType {
96 Int8,
97 UInt8,
98 Int16,
99 UInt16,
100 Int32,
101 UInt32,
102 Float32,
103 Float64,
104}
105
106impl CoordType {
107 pub fn from_buffer<T: AsRef<[u8]>>(data: &T) -> Result<Self, GeoIndexError> {
128 let data = data.as_ref();
129 let magic = data[0];
130 if magic != 0xfb && magic != KDBUSH_MAGIC {
131 return Err(GeoIndexError::General(
132 "Data not in Flatbush or Kdbush format.".to_string(),
133 ));
134 }
135
136 let version_and_type = data[1];
137 let type_ = version_and_type & 0x0f;
138 let result = match type_ {
139 i8::TYPE_INDEX => CoordType::Int8,
140 u8::TYPE_INDEX => CoordType::UInt8,
141 U8_CLAMPED_TYPE_INDEX => CoordType::UInt8,
142 i16::TYPE_INDEX => CoordType::Int16,
143 u16::TYPE_INDEX => CoordType::UInt16,
144 i32::TYPE_INDEX => CoordType::Int32,
145 u32::TYPE_INDEX => CoordType::UInt32,
146 f32::TYPE_INDEX => CoordType::Float32,
147 f64::TYPE_INDEX => CoordType::Float64,
148 t => return Err(GeoIndexError::General(format!("Unexpected type {}.", t))),
149 };
150 Ok(result)
151 }
152}
153
154mod private {
156 pub trait Sealed {}
157
158 impl Sealed for i8 {}
159 impl Sealed for u8 {}
160 impl Sealed for i16 {}
161 impl Sealed for u16 {}
162 impl Sealed for i32 {}
163 impl Sealed for u32 {}
164 impl Sealed for f32 {}
165 impl Sealed for f64 {}
166}
167
168pub struct Coord<N: IndexableNum> {
172 pub(crate) x: N,
173 pub(crate) y: N,
174}
175
176impl<N: IndexableNum> CoordTrait for Coord<N> {
177 type T = N;
178
179 fn dim(&self) -> geo_traits::Dimensions {
180 geo_traits::Dimensions::Xy
181 }
182
183 fn x(&self) -> Self::T {
184 self.x
185 }
186
187 fn y(&self) -> Self::T {
188 self.y
189 }
190
191 fn nth_or_panic(&self, n: usize) -> Self::T {
192 match n {
193 0 => self.x,
194 1 => self.y,
195 _ => panic!("Invalid index of coord"),
196 }
197 }
198}