diff --git a/server/algorithms/src/bootstrap/s2.rs b/server/algorithms/src/bootstrap/s2.rs index b4b2f7a4..3279801e 100644 --- a/server/algorithms/src/bootstrap/s2.rs +++ b/server/algorithms/src/bootstrap/s2.rs @@ -1,7 +1,11 @@ #![allow(dead_code, unused)] use std::{fmt::Display, time::Instant}; -use crate::{routing, rtree, s2::ToPointArray, stats::Stats}; +use crate::{ + routing, rtree, + s2::{BuildGrid, Dir, ToPointArray, Traverse}, + stats::Stats, +}; use geo::{ConcaveHull, ConvexHull, Intersects, MultiPolygon, Polygon, Simplify}; use geojson::{Feature, Value}; @@ -11,74 +15,11 @@ use model::{ db::sea_orm_active_enums::Type, }; use rayon::{ - iter::IntoParallelIterator, + iter::{Either, IntoParallelIterator}, prelude::{IntoParallelRefIterator, ParallelIterator}, }; use s2::{cell::Cell, cellid::CellID, rect::Rect, region::RegionCoverer}; -enum Dir { - N, - E, - S, - W, -} - -impl Display for Dir { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Dir::N => "North", - Dir::E => "East", - Dir::S => "South", - Dir::W => "West", - } - ) - } -} -trait Traverse { - fn traverse(self, dir: Dir, count: u8) -> Self; - fn traverse_mut(&mut self, dir: Dir, count: u8); -} - -impl Traverse for CellID { - fn traverse(self, dir: Dir, count: u8) -> Self { - let mut new_cell = self; - new_cell.traverse_mut(dir, count); - new_cell - } - - fn traverse_mut(&mut self, dir: Dir, count: u8) { - for _ in 0..count { - let [lat, lng] = self.point_array(); - let neighbors = self.edge_neighbors(); - let mut direction = 0; - let mut largest = match dir { - Dir::E | Dir::W => lng, - Dir::N | Dir::S => lat, - }; - - for (j, neighbor) in neighbors.iter().enumerate() { - let [next_lat, next_lng] = neighbor.point_array(); - if match dir { - Dir::N => largest < next_lat, - Dir::S => largest > next_lat, - Dir::E => largest < next_lng, - Dir::W => largest > next_lng, - } { - largest = match dir { - Dir::E | Dir::W => next_lng, - Dir::N | Dir::S => next_lat, - }; - direction = j; - } - } - *self = neighbors[direction]; - } - } -} - #[derive(Debug)] pub struct BootstrapS2<'a> { feature: &'a Feature, @@ -138,17 +79,6 @@ impl<'a> BootstrapS2<'a> { new_feature } - fn get_neighbors(&self, cell: CellID, iteration: u8) -> Vec { - if iteration <= 1 { - cell.all_neighbors(self.level) - } else { - cell.all_neighbors(self.level) - .into_iter() - .flat_map(|neighbor| self.get_neighbors(neighbor, iteration - 2)) - .collect() - } - } - fn build_polygons(&self) -> Vec { if let Some(geometry) = self.feature.geometry.as_ref() { match geometry.value { @@ -245,8 +175,26 @@ impl<'a> BootstrapS2<'a> { let cell_grids = if self.size == 1 { cells.0 } else { - let lo = CellID::from(region.lo()).parent(self.level); - let hi = CellID::from(region.hi()).parent(self.level); + let origin_low = CellID::from(region.lo()).parent(self.level); + let lo = CellID::from(region.lo()) + .parent(self.level) + .traverse(Dir::S, self.size) + .traverse(Dir::W, self.size); + if origin_low == lo { + log::info!("origin: {} | lo: {}", origin_low.face(), lo.face()); + } else { + log::error!("origin: {} | lo: {}", origin_low.face(), lo.face()); + } + let origin_hi = CellID::from(region.hi()).parent(self.level); + let hi = CellID::from(region.hi()) + .parent(self.level) + .traverse(Dir::N, self.size) + .traverse(Dir::E, self.size); + if origin_hi == hi { + log::info!("origin: {} | hi: {}", origin_hi.face(), hi.face()); + } else { + log::error!("origin: {} | hi: {}", origin_hi.face(), hi.face()); + } let mut cell_grids = vec![]; @@ -270,8 +218,8 @@ impl<'a> BootstrapS2<'a> { let time = Instant::now(); traversing += 1; current_west.traverse_mut(Dir::W, self.size); - if self - .get_neighbors(current_west, self.size - 2) + if current_west + .build_grid(self.size) .into_par_iter() .find_any(|cell| cells.contains_cellid(cell)) .is_none() @@ -288,7 +236,6 @@ impl<'a> BootstrapS2<'a> { let time = Instant::now(); [current_north, current_east] = current_lng.point_array(); traversing += 1; - // let cell_grid = self.get_neighbors(current_lng, self.size - 2); cell_grids.push(current_lng); current_lng.traverse_mut(Dir::E, self.size); log::debug!("traversing E: {traversing}"); @@ -324,11 +271,11 @@ impl<'a> BootstrapS2<'a> { let mut cell_grids = cell_grids .into_par_iter() .filter_map(|cell| { - // return Some(self.find_center_cell(&grid).point_array()); + // return Some(self.find_center_cell(&vec![cell]).point_array()); let grid = if self.size == 1 { vec![cell] } else { - self.get_neighbors(cell, self.size - 2) + cell.build_grid(self.size) }; let grid_poly = self.get_grid_polygon(&grid); if polygons @@ -353,8 +300,6 @@ impl<'a> BootstrapS2<'a> { cell_grids.len() ); // cell_grids.clear(); - // cell_grids.push(lo.point_array()); - // cell_grids.push(hi.point_array()); cell_grids } } diff --git a/server/algorithms/src/s2.rs b/server/algorithms/src/s2.rs index 8c9109c7..df54516f 100644 --- a/server/algorithms/src/s2.rs +++ b/server/algorithms/src/s2.rs @@ -1,5 +1,6 @@ use std::{ collections::{HashMap, HashSet}, + fmt::Display, sync::{Arc, Mutex}, }; @@ -84,6 +85,154 @@ impl ToGeoJson for CellID { } } +pub enum Dir { + N, + E, + S, + W, +} + +impl Display for Dir { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Dir::N => "North", + Dir::E => "East", + Dir::S => "South", + Dir::W => "West", + } + ) + } +} +pub trait Traverse { + fn traverse(self, dir: Dir, count: u8) -> Self; + fn traverse_mut(&mut self, dir: Dir, count: u8); +} + +impl Traverse for CellID { + fn traverse(self, dir: Dir, count: u8) -> Self { + let mut new_cell = self; + new_cell.traverse_mut(dir, count); + new_cell + } + + fn traverse_mut(&mut self, dir: Dir, count: u8) { + for _ in 0..count { + let neighbors = self.edge_neighbors(); + let lng_match = self.point_array()[1] / 45.; + let lng_match = if lng_match < -3. { + lng_match.ceil() as i8 + } else { + lng_match.floor() as i8 + }; + *self = match self.face() { + 0 | 1 => match dir { + Dir::N => neighbors[2], + Dir::E => neighbors[1], + Dir::S => neighbors[0], + Dir::W => neighbors[3], + }, + 2 => match lng_match { + -1 | 0 => match dir { + Dir::N => neighbors[1], + Dir::E => neighbors[0], + Dir::S => neighbors[3], + Dir::W => neighbors[2], + }, + 1 | 2 => match dir { + Dir::N => neighbors[2], + Dir::E => neighbors[1], + Dir::S => neighbors[0], + Dir::W => neighbors[3], + }, + -2 => match dir { + Dir::N => neighbors[0], + Dir::E => neighbors[3], + Dir::S => neighbors[2], + Dir::W => neighbors[1], + }, + -3 | 3 => match dir { + Dir::N => neighbors[3], + Dir::E => neighbors[2], + Dir::S => neighbors[1], + Dir::W => neighbors[0], + }, + _ => { + log::error!("invalid on face 2: {}", lng_match); + panic!() + } + }, + 3 | 4 => match dir { + Dir::N => neighbors[3], + Dir::E => neighbors[2], + Dir::S => neighbors[1], + Dir::W => neighbors[0], + }, + 5 => match lng_match { + -1 | 0 => match dir { + Dir::N => neighbors[2], + Dir::E => neighbors[1], + Dir::S => neighbors[0], + Dir::W => neighbors[3], + }, + 1 | 2 => match dir { + Dir::N => neighbors[1], + Dir::E => neighbors[0], + Dir::S => neighbors[3], + Dir::W => neighbors[2], + }, + -2 => match dir { + Dir::N => neighbors[3], + Dir::E => neighbors[2], + Dir::S => neighbors[1], + Dir::W => neighbors[0], + }, + -3 | 3 => match dir { + Dir::N => neighbors[0], + Dir::E => neighbors[3], + Dir::S => neighbors[2], + Dir::W => neighbors[1], + }, + _ => { + log::error!("invalid on face 5: {}", lng_match); + panic!() + } + }, + _ => { + log::error!("invalid face: {}", self.face()); + panic!() + } + }; + } + } +} + +pub trait BuildGrid { + fn build_grid(&self, size: u8) -> Vec; +} + +impl BuildGrid for CellID { + fn build_grid(&self, size: u8) -> Vec { + let get_to_start = ((size as f32 / 2.).floor() as u8) + 1; + let mut starting_cell = self + .traverse(Dir::W, get_to_start) + .traverse(Dir::S, get_to_start); + let mut neighbors = vec![]; + for _ in 0..size { + let mut h_cell = starting_cell; + neighbors.push(h_cell); + for _ in 1..size { + h_cell.traverse_mut(Dir::E, 1); + neighbors.push(h_cell); + } + starting_cell.traverse_mut(Dir::N, 1); + } + neighbors + } +} + pub fn get_region_cells( min_lat: f64, max_lat: f64, @@ -251,25 +400,21 @@ fn check_neighbors(lat: f64, lon: f64, level: u8, circle: &geo::Polygon, covered pub fn cell_coverage(lat: f64, lon: f64, size: u8, level: u8) -> Covered { let covered = Arc::new(Mutex::new(HashSet::new())); - let mut center = CellID::from(s2::latlng::LatLng::from_degrees(lat, lon)).parent(level as u64); + let center = CellID::from(s2::latlng::LatLng::from_degrees(lat, lon)).parent(level as u64); + if size == 1 { covered.lock().unwrap().insert(center.0); } else { - for i in 0..((size / 2) + 1) { - if i != 0 { - if if size % 2 == 0 { i != 1 } else { true } { - center = center.edge_neighbors()[2]; + let neighbors = center.build_grid(size); + for neighbor in neighbors { + match covered.lock() { + Ok(mut c) => { + c.insert(neighbor.0); } - } - center = center.edge_neighbors()[3]; - } - for _ in 0..size { - center = center.edge_neighbors()[1]; - let mut h_cell_id = center.clone(); - for _ in 0..size { - covered.lock().unwrap().insert(h_cell_id.0); - h_cell_id = h_cell_id.edge_neighbors()[0]; - } + Err(e) => { + log::error!("[S2] Error locking `covered` to insert: {}", e) + } + }; } } covered