174 lines
4.7 KiB
Markdown
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.
|