No description
Find a file
2026-05-06 23:13:58 +01:00
.forgejo/workflows More 2026-05-04 17:21:26 +01:00
.vscode Update config 2026-02-07 10:02:01 +00:00
analyses Fmt 2026-03-15 21:22:28 +00:00
frontend Lint 2026-05-06 23:13:58 +01:00
manual-data Rerun prepare script 2026-04-06 11:13:52 +01:00
pipeline Lint 2026-05-06 23:13:58 +01:00
property-data Changes 2026-02-08 18:40:17 +00:00
r5-java seems fine 2026-05-05 22:29:28 +01:00
screenshot Tonight 2026-05-06 22:40:46 +01:00
scripts Remove finder 2026-04-04 22:59:28 +01:00
server-rs Lint 2026-05-06 23:13:58 +01:00
video Tonight 2026-05-06 22:40:46 +01:00
.dockerignore Deploy again 2026-02-19 22:24:06 +00:00
.gitignore Lint 2026-05-06 23:13:58 +01:00
.python-version initial 2026-01-23 20:58:41 +00:00
check.sh Hacky demo changes 2026-05-06 19:36:04 +01:00
docker-compose.yml Fun changes 2026-04-04 22:59:44 +01:00
Dockerfile Small changes and fix zooming 2026-05-05 20:30:04 +01:00
grafana-dashboard.json Fun changes 2026-04-04 22:59:44 +01:00
Makefile.data More 2026-05-04 17:21:26 +01:00
pitch.md lmao 2026-02-15 22:39:54 +00:00
price_model.ipynb Fmt 2026-03-15 21:22:28 +00:00
pyproject.toml More 2026-05-04 17:21:26 +01:00
README.md More 2026-05-04 17:21:26 +01:00
uv.lock More 2026-05-04 17:21:26 +01:00

Property Map

Interactive UK property intelligence map. The app combines transaction, EPC, postcode, neighbourhood, transport, POI, and travel-time data into local parquet files, serves fast geospatial aggregations from Rust, and renders the result as a React/deck.gl map.

The public product is branded as Perfect Postcodes, while this repository is still named property-map.

What Is In Here

  • frontend/ - React 18, TypeScript, Tailwind, MapLibre, and deck.gl. The app has a landing page, map dashboard, saved searches/properties, account pages, pricing, invites, and shareable URLs.
  • server-rs/ - Rust Axum API. It loads the generated parquet data into memory, builds spatial indexes, serves H3/postcode aggregations, proxies PocketBase, serves PMTiles, handles AI filter parsing, screenshots, exports, checkout, and telemetry.
  • pipeline/ - Python/Polars download and transform pipeline. Makefile.data orchestrates the data DAG.
  • r5-java/ - Batch travel-time generator using Conveyal R5. It writes sparse per-destination parquet files for car, bicycle, walking, and transit.
  • screenshot/ - Playwright/Express service used by the Rust API for map screenshots and Open Graph images.
  • property-data/ and manual-data/ - Local generated/downloaded data. These are runtime inputs, not source code.

Runtime Data

The Rust server expects these files or directories to exist:

property-data/properties.parquet
property-data/postcode.parquet
property-data/filtered_uk_pois.parquet
property-data/places.parquet
property-data/uk.pmtiles
property-data/postcode_boundaries/
property-data/travel-times/

Most data can be downloaded or generated through Makefile.data. Some inputs are deliberately manual:

  • manual-data/certificates.csv from the EPC register
  • manual-data/crime/ CSV exports from police.uk
  • postcode boundaries, generated from OA boundaries, INSPIRE polygons, and UPRN lookup data

Build the main property datasets with:

uv sync
make -f Makefile.data prepare
make -f Makefile.data tiles
make -f Makefile.data download-places
make -f Makefile.data generate-postcode-boundaries

generate-postcode-boundaries writes to manual-data/postcode_boundaries/. The running server expects the same structure under property-data/postcode_boundaries/; copy or symlink it if needed.

Travel times are built separately because they are expensive:

make -f Makefile.data download-transit-network
./r5-java/run.sh --threads 8 --heap 40g

For a quick R5 smoke test:

./r5-java/run.sh --demo

Local Development

With the required files in property-data/, the full stack can be started with Docker Compose:

docker compose up --build

Services:

The frontend dev server proxies /api and /s to the Rust API and /pb to PocketBase.

To run pieces directly:

cd frontend
npm install
npm run dev

Export the server's service configuration first:

export SCREENSHOT_URL=http://localhost:8002
export PUBLIC_URL=http://localhost:3001
export POCKETBASE_URL=http://localhost:8090
export POCKETBASE_ADMIN_EMAIL=...
export POCKETBASE_ADMIN_PASSWORD=...
export GEMINI_API_KEY=...
export GEMINI_MODEL=...
export GOOGLE_MAPS_API_KEY=...
export STRIPE_SECRET_KEY=...
export STRIPE_WEBHOOK_SECRET=...
export STRIPE_REFERRAL_COUPON_ID=...
export GOOGLE_OAUTH_CLIENT_ID=...
export GOOGLE_OAUTH_CLIENT_SECRET=...
cd server-rs
cargo run -- \
  --properties ../property-data/properties.parquet \
  --postcode-features ../property-data/postcode.parquet \
  --pois ../property-data/filtered_uk_pois.parquet \
  --places ../property-data/places.parquet \
  --tiles ../property-data/uk.pmtiles \
  --postcodes ../property-data/postcode_boundaries \
  --travel-times ../property-data/travel-times

Checks

Run the combined local check script:

./check.sh

It runs Python lint/tests, frontend lint/format/typecheck/tests, screenshot service tests, and Rust clippy/format/tests.

Useful focused commands:

uv run ruff check .
uv run pytest

cd frontend
npm run lint
npm run typecheck
npm run test
npm run build

cd ../server-rs
cargo clippy --all-targets -- -D warnings
cargo fmt --all --check
cargo test

Production Build

The root Dockerfile builds the frontend and Rust server into a runtime image. Data is mounted at /app/data; it is not baked into the image.

docker build -t property-map .

The container entrypoint runs property-map-server with the expected data paths under /app/data and serves frontend/dist when --dist is present.