use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr::NonNull;
use geos_sys::*;
use crate::context_handle::with_context;
#[cfg(feature = "v3_12_0")]
use crate::functions::errcheck;
use crate::functions::nullcheck;
use crate::{AsRaw, AsRawMut, GResult, Geom};
pub trait SpatialIndex<I> {
fn insert<G: Geom>(&mut self, geometry: &G, item: I);
fn query<G: Geom, V: FnMut(&I)>(&self, geometry: &G, visitor: V);
#[cfg(feature = "v3_12_0")]
fn build(&self) -> GResult<()>;
}
pub struct STRtree<I> {
pub(crate) ptr: NonNull<GEOSSTRtree>,
item_type: PhantomData<I>,
}
impl<I> STRtree<I> {
pub fn with_capacity(node_capacity: usize) -> GResult<Self> {
with_context(|ctx| unsafe {
let ptr = nullcheck!(GEOSSTRtree_create_r(ctx.as_raw(), node_capacity))?;
Ok(Self {
ptr,
item_type: PhantomData,
})
})
}
pub fn iterate<V>(&mut self, visitor: V)
where
V: FnMut(&I),
{
with_context(|ctx| unsafe {
let (closure, callback) = unpack_closure(&visitor);
GEOSSTRtree_iterate_r(ctx.as_raw(), self.as_raw_mut(), Some(callback), closure);
});
}
}
impl<I> SpatialIndex<I> for STRtree<I> {
fn insert<G: Geom>(&mut self, geometry: &G, item: I) {
with_context(|ctx| unsafe {
GEOSSTRtree_insert_r(
ctx.as_raw(),
self.as_raw_mut(),
geometry.as_raw(),
Box::into_raw(Box::new(item)).cast(),
);
});
}
fn query<G: Geom, V: FnMut(&I)>(&self, geometry: &G, visitor: V) {
with_context(|ctx| unsafe {
let (closure, callback) = unpack_closure(&visitor);
GEOSSTRtree_query_r(
ctx.as_raw(),
self.as_raw_mut_override(),
geometry.as_raw(),
Some(callback),
closure,
);
});
}
#[cfg(feature = "v3_12_0")]
fn build(&self) -> GResult<()> {
with_context(|ctx| unsafe {
errcheck!(GEOSSTRtree_build_r(
ctx.as_raw(),
self.as_raw_mut_override(),
))?;
Ok(())
})
}
}
impl<I> AsRaw for STRtree<I> {
type RawType = GEOSSTRtree;
fn as_raw(&self) -> *const Self::RawType {
self.ptr.as_ptr()
}
}
impl<I> AsRawMut for STRtree<I> {
unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType {
self.ptr.as_ptr()
}
}
unsafe impl<I: Send> Send for STRtree<I> {}
unsafe impl<I: Sync> Sync for STRtree<I> {}
impl<I> Drop for STRtree<I> {
fn drop(&mut self) {
unsafe extern "C" fn callback<I>(item: *mut c_void, _data: *mut c_void) {
drop(Box::from_raw(item.cast::<I>()));
}
with_context(|ctx| unsafe {
GEOSSTRtree_iterate_r(
ctx.as_raw(),
self.as_raw_mut(),
Some(callback::<I>),
std::ptr::null_mut(),
);
GEOSSTRtree_destroy_r(ctx.as_raw(), self.as_raw_mut());
});
}
}
unsafe fn unpack_closure<F, I>(
closure: &F,
) -> (*mut c_void, extern "C" fn(*mut c_void, *mut c_void))
where
F: FnMut(&I),
{
extern "C" fn trampoline<F, I>(item: *mut c_void, data: *mut c_void)
where
F: FnMut(&I),
{
unsafe {
let closure: &mut F = &mut *data.cast();
(*closure)(&mut *item.cast());
}
}
(closure as *const F as *mut c_void, trampoline::<F, I>)
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use crate::{Geometry, STRtree, SpatialIndex};
#[test]
fn test_strtree() {
let mut tree = STRtree::<&str>::with_capacity(10).unwrap();
let point = Geometry::new_from_wkt("POINT(5 5)").unwrap();
let line = Geometry::new_from_wkt("LINESTRING (0 0, 10 0)").unwrap();
let polygon = Geometry::new_from_wkt("POLYGON((2 2, 8 2, 8 8, 2 8, 2 2))").unwrap();
tree.insert(&point, "Point");
tree.insert(&line, "Line");
tree.insert(&polygon, "Polygon");
let mut items = HashSet::<&str>::new();
tree.iterate(|item| {
items.insert(*item);
});
assert_eq!(
items,
vec!["Line", "Point", "Polygon"].into_iter().collect()
);
items.clear();
tree.query(&point, |item| {
items.insert(*item);
});
assert_eq!(items, vec!["Point", "Polygon"].into_iter().collect());
}
}