AI-powered pedestrian safety routing for New York City β built for students walking at night.
SafeWalk NYC combines real-time crime data, streetlight complaints, traffic signal density, and live news alerts to score walking routes 0β100. Claude AI explains the recommendation in plain English so users make informed decisions, not just fast ones.
User: "Walk me from 3009 Broadway to Times Square"
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3 ROUTES FOUND RECOMMENDED: Route 1 β
β β
β Route 1 β
Best 98/100 Safe via Broadway 3.97 mi β
β Route 2 98/100 Safe via Central Park 4.74 mi β
β Route 3 97/100 Safe via Riverside 4.43 mi β
β β
β "Route 1 via Broadway is your best option β zero crime β
β incidents, shortest at 3.97 mi, well-traveled corridor." β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
All 3 routes appear on the Mapbox map simultaneously, each color-coded by safety score with floating score badges.
- Conversational input β describe your destination naturally; Claude parses intent into street addresses
- 3 route alternatives β direct path + two corridor variants (east/west) scored independently
- Live safety data β NYPD crime reports (90-day window), NYC 311 lighting complaints, traffic signal density
- Real-time news β Linkup API surfaces construction closures and safety alerts near your route
- Score badges on map β all 3 routes visible simultaneously with floating score chips
- Per-route cards β each card shows its distinguishing corridor, Crime/Lighting/Signal bars, and an expandable explanation
- Time-aware scoring β same street scores lower at 2 AM (Γ2.0) than at noon (Γ1.0)
- Campus safety alerts β "I feel unsafe" button routes to Columbia, NYU, Barnard, or 911
User message (e.g. "Walk me from 3009 Broadway to Times Square")
β
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β Step 1 β Claude: Parse Intent β
β β
β Extracts origin + destination as β
β precise street addresses. β
β "Columbia Biz School" β "665 W 130th St"
βββββββββββββββββββββ¬βββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββ
β Step 2 β Geocoding (parallel) β
β β
β Mapbox Geocoding v5 β
β β’ types: address, poi, neighborhood β
β β’ bbox: NYC boroughs only β
β β’ NYC coordinate validation β
β β
β Nominatim (OSM) fallback β
β β’ handles institutional POIs β
β β’ viewbox: NYC, bounded=1 β
βββββββββββββββββββββ¬βββββββββββββββββββ
β origin + dest coordinates
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Step 3 β Mapbox Directions (3 parallel requests) β
β β
β Request A: direct route (no waypoint) β
β Request B: eastern variant (mid + perp Γ 0.012Β°) β
β Request C: western variant (mid β perp Γ 0.012Β°) β
β β
β Deduplicate by 2nd-most-prominent street + distance β
β β up to 3 distinct MapboxRoute objects β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Step 4 β Segment Splitting (~200 m chunks) β
β β
β Each Mapbox step β RouteSegment { start, end, geom } β
β Shared endpoint deduplication β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Step 5 β Data Enrichment (per segment, all parallel) β
β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββ β
β β NYPD Crimes β β 311 Lighting β β Traffic β β
β β 250 m radius β β 150 m radius β β Signals β β
β β 90 days back β β open only β β 150 m β β
β β 5uac-w243 β β erm2-nwe9 β β e5mv-wy8β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββ β
β β
β + Linkup news (once per route, at route midpoint) β
β β construction closures, crime alerts, detours β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Step 6 β Safety Scoring β
β β
β Per segment: β
β crimePenalty = Ξ£(crime Γ severity) [FELONY=10] β
β lightPenalty = openComplaints Γ 8 [cap 40] β
β signalPenalty = 0/5/15 for β₯2/1/0 signals β
β newsPenalty = Ξ£ severity [cap 15] β
β β
β raw = crimeΓ0.35 + lightΓ0.25 β
β + signalΓ0.15 + newsΓ0.10 β
β score = 100 β raw Γ timeModifier β
β β
β timeModifier: 06β20h Γ1.0 Β· 20β24h Γ1.5 β
β 00β04h Γ2.0 Β· 04β06h Γ1.3 β
β β
β Route score = distance-weighted avg of segments β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Step 7 β Claude: Explanation β
β β
β Summarises all 3 routes, recommends the best by name, β
β cites crime counts, lighting issues, worst block. β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β
βΌ
API Response
{ routes[], recommended, explanation }
Crime (35%) Lighting (25%)
β β
NYPD data βββββ€ 311 data βββ€
90 days β open only β
250 m β 150 m β
βΌ βΌ
crimePenalty lightPenalty
β β
ββββββββββ¬βββββββββββ
β
Signal (15%) βββββ€βββ Traffic signals 150 m
News (10%) βββββ€βββ Linkup alerts/closures
β
βΌ
raw = weighted sum
β
βΌ
score = 100 β raw Γ T
β
βββββββββββββββββΌββββββββββββββββ
βΌ βΌ βΌ
T = 1.0 T = 1.5 T = 2.0
(6 AMβ8 PM) (8 PMβmidnight) (midnightβ4 AM)
Score labels: β₯ 80 π’ Safe
β₯ 60 π‘ Moderate
β₯ 40 π Use Caution
< 40 π΄ High Risk
route-planner/page.tsx
β
β User types destination
βΌ
ChatInput.tsx ββPOST /api/chatβββΊ Claude (conversational)
β β
β onRouteExtracted(org, dst) β plain-text reply
βΌ βΌ
handleRouteExtracted() Chat bubble in sidebar
β
β POST /api/score-route
βΌ
ApiResponse { routes[], recommended, explanation }
β
ββββΊ MapView.tsx (Mapbox GL JS, SSR: false)
β β’ All 3 polylines drawn simultaneously
β β’ Color by score: green / yellow / orange / red
β β’ Selected route: opacity 1.0, width 6 px
β β’ Others: opacity 0.45, width 3 px
β β’ Score badge (pill) at each route midpoint
β β’ Origin (blue dot) + Destination (red dot)
β β’ Camera fits all routes on load
β
ββββΊ RouteCard.tsx Γ3 (one per route)
β’ "Route N Β· via [street]"
β’ β
Best badge on recommended
β’ Safety score + Shield icon
β’ Duration Β· Distance Β· Incidents
β’ Crime / Lighting / Signals progress bars
β’ Expandable explanation
- Recommended β full Claude narrative
- Others β auto-generated from data
safewalk-nyc/
βββ app/
β βββ page.tsx Landing page with email signup
β βββ layout.tsx Root layout β loads globals.css
β βββ globals.css Mapbox CSS + Tailwind directives
β βββ route-planner/
β β βββ page.tsx Main app: map + chat sidebar
β βββ api/
β βββ score-route/route.ts POST β full 7-step scoring pipeline
β βββ chat/route.ts POST β conversational assistant
β βββ alert/route.ts POST β campus safety alert routing
β
βββ components/
β βββ MapView.tsx Mapbox GL map (dynamic import, SSR false)
β βββ RouteCard.tsx Per-route card with safety bars
β βββ ChatInput.tsx Conversational input + message history
β βββ AlertButton.tsx Persistent "I feel unsafe" button
β βββ SignupForm.tsx Email signup with university detection
β
βββ lib/
β βββ types.ts All shared TypeScript interfaces
β βββ scoring.ts scoreSegment() Β· scoreRoute() Β· scoreLabel()
β βββ alert-routing.ts Email domain β campus safety contacts
β βββ api/
β βββ nyc-open-data.ts NYPD Β· 311 Β· traffic signal fetchers
β βββ linkup.ts Linkup search + news penalty calculator
β βββ claude.ts Anthropic SDK wrappers
β
βββ .env.local.example Required environment variables template
βββ CLAUDE.md Project conventions (AI-assisted coding)
βββ README.md
| Layer | Technology |
|---|---|
| Framework | Next.js 14 β App Router, TypeScript |
| Styling | Tailwind CSS |
| Map | Mapbox GL JS (dynamic import, SSR disabled) |
| AI | Anthropic Claude claude-sonnet-4-6 |
| Crime data | NYC Open Data β NYPD Complaints 5uac-w243 |
| Lighting data | NYC Open Data β 311 Service Requests erm2-nwe9 |
| Signal data | NYC Open Data β Traffic Signals e5mv-wy8f |
| News alerts | Linkup API |
| Geocoding | Mapbox Geocoding v5 + Nominatim (OSM) fallback |
| Routing | Mapbox Directions v5 β 3 variants via forced waypoints |
git clone https://github.com/Tanmay244/safewalk-nyc.git
cd safewalk-nyc
npm installcp .env.local.example .env.localEdit .env.local:
ANTHROPIC_API_KEY=sk-ant-... # console.anthropic.com
LINKUP_API_KEY=... # linkup.so
NEXT_PUBLIC_MAPBOX_TOKEN=pk.eyJ... # mapbox.comnpm run dev
# β http://localhost:3000Navigate to /route-planner and try:
Walk me from 3009 Broadway to Times Square
You should see 3 routes on the map with safety score badges.
Score 3 walking route alternatives between two NYC addresses.
Request body
{
"message": "Walk me from 3009 Broadway to Times Square",
"userLocation": { "lat": 40.8095, "lng": -73.9634 }
}Response
{
"routes": [
{
"path": { "type": "LineString", "coordinates": [[...]] },
"score": 98,
"scoreLabel": { "label": "Safe", "color": "green" },
"distanceMiles": 3.97,
"durationMinutes": 74,
"label": "via Broadway",
"segments": [
{
"streetName": "Broadway",
"score": 98,
"crimePenalty": 0,
"lightingPenalty": 4,
"trafficSignalPenalty": 0,
"crimeCount": 0,
"openLightingIssues": 1,
"nearbySignals": 3
}
]
}
],
"recommended": 0,
"explanation": "Route 1 via Broadway is your best option...",
"origin": { "lat": 40.8095, "lng": -73.9634 },
"destination": { "lat": 40.7580, "lng": -73.9855 },
"timeOfDay": 13
}Conversational assistant for safety questions and route queries.
Request
{
"messages": [{ "role": "user", "content": "Is it safe to walk at midnight?" }],
"context": { "currentLocation": "Columbia University" }
}Response
{
"reply": "Walking late at night has some risks worth knowing...",
"route": { "origin": "Columbia University", "destination": "Penn Station" }
}Route a safety alert to campus security or emergency contacts.
Request
{
"email": "student@columbia.edu",
"location": { "lat": 40.8075, "lng": -73.9626 },
"message": "I feel unsafe near 120th and Amsterdam"
}Email domain detection automatically routes alerts to the right team.
| University | Domain | Emergency |
|---|---|---|
| Columbia University | columbia.edu |
212-854-5555 |
| NYU | nyu.edu |
212-998-2222 |
| Barnard College | barnard.edu |
Barnard Security |
| The New School | newschool.edu |
Campus Safety |
| Fordham University | fordham.edu |
Fordham Security |
Adding a university β append to UNIVERSITIES in lib/alert-routing.ts:
{ name: "My University", domain: "myuni.edu", safetyEmail: "safety@myuni.edu", color: "#3b82f6" }| Score | Label | Meaning |
|---|---|---|
| 80β100 | π’ Safe | Well-lit, low crime, good pedestrian signals |
| 60β79 | π‘ Moderate | Some concerns; take normal precautions |
| 40β59 | π Use Caution | Elevated risk; stay aware of surroundings |
| 0β39 | π΄ High Risk | Avoid if possible, especially at night |
Scores are time-adjusted β the same block is scored lower after dark:
06:00 β 20:00 Γ1.0 (baseline)
20:00 β 24:00 Γ1.5 (evening penalty)
00:00 β 04:00 Γ2.0 (night penalty)
04:00 β 06:00 Γ1.3 (pre-dawn penalty)
- Real-time MTA transit alerts β route avoidance
- Email delivery for safety alerts (Resend / SendGrid)
- Persist signups to Supabase
- Rate limiting via
@upstash/ratelimit - User accounts + saved routes
- Mobile app (React Native / Expo)
Pull requests welcome. For major changes, open an issue first.
MIT β built for NYC students who deserve to feel safe getting home.