perfect-postcode/README.md
Andras Schmelczer 05a1f316e1
Some checks failed
CI / Check (push) Failing after 2m14s
Build and publish Docker image / build-and-push (push) Failing after 2m38s
More
2026-05-04 17:21:26 +01:00

174 lines
4.7 KiB
Markdown

# 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:
```text
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:
```bash
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:
```bash
make -f Makefile.data download-transit-network
./r5-java/run.sh --threads 8 --heap 40g
```
For a quick R5 smoke test:
```bash
./r5-java/run.sh --demo
```
## Local Development
With the required files in `property-data/`, the full stack can be started with
Docker Compose:
```bash
docker compose up --build
```
Services:
- frontend: http://localhost:3001
- API: http://localhost:8001
- PocketBase: http://localhost:8090
- screenshot service: http://localhost:8002
The frontend dev server proxies `/api` and `/s` to the Rust API and `/pb` to
PocketBase.
To run pieces directly:
```bash
cd frontend
npm install
npm run dev
```
Export the server's service configuration first:
```bash
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=...
```
```bash
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:
```bash
./check.sh
```
It runs Python lint/tests, frontend lint/format/typecheck/tests, screenshot
service tests, and Rust clippy/format/tests.
Useful focused commands:
```bash
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.
```bash
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.