Various fixes
This commit is contained in:
parent
34a4d0ba86
commit
55598aaaa0
14 changed files with 1250 additions and 130 deletions
|
|
@ -1,6 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::Query;
|
||||
use axum::extract::{Path, Query};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::Json;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::{Map, Value};
|
||||
use tracing::info;
|
||||
|
||||
use crate::parsing::{parse_bounds, parse_filters, row_passes_filters};
|
||||
use crate::parsing::{bounds_intersect, parse_bounds, parse_filters, row_passes_filters};
|
||||
use crate::state::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
@ -96,7 +96,7 @@ pub async fn get_postcodes(
|
|||
let filters_str = params.filters.clone();
|
||||
let (parsed_filters, parsed_enum_filters) = parse_filters(
|
||||
params.filters.as_deref(),
|
||||
&state.data.feature_names,
|
||||
&state.feature_name_to_index,
|
||||
&state.data.enum_values,
|
||||
);
|
||||
let num_filters = parsed_filters.len() + parsed_enum_filters.len();
|
||||
|
|
@ -113,11 +113,7 @@ pub async fn get_postcodes(
|
|||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
state
|
||||
.data
|
||||
.feature_names
|
||||
.iter()
|
||||
.position(|feat| feat == name)
|
||||
state.feature_name_to_index.get(name).copied()
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
|
@ -134,12 +130,6 @@ pub async fn get_postcodes(
|
|||
let has_selective = field_indices.is_some();
|
||||
let sel_indices = field_indices.as_deref().unwrap_or(&[]);
|
||||
|
||||
// Step 1: Find postcodes within bounds using spatial grid on centroids
|
||||
let postcode_indices: Vec<u32> = postcode_data.grid.query(south, west, north, east);
|
||||
|
||||
// Step 2: For each postcode, aggregate properties
|
||||
let mut postcode_aggs: FxHashMap<usize, PostcodeAgg> = FxHashMap::default();
|
||||
|
||||
// Build postcode -> rows mapping by iterating properties in bounds
|
||||
// and grouping by their postcode
|
||||
let mut postcode_rows: FxHashMap<usize, Vec<usize>> = FxHashMap::default();
|
||||
|
|
@ -165,24 +155,23 @@ pub async fn get_postcodes(
|
|||
}
|
||||
});
|
||||
|
||||
// Now aggregate for each postcode that's in bounds and has properties
|
||||
for &pc_idx in &postcode_indices {
|
||||
let idx = pc_idx as usize;
|
||||
if let Some(rows) = postcode_rows.get(&idx) {
|
||||
let agg = postcode_aggs
|
||||
.entry(idx)
|
||||
.or_insert_with(|| PostcodeAgg::new(num_features));
|
||||
for &row in rows {
|
||||
if has_selective {
|
||||
agg.add_row_selective(feature_data, row, num_features, sel_indices);
|
||||
} else {
|
||||
agg.add_row(feature_data, row, num_features);
|
||||
}
|
||||
// Aggregate for each postcode that has properties in bounds
|
||||
// (polygon intersection check happens later when building response)
|
||||
let mut postcode_aggs: FxHashMap<usize, PostcodeAgg> = FxHashMap::default();
|
||||
for (&pc_idx, rows) in &postcode_rows {
|
||||
let agg = postcode_aggs
|
||||
.entry(pc_idx)
|
||||
.or_insert_with(|| PostcodeAgg::new(num_features));
|
||||
for &row in rows {
|
||||
if has_selective {
|
||||
agg.add_row_selective(feature_data, row, num_features, sel_indices);
|
||||
} else {
|
||||
agg.add_row(feature_data, row, num_features);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build response
|
||||
// Build response, filtering postcodes to only those whose polygon intersects query bounds
|
||||
let mut features = Vec::with_capacity(postcode_aggs.len());
|
||||
|
||||
for (pc_idx, aggregation) in postcode_aggs {
|
||||
|
|
@ -190,6 +179,23 @@ pub async fn get_postcodes(
|
|||
continue;
|
||||
}
|
||||
|
||||
// Compute postcode polygon bounding box and check intersection with query bounds
|
||||
let vertices = &postcode_data.vertices[pc_idx];
|
||||
let (mut pc_south, mut pc_north) = (f64::INFINITY, f64::NEG_INFINITY);
|
||||
let (mut pc_west, mut pc_east) = (f64::INFINITY, f64::NEG_INFINITY);
|
||||
for &[lon, lat] in vertices {
|
||||
let lon_f = lon as f64;
|
||||
let lat_f = lat as f64;
|
||||
if lat_f < pc_south { pc_south = lat_f; }
|
||||
if lat_f > pc_north { pc_north = lat_f; }
|
||||
if lon_f < pc_west { pc_west = lon_f; }
|
||||
if lon_f > pc_east { pc_east = lon_f; }
|
||||
}
|
||||
|
||||
if !bounds_intersect(pc_south, pc_west, pc_north, pc_east, south, west, north, east) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut map = Map::new();
|
||||
map.insert(
|
||||
"postcode".into(),
|
||||
|
|
@ -198,7 +204,7 @@ pub async fn get_postcodes(
|
|||
map.insert("count".into(), Value::Number(aggregation.count.into()));
|
||||
|
||||
// Add vertices as array of [lon, lat] pairs
|
||||
let vertices_array: Vec<Value> = postcode_data.vertices[pc_idx]
|
||||
let vertices_array: Vec<Value> = vertices
|
||||
.iter()
|
||||
.map(|[lon, lat]| Value::Array(vec![Value::from(*lon as f64), Value::from(*lat as f64)]))
|
||||
.collect();
|
||||
|
|
@ -244,3 +250,44 @@ pub async fn get_postcodes(
|
|||
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PostcodeLookupResponse {
|
||||
pub postcode: String,
|
||||
pub latitude: f64,
|
||||
pub longitude: f64,
|
||||
/// Polygon vertices as [[lon, lat], ...] for rendering highlight
|
||||
pub vertices: Vec<[f64; 2]>,
|
||||
}
|
||||
|
||||
/// Look up a single postcode and return its centroid coordinates and polygon.
|
||||
pub async fn get_postcode_lookup(
|
||||
state: Arc<AppState>,
|
||||
Path(postcode): Path<String>,
|
||||
) -> Result<Json<PostcodeLookupResponse>, StatusCode> {
|
||||
// Normalize the postcode: uppercase, remove extra spaces, ensure single space
|
||||
let normalized = postcode
|
||||
.to_uppercase()
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
|
||||
let postcode_data = &state.postcode_data;
|
||||
|
||||
if let Some(&idx) = postcode_data.postcode_to_idx.get(&normalized) {
|
||||
let (lat, lon) = postcode_data.centroids[idx];
|
||||
let vertices: Vec<[f64; 2]> = postcode_data.vertices[idx]
|
||||
.iter()
|
||||
.map(|[lo, la]| [*lo as f64, *la as f64])
|
||||
.collect();
|
||||
info!(postcode = %normalized, "GET /api/postcode/{postcode}");
|
||||
Ok(Json(PostcodeLookupResponse {
|
||||
postcode: normalized,
|
||||
latitude: lat as f64,
|
||||
longitude: lon as f64,
|
||||
vertices,
|
||||
}))
|
||||
} else {
|
||||
Err(StatusCode::NOT_FOUND)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue