Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Zenvoy — Complete Project Context
What this project is
Zenvoy is a safety-first navigation web app built for a 48-hour hackathon (Stellaris 2026) at KIET Group of Institutions. Team: Build-Bros (Aniket Singh, Aadi Jain, Uday Pratap Singh).
The core idea: Google Maps routes you fastest. Zenvoy routes you safest. It calculates a "Safety Score" for every street segment in Delhi using crime data and AI-detected lighting, then runs a weighted pathfinding algorithm to find the lowest-risk route. It also has a one-click SOS button that sends a real SMS with live GPS coordinates to an emergency contact.
Three repos, three concerns
zenvoy-webzenvoy-backendzenvoy-mobileThis document covers the web repo in full. Backend and mobile are described architecturally so you can build or debug them.
Service map — what each external service does
zenvoy-webfrontend onlyzenvoy-webSearchBar componentzenvoy-backend/scripts/preprocess.pyonly. Never called at runtime.light_scorezenvoy-backend/scripts/preprocess.pyonly. Never called at runtime.zenvoy-backendreads at runtimedelhi_walk.graphmlzenvoy-backendat runtimezenvoy-backend/app/sos.pyCritical distinction: Mapillary and YOLOv8 are PREPROCESSING tools only. They run once before the demo to populate MongoDB. They are never called when a user requests a route. The routing engine reads pre-cached scores from MongoDB.
How routing actually works (the core algorithm)
Mapbox has no involvement in routing. It only draws the lines.
API contract (backend must implement exactly this)
All endpoints served from
http://localhost:8000, exposed via ngrok at demo time.GET /route/safe
GET /route/fast
POST /sos
CORS: Backend must allow all origins (
*) — web and mobile both call it.Coordinate format: Backend returns
[lat, lng]. Mapbox GL JS requires[lng, lat]. The conversion happens inMapView.jsxincoordsToGeoJSON(). Do not change this.MongoDB schema
Collection:
crime_incidents{ "_id": "ObjectId", "location": { "type": "Point", "coordinates": [77.2090, 28.6139] }, "severity": 3, "type": "snatch", "date": "2024-11-15" }Index:
{ "location": "2dsphere" }— must exist for geospatial queries to work.Collection:
edge_safety_scores{ "_id": "ObjectId", "edge_id": "123456789_987654321", "light_score": 0.82, "darkness_penalty": 0.18 }edge_idformat:{osm_node_u}_{osm_node_v}— matches the NetworkX graph edge keys from OSMnx.zenvoy-web — complete file listing
zenvoy-web — complete source code
package.json
{ "name": "zenvoy-web", "version": "0.1.0", "private": true, "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "mapbox-gl": "^3.3.0", "@mapbox/mapbox-gl-geocoder": "^5.0.2" }, "devDependencies": { "@vitejs/plugin-react": "^4.2.1", "vite": "^5.2.0" } }vite.config.js
index.html
.env.example
src/main.jsx
src/index.css
src/api.js
src/App.jsx
src/components/MapView.jsx
src/components/SearchBar.jsx
src/components/SafetyPanel.jsx
See full source in the repo. Key props:
safeRoute— full route response object includingscore_breakdownfastRoute— full route response object includingscore_breakdownopen— boolean controlling slide-up animationonToggle— callback to toggle open statescore_breakdownshape the component expects:src/components/SOSButton.jsx
See full source in the repo. Key behaviour:
handleSend()→ POST /sos → sent/error stateSOS_USER_NAMEandSOS_CONTACT_NUMBERconstants at top of file — update before demozenvoy-backend — architecture (not built yet)
Folder structure to create
requirements.txt
.env variables needed
app/main.py responsibilities
allow_origins=["*"]delhi_walk.graphmlonce at startup into a module-level variable/route/safe,/route/fast,/sosendpointsapp/router.py responsibilities
slat, slng, elat, elngfloatsox.nearest_nodes(G, lng, lat)networkx.shortest_path(G, source, target, weight='length')networkx.shortest_path(G, source, target, weight='safety_cost')safety_costmust be pre-computed on graph edges at startup by readingedge_safety_scoresfrom MongoDB[[lat, lng], ...](note: lat first, lng second — frontend handles the flip)score_breakdownobject computed from aggregated edge scores along the routeapp/safety_score.py responsibilities
get_crime_penalty(lat, lng, db)— querycrime_incidentscollection using$nearSpherewith$maxDistance: 50, count results, normalize 0-1get_darkness_penalty(edge_id, db)— lookupedge_safety_scoresbyedge_id, returndarkness_penaltyfield (default 0.5 if not found)compute_safety_cost(length, lat, lng, edge_id, db)— returnslength * (1 + crime_penalty + darkness_penalty)app/sos.py responsibilities
"🚨 ZENVOY SAFETY ALERT\n{user_name} may need help.\nLocation: https://maps.google.com/?q={lat},{lng}\nSent via Zenvoy"{ "status": "sent", "message": "Alert dispatched" }Graph startup sequence
preprocess.py flow (run once before demo)
delhi_walk.graphmllight_scorefrom brightness heuristic{edge_id, light_score, darkness_penalty: 1-light_score}intoedge_safety_scorescrime_incidentswithlocationas GeoJSON Pointzenvoy-mobile — architecture (not built yet)
Stack
react-native-mapsfor map displayexpo-locationfor GPSKey difference from web
api.jsstructure withUSE_MOCKflagexpo-locationreplacesnavigator.geolocationfor SOSCoordinate note
Same
[lat, lng]format from backend. React Native Maps expects{ latitude, longitude }objects — conversion needed in the mobile equivalent ofMapView.Demo routes (pre-compute everything for these two)
Route 1: Hauz Khas Metro Station → Hauz Khas Village
Route 2: Lajpat Nagar Metro → Central Market
For each demo route, pre-cache in MongoDB and verify safe ≠ fast visually on map before demo.
Critical rules for any agent working on this
Never change the coordinate format contract. Backend returns
[lat, lng]. Frontend flips to[lng, lat]for Mapbox incoordsToGeoJSON(). Do not "fix" this — it is intentional.USE_MOCK = trueis the safety net. If backend breaks during demo, flip this and the app works with pre-cached data. Never delete mock data.CORS must be
allow_origins=["*"]on the backend. Both web (localhost:5173) and mobile (ngrok) call it.The graph file is NOT in the repo.
delhi_walk.graphmlis gitignored and lives only on the developer's machine. Download command: runscripts/download_graph.pywhich callsox.graph_from_place("Delhi, India", network_type="walk").MongoDB
2dsphereindex must exist oncrime_incidents.locationbefore crime queries will work. Creating it:db.crime_incidents.create_index([("location", "2dsphere")]).Twilio free tier requires verified "To" numbers. The contact number in
SOSButton.jsxand in any test must be verified in the Twilio console first.Response time must be under 5 seconds. Load the graph at startup, not per request. Pre-compute
safety_coston edges at startup, not per request.Known issues / watch out for
mapboxgl-ctrl-geocodermounts into a DOM ref — in React Strict Mode (dev), effects run twice. Theif (originGeoRef.current) returnguard prevents double-mounting.map.isStyleLoaded()check inMapView.jsxis necessary — route updates can fire before the map style is fully loaded, causing silent failures ongetSource().shortest_pathwill throwNetworkXNoPathif origin/destination nodes are not connected. Backend must catch this and return a 404 with a clear message.nearest_nodestakes(G, X, Y)where X=longitude, Y=latitude — this is counterintuitive. Do not swap them.