use crate::affine::Affine;
use crate::distances;
use rustc_hash::FxHashMap;
use std::collections::HashMap;
use std::rc::Rc;
pub type NodeId = u64;
pub type NodeData = (NodeId, f32, f32, f32, f32, Rc<Vec<Option<f32>>>);
#[derive(Default)]
pub struct NodeMap {
pub origin: (i32, i32),
pub shape: (usize, usize),
pub index: Vec<usize>,
pub data: Vec<NodeData>,
}
impl NodeMap {
#[inline]
pub fn get(&self, &(i, j): &(i32, i32)) -> Option<&NodeData> {
let row = i - self.origin.0;
let col = j - self.origin.1;
if row < 0 || col < 0 || row as usize >= self.shape.0 || col as usize >= self.shape.1 {
return None;
}
self.data
.get(self.index[row as usize * self.shape.1 + col as usize])
}
}
#[derive(Debug, Clone)]
pub struct EdgeData {
pub adj_dist: f32, pub geo_dist: f32, pub condition: f32, pub pa: f32, pub num_cells: f32, pub similarities: Rc<Vec<Option<f32>>>, }
#[derive(Debug, Clone)]
pub struct Graph {
pub data: FxHashMap<(NodeId, NodeId), EdgeData>,
pub source: NodeId, }
impl Graph {
pub fn new(cap: Option<usize>) -> Self {
let mut data = FxHashMap::default();
if let Some(c) = cap {
data.reserve(c);
}
let source = 0;
Self { data, source }
}
pub fn add_node(&mut self, key: (NodeId, NodeId), value: EdgeData) {
self.data.insert(key, value);
}
#[cfg(test)]
pub fn get(&self, key: &(NodeId, NodeId)) -> Option<&EdgeData> {
self.data.get(&key)
}
#[cfg(test)]
pub fn count_edges(&self) -> FxHashMap<NodeId, usize> {
let mut edge_counts = FxHashMap::default();
edge_counts.reserve(self.data.len());
for &(u, _) in self.data.keys() {
*edge_counts.entry(u).or_insert(0) += 1;
}
edge_counts
}
pub fn clear(&mut self) {
self.data.clear();
}
}
impl Graph {
#[inline]
pub fn neighbours(
&mut self,
i: i32,
j: i32,
u: NodeId, i_ngb: &[i32],
j_ngb: &[i32],
factor: f32,
node_mapping: &NodeMap,
transform: &Affine,
is_wgs: bool,
pa_to_pa: bool,
) -> bool {
let mut is_isolated = u == self.source;
let (x1, y1) = transform.xy(i, j);
for (&di, &dj) in i_ngb.iter().zip(j_ngb.iter()) {
let ni = i + di;
let nj = j + dj;
if let Some(&(v, z, t, c, pa_v, ref s)) = node_mapping.get(&(ni, nj)) {
let (x2, y2) = transform.xy(ni, nj);
let dist: f32 = distances::distance_km(x1, y1, x2, y2, is_wgs);
let w: f32 = (1.0 - factor) * t + factor;
self.add_node(
(u, v),
EdgeData {
adj_dist: w * dist,
geo_dist: dist,
condition: z,
pa: pa_v,
num_cells: c,
similarities: s.clone(),
},
);
is_isolated = false;
}
}
if is_isolated {
if pa_to_pa {
self.clear();
} else if let Some(&(_, z, t, c, pa_v, ref s)) = node_mapping.get(&(i, j)) {
let (x2, y2) = transform.xy(i + 1, j);
let dist: f32 = distances::distance_km(x1, y1, x2, y2, is_wgs);
let w: f32 = (1.0 - factor) * t + factor;
let fake_v = u + 1;
self.clear();
self.add_node(
(u, fake_v),
EdgeData {
adj_dist: w * dist,
geo_dist: dist,
condition: z,
pa: pa_v,
num_cells: c,
similarities: s.clone(),
},
);
}
}
is_isolated
}
}
impl Graph {
#[inline]
pub fn fringe(
&mut self,
i: i32,
j: i32,
factor: f32,
level: i32,
node_mapping: &NodeMap,
node_mapping_higher: &NodeMap,
transforms: &HashMap<i32, Affine>,
is_wgs: bool,
offsets: (usize, usize),
) {
if let Some(&(uu, _, _, _, _, _)) = node_mapping.get(&(i, j)) {
let higher_level: i32 = level * 2;
let (higher_neighbours, n_higher_neighbours) =
get_edge_neighbours(i, j, level, offsets);
let transform: &Affine = transforms.get(&level).unwrap();
let transform_upper: &Affine = transforms.get(&higher_level).unwrap();
let (x1, y1) = transform.xy(i, j);
for &(ni, nj) in higher_neighbours[..n_higher_neighbours].iter() {
if let Some(&(v, z, t, c, pa_v, ref s)) = node_mapping_higher.get(&(ni, nj)) {
let (x2, y2) = transform_upper.xy(ni, nj);
let dist: f32 = distances::distance_km(x1, y1, x2, y2, is_wgs);
let w = (1.0 - factor) * t + factor;
self.add_node(
(uu, v),
EdgeData {
adj_dist: w * dist,
geo_dist: dist,
condition: z,
pa: pa_v,
num_cells: c,
similarities: s.clone(),
},
);
}
}
}
}
}
pub fn get_edge_neighbours(
i: i32,
j: i32,
level: i32,
offsets: (usize, usize),
) -> ([(i32, i32); 4], usize) {
let (tile_row0_u, tile_col0_u) = offsets;
let tile_row0 = tile_row0_u as i32;
let tile_col0 = tile_col0_u as i32;
#[inline]
fn to_higher_local(idx: i32, level: i32, tile0: i32) -> i32 {
let curr_origin = tile0.div_euclid(level);
let higher_origin = tile0.div_euclid(level * 2);
(curr_origin + idx).div_euclid(2) - higher_origin
}
let target_higher = (
to_higher_local(i, level, tile_row0),
to_higher_local(j, level, tile_col0),
);
const OFFSETS: [(i32, i32); 8] = [
(-1, 0),
(1, 0),
(0, -1),
(0, 1),
(-1, -1),
(-1, 1),
(1, -1),
(1, 1),
];
let mut higher_cells = [(0, 0); 4];
let mut count = 1;
higher_cells[0] = target_higher;
for (di, dj) in OFFSETS {
let ni = i + di;
let nj = j + dj;
let higher = (
to_higher_local(ni, level, tile_row0),
to_higher_local(nj, level, tile_col0),
);
if higher == target_higher {
continue;
}
let mut found = false;
for k in 0..count {
if higher_cells[k] == higher {
found = true;
break;
}
}
if !found && count < higher_cells.len() {
higher_cells[count] = higher;
count += 1;
}
}
(higher_cells, count)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn includes_parent_cell() {
let (cells, count) = get_edge_neighbours(1, 1, 1, (0, 0));
let cells = &cells[..count];
assert_eq!(count, 4);
assert!(cells.contains(&(0, 0)));
assert!(cells.contains(&(1, 0)));
assert!(cells.contains(&(0, 1)));
assert!(cells.contains(&(1, 1)));
}
}