ArcGIS Pro & Python — Analyzing Land Cover Change in Major Global Urban Centers

Colin T. Stiles, GISP
9 min readMar 27, 2023

--

Gardens by the Bay, Singapore. Photo by Isaac Matthew on Unsplash

Populations around the world have increasingly shifted away from rural localities to urban centers. This trend has become so persistent that more than half of the global population now lives in urban places. This shift in geography demographics has the potential to cause unintended negative consequences, especially in regards to environmental issues.

Analyzing land cover change is a common way to observe the expansion of urban areas and the diminishment of vegetated areas to accompany population growth. Computing the change in land cover can provide regional planners, government authorities, and conservation organizations important data to develop cities and mitigate environmental hazards more effectively and sustainably.

Urban vs. Rural Populations in 1953
Urban vs. Rural Populations in 2023

Step One — Set the Working Environment

1–0. Geodatabases

Before downloading any data, I will create several geodatabases where I will save and store all data and subsequent layers that I will create. One geodatabase will be created for each of the five cities that I will analyze, and will be called:

“Phoenix.gdb”

“Singapore.gdb”

“Berlin.gdb”

“Buenos_Aires.gdb”

“Addis_Ababa.gdb”

To do this, I am going to open a new python notebook in ArcGIS Pro to run the python scripts and call it “Land_Cover_Notebook.ipynb.” The integrated python notebooks in ArcGIS Pro are based on Jupyter Notebooks, so they are intuitive and easy to use.

Successful Python scripts for creating new geodatabases

Here is my script to create a new geodatabase:

# Import system modules

import arcpy

# Create file geodatabase

arcpy.management.CreateFileGDB(r"C:\Users\cstile11\Desktop\Global Land Cover","Berlin.gdb")

ArcGIS Pro also has the Python Window, which is a great option for running simple and quick scripts while seeing real-time results in the map above the window.

The Python Window at the bottom of the screen for quick and simple scripts

1–1. Coordinate Systems

After creating unique geodatabases, I have added a new map for each of the locations I am going to analyze. I now want to change the coordinate system of each map to match that of the location, and to make sure all of the coordinate systems are consistent with one another.

I am going to use the Universal Transverse Mercator (UTM) zones for my projected coordinate systems. UTM divides the world into 60 north-south zones, each 6 degrees of longitude wide. The zones are numberd consecutively, and coordinates are measured north and east in meters.

The ArcGIS Hub World UTM Grid is a useful interactive map that has all of the UTM zones of the world. I am going to use this web map to find all of my locations and which zone they lie within.

Berlin is in UTM Zone 33

Here are the UTM Zones for each city:

Phoenix — UTM Zone 12

Singapore — UTM Zone 48

Berlin — UTM Zone 33

Buenos Aires — UTM Zone 21

Addis Ababa — UTM Zone 37

In the properties window of each map, I can find the UTM coordinate system that I want to use and change them. There are several UTM systems available in ArcGIS Pro; some are specific to continents and regions of the world. Since I am analyzing cities in various countries, I am going to use the “WGS 1984 UTM” system for each map. All locations, except for Buenos Aires, are in the northern hemisphere.

The correct UTM coordinate system for Berlin

1–2. Land Cover Data

Land cover data is available for download from various websites — some global and some specific to countries. Instead of hunting for this data for each city online, I can navigate to the Living Atlas portal in ArcGIS Pro in the Catalog pane.

A quick search of “Land Cover” provides many results. Included in those results is “Global Land Cover 1992–2019” which is one raster layer that covers land cover for the entire globe. There is also a timeline to this dataset that allows me to select specific years and display those results, which is necessary to compute the changes between various years. I am going to add this data to each map.

The Global Land Cover data in the Living Atlas portal

This land cover raster layer is in 300 meter cell size resolution and 8 bit unsigned pixel type and depth.

Step Two — Edit Land Cover Data

2–0. Set the 1992 Timestamp

Since the land cover raster layer from the portal contains data from every year between 1992 and 2019, I need to timestamp each year to create a new layer so I can compare changes later.

To timestamp, I am going to create a new definition query that will display the data for the earliest time that is available, which is January 1, 1992.

Definition Query = WHERE “Start Date” is equal to “1/11992 12:00:00 AM”

Now the 1992 data is displayed in my map where I will export that data as its own layer.

2–1. Export the 1992 Raster

To export the 1992 data, I will use the “Export Raster” tool. Before running the tool, I will make sure that my map’s current display is at a 1:400,000 scale, which will be the same for every city’s map so I have consistency across all maps and layers.

This tool will also allow me to set the correct coordinate system as well as to clip down the layer to a more manageable size. An optional Python script, which I will show later, can accomplish the same task.

The new layer created has been called “Berlin_1992.gdb.”

Exporting the 1992 raster

2–2. Set the 2019 Timestamp

Setting the 2019 timestamp will be the same method as the 1992 data. I need to make sure this definition query is executed on the original raster layer that contains all years, not the new layer I just created for 1992.

Definition Query = WHERE “Start Date” is equal to “1/1/2019 12:00:00 AM”

2–3. Export the 2019 Raster

Just like with the 2019 data, I will use the “Export Raster” tool to make a new raster layer for the 2019 data. This layer will be called “Berlin_2019.gdb.”

I should now have two new rasters, one for 1992 and one for 2019. This process will be repeated for each city. These two rasters will be used for the analyses to detect changes throughout the years.

2–4. (ALTERNATE OPTION) Use Python to Clip & Change Coordinate System

Instead of setting the clipping extent and coordinate system while exporting the raster, here is an alternate option to accomplish those same tasks using a Python script.

The python script will use a “with” command and run the coordinate system first, then it will be able to execute the “Clip” tool. This time, I ran the script in the Python Window.

Python script below, with resulting layer in the map above, Contents pane to left, and Catalog pane in the Berlin geodatabase

Here is my script to set the projected coordinate system and clip the raster for Berlin:

# Clip raster with correct coordinate system

with arcpy.EnvManager(outputCoordinateSystem='PROJCS["WGS_1984_UTM_Zone_33N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",15.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]', extent='320748.742880528 5775837.65853288 474065.083709664 5867894.41339754 PROJCS["WGS_1984_UTM_Zone_33N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",15.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]'):
arcpy.management.Clip("Global Land Cover 1992-2019", "320748.742880528 5775837.65853288 474065.083709664 5867894.41339754", r"C:\Users\colin.stiles\Desktop\GIS Training\Tutorials\Global Land Cover\Berlin.gdb\Berlin_Land_Cover_1992_2019", None, "255", "NONE", "NO_MAINTAIN_EXTENT")

Step Three — Computing Land Cover Change

With all land cover raster layers edited and ready to go for each city, I can now analyze the change in land cover between 1992 and 2019. There are several ways within ArcGIS Pro to analyze the change in rasters from two different years.

3–0. Alternate Tools

A common option that is used is the “Change Detection Wizard” which is a simple and intuitive tool that allows the user to preview results while running the wizard. The Wizard is a guided workflow that encompasses three steps (for categorical change): Configure, Class Configuration, and Output Generation.

There is also the “Compute Change Raster” geoprocessing tool, which accomplishes the exact same thing.

3–1. Executing the Python Script

Instead of using any of the GUI (graphics user interface) tools, I ran my analyses from the following Python script:

# Check Image Analyst extension license

arcpy.CheckOutExtension("ImageAnalyst")

# Compute land cover change from 1992 to 2019

out_raster_dataset = arcpy.ia.ComputeChangeRaster("Berlin_1992", "Berlin_2019", "CATEGORICAL_DIFFERENCE", "'Rainfed Cropland';'Herbaceous Cropland';'Tree or Shrub Cropland';'Mostly Cropland in a Mosaic with Natural Vegetation';'Mostly Natural Vegetation in a Mosaic with Cropland';'Closed to Open Canopy Broadleaved Deciduous Tree Cover';'Closed to Open Canopy Needleleaved Evergreen Tree Cover';'Closed to Open Canopy Needleleaved Deciduous Tree Cover';'Mixed Tree Cover';'Mostly Trees and Shrubs in a Mosaic with Herbaceous Cover';'Mostly Herbaceous Cover in a Mosaic with Trees and Shrubs';Grassland;'Sparse Vegetation';'Flooded Shrub or Herbaceous Cover';'Urban Areas';'Consolidated Bare Areas';'Bodies of Water'", "'Urban Areas'", "CHANGED_PIXELS_ONLY", "AVERAGE", "ClassName", "ClassName")

# Save the output

out_raster_dataset.save(r"C:\Users\cstile11\Desktop\Global Land Cover\Berlin.gdb\Berlin_Change")

Checking the Image Analyst extension is not required, but having the extension license is required in order to run the ArcPy tool. For this Berlin example, the output raster was saved as “Berlin_Change.gdb” in the corresponding geodatabase.

Here is the output raster for Berlin:

All of the cells that changed to urban areas from 1992 to 2019 in Berlin

By clicking on one of these cells, I can see the transformation that took place for the single cell, which is 300 square meters in size. For example, choosing a pink cell will tell me that the area went from Rainfed Cropland in 1992 to Urban Area in 2019. All of the land cover transformations are listed in the Contents pane next to the map.

Step Four — Analyze Land Cover Change Results with a Bar Graph

Creating a bar chart of the results is another useful way to better visualize the resulting changes in land cover. With the “Create Chart” tool in ArcGIS Pro, I can quickly create several types of charts visualizing my analyses.

4–0. Clean Attribute Data

Before creating the chart, I need to organize and clean the data. After opening up the attribute table on the raster and sorting by ‘Area’ in descending order, I can see that there is one row named ‘Other’ and one named ‘No Change’ containing all the pixels that did not experience any change between the years. I do not need these data for the chart, so I will delete them.

4–1. Create the Bar Chart

With the “Create Chart” tool, I am going to create a new bar chart. I want my 1992 land cover classes on the x-axis and the total area that was changed on the y-axis.

Setting the chart variables

4–2. Edit the Chart Properties

After editing the chart series and formatting, here is the final bar chart that displays the type and amount of land cover from 1992 that changed to urban land cover in 2019:

The greatest amount of land cover that changed to Urban was Herbaceous Cropland

Step Five — Compare All Cities

Each city ended up with different results, but the general theme is the same — in the years from 1992 to 2019, all five cities experienced significant urban growth. Much of this urban growth came at the cost of cropland, forested areas, and shrublands.

5–0. Phoenix Results

Phoenix: Shrubland → Urban = 1,469,143,888.29 sq. meters

5–1. Addis Ababa Results

Addis Ababa: Rainfed Cropland → Urban = 107,386,045.762 sq. meters

5–2. Buenos Aires Results

Buenos Aires: Rainfed Cropland → Urban = 234,686,153.85 sq. meters

5–3. Singapore Results

Singapore: Herbaceous Cropland → Urban = 385,112,269.798 sq. meters

5–4. Berlin Results

1992 vs. 2019
Berlin: Herbaceous Cropland → Urban = 194,751,000 sq. meters

Conclusion

Using tools in ArcGIS Pro and Python, cities and regions have the ability to analyze changes in land cover throughout many years. As we have seen here, no two cities will have the same results. To take this analysis even further, we could compare year by year to identify which years experienced the most change, and why. These analyses can help cities plan for the future to grow sustainably and more efficiently, taking any guesswork out of development and prioritizing the preservation of natural landscapes.

Resources

ArcGIS Pro 3.0.2. Copyright 1995–2022. Esri, Inc.

--

--

Colin T. Stiles, GISP
Colin T. Stiles, GISP

Written by Colin T. Stiles, GISP

Welcome to my portfolio! Here you can find a small sample of GIS case studies of mine. Subscribe or check back regularly for new stories. I hope you enjoy!