National Disasters Tracker

Distributed Geospatial Disaster Monitoring

National Disasters Canada (NDC) Tracker is a distributed system for reporting and monitoring environmental disasters across geographical regions. It implements a registry-based architecture where a central Hub coordinates multiple independent regional Subhubs—each responsible for a specific geographic zone defined by coordinates and a coverage radius. The frontend integrates LeafletJS for interactive map visualization, allowing users to report, edit, and geographically pin disasters in real-time, while the backend uses geofencing algorithms to automatically route requests to the correct regional node.

Architecture & How It Works

The system is split into three tiers: a Central Hub (hub.py) acting as a Service Registry and Discovery API, multiple Regional Subhubs (subhub.py) managing CRUD operations for their geographic zone, and a Next.js frontendwith LeafletJS map rendering. When a subhub starts, it self-registers with the hub via a POST request. The frontend queries the hub for active subhubs, then communicates directly with the relevant subhub for disaster data—creating a truly distributed system where data is “owned” by the node covering that area.

1. Service Registry & Self-Registration

The Central Hub acts as a Service Registry. When each regional subhub starts, it automatically sends a POST request to the hub with its metadata—IP address, port, geographic coordinates, and coverage radius. The hub stores this in an in-memory registry and exposes a /get_subhubs discovery endpoint for the frontend.

# Subhub self-registration on startup
def register_with_hub(hub_host, hub_port, metadata):
    """Called once when subhub.py initializes"""
    url = "http://" + hub_host + ":" + str(hub_port) + "/register"
    requests.post(url, json={
        "id":         metadata["id"],
        "name":       metadata["name"],        # e.g. "Hamilton"
        "ip":         metadata["ip"],
        "port":       metadata["port"],
        "latitude":   metadata["latitude"],    # 43.26
        "longitude":  metadata["longitude"],   # -79.77
        "radius_km":  metadata["radius_km"]    # 50
    })

2. Geospatial Matching (Geofencing)

The Hub implements an is_point_in_circle geofencing algorithm to match user coordinates to the nearest registered subhub. This determines which regional node should handle a disaster report:

  • Haversine Distance: Calculates the great-circle distance between the user's coordinates and each subhub's center point using the Haversine formula.
  • Radius Check: If the computed distance is less than the subhub's radius_km, the point falls within that subhub's coverage zone.
  • Automatic Routing: The frontend uses this to route disaster CRUD operations directly to the correct subhub API, bypassing the hub for data operations.
    is_point_in_circle(user_lat, user_lon, hub_lat, hub_lon, radius_km) => boolean

3. Factory Pattern & CSV Persistence

Each subhub uses a DisasterFactory to instantiate typed disaster objects—Natural, Biological, or Man-made—ensuring consistent data structures across the distributed system. Instead of a centralized database, each subhub persists its disaster data locally to CSV files in backend/[subhub_name]/data.csv. This distributed state model means each regional node is fully self-contained—it can operate independently even if the hub goes offline, as long as the frontend already has the subhub's address cached from the initial discovery call.

Tech Stack & Tools Used

Frontend & Visualization

  • Next.js (App Router): React-based framework providing SSR, file-system routing, and optimized builds.
  • LeafletJS: Interactive map library rendering OpenStreetMap tiles for geographic disaster pinning.
  • TailwindCSS & Shadcn/UI: Utility-first styling with accessible, pre-built component primitives.
  • TypeScript: End-to-end type safety across the frontend application.

Backend & Infrastructure

  • Python & Flask: Lightweight API framework running both the central hub and regional subhub instances.
  • Flask-CORS & Requests: Cross-origin handling and inter-service HTTP communication for hub-subhub registration.
  • CSV Persistence: File-based data layer using Python's csv module for local disaster record storage.
  • Docker & GitHub Actions: Containerization and CI/CD pipeline for distributed deployment and testing.

Architecture Diagram

📐Distributed Hub-Subhub Architecture

[ ARCHITECTURE OVERVIEW ]

┌─────────────────────────────────────────────────────────────┐
│                    FRONTEND LAYER                           │
│                    Next.js (App Router)                     │
│                                                             │
│   ┌───────────────────┐    ┌───────────────────┐            │
│   │  Dashboard Page   │    │   Report Form     │            │
│   │  LeafletJS Map    │    │   Disaster CRUD   │            │
│   │  OpenStreetMap    │    │   Type Selector   │            │
│   └────────┬──────────┘    └────────┬──────────┘            │
│            │ GET /get_subhubs       │ POST /disasters       │
│            │ (Discovery)            │ PUT /disasters/:id    │
│            │                        │ DELETE /disasters/:id │
│   ┌────────┴────────────────────────┴──────────┐            │
│   │     TailwindCSS + Shadcn/UI + TypeScript   │            │
│   └────────────────────┬───────────────────────┘            │
└────────────────────────┼────────────────────────────────────┘
                         │
          ┌──────────────┴──────────────┐
          ▼                             ▼
┌──────────────────┐    ┌──────────────────────────────────┐
│   CENTRAL HUB    │    │     REGIONAL SUBHUBS             │
│   hub.py         │    │     subhub.py (× N instances)    │
│   Port: 3000     │    │     Ports: 3001, 3002, ...       │
│                  │    │                                  │
│  ┌────────────┐  │    │  ┌────────────────────────────┐  │
│  │ Service    │  │    │  │ On Startup:                │  │
│  │ Registry   │  │    │  │ POST /register → Hub       │  │
│  │            │  │    │  │ { ip, port, coords,        │  │
│  │ subhubs[]  │  │    │  │   radius_km, name }        │  │
│  │ = [{...}]  │  │    │  └────────────────────────────┘  │
│  └────────────┘  │    │                                  │
│                  │    │  ┌────────────────────────────┐  │
│  ┌────────────┐  │    │  │ DisasterFactory            │  │
│  │ Geofencing │  │    │  │                            │  │
│  │ Algorithm  │  │    │  │ create("Natural")          │  │
│  │            │  │    │  │ create("Biological")       │  │
│  │ is_point_  │  │    │  │ create("Man-made")         │  │
│  │ in_circle  │  │    │  └────────────────────────────┘  │
│  │ (lat,lon,  │  │    │                                  │
│  │  radius)   │  │    │  ┌────────────────────────────┐  │
│  └────────────┘  │    │  │ CSV Persistence Layer      │  │
│                  │    │  │ backend/[name]/data.csv    │  │
│  GET /get_       │    │  │ Read/Write on CRUD ops     │  │
│  subhubs         │    │  └────────────────────────────┘  │
└──────────────────┘    └──────────────────────────────────┘

Registration & Discovery Flow:

  [Subhub Starts] → POST /register → Hub
       │
       ▼
  Hub stores: {
    id, name, ip, port,
    latitude, longitude, radius_km
  }
       │
       ▼
  [Frontend Loads] → GET /get_subhubs → Hub
       │
       ▼
  Hub returns: [all registered subhubs]
       │
       ▼
  [User Clicks Map] → is_point_in_circle(lat, lon)
       │
       ├─ Match found → Route to Subhub API
       └─ No match   → "No coverage" message

Disaster CRUD Flow:

  [User Reports Disaster] → POST /disasters → Subhub
       │
       ├─ DisasterFactory.create(type)
       ├─ Validate & store in memory
       └─ Sync to CSV: backend/[name]/data.csv
       │
       ▼
  { disaster_id, type, severity, coords, timestamp }

Local Execution & Testing

This distributed system requires at least three terminal sessions: one for the Central Hub, one for each Regional Subhub, and one for the Next.js frontend. The hub must be started first since subhubs register with it on startup:

# Install Backend Dependenciescd backendpip install -r requirements.txt
# Terminal 1: Start the Central Hubpython hub.py --host 0.0.0.0 --port 3000
# Terminal 2: Start a Regional Subhub (e.g. Hamilton)python subhub.py --hub-host 127.0.0.1 --hub-port 3000 --ip 127.0.0.1 --host 0.0.0.0 --port 3001 --id 1 --name "Hamilton" --longitude 43.26 --latitude -79.77 --radius_km 50
# Terminal 3: Start the Frontendcd frontendnpm installnpm run dev

Testing Strategy

  • API Validation: Test subhub registration by calling /get_subhubs on the Hub to verify the registry is populated correctly.
  • Map Verification: Use the LeafletJS map to report a disaster and verify a CSV file is generated in backend/[subhub_name]/.
  • Container Testing: Run docker build -t ndc-hub . to containerize services for distributed testing environments.