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
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.
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%
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
Shape__AreaandShape__Lengthon 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
.areawrong 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.
Internal links
- How to download CAL FIRE wildfire perimeters — the data used here
- How to calculate area and distance in GeoPandas — area in general
- How to choose a projected CRS for area — picking the equal-area projection
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.