How Vantage was built — from raw rolls to a clickable city
An end-to-end walkthrough of the data pipeline, the warehouse model, and the rendering layer behind this MVP. Everything below runs on public, audit-friendly sources — no scraped MLS, no gray-area licensing.
The problem
Vancouver real estate has rich public data — BC Assessment's annual valuations, City parcel polygons, zoning bylaws — but it sits in CSV dumps, shapefiles, and government APIs that don't talk to each other. Vantage stitches them into a single map-first explorer: click any parcel, drill into a building, and inspect every strata title within it.
Data layer
- Sources — BC Assessment yearly roll (2016 – 2026), City of Vancouver parcel polygons, zoning bylaw shapefiles.
- Store — PostgreSQL 18 + PostGIS 3.6 on Railway.
- Schema — a wide
core.property_currenttable (last-known state per PID),core.assessment_yearlyfor time series, andcore.site_eventsfor subdivisions / demolitions / redevelopments. - Lineage — a UUID per physical lot (
lineage_id) groups strata titles and tracks parcels across PID churn.
The strata-to-lot link (the hard part)
BC Assessment doesn't expose a foreign key from a condo PID to its parent land parcel. The bridge is spatial: each STRATA centroid is point-in-polygon joined to the LAND parcel that covers it via ST_Covers(land.parcel_polygon, strata.centroid). That's how clicking 833 Seymour reveals all 374 strata titles inside Capitol Residences.
Backend
- Stack — FastAPI + Pydantic + SQLAlchemy Core, deployed on Railway.
- Endpoints— bbox parcel fetch with truncation guard, point-in-polygon lookup, address search, per-PID detail & history, per-lot timeline (events + aggregates), per-PID units list.
- Performance — GiST spatial indexes on every polygon column, server-side bbox clipping, hard cap at 4,000 features per viewport request.
- Governance — every response carries a disclaimer field; CORS locked at the edge; no auth surface (read-only public data).
Frontend
- Stack — Next.js 16 (App Router) + React 19 + Tailwind 4 + TanStack Query.
- Map — Mapbox GL JS with a Vancouver-constrained geocoder. Parcel layer is a choropleth keyed on assessed value or zoning category; clicking outlines the lot and opens the inspector drawer.
- IA — two tabs per building: Building (aggregate story — total stack value, sparkline, lot events, yearly table) and Units (per-strata drill-in with search). Single-PID lots hide the Units tab.
- UX — recent searches in localStorage, default landing on a 374-unit strata so the flow is visible on first paint, color-by toggle so the entire map carries information at a glance.
What's next
- Optional sales data overlay (LTSA sample) for transaction prices.
- Permit pipeline overlay — approved + under-review developments.
- Comparables and $/sqft analytics within a buffer or neighborhood.
- Portfolio overlay — paste your own PIDs and see your buildings highlighted.