Seneca Polytechnic Hackathon 2026 Theme 3 β Community Energy, Equity and Sustainability | Problem Statement 2
A real-time web app that helps Toronto communities stay safe during extreme heat events β combining live Raspberry Pi sensor data, urban heat island mapping, GPS-based routing, and cross-checked risk alerts.
https://safespot-frontend-qahl.onrender.com/
In June 2021, a heat dome killed 570 people in British Columbia. Many victims lived near cooling centres β but had no way of knowing. Climate impacts like heatwaves disproportionately affect vulnerable populations, and there's no simple way to see where climate risk and limited shelter access overlap.
SafeSpot Toronto deploys a Raspberry Pi temperature sensor in a high-risk heat island zone, cross-checks live sensor readings against urban heat island data, and guides users to the nearest safe space when conditions become dangerous.
Unlike weather apps that report a city-wide average, SafeSpot measures the actual temperature where you are.
| Name | Role |
|---|---|
| Gary | Hardware β Raspberry Pi & Temperature Sensor |
| Marcos | Backend β Node.js (Hono + Drizzle + SQLite) |
| Paul | Frontend β Next.js, Leaflet.js Map Integration |
| Seulgi | Frontend β UI/UX Design, Alert System |
| Arun | Frontend β GPS Location, Nearest Shelter Routing |
| Layer | Technology |
|---|---|
| Frontend | Next.js, Tailwind CSS, Leaflet.js |
| Backend | Hono.js (Node.js) + Drizzle ORM + SQLite |
| Deployment | Render (backend) Β· Docker |
| Hardware | Raspberry Pi + Temperature Sensor |
| Map Library | Leaflet.js with GeoJSON layers |
| Data Sources | ArcGIS REST API, City of Toronto Open Data |
- Raspberry Pi sensor physically deployed in a heat island zone
- Captures actual ground-level temperature β not a city-wide average
- Sensor readings POSTed to Hono backend at regular intervals
- Frontend polls
GET /api/sensor-latestevery 5 seconds (near real-time) - Source badge: distinguishes live sensor data (
sensor) from test overrides (override)
- Interactive Leaflet map with Toronto's urban heat island layer
- Colour-coded risk areas: π΄ High / π Medium / π‘ Low Heat Area
- Data from ArcGIS: Impervious Surface and Urban Heat Island Effect in Toronto
- Historical surface temperature averages (not real-time β this is where the Pi matters)
-
User's GPS location is checked against the heat island GeoJSON polygons using ray-casting point-in-polygon (no external dependencies)
-
getCombinedRisk(zoneLevel, sensorTemp)calculates actual risk:Zone Sensor Temp Risk Level Any < 30Β°C Silent (no warning) HIGH 30β35Β°C β οΈ CautionHIGH 35β40Β°C π¨ Danger HIGH β₯ 40Β°C π΄ Extreme MEDIUM 35β40Β°C β οΈ CautionMEDIUM β₯ 40Β°C π¨ Danger -
Risk card only appears at caution or above β no false warnings on safe-temperature days
- Full-screen alert banner when sensor reads 35Β°C or above AND user is in a heat island zone
- Web Audio API siren sound (extreme / danger / caution tones)
- Browser push notifications with
requireInteractionfor extreme alerts - Alert levels:
safeβcautionβdangerβextreme
- Browser GPS detects the user's current location
- Haversine formula calculates distance to all cooling centres and libraries
- Shows nearest cooling centre + nearest library with compass direction
- Google Maps directions link with one tap
| Method | Endpoint | Description |
|---|---|---|
GET |
/ |
Health check |
GET |
/health |
Health check |
POST |
/api/sensor-data |
Receives temperature from Raspberry Pi |
GET |
/api/sensor-latest |
Returns most recent sensor reading with alert level |
POST |
/api/sensor-override |
Manual temperature input for testing |
GET |
/docs |
Swagger UI (OpenAPI 3.1) |
GET |
/openapi.json |
OpenAPI spec |
Raspberry Pi (Temperature Sensor β deployed in heat island zone)
β
βΌ POST /api/sensor-data {"temperature": 36.2}
Hono Backend (Node.js + Drizzle + SQLite) β deployed on Render
β stores reading with timestamp + source
βΌ GET /api/sensor-latest (every 5s)
Next.js Frontend
β
ββ SensorCard β live Pi temperature + alert bar
ββ WeatherCard β city outdoor temp (OpenWeather API)
ββ ShelterMarkers β Leaflet map + heat island layer + shelter pins
ββ NearestShelter β GPS location + heat zone cross-check + routing
β
βΌ
User Browser (GPS + push notifications + sound alerts)
| Dataset | Source | Format |
|---|---|---|
| Urban Heat Island Effect | ArcGIS REST API (Seneca) | GeoJSON |
| Air Conditioned & Cool Spaces | City of Toronto Open Data | GeoJSON |
| Library Branch Locations | City of Toronto Open Data | GeoJSON |
| Live Temperature | Raspberry Pi Sensor | POST via Hono |
| Outdoor Weather | OpenWeather API | JSON |
SafeSpot/
βββ frontend/ # Next.js app
β βββ app/
β β βββ page.js # Main page (3 sections: Story, How It Works, Dashboard)
β β βββ layout.js
β βββ components/
β β βββ SensorCard.js # Live Pi temperature + alert bar + sound
β β βββ AlertBanner.js # Full-screen heat warning banner
β β βββ NearestShelter.js # GPS + heat zone cross-check + routing
β β βββ ShelterMarkers.js # Leaflet map + heat island + shelter pins
β β βββ WeatherCard.js # OpenWeather city temperature
β β βββ UserLocation.js # GPS marker on map
β β βββ Navbar.js
β β βββ Footer.js
β βββ .env.local # API keys + NEXT_PUBLIC_BACKEND_URL
βββ backend/ # Hono server
βββ Dockerfile # Multi-stage Docker build (Node 22 slim)
βββ src/
β βββ index.js # Server entry point (port from env)
β βββ app.js # Hono app factory + CORS + Swagger
β βββ routes/
β β βββ sensor.js # POST /api/sensor-data, GET /api/sensor-latest, POST /api/sensor-override
β βββ sensor-store.js # DB read/write logic
β βββ alerts.js # getAlertLevel() β safe/caution/danger/extreme
β βββ schema.js # Drizzle schema (sensor_readings table)
β βββ db.js # SQLite init + mock data seeding
βββ tests/ # Vitest test suite
cd backend
npm install
npm run dev
# β http://localhost:8000
# β Swagger docs: http://localhost:8000/docscd backend
docker build -t safespot-backend .
docker run -p 8000:8000 safespot-backendThe backend is live on Render:
https://safespot-backend-bbn6.onrender.com
Note: Render's free tier spins down after 15 minutes of inactivity. The first request after idle may take 30β60 seconds to respond.
cd frontend
npm install
npm run dev
# β http://localhost:3000NEXT_PUBLIC_BACKEND_URL=https://safespot-backend-bbn6.onrender.com
NEXT_PUBLIC_OPENWEATHER_KEY=your_key
NEXT_PUBLIC_MAPBOX_TOKEN=your_tokenimport requests
import time
BACKEND_URL = "https://safespot-backend-bbn6.onrender.com"
while True:
temp = read_sensor() # DHT22 / DS18B20
requests.post(f"{BACKEND_URL}/api/sensor-data", json={"temperature": temp})
time.sleep(5)- Web app opens β Toronto map loads with heat island risk areas
- Browser GPS detects user location β marked on map
- App checks if user is inside a heat island polygon (point-in-polygon)
- Temperature is raised above 35Β°C (via Raspberry Pi sensor or simulation)
- App cross-checks: heat island zone + dangerous temperature β π¨ Danger alert fires
- Full-screen banner + siren sound + push notification
- App displays: "Nearest Cooling Centre: [Name], 0.3 km away β Get Directions"
| PS2 Requirement | Our Solution |
|---|---|
| Visualize climate risk areas | Urban heat island layer (High / Medium / Low Heat Areas) |
| Identify vulnerable communities | Heat zone colour coding by risk area |
| Show proximity to safe shelter | Cooling centres & libraries with GPS routing + Google Maps directions |
| Help communities prepare | Real-time alerts + nearest safe space guidance |
| Innovative data approach | Raspberry Pi sensor cross-checked with heat island map for actual risk |
Weather APIs report a city-wide average. Urban heat islands mean a specific block can be 5β10Β°C hotter than the official city temperature. SafeSpot deploys the Pi inside a heat island zone to measure the actual ground-level temperature β then cross-checks that reading against the historical heat map to calculate your real exposure risk.
"Official forecast: 28Β°C. Your heat island zone: 36Β°C. That difference can be life or death."