Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/danizd/GeoSentinel/llms.txt

Use this file to discover all available pages before exploring further.

GeoSentinel polls the NASA FIRMS (Fire Information for Resource Management System) API every hour to ingest active fire and volcanic hotspots detected by satellite instruments. FIRMS aggregates near-real-time fire detections from two satellite-borne sensors: VIIRS (Visible Infrared Imaging Radiometer Suite) aboard the Suomi-NPP and NOAA-20 satellites at 375-meter resolution, and MODIS (Moderate Resolution Imaging Spectroradiometer) at 1-kilometer resolution. GeoSentinel defaults to the VIIRS_SNPP_NRT product for its higher spatial precision. Each hotspot detection includes a Fire Radiative Power (FRP) measurement in megawatts that drives GeoSentinel’s severity score.

What FIRMS Is

FIRMS ingests satellite thermal anomaly data and publishes it as near-real-time (NRT) CSV feeds. Each record represents a single satellite overpass detection of elevated thermal energy — either an active fire (type=0), a volcanic hotspot (type=1), an industrial or pipeline flare (type=2), or an offshore gas flare (type=3). GeoSentinel filters to types 0 and 1 only; types 2 and 3 are explicitly excluded as infrastructure noise rather than natural or conflict-linked fire events.
FIRMS data has a NRT lag of approximately 3 hours from satellite overpass to API availability. The 1-hour polling interval is faster than data availability — on most polls the ingestor will receive zero new hotspots, which is expected behavior.

API Details

The FIRMS CSV API constructs a dynamic URL embedding the map key, product, bounding box, and day count:
https://firms.modaps.eosdis.nasa.gov/api/area/csv/{FIRMS_MAP_KEY}/{PRODUCT}/{lon_min,lat_min,lon_max,lat_max}/{DAYS}/
PropertyValue
Base URLhttps://firms.modaps.eosdis.nasa.gov/api/area/csv
Default productVIIRS_SNPP_NRT (375 m resolution)
Alt productMODIS_NRT (1 km resolution)
AuthFIRMS_MAP_KEY embedded in URL path
Polling intervalEvery 3 600 seconds (1 hour)
Default days lookback1 day
Bounding boxPer active AOI (lon_min, lat_min, lon_max, lat_max)
To obtain a map key, register at https://firms.modaps.eosdis.nasa.gov/api/map_key/. The URL is built at runtime by _build_url():
def _build_url(self, bbox: tuple[float, float, float, float], days: int = 1) -> str:
    lon_min, lat_min, lon_max, lat_max = bbox
    coords = f"{lon_min},{lat_min},{lon_max},{lat_max}"
    return f"{FIRMS_BASE_URL}/{self.map_key}/{self.product}/{coords}/{days}/"

AOI-Based Polling

The ingestor fetches hotspots per active AOI bounding box rather than globally. Active AOIs are queried from PostGIS at each poll cycle:
bboxes = get_active_aois(session)
if not bboxes:
    bboxes = [(-180, -90, 180, 90)]  # global fallback

for bbox in bboxes:
    result = ingestor.run(session, bbox=bbox)
This restricts satellite queries to monitored regions and avoids processing millions of hotspots from unmonitored areas.

Confidence Filter

FIRMS reports a per-detection confidence field with values low, nominal, and high. GeoSentinel accepts only nominal and high detections:
FIRMS_CONFIDENCE_FILTER = {"nominal", "high"}
Detections with confidence=low raise a ValueError("Confidence '...' not in ...") that the ingestor catches and counts as skipped_low_confidence — they are not quarantined but are silently dropped before validation.

Type Filter

Only fire type 0 (vegetation fire / wildfire) and type 1 (volcanic hotspot) are ingested:
FIRMS_TYPE_FILTER = {0, 1}
FIRMS typeMeaningGeoSentinel action
0Vegetation fire / wildfire✅ Ingested — event_type = "wildfire_hotspot", category = WILDFIRE
1Volcanic hotspot✅ Ingested — event_type = "volcanic_hotspot", category = OTHER
2Other hotspot (industrial, pipeline)❌ Excluded — skipped_invalid_type counter
3Offshore hotspot (gas flare, offshore platform)❌ Excluded — skipped_invalid_type counter

Severity Mapping

Severity (0–10) is derived from Fire Radiative Power (FRP) in megawatts — a direct proxy for fire intensity and energy output:
FRP range (MW)Severity
0–3.9 MW (< 4)1.0
4–7.9 MW (< 8)2.5
8–24.9 MW (< 25)5.0
25–49.9 MW (< 50)7.5
≥ 50 MW10.0
A 200 MW fire — comparable to a large forest fire crown run — would receive severity 10.0. A single burning field at 3 MW would receive severity 1.0.

Confidence Score Assignment

The mapper assigns a numeric confidence based on the detection confidence label:
confidence_value = 8.0 if confidence == "high" else 6.0
high confidence detections (strong thermal signal, satellite geometry favorable) receive 8.0. nominal detections receive 6.0.

Deduplication

Because FIRMS has no stable row identifier, GeoSentinel generates a SHA-256 hash from the observation’s physical fingerprint:
def _generate_event_id(latitude, longitude, acq_date, acq_time, satellite) -> str:
    key = f"{latitude}|{longitude}|{acq_date}|{acq_time}|{satellite}"
    return hashlib.sha256(key.encode()).hexdigest()[:32]
The first 32 hex characters of the SHA-256 hash are used as event_id_source. The same satellite detection queried across multiple polls produces an identical hash, preventing duplicate insertion.

Location Accuracy

Spatial accuracy varies by instrument:
Productlocation_accuracy_km
VIIRS_SNPP_NRT (and variants)0.375 km (375 m)
MODIS_NRT1.0 km
The mapper derives the accuracy from the product prefix:
location_accuracy = LOCATION_ACCURACY.get(product.split("_")[0], 0.375)

Event Timestamp Parsing

Acquisition date and time are parsed together from separate FIRMS fields:
def _parse_datetime(acq_date: str, acq_time: str) -> datetime:
    acq_datetime = datetime.strptime(f"{acq_date} {acq_time.zfill(4)}", "%Y-%m-%d %H%M")
    return acq_datetime.replace(tzinfo=timezone.utc)
acq_time is zero-padded to four digits (e.g., "845""0845" = 08:45 UTC).

Source References

Each normalized FIRMS event includes the satellite and instrument in source_refs:
source_refs = [
    f"satellite: {satellite}",        # e.g. "N" for NOAA-20, "T" for Terra
    f"instrument: {instrument}",      # e.g. "VIIRS"
    f"brightness: {brightness}K",     # brightness temperature if available
]

Independence Class

FIRMS is classified as sensor — carrying a ×2.0 confidence weight during incident aggregation. Satellite thermal anomaly detections are direct physical measurements with no editorial intermediary. See Independence Classes.

Retry and Backoff

The ingestor retries up to 5 times with exponential backoff (base 2, cap 60 seconds). HTTP 429 responses trigger the backoff loop. The requests.Session is pre-configured with urllib3.util.retry.Retry for transient 5xx errors.

Environment Variable

FIRMS_MAP_KEY=your_nasa_map_key   # from firms.modaps.eosdis.nasa.gov/api/map_key/

Running the Ingestor

cd backend
uv run python backend/scripts/run_firms.py
To test the ingestor against a specific region without configuring AOIs in the database, call FIRMSIngestor.run() with an explicit bbox parameter:
ingestor.run(session, bbox=(-10.0, 35.0, 5.0, 45.0), days=1)

Build docs developers (and LLMs) love