rapidgeo_similarity/lib.rs
1//! # RapidGeo Similarity
2//!
3//! Fast trajectory similarity measures for geographic polylines.
4//!
5//! This crate provides efficient implementations of curve similarity algorithms
6//! specifically designed for geographic data. It includes:
7//!
8//! - **Fréchet distance** - Measures similarity while considering point order
9//! - **Hausdorff distance** - Measures maximum deviation between curves
10//! - **Batch processing** - Parallel computation for multiple comparisons
11//!
12//! All algorithms use great-circle distance (haversine) for geographic accuracy.
13//!
14//! ## Quick Start
15//!
16//! ```rust
17//! use rapidgeo_similarity::{discrete_frechet_distance, LngLat};
18//!
19//! let route1 = vec![
20//! LngLat::new_deg(-122.0, 37.0),
21//! LngLat::new_deg(-122.1, 37.1),
22//! ];
23//! let route2 = vec![
24//! LngLat::new_deg(-122.0, 37.0),
25//! LngLat::new_deg(-122.2, 37.2),
26//! ];
27//!
28//! let distance = discrete_frechet_distance(&route1, &route2).unwrap();
29//! println!("Fréchet distance: {} meters", distance);
30//! ```
31//!
32//! ## Choosing an Algorithm
33//!
34//! - **Fréchet distance**: Use when point order matters (e.g., comparing GPS tracks)
35//! - **Hausdorff distance**: Use when only shape matters (e.g., comparing building outlines)
36//!
37//! ## Features
38//!
39//! - `batch` - Enables parallel batch processing functions (requires rayon)
40
41pub use rapidgeo_distance::LngLat;
42
43pub mod frechet;
44pub mod hausdorff;
45
46#[cfg(feature = "batch")]
47pub mod batch;
48
49// Re-export main functions and types for convenience
50pub use frechet::{
51 discrete_frechet_distance, discrete_frechet_distance_with_threshold, DiscreteFrechet,
52};
53pub use hausdorff::{hausdorff_distance, hausdorff_distance_with_threshold, Hausdorff};
54
55#[cfg(feature = "batch")]
56pub use batch::{
57 batch_frechet_distance, batch_frechet_distance_threshold, pairwise_frechet_matrix,
58};
59
60/// A trait for measuring similarity between two polylines.
61///
62/// This trait provides a common interface for different similarity algorithms
63/// like Fréchet distance and Hausdorff distance. All implementations use
64/// great-circle distance for geographic coordinates.
65pub trait SimilarityMeasure {
66 /// Calculate the distance between two polylines.
67 ///
68 /// # Arguments
69 ///
70 /// * `a` - First polyline as a slice of longitude/latitude points
71 /// * `b` - Second polyline as a slice of longitude/latitude points
72 ///
73 /// # Returns
74 ///
75 /// The similarity distance in meters, or an error if the input is invalid.
76 ///
77 /// # Errors
78 ///
79 /// Returns `SimilarityError::EmptyInput` if either polyline is empty.
80 fn distance(&self, a: &[LngLat], b: &[LngLat]) -> Result<f64, SimilarityError>;
81}
82
83/// Errors that can occur when computing similarity measures.
84#[derive(Debug, Clone, PartialEq)]
85pub enum SimilarityError {
86 /// One or both input polylines are empty.
87 EmptyInput,
88 /// Input contains invalid data.
89 InvalidInput(String),
90}
91
92impl std::fmt::Display for SimilarityError {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94 match self {
95 SimilarityError::EmptyInput => write!(f, "input polylines cannot be empty"),
96 SimilarityError::InvalidInput(msg) => write!(f, "invalid input: {}", msg),
97 }
98 }
99}
100
101impl std::error::Error for SimilarityError {}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_similarity_error_display() {
109 let empty_error = SimilarityError::EmptyInput;
110 assert_eq!(empty_error.to_string(), "input polylines cannot be empty");
111
112 let invalid_error = SimilarityError::InvalidInput("test message".to_string());
113 assert_eq!(invalid_error.to_string(), "invalid input: test message");
114 }
115}