Personal student project. Not affiliated with, endorsed by, or maintained by Northwestern University. Building hours and access rules can change; check the official sources linked in
backend/data/before relying on a route.
Walking routes on Northwestern's Evanston campus that stay indoors, stay in the shade and avoid inclines. Area: the whole Evanston campus, from Clark St to Lincoln St. 32 buildings can be walked through, and 121 named Northwestern buildings can be picked as a start or destination.
Which buildings are shown (is_northwestern() in tools/build_graph.py): a building counts if OSM says it is run by Northwestern University. Otherwise it counts if it is inside the OSM "Northwestern University" campus outline or in ZIP 60208, minus two exceptions:
- the Garrett-Evangelical seminary grounds;
- a short list of on-campus buildings run by other organizations (Sheil, Hillel, University Christian Ministry, Canterbury House, Howes Chapel, Levere Memorial Temple).
Downtown Evanston buildings (shops, hotels, churches, apartments) are not drawn or listed, and the basemap's own building shapes are hidden. They still cast shadows in the routing, but shadows are drawn only on and around campus.
frontend/: Next.js + TypeScript + MapLibre GL JS. It is a single page: a map plus a panel (a bottom sheet on phones).backend/: Python FastAPI. It does the routing (A*), sun position, building shadows and access rules.
cd backend && python3.12 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/uvicorn app.main:app --port 8000cd frontend && npm install && npm run devOpen http://localhost:3210. The frontend forwards /api/* to API_URL, which defaults to http://127.0.0.1:8000.
Tests: cd backend && .venv/bin/python -m pytest (26 tests) and cd frontend && npm run typecheck.
- Network (
backend/tools/build_graph.py→data/graph.json).- Outdoor edges: every walkable OpenStreetMap way, split into pieces of 10 m or less.
- Elevation: each node gets a value from the USGS 3DEP 1 m lidar DEM. Slope is measured over 20 m of path, because lidar is noisy right next to walls.
- Indoor nodes: each building's mapped doors plus its bridge landings.
- Bridges: only those in
data/connections.json.
- Per request (
backend/app/router.py).- Sun position: NOAA algorithm, for the departure time in Evanston time.
- Shadows: each footprint's height is extruded away from the sun. Each outdoor edge records how many metres of it are in shadow.
- Access: each building is public, Wildcard-only or closed at that time (
data/access.json,data/closures.json). Closed buildings and bridges are removed. Users without a Wildcard can only enter by an OSMentrance=maindoor. Leaving a building is always allowed.
- Cost. Every mode adds an incline penalty:
- 40 m per floor change
- 40 m per outdoor staircase
- extra cost for slopes over 2%
- Modes. All three use A* with a straight-line heuristic.
- Shortest: least distance plus the incline penalty.
- Winter / Shade: the router tries increasing weights on outdoor metres (Winter) or sun-exposed metres (Shade). It keeps the least-exposed route that is at most 10% longer than Shortest. It returns Shortest unless exposure drops by at least 15 m and at least 15%.
- Shade after sunset: returns Shortest.
| Data | Source |
|---|---|
| Walkways, buildings, doors | OpenStreetMap (Overpass), fetched 2026-10-17, tools/fetch_osm.py |
| Building heights | Overture Maps 2026-09-23.1 (OSM + Microsoft ML Buildings), tools/fetch_overture.py |
| Elevation | USGS 3DEP 1 m DEM (ImageServer getSamples), cached in data/raw/elevation.json |
| Indoor connections | data/connections.json: each has a quote and URL from a Northwestern page |
| Hours / Wildcard | data/access.json: Facilities standard-hours list (25 Evanston buildings), library LibCal hours, Bienen, Norris and Religious Life pages |
| Basemap / satellite | OpenFreeMap (OSM) / Esri World Imagery |
Buildings that can be walked through (data/access.json):
- Facilities standard hours (weekdays 7a–6p: main entrance open, other doors by Wildcard; 6p–11p and weekends 7a–11p: Wildcard; 11p–7a: authorized only):
- Tech, Ford, Cook, Hogan, Pancoe, Silverman, Ryan Hall, Frances Searle, Catalysis Center, Cresap, Lunt, Swift, Annenberg
- 2001 Sheridan (Jacobs Center), Annie May Swift, Fisk, Harris, Kresge, Crowe, Locy, McCormick Foundation Center, University Hall, Wirtz Center
- Bienen (Ryan Center for the Musical Arts, Regenstein): standard hours, but "A registered Wildcard is required to enter all Bienen School spaces."
- Libraries (University Library, Deering): Wildcard 8a–midnight (shorter on Fri–Sun). The public may enter Mon–Sat until 6pm. These are this week's LibCal hours, and they change by week.
- Mudd Library: Wildcard 24/7.
- Norris, Alice Millar Chapel and Parkes Hall: posted hours, but the pages don't say who may enter, so only Wildcard holders are routed through.
- ADA entrances open to everyone weekdays 7a–6p. They are used only where the side the Facilities page describes matches a door drawn in OSM: Kresge (east), McCormick Foundation Center (south), University Hall (south).
- Listed but not drawable: John J. Louis Hall is not in OSM. Swift Annex has no separate outline.
Verified connections between buildings. Each has a source quote:
- Tech–Mudd, Tech–Cook, Tech–Ford, Mudd–Cook, Cook–Hogan, Cook–Pancoe, Hogan–Pancoe, Pancoe–Silverman, Silverman–Ryan Hall
- University Library–Deering, Kresge–Crowe, Ryan Center–Regenstein
Shown on the map but never routed:
- Tech–Catalysis (OSM only)
- Alice Millar–Parkes (Wikipedia only)
Searched for but not found (not in the graph):
- Engineering: Ford–Mudd, Ford–Cook, Hogan–Silverman, Hogan–Tech, Silverman–Tech, Ryan Hall–Tech, Ryan Hall–Catalysis
- South campus: Locy–Lunt, Locy–Fisk, Fisk–McCormick Foundation Center, Louis–McCormick Foundation Center, Harris–University Hall, Swift–Cresap, Ryan Center–Pick-Staiger
- No connections at all: Norris, Annenberg, Kellogg Global Hub, Block Museum, residence halls
Unknown hours (never routed through): Kellogg Global Hub, Mudd Hall's non-library floors, Scott Hall, Pick-Staiger, residence halls (residents only).
- Profiles. The app asks "I am": NU student (has a Wildcard), Kellogg student (Wildcard plus Kellogg-only spaces) or Visitor (no Wildcard).
- School-only buildings. A building can be limited to one school with
only_forinaccess.json, but only when a source documents it. - Student reports. Tap a building, then Wrong info?. The choices are "Needs a Wildcard", "Kellogg students only" and "Closed right now". Reports are saved to
data/reports.jsonand take effect at once. - Reports only make access stricter. They never open a building, so a wrong report can make a route longer, never send someone to a locked door. A "closed" report stops counting after 3 days.
- Seeded reports. Kellogg Global Hub has two, entered 2026-10-17: Wildcard needed, and Kellogg students only. I found no official page on Hub access.
- Paths inside buildings: these are straight-line estimates (× 1.3) between doors and bridge landings, not mapped corridors.
- Bridge landing points: only Tech–Mudd's comes from OSM. The others join the nearest points of the two footprints.
- Bridge floors: "second floor" and "raised" bridges cost one floor change each way. Tech–Ford and Silverman–Ryan have no documented floor, so one floor change is assumed and the route says so.
- Interior doors: they can have their own hours or card readers (Facilities page). These are not modeled.
- Doors: many buildings (Cook, Hogan, Pancoe, Silverman, Ryan Hall, Lunt, Locy and others) have no doors in OSM. Routes to them end at the nearest walkway, and the route says so.
- Bridges counted as outdoor: a bridge counts as outdoor exposure unless a source says it is enclosed. Only the Tech–Ford "glass bridge" is counted as enclosed.
- Shadows: these come from buildings only. Trees, terrain and the time spent walking are ignored.
- Building heights: these come from Overture and may be off by a floor. Tech's 11.7 m looks low.
- Holidays: university holidays are not modeled.
- To change the area, edit
BBOXintools/fetch_osm.py, then re-runfetch_osm.py,fetch_overture.pyandbuild_graph.py. - To make a building walk-through, add it to
data/access.jsonwith a documented schedule. Library hours need updating each term. - Add connections to
data/connections.json, each with a source quote and URL. - To get real corridor geometry, add
indoor=corridorways (or official floor plans) for a building. Then replace its straight-line interior edges inbuild_indoor().
- Code: MIT. See LICENSE.
- Map data in
backend/data/: derived from OpenStreetMap and Overture Maps. It is available under the Open Database License (ODbL) 1.0, © OpenStreetMap contributors and Overture Maps Foundation.- Elevation: from USGS 3DEP (public domain).
- Building hours and connection notes: facts with short quotes, each linked to its Northwestern source page. The source pages belong to their owners.
- Not stored in this repository:
- Basemap tiles: OpenFreeMap.
- Satellite imagery: Esri World Imagery. It is loaded live from Esri's servers under Esri's terms.