Project Info
Inspiration
Cities are changing faster than ever due to climate change, housing shortages, rising temperatures, flooding, and shifting transportation needs. Yet most residents, planners, and policymakers struggle to access planning data because it is scattered across dozens of government websites, reports, and GIS platforms. As students interested in urban planning, data science, design, and artificial intelligence, we wanted to create a tool that makes complex planning information understandable for everyone. We asked a simple question: What if anyone could enter an address and instantly understand both its current conditions and its future risks? UrbanPilot was built to bridge the gap between public data and public understanding.
What it does
UrbanPilot analyzes any address and combines real public planning data with specialized AI agents to explain current conditions, identify risks, and visualize how a neighborhood could evolve by 2040 and 2075. Unlike traditional planning tools, UrbanPilot grounds its analysis in verified public datasets including Census, FEMA, GTFS transit, NLCD tree canopy, and Open-Meteo environmental data, while clearly distinguishing between measured data and AI-generated projections. The platform evaluates: Climate risk Flood risk Heat vulnerability Housing conditions Accessibility and transit access Environmental factors Community resilience indicators Each address gets one overall score, averaged only from the categories with valid data: $$\text{Overall Score} = \frac{1}{n}\sum_{i \,\in\, {\text{climate, accessibility, housing}}} \text{Score}_i$$ Users can explore both current conditions and projected future scenarios for 2040 and 2075. The platform combines verified public datasets with AI-generated planning recommendations while clearly distinguishing between measured data and projected estimates. UrbanPilot also generates AI-created future visualizations showing how neighborhoods may evolve under changing environmental conditions, helping users understand future impacts in a way that maps and spreadsheets cannot. Finally, users can chat with UrbanPilot AI to ask planning-related questions and receive evidence-based recommendations grounded in verified planning data.
How we built it
We built UrbanPilot as a full-stack web application using: React for the frontend Node.js and Express for the backend Tailwind CSS and shadcn/ui for the frontend design system Leaflet + OpenStreetMap/CARTO for the interactive map (Google Maps is used server-side only, for Street View/satellite imagery and geocoding) Climate and flood risk datasets Specialized AI agents for housing, accessibility, climate, vision generation, and planning assistance Claude and Anthropic APIs for reasoning and recommendations Multi-agent workflows for data collection, analysis, and report generation Midjourney's MCP server for AI-generated future neighborhood visualizations Sentry for production error monitoring ACS Census 2024 5-Year estimates 511 SF Bay GTFS transit data FEMA National Flood Hazard Layer NLCD Tree Canopy Cover 2023 Open-Meteo weather and air quality data The Token Company's prompt-compression SDK, layered on top of a custom-built compact prompt encoding, to reduce per-request token cost The system combines five public datasets, geospatial analysis, and specialized AI agents into a single workflow that transforms a simple address into actionable planning insights. We also treated token efficiency as a first-class engineering problem, not an afterthought. Every agent prompt that sends verified third-party data (Census, FEMA, NLCD, GTFS) to Claude was rewritten from a pretty-printed, human-readable format into a hand-built compact encoding — measured against real Anthropic API calls, this cut input tokens by 27.8–35.4% per agent with zero loss of output quality (every response still parsed as valid JSON and cited the exact verified figures). We then layered The Token Company's compression SDK on top of that in production, and benchmarked both independently: their generic compressor performs best on prose-heavy, unstructured prompts (20.6% reduction on our AI assistant's context), while our domain-specific encoding outperforms it by roughly 7–9x on structured, schema-heavy prompts — because it can safely remove formatting overhead that a generic compressor has to leave alone. Project Background UrbanPilot began as a simple location-search prototype with Google Maps integration and address lookup functionality. During the hackathon, we built the core platform functionality, including the multi-agent planning architecture, public data integrations, future scenario generation, AI planning assistant, climate and accessibility analysis, visualization pipeline, and data-grounded recommendation system that define UrbanPilot today. What we built during the hackathon New functionality built during the event includes: Multi-agent planning architecture ACS Census data integration and grounding Housing Agent grounded with ACS Census data Climate Agent grounded with weather, air quality, flood, and canopy data Accessibility Agent grounded with GTFS transit data FEMA flood risk integration GTFS transit accessibility integration Tree canopy analysis Future scenario generation for 2040 and 2075 AI-powered planning assistant Data transparency and source disclosure framework Planning recommendation engine New dashboard and analysis workflow Unified urban planning scoring framework AI-generated future neighborhood visualizations using Midjourney Token-efficient prompt engineering and a production integration with The Token Company's compression SDK These additions introduced the core intelligence, planning analysis, forecasting, and decision-support capabilities that define UrbanPilot today. ##
Challenges we ran into
One of our biggest challenges was integrating multiple planning and environmental datasets that often use different formats, scales, and geographic boundaries. Another challenge was ensuring our AI recommendations remained grounded in real planning data rather than generating unsupported conclusions. We also faced difficulties creating realistic future visualizations that accurately reflected environmental risks while remaining understandable to everyday users. A major challenge was preventing AI hallucinations. We wanted UrbanPilot to provide useful planning recommendations without presenting AI-generated estimates as verified facts. This led us to build a data disclosure framework that explicitly separates grounded public data from AI-generated analysis. Finally, coordinating frontend mapping systems, backend APIs, and AI agents within the hackathon timeframe required significant debugging and iteration. Claude doesn't always return perfectly-formed JSON, especially under truncation — we built a fallback parser per agent that can recover a partial, truncated response into valid structured data rather than failing the entire analysis over one malformed character. Integrating a third-party SDK into the same client every agent depends on also meant vetting it carefully before trusting it with real request data — checking package provenance, confirming what it actually transmits and to where, and working through an ESM/CommonJS interop issue before it was safe to wire into production. ##
Accomplishments we're proud of
Transformed an early planning prototype into a data-grounded AI urban planning platform with multi-agent analysis, future scenario generation, and climate risk forecasting. Grounded AI analysis using real Census, transit, flood, tree canopy, weather, and air quality datasets. Generated future-focused neighborhood visualizations for 2040 and 2075. Created an interactive map-based experience with address-level analysis. Developed a system that makes complex planning information accessible to non-experts. Combined urban planning, climate science, GIS, and AI into a single platform. Integrated five independent public data sources into a unified planning workflow. Built a transparent AI system that distinguishes verified data from AI-generated recommendations. Created an address-level planning tool that combines housing, climate, accessibility, and future scenario generation in a single experience. Cut LLM input-token cost per agent call by up to 35% through a custom compact encoding, verified with real API calls rather than estimates, with zero loss of output accuracy. Each specialist agent fails independently — if one data source or Claude call goes down, that section shows as unavailable while the rest of the analysis completes normally. ##
What we learned
Through UrbanPilot, we learned how difficult it is to translate technical planning and environmental data into tools that ordinary people can understand. We learned that trustworthy AI requires transparency. To address this, UrbanPilot distinguishes between verified data sources and AI-generated estimates through a built-in data disclosure framework. We also gained experience building multi-agent AI systems, integrating geospatial datasets, creating AI-generated visualizations, and designing interfaces that communicate uncertainty responsibly. We also learned that compression isn't one-size-fits-all: generic, automatic compression and domain-specific prompt engineering solve different problems, and the biggest token savings came from understanding why a prompt was expensive, not just bolting on a tool. Most importantly, we learned that AI can be a powerful tool for civic engagement when paired with trustworthy public data. We also learned that transparency is just as important as accuracy when building AI systems intended to support real-world decision making. ##
What's next
Our vision is to turn UrbanPilot into a comprehensive civic intelligence platform. Future plans include: Additional climate adaptation scenarios Neighborhood comparison tools Property-level resilience scoring Transit and infrastructure forecasting Community planning collaboration features Citywide planning dashboards for governments and nonprofits Expanded support for cities across the United States We also plan to expand beyond the Bay Area by supporting additional regions and incorporating more local planning datasets so UrbanPilot can serve communities nationwide. Long-term, we hope UrbanPilot can help communities make more informed decisions about where they live, invest, and build for the future.
UrbanPilot
A multi-agent urban planning copilot. Search any real address, get an AI-driven analysis across climate resilience, accessibility, and housing, and see Current/2040/2075 scenarios — Current backed by a real Street View/satellite photo of the site, 2040/2075 generated by Midjourney.
Architecture
src/ React frontend (Create React App + Tailwind + Leaflet)
App.js App shell — state/handlers, composes the components below
components/
TopHeader.js Logo, compact location search, live conditions bar
LocationSearch.js Google Places Autocomplete (proxied through the backend)
ConditionsBar.js Live weather/AQI/heat/flood badges
ControlStrip.js Planning-goal / target-year picker + Analyze trigger
AnalysisStatusBar.js Compact status line while agents are running
ReadyToAnalyzeCard.js Pre-analysis onboarding state — no fabricated scores/data
MainMapPanel.js Leaflet map workspace: marker, zoom controls, scenario overlay image
ProjectedScenarioChanges.js Per-scenario projected-change stat strip, beside the map
StreetViewPanel.js Collapsible wrapper around PresentDayView
PresentDayView.js Google Maps JS API panel: Street View / satellite toggle
VisualizeStreetscapeAction.js "Visualize Proposed Streetscape" trigger (Midjourney)
ReferenceImageInput.js Upload-your-own-photo workflow for the Midjourney reference image
CurrentConditionsPanel.js Verified climate/accessibility/housing snapshot
PlanningFindings.js Tabbed container: Risks / Recommendations / Interventions
RisksPanel.js, RecommendationsPanel.js, InterventionsPanel.js, InterventionCard.js
Right-hand analysis panel content (gated behind a completed analysis)
ScoreBreakdownPanel.js Per-category score breakdown
DataMethodologySection.js Collapsed-by-default section hosting the 4 full AgentCard.js cards
AgentCard.js One card per specialist agent (climate/accessibility/housing/urban design)
AIAssistantPanel.js Docked Ask-AI chat panel
ui/ shadcn/ui primitives (Card, Badge, Tabs, Tooltip, ScrollArea)
constants/planning.js Default location + shared planning constants
lib/utils.js shadcn's cn() className helper
utils/ formatters.js (null-safe display formatting), planningHelpers.js
(cost/weather icon + color maps)
services/analysisApi.js All fetch calls to the backend, in one place
server/ Node/Express backend
agents/ One file per Claude agent (climate, accessibility, housing,
urbanDesign, vision, ask) + coordinator.js orchestrating them
routes/ analysis, ask, conditions, location, upload, visualize, health
services/
claudeService.js Wraps the Anthropic SDK; the client is wrapped again with
the-token-company's withCompression (see Token compression below)
promptCompression.js Shared compact-encoding helpers used by the housing/climate/
accessibility agent prompts
censusService.js U.S. Census Geocoder → block group → ACS 5-Year housing metrics
openMeteoService.js Live weather + US AQI for Climate Agent grounding (no key needed)
femaNfhlService.js FEMA National Flood Hazard Layer flood-zone lookup (no key needed)
nlcdTccService.js NLCD Tree Canopy Cover lookup (no key needed)
transit511Service.js 511 SF Bay Regional GTFS → verified transit proximity metrics
*AgentParser.js Per-agent (housing/climate/accessibility) JSON extraction +
fallback repair for truncated/malformed Claude responses
conditionsService.js Live weather/AQI via Open-Meteo (no key needed) — powers the
frontend's top "Live Data" conditions bar specifically
googleMapsService.js Places (New) Autocomplete/Details, Street View status, image proxies
midjourneyMcpClient.js
OAuth + connection management for Midjourney's MCP server
midjourneyService.js generateImage() — the actual Midjourney call
renderingProvider.js FutureRenderingProvider abstraction over midjourneyService
scripts/ One-off scripts that hit real external APIs: verify-housing-census.js,
verify-climate-{fema,nlcd,openmeteo}.js, verify-accessibility-transit.js,
verify-ask-grounding.js, verify-vision-baselines.js, plus the token
compression benchmark (compression-bench.js, compression_benchmark_ttc*.py)
Data flow for an analysis: LocationSearch resolves an address to {placeId, formattedAddress, latitude, longitude, viewport} (the single source of truth, selectedLocation in App.js) → /api/analyze runs the climate/accessibility/housing/urbanDesign/vision agents in parallel, each grounding itself in a real verified data source (Census ACS, Open-Meteo, FEMA NFHL, NLCD, 511 GTFS) before asking Claude about the site → results populate the AI agent cards, Score Breakdown, Top Risks, and Top Recommendations panels (all empty/idle until that analysis completes — there's no bundled demo data to fall back to). The Current scenario shows a real photo (Street View if covered, otherwise satellite) fetched through /api/location/street-view-image and /api/location/satellite-image — these proxy routes exist so the Google API key never reaches the browser. 2040/2075 generate via Midjourney, using that same real photo as a composition reference by default (or your own uploaded photo).
A diagram of the agent pipeline (parallel specialist agents → synthesis → vision → response) is in
docs/agent-workflow.png.
Setup
git clone https://github.com/KangJustin/urbanpilot.git
cd urbanpilot
npm install
cd server && npm install && cd ..
Copy the two .env.example files and fill in real values:
cp .env.example .env
cp server/.env.example server/.env
Required environment variables
| Variable | Where | What it's for |
|---|---|---|
ANTHROPIC_API_KEY | server/.env | Powers every Claude agent. Without it, each agent's Claude call fails and that section of the analysis shows as temporarily unavailable rather than a result (there's no mock-data fallback). |
GOOGLE_MAPS_SERVER_API_KEY | server/.env | Server-only key for Places API (New), Geocoding API, Street View Static API, Maps Static API. Never put this in the frontend. |
CENSUS_API_KEY | server/.env | U.S. Census Bureau key for the Housing Agent's verified ACS metrics. Without it, the Housing Agent still runs, just without verified Census grounding. |
TRANSIT_511_API_KEY | server/.env | 511 SF Bay Open Data key for the Accessibility Agent's verified GTFS transit metrics. Same degrade-gracefully behavior without it. |
TTC_API_KEY | server/.env | The Token Company key. claudeService.js wraps the Anthropic client with their withCompression on every agent call — see Token compression below. |
REACT_APP_GOOGLE_MAPS_API_KEY | .env (root) | Client-side key for the Maps JavaScript API, used specifically by PresentDayView.js's Street View/satellite panel (the main map workspace itself is Leaflet, not Google Maps JS). Restrict it by HTTP referrer in Google Cloud Console — it's visible in the browser by design. |
Optional:
| Variable | Where | What it's for |
|---|---|---|
MIDJOURNEY_OAUTH_PORT | server/.env | Local callback port for the one-time Midjourney OAuth login (default 8090). |
ALLOWED_ORIGINS | server/.env | Comma-separated extra CORS origins, e.g. for sharing over LAN. |
REACT_APP_API_URL | .env (root) | Override the backend URL the frontend calls (default http://localhost:3001). |
Google Cloud setup
In console.cloud.google.com, enable Billing, then enable: Maps JavaScript API, Places API (New), Geocoding API, Street View Static API, Maps Static API. Create two separate API keys under APIs & Services → Credentials:
- Client key — restrict to HTTP referrers (
http://localhost:3000/*for dev), API-restrict to Maps JavaScript API only. This isREACT_APP_GOOGLE_MAPS_API_KEY. - Server key — API-restrict to the other four APIs above (not Maps JavaScript API). This is
GOOGLE_MAPS_SERVER_API_KEY. Never expose it client-side.
Midjourney setup
No API key needed — the backend connects directly to Midjourney's hosted MCP server
(mcp.midjourney.com) via OAuth. The first time you click "Generate with Midjourney," a
browser window opens asking you to log in and authorize. After that, tokens are cached in
server/.mcp-auth/ (gitignored) and refresh automatically — you won't need to log in again
unless that cache is deleted.
Run it
Two terminals:
npm start # frontend, http://localhost:3000
cd server && node index.js # backend, http://localhost:3001
The frontend hot-reloads on save. The backend doesn't — restart it after editing anything in
server/. (npm run dev in server/ uses node --watch if you'd rather it restart itself.)
Token compression (The Token Company)
Standalone benchmark
We measured the-token-company's with_compression() wrapper
against UrbanPilot's own real production prompts — real Anthropic API calls, real
response.usage.input_tokens, no estimates. Two prompt shapes were tested against Downtown
Berkeley: a structured JSON-schema generation prompt (the Housing Agent's verified-ACS-census
prompt) and a long, prose-heavy context prompt (the Ask UrbanPilot AI assistant's context).
| Prompt type | Raw Anthropic (input tokens) | + the-token-company | Reduction |
|---|---|---|---|
| Structured JSON-schema prompt (Housing Agent) | 924 | 888 | 3.9% |
| Long, prose-heavy context prompt (Ask AI) | 4,757 | 3,775 | 20.6% |
In both cases, compression came at no cost to output quality — every compressed response still
parsed as valid structured JSON and cited the exact verified figures (Census income/rent, real
risk severities, scenario specifics) from the source data, with no hallucination observed.
The benchmark scripts that produced these numbers live in server/scripts/compression-bench.js,
compression_benchmark_ttc.py, and compression_benchmark_ttc_longcontext.py.
Our own compact-encoding layer
Independent of the-token-company, every scoring agent (housing.js, climate.js,
accessibility.js) builds its verified-data block and JSON response schema in a hand-written
terse encoding (services/promptCompression.js) instead of the original pretty-printed,
labeled-block format — no third party involved, just a denser prompt shape. Measured against
the original verbose prompts with real API calls (server/scripts/compression-bench.js):
| Agent | Original (input tokens) | Compact encoding | Reduction |
|---|---|---|---|
| Housing | 1,155 | 834 | 27.8% |
| Climate | 1,507 | 973 | 35.4% |
| Accessibility | 1,247 | 827 | 33.7% |
Output quality held in every case: valid JSON, exact verified-number citations, and intact risk/recommendation arrays.
Production integration
server/services/claudeService.js wraps the live Anthropic client every agent uses
(the-token-company/anthropic's withCompression), so every real request — not just the
benchmark — passes through TTC's compression before reaching Claude. Because the compact
encoding above already strips most of the redundancy out of these prompts, TTC's incremental
contribution on top is small in practice (observed: ~1% additional reduction on an
already-compacted Housing Agent prompt) — the two techniques target the same redundancy, so
gains aren't simply additive. TTC's wrapper exposes live stats
(client.compression.totalTokensSaved), logged on every call.
Known limitations
- No test suite.
npm testreports "No tests found" — this is the current state of the repo, not a missing setup step. - Midjourney can't fetch
localhostor LAN-IP URLs. The 2040/2075 "use the real photo as a reference" feature silently falls back to text-only generation in local dev, since Midjourney rejects non-public reference URLs. It'll work automatically once deployed somewhere with a real public URL — no code change needed then. - No bundled mock data, anywhere. The old Berkeley demo dataset was removed entirely. The
pristine pre-search state is
ReadyToAnalyzeCard.js— purely presentational onboarding copy, never a fabricated score, risk, or recommendation. Every result panel is empty/idle until a real analysis completes; a failed analysis shows a real error, never substituted data. - Google Maps/Street View/satellite imagery is only ever displayed, never used as Midjourney
training/input data except for the explicit, accepted-risk reference-image case above — see
the comments in
renderingProvider.jsandgoogleMapsService.jsfor the reasoning.
Analysis
View
Metric
- 54
- 44
- 16
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- CSSIn code
- ExpressIn code
- HTMLIn code
- JavaScriptIn code
- PythonIn code
- ReactIn code
- Tailwind CSSIn code
- Node.jsClaimed
- RedisClaimed
8 of 10 appear in the indexed code. 2 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
- Claude CodeCommits
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
354 KB
Source files
103
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
KangJustin/urbanpilot
127 files · 11.7 MB · @ 84ee07c
Structure
Interface
46 files · 36%Screens, components and styles rendered to the user.
API & routing
33 files · 26%Request entry points: routes, handlers and controllers.
Application logic
11 files · 9%Domain rules, services and shared utilities.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- JavaScript86%
- Python6%
- Markdown6%
- CSS1%
- HTML0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
package.json
npm · 24- @googlemaps/js-api-loader
- @radix-ui/react-scroll-area
- @radix-ui/react-slot
- @radix-ui/react-tabs
- @radix-ui/react-tooltip
- @sentry/react
- @testing-library/dom
- @testing-library/jest-dom
- @testing-library/react
- @testing-library/user-event
- class-variance-authority
- clsx
- leaflet
- lucide-react
- react
- react-dom
- react-leaflet
- react-scripts
- +6 more
server/package.json
npm · 8- @anthropic-ai/sdk
- @modelcontextprotocol/sdk
- adm-zip
- cors
- dotenv
- express
- multer
- the-token-company
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.