use crate::definition::{Layer, Layers};
use crate::generation::LayerGeneration;
use crate::transition::*;
use geo::{Distance, Haversine, Point};
use itertools::Itertools;
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use routers_network::{Entry, Metadata, Network};
use std::collections::HashMap;
#[derive(Copy, Clone)]
pub struct StandardGenerator<'a, E, M, Emmis>
where
E: Entry,
M: Metadata,
Emmis: EmissionStrategy + Send + Sync,
{
pub search_distance: f64,
pub emission: &'a Emmis,
map: &'a dyn Network<E, M>,
}
struct PartiallyGeneratedCandidate<E: Entry> {
candidate: Candidate<E>,
candidate_ref: CandidateRef,
layer_id: usize,
}
#[derive(Default)]
struct PartialLayerGeneration<E: Entry> {
candidate_graph: OpenCandidateGraph,
lookup: scc::HashMap<CandidateId, Candidate<E>>,
layers: HashMap<usize, Layer>,
}
impl<'a, E, M, Emmis> StandardGenerator<'a, E, M, Emmis>
where
E: Entry,
M: Metadata,
Emmis: EmissionStrategy + Send + Sync,
{
pub fn new(map: &'a dyn Network<E, M>, emission: &'a Emmis, search_distance: f64) -> Self {
StandardGenerator {
map,
emission,
search_distance,
}
}
fn discover_candidates<'iter>(
&'a self,
layer_id: usize,
origin: &'iter Point,
) -> impl IntoParallelIterator<Item = PartiallyGeneratedCandidate<E>>
where
'a: 'iter,
{
use rayon::iter::ParallelBridge;
self.map
.nearest_nodes_projected::<'iter>(origin, self.search_distance)
.enumerate()
.par_bridge()
.map(move |(node_id, (position, edge))| {
let location = CandidateLocation { layer_id, node_id };
let distance = Haversine.distance(position, *origin);
let emission = self.emission.cost(EmissionContext::new(
&position,
origin,
distance,
edge.weight,
));
let candidate = Candidate::new(edge.thin(), position, emission, location);
let candidate_reference = CandidateRef::new(emission);
(candidate, candidate_reference)
})
.map(
move |(candidate, candidate_ref): (Candidate<E>, CandidateRef)| {
PartiallyGeneratedCandidate {
candidate,
candidate_ref,
layer_id,
}
},
)
}
fn prepare_candidate(
mut layer: PartialLayerGeneration<E>,
candidate: &PartiallyGeneratedCandidate<E>,
origin: &Point,
) -> PartialLayerGeneration<E> {
let node_index: CandidateId = layer.candidate_graph.add_node(candidate.candidate_ref);
let _ = layer.lookup.insert(node_index, candidate.candidate);
layer
.layers
.entry(candidate.layer_id)
.and_modify(|layer| {
layer.nodes.push(node_index);
})
.or_insert(Layer {
nodes: vec![node_index],
origin: *origin,
});
layer
}
}
fn hashmap_to_vec<T>(map: HashMap<usize, T>) -> Vec<T> {
map.into_iter()
.sorted_by_key(|(i, _)| *i)
.map(|(_, v)| v)
.collect()
}
impl<Emmis, E, M> LayerGeneration<E> for StandardGenerator<'_, E, M, Emmis>
where
E: Entry,
M: Metadata,
Emmis: EmissionStrategy + Send + Sync,
{
fn generate(self, input: &[Point]) -> (Layers, Candidates<E>) {
let fold_binding = |layer, candidate| {
Self::prepare_candidate(layer, &candidate, &input[candidate.layer_id])
};
let PartialLayerGeneration {
candidate_graph,
lookup,
layers,
} = input
.into_par_iter()
.enumerate()
.flat_map(|(i, o)| self.discover_candidates(i, o))
.collect::<Vec<PartiallyGeneratedCandidate<E>>>()
.into_iter()
.fold(PartialLayerGeneration::<E>::default(), fold_binding);
let layers = hashmap_to_vec(layers);
let candidates = Candidates::new(candidate_graph, lookup);
(Layers { layers }, candidates)
}
}