| .forgejo/workflows | ||
| .vscode | ||
| analyses | ||
| frontend | ||
| manual-data | ||
| pipeline | ||
| property-data | ||
| property-data2 | ||
| r5-java | ||
| screenshot | ||
| scripts | ||
| server-rs | ||
| video | ||
| .dockerignore | ||
| .gitignore | ||
| .python-version | ||
| check.sh | ||
| docker-compose.yml | ||
| Dockerfile | ||
| grafana-dashboard.json | ||
| Makefile.data | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
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.
Public SEO Pages
The indexable public pages are listed in frontend/public/sitemap.xml and
prerendered by frontend/scripts/prerender.mjs:
- Home -
/ - Learn -
/learn - Pricing -
/pricing - Property price map -
/property-price-map - Postcode property search -
/postcode-property-search - Commute property search -
/commute-property-search - School property search -
/school-property-search - Postcode checker -
/postcode-checker - Birmingham property search -
/property-search/birmingham - Manchester property search -
/property-search/manchester - Bristol property search -
/property-search/bristol - Data sources -
/data-sources - Methodology -
/methodology - Privacy and security -
/privacy-security
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.dataorchestrates 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/andmanual-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, including police.uk crime archives, can be downloaded or generated
through Makefile.data. Some inputs are deliberately manual:
manual-data/domestic-csv.zipfrom the EPC register- 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 generate-travel-times
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:
- 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:
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.