Why Are Most Wildfires Missing From the Fire Perimeter Data?

On this page

Problem statement

Count the 2024 fires in CAL FIRE's perimeter database and you get 553, or 218 if you use its popular last-five-years view, which quietly keeps only fires over 50 acres. California's agencies reported more than 14,000 wildfire incidents that year. None of these numbers is wrong. A perimeter database records fires above a size threshold, mapped after the fact, while the incident record logs every reported ignition, most of them put out within an acre. Using perimeters to count fires, to study what starts them or to map where ignitions happen leaves out almost every fire. Using them for burned area loses almost nothing.

This page joins the 2024 California perimeters, from the full historic layer, to the national incident record (NIFC's WFIGS) by their shared IRWIN identifier. It measures what the perimeter data leaves out, by size class.

Quick answer

Join the two sources on the IRWIN ID and compare the share of fires with the share of area:

inc["perimeter"] = inc.irwin.isin(set(per.irwin.dropna()))
sized = inc.dropna(subset=["acres"])
print(f"{len(inc):,} wildfire incidents; {len(per)} perimeters; {inc.perimeter.sum()} incidents matched to a perimeter")
print(f"of {len(sized):,} incidents with a reported size, those with a perimeter are {sized.perimeter.mean():.1%} of fires "
      f"and {sized.loc[sized.perimeter, 'acres'].sum() / sized.acres.sum():.1%} of reported acres")
14,065 wildfire incidents; 553 perimeters; 425 incidents matched to a perimeter
of 7,937 incidents with a reported size, those with a perimeter are 4.3% of fires and 96.3% of reported acres
Map of California with 2024 wildfire incidents from NIFC WFIGS: 13,639 small dark dots without a perimeter, dense along the Sierra Nevada foothills, the Central Valley and southern California, and 425 red dots for incidents that have a CAL FIRE perimeter, with the perimeters shaded; the Park Fire is the only large shape. Map of California with 2024 wildfire incidents from NIFC WFIGS: 13,639 small dark dots without a perimeter, dense along the Sierra Nevada foothills, the Central Valley and southern California, and 425 red dots for incidents that have a CAL FIRE perimeter, with the perimeters shaded; the Park Fire is the only large shape.
Every reported wildfire start in California in 2024, against the fires the perimeter data maps.

Collection rules, size classes, unclassified fires

1. Look at the collection criteria

The perimeter layer states its rules. CAL FIRE and contract counties submit perimeters for fires of at least 10 acres in timber, 50 in brush or 300 in grass, or that damaged three structures or killed someone. Other agencies submit fires of 10 acres or more. Smaller fires are not required, though some are mapped anyway. A published view can add its own filter on top: the last-five-years view keeps only GIS_ACRES > 50, as its definition query, printed in the setup below, shows.

2. Compare by size class

Group the incidents by reported size and count how many have a perimeter:

bins = [0, 1, 10, 50, 300, 1000, 10000, np.inf]
labels = ["<1", "1–10", "10–50", "50–300", "300–1,000", "1,000–10,000", "10,000+"]
sized = sized.assign(size=pd.cut(sized.acres, bins, right=False, labels=labels))
t = sized.groupby("size", observed=False).agg(fires=("acres", "size"), with_perimeter=("perimeter", "sum"), acres=("acres", "sum"))
t["share of acres"] = t.acres / t.acres.sum()
print(t.to_string(formatters={"acres": "{:,.0f}".format, "share of acres": "{:.1%}".format}))
              fires  with_perimeter   acres share of acres
size                                                      
<1             6243              35     797           0.1%
1–10           1199              49   3,419           0.4%
10–50           283             103   6,334           0.7%
50–300          113              65  12,928           1.3%
300–1,000        50              44  25,964           2.7%
1,000–10,000     34              30 112,101          11.7%
10,000+          15              15 798,837          83.2%
Two-panel bar chart of 7,937 California wildfire incidents in 2024 with a reported size: on a log scale, 35 of 6,243 fires under 1 acre have a perimeter, 49 of 1,199 from 1 to 10 acres, 103 of 283 from 10 to 50, 65 of 113 from 50 to 300, 44 of 50 from 300 to 1,000, 30 of 34 from 1,000 to 10,000 and all 15 above 10,000; the fires above 10,000 acres hold 83.2 percent of the reported acres and those under 10 acres 0.5 percent. Two-panel bar chart of 7,937 California wildfire incidents in 2024 with a reported size: on a log scale, 35 of 6,243 fires under 1 acre have a perimeter, 49 of 1,199 from 1 to 10 acres, 103 of 283 from 10 to 50, 65 of 113 from 50 to 300, 44 of 50 from 300 to 1,000, 30 of 34 from 1,000 to 10,000 and all 15 above 10,000; the fires above 10,000 acres hold 83.2 percent of the reported acres and those under 10 acres 0.5 percent.
Almost no small fires have a perimeter, and almost no area is lost by leaving them out.

3. Count what cannot be classified

Many incidents carry no size at all, so they cannot be placed in a class:

nosize = inc.acres.isna()
print(f"{nosize.sum():,} of {len(inc):,} incidents ({nosize.mean():.0%}) have no reported size")
by = inc.groupby("agency").acres.agg(incidents="size", no_size=lambda s: s.isna().mean())
print(by.sort_values("incidents", ascending=False).head(4).to_string(formatters={"no_size": "{:.0%}".format}))
6,128 of 14,065 incidents (44%) have no reported size
          incidents no_size
agency                     
CAL FIRE       7848     15%
C&L            4834    100%
USFS            862      2%
BLM             259      2%

The contract counties and local agencies (C&L) report thousands of incidents without a size, so a size-class table covers mainly the fires that CAL FIRE and the federal agencies protect.

4. Check the join in both directions

Many perimeters have no IRWIN ID, mostly those from contract counties, so they cannot be joined by ID:

lost = per[~per.irwin.isin(set(inc.irwin))]
print(f"{len(lost)} of {len(per)} perimeters have no matching incident; {lost.irwin.isna().sum()} have no IRWIN ID at all; "
      f"{lost.GIS_ACRES.sum():,.0f} acres")
print(lost.AGENCY.value_counts().to_string())
128 of 553 perimeters have no matching incident; 117 have no IRWIN ID at all; 25,691 acres
AGENCY
CCO    106
LRA     14
CDF      6
USF      1
FWS      1

Large fires without a perimeter

The data used on this page

WFIGS Incident Locations from the National Interagency Fire Center (wildfire incidents with a point of origin in California, discovered in 2024), and CAL FIRE fire perimeters for 2024 from the full historic layer (California_Historic_Fire_Perimeters), both public ArcGIS feature services. The incident service returns at most 2,000 records a request, so the download pages through resultOffset.

from pathlib import Path

import geopandas as gpd
import numpy as np
import pandas as pd
import requests

Path("data").mkdir(exist_ok=True)
WFIGS = "https://services3.arcgis.com/T4QMspbfLg3qTGWY/arcgis/rest/services/WFIGS_Incident_Locations/FeatureServer/0/query"
where = ("POOState = 'US-CA' AND IncidentTypeCategory = 'WF' AND FireDiscoveryDateTime >= TIMESTAMP '2024-01-01 00:00:00' "
         "AND FireDiscoveryDateTime < TIMESTAMP '2025-01-01 00:00:00'")
rows, offset = [], 0
while True:
    page = requests.get(WFIGS, params={"where": where, "outFields": "IrwinID,IncidentName,IncidentSize,FinalAcres,POOProtectingAgency",
                                       "outSR": 4326, "resultOffset": offset, "resultRecordCount": 2000, "f": "json"}, timeout=120).json()
    rows += [dict(f["attributes"], lon=f["geometry"]["x"], lat=f["geometry"]["y"]) for f in page["features"] if f.get("geometry")]
    offset += len(page["features"])
    if not page.get("exceededTransferLimit"):
        break
inc = pd.DataFrame(rows)
inc["acres"] = inc.FinalAcres.fillna(inc.IncidentSize)
inc["irwin"] = inc.IrwinID.str.strip("{}").str.upper()
inc = inc.rename(columns={"POOProtectingAgency": "agency"})
SERVICES = "https://services1.arcgis.com/jUJYIo9tSA7EHvfZ/arcgis/rest/services/"
HIST = SERVICES + "California_Historic_Fire_Perimeters/FeatureServer/0"
VIEW = SERVICES + "California_Fire_Perimeters_last_5_years_View/FeatureServer/0"
GEOJSON = Path("data/calfire_2024_all.geojson")
if not GEOJSON.exists():
    GEOJSON.write_bytes(requests.get(HIST + "/query", params={"where": "YEAR_ = 2024", "outFields": "*", "outSR": 4326, "f": "geojson"}, timeout=300).content)
per = gpd.read_file(GEOJSON)
per["irwin"] = per.IRWINID.str.strip("{}").str.upper()
view_filter = requests.get(VIEW, params={"f": "json"}, timeout=60).json().get("viewDefinitionQuery")
in_view = requests.get(VIEW + "/query", params={"where": "YEAR_ = 2024", "returnCountOnly": "true", "f": "json"}, timeout=60).json()["count"]
print(f"{offset:,} incident records read ({len(inc):,} with a location)")
print(f"2024 perimeters: {len(per)} in the historic layer, {in_view} in the last-five-years view ({view_filter})")
14,065 incident records read (14,065 with a location)
2024 perimeters: 553 in the historic layer, 218 in the last-five-years view (YEAR_ IN (2024,2025,2021,2022,2023) AND GIS_ACRES > 50)

Example 1: fires above the threshold without a perimeter

big = sized[(sized.acres >= 300) & ~sized.perimeter]
print(f"{len(big)} incidents of 300 acres or more have no matched perimeter, {big.acres.sum():,.0f} acres:")
print(big.nlargest(5, "acres")[["IncidentName", "acres", "agency"]].to_string(index=False))
10 incidents of 300 acres or more have no matched perimeter, 22,694 acres:
IncidentName   acres   agency
     ROBERTS 7030.25 CAL FIRE
    BOLT 3-2 6743.00 CAL FIRE
   FLASH 3-3 3600.00 CAL FIRE
        WEST 1575.00      C&L
         HAY  869.00 CAL FIRE

Heavy tails: area captured, fires missed

Fire sizes follow a heavy-tailed distribution: a few fires burn almost all the area, and almost all fires stay tiny. A database with a size threshold therefore captures the area and misses the fires. That suits its purpose: perimeters exist to show what burned, for fire history, fuel planning and reburn analysis, and a fire of a tenth of an acre has nothing useful to map.

From California wildfire incidents in 2024 to fires with a mapped perimeter. from incidents to perimeters, California 2024incidents (WFIGS)14,065size reportedwith a reported size7,937above the smallest thresholdΓ· 1610 acres or more495IRWIN ID joinand a matched perimeter257The 257 fires hold most of the burned area; the 14,065 hold the ignitions spatialworkflow.io
From reported fires to mapped perimeters.

The trouble comes when the data is used for something else. Ignition counts, cause studies, the share of fires started by people, the density of fires along roads: all of these need the small fires that the perimeters leave out. The incident record has them, as points of origin with a reported size and cause. Use perimeters for area, incidents for counts, and say which you used.

Reported sizes, missing ids and prescribed burns

Fire perimeter coverage problems and fixes. perimeter coverage problemsSymptomFix553 fires in a year of 14,065perimeters have a size thresholdthe view shows only 218it keeps GIS_ACRES > 50; use thefull layerignition or cause counts too lowcount from incident recordsperimeters that will not joinno IRWIN ID: match name, date,placeCAL FIRE perimeters and NIFC WFIGS incidents, 2024 spatialworkflow.io
Perimeter coverage problems and fixes.
  • Reported sizes in WFIGS are what the incident reported, sometimes before final mapping; they will not equal GIS_ACRES for the same fire.
  • No IRWIN ID: contract-county and local perimeters often lack one; match them by name, date and location instead, and expect some to stay unmatched.
  • Older years: IRWIN started in 2014 and the perimeter layer has carried its ID only since 2022; earlier fires need a spatial and date match.
  • Prescribed burns are a separate CAL FIRE layer; WFIGS marks them with IncidentTypeCategory = 'RX'.
  • Complexes appear as one perimeter but several incidents; count them deliberately.

FAQ

Why does the fire perimeter database have so few fires?

It only includes fires above a size threshold: 10 acres in timber, 50 in brush, 300 in grass for CAL FIRE, and 10 acres for other agencies. Most wildfires are much smaller.

Where can I get data on all wildfires, including small ones?

From incident records: NIFC's WFIGS Incident Locations for recent years, or the US Forest Service Fire Program Analysis fire-occurrence database (FPA FOD) for a long national record.

Can I use perimeters to count wildfires?

No. Use them for burned area; count fires from incident or occurrence records.

How do I join fire perimeters to incident records?

On the IRWIN ID where both have it. Otherwise, match by name, discovery date and location.