1use std::ffi::c_void;
2use std::marker::PhantomData;
3
4use geos_sys::*;
5
6use crate::context_handle::{with_context, PtrWrap};
7use crate::Geom;
8use crate::{AsRaw, AsRawMut, GResult};
9
10pub trait SpatialIndex<I> {
11 fn insert<G: Geom>(&mut self, geometry: &G, item: I);
12
13 fn query<G: Geom, V: FnMut(&I)>(&self, geometry: &G, visitor: V);
14}
15
16pub struct STRtree<I> {
17 pub(crate) ptr: PtrWrap<*mut GEOSSTRtree>,
18 item_type: PhantomData<I>,
19}
20
21impl<I> STRtree<I> {
22 pub fn with_capacity(node_capacity: usize) -> GResult<STRtree<I>> {
23 with_context(|ctx| unsafe {
24 let ptr = GEOSSTRtree_create_r(ctx.as_raw(), node_capacity);
25 Ok(STRtree {
26 ptr: PtrWrap(ptr),
27 item_type: PhantomData,
28 })
29 })
30 }
31
32 pub fn iterate<V>(&self, visitor: V)
33 where
34 V: FnMut(&I),
35 {
36 with_context(|ctx| unsafe {
37 let (closure, callback) = unpack_closure(&visitor);
38 GEOSSTRtree_iterate_r(ctx.as_raw(), *self.ptr, Some(callback), closure);
39 })
40 }
41}
42
43impl<I> SpatialIndex<I> for STRtree<I> {
44 fn insert<G: Geom>(&mut self, geometry: &G, item: I) {
45 with_context(|ctx| unsafe {
46 GEOSSTRtree_insert_r(
47 ctx.as_raw(),
48 *self.ptr,
49 geometry.as_raw(),
50 Box::into_raw(Box::new(item)) as *mut c_void,
51 );
52 })
53 }
54
55 fn query<'b, G: Geom, V: FnMut(&I)>(&self, geometry: &G, visitor: V) {
56 with_context(|ctx| unsafe {
57 let (closure, callback) = unpack_closure(&visitor);
58 GEOSSTRtree_query_r(
59 ctx.as_raw(),
60 *self.ptr,
61 geometry.as_raw(),
62 Some(callback),
63 closure,
64 );
65 })
66 }
67}
68
69impl<I> AsRaw for STRtree<I> {
70 type RawType = GEOSSTRtree;
71
72 fn as_raw(&self) -> *const Self::RawType {
73 *self.ptr
74 }
75}
76
77impl<I> AsRawMut for STRtree<I> {
78 type RawType = GEOSSTRtree;
79
80 unsafe fn as_raw_mut_override(&self) -> *mut Self::RawType {
81 *self.ptr
82 }
83}
84
85impl<I> Drop for STRtree<I> {
86 fn drop(&mut self) {
87 unsafe extern "C" fn callback<I>(item: *mut c_void, _data: *mut c_void) {
88 drop(Box::from_raw(item as *mut I));
89 }
90
91 with_context(|ctx| unsafe {
92 GEOSSTRtree_iterate_r(
93 ctx.as_raw(),
94 *self.ptr,
95 Some(callback::<I>),
96 std::ptr::null_mut(),
97 );
98 GEOSSTRtree_destroy_r(ctx.as_raw(), *self.ptr);
99 })
100 }
101}
102
103unsafe fn unpack_closure<F, I>(
104 closure: &F,
105) -> (*mut c_void, extern "C" fn(*mut c_void, *mut c_void))
106where
107 F: FnMut(&I),
108{
109 extern "C" fn trampoline<F, I>(item: *mut c_void, data: *mut c_void)
110 where
111 F: FnMut(&I),
112 {
113 unsafe {
114 let closure: &mut F = &mut *(data as *mut F);
115 (*closure)(&mut *(item as *mut I));
116 }
117 }
118
119 (closure as *const F as *mut c_void, trampoline::<F, I>)
120}
121
122#[cfg(test)]
123mod test {
124 use std::collections::HashSet;
125
126 use crate::{Geometry, STRtree, SpatialIndex};
127
128 #[test]
129 fn test_strtree() {
130 let mut tree = STRtree::<&str>::with_capacity(10).unwrap();
131
132 let point = Geometry::new_from_wkt("POINT(5 5)").unwrap();
133 let line = Geometry::new_from_wkt("LINESTRING (0 0, 10 0)").unwrap();
134 let polygon = Geometry::new_from_wkt("POLYGON((2 2, 8 2, 8 8, 2 8, 2 2))").unwrap();
135
136 tree.insert(&point, "Point");
137 tree.insert(&line, "Line");
138 tree.insert(&polygon, "Polygon");
139
140 let mut items = HashSet::<&str>::new();
143 tree.iterate(|item| {
144 items.insert(*item);
145 });
146
147 assert_eq!(
148 items,
149 vec!["Line", "Point", "Polygon"].into_iter().collect()
150 );
151
152 items.clear();
155 tree.query(&point, |item| {
156 items.insert(*item);
157 });
158
159 assert_eq!(items, vec!["Point", "Polygon"].into_iter().collect());
160 }
161}