use std::borrow::Borrow;
use std::ops::Add;
use num_traits::Zero;
use rayon::prelude::*;
use h3ron::collections::hashbrown::hash_map::Entry;
use h3ron::collections::{H3CellMap, H3Treemap, HashMap};
use h3ron::iter::change_resolution;
use h3ron::{H3Cell, HasH3Resolution};
use crate::algorithm::dijkstra::edge_dijkstra;
use crate::algorithm::path::Path;
use crate::algorithm::NearestGraphNodes;
use crate::error::Error;
use crate::graph::{GetCellEdges, GetCellNode};
pub trait ShortestPathOptions {
fn max_distance_to_graph(&self) -> u32 {
0
}
fn num_destinations_to_reach(&self) -> Option<usize> {
None
}
}
#[derive(Default)]
pub struct DefaultShortestPathOptions {}
impl ShortestPathOptions for DefaultShortestPathOptions {}
impl DefaultShortestPathOptions {
pub fn new() -> Self {
Default::default()
}
}
pub trait ShortestPath<W> {
fn shortest_path<I, OPT: ShortestPathOptions>(
&self,
origin_cell: H3Cell,
destination_cells: I,
options: &OPT,
) -> Result<Vec<Path<W>>, Error>
where
I: IntoIterator,
I::Item: Borrow<H3Cell>;
}
pub trait ShortestPathManyToMany<W>
where
W: Send + Sync + Ord + Copy,
{
#[inline]
fn shortest_path_many_to_many<I, OPT>(
&self,
origin_cells: I,
destination_cells: I,
options: &OPT,
) -> Result<H3CellMap<Vec<Path<W>>>, Error>
where
I: IntoIterator,
I::Item: Borrow<H3Cell>,
OPT: ShortestPathOptions + Send + Sync,
{
self.shortest_path_many_to_many_map(origin_cells, destination_cells, options, Ok)
}
fn shortest_path_many_to_many_map<I, OPT, PM, O>(
&self,
origin_cells: I,
destination_cells: I,
options: &OPT,
path_transform_fn: PM,
) -> Result<H3CellMap<Vec<O>>, Error>
where
I: IntoIterator,
I::Item: Borrow<H3Cell>,
OPT: ShortestPathOptions + Send + Sync,
PM: Fn(Path<W>) -> Result<O, Error> + Send + Sync,
O: Send + Ord + Clone;
}
impl<W, G> ShortestPathManyToMany<W> for G
where
G: GetCellEdges<EdgeWeightType = W> + GetCellNode + HasH3Resolution + NearestGraphNodes + Sync,
W: PartialOrd + PartialEq + Add + Copy + Send + Ord + Zero + Sync,
{
fn shortest_path_many_to_many_map<I, OPT, PM, O>(
&self,
origin_cells: I,
destination_cells: I,
options: &OPT,
path_transform_fn: PM,
) -> Result<H3CellMap<Vec<O>>, Error>
where
I: IntoIterator,
I::Item: Borrow<H3Cell>,
OPT: ShortestPathOptions + Send + Sync,
PM: Fn(Path<W>) -> Result<O, Error> + Send + Sync,
O: Send + Ord + Clone,
{
let filtered_origin_cells = substitute_origin_cells(
self,
options.max_distance_to_graph(),
origin_cells,
true, )?;
if filtered_origin_cells.is_empty() {
return Ok(Default::default());
}
let destination_substmap = {
let origins_treemap: H3Treemap<H3Cell> =
filtered_origin_cells.iter().map(|(k, _)| *k).collect();
substitute_destination_cells(
self,
options.max_distance_to_graph(),
destination_cells,
&origins_treemap,
)?
};
if destination_substmap.0.is_empty() {
return Ok(Default::default());
}
let destination_treemap =
H3Treemap::from_iter_with_sort(destination_substmap.0.keys().copied());
log::debug!(
"shortest_path many-to-many: from {} cells to {} cells at resolution {} with max_distance_to_graph = {}",
filtered_origin_cells.len(),
destination_substmap.0.len(),
self.h3_resolution(),
options.max_distance_to_graph()
);
let mut cellmap: HashMap<H3Cell, Vec<O>> = Default::default();
for par_result in filtered_origin_cells
.par_iter()
.map(|(graph_connected_origin_cell, output_origin_cells)| {
shortest_path_many_worker(
self,
graph_connected_origin_cell,
output_origin_cells.as_slice(),
&destination_treemap,
&destination_substmap,
options,
|path| {
let origin_cell = path.origin_cell;
path_transform_fn(path).map(|transformed| (origin_cell, transformed))
},
)
})
.collect::<Result<Vec<_>, _>>()?
{
for (origin_cell, transformed) in par_result {
match cellmap.entry(origin_cell) {
Entry::Occupied(mut entry) => entry.get_mut().push(transformed),
Entry::Vacant(entry) => {
entry.insert(vec![transformed]);
}
}
}
}
Ok(cellmap)
}
}
impl<W, G> ShortestPath<W> for G
where
G: GetCellEdges<EdgeWeightType = W> + GetCellNode + HasH3Resolution + NearestGraphNodes,
W: PartialOrd + PartialEq + Add + Copy + Ord + Zero,
{
fn shortest_path<I, OPT>(
&self,
origin_cell: H3Cell,
destination_cells: I,
options: &OPT,
) -> Result<Vec<Path<W>>, Error>
where
I: IntoIterator,
I::Item: Borrow<H3Cell>,
OPT: ShortestPathOptions,
{
let (graph_connected_origin_cell, requested_origin_cells) = {
let mut filtered_origin_cells = substitute_origin_cells(
self,
options.max_distance_to_graph(),
std::iter::once(origin_cell),
false, )?;
if filtered_origin_cells.is_empty() {
return Ok(Default::default());
} else {
filtered_origin_cells.remove(0)
}
};
let destination_substmap = {
let mut origins_treemap: H3Treemap<H3Cell> = Default::default();
origins_treemap.insert(graph_connected_origin_cell);
substitute_destination_cells(
self,
options.max_distance_to_graph(),
destination_cells,
&origins_treemap,
)?
};
if destination_substmap.0.is_empty() {
return Ok(Default::default());
}
let destination_treemap =
H3Treemap::from_iter_with_sort(destination_substmap.0.keys().copied());
shortest_path_many_worker(
self,
&graph_connected_origin_cell,
requested_origin_cells.as_slice(),
&destination_treemap,
&destination_substmap,
options,
Ok,
)
}
}
fn shortest_path_many_worker<G, W, OPT, PM, O>(
graph: &G,
origin_cell: &H3Cell,
requested_origin_cells: &[H3Cell],
destination_cells: &H3Treemap<H3Cell>,
destination_substmap: &SubstituteMap,
options: &OPT,
path_transform_fn: PM,
) -> Result<Vec<O>, Error>
where
G: GetCellEdges<EdgeWeightType = W>,
W: Add + Copy + Ord + Zero,
PM: Fn(Path<W>) -> Result<O, Error>,
O: Clone,
OPT: ShortestPathOptions,
{
let found_paths = edge_dijkstra(
graph,
origin_cell,
destination_cells,
options.num_destinations_to_reach(),
)?;
let mut transformed_paths = Vec::with_capacity(found_paths.len());
for path in found_paths.into_iter() {
for destination_cell in destination_substmap.cells_substituted_by(&path.destination_cell) {
for origin_cell in requested_origin_cells {
let mut this_path = path.clone();
this_path.origin_cell = *origin_cell;
this_path.destination_cell = *destination_cell;
transformed_paths.push(path_transform_fn(this_path)?);
}
}
}
Ok(transformed_paths)
}
#[derive(Default)]
struct SubstituteMap(H3CellMap<Vec<H3Cell>>);
impl SubstituteMap {
fn cells_substituted_by(&self, cell: &H3Cell) -> &[H3Cell] {
self.0
.get(cell)
.map_or_else(|| &[] as &[H3Cell], |sub| sub.as_slice())
}
fn add_substitute(&mut self, cell: H3Cell, substituted_by: H3Cell) {
match self.0.entry(substituted_by) {
Entry::Occupied(mut occupied) => occupied.get_mut().push(cell),
Entry::Vacant(vacant) => {
vacant.insert(vec![cell]);
}
}
}
fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
fn substitute_destination_cells<G, I>(
graph: &G,
max_distance_to_graph: u32,
destination_cells: I,
origins_treemap: &H3Treemap<H3Cell>,
) -> Result<SubstituteMap, Error>
where
G: GetCellNode + NearestGraphNodes + HasH3Resolution,
I: IntoIterator,
I::Item: Borrow<H3Cell>,
{
let mut destination_substmap = SubstituteMap::default();
for destination in change_resolution(destination_cells, graph.h3_resolution()) {
let destination_cell = destination?;
for (graph_cell, node_type, _) in
graph.nearest_graph_nodes(&destination_cell, max_distance_to_graph)?
{
if node_type.is_destination() || origins_treemap.contains(&graph_cell) {
destination_substmap.add_substitute(destination_cell, graph_cell);
break;
}
}
}
if destination_substmap.is_empty() {
return Err(Error::DestinationsNotInGraph);
}
Ok(destination_substmap)
}
fn substitute_origin_cells<G, I>(
graph: &G,
max_distance_to_graph: u32,
origin_cells: I,
return_sorted: bool,
) -> Result<Vec<(H3Cell, Vec<H3Cell>)>, Error>
where
G: GetCellNode + NearestGraphNodes + HasH3Resolution,
I: IntoIterator,
I::Item: Borrow<H3Cell>,
{
let mut origin_substmap = SubstituteMap::default();
for cell in change_resolution(origin_cells, graph.h3_resolution()) {
let cell = cell?;
for (graph_cell, node_type, _) in graph.nearest_graph_nodes(&cell, max_distance_to_graph)? {
if node_type.is_origin() {
origin_substmap.add_substitute(cell, graph_cell);
break;
}
}
}
let mut out_vec: Vec<_> = origin_substmap.0.drain().collect();
if return_sorted {
out_vec.sort_unstable_by_key(|v| v.0);
}
Ok(out_vec)
}
#[cfg(test)]
mod tests {
use std::convert::TryInto;
use geo_types::Coord;
use h3ron::H3Cell;
use crate::algorithm::shortest_path::{DefaultShortestPathOptions, ShortestPathManyToMany};
use crate::graph::{H3EdgeGraph, PreparedH3EdgeGraph};
#[test]
fn test_shortest_path_same_origin_and_destination() {
let res = 8;
let origin = H3Cell::from_coordinate(Coord::from((23.3, 12.3)), res).unwrap();
let edge = origin.directed_edges().unwrap().first().unwrap();
let destination = edge.destination_cell().unwrap();
let prepared_graph: PreparedH3EdgeGraph<_> = {
let mut graph = H3EdgeGraph::new(res);
graph.add_edge(edge, 5_u32).unwrap();
graph.try_into().unwrap()
};
let paths = prepared_graph
.shortest_path_many_to_many(
&vec![origin],
&vec![origin, destination],
&DefaultShortestPathOptions::default(),
)
.unwrap();
assert_eq!(paths.len(), 1);
let path_vec = paths.get(&origin).unwrap();
assert_eq!(path_vec.len(), 2);
for path in path_vec.iter() {
if path.destination_cell == origin {
assert!(path.is_empty());
assert_eq!(path.cost, 0);
} else if path.destination_cell == destination {
assert!(!path.is_empty());
assert_eq!(path.cost, 5);
} else {
unreachable!()
}
}
}
}