Small changes
This commit is contained in:
parent
a7d528fb68
commit
f9bd218a3e
9 changed files with 212 additions and 16 deletions
|
|
@ -228,7 +228,7 @@ Every UI element must use the correct token from this table. Do not invent new p
|
|||
- Style built by `@protomaps/basemaps` library with `namedFlavor(theme)` for light/dark
|
||||
- Font glyphs and twemoji PNGs served locally from `frontend/public/assets/` (no external CDN deps at runtime)
|
||||
- `CopyWebpackPlugin` copies `frontend/public/` → `dist/` on build; Rust `ServeDir` fallback serves them in prod
|
||||
- Download assets with `task download:map-assets` (script: `scripts/download_map_assets.py`)
|
||||
- Download assets with `task download:map-assets` (script: `pipeline/download/map_assets.py`)
|
||||
|
||||
**HomePage (landing page):**
|
||||
- Page bg: `bg-warm-50 dark:bg-warm-900`
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
"import polars as pl\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"pl.scan_parquet(\"../data/wide.parquet\").head(20).collect()\n"
|
||||
"pl.scan_parquet(\"../data/wide.parquet\").head(20).collect()"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@ services:
|
|||
command: >
|
||||
bash -c "
|
||||
cargo install cargo-watch &&
|
||||
cargo watch -x 'run -- --data /data/wide.parquet --pois /data/filtered_uk_pois.parquet --tiles /data/uk.pmtiles --postcodes /data/postcodes --pocketbase-url http://pocketbase:8090'
|
||||
cargo watch -x 'run -- --data /data/wide.parquet --pois /data/filtered_uk_pois.parquet --tiles /data/uk.pmtiles --postcodes /data/postcodes'
|
||||
"
|
||||
ports:
|
||||
- "8001:8001"
|
||||
networks:
|
||||
- ollama-network
|
||||
- dev-network
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
volumes:
|
||||
- .:/app
|
||||
- /bulk/property-data:/data:ro
|
||||
|
|
@ -19,7 +20,8 @@ services:
|
|||
- cargo-target:/app/server-rs/target
|
||||
environment:
|
||||
POCKETBASE_URL: http://pocketbase:8090
|
||||
G_SIDECAR_URL: http://og-screenshot:8002
|
||||
OG_SIDECAR_URL: http://og-screenshot:8002
|
||||
OLLAMA_URL: http://host.docker.internal:11434
|
||||
depends_on:
|
||||
pocketbase:
|
||||
condition: service_healthy
|
||||
|
|
@ -72,6 +74,8 @@ services:
|
|||
- "8090:8090"
|
||||
volumes:
|
||||
- pb-data:/pb/pb_data
|
||||
networks:
|
||||
- dev-network
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8090/api/health"]
|
||||
interval: 10s
|
||||
|
|
@ -87,5 +91,4 @@ volumes:
|
|||
og-cache:
|
||||
|
||||
networks:
|
||||
ollama-network:
|
||||
dev-network:
|
||||
|
|
|
|||
29
server-rs/Cargo.lock
generated
29
server-rs/Cargo.lock
generated
|
|
@ -2367,6 +2367,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"axum",
|
||||
"clap",
|
||||
"futures",
|
||||
"h3o",
|
||||
"lasso",
|
||||
"metrics",
|
||||
|
|
@ -2382,6 +2383,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
|
@ -2658,6 +2660,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
|
|
@ -2682,12 +2685,14 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
|
@ -3323,6 +3328,17 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.18"
|
||||
|
|
@ -3657,6 +3673,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ tracing = "0.1"
|
|||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
metrics = "0.24"
|
||||
metrics-exporter-prometheus = "0.16"
|
||||
reqwest = { version = "0.12", features = ["rustls-tls", "json"] }
|
||||
reqwest = { version = "0.12", features = ["rustls-tls", "json", "stream"] }
|
||||
futures = "0.3"
|
||||
tokio-stream = "0.1"
|
||||
regex = "1"
|
||||
urlencoding = "2"
|
||||
rust_xlsxwriter = "0.79"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ pub struct FeatureConfig {
|
|||
pub detail: &'static str,
|
||||
/// Data source slug for linking to /data-sources#<slug>
|
||||
pub source: &'static str,
|
||||
/// Display prefix (e.g. "£")
|
||||
pub prefix: &'static str,
|
||||
/// Display suffix (e.g. " mins")
|
||||
pub suffix: &'static str,
|
||||
/// If true, show full integer (no k/M abbreviation)
|
||||
pub raw: bool,
|
||||
}
|
||||
|
||||
/// Features whose histogram bins should be exactly 1 unit wide (one per integer).
|
||||
|
|
@ -58,7 +64,6 @@ pub const IGNORED_COLUMNS: &[&str] = &[
|
|||
"Postcode",
|
||||
"historical_prices",
|
||||
"Is construction date approximate",
|
||||
"Previous transaction date",
|
||||
"Current energy rating",
|
||||
"Potential energy rating",
|
||||
];
|
||||
|
|
@ -77,6 +82,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Most recent sale price from the Land Registry",
|
||||
detail: "The last recorded sale price for this property from HM Land Registry Price Paid data. Covers residential sales in England and Wales. May be years old if the property hasn't sold recently.",
|
||||
source: "price-paid",
|
||||
prefix: "£",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Price per sqm",
|
||||
|
|
@ -88,6 +96,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Sale price divided by total floor area",
|
||||
detail: "Calculated by dividing the last known sale price by the total floor area from the EPC certificate. Useful for comparing value across different-sized properties. Only available where both price and floor area data exist.",
|
||||
source: "price-paid",
|
||||
prefix: "£",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Total floor area (sqm)",
|
||||
|
|
@ -99,6 +110,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Internal floor area from the EPC survey",
|
||||
detail: "Total useful floor area in square metres as measured during the Energy Performance Certificate assessment. Includes all habitable rooms but excludes garages, outbuildings, and external areas.",
|
||||
source: "epc",
|
||||
prefix: "",
|
||||
suffix: " sqm",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Number of bedrooms & living rooms",
|
||||
|
|
@ -110,17 +124,23 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Count of habitable rooms from the EPC survey",
|
||||
detail: "Total number of habitable rooms (bedrooms plus living rooms) as recorded in the Energy Performance Certificate. Kitchens and bathrooms are typically excluded unless they are large enough to count as habitable rooms.",
|
||||
source: "epc",
|
||||
prefix: "",
|
||||
suffix: " rooms",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Transaction year",
|
||||
name: "Date of last transaction",
|
||||
bounds: Bounds::Fixed {
|
||||
min: 1995.0,
|
||||
max: 2026.0,
|
||||
},
|
||||
step: 1.0,
|
||||
description: "Year of the most recent sale from the Land Registry",
|
||||
detail: "The year (and fractional month) of the most recent recorded sale for this property from HM Land Registry Price Paid data. Used to show price history trends in the area chart.",
|
||||
description: "Date of the most recent sale from the Land Registry",
|
||||
detail: "The date of the most recent recorded sale for this property from HM Land Registry Price Paid data. Stored as a datetime in the data; converted to fractional year for filtering and charting.",
|
||||
source: "price-paid",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: true,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Construction age",
|
||||
|
|
@ -132,6 +152,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Estimated year of construction from the EPC",
|
||||
detail: "The approximate year of construction as recorded in the Energy Performance Certificate. Derived from the construction age band (e.g. '1930-1949') by taking the midpoint. May be approximate, especially for older buildings.",
|
||||
source: "epc",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -148,6 +171,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Public transport journey time to Bank station",
|
||||
detail: "Journey time in minutes by public transport to Bank station in the City of London, using TfL's Journey Planner API. Calculated for weekday morning commute times.",
|
||||
source: "tfl-journey-times",
|
||||
prefix: "",
|
||||
suffix: " mins",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Public transport to Fitzrovia (mins)",
|
||||
|
|
@ -159,6 +185,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Public transport journey time to Fitzrovia",
|
||||
detail: "Journey time in minutes by public transport to Fitzrovia in central London, using TfL's Journey Planner API. Calculated for weekday morning commute times.",
|
||||
source: "tfl-journey-times",
|
||||
prefix: "",
|
||||
suffix: " mins",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Cycling to Bank (mins)",
|
||||
|
|
@ -170,6 +199,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Cycling time to Bank station",
|
||||
detail: "Cycling journey time in minutes to Bank station, as calculated by the TfL Journey Planner API. Uses TfL's default cycling speed and route preferences.",
|
||||
source: "tfl-journey-times",
|
||||
prefix: "",
|
||||
suffix: " mins",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Cycling to Fitzrovia (mins)",
|
||||
|
|
@ -181,9 +213,12 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Cycling time to Fitzrovia",
|
||||
detail: "Cycling journey time in minutes to Fitzrovia, as calculated by the TfL Journey Planner API. Uses TfL's default cycling speed and route preferences.",
|
||||
source: "tfl-journey-times",
|
||||
prefix: "",
|
||||
suffix: " mins",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Public transport within 2km",
|
||||
name: "Number of public transport stations within 2km",
|
||||
bounds: Bounds::Percentile {
|
||||
low: 5.0,
|
||||
high: 95.0,
|
||||
|
|
@ -192,6 +227,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Number of public transport stops within 2km",
|
||||
detail: "Count of bus stops, rail stations, tube stations, tram stops, and other public transport access points within a 2km radius of the property's postcode. Derived from the NaPTAN (National Public Transport Access Nodes) dataset.",
|
||||
source: "naptan",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -208,6 +246,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "IoD education deprivation score for the local area",
|
||||
detail: "From the English Indices of Deprivation. Measures deprivation in education, skills and training in the local area (LSOA). Higher scores indicate greater deprivation. Combines children/young people sub-domain (school attainment, entry to higher education) and adult skills sub-domain (adult qualifications, English language proficiency).",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Good+ primary schools within 5km",
|
||||
|
|
@ -219,6 +260,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Primary schools rated Good or Outstanding by Ofsted nearby",
|
||||
detail: "Number of state-funded primary schools within 5km that have a current Ofsted rating of Good or Outstanding. Based on the latest inspection outcomes dataset. Schools that have not yet been inspected are excluded.",
|
||||
source: "ofsted",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Good+ secondary schools within 5km",
|
||||
|
|
@ -230,6 +274,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Secondary schools rated Good or Outstanding by Ofsted nearby",
|
||||
detail: "Number of state-funded secondary schools within 5km that have a current Ofsted rating of Good or Outstanding. Based on the latest inspection outcomes dataset. Schools that have not yet been inspected are excluded.",
|
||||
source: "ofsted",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -243,6 +290,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Proportion of the population experiencing income deprivation",
|
||||
detail: "From the English Indices of Deprivation. The proportion of the local population experiencing deprivation relating to low income. Includes people on Income Support, income-based Jobseeker's Allowance, income-based Employment and Support Allowance, Pension Credit, Working Tax Credit and Child Tax Credit, Universal Credit, and asylum seekers.",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Employment Score (rate)",
|
||||
|
|
@ -251,6 +301,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Proportion of the working-age population involuntarily excluded from work",
|
||||
detail: "From the English Indices of Deprivation. The proportion of the working-age population involuntarily excluded from the labour market. Includes claimants of Jobseeker's Allowance, Employment and Support Allowance, Incapacity Benefit, Severe Disablement Allowance, Carer's Allowance, and relevant Universal Credit claimants.",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Health Deprivation and Disability Score",
|
||||
|
|
@ -262,6 +315,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Risk of premature death and quality of life impairment",
|
||||
detail: "From the English Indices of Deprivation. Measures the risk of premature death and impairment of quality of life through poor physical or mental health. Derived from years of potential life lost, comparative illness and disability ratio, acute morbidity, and mood and anxiety disorders.",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Living Environment Score",
|
||||
|
|
@ -273,6 +329,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Quality of the local indoor and outdoor environment",
|
||||
detail: "From the English Indices of Deprivation. Measures deprivation in the quality of the local environment. Combines the Indoors sub-domain (housing quality, central heating, housing conditions) and Outdoors sub-domain (air quality, road traffic accidents). Higher scores indicate poorer living environments.",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Indoors Sub-domain Score",
|
||||
|
|
@ -284,6 +343,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Housing quality and conditions in the local area",
|
||||
detail: "From the English Indices of Deprivation, Living Environment domain. Measures the quality of housing stock: houses without central heating, housing in poor condition, and houses failing Decent Homes standards. Higher scores indicate worse housing conditions.",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Outdoors Sub-domain Score",
|
||||
|
|
@ -295,6 +357,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Air quality and road safety in the local area",
|
||||
detail: "From the English Indices of Deprivation, Living Environment domain. Measures the outdoor living environment quality through air quality indicators and road traffic accident casualties involving pedestrians and cyclists. Higher scores indicate poorer outdoor environments.",
|
||||
source: "iod",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -311,6 +376,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly anti-social behaviour incidents in the area",
|
||||
detail: "Average number of anti-social behaviour incidents per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes nuisance, environmental, and personal anti-social behaviour.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Violence and sexual offences (avg/yr)",
|
||||
|
|
@ -322,6 +390,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly violent and sexual offences in the area",
|
||||
detail: "Average number of violence and sexual offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes assault, harassment, and sexual offences.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Criminal damage and arson (avg/yr)",
|
||||
|
|
@ -333,6 +404,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly criminal damage and arson in the area",
|
||||
detail: "Average number of criminal damage and arson incidents per year in the LSOA, from police.uk street-level crime data (2023-2025).",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Burglary (avg/yr)",
|
||||
|
|
@ -344,6 +418,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly burglary offences in the area",
|
||||
detail: "Average number of burglary offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes residential and commercial burglary.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Vehicle crime (avg/yr)",
|
||||
|
|
@ -355,6 +432,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly vehicle crime in the area",
|
||||
detail: "Average number of vehicle crime incidents per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes theft of and from vehicles.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Robbery (avg/yr)",
|
||||
|
|
@ -366,6 +446,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly robbery offences in the area",
|
||||
detail: "Average number of robbery offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Robbery involves theft with force or threat of force.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Other theft (avg/yr)",
|
||||
|
|
@ -377,6 +460,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly other theft offences in the area",
|
||||
detail: "Average number of 'other theft' offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes theft not classified under burglary, vehicle crime, shoplifting, or bicycle theft.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Shoplifting (avg/yr)",
|
||||
|
|
@ -388,6 +474,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly shoplifting offences in the area",
|
||||
detail: "Average number of shoplifting offences per year in the LSOA, from police.uk street-level crime data (2023-2025).",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Drugs (avg/yr)",
|
||||
|
|
@ -399,6 +488,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly drug offences in the area",
|
||||
detail: "Average number of drug offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes possession and trafficking offences.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Possession of weapons (avg/yr)",
|
||||
|
|
@ -410,6 +502,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly weapons possession offences in the area",
|
||||
detail: "Average number of possession of weapons offences per year in the LSOA, from police.uk street-level crime data (2023-2025).",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Public order (avg/yr)",
|
||||
|
|
@ -421,6 +516,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly public order offences in the area",
|
||||
detail: "Average number of public order offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes causing fear, alarm, or distress.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Bicycle theft (avg/yr)",
|
||||
|
|
@ -432,6 +530,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly bicycle theft in the area",
|
||||
detail: "Average number of bicycle theft offences per year in the LSOA, from police.uk street-level crime data (2023-2025).",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Theft from the person (avg/yr)",
|
||||
|
|
@ -443,6 +544,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly theft from the person in the area",
|
||||
detail: "Average number of theft from the person offences per year in the LSOA, from police.uk street-level crime data (2023-2025). Includes pickpocketing and bag snatching without force.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Other crime (avg/yr)",
|
||||
|
|
@ -454,6 +558,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Average yearly other crime in the area",
|
||||
detail: "Average number of other crime offences per year in the LSOA, from police.uk street-level crime data (2023-2025). A catch-all category for offences not classified elsewhere.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Serious crime (avg/yr)",
|
||||
|
|
@ -465,6 +572,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Aggregate of serious crime categories per year",
|
||||
detail: "Sum of violence, robbery, burglary, and weapons possession per year in the LSOA, from police.uk street-level crime data (2023-2025). Provides a single serious crime metric.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Minor crime (avg/yr)",
|
||||
|
|
@ -476,6 +586,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Aggregate of minor crime categories per year",
|
||||
detail: "Sum of anti-social behaviour, shoplifting, bicycle theft, and other lower-severity crime per year in the LSOA, from police.uk street-level crime data (2023-2025). Provides a single minor crime metric.",
|
||||
source: "crime",
|
||||
prefix: "",
|
||||
suffix: "/yr",
|
||||
raw: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -492,6 +605,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Percentage of population identifying as White",
|
||||
detail: "From the 2021 Census. Percentage of the local authority population identifying as White (English, Welsh, Scottish, Northern Irish, British, Irish, Gypsy or Irish Traveller, Roma, or any other White background).",
|
||||
source: "ethnicity",
|
||||
prefix: "",
|
||||
suffix: "%",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "% Asian",
|
||||
|
|
@ -503,6 +619,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Percentage of population identifying as Asian",
|
||||
detail: "From the 2021 Census. Percentage of the local authority population identifying as Asian or Asian British (Indian, Pakistani, Bangladeshi, Chinese, or any other Asian background).",
|
||||
source: "ethnicity",
|
||||
prefix: "",
|
||||
suffix: "%",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "% Black",
|
||||
|
|
@ -514,6 +633,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Percentage of population identifying as Black",
|
||||
detail: "From the 2021 Census. Percentage of the local authority population identifying as Black, Black British, Caribbean, or African.",
|
||||
source: "ethnicity",
|
||||
prefix: "",
|
||||
suffix: "%",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "% Mixed",
|
||||
|
|
@ -525,6 +647,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Percentage of population identifying as Mixed or Multiple ethnic groups",
|
||||
detail: "From the 2021 Census. Percentage of the local authority population identifying as Mixed or Multiple ethnic groups (White and Black Caribbean, White and Black African, White and Asian, or any other Mixed or Multiple background).",
|
||||
source: "ethnicity",
|
||||
prefix: "",
|
||||
suffix: "%",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "% Other",
|
||||
|
|
@ -536,6 +661,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Percentage of population identifying as Other ethnic group",
|
||||
detail: "From the 2021 Census. Percentage of the local authority population identifying as Other ethnic group (Arab or any other ethnic group not covered by the main categories).",
|
||||
source: "ethnicity",
|
||||
prefix: "",
|
||||
suffix: "%",
|
||||
raw: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -543,7 +671,7 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
name: "Amenities",
|
||||
features: &[
|
||||
FeatureConfig {
|
||||
name: "Restaurants within 2km",
|
||||
name: "Number of restaurants within 2km",
|
||||
bounds: Bounds::Percentile {
|
||||
low: 5.0,
|
||||
high: 95.0,
|
||||
|
|
@ -552,9 +680,12 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Number of restaurants and cafes within 2km",
|
||||
detail: "Count of restaurants, cafes, and food establishments within a 2km radius of the property's postcode centroid. Derived from OpenStreetMap POI data using haversine distance calculation with a 0.05° spatial grid for candidate reduction.",
|
||||
source: "osm-pois",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Groceries within 2km",
|
||||
name: "Number of grocery shops and supermarkets within 2km",
|
||||
bounds: Bounds::Percentile {
|
||||
low: 5.0,
|
||||
high: 95.0,
|
||||
|
|
@ -563,9 +694,12 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Number of grocery shops and supermarkets within 2km",
|
||||
detail: "Count of supermarkets, convenience stores, and other grocery shops within a 2km radius of the property's postcode centroid. Derived from OpenStreetMap POI data.",
|
||||
source: "osm-pois",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Parks within 2km",
|
||||
name: "Number of parks within 2km",
|
||||
bounds: Bounds::Percentile {
|
||||
low: 5.0,
|
||||
high: 95.0,
|
||||
|
|
@ -574,6 +708,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Number of parks and green spaces within 2km",
|
||||
detail: "Count of parks, gardens, nature reserves, and other green spaces within a 2km radius of the property's postcode centroid. Derived from OpenStreetMap POI data.",
|
||||
source: "osm-pois",
|
||||
prefix: "",
|
||||
suffix: "",
|
||||
raw: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -590,6 +727,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Road noise level at the postcode in decibels (Lden)",
|
||||
detail: "Road noise level in decibels (Lden — day-evening-night 24-hour weighted average) from Defra's Strategic Noise Mapping Round 4 (2022). Modelled at 4m above ground on a 10m grid. Sampled at postcode centroids via WCS GeoTIFF tiles. Values above ~55 dB are generally considered noticeable; above ~70 dB can affect health.",
|
||||
source: "noise",
|
||||
prefix: "",
|
||||
suffix: " dB",
|
||||
raw: false,
|
||||
},
|
||||
FeatureConfig {
|
||||
name: "Max available download speed (Mbps)",
|
||||
|
|
@ -601,6 +741,9 @@ pub static FEATURE_GROUPS: &[FeatureGroup] = &[
|
|||
description: "Maximum broadband download speed available at the postcode",
|
||||
detail: "Maximum available fixed broadband download speed in Megabits per second, from Ofcom's Connected Nations 2025 report. Measured at Output Area level and represents the maximum speed available from any provider, not actual achieved speeds.",
|
||||
source: "broadband",
|
||||
prefix: "",
|
||||
suffix: " Mbps",
|
||||
raw: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
mod area_summary;
|
||||
mod export;
|
||||
mod features;
|
||||
mod hexagon_stats;
|
||||
|
|
@ -10,6 +11,7 @@ mod postcodes;
|
|||
pub(crate) mod properties;
|
||||
mod tiles;
|
||||
|
||||
pub use area_summary::post_area_summary;
|
||||
pub use export::get_export;
|
||||
pub use features::{build_features_response, get_features, FeatureInfo, FeaturesResponse};
|
||||
pub use hexagon_stats::get_hexagon_stats;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@ use crate::data::{Histogram, PropertyData};
|
|||
use crate::features::{ENUM_FEATURE_GROUPS, FEATURE_GROUPS};
|
||||
use crate::state::AppState;
|
||||
|
||||
fn is_empty(v: &str) -> bool {
|
||||
v.is_empty()
|
||||
}
|
||||
|
||||
fn is_false(v: &bool) -> bool {
|
||||
!v
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum FeatureInfo {
|
||||
|
|
@ -21,6 +29,12 @@ pub enum FeatureInfo {
|
|||
description: &'static str,
|
||||
detail: &'static str,
|
||||
source: &'static str,
|
||||
#[serde(skip_serializing_if = "is_empty")]
|
||||
prefix: &'static str,
|
||||
#[serde(skip_serializing_if = "is_empty")]
|
||||
suffix: &'static str,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
raw: bool,
|
||||
},
|
||||
#[serde(rename = "enum")]
|
||||
Enum {
|
||||
|
|
@ -82,6 +96,9 @@ pub fn build_features_response(data: &PropertyData) -> FeaturesResponse {
|
|||
description: feature_config.description,
|
||||
detail: feature_config.detail,
|
||||
source: feature_config.source,
|
||||
prefix: feature_config.prefix,
|
||||
suffix: feature_config.suffix,
|
||||
raw: feature_config.raw,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ pub async fn get_hexagon_stats(
|
|||
|
||||
// Collect price history (year, price) pairs
|
||||
let price_history = {
|
||||
let year_idx = state.feature_name_to_index.get("Transaction year").copied();
|
||||
let year_idx = state.feature_name_to_index.get("Date of last transaction").copied();
|
||||
let price_idx = state.feature_name_to_index.get("Last known price").copied();
|
||||
match (year_idx, price_idx) {
|
||||
(Some(yi), Some(pi)) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue