1#![warn(missing_docs)]
53#![warn(clippy::all)]
54
55pub mod aggregate;
56pub mod analysis;
57pub mod clustering;
59pub mod crs;
60pub mod crs_transform;
61pub mod error;
62pub mod functions;
63pub mod geometry;
64pub mod index;
65pub mod performance;
66pub mod reasoning;
67pub mod sparql_integration;
68pub mod validation;
69pub mod vocabulary;
70
71pub mod geo_serializer;
73
74pub mod coordinate_transformer;
76
77pub mod raster_sampler;
79
80pub mod topology_checker;
82
83pub mod wkt_parser;
85
86pub mod spatial_index;
88
89pub mod wkt_writer;
91
92pub mod bounding_box;
94
95pub mod area_calculator;
97
98pub mod distance_calculator;
100
101pub mod intersection_detector;
104
105pub mod convex_hull;
107
108pub mod simplifier;
110
111pub mod coordinate_converter;
113
114pub use aggregate::{
116 aggregate_bounding_box, AggregateBoundingBox, BoundingBoxResult, GEO_AGG_BOUNDING_BOX,
117};
118pub use crs::crs_literal::{
119 encode_crs_wkt_literal, parse_crs_wkt_literal, CrsGeometryTransformer, CrsLiteral, CRS84_URI,
120 GEO_CRS, GEO_WKT_LITERAL,
121};
122pub use crs::osgb36::{coordinate_to_osgb_grid_ref, osgb_grid_ref_to_coordinate, OsgbCoordinate};
123pub use crs::utm::{utm_to_wgs84_batch, wgs84_to_utm_batch, UtmCoordinate, WgsCoordinate};
124pub use crs::{CrsKind, CrsTransformer, GeometryWithCrs};
125pub use error::{GeoSparqlError, Result};
126pub use functions::ogc11::{
127 area_with_unit, area_with_unit_uri, concave_hull, distance_with_unit, distance_with_unit_uri,
128 length_with_unit, length_with_unit_uri, UnitOfMeasure, UOM_PREFIX,
129};
130pub use geometry::geometry3d::{
131 BoundingBox3D, Geometry3DEnum, LineString3D, LinearRing3D, Point3D, Polygon3D,
132};
133pub use geometry::{Crs, Geometry};
134pub use index::SpatialIndex;
135pub use index::{PureRTree, RTreeBBox};
136pub use index::{RtreeEntry, SpatialRtreeIndex};
137pub use performance::BatchProcessor;
138
139pub struct GeoSparqlRegistry;
144
145impl GeoSparqlRegistry {
146 pub fn simple_features_functions() -> Vec<(&'static str, &'static str)> {
150 vec![
151 (vocabulary::GEO_SF_EQUALS, "sfEquals"),
152 (vocabulary::GEO_SF_DISJOINT, "sfDisjoint"),
153 (vocabulary::GEO_SF_INTERSECTS, "sfIntersects"),
154 (vocabulary::GEO_SF_TOUCHES, "sfTouches"),
155 (vocabulary::GEO_SF_CROSSES, "sfCrosses"),
156 (vocabulary::GEO_SF_WITHIN, "sfWithin"),
157 (vocabulary::GEO_SF_CONTAINS, "sfContains"),
158 (vocabulary::GEO_SF_OVERLAPS, "sfOverlaps"),
159 ]
160 }
161
162 #[cfg(feature = "geos-backend")]
166 pub fn egenhofer_functions() -> Vec<(&'static str, &'static str)> {
167 vec![
168 (vocabulary::GEO_EH_EQUALS, "ehEquals"),
169 (vocabulary::GEO_EH_DISJOINT, "ehDisjoint"),
170 (vocabulary::GEO_EH_MEET, "ehMeet"),
171 (vocabulary::GEO_EH_OVERLAP, "ehOverlap"),
172 (vocabulary::GEO_EH_COVERS, "ehCovers"),
173 (vocabulary::GEO_EH_COVERED_BY, "ehCoveredBy"),
174 (vocabulary::GEO_EH_INSIDE, "ehInside"),
175 (vocabulary::GEO_EH_CONTAINS, "ehContains"),
176 ]
177 }
178
179 #[cfg(feature = "geos-backend")]
183 pub fn rcc8_functions() -> Vec<(&'static str, &'static str)> {
184 vec![
185 (vocabulary::GEO_RCC8_EQ, "rcc8eq"),
186 (vocabulary::GEO_RCC8_DC, "rcc8dc"),
187 (vocabulary::GEO_RCC8_EC, "rcc8ec"),
188 (vocabulary::GEO_RCC8_PO, "rcc8po"),
189 (vocabulary::GEO_RCC8_TPPI, "rcc8tppi"),
190 (vocabulary::GEO_RCC8_TPP, "rcc8tpp"),
191 (vocabulary::GEO_RCC8_NTPP, "rcc8ntpp"),
192 (vocabulary::GEO_RCC8_NTPPI, "rcc8ntppi"),
193 ]
194 }
195
196 pub fn property_functions() -> Vec<(&'static str, &'static str)> {
200 vec![
201 (vocabulary::GEO_DIMENSION, "dimension"),
202 (vocabulary::GEO_COORDINATE_DIMENSION, "coordinateDimension"),
203 (vocabulary::GEO_SPATIAL_DIMENSION, "spatialDimension"),
204 (vocabulary::GEO_IS_EMPTY, "isEmpty"),
205 (vocabulary::GEO_IS_SIMPLE, "isSimple"),
206 (vocabulary::GEO_AS_WKT, "asWKT"),
207 ]
208 }
209
210 #[cfg(feature = "geos-backend")]
214 pub fn operation_functions() -> Vec<(&'static str, &'static str)> {
215 vec![
216 (vocabulary::GEO_BUFFER, "buffer"),
217 (vocabulary::GEO_CONVEX_HULL, "convexHull"),
218 (vocabulary::GEO_INTERSECTION, "intersection"),
219 (vocabulary::GEO_UNION, "union"),
220 (vocabulary::GEO_DIFFERENCE, "difference"),
221 (vocabulary::GEO_SYM_DIFFERENCE, "symDifference"),
222 (vocabulary::GEO_ENVELOPE, "envelope"),
223 (vocabulary::GEO_BOUNDARY, "boundary"),
224 ]
225 }
226
227 pub fn distance_functions() -> Vec<(&'static str, &'static str)> {
231 vec![(vocabulary::GEO_DISTANCE, "distance")]
232 }
233
234 pub fn all_filter_functions() -> Vec<(&'static str, &'static str)> {
238 #[cfg(not(feature = "geos-backend"))]
239 {
240 Self::simple_features_functions()
241 }
242
243 #[cfg(feature = "geos-backend")]
244 {
245 let mut functions = Self::simple_features_functions();
246 functions.extend(Self::egenhofer_functions());
247 functions.extend(Self::rcc8_functions());
248 functions
249 }
250 }
251
252 pub fn all_property_functions() -> Vec<(&'static str, &'static str)> {
256 let mut functions = Self::property_functions();
257 functions.extend(Self::distance_functions());
258
259 #[cfg(feature = "geos-backend")]
260 {
261 functions.extend(Self::operation_functions());
262 }
263
264 functions
265 }
266}
267
268type GeoSparqlFunctions = (
270 Vec<(&'static str, &'static str)>,
271 Vec<(&'static str, &'static str)>,
272);
273
274pub fn init() -> GeoSparqlFunctions {
285 tracing::info!("Initializing GeoSPARQL support");
286
287 let filter_functions = GeoSparqlRegistry::all_filter_functions();
288 let property_functions = GeoSparqlRegistry::all_property_functions();
289
290 tracing::info!(
291 "Registered {} GeoSPARQL filter functions",
292 filter_functions.len()
293 );
294 tracing::info!(
295 "Registered {} GeoSPARQL property functions",
296 property_functions.len()
297 );
298
299 (filter_functions, property_functions)
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_init() {
308 let (filter_functions, property_functions) = init();
309
310 #[cfg(feature = "geos-backend")]
312 {
313 assert_eq!(filter_functions.len(), 24); assert_eq!(property_functions.len(), 15); }
316 #[cfg(not(feature = "geos-backend"))]
317 {
318 assert_eq!(filter_functions.len(), 8); assert_eq!(property_functions.len(), 7); }
321 }
322
323 #[test]
324 fn test_simple_features_registry() {
325 let functions = GeoSparqlRegistry::simple_features_functions();
326
327 assert_eq!(functions.len(), 8);
328
329 assert!(functions.contains(&(vocabulary::GEO_SF_EQUALS, "sfEquals")));
331 assert!(functions.contains(&(vocabulary::GEO_SF_CONTAINS, "sfContains")));
332 assert!(functions.contains(&(vocabulary::GEO_SF_INTERSECTS, "sfIntersects")));
333 }
334
335 #[test]
336 #[cfg(feature = "geos-backend")]
337 fn test_egenhofer_registry() {
338 let functions = GeoSparqlRegistry::egenhofer_functions();
339
340 assert_eq!(functions.len(), 8);
341
342 assert!(functions.contains(&(vocabulary::GEO_EH_EQUALS, "ehEquals")));
344 assert!(functions.contains(&(vocabulary::GEO_EH_MEET, "ehMeet")));
345 assert!(functions.contains(&(vocabulary::GEO_EH_CONTAINS, "ehContains")));
346 }
347
348 #[test]
349 #[cfg(feature = "geos-backend")]
350 fn test_rcc8_registry() {
351 let functions = GeoSparqlRegistry::rcc8_functions();
352
353 assert_eq!(functions.len(), 8);
354
355 assert!(functions.contains(&(vocabulary::GEO_RCC8_EQ, "rcc8eq")));
357 assert!(functions.contains(&(vocabulary::GEO_RCC8_DC, "rcc8dc")));
358 assert!(functions.contains(&(vocabulary::GEO_RCC8_PO, "rcc8po")));
359 }
360
361 #[test]
362 fn test_property_functions_registry() {
363 let functions = GeoSparqlRegistry::property_functions();
364
365 assert_eq!(functions.len(), 6);
366
367 assert!(functions.contains(&(vocabulary::GEO_DIMENSION, "dimension")));
369 assert!(functions.contains(&(vocabulary::GEO_IS_EMPTY, "isEmpty")));
370 assert!(functions.contains(&(vocabulary::GEO_IS_SIMPLE, "isSimple")));
371 }
372
373 #[test]
374 #[cfg(feature = "geos-backend")]
375 fn test_operation_functions_registry() {
376 let functions = GeoSparqlRegistry::operation_functions();
377
378 assert_eq!(functions.len(), 8);
379
380 assert!(functions.contains(&(vocabulary::GEO_BUFFER, "buffer")));
382 assert!(functions.contains(&(vocabulary::GEO_INTERSECTION, "intersection")));
383 assert!(functions.contains(&(vocabulary::GEO_UNION, "union")));
384 }
385
386 #[test]
387 fn test_distance_functions_registry() {
388 let functions = GeoSparqlRegistry::distance_functions();
389
390 assert_eq!(functions.len(), 1);
391 assert!(functions.contains(&(vocabulary::GEO_DISTANCE, "distance")));
392 }
393
394 #[test]
395 fn test_all_filter_functions() {
396 let functions = GeoSparqlRegistry::all_filter_functions();
397
398 assert!(functions.len() >= 8);
400
401 assert!(functions.contains(&(vocabulary::GEO_SF_EQUALS, "sfEquals")));
403 assert!(functions.contains(&(vocabulary::GEO_SF_DISJOINT, "sfDisjoint")));
404 assert!(functions.contains(&(vocabulary::GEO_SF_INTERSECTS, "sfIntersects")));
405 assert!(functions.contains(&(vocabulary::GEO_SF_TOUCHES, "sfTouches")));
406 assert!(functions.contains(&(vocabulary::GEO_SF_CROSSES, "sfCrosses")));
407 assert!(functions.contains(&(vocabulary::GEO_SF_WITHIN, "sfWithin")));
408 assert!(functions.contains(&(vocabulary::GEO_SF_CONTAINS, "sfContains")));
409 assert!(functions.contains(&(vocabulary::GEO_SF_OVERLAPS, "sfOverlaps")));
410 }
411
412 #[test]
413 fn test_all_property_functions() {
414 let functions = GeoSparqlRegistry::all_property_functions();
415
416 assert!(functions.len() >= 7);
418
419 assert!(functions.contains(&(vocabulary::GEO_DIMENSION, "dimension")));
421 assert!(functions.contains(&(vocabulary::GEO_DISTANCE, "distance")));
422 }
423}