How to Find Parks and Green Space in OpenStreetMap With Python

On this page

Problem statement

How much park is there per resident, and how far do people live from one? Questions like these start with a map of parks, and OpenStreetMap is the most detailed open source for one. Parks are tagged leisure=park, but green space more broadly includes woods, nature reserves, playing fields, golf courses and cemeteries. Large parks are often multipolygon relations, and small parks overlap with the land-use polygons around them, so a careless total counts the same ground twice.

This page extracts the parks of Washington, D.C. from the OpenStreetMap extract of 1 January 2026. It measures their area and size distribution, finds the largest, computes park area per resident with the Census population, and checks how much of the District lies within 400 m of a park.

Quick answer

Read leisure=park with areas enabled, clip to the boundary, and measure the union:

fp = (osmium.FileProcessor(str(PBF)).with_areas()
      .with_filter(osmium.filter.TagFilter(("leisure", "park")))
      .with_filter(osmium.filter.GeoInterfaceFilter(tags=["leisure", "name", "operator"])))
P = gpd.GeoDataFrame.from_features(fp, crs=4326)
P = gpd.clip(P[P.geom_type.isin(["Polygon", "MultiPolygon"])], DC).to_crs(32618)
P["ha"] = P.area / 1e4
park_km2 = P.union_all().area / 1e6
print(f"{len(P):,} park polygons inside D.C.; {park_km2:.1f} km² ({park_km2 / LAND_KM2:.0%} of the land)")
724 park polygons inside D.C.; 21.2 km² (13% of the land)

There are 724 leisure=park polygons inside the District, together 21.2 km², 13% of the land.

Map of D.C. green space: solid teal leisure=park polygons including the Mall, East Potomac Park, Anacostia Park and Fort Dupont, and pale teal for other green tags, among them the long strip of Rock Creek Park in the north-west. Map of D.C. green space: solid teal leisure=park polygons including the Mall, East Potomac Park, Anacostia Park and Fort Dupont, and pale teal for other green tags, among them the long strip of Rock Creek Park in the north-west.
Parks and other green space.

Size distribution, park area per resident, 400 m access

1. Size distribution and the largest parks

Most parks are small; a few large ones hold most of the area:

bins = [0, 0.1, 1, 10, 100, 1e6]
sizes = pd.cut(P.ha, bins, labels=["<0.1 ha", "0.1-1 ha", "1-10 ha", "10-100 ha", ">100 ha"]).value_counts(sort=False)
print(sizes.to_dict())
print(P.nlargest(5, "ha")[["name", "operator", "ha"]].round(0).to_string(index=False))
{'<0.1 ha': 280, '0.1-1 ha': 255, '1-10 ha': 154, '10-100 ha': 30, '>100 ha': 5}
                             name              operator    ha
                    National Mall National Park Service 301.0
          U.S. National Arboretum                   NaN 183.0
                 Fort Dupont Park                   NaN 153.0
                East Potomac Park National Park Service 137.0
Kenilworth Park & Aquatic Gardens National Park Service 132.0

Most parks are tiny: 280 are under 0.1 hectare, and only 5 are over 100 hectares. The largest leisure=park polygon is now the National Mall (301 ha), followed by the National Arboretum (183 ha) and Fort Dupont Park (153 ha). Rock Creek Park is missing: in OSM it is tagged leisure=nature_reserve and boundary=national_park, not leisure=park, so a parks-only filter misses one of the city's largest green spaces.

Number of leisure=park polygons in Washington, D.C., by size. parks by sizeunder 0.1 ha2800.1-1 ha2551-10 ha15410-100 ha30over 100 ha50100200300724 parks; those under 1 ha hold 4.2% of the area spatialworkflow.io
Most parks are small.

2. Park area per resident

pop = int(est.loc[est.GEOID == "11001", "POPESTIMATE2023"].iloc[0])
print(f"population 2023: {pop:,}; park area per resident: {park_km2 * 1e6 / pop:.0f} m²")
nps = P[P.operator.fillna("").str.contains("National Park Service")]
print(f"parks operated by the National Park Service: {len(nps)} polygons, {nps.union_all().area / 1e6:.1f} km² ({nps.union_all().area / 1e6 / park_km2:.0%} of park area)")
population 2023: 678,972; park area per resident: 31 m²
parks operated by the National Park Service: 40 polygons, 8.3 km² (39% of park area)

That is 31 m² of leisure=park per resident. Only 40 polygons, 39% of the park area, carry National Park Service as operator. Several NPS parks, Fort Dupont among them, have no operator tag, so the true federal share is higher.

3. Within 400 m of a park

A 400 m buffer, about five minutes' walk, around every park shows which parts of the District are close to one. Small parks matter here, not area:

near = P.buffer(400).union_all().intersection(DC.to_crs(32618).geometry.iloc[0])
print(f"land within 400 m of a park: {near.area / 1e6:.1f} km² of {DC.to_crs(32618).area.iloc[0] / 1e6:.1f} km² ({near.area / DC.to_crs(32618).area.iloc[0]:.0%} of the District)")
small = P[P.ha < 1]
print(f"parks under 1 ha: {len(small):,} ({len(small) / len(P):.0%} of parks, {small.ha.sum() / P.ha.sum():.1%} of summed park area)")
land within 400 m of a park: 143.3 km² of 176.9 km² (81% of the District)
parks under 1 ha: 535 (74% of parks, 4.2% of summed park area)

81% of the District lies within 400 m of a park. The 535 parks under one hectare, 74% of all parks, hold only 4.2% of the area, but they are what puts so much of the city close to one.

Other kinds of green space

The data used on this page

The Geofabrik OpenStreetMap extract for the District of Columbia dated 1 January 2026 (© OpenStreetMap contributors, ODbL), the US Census Bureau's 2023 county cartographic boundaries at 1:500,000 and the Vintage 2023 population estimates.

from pathlib import Path

import geopandas as gpd
import osmium
import pandas as pd
import requests

Path("data").mkdir(exist_ok=True)
def cached(path, url):
    p = Path(path)
    if not p.exists():
        r = requests.get(url, timeout=600)
        r.raise_for_status()
        p.write_bytes(r.content)
    return p
PBF = cached("data/district-of-columbia-260101.osm.pbf", "https://download.geofabrik.de/north-america/us/district-of-columbia-260101.osm.pbf")
C = gpd.read_file(cached("data/cb_2023_us_county_500k.zip", "https://www2.census.gov/geo/tiger/GENZ2023/shp/cb_2023_us_county_500k.zip"))
DC = C[C.GEOID == "11001"].to_crs(4326)
LAND_KM2 = C.loc[C.GEOID == "11001", "ALAND"].iloc[0] / 1e6
est = pd.read_csv(cached("data/co-est2023-alldata.csv", "https://www2.census.gov/programs-surveys/popest/datasets/2020-2023/counties/totals/co-est2023-alldata.csv"),
                  encoding="latin-1", dtype={"STATE": str, "COUNTY": str})
est = est[est.SUMLEV == 50].assign(GEOID=lambda d: d.STATE + d.COUNTY)
print(f"D.C. land area: {LAND_KM2:.1f} km²")
D.C. land area: 158.3 km²

The District has 158.3 km² of land.

Example 1: other green space

Parks are not all of the green. Add the other green tags and compare:

fp = (osmium.FileProcessor(str(PBF)).with_areas()
      .with_filter(osmium.filter.TagFilter(("natural", "wood"), ("leisure", "nature_reserve"), ("leisure", "golf_course"),
                                           ("leisure", "pitch"), ("landuse", "cemetery"), ("landuse", "forest"), ("landuse", "grass")))
      .with_filter(osmium.filter.GeoInterfaceFilter(tags=["natural", "leisure", "landuse"])))
O = gpd.GeoDataFrame.from_features(fp, crs=4326)
O = gpd.clip(O[O.geom_type.isin(["Polygon", "MultiPolygon"])], DC).to_crs(32618)
all_green = pd.concat([P.geometry, O.geometry]).union_all()
outside = all_green.difference(P.union_all())
print(f"parks: {park_km2:.1f} km²; all green tags: {all_green.area / 1e6:.1f} km²; green outside parks: {outside.area / 1e6:.1f} km²")
parks: 21.2 km²; all green tags: 40.8 km²; green outside parks: 19.6 km²

Counting woods, nature reserves, pitches, golf courses, cemeteries and grass nearly doubles the green area to 40.8 km², with 19.6 km² outside the leisure=park polygons. Rock Creek Park is among it.

Map of D.C. with 400-metre buffers around parks in blue covering 81% of the District and the rest in pale red, including stretches of Rock Creek Park in the north-west, which is not tagged as a park, and the rivers and Joint Base Anacostia-Bolling in the south. Map of D.C. with 400-metre buffers around parks in blue covering 81% of the District and the rest in pale red, including stretches of Rock Creek Park in the north-west, which is not tagged as a park, and the rivers and Joint Base Anacostia-Bolling in the south.
Within 400 m of a park.

What leisure=park means

Parks compared with all green space in OpenStreetMap. parks or green space?leisure=park: 21.2 km²✓724 polygons✓31 m² per resident✓misses Rock Creek Parkall green tags: 40.8 km²✓woods, reserves, pitches✓golf, cemeteries, grass✓19.6 km² outside parksUnion areas inside D.C. spatialworkflow.io
Parks or green space?

leisure=park marks an area kept open for recreation, usually public, from a pocket park on a street corner to a large wooded valley. Other green tags describe related things: natural=wood and landuse=forest for trees, leisure=nature_reserve for protected land, leisure=pitch and golf_course for sport, landuse=cemetery and grass. Which of these count as green space is a definition the analysis has to make, and the answer changes the numbers a great deal.

Washington's parks show why the measure matters as much as the data. Area is dominated by a few very large federal parks, so park area per resident is high, while access depends on many small parks spread through neighbourhoods. A buffer around every park measures that access better than area does. A proper analysis uses walking distances along the street network and the population of each block. Whatever the method, overlaps must be removed by taking unions, since park polygons often nest within each other and within land-use polygons.

Access tags, entrances and official data

Checks before measuring parks from OpenStreetMap. before measuring parkscheck the big parks' tagsRock Creek is a nature_reserveunion the polygonsparks nest and overlapaccess=privatenot public parksoperator is incompleteNPS on 40 polygonswalking distancenot a straight bufferD.C., OSM extract of 1 January 2026 spatialworkflow.io
Before measuring parks.
  • Access: access=private or access=no marks parks the public cannot enter; filter them for accessibility studies.
  • Park entrances: large parks are reachable only at entrances and along paths; distance to the polygon edge overstates access.
  • Name duplicates: one park can be split into several polygons with the same name; dissolve by name for park counts.
  • Official data: many cities publish park boundaries; compare them with OSM before relying on either.

FAQ

How do I get parks from OpenStreetMap?

Filter for leisure=park with areas enabled, keep the polygons, and take the union before measuring area.

What counts as green space in OSM?

Parks (leisure=park) plus, depending on the definition, woods, nature reserves, pitches, golf courses, cemeteries and grass.

How do I calculate park area per person?

Divide the union area of the park polygons inside the boundary by the population from census data.

How do I measure access to parks?

Buffer each park by a walking distance, or better, compute network distances from homes to park entrances.