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.
Live Demo: https://safe-spot-seneca-hackathon.vercel.app
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 + PostgreSQL) |
| 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 16, React 19, Tailwind CSS v4, Leaflet.js |
| Backend | Hono.js (Node.js) + Drizzle ORM + PostgreSQL |
| Deployment | Vercel (frontend) Β· Render (backend) |
| Hardware | Raspberry Pi + Temperature/Humidity Sensor |
| Map Library | Leaflet.js with CartoDB Dark Matter tiles |
| 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/sensorsevery 5 seconds (near real-time) - Source badge: distinguishes live sensor data (
sensor) from test overrides (test) - Smart storage: danger/extreme readings stored immediately; caution throttled to 1 min; safe throttled to 5 min
- Interactive Leaflet map with Toronto's urban heat island layer
- Colour-coded risk areas: π΄ High (β₯30Β°C surface) / π Medium (β₯25Β°C) / π‘ Low
- 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 heat island GeoJSON polygons using ray-casting point-in-polygon (no external dependencies)
-
Combined risk from zone + sensor temperature:
Sensor Temp Risk Level < 30Β°C Safe (no alert) 30β35Β°C β οΈ Caution35β40Β°C π¨ Danger β₯ 40Β°C π΄ Extreme -
Alert fires only at caution or above β no false warnings on safe-temperature days
- Displays Toronto's current temperature from OpenWeather API (
data.main.temp) - Refreshes every 60 minutes
- Shows city name and weather description alongside actual temperature
- Full-screen alert banner when sensor reads 35Β°C or above
- Web Audio API siren sound:
- Extreme (β₯40Β°C): fast wailing siren (600β1400 Hz, 2s)
- Danger (35β40Β°C): slow wailing siren (500β1100 Hz, 1.6s)
- Caution (30β35Β°C): morse-style beep pattern (880 Hz sine)
- Browser push notifications on alert level change
- Browser GPS detects the user's current location (cached to localStorage)
- Haversine formula calculates distance to all cooling centres and libraries
- Shows nearest cooling centre + nearest library with compass direction (N, NE, Eβ¦)
- Google Maps directions link with one tap
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Health check β returns { "status": "ok" } |
POST |
/api/sensors |
Receive temperature reading from Raspberry Pi |
GET |
/api/sensors |
Return latest sensor reading with alert level |
GET |
/api/alerts |
List alert logs (filterable by level, zone, date) |
GET |
/api/logs/alerts |
Alias for /api/alerts |
GET |
/docs |
Swagger UI (OpenAPI 3.1) |
GET |
/openapi.json |
OpenAPI spec |
{
"temperature": 36.2,
"humidity": 65,
"source": "sensor",
"lat": 43.7,
"lng": -79.42,
"zone": "high"
}{
"temperature": 36.2,
"humidity": 65,
"humidex": 38.5,
"timestamp": "2026-05-30T15:23:42.123Z",
"alert": {
"level": "danger",
"message": "Extreme Heat Warning - Find a Cool Space Now"
}
}Raspberry Pi (Temperature/Humidity Sensor β deployed in heat island zone)
β
βΌ POST /api/sensors {"temperature": 36.2, "humidity": 65}
Hono Backend (Node.js + Drizzle + PostgreSQL) β deployed on Render
β stores reading Β· calculates humidex Β· logs alert events
βΌ GET /api/sensors (every 5s)
Next.js Frontend β deployed on Vercel
β
ββ Sensor Card β live Pi humidex + alert level
ββ City Weather β Toronto actual temp (OpenWeather API)
ββ Leaflet Map β heat island polygons + shelter markers
ββ Nearest Shelter β GPS cross-check + Haversine routing
β
βΌ
User Browser (GPS + push notifications + audio alerts)
| Dataset | Source | Format |
|---|---|---|
| Urban Heat Island Effect | ArcGIS REST API | GeoJSON |
| Air Conditioned & Cool Spaces | City of Toronto Open Data | GeoJSON |
| Library Branch Locations | City of Toronto Open Data | GeoJSON |
| Live Temperature + Humidity | Raspberry Pi Sensor | POST via Hono |
| City Weather | OpenWeather API | JSON |
| Reverse Geocoding | OpenStreetMap Nominatim | JSON |
SafeSpot/
βββ frontend/ # Next.js 16 app
β βββ app/
β β βββ page.js # Main page (Story Β· How It Works Β· Dashboard)
β β βββ layout.js # Root layout + metadata
β βββ components/
β β βββ AlertBanner.js # Full-screen heat warning banner
β β βββ SensorCard.js # Live Pi humidex + alert indicator
β β βββ WeatherCard.js # OpenWeather city temperature
β β βββ NearestShelter.js # GPS + heat zone cross-check + routing
β β βββ ShelterMarkers.js # Leaflet map + heat island + shelter pins
β β βββ UserLocation.js # GPS marker on map
β β βββ LogViewer.js # Sensor & alert log viewer
β β βββ SmoothScroll.js # Lenis smooth scroll wrapper
β β βββ Navbar.js
β β βββ Footer.js
β βββ .env.local # API keys (see Setup)
βββ backend/ # Hono.js API server
βββ src/
β βββ index.js # Server entry point (port from env)
β βββ app.js # Hono app + CORS + Swagger UI
β βββ db.js # PostgreSQL pool + table init
β βββ schema.js # Drizzle ORM schema
β βββ humidex.js # Canadian humidex formula
β βββ alerts.js # getAlertLevel() thresholds
β βββ sensor-store.js # sensor_readings DB read/write
β βββ alerts-store.js # alert_logs DB read/write
β βββ routes/
β βββ sensor.js # All API route definitions
βββ tests/ # Vitest test suite
cd backend
npm install
# Create .env with your PostgreSQL connection string:
# PG_URL=postgresql://user:password@host:port/dbname
npm run dev
# β http://localhost:8000
# β Swagger docs: http://localhost:8000/docsThe 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_openweathermap_api_keyimport requests
import time
BACKEND_URL = "https://safespot-backend-bbn6.onrender.com"
while True:
temp, humidity = read_sensor() # DHT22 / DS18B20
requests.post(f"{BACKEND_URL}/api/sensors", json={
"temperature": temp,
"humidity": humidity,
"source": "sensor"
})
time.sleep(5)- Web app opens β Toronto map loads with heat island risk areas
- Browser GPS detects user location β marked on map with pulsing green dot
- App checks if user is inside a heat island polygon (point-in-polygon)
- Raspberry Pi sensor reading arrives above 35Β°C
- App cross-checks: dangerous temperature β π¨ Danger alert fires
- Full-screen banner + siren sound + push notification
- App displays: "Nearest Cooling Centre: [Name], 0.3 km NE β Get Directions"
| PS2 Requirement | Our Solution |
|---|---|
| Visualize climate risk areas | Urban heat island layer (High / Medium / Low heat areas on map) |
| Identify vulnerable communities | Heat zone colour coding by risk level |
| 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."