Add postcodes

This commit is contained in:
Andras Schmelczer 2026-02-04 21:35:12 +00:00
parent 004948385d
commit ce4c0cc08c
5 changed files with 325 additions and 1 deletions

View file

@ -1,6 +1,7 @@
mod consts;
mod data;
mod features;
mod metrics;
mod og_middleware;
pub mod parsing;
mod routes;
@ -35,6 +36,14 @@ struct Cli {
#[arg(long)]
pois: PathBuf,
/// Path to the postcode boundaries directory
#[arg(long)]
postcodes: PathBuf,
/// Path to the PMTiles file for map tiles
#[arg(long)]
tiles: PathBuf,
/// Path to the frontend dist directory
#[arg(long)]
dist: Option<PathBuf>,
@ -61,6 +70,10 @@ async fn main() -> anyhow::Result<()> {
.with_ansi(true)
.init();
// Initialize Prometheus metrics
let metrics_handle = metrics::init_metrics();
info!("Prometheus metrics initialized");
let cli = Cli::parse();
let parquet_path = &cli.data;
@ -107,6 +120,33 @@ async fn main() -> anyhow::Result<()> {
let poi_grid =
utils::GridIndex::build(&poi_data.lat, &poi_data.lng, consts::GRID_CELL_SIZE);
// Load postcode boundaries
let postcodes_path = &cli.postcodes;
if !postcodes_path.exists() {
bail!(
"Postcode boundaries not found: {}",
postcodes_path.display()
);
}
info!(
"Loading postcode boundaries from {}",
postcodes_path.display()
);
let postcode_data = data::PostcodeData::load(postcodes_path)?;
info!(
postcodes = postcode_data.postcodes.len(),
"Postcode boundaries loaded"
);
// Initialize tile reader
let tiles_path = &cli.tiles;
if !tiles_path.exists() {
bail!("PMTiles file not found: {}", tiles_path.display());
}
info!("Loading PMTiles from {}", tiles_path.display());
let tile_reader = Arc::new(routes::init_tile_reader(tiles_path).await?);
info!("PMTiles loaded successfully");
let min_keys: Vec<String> = property_data
.feature_names
.iter()
@ -165,12 +205,20 @@ async fn main() -> anyhow::Result<()> {
"Precomputed features response"
);
// Record data loading metrics
metrics::record_data_stats(
property_data.lat.len(),
poi_data.lat.len(),
postcode_data.postcodes.len(),
);
let state = Arc::new(AppState {
data: property_data,
grid,
h3_cells,
poi_data,
poi_grid,
postcode_data,
min_keys,
max_keys,
poi_category_groups,
@ -188,6 +236,7 @@ async fn main() -> anyhow::Result<()> {
let state_features = state.clone();
let state_hexagons = state.clone();
let state_postcodes = state.clone();
let state_pois = state.clone();
let state_poi_categories = state.clone();
let state_hexagon_properties = state.clone();
@ -204,6 +253,10 @@ async fn main() -> anyhow::Result<()> {
"/api/hexagons",
get(move |query| routes::get_hexagons(state_hexagons.clone(), query)),
)
.route(
"/api/postcodes",
get(move |query| routes::get_postcodes(state_postcodes.clone(), query)),
)
.route(
"/api/pois",
get(move |query| routes::get_pois(state_pois.clone(), query)),
@ -227,6 +280,22 @@ async fn main() -> anyhow::Result<()> {
get(move |query| routes::get_og_image(state_og_image.clone(), query)),
);
// Add tile routes
let reader_tile = tile_reader.clone();
let reader_style = tile_reader.clone();
let api = api
.route(
"/api/tiles/{z}/{x}/{y}",
get(move |path| routes::get_tile(axum::extract::State(reader_tile.clone()), path)),
)
.route(
"/api/tiles/style.json",
get(move |headers, query| {
routes::get_style(axum::extract::State(reader_style.clone()), headers, query)
}),
)
.route("/metrics", get(move || metrics::metrics_handler(metrics_handle.clone())));
let app = if frontend_dist.exists() {
api.fallback_service(ServeDir::new(&frontend_dist))
} else {
@ -234,6 +303,7 @@ async fn main() -> anyhow::Result<()> {
};
let app = app
.layer(middleware::from_fn(metrics::track_metrics))
.layer(middleware::from_fn(
move |req: axum::extract::Request, next: middleware::Next| {
let st = state_crawler.clone();