Data Analysis · Data Viz · Environmental Equity

Richmond's Tree Canopy Gap

Richmond's last tree inventory was completed 12 years ago. Using five datasets and Python, I mapped how canopy gaps fall hardest on low-income neighborhoods and communities of color — analysis that informed a published investigation into the city's $1M grant to rebuild its urban forestry system.

PythonDatawrapperGIS mappingGoogle Sheets

Data & process

I obtained Richmond's full tree inventory dataset from the city's data portal, Transparent Richmond. This dataset includes fields such as species, location, condition, last survey date, and so on. To confirm whether the dataset was still actively maintained, I interviewed Parks Maintenance Superintendent, and he explained that the city no longer routinely updates this data.

I downloaded the dataset and used Python to keep only the columns I needed (having too many fields made Google Sheets lag). Then I imported the trimmed file into Google Sheets to filter and clean it. I removed all rows without latitude or longitude and ended up with 35,584 valid records.

Python · data cleaning
import pandas as pd

df = pd.read_csv("Richmond_Urban_Forestry_Inventory.csv")

keep_cols = [
    "Tree Number", "Species",
    "Lat", "Long", "Condition",
    "New Georeferenced Column"
]

df_clean = df[keep_cols].copy()

# remove rows without coordinates
df_clean = df_clean.dropna(subset=["Lat", "Long"])

print(f"{len(df_clean)} valid records")

df_clean.to_csv("richmond_trees_clean.csv", index=False)

I also used external datasets for cross-checking:

All of these platforms provided downloadable, original datasets along with methodological documentation. I reviewed their methodologies and extracted the specific variables I needed using Python. For example, I got tree canopy data from American Forests. The canopy data was donated by Google and derived from the Google Environmental Insights Explorer, which offered high-resolution tree canopy estimates aggregated at the Census block group level.

I downloaded Richmond's block group shapefile from TIGER/Line Shapefiles and imported both the map and the datasets into Datawrapper. I then used geocoding to match block group GEOIDs with the canopy and demographic datasets. I also downloaded population and socioeconomic indicators from EJScreen and found that areas with the lowest canopy coverage strongly overlapped with neighborhoods that are predominantly people of color and low-income.

To understand how reliable the dataset was and why the gaps existed, I also spoke with UC Berkeley professor emerita Jennifer Wolch, an expert on urban planning and urban greening.

THE STORY BEHIND THE STORY

I started by doing clip searches. Richmondside published an entire series called "What's fueling Richmond's air pollution?" and I read almost every story in it to understand the environmental context for Richmond's canopy disparities. I also looked at past Richmond Confidential coverage, including previous Arbor Day reporting, to see how the city and community groups have talked about tree equity in the past.

Then, I tried to make connections between communities. I talked with residents at the Arbor Day event, joined the volunteers, and even planted my first tree in Richmond (which actually became my first personal connection to the city's urban forestry issues). I also established a relationship with Groundwork Richmond and stayed in touch with their director, who helped me understand the nonprofit side of tree planting and the challenges of working in neighborhoods with historically low canopy.

After that, I also attended the Urban Forest Advisory Committee meeting and multiple City Council discussions to follow funding decisions and hear how staff framed the issue.

While reading a previous article about equitable tree coverage in Richmond, I came across Professor Jennifer Wolch's work. Following that clue, I researched her academic background and eventually interviewed her, which gave me a statewide perspective on why cities struggle with maintaining accurate tree inventories.

I’m a little bit frustrated because much of this analysis and visualization did not appear in the final published story due to length, but I still think this investigative process is meaningful because it informed my reporting and confirmed the gap between existing records and residents’ lived experiences.

WHAT I LEARNED

Start early, and take time to see what reporters before you have already done. Talk to as many people as you can. Most sources and experts are surprisingly generous, and they can help you find your direction much faster than trying to figure everything out alone.

Don't get lost in data analysis too early.It's better to have a general idea of the story first and then talk to people so you know what data actually matters. Otherwise, you can end up doing a lot of unnecessary work — which is a lesson I definitely learned the hard way.

I am on the data beat, and at first it was honestly confusing. I didn't know how to connect all the datasets to real local stories, and I spent a lot of time analyzing numbers that didn't end up being useful. What I learned is that you should always find the story first — talk to people, go out into the community, and humanize all the abstract data. That will help you figure out what data you actually need.

Go to meetings — especially the small committee meetings. Richmond has a lot of them, and important information is often shared informally before it ever shows up in City Council agendas. Build relationships early with city staff and community groups; they can give you context you won't find in any dataset.

And don't underestimate residents. Someone living next to a dying tree can teach you just as much as a city official.