Format rust
This commit is contained in:
parent
0fde087c3d
commit
f60fbec9d4
5 changed files with 191 additions and 94 deletions
|
|
@ -8,7 +8,8 @@ use axum::response::{IntoResponse, Json};
|
|||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::data::{Histogram, PropertyData, POIData, POI, DEFAULT_RESOLUTION, MAX_RESOLUTION, MIN_RESOLUTION};
|
||||
use crate::consts::{H3_PRECOMPUTE_MAX, H3_PRECOMPUTE_MIN};
|
||||
use crate::data::{Histogram, POIData, PropertyData, POI};
|
||||
use crate::index::GridIndex;
|
||||
|
||||
/// Shared application state
|
||||
|
|
@ -82,7 +83,7 @@ pub async fn get_features(state: Arc<AppState>) -> Json<FeaturesResponse> {
|
|||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HexagonParams {
|
||||
resolution: Option<u8>,
|
||||
resolution: u8,
|
||||
bounds: Option<String>,
|
||||
/// Comma-separated filters: `name:min:max,...`
|
||||
/// Rows must have non-NaN values within [min,max] for each filter.
|
||||
|
|
@ -130,7 +131,6 @@ impl CellAgg {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Write the hexagons JSON response directly to a String buffer,
|
||||
|
|
@ -172,20 +172,21 @@ pub async fn get_hexagons(
|
|||
state: Arc<AppState>,
|
||||
Query(params): Query<HexagonParams>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||
let resolution = params.resolution.unwrap_or(DEFAULT_RESOLUTION);
|
||||
if resolution > MAX_RESOLUTION {
|
||||
let resolution = params.resolution;
|
||||
if resolution < H3_PRECOMPUTE_MIN || resolution > H3_PRECOMPUTE_MAX {
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!(
|
||||
"resolution must be between {} and {}",
|
||||
MIN_RESOLUTION, MAX_RESOLUTION
|
||||
H3_PRECOMPUTE_MIN, H3_PRECOMPUTE_MAX
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let bounds_str = params
|
||||
.bounds
|
||||
.ok_or((StatusCode::BAD_REQUEST, "bounds parameter is required".into()))?;
|
||||
let bounds_str = params.bounds.ok_or((
|
||||
StatusCode::BAD_REQUEST,
|
||||
"bounds parameter is required".into(),
|
||||
))?;
|
||||
|
||||
let parts: Vec<f64> = bounds_str
|
||||
.split(',')
|
||||
|
|
@ -286,46 +287,44 @@ pub async fn get_hexagons(
|
|||
|
||||
if let Some(precomputed) = h3_cells_for_res {
|
||||
// Fast path: precomputed H3 + visitor pattern
|
||||
state.grid.for_each_in_bounds(south, west, north, east, |row_idx| {
|
||||
let row = row_idx as usize;
|
||||
if !row_passes(row) {
|
||||
return;
|
||||
}
|
||||
let cell_id = precomputed[row];
|
||||
groups
|
||||
.entry(cell_id)
|
||||
.or_insert_with(|| CellAgg::new(num_features))
|
||||
.add_row(feature_data, row, num_features);
|
||||
});
|
||||
state
|
||||
.grid
|
||||
.for_each_in_bounds(south, west, north, east, |row_idx| {
|
||||
let row = row_idx as usize;
|
||||
if !row_passes(row) {
|
||||
return;
|
||||
}
|
||||
let cell_id = precomputed[row];
|
||||
groups
|
||||
.entry(cell_id)
|
||||
.or_insert_with(|| CellAgg::new(num_features))
|
||||
.add_row(feature_data, row, num_features);
|
||||
});
|
||||
} else {
|
||||
// Fallback: compute H3 on-the-fly
|
||||
let h3_res = h3o::Resolution::try_from(resolution).unwrap();
|
||||
state.grid.for_each_in_bounds(south, west, north, east, |row_idx| {
|
||||
let row = row_idx as usize;
|
||||
if !row_passes(row) {
|
||||
return;
|
||||
}
|
||||
let cell_id = h3o::LatLng::new(state.data.lat[row], state.data.lon[row])
|
||||
.map(|c| u64::from(c.to_cell(h3_res)))
|
||||
.unwrap_or(0);
|
||||
groups
|
||||
.entry(cell_id)
|
||||
.or_insert_with(|| CellAgg::new(num_features))
|
||||
.add_row(feature_data, row, num_features);
|
||||
});
|
||||
state
|
||||
.grid
|
||||
.for_each_in_bounds(south, west, north, east, |row_idx| {
|
||||
let row = row_idx as usize;
|
||||
if !row_passes(row) {
|
||||
return;
|
||||
}
|
||||
let cell_id = h3o::LatLng::new(state.data.lat[row], state.data.lon[row])
|
||||
.map(|c| u64::from(c.to_cell(h3_res)))
|
||||
.unwrap_or(0);
|
||||
groups
|
||||
.entry(cell_id)
|
||||
.or_insert_with(|| CellAgg::new(num_features))
|
||||
.add_row(feature_data, row, num_features);
|
||||
});
|
||||
}
|
||||
|
||||
let t_agg = t0.elapsed();
|
||||
|
||||
// Write JSON directly (no serde_json::Value allocation overhead)
|
||||
let mut json_buf = String::with_capacity(groups.len() * 128);
|
||||
write_hexagons_json(
|
||||
&mut json_buf,
|
||||
&groups,
|
||||
&min_keys,
|
||||
&max_keys,
|
||||
num_features,
|
||||
);
|
||||
write_hexagons_json(&mut json_buf, &groups, &min_keys, &max_keys, num_features);
|
||||
|
||||
let t_total = t0.elapsed();
|
||||
eprintln!(
|
||||
|
|
@ -364,9 +363,10 @@ pub async fn get_pois(
|
|||
state: Arc<AppState>,
|
||||
Query(params): Query<POIParams>,
|
||||
) -> Result<Json<POIsResponse>, (StatusCode, String)> {
|
||||
let bounds_str = params
|
||||
.bounds
|
||||
.ok_or((StatusCode::BAD_REQUEST, "bounds parameter is required".into()))?;
|
||||
let bounds_str = params.bounds.ok_or((
|
||||
StatusCode::BAD_REQUEST,
|
||||
"bounds parameter is required".into(),
|
||||
))?;
|
||||
|
||||
let parts: Vec<f64> = bounds_str
|
||||
.split(',')
|
||||
|
|
@ -501,7 +501,12 @@ pub struct HexagonPropertiesResponse {
|
|||
}
|
||||
|
||||
/// Helper function to check if a row passes all filters
|
||||
fn row_passes_filters(row: usize, filters: &[ParsedFilter], feature_data: &[f64], num_features: usize) -> bool {
|
||||
fn row_passes_filters(
|
||||
row: usize,
|
||||
filters: &[ParsedFilter],
|
||||
feature_data: &[f64],
|
||||
num_features: usize,
|
||||
) -> bool {
|
||||
filters.iter().all(|f| {
|
||||
let v = feature_data[row * num_features + f.feat_idx];
|
||||
v.is_finite() && v >= f.min && v <= f.max
|
||||
|
|
@ -520,7 +525,10 @@ pub async fn get_hexagon_properties(
|
|||
// 2. Validate resolution
|
||||
let resolution = params.resolution as usize;
|
||||
if resolution >= state.h3_cells.len() || state.h3_cells[resolution].is_empty() {
|
||||
return Err((StatusCode::BAD_REQUEST, "Invalid or non-precomputed resolution".to_string()));
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
"Invalid or non-precomputed resolution".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// 3. Parse filters (reuse existing filter parsing logic from get_hexagons)
|
||||
|
|
@ -592,7 +600,11 @@ pub async fn get_hexagon_properties(
|
|||
|
||||
// Helper to get non-empty string
|
||||
let get_string = |s: &str| -> Option<String> {
|
||||
if s.is_empty() { None } else { Some(s.to_string()) }
|
||||
if s.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(s.to_string())
|
||||
}
|
||||
};
|
||||
|
||||
Property {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue