All required
This commit is contained in:
parent
44b4e0d72f
commit
df63764a9f
7 changed files with 45 additions and 128 deletions
|
|
@ -21,10 +21,6 @@ pub struct FrontendConfig {
|
|||
pub send_default_pii: bool,
|
||||
}
|
||||
|
||||
pub fn env_nonempty(name: &str) -> Option<String> {
|
||||
std::env::var(name).ok().and_then(nonempty)
|
||||
}
|
||||
|
||||
pub fn nonempty(value: String) -> Option<String> {
|
||||
let trimmed = value.trim();
|
||||
(!trimmed.is_empty()).then(|| trimmed.to_owned())
|
||||
|
|
|
|||
|
|
@ -40,14 +40,6 @@ pub struct CrimeByYearData {
|
|||
}
|
||||
|
||||
impl CrimeByYearData {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
crime_types: Vec::new(),
|
||||
years_by_type: Vec::new(),
|
||||
series_by_postcode: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load(path: &Path) -> anyhow::Result<Self> {
|
||||
run_polars_io(|| Self::load_inner(path))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,29 +251,29 @@ struct Cli {
|
|||
#[arg(long)]
|
||||
tiles: PathBuf,
|
||||
|
||||
/// Optional PMTiles raster basemap for satellite imagery.
|
||||
/// PMTiles raster basemap for satellite imagery.
|
||||
#[arg(long, env = "SATELLITE_TILES")]
|
||||
satellite_tiles: Option<PathBuf>,
|
||||
satellite_tiles: PathBuf,
|
||||
|
||||
/// Optional PMTiles raster overlay for high-resolution EA aerial photography.
|
||||
/// PMTiles raster overlay for high-resolution EA aerial photography.
|
||||
#[arg(long, env = "SATELLITE_HIGHRES_TILES")]
|
||||
satellite_highres_tiles: Option<PathBuf>,
|
||||
satellite_highres_tiles: PathBuf,
|
||||
|
||||
/// Optional PMTiles raster overlay for high-resolution strategic noise.
|
||||
/// PMTiles raster overlay for high-resolution strategic noise.
|
||||
#[arg(long, env = "NOISE_OVERLAY_TILES")]
|
||||
noise_overlay_tiles: Option<PathBuf>,
|
||||
noise_overlay_tiles: PathBuf,
|
||||
|
||||
/// Optional PMTiles vector overlay for crime heatmap points.
|
||||
/// PMTiles vector overlay for crime heatmap points.
|
||||
#[arg(long, env = "CRIME_HOTSPOT_TILES")]
|
||||
crime_hotspot_tiles: Option<PathBuf>,
|
||||
crime_hotspot_tiles: PathBuf,
|
||||
|
||||
/// Optional PMTiles vector overlay for Trees Outside Woodland polygons.
|
||||
/// PMTiles vector overlay for Trees Outside Woodland polygons.
|
||||
#[arg(long, env = "TREE_OVERLAY_TILES")]
|
||||
tree_overlay_tiles: Option<PathBuf>,
|
||||
tree_overlay_tiles: PathBuf,
|
||||
|
||||
/// Optional PMTiles vector overlay for INSPIRE property-border polygons.
|
||||
/// PMTiles vector overlay for INSPIRE property-border polygons.
|
||||
#[arg(long, env = "PROPERTY_BORDER_TILES")]
|
||||
property_border_tiles: Option<PathBuf>,
|
||||
property_border_tiles: PathBuf,
|
||||
|
||||
/// Path to the frontend dist directory (optional; disables static serving and OG injection when omitted)
|
||||
#[arg(long)]
|
||||
|
|
@ -311,13 +311,13 @@ struct Cli {
|
|||
#[arg(long, env = "TRAVEL_TIMES")]
|
||||
travel_times: PathBuf,
|
||||
|
||||
/// Optional path to a parquet of live online listings (Rightmove etc.) to overlay on the map.
|
||||
/// Path to a parquet of live online listings (Rightmove etc.) to overlay on the map.
|
||||
#[arg(long, env = "ACTUAL_LISTINGS_PATH")]
|
||||
actual_listings_path: Option<PathBuf>,
|
||||
actual_listings_path: PathBuf,
|
||||
|
||||
/// Optional path to the per-LSOA per-year crime parquet (display-only side table for the right pane).
|
||||
/// Path to the per-LSOA per-year crime parquet (display-only side table for the right pane).
|
||||
#[arg(long, env = "CRIME_BY_YEAR_PATH")]
|
||||
crime_by_year_path: Option<PathBuf>,
|
||||
crime_by_year_path: PathBuf,
|
||||
|
||||
/// Google Maps API key for Street View metadata lookups
|
||||
#[arg(long, env = "GOOGLE_MAPS_API_KEY")]
|
||||
|
|
@ -345,22 +345,22 @@ struct Cli {
|
|||
|
||||
/// Bugsink DSN for backend error reporting
|
||||
#[arg(long, env = "BUGSINK_DSN", hide_env_values = true)]
|
||||
bugsink_dsn: Option<String>,
|
||||
bugsink_dsn: String,
|
||||
|
||||
/// Bugsink DSN injected into the browser app; falls back to BUGSINK_DSN when omitted
|
||||
/// Bugsink DSN injected into the browser app
|
||||
#[arg(long, env = "FRONTEND_BUGSINK_DSN", hide_env_values = true)]
|
||||
frontend_bugsink_dsn: Option<String>,
|
||||
frontend_bugsink_dsn: String,
|
||||
|
||||
/// Bugsink/Sentry environment name
|
||||
#[arg(long, env = "BUGSINK_ENVIRONMENT")]
|
||||
bugsink_environment: Option<String>,
|
||||
bugsink_environment: String,
|
||||
|
||||
/// Bugsink/Sentry release name
|
||||
#[arg(long, env = "BUGSINK_RELEASE")]
|
||||
bugsink_release: Option<String>,
|
||||
bugsink_release: String,
|
||||
|
||||
/// Include default PII in Bugsink events
|
||||
#[arg(long, env = "BUGSINK_SEND_DEFAULT_PII", default_value_t = false)]
|
||||
#[arg(long, env = "BUGSINK_SEND_DEFAULT_PII", action = clap::ArgAction::Set)]
|
||||
bugsink_send_default_pii: bool,
|
||||
}
|
||||
|
||||
|
|
@ -404,49 +404,19 @@ async fn init_required_tile_reader(
|
|||
Ok(Arc::new(routes::init_tile_reader(path).await?))
|
||||
}
|
||||
|
||||
fn configured_or_default_overlay_path(
|
||||
configured: &Option<PathBuf>,
|
||||
tiles_path: &Path,
|
||||
file_name: &str,
|
||||
) -> PathBuf {
|
||||
if let Some(path) = configured {
|
||||
return path.clone();
|
||||
}
|
||||
|
||||
tiles_path
|
||||
.parent()
|
||||
.map(|parent| parent.join(file_name))
|
||||
.unwrap_or_else(|| PathBuf::from(file_name))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let cli = Cli::parse();
|
||||
let bugsink_environment = cli
|
||||
.bugsink_environment
|
||||
.clone()
|
||||
.or_else(|| bugsink::env_nonempty("SENTRY_ENVIRONMENT"));
|
||||
let bugsink_release = cli
|
||||
.bugsink_release
|
||||
.clone()
|
||||
.or_else(|| bugsink::env_nonempty("SENTRY_RELEASE"));
|
||||
let backend_bugsink_dsn = cli
|
||||
.bugsink_dsn
|
||||
.clone()
|
||||
.or_else(|| bugsink::env_nonempty("SENTRY_DSN"));
|
||||
let _bugsink_guard = bugsink::init_backend(&bugsink::BackendConfig {
|
||||
dsn: backend_bugsink_dsn.clone(),
|
||||
environment: bugsink_environment.clone(),
|
||||
release: bugsink_release.clone(),
|
||||
dsn: Some(cli.bugsink_dsn.clone()),
|
||||
environment: Some(cli.bugsink_environment.clone()),
|
||||
release: Some(cli.bugsink_release.clone()),
|
||||
send_default_pii: cli.bugsink_send_default_pii,
|
||||
});
|
||||
let bugsink_frontend_config = bugsink::frontend_config(
|
||||
cli.frontend_bugsink_dsn
|
||||
.clone()
|
||||
.or_else(|| bugsink::env_nonempty("PUBLIC_BUGSINK_DSN"))
|
||||
.or(backend_bugsink_dsn),
|
||||
bugsink_environment.clone(),
|
||||
bugsink_release.clone(),
|
||||
Some(cli.frontend_bugsink_dsn.clone()),
|
||||
Some(cli.bugsink_environment.clone()),
|
||||
Some(cli.bugsink_release.clone()),
|
||||
cli.bugsink_send_default_pii,
|
||||
);
|
||||
|
||||
|
|
@ -569,44 +539,17 @@ async fn main() -> anyhow::Result<()> {
|
|||
let tile_reader = Arc::new(routes::init_tile_reader(tiles_path).await?);
|
||||
info!("PMTiles loaded successfully");
|
||||
|
||||
let noise_overlay_tiles = configured_or_default_overlay_path(
|
||||
&cli.noise_overlay_tiles,
|
||||
tiles_path,
|
||||
"noise_lden_10m.pmtiles",
|
||||
);
|
||||
let satellite_tiles =
|
||||
configured_or_default_overlay_path(&cli.satellite_tiles, tiles_path, "satellite.pmtiles");
|
||||
let satellite_highres_tiles = configured_or_default_overlay_path(
|
||||
&cli.satellite_highres_tiles,
|
||||
tiles_path,
|
||||
"satellite_highres.pmtiles",
|
||||
);
|
||||
let crime_hotspot_tiles = configured_or_default_overlay_path(
|
||||
&cli.crime_hotspot_tiles,
|
||||
tiles_path,
|
||||
"crime_hotspots.pmtiles",
|
||||
);
|
||||
let tree_overlay_tiles = configured_or_default_overlay_path(
|
||||
&cli.tree_overlay_tiles,
|
||||
tiles_path,
|
||||
"trees_outside_woodlands.pmtiles",
|
||||
);
|
||||
let property_border_tiles = configured_or_default_overlay_path(
|
||||
&cli.property_border_tiles,
|
||||
tiles_path,
|
||||
"property_borders.pmtiles",
|
||||
);
|
||||
|
||||
let noise_overlay_reader = init_required_tile_reader("Noise", &noise_overlay_tiles).await?;
|
||||
let satellite_reader = init_required_tile_reader("Satellite", &satellite_tiles).await?;
|
||||
let noise_overlay_reader =
|
||||
init_required_tile_reader("Noise", &cli.noise_overlay_tiles).await?;
|
||||
let satellite_reader = init_required_tile_reader("Satellite", &cli.satellite_tiles).await?;
|
||||
let satellite_highres_reader =
|
||||
init_required_tile_reader("Satellite high-res", &satellite_highres_tiles).await?;
|
||||
init_required_tile_reader("Satellite high-res", &cli.satellite_highres_tiles).await?;
|
||||
let crime_hotspot_reader =
|
||||
init_required_tile_reader("Crime hotspots", &crime_hotspot_tiles).await?;
|
||||
init_required_tile_reader("Crime hotspots", &cli.crime_hotspot_tiles).await?;
|
||||
let tree_overlay_reader =
|
||||
init_required_tile_reader("Trees outside woodland", &tree_overlay_tiles).await?;
|
||||
init_required_tile_reader("Trees outside woodland", &cli.tree_overlay_tiles).await?;
|
||||
let property_border_reader =
|
||||
init_required_tile_reader("Property borders", &property_border_tiles).await?;
|
||||
init_required_tile_reader("Property borders", &cli.property_border_tiles).await?;
|
||||
|
||||
let feature_name_to_index: rustc_hash::FxHashMap<String, usize> = property_data
|
||||
.feature_names
|
||||
|
|
@ -720,7 +663,8 @@ async fn main() -> anyhow::Result<()> {
|
|||
let superuser_token_cache = Arc::new(pocketbase::SuperuserTokenCache::new());
|
||||
let share_cache = Arc::new(licensing::ShareBoundsCache::new());
|
||||
|
||||
let actual_listings = if let Some(path) = cli.actual_listings_path.as_ref() {
|
||||
let actual_listings = {
|
||||
let path = &cli.actual_listings_path;
|
||||
if !path.exists() {
|
||||
bail!("Actual listings parquet not found: {}", path.display());
|
||||
}
|
||||
|
|
@ -728,22 +672,17 @@ async fn main() -> anyhow::Result<()> {
|
|||
let listings = data::ActualListingData::load(path, &property_data)?;
|
||||
trim_allocator("actual listings load");
|
||||
info!(rows = listings.lat.len(), "Actual listings loaded");
|
||||
Some(Arc::new(listings))
|
||||
} else {
|
||||
info!("ACTUAL_LISTINGS_PATH not set; live listings overlay disabled");
|
||||
None
|
||||
Arc::new(listings)
|
||||
};
|
||||
|
||||
let crime_by_year = if let Some(path) = cli.crime_by_year_path.as_ref() {
|
||||
let crime_by_year = {
|
||||
let path = &cli.crime_by_year_path;
|
||||
if !path.exists() {
|
||||
bail!("Crime-by-year parquet not found: {}", path.display());
|
||||
}
|
||||
let data = data::CrimeByYearData::load(path)?;
|
||||
trim_allocator("crime-by-year load");
|
||||
Arc::new(data)
|
||||
} else {
|
||||
info!("CRIME_BY_YEAR_PATH not set; crime-over-time chart disabled");
|
||||
Arc::new(data::CrimeByYearData::empty())
|
||||
};
|
||||
|
||||
let app_state = AppState {
|
||||
|
|
|
|||
|
|
@ -65,14 +65,7 @@ pub async fn get_actual_listings(
|
|||
);
|
||||
}
|
||||
|
||||
let Some(actual_listings) = state.actual_listings.clone() else {
|
||||
return Ok(Json(ActualListingsResponse {
|
||||
listings: Vec::new(),
|
||||
total: 0,
|
||||
offset,
|
||||
truncated: false,
|
||||
}));
|
||||
};
|
||||
let actual_listings = state.actual_listings.clone();
|
||||
let (south, west, north, east) =
|
||||
require_bounds(params.bounds).map_err(IntoResponse::into_response)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,10 +44,9 @@ pub struct AppState {
|
|||
pub poi_category_groups: Arc<Vec<POICategoryGroup>>,
|
||||
/// Precomputed travel time data store
|
||||
pub travel_time_store: Arc<TravelTimeStore>,
|
||||
/// Optional real-world listings (e.g. Rightmove / Zoopla data) loaded from ACTUAL_LISTINGS_PATH.
|
||||
pub actual_listings: Option<Arc<ActualListingData>>,
|
||||
/// Real-world listings (e.g. Rightmove / Zoopla data) loaded from ACTUAL_LISTINGS_PATH.
|
||||
pub actual_listings: Arc<ActualListingData>,
|
||||
/// Per-LSOA per-year crime counts used by the right pane to plot trends.
|
||||
/// Empty when the side parquet was not supplied.
|
||||
pub crime_by_year: Arc<CrimeByYearData>,
|
||||
/// Token validation cache (60s TTL)
|
||||
pub token_cache: Arc<TokenCache>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue