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}