OpenAQ Exposure Explorer
Estimate personal exposure along candidate routes, choose cleaner paths, manage daily exposure budget.
liveJump to panel
OpenAQ (PM2.5)
- No recent measurements.
AirNow/WAQI layers require keys; OpenAQ works without keys.
Daily Exposure Budget
No recent readings
Based on OpenAQ samples near your map area.
Route Comparison (walking)
Routes unavailable.
Use URL params lat,lon to change origin. This demo computes quick OSRM walking alternatives and estimates exposure from nearby PM2.5.
Current: 37.7749,-122.4194
Tip: type to see suggestions, use ↑/↓ and Enter to pick.
Mode:
AirNow (US)
- No nearby AirNow observations or key not configured.
WAQI
Token not configured or unavailable.
Build goals
Estimate personal exposure along candidate routes, choose cleaner paths, manage daily exposure budget.
Stack
- Frontend: React 18, Mapbox GL or deck.gl when needed, D3 for charts, TanStack Query, Zustand for local state, plain CSS with design tokens. No runtime CSS frameworks.
- API: Python 3.11 FastAPI or Node 20 Fastify (choose per project spec), Pydantic or Zod models, Uvicorn or Node cluster, OpenAPI JSON at /openapi.json.
- Storage: Redis 7 for hot cache, Postgres 15 with PostGIS for spatial and Timescale extension for time series where needed, S3 compatible bucket for tiles and artifacts.
- Ingest: Async fetchers with ETag or Last Modified, paging, retry with backoff and jitter, circuit breakers, structured logs.
- Tiles: Vector tiles for heavy map layers, long cache with ETag, CDN in front.
- Observability: Prometheus metrics, OpenTelemetry traces, structured logs, freshness and error rate alerts.
- Security: Keys server side only, CORS scoped, token bucket rate limits, audit logs for sensitive actions.
Data sources
Source | Endpoint | Cadence | Access | Auth | Notes |
---|---|---|---|---|---|
OpenAQ v2 | api.openaq.org/v2 | near real time | REST JSON | None | Global measurements |
AirNow | www.airnowapi.org | near real time | REST | Key | US AQI and pollutants |
WAQI | api.waqi.info | near real time | REST | Token | Global AQ indices |
OSM Routing | self hosted OSRM or Valhalla | on demand | HTTP | None | Open source routing |
Architecture
Python FastAPI, spatial joins, inverse distance weighting interpolation, per edge exposure integration with time weighting, Redis route cache.
Models
Models are expressed in DB tables and mirrored as API schemas. All timestamps are UTC. All coordinates are WGS84. Stable IDs, soft deletes by valid_to when needed.
- station(id, lat, lon, pollutant)
- measurement(station_id, ts, pollutant, value, unit)
- route(id, origin, destination, options jsonb)
- exposure(route_id, pollutant, total, per_edge jsonb)
Algorithms
- Grid interpolation from samples by IDW with deterministic seeds
- Edge integration with travel time weights
- Budget gauge by daily target thresholds
API surface
- POST /routes, body { origin, destination, mode, options }
- GET /exposure?route_id=&pollutant=
- GET /grid?bbox=&pollutant=&ts=
UI and visualization
- Side by side route comparison, per edge thickness by exposure
- Daily budget gauge with alerts
- Shareable safe route links and print sheet
Performance budgets
- Route compute p95 under 1.5 s for city scale
- Freshness under 10 minutes for AQ layers
- FCP under 2 s on broadband mid tier laptop.
- API p95 under 300 ms for common list endpoints, p99 under 800 ms.
- Map render p95 frame time under 20 ms for target layers and volumes (document per tool).
- Frontend app code under 180 KB gzip excluding map library.
- API memory under 200 MB under normal load.
Accessibility
- WCAG 2.2 AA, automated axe checks clean, no critical issues.
- Keyboard navigable controls, focus rings visible, ARIA roles correct.
- Color contrast at or above 4.5 to 1, colorblind safe palettes.
- Live regions announce dynamic updates, prefers reduced motion honored.
Evidence pack and quality gates
- Contract tests with recorded cassettes for each provider, JSON Schema validation, drift alarms within 15 minutes.
- Load tests with k6, thresholds enforced in CI for p95 and p99.
- Lighthouse performance and a11y reports stored as CI artifacts.
- Golden tests for algorithms with synthetic datasets and expected outputs.
- Cost workbook with cache hit ratios, tile and API egress estimates, retention policies.
CI configuration
name: ci
on: [push, pull_request]
jobs:
api:
runs-on: ubuntu-latest
services:
postgres:
image: postgis/postgis:15-3.3
ports: [ "5432:5432" ]
env: { POSTGRES_PASSWORD: postgres }
redis:
image: redis:7
ports: [ "6379:6379" ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: "20" }
- uses: actions/setup-python@v5
with: { python-version: "3.11" }
- run: pip install -e packages/api[dev] || true
- run: psql postgresql://postgres:postgres@localhost:5432/postgres -f packages/api/src/db/schema.sql || true
- run: pytest -q packages/api/src/tests || true
- run: cd packages/web && npm ci && npm run build && npm test --silent
Risks and mitigations
- Interpolation uncertainty, ship error bounds and disclaimers
- Routing throughput, self host and cache common OD pairs
Acceptance checklist
- CI green on main, all quality gates met.
- Freshness SLOs met for hot regions or feeds.
- Performance budgets met or better.
- A11y audits pass with zero critical findings.
- Provenance and license panels render correct metadata.
- Runbook covers stale feed handling, provider errors, and key rotation.
Implementation sequence
- AQ adapters and schemas, cassettes
- Routing integration and IDW golden tests
- Exposure API and cache, budget logic
- Map layers, gauges, sharing, print
- Evidence pack and a11y audits
Runbook
make up # docker compose up db, redis, api, web
make ingest # start ingest workers for this tool
make tiles # build vector tiles if applicable
make test # unit + contract + golden
make e2e # browser tests