About the Project
Inspiration
When it comes to breaking news, it’s hard to tell which events truly demand attention. Wildfire alerts, conflict updates, health notices, and weather hazards all arrive in different formats and on different timelines. I wanted a single website that shows what’s happening now, where it is, and how serious it might be, without forcing people to read dozens of raw feeds.
What I Built
Crisis Navigator is a live crisis dashboard that:
- Crisis Navigator — a real‑time disaster dashboard that fuses NASA EONET (natural hazards), ReliefWeb v2 (humanitarian), and GDACS (multi‑hazard) into one map.
- Events are normalized to a common schema and limited to the last 30 days.
- Gemini generates situation briefs and a 1–10 severity aligned to a transparent rubric (fatalities, displacement, infrastructure failure, emergency declarations, cross‑border impact, rapid escalation, major losses).
- The UI shows severity‑colored pins, a narrative panel, live Global Statistics, and Activities, with WebSockets streaming updates.
How I Built It
- TypeScript, Node.js, Express backend with scheduled ingesters and REST endpoints (
/api/disasters,/api/stats,/api/activities) and manual Ingest / Process actions. - Gemini integration: batched analysis, strict schema validation, evidence checks, and post‑processing that clamps scores to Low (1–3), Medium (4–6), or High (7–10). Quota‑aware controls via
ANALYSIS_MODE=auto|manual|offto pause background runs. - React + Vite + Leaflet frontend; a WebSocket channel (
/ws) broadcasts stats, activities, and recent disasters every 15 seconds. - Deduplication by title ±1 day (plus country suffix) and a configurable recency window (
DISASTER_MAX_AGE_DAYS). - Built with Vite + esbuild and deployed on Render Web Services with environment variables and automatic HTTPS.
Severity Rubric
I designed a simple, explainable rubric based on confirmed impact and operational signals.
Let the following indicator functions capture high‑impact triggers:
$$ \begin{aligned} I_{\text{fatal}} &= [\text{fatalities} \ge 1] \\ I_{\text{disp}} &= [\text{displaced} \ge 1000] \\ I_{\text{infra}} &= [\text{major infrastructure disruption}] \\ I_{\text{cross}} &= [\text{cross-border/state impact}] \\ I_{\text{emg}} &= [\text{emergency declared}] \\ I_{\text{aid}} &= [\text{international aid mobilized}] \\ I_{\text{escal}} &= [\text{rapid, uncontrolled escalation}] \\ I_{\text{econ}} &= [\text{economic loss} \ge \$100\text{M}] \end{aligned} $$
Define the high‑trigger count:
$$ H = I_{\text{fatal}} + I_{\text{disp}} + I_{\text{infra}} + I_{\text{cross}} + I_{\text{emg}} + I_{\text{aid}} + I_{\text{escal}} + I_{\text{econ}} . $$
Define a moderate‑impact condition:
$$ M = [\text{injuries} > 0] \;\lor\; [100 \le \text{displaced} < 1000] \;\lor\; [\text{regional emergency}] \;\lor\; [\text{loss} \ge \$5\text{M}] . $$
Severity band:
$$ \text{Band} = \begin{cases} \text{High} & \text{if } H \ge 2, \\ \text{Medium} & \text{if } H = 1 \ \text{or}\ M, \\ \text{Low} & \text{otherwise.} \end{cases} $$
Numeric score clamp:
$$ \text{Severity} = \begin{cases} 7\text{–}10 & \text{if High},\\ 4\text{–}6 & \text{if Medium},\\ 1\text{–}3 & \text{if Low}. \end{cases} $$
Within each band, scores center at the midpoint (Low≈2, Medium≈5, High≈8) and nudge up/down when more triggers or stronger evidence are present. This keeps ratings stable, interpretable, and flexible to sparse data.
Inline math example: the high band is chosen when ( H \ge 2 ); medium when ( H = 1 ) or ( M = 1 ).
What I Learned
Building this taught me that data cleanliness beats volume. There were a lot of time zones that didn’t line up, reports with missing or country‑only coordinates, and titles that looked different but referred to the same incident. After I normalized timestamps, deduped by title + time, and added a small deterministic jitter to spread markers that shared a centroid, the whole pipeline became calmer and far more readable. This was also my first time building and deploying a website end‑to‑end, so I learned a lot about the practical web stack: React + Vite on the client, Express routing on the server, environment variables, cloud deployment details like build vs. start commands, the PORT variable, and making sure devDependencies are installed in production. I also learned how much API soft caps can shape your work, and how to work around them and still make the most of your time.
Challenges
The biggest blocker was the Gemini API daily rate limit. Once the quota (Q_{\text{daily}}) was exhausted, I couldn’t analyze additional events until the next window, which paused progress until 3 AM on sunday morning. That constraint also limited scope, as I couldn’t safely add more global weather feeds or higher‑volume sources without going past the quota. Because of that, for now the map has more NASA’s wildfire‑heavy stream instead of a fully balanced worldwide blend of weather-based disasters. Another challenge was that humanitarian reports are qualitative and often number‑light. Many describe displacement, infrastructure stress, or public‑health risk without confirmed counts, and they frequently cover multiple countries. That created two problems: deciding a fair severity when evidence is incomplete, and avoiding marker piles when only a country is given. I addressed both by biasing uncertain cases to Medium, mapping to country centroids, and applying deterministic jitter so pins don’t stack while still staying near the right place. Even with those fixes, the combination of sparse evidence and daily analysis caps forced me to carefully code and give multiple trade-offs.
Next Steps for CrisisNavigator
- Add a persistent database and historical analytics.
- Enrich with population density/exposure layers for contextual risk.
- Show per‑event evidence panels that trace the score to specific triggers.
- Add more APIs that show weather-based disasters for all around the world.
- Update the UI to fix small bugs.
Built With
- express.js
- google-gemini-api
- leaflet.js
- nasa-eonet-api
- node.js
- react
- reliefweb-v2-api
- render-(web-service-hosting)
- typescript
- vite
- websockets
Log in or sign up for Devpost to join the conversation.