70 lines
2.2 KiB
Rust
70 lines
2.2 KiB
Rust
use std::sync::Arc;
|
|
|
|
use axum::extract::{Query, State};
|
|
use axum::response::{IntoResponse, Json, Response};
|
|
use axum::Extension;
|
|
use serde::{Deserialize, Serialize};
|
|
use tracing::info;
|
|
|
|
use crate::auth::OptionalUser;
|
|
use crate::data::DevelopmentSite;
|
|
use crate::licensing::{check_license_bounds, resolve_share_code};
|
|
use crate::parsing::require_bounds;
|
|
use crate::state::SharedState;
|
|
|
|
/// Hard cap on sites returned per viewport. Biggest schemes (by dwelling count)
|
|
/// are kept when a dense viewport exceeds this; `truncated` flags the clamp.
|
|
const DEVELOPMENTS_LIMIT: usize = 4000;
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct DevelopmentsParams {
|
|
bounds: Option<String>,
|
|
/// Share-link code; grants bbox-scoped access for unlicensed users.
|
|
share: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub struct DevelopmentsResponse {
|
|
pub developments: Vec<DevelopmentSite>,
|
|
pub total: usize,
|
|
pub truncated: bool,
|
|
}
|
|
|
|
/// Forward-looking "where new homes are coming" layer: planned/pipeline
|
|
/// development sites (MHCLG Brownfield Land register + Homes England Land Hub),
|
|
/// served as points within a viewport. Public OGL data, but still gated by the
|
|
/// normal demo/licence bounds check so unlicensed users only see their free zone.
|
|
pub async fn get_developments(
|
|
State(shared): State<Arc<SharedState>>,
|
|
Extension(user): Extension<OptionalUser>,
|
|
Extension(geo): Extension<crate::demo_zone::DemoZone>,
|
|
Query(params): Query<DevelopmentsParams>,
|
|
) -> Result<Json<DevelopmentsResponse>, Response> {
|
|
let state = shared.load_state();
|
|
|
|
let (south, west, north, east) =
|
|
require_bounds(params.bounds).map_err(IntoResponse::into_response)?;
|
|
|
|
let share_bounds = resolve_share_code(&state, params.share.as_deref()).await;
|
|
check_license_bounds(
|
|
&user.0,
|
|
(south, west, north, east),
|
|
geo.free_zone,
|
|
share_bounds,
|
|
)?;
|
|
|
|
let developments = state.developments.clone();
|
|
let (sites, total, truncated) =
|
|
developments.query_bounds(south, west, north, east, DEVELOPMENTS_LIMIT);
|
|
|
|
info!(
|
|
results = sites.len(),
|
|
total, truncated, "GET /api/developments"
|
|
);
|
|
|
|
Ok(Json(DevelopmentsResponse {
|
|
developments: sites,
|
|
total,
|
|
truncated,
|
|
}))
|
|
}
|