use geo::{Bearing, Haversine, Length, Line, LineString};
use itertools::Itertools;
use rayon::prelude::*;
use routee_compass_core::model::network::{Edge, EdgeId, EdgeList, EdgeListId, Vertex, VertexId};
use std::collections::{HashMap, HashSet};
use crate::{
collection::{
OvertureMapsCollectionError, SegmentAccessRestrictionWhen, SegmentFullType,
TransportationConnectorRecord, TransportationSegmentRecord,
},
graph::{consts, omf_graph::OmfEdgeList, segment_split::SegmentSplit},
};
pub fn create_vertices_and_lookup(
connectors: &[TransportationConnectorRecord],
keep_list: Option<&HashSet<&String>>,
) -> Result<(Vec<Vertex>, HashMap<String, usize>), OvertureMapsCollectionError> {
let keep_connectors = match keep_list {
Some(keep) => connectors
.iter()
.filter(|c| keep.contains(&c.id))
.collect_vec(),
None => connectors.iter().collect_vec(),
};
let vertices = keep_connectors
.par_iter()
.enumerate()
.map(|(idx, c)| c.try_to_vertex(idx))
.collect::<Result<Vec<Vertex>, OvertureMapsCollectionError>>()?;
let mapping: HashMap<String, usize> = keep_connectors
.iter()
.enumerate()
.map(|(idx, c)| (c.id.clone(), idx))
.collect();
Ok((vertices, mapping))
}
pub fn create_segment_lookup(segments: &[&TransportationSegmentRecord]) -> HashMap<String, usize> {
segments
.iter()
.enumerate()
.map(|(idx, c)| (c.id.clone(), idx))
.collect()
}
pub fn find_splits(
segments: &[&TransportationSegmentRecord],
when: Option<&SegmentAccessRestrictionWhen>,
split_op: fn(
&TransportationSegmentRecord,
Option<&SegmentAccessRestrictionWhen>,
) -> Result<Vec<SegmentSplit>, OvertureMapsCollectionError>,
) -> Result<Vec<SegmentSplit>, OvertureMapsCollectionError> {
let result = segments
.par_iter()
.map(|s| split_op(s, when))
.collect::<Result<Vec<Vec<SegmentSplit>>, OvertureMapsCollectionError>>()?
.into_iter()
.flatten()
.collect();
Ok(result)
}
pub fn create_edges(
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
vertices: &[Vertex],
vertex_lookup: &HashMap<String, usize>,
edge_list_id: EdgeListId,
) -> Result<Vec<Edge>, OvertureMapsCollectionError> {
splits
.iter()
.enumerate()
.collect_vec()
.par_iter()
.map(|(idx, split)| {
split.create_edge_from_split(
EdgeId(*idx),
edge_list_id,
segments,
segment_lookup,
vertices,
vertex_lookup,
)
})
.collect::<Result<Vec<Edge>, OvertureMapsCollectionError>>()
}
pub fn create_geometries(
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
) -> Result<Vec<LineString<f32>>, OvertureMapsCollectionError> {
splits
.par_iter()
.map(|split| split.create_geometry_from_split(segments, segment_lookup))
.collect::<Result<Vec<LineString<f32>>, OvertureMapsCollectionError>>()
}
pub fn create_speeds(
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
) -> Result<Vec<Option<f64>>, OvertureMapsCollectionError> {
splits
.par_iter()
.map(|split| split.get_split_speed(segments, segment_lookup))
.collect::<Result<Vec<Option<f64>>, OvertureMapsCollectionError>>()
}
pub fn create_segment_full_types(
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
) -> Result<Vec<SegmentFullType>, OvertureMapsCollectionError> {
splits
.par_iter()
.map(|split| split.get_split_segment_full_type(segments, segment_lookup))
.collect::<Result<Vec<SegmentFullType>, OvertureMapsCollectionError>>()
}
pub fn create_speed_by_segment_type_lookup<'a>(
initial_speeds: &[Option<f64>],
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
classes: &'a [SegmentFullType],
) -> Result<HashMap<&'a SegmentFullType, f64>, OvertureMapsCollectionError> {
let split_lenghts = splits
.par_iter()
.map(|split| {
split
.get_split_length_meters(segments, segment_lookup)
.map(|v_f32| v_f32 as f64)
})
.collect::<Result<Vec<f64>, OvertureMapsCollectionError>>()?;
let mut speed_sum_lookup: HashMap<&SegmentFullType, (f64, f64)> = HashMap::new();
for ((class, w), speed) in classes.iter().zip(split_lenghts).zip(initial_speeds) {
let Some(x) = speed else { continue };
let element = speed_sum_lookup.entry(class).or_insert((0.0, 0.0));
element.0 += w * x;
element.1 += w;
}
Ok(speed_sum_lookup
.into_iter()
.filter(|&(_k, (_wx, w))| w != 0.0)
.map(|(k, (wx, w))| (k, wx / w))
.collect::<HashMap<&SegmentFullType, f64>>())
}
pub fn get_segment_omf_ids(
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
) -> Result<Vec<(String, f64)>, OvertureMapsCollectionError> {
splits
.par_iter()
.map(|split| split.get_omf_segment_id_and_linear_ref(segments, segment_lookup))
.collect()
}
pub fn get_global_average_speed(
initial_speeds: &[Option<f64>],
segments: &[&TransportationSegmentRecord],
segment_lookup: &HashMap<String, usize>,
splits: &[SegmentSplit],
) -> Result<f64, OvertureMapsCollectionError> {
let split_lenghts = splits
.par_iter()
.map(|split| {
split
.get_split_length_meters(segments, segment_lookup)
.map(|v_f32| v_f32 as f64)
})
.collect::<Result<Vec<f64>, OvertureMapsCollectionError>>()?;
let mut total_length = 0.;
let mut weighted_sum = 0.;
for (opt_speed, length) in initial_speeds.iter().zip(split_lenghts) {
let Some(speed) = opt_speed else { continue };
total_length += length;
weighted_sum += length * speed;
}
if total_length < consts::F64_DISTANCE_TOLERANCE {
return Err(OvertureMapsCollectionError::InternalError(format!(
"internal division by zero when computing average speed: {initial_speeds:?}"
)));
}
Ok(weighted_sum / total_length)
}
pub fn bearing_deg_from_geometries(
geometries: &[LineString<f32>],
) -> Result<Vec<f64>, OvertureMapsCollectionError> {
geometries
.par_iter()
.map(|linestring| {
let n = linestring.0.len();
if n < 2 {
return Err(OvertureMapsCollectionError::InternalError(format!(
"cannot compute bearing on linestring with less than two points: {linestring:?}"
)));
}
let p1 = linestring.0[n - 1];
let mut p0 = linestring.0[n - 2];
for i in (0..n - 1).rev() {
let candidate = linestring.0[i];
let line = Line::new(candidate, p1);
if Haversine.length(&line) > consts::F32_DISTANCE_TOLERANCE {
p0 = candidate;
break;
}
}
Ok(Haversine.bearing(p0.into(), p1.into()) as f64)
})
.collect()
}
pub fn compute_vertex_remapping(
vertices: &[Vertex],
edge_lists: &[OmfEdgeList],
island_edges: &[(EdgeListId, EdgeId)],
) -> Result<Vec<Option<VertexId>>, OvertureMapsCollectionError> {
let removed_edges: HashSet<(EdgeListId, EdgeId)> = island_edges.iter().cloned().collect();
let mut valid_vertices = HashSet::new();
for edge_list in edge_lists {
for edge in edge_list.edges.0.iter() {
if !removed_edges.contains(&(edge.edge_list_id, edge.edge_id)) {
valid_vertices.insert(edge.src_vertex_id);
valid_vertices.insert(edge.dst_vertex_id);
}
}
}
let mut new_id: usize = 0;
Ok((0..vertices.len())
.map(|old_id| {
if valid_vertices.contains(&VertexId(old_id)) {
new_id += 1;
Some(VertexId(new_id - 1))
} else {
None
}
})
.collect())
}
pub fn clean_omf_edge_list(
omf_list: OmfEdgeList,
mask: Vec<bool>,
vertex_remapping: &[Option<VertexId>],
) -> Result<OmfEdgeList, OvertureMapsCollectionError> {
let edges = EdgeList(
omf_list
.edges
.0
.iter()
.filter(|edge| mask[edge.edge_id.0])
.enumerate()
.collect::<Vec<_>>()
.into_par_iter()
.map(|(new_idx, edge)| {
let new_src_id = vertex_remapping
.get(edge.src_vertex_id.0)
.copied()
.flatten()
.ok_or(OvertureMapsCollectionError::InternalError(format!(
"src vertex_id {} for edge ({},{}) was removed",
edge.src_vertex_id, edge.edge_list_id, edge.edge_id
)))?;
let new_dst_id = vertex_remapping
.get(edge.dst_vertex_id.0)
.copied()
.flatten()
.ok_or(OvertureMapsCollectionError::InternalError(format!(
"dst vertex_id {} for edge ({},{}) was removed",
edge.dst_vertex_id, edge.edge_list_id, edge.edge_id
)))?;
Ok(Edge {
edge_id: EdgeId(new_idx),
src_vertex_id: new_src_id,
dst_vertex_id: new_dst_id,
..*edge
})
})
.collect::<Result<Vec<Edge>, OvertureMapsCollectionError>>()?
.into_boxed_slice(),
);
let geometries = omf_list
.geometries
.into_iter()
.enumerate()
.filter(|(idx, _)| mask[*idx])
.collect::<Vec<_>>()
.into_par_iter()
.map(|(_, ls)| ls)
.collect();
let classes = omf_list
.classes
.into_iter()
.enumerate()
.filter(|(idx, _)| mask[*idx])
.collect::<Vec<_>>()
.into_par_iter()
.map(|(_, cls)| cls)
.collect();
let speeds = omf_list
.speeds
.into_iter()
.enumerate()
.filter(|(idx, _)| mask[*idx])
.collect::<Vec<_>>()
.into_par_iter()
.map(|(_, s)| s)
.collect();
let bearings = omf_list
.bearings
.into_iter()
.enumerate()
.filter(|(idx, _)| mask[*idx])
.collect::<Vec<_>>()
.into_par_iter()
.map(|(_, b)| b)
.collect();
let omf_segment_ids = omf_list
.omf_segment_ids
.into_iter()
.enumerate()
.filter(|(idx, _)| mask[*idx])
.collect::<Vec<_>>()
.into_par_iter()
.map(|(_, i)| i)
.collect();
Ok(OmfEdgeList {
edge_list_id: omf_list.edge_list_id,
edges,
geometries,
classes,
speeds,
speed_lookup: omf_list.speed_lookup,
bearings,
omf_segment_ids,
})
}