use std::fmt::Debug;
use geo_traits::CoordTrait;
use num_traits::{Bounded, Num, NumCast};
use crate::kdtree::constants::KDBUSH_MAGIC;
use crate::GeoIndexError;
pub trait IndexableNum:
private::Sealed + Num + NumCast + PartialOrd + Debug + Send + Sync + bytemuck::Pod + Bounded
{
const TYPE_INDEX: u8;
const BYTES_PER_ELEMENT: usize;
fn to_f64(self) -> Option<f64> {
NumCast::from(self)
}
fn from_f64(value: f64) -> Option<Self> {
NumCast::from(value)
}
fn sqrt(self) -> Option<Self> {
self.to_f64()
.and_then(|value| {
if value >= 0.0 {
Some(value.sqrt())
} else {
None
}
})
.and_then(NumCast::from)
}
}
impl IndexableNum for i8 {
const TYPE_INDEX: u8 = 0;
const BYTES_PER_ELEMENT: usize = 1;
}
impl IndexableNum for u8 {
const TYPE_INDEX: u8 = 1;
const BYTES_PER_ELEMENT: usize = 1;
}
impl IndexableNum for i16 {
const TYPE_INDEX: u8 = 3;
const BYTES_PER_ELEMENT: usize = 2;
}
impl IndexableNum for u16 {
const TYPE_INDEX: u8 = 4;
const BYTES_PER_ELEMENT: usize = 2;
}
impl IndexableNum for i32 {
const TYPE_INDEX: u8 = 5;
const BYTES_PER_ELEMENT: usize = 4;
}
impl IndexableNum for u32 {
const TYPE_INDEX: u8 = 6;
const BYTES_PER_ELEMENT: usize = 4;
}
impl IndexableNum for f32 {
const TYPE_INDEX: u8 = 7;
const BYTES_PER_ELEMENT: usize = 4;
}
impl IndexableNum for f64 {
const TYPE_INDEX: u8 = 8;
const BYTES_PER_ELEMENT: usize = 8;
}
const U8_CLAMPED_TYPE_INDEX: u8 = 2;
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub enum CoordType {
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Float32,
Float64,
}
impl CoordType {
pub fn from_buffer<T: AsRef<[u8]>>(data: &T) -> Result<Self, GeoIndexError> {
let data = data.as_ref();
let magic = data[0];
if magic != 0xfb && magic != KDBUSH_MAGIC {
return Err(GeoIndexError::General(
"Data not in Flatbush or Kdbush format.".to_string(),
));
}
let version_and_type = data[1];
let type_ = version_and_type & 0x0f;
let result = match type_ {
i8::TYPE_INDEX => CoordType::Int8,
u8::TYPE_INDEX => CoordType::UInt8,
U8_CLAMPED_TYPE_INDEX => CoordType::UInt8,
i16::TYPE_INDEX => CoordType::Int16,
u16::TYPE_INDEX => CoordType::UInt16,
i32::TYPE_INDEX => CoordType::Int32,
u32::TYPE_INDEX => CoordType::UInt32,
f32::TYPE_INDEX => CoordType::Float32,
f64::TYPE_INDEX => CoordType::Float64,
t => return Err(GeoIndexError::General(format!("Unexpected type {}.", t))),
};
Ok(result)
}
}
mod private {
pub trait Sealed {}
impl Sealed for i8 {}
impl Sealed for u8 {}
impl Sealed for i16 {}
impl Sealed for u16 {}
impl Sealed for i32 {}
impl Sealed for u32 {}
impl Sealed for f32 {}
impl Sealed for f64 {}
}
pub struct Coord<N: IndexableNum> {
pub(crate) x: N,
pub(crate) y: N,
}
impl<N: IndexableNum> CoordTrait for Coord<N> {
type T = N;
fn dim(&self) -> geo_traits::Dimensions {
geo_traits::Dimensions::Xy
}
fn x(&self) -> Self::T {
self.x
}
fn y(&self) -> Self::T {
self.y
}
fn nth_or_panic(&self, n: usize) -> Self::T {
match n {
0 => self.x,
1 => self.y,
_ => panic!("Invalid index of coord"),
}
}
}