pelorus 0.1.0

A high-performance GPS map-matching library for geospatial road network processing
Documentation
use approx::assert_abs_diff_eq;
use geo::{Coord, LineString, Point};
use pelorus::types::{CandidatePoint, GpsMeasurement, RoadSegment};

mod fixtures {
    pub const GPS_COORDS: (f64, f64) = (40.4168, -3.7038); // Madrid coordinates (lat, lon)
    pub const GPS_TIMESTAMP: f64 = 1234567890.0;

    pub const SEGMENT_ID: u32 = 42;
    pub const SEGMENT_COORDS: [(f64, f64); 3] =
        [(40.4, -3.7), (40.5, -3.6), (40.6, -3.5)]; // (lat, lon)

    pub const CANDIDATE_SEGMENT_ID: u32 = 10;
}

#[test]
fn test_gps_measurement_creation_and_access() {
    // TODO: Coordinates are currently unitless (degrees), should consider projection systems
    // Test GeogPoint methods - creation from lat/lon
    let point = Point::new(fixtures::GPS_COORDS.1, fixtures::GPS_COORDS.0);
    assert_eq!(point.x(), fixtures::GPS_COORDS.1);
    assert_eq!(point.y(), fixtures::GPS_COORDS.0);

    let measurement = GpsMeasurement {
        point,
        timestamp: fixtures::GPS_TIMESTAMP,
    };

    assert_eq!(measurement.point.x(), fixtures::GPS_COORDS.1);
    assert_eq!(measurement.point.y(), fixtures::GPS_COORDS.0);
    assert_eq!(measurement.timestamp, fixtures::GPS_TIMESTAMP);
}

#[test]
fn test_road_segment_creation() {
    let coords: Vec<Coord> = fixtures::SEGMENT_COORDS
        .iter()
        .map(|&(lat, lon)| Coord::from((lon, lat))) // Convert (lat, lon) to (x, y)
        .collect();
    let segment = RoadSegment {
        id: fixtures::SEGMENT_ID,
        geometry: LineString::new(coords),
    };

    assert_eq!(segment.id, fixtures::SEGMENT_ID);
    assert_eq!(
        segment.geometry.coords().count(),
        fixtures::SEGMENT_COORDS.len()
    );
    let first_coord = segment.geometry.coords().next().unwrap();
    assert_eq!(first_coord.x, fixtures::SEGMENT_COORDS[0].1); // lon
    assert_eq!(first_coord.y, fixtures::SEGMENT_COORDS[0].0); // lat
}

#[test]
fn test_candidate_point_creation() {
    let coords: Vec<Coord> = fixtures::SEGMENT_COORDS
        .iter()
        .map(|&(lat, lon)| Coord::from((lon, lat)))
        .collect();
    let segment = RoadSegment {
        id: fixtures::CANDIDATE_SEGMENT_ID,
        geometry: LineString::new(coords),
    };

    let gps_point = Point::new(fixtures::GPS_COORDS.1, fixtures::GPS_COORDS.0);
    let candidate = CandidatePoint {
        segment_id: fixtures::CANDIDATE_SEGMENT_ID,
        segment: &segment,
        gps_point,
    };

    assert_eq!(candidate.segment_id, fixtures::CANDIDATE_SEGMENT_ID);
    assert_eq!(candidate.segment.id, fixtures::CANDIDATE_SEGMENT_ID);

    // Test computed methods
    let projected = candidate
        .projected_point()
        .expect("Should be able to project point");
    let distance = candidate.distance_to_gps();

    // TODO: Distance is currently unitless (degrees), should be in meters
    // Test actual computed values using approx for floating point comparison
    assert_abs_diff_eq!(projected.x(), -3.6935, epsilon = 1e-4);
    assert_abs_diff_eq!(projected.y(), 40.4065, epsilon = 1e-4);
    assert_abs_diff_eq!(distance, 0.0146, epsilon = 1e-4);
}