Refactor parts of the server
This commit is contained in:
parent
ccb7c8fbd7
commit
47de4e563f
6 changed files with 369 additions and 90 deletions
147
server-rs/src/grid_index.rs
Normal file
147
server-rs/src/grid_index.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/// Grid-based spatial index for fast rectangle queries over property rows.
|
||||
///
|
||||
/// Divides the UK bounding box into cells of ~0.01 degrees (~1km),
|
||||
/// each storing indices of rows whose lat/lon falls within that cell.
|
||||
pub struct GridIndex {
|
||||
min_lat: f64,
|
||||
min_lon: f64,
|
||||
cell_size: f64,
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
/// cells[row * cols + col] = vec of row indices
|
||||
cells: Vec<Vec<u32>>,
|
||||
}
|
||||
|
||||
impl GridIndex {
|
||||
pub fn build(lat: &[f64], lon: &[f64], cell_size: f64) -> Self {
|
||||
let mut min_lat = f64::INFINITY;
|
||||
let mut max_lat = f64::NEG_INFINITY;
|
||||
let mut min_lon = f64::INFINITY;
|
||||
let mut max_lon = f64::NEG_INFINITY;
|
||||
|
||||
for i in 0..lat.len() {
|
||||
if lat[i] < min_lat {
|
||||
min_lat = lat[i];
|
||||
}
|
||||
if lat[i] > max_lat {
|
||||
max_lat = lat[i];
|
||||
}
|
||||
if lon[i] < min_lon {
|
||||
min_lon = lon[i];
|
||||
}
|
||||
if lon[i] > max_lon {
|
||||
max_lon = lon[i];
|
||||
}
|
||||
}
|
||||
|
||||
min_lat -= cell_size;
|
||||
min_lon -= cell_size;
|
||||
max_lat += cell_size;
|
||||
max_lon += cell_size;
|
||||
|
||||
let rows = ((max_lat - min_lat) / cell_size).ceil() as usize + 1;
|
||||
let cols = ((max_lon - min_lon) / cell_size).ceil() as usize + 1;
|
||||
|
||||
tracing::debug!(
|
||||
rows_grid = rows,
|
||||
cols_grid = cols,
|
||||
total_cells = rows * cols,
|
||||
cell_size,
|
||||
"Building grid index"
|
||||
);
|
||||
|
||||
let mut cells: Vec<Vec<u32>> = vec![Vec::new(); rows * cols];
|
||||
|
||||
for i in 0..lat.len() {
|
||||
let grid_row = ((lat[i] - min_lat) / cell_size) as usize;
|
||||
let grid_col = ((lon[i] - min_lon) / cell_size) as usize;
|
||||
let idx = grid_row * cols + grid_col;
|
||||
cells[idx].push(i as u32);
|
||||
}
|
||||
|
||||
tracing::debug!("Grid index built");
|
||||
|
||||
GridIndex {
|
||||
min_lat,
|
||||
min_lon,
|
||||
cell_size,
|
||||
cols,
|
||||
rows,
|
||||
cells,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query(&self, south: f64, west: f64, north: f64, east: f64) -> Vec<u32> {
|
||||
let Some((row_min, row_max, col_min, col_max)) =
|
||||
self.clamp_bounds(south, west, north, east)
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let mut result = Vec::new();
|
||||
for row in row_min..=row_max {
|
||||
let row_start = row * self.cols;
|
||||
for col in col_min..=col_max {
|
||||
result.extend_from_slice(&self.cells[row_start + col]);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn for_each_in_bounds(
|
||||
&self,
|
||||
south: f64,
|
||||
west: f64,
|
||||
north: f64,
|
||||
east: f64,
|
||||
mut f: impl FnMut(u32),
|
||||
) {
|
||||
let Some((row_min, row_max, col_min, col_max)) =
|
||||
self.clamp_bounds(south, west, north, east)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for row in row_min..=row_max {
|
||||
let row_start = row * self.cols;
|
||||
for col in col_min..=col_max {
|
||||
for &row_idx in &self.cells[row_start + col] {
|
||||
f(row_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clamp_bounds(
|
||||
&self,
|
||||
south: f64,
|
||||
west: f64,
|
||||
north: f64,
|
||||
east: f64,
|
||||
) -> Option<(usize, usize, usize, usize)> {
|
||||
let row_min_raw = ((south - self.min_lat) / self.cell_size) as isize;
|
||||
let row_max_raw = ((north - self.min_lat) / self.cell_size) as isize;
|
||||
let col_min_raw = ((west - self.min_lon) / self.cell_size) as isize;
|
||||
let col_max_raw = ((east - self.min_lon) / self.cell_size) as isize;
|
||||
|
||||
let row_min = row_min_raw.max(0) as usize;
|
||||
let row_max_clamped = row_max_raw.min(self.rows as isize - 1);
|
||||
let col_min = col_min_raw.max(0) as usize;
|
||||
let col_max_clamped = col_max_raw.min(self.cols as isize - 1);
|
||||
|
||||
if row_max_clamped < 0 || col_max_clamped < 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let row_max = row_max_clamped as usize;
|
||||
let col_max = col_max_clamped as usize;
|
||||
|
||||
if row_min > row_max || col_min > col_max {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((row_min, row_max, col_min, col_max))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue