Refactor and improve

This commit is contained in:
Andras Schmelczer 2026-02-03 20:26:57 +00:00
parent 1f148b2185
commit 242acff987
22 changed files with 754 additions and 1053 deletions

View file

@ -2,15 +2,31 @@ use std::sync::Arc;
use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Json};
use axum::response::Json;
use serde::{Deserialize, Serialize};
use tracing::info;
use crate::consts::MAX_POIS_PER_REQUEST;
use crate::state::{AppState, POICategoryGroup};
use crate::data::POICategoryGroup;
use crate::parsing::parse_bounds;
use crate::state::AppState;
use super::hexagons::write_json_escaped;
use super::parse::parse_bounds;
#[derive(Serialize)]
#[allow(clippy::upper_case_acronyms)]
pub struct POI {
id: String,
name: String,
category: String,
group: String,
lat: f32,
lng: f32,
emoji: String,
}
#[derive(Serialize)]
pub struct POIsResponse {
pois: Vec<POI>,
}
#[derive(Deserialize)]
pub struct POIParams {
@ -22,7 +38,7 @@ pub struct POIParams {
pub async fn get_pois(
state: Arc<AppState>,
Query(params): Query<POIParams>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
) -> Result<Json<POIsResponse>, (StatusCode, String)> {
let bounds_str = params.bounds.ok_or((
StatusCode::BAD_REQUEST,
"bounds parameter is required".into(),
@ -43,12 +59,10 @@ pub async fn get_pois(
let num_categories = category_filter.as_ref().map(|cats| cats.len()).unwrap_or(0);
let json_body = tokio::task::spawn_blocking(move || {
let pois = tokio::task::spawn_blocking(move || {
let t0 = std::time::Instant::now();
let row_indices = state.poi_grid.query(south, west, north, east);
// Collect matching row indices first, then sample randomly so the
// subset covers the viewport uniformly instead of clustering in one area.
let mut matching_rows: Vec<usize> = row_indices
.iter()
.filter_map(|&row_idx| {
@ -73,36 +87,22 @@ pub async fn get_pois(
}
}
// Write JSON directly to string buffer, avoiding intermediate POI allocations
let mut buf = String::with_capacity(matching_rows.len() * 128);
buf.push_str("{\"pois\":[");
for (i, &row) in matching_rows.iter().enumerate() {
if i > 0 {
buf.push(',');
}
buf.push_str("{\"id\":\"");
write_json_escaped(&mut buf, &state.poi_data.id[row]);
buf.push_str("\",\"name\":\"");
write_json_escaped(&mut buf, &state.poi_data.name[row]);
buf.push_str("\",\"category\":\"");
write_json_escaped(&mut buf, state.poi_data.category.get(row));
buf.push_str("\",\"group\":\"");
write_json_escaped(&mut buf, state.poi_data.group.get(row));
buf.push_str("\",\"lat\":");
buf.push_str(&state.poi_data.lat[row].to_string());
buf.push_str(",\"lng\":");
buf.push_str(&state.poi_data.lng[row].to_string());
buf.push_str(",\"emoji\":\"");
write_json_escaped(&mut buf, state.poi_data.emoji.get(row));
buf.push_str("\"}");
}
buf.push_str("]}");
let pois: Vec<POI> = matching_rows
.iter()
.map(|&row| POI {
id: state.poi_data.id[row].clone(),
name: state.poi_data.name[row].clone(),
category: state.poi_data.category.get(row).to_string(),
group: state.poi_data.group.get(row).to_string(),
lat: state.poi_data.lat[row],
lng: state.poi_data.lng[row],
emoji: state.poi_data.emoji.get(row).to_string(),
})
.collect();
let elapsed = t0.elapsed();
info!(
results = matching_rows.len(),
results = pois.len(),
candidates = row_indices.len(),
categories = num_categories,
categories_raw = categories_str.as_deref().unwrap_or("-"),
@ -110,12 +110,12 @@ pub async fn get_pois(
"GET /api/pois"
);
buf
pois
})
.await
.map_err(|error| (StatusCode::INTERNAL_SERVER_ERROR, error.to_string()))?;
Ok(([("content-type", "application/json")], json_body))
Ok(Json(POIsResponse { pois }))
}
#[derive(Serialize)]