Why Is My Wildfire Area Too Large?

On this page

Problem statement

You download fire perimeters, sum the area, and get a number that disagrees with the official acreage, often by more than half. The Park Fire, the largest California fire of 2024, is officially 429,603 acres (1,739 km²). Its perimeter record on ArcGIS Online carries a Shape__Area of almost 3,000 km². Compute .area on the GeoJSON you downloaded and you get a fraction of one: square degrees, not square kilometres.

Neither number is a data error. Shape__Area is measured in Web Mercator, the projection the service stores its geometry in, and Mercator inflates area by a factor that grows with latitude. .area in a geographic CRS measures in degrees, a unit that is not even the same size everywhere. A fire's area needs an equal-area CRS or a measurement on the ellipsoid.

This page measures the Park Fire four ways, then shows how much Shape__Area overstates every fire over 50 acres in CAL FIRE's last-five-years view.

Quick answer

Project to an equal-area CRS before calling .area. For California, California Albers (EPSG:3310) is the standard choice:

park_ll = PERIMS[(PERIMS.FIRE_NAME == "PARK") & (PERIMS.YEAR_ == 2024)]
km2 = park_ll.to_crs(3310).area.sum() / 1e6
print(f"Park Fire: {km2:,.1f} km² in EPSG:3310 = {km2 / ACRE_KM2:,.0f} acres; GIS_ACRES says {park_ll.GIS_ACRES.sum():,.1f}")
Park Fire: 1,738.5 km² in EPSG:3310 = 429,603 acres; GIS_ACRES says 429,602.8
Two outlines of the 2024 Park Fire drawn at the same scale: on the left in California Albers, 1,739 square kilometres; on the right in Web Mercator, visibly larger at 2,976 square kilometres, with the equal-area outline dashed inside it. Two outlines of the 2024 Park Fire drawn at the same scale: on the left in California Albers, 1,739 square kilometres; on the right in Web Mercator, visibly larger at 2,976 square kilometres, with the equal-area outline dashed inside it.
The same perimeter projected two ways. Web Mercator stretches it by 1.31 in each direction at this latitude, so its area comes out 1.71 times too large.

Recognise square degrees and Web Mercator, check on the ellipsoid

1. Recognise square degrees

A GeoJSON from a web service is in longitude and latitude (EPSG:4326). .area there returns square degrees, and GeoPandas warns about it:

import warnings
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter("always")
    deg2 = park_ll.area.sum()
print(f"area in EPSG:4326: {deg2:.4f} (square degrees); warning: {w[0].message.args[0][:60]}...")
area in EPSG:4326: 0.1837 (square degrees); warning: Geometry is in a geographic CRS. Results from 'area' are lik...

A square degree is about 12,300 km² at the equator and 9,500 km² at the Park Fire's latitude, so no single factor converts it. Do not try.

2. Recognise Web Mercator

Shape__Area is computed by ArcGIS Online in the layer's storage projection, Web Mercator (EPSG:3857). Reproduce it, then compare with the equal-area value:

merc = park_ll.to_crs(3857).area.sum() / 1e6
lat = park_ll.to_crs(3310).union_all().centroid
lat = gpd.GeoSeries([lat], crs=3310).to_crs(4326).iloc[0].y
print(f"Shape__Area: {park_ll.Shape__Area.sum() / 1e6:,.1f} km²; .area in EPSG:3857: {merc:,.1f} km²")
print(f"ratio to the true area: {merc / km2:.3f}; 1 / cos²(latitude {lat:.2f}°) = {1 / np.cos(np.radians(lat)) ** 2:.3f}")
Shape__Area: 2,976.3 km²; .area in EPSG:3857: 2,976.3 km²
ratio to the true area: 1.712; 1 / cos²(latitude 40.12°) = 1.710

The ratio matches the Mercator scale factor squared, to within 0.2%: Mercator stretches distances by 1/cos(latitude) in both directions, so area grows by its square.

Scatter plot of 746 CAL FIRE perimeters from 2021 to 2025: the ratio of Shape__Area to the equal-area area rises from 1.41 at latitude 32.6 degrees to 1.81 at 42 degrees, every fire lying on the curve one over cosine squared of latitude; the Park Fire is marked at 1.71. Scatter plot of 746 CAL FIRE perimeters from 2021 to 2025: the ratio of Shape__Area to the equal-area area rises from 1.41 at latitude 32.6 degrees to 1.81 at 42 degrees, every fire lying on the curve one over cosine squared of latitude; the Park Fire is marked at 1.71.
Every fire's Shape__Area is too large by the Mercator factor for its latitude, within 0.004.

3. Check on the ellipsoid

pyproj.Geod measures area on the WGS 84 ellipsoid without any projection. It agrees with California Albers:

geod_km2 = abs(GEOD.geometry_area_perimeter(park_ll.union_all())[0]) / 1e6
print(f"ellipsoid: {geod_km2:,.1f} km²; EPSG:3310: {km2:,.1f} km²; difference {geod_km2 / km2 - 1:+.3%}")
ellipsoid: 1,738.5 km²; EPSG:3310: 1,738.5 km²; difference +0.000%
The 2024 Park Fire measured four ways. one fire, four areasmeasured asresultverdict.area in EPSG:43260.1837 square degreesnot an areaShape__Area (Web Mercator)2,976.3 km²1.71x too large.area in EPSG:33101,738.5 km²equal-areapyproj.Geod on WGS 841,738.5 km²ellipsoidGIS_ACRES 429,602.8 acres = 1,738.5 km² spatialworkflow.io
Only an equal-area projection or the ellipsoid gives the official area.

How much a service's area field overstates

The data used on this page

CAL FIRE California Fire Perimeters, last-five-years public view (California_Fire_Perimeters_last_5_years_View on ArcGIS Online, which holds fires over 50 acres), downloaded as GeoJSON in EPSG:4326 with every attribute, including the service's own Shape__Area.

from pathlib import Path

import geopandas as gpd
import numpy as np
import pandas as pd
import requests
from pyproj import Geod

Path("data").mkdir(exist_ok=True)
LAYER = "https://services1.arcgis.com/jUJYIo9tSA7EHvfZ/arcgis/rest/services/California_Fire_Perimeters_last_5_years_View/FeatureServer/0"
GEOJSON = Path("data/calfire_last5.geojson")
if not GEOJSON.exists():
    r = requests.get(LAYER + "/query", params={"where": "1=1", "outFields": "*", "outSR": 4326, "f": "geojson"}, timeout=300)
    GEOJSON.write_bytes(r.content)
PERIMS = gpd.read_file(GEOJSON)
PERIMS["geometry"] = PERIMS.geometry.make_valid()
ACRE_KM2 = 0.0040468564224
GEOD = Geod(ellps="WGS84")
print(f"{len(PERIMS)} perimeters, {PERIMS.YEAR_.min()}–{PERIMS.YEAR_.max()}, CRS {PERIMS.crs.to_string()}")
746 perimeters, 2021–2025, CRS EPSG:4326

Example 1: how much Shape__Area overstates every fire

eq = PERIMS.to_crs(3310)
cent = gpd.GeoSeries(eq.centroid, crs=3310).to_crs(4326)
ratio = pd.DataFrame({"lat": cent.y, "ratio": PERIMS.Shape__Area / eq.area})
print(f"Shape__Area / true area across {len(ratio)} fires: {ratio.ratio.min():.2f} to {ratio.ratio.max():.2f}, "
      f"at latitudes {ratio.lat.min():.1f}° to {ratio.lat.max():.1f}°")
print(f"largest gap from 1/cos²(lat): {(ratio.ratio - 1 / np.cos(np.radians(ratio.lat)) ** 2).abs().max():.4f}")
Shape__Area / true area across 746 fires: 1.41 to 1.81, at latitudes 32.6° to 42.0°
largest gap from 1/cos²(lat): 0.0040

Example 2: a season total

for y in (2021, 2024):
    sub = PERIMS[PERIMS.YEAR_ == y]
    print(f"{y}: Shape__Area sum {sub.Shape__Area.sum() / 1e6:,.0f} km²; EPSG:3310 {sub.to_crs(3310).area.sum() / 1e6:,.0f} km²; "
          f"GIS_ACRES {sub.GIS_ACRES.sum() * ACRE_KM2:,.0f} km²")
2021: Shape__Area sum 17,270 km²; EPSG:3310 10,153 km²; GIS_ACRES 10,153 km²
2024: Shape__Area sum 6,568 km²; EPSG:3310 4,073 km²; GIS_ACRES 4,073 km²

What Web Mercator does to area

Every map projection distorts something. Web Mercator keeps angles and shapes locally correct, which is why web maps use it, and pays for it in area: a region at latitude φ is drawn 1/cos²φ times too large. At California's latitudes that is 1.4 to 1.8 times, so a fire in the north is inflated more than one in the south, and a statewide total is off by an amount that depends on where the year's fires burned: summed Shape__Area overstates the 2021 season by 70% and the 2024 season by 61%.

Equal-area projections such as California Albers (EPSG:3310), CONUS Albers (EPSG:5070) or a Lambert azimuthal equal-area projection centred on the region keep area exact, at the cost of shape far from their centre. Measurement on the ellipsoid avoids the choice altogether. GIS_ACRES in the CAL FIRE data is computed in an equal-area projection, which is why it matches EPSG:3310 and not Shape__Area.

Service area fields, UTM and unburned islands

Fire area problems and fixes. area problemsSymptomFixarea below 1 and a CRS warningsquare degrees: project before.area1.4 to 1.8 times the officialacresWeb Mercator: use EPSG:3310 or5070northern fires inflated moreMercator grows as 1/cos² oflatitudeShape__Area summed for a seasonrecompute; it is a display valueCAL FIRE perimeters, 2021–2025 spatialworkflow.io
Fire area problems and fixes.
  • Shape__Area and Shape__Length on any ArcGIS Online hosted layer are in the storage projection, almost always Web Mercator. Treat them as display values.
  • UTM zones are not equal-area, but within a zone the error is under about 0.1%; acceptable for one fire, not ideal for a statewide total crossing zones 10 and 11.
  • Invalid geometry can make .area wrong in any CRS; make_valid() first (see fire-perimeter-invalid-geometry).
  • Hectares and acres: 1 km² is 100 ha or 247.1 acres.
  • Interior unburned islands are holes in the polygon; they are already excluded from the area.

FAQ

Why does my fire area not match the official acreage?

You are probably measuring in Web Mercator (Shape__Area, or data reprojected to EPSG:3857), which inflates area by 1/cos²(latitude), or in degrees. Project to an equal-area CRS first.

Which CRS should I use to measure area in California?

California Albers, EPSG:3310. For the contiguous United States use CONUS Albers, EPSG:5070.

What is Shape__Area in an ArcGIS layer?

An area the service computes in its storage projection, usually Web Mercator. It is not a true ground area.

How do I measure area without choosing a projection?

Use pyproj.Geod(ellps="WGS84").geometry_area_perimeter(geom), which measures on the ellipsoid.