pub mod ripley;
pub mod envelopes;
pub mod inhomogeneous;
pub mod diagnostics;
pub use ripley::{KFunction, KFunctionResult};
pub use envelopes::{EnvelopeResult, CriticalBandEnvelope};
pub use inhomogeneous::{InhomogeneousKProcess, InhomogeneousResult};
pub use diagnostics::{PointProcessResiduals, ResidualType};
use crate::GeostatError;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DistanceRange {
pub min: f64,
pub max: f64,
pub n_bins: usize,
}
impl DistanceRange {
pub fn new(min: f64, max: f64, n_bins: usize) -> Result<Self, GeostatError> {
if !min.is_finite() || !max.is_finite() || min < 0.0 || max <= min {
return Err(GeostatError::InvalidParameters(
"distance range must have 0 <= min < max (both finite)".to_string(),
));
}
if n_bins < 2 {
return Err(GeostatError::InvalidParameters(
"must have at least 2 distance bins".to_string(),
));
}
Ok(DistanceRange { min, max, n_bins })
}
pub fn bin_edges(&self) -> Vec<f64> {
(0..=self.n_bins)
.map(|i| self.min + i as f64 * (self.max - self.min) / self.n_bins as f64)
.collect()
}
pub fn bin_centers(&self) -> Vec<f64> {
let edges = self.bin_edges();
edges.windows(2).map(|w| (w[0] + w[1]) / 2.0).collect()
}
pub fn bin_width(&self) -> f64 {
(self.max - self.min) / self.n_bins as f64
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum StudyAreaType {
Rectangle,
ConvexHull,
Circle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PatternSummary {
pub n_points: usize,
pub area: f64,
pub intensity: f64,
pub mean_nnd: f64,
pub min_x: f64,
pub max_x: f64,
pub min_y: f64,
pub max_y: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_distance_range_creation() {
let dr = DistanceRange::new(0.0, 100.0, 10).unwrap();
assert_eq!(dr.n_bins, 10);
assert_eq!(dr.bin_edges().len(), 11);
}
#[test]
fn test_distance_range_bin_centers() {
let dr = DistanceRange::new(0.0, 100.0, 10).unwrap();
let centers = dr.bin_centers();
assert_eq!(centers.len(), 10);
assert!((centers[0] - 5.0).abs() < 1e-10);
assert!((centers[9] - 95.0).abs() < 1e-10);
}
#[test]
fn test_distance_range_invalid() {
assert!(DistanceRange::new(100.0, 50.0, 10).is_err()); assert!(DistanceRange::new(0.0, 100.0, 1).is_err()); assert!(DistanceRange::new(0.0, -1.0, 10).is_err()); }
}