use super::types::{RoadNetwork, SpatialSegment};
use crate::types::{CandidatePoint, RoadSegment, SegmentId};
use geo::{algorithm::euclidean_distance::EuclideanDistance, Point};
use rstar::PointDistance;
pub struct ProjectionResult {
pub projected_point: Point<f64>,
pub distance: f64,
}
impl RoadNetwork {
pub fn query_segments_near(
&self,
point: Point<f64>,
radius: f64,
) -> Vec<&RoadSegment> {
let query_point = [point.x(), point.y()];
let nearby_spatial_segments: Vec<&SpatialSegment> = self
.spatial_index
.locate_within_distance(query_point, radius * radius) .collect();
nearby_spatial_segments
.into_iter()
.filter_map(|spatial_segment| {
self.segments.get(&spatial_segment.segment_id)
})
.collect()
}
pub fn project_point_to_segment(
&self,
segment_id: SegmentId,
point: Point<f64>,
) -> Option<ProjectionResult> {
let segment = self.segments.get(&segment_id)?;
let candidate = CandidatePoint {
segment_id,
segment,
gps_point: point,
};
match candidate.projected_point() {
Ok(projected) => {
let distance = candidate.distance_to_gps();
Some(ProjectionResult {
projected_point: projected,
distance,
})
}
Err(_) => None,
}
}
}
impl PointDistance for SpatialSegment {
fn distance_2(&self, point: &[f64; 2]) -> f64 {
let query_point = Point::new(point[0], point[1]);
let center_coord = self.bounding_box.center();
let center_point = Point::from(center_coord);
center_point.euclidean_distance(&query_point).powi(2)
}
}