# Import libraries ----
# For general analysis
import numpy as np
import pandas as pd
# For spatial data
import geopandas as gpd
import rioxarray as rioxr
from shapely import box
# For STAC API
import pystac_client # To access STAC catalogs
import planetary_computer # To sign items from the MPC STAC catalog
# For plotting
import matplotlib.pyplot as plt
from IPython.display import Image # To nicely display images
import contextily as cx # For basemap
import matplotlib.patches as mpatches # For creating custom legend
# Set Pandas to display all columns
"display.max.columns", None) pd.set_option(
About
In 2021, Maricopa County —home to the Phoenix metropolitan area— was identified as the U.S. county with the most significant increase in developed land since 2001 [1]. This rapid urban sprawl has profound implications for biodiversity and the health of surrounding natural ecosystems.
In this notebook, I investigate the impacts of urban expansion by analyzing a dataset that captures values for the Biodiversity Intactness Index (BII) [2]. The Biodiversity Intactness Index (BII) measures biodiversity change using abundance data on plants, fungi and animals worldwide. The BII shows how local terrestrial biodiversity responds to human pressures such as land use change and intensification [3].
I examine changes in BII in the Phoenix county subdivision area between 2017 and 2020, shedding light on how urban growth affects biodiversity over time.
See my GitHub repository: https://github.com/marinakochuten/phoenix-bii-change
Highlights
- Access data through an API
- Vector raster intersctions and raster algebra
- Visualizing spatial data using
matplotlib
Data
Biodiversity Intactness Index (BII) Time Series: I access the io-biodiversity collection from the Microsoft Planetary Computer STAC catalog using an API. In my analysis, I use the 2017 and 2020 rasters covering the Phoenix subdivision.
Citation: Microsoft Planetary Computer data catalogue (2024), io-biodiversity Collection [Data set] Available from: https://planetarycomputer.microsoft.com/dataset/io-biodiversity. Access date: December 3, 2024.
Phoenix Subdivision Shapefile: I use a shapefile containing county boundary lines from the U.S. Census Bureau
Citation: U.S. Census Bureau. (2022). “tl_2022_04_cousub”, TIGER/Line Shapefiles. https://www.census.gov/cgi-bin/geo/shapefiles/index.php?year=2022&layergroup=County+Subdivisions. Access date: December 3, 2024.
References
[1] Z. Levitt and J. Eng, “Where America’s developed areas are growing: ‘Way off into the horizon’,” The Washington Post, Aug. 2021, Available: https://www.washingtonpost.com/nation/interactive/2021/land-development-urban-growth-maps/ [Accessed: Nov. 22, 2024]
[2] F. Gassert, J. Mazzarello, and S. Hyde, “Global 100m Projections of Biodiversity Intactness for the years 2017-2020 [Technical Whitepaper].” Aug. 2022. Available: https://ai4edatasetspublicassets.blob.core.windows.net/assets/pdfs/io -biodiversity/Biodiversity_Intactness_whitepaper.pdf
[3] Measurement of Biodiversity. (2024, 16 November). In Wikipedia. https://en.wikipedia.org/wiki/Measurement_of_biodiversity
Import libraries
Access Data: BII Time Series
For the BII time series, I access the io-biodiversity
collection from the Microsoft Planetary Computer STAC catalog and use the 2017 and 2020 rasters covering the Phoenix subdivision (bounding box coordinates:[-112.826843, 32.974108, -111.184387, 33.863574])
# Create bounding box for search
= [-112.826843, 32.974108, -111.184387, 33.863574]
bbox bbox
[-112.826843, 32.974108, -111.184387, 33.863574]
# Open MPC data catalog (establish connection to API)
= pystac_client.Client.open(
catalog "https://planetarycomputer.microsoft.com/api/stac/v1",
=planetary_computer.sign_inplace,
modifier
)
# Search MPC catalog
= catalog.search(collections = ['io-biodiversity'],
search = bbox
bbox
)
# Retrive search items
= search.item_collection()
items print(f"Returned {len(items)} Items")
items
Returned 4 Items
- type "FeatureCollection"
features[] 4 items
0
- type "Feature"
- stac_version "1.0.0"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/projection/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 2 "https://stac-extensions.github.io/version/v1.1.0/schema.json"
- id "bii_2020_34.74464974521749_-115.38597824385106_cog"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 9 items
0[] 2 items
- 0 -114.7625474
- 1 27.565314
1[] 2 items
- 0 -108.2066425
- 1 27.565314
2[] 2 items
- 0 -108.2066425
- 1 34.7446497
3[] 2 items
- 0 -115.3859782
- 1 34.7446497
4[] 2 items
- 0 -115.3859782
- 1 29.5649638
5[] 2 items
- 0 -115.3581305
- 1 28.0791503
6[] 2 items
- 0 -115.2036202
- 1 27.8662496
7[] 2 items
- 0 -114.9988044
- 1 27.7099428
8[] 2 items
- 0 -114.7625474
- 1 27.565314
bbox[] 4 items
- 0 -115.3859782
- 1 27.565314
- 2 -108.2066425
- 3 34.7446497
properties
- datetime None
- proj:epsg 4326
proj:shape[] 2 items
- 0 7992
- 1 7992
- end_datetime "2020-12-31T23:59:59Z"
proj:transform[] 9 items
- 0 0.0008983152841195215
- 1 0.0
- 2 -115.38597824385106
- 3 0.0
- 4 -0.0008983152841195215
- 5 34.74464974521749
- 6 0.0
- 7 0.0
- 8 1.0
- start_datetime "2020-01-01T00:00:00Z"
links[] 5 items
0
- rel "collection"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
1
- rel "parent"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
2
- rel "root"
- href "https://planetarycomputer.microsoft.com/api/stac/v1"
- type "application/json"
- title "Microsoft Planetary Computer STAC API"
3
- rel "self"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity/items/bii_2020_34.74464974521749_-115.38597824385106_cog"
- type "application/geo+json"
4
- rel "preview"
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/map?collection=io-biodiversity&item=bii_2020_34.74464974521749_-115.38597824385106_cog"
- type "text/html"
- title "Map of item"
assets
data
- href "https://pcdata01euw.blob.core.windows.net/impact/bii-v1/bii_2020/bii_2020_34.74464974521749_-115.38597824385106_cog.tif?st=2024-12-12T00%3A25%3A30Z&se=2024-12-13T01%3A10%3A30Z&sp=rl&sv=2024-05-04&sr=c&skoid=9c8ff44a-6a2c-4dfb-b298-1c9212f64d9a&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2024-12-12T14%3A50%3A12Z&ske=2024-12-19T14%3A50%3A12Z&sks=b&skv=2024-05-04&sig=wSVIBt95DPkLDSnBaVv0RbfnRgEhYRCL4sEMwMxQE2c%3D"
- type "image/tiff; application=geotiff; profile=cloud-optimized"
- title "Biodiversity Intactness"
- description "Terrestrial biodiversity intactness at 100m resolution"
- version "v1"
raster:bands[] 1 items
0
- sampling "area"
- data_type "float32"
- spatial_resolution 100
roles[] 1 items
- 0 "data"
tilejson
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/tilejson.json?collection=io-biodiversity&item=bii_2020_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "application/json"
- title "TileJSON with default rendering"
roles[] 1 items
- 0 "tiles"
rendered_preview
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/preview.png?collection=io-biodiversity&item=bii_2020_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "image/png"
- title "Rendered preview"
- rel "preview"
roles[] 1 items
- 0 "overview"
- collection "io-biodiversity"
1
- type "Feature"
- stac_version "1.0.0"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/projection/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 2 "https://stac-extensions.github.io/version/v1.1.0/schema.json"
- id "bii_2019_34.74464974521749_-115.38597824385106_cog"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 10 items
0[] 2 items
- 0 -114.7625474
- 1 27.565314
1[] 2 items
- 0 -108.2066425
- 1 27.565314
2[] 2 items
- 0 -108.2066425
- 1 34.7446497
3[] 2 items
- 0 -115.3859782
- 1 34.7446497
4[] 2 items
- 0 -115.3859782
- 1 29.5649638
5[] 2 items
- 0 -115.3581305
- 1 28.0791503
6[] 2 items
- 0 -115.2161967
- 1 27.8761311
7[] 2 items
- 0 -115.0068892
- 1 27.7180276
8[] 2 items
- 0 -114.8469891
- 1 27.6174163
9[] 2 items
- 0 -114.7625474
- 1 27.565314
bbox[] 4 items
- 0 -115.3859782
- 1 27.565314
- 2 -108.2066425
- 3 34.7446497
properties
- datetime None
- proj:epsg 4326
proj:shape[] 2 items
- 0 7992
- 1 7992
- end_datetime "2019-12-31T23:59:59Z"
proj:transform[] 9 items
- 0 0.0008983152841195215
- 1 0.0
- 2 -115.38597824385106
- 3 0.0
- 4 -0.0008983152841195215
- 5 34.74464974521749
- 6 0.0
- 7 0.0
- 8 1.0
- start_datetime "2019-01-01T00:00:00Z"
links[] 5 items
0
- rel "collection"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
1
- rel "parent"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
2
- rel "root"
- href "https://planetarycomputer.microsoft.com/api/stac/v1"
- type "application/json"
- title "Microsoft Planetary Computer STAC API"
3
- rel "self"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity/items/bii_2019_34.74464974521749_-115.38597824385106_cog"
- type "application/geo+json"
4
- rel "preview"
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/map?collection=io-biodiversity&item=bii_2019_34.74464974521749_-115.38597824385106_cog"
- type "text/html"
- title "Map of item"
assets
data
- href "https://pcdata01euw.blob.core.windows.net/impact/bii-v1/bii_2019/bii_2019_34.74464974521749_-115.38597824385106_cog.tif?st=2024-12-12T00%3A25%3A30Z&se=2024-12-13T01%3A10%3A30Z&sp=rl&sv=2024-05-04&sr=c&skoid=9c8ff44a-6a2c-4dfb-b298-1c9212f64d9a&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2024-12-12T14%3A50%3A12Z&ske=2024-12-19T14%3A50%3A12Z&sks=b&skv=2024-05-04&sig=wSVIBt95DPkLDSnBaVv0RbfnRgEhYRCL4sEMwMxQE2c%3D"
- type "image/tiff; application=geotiff; profile=cloud-optimized"
- title "Biodiversity Intactness"
- description "Terrestrial biodiversity intactness at 100m resolution"
- version "v1"
raster:bands[] 1 items
0
- sampling "area"
- data_type "float32"
- spatial_resolution 100
roles[] 1 items
- 0 "data"
tilejson
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/tilejson.json?collection=io-biodiversity&item=bii_2019_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "application/json"
- title "TileJSON with default rendering"
roles[] 1 items
- 0 "tiles"
rendered_preview
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/preview.png?collection=io-biodiversity&item=bii_2019_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "image/png"
- title "Rendered preview"
- rel "preview"
roles[] 1 items
- 0 "overview"
- collection "io-biodiversity"
2
- type "Feature"
- stac_version "1.0.0"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/projection/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 2 "https://stac-extensions.github.io/version/v1.1.0/schema.json"
- id "bii_2018_34.74464974521749_-115.38597824385106_cog"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 10 items
0[] 2 items
- 0 -114.7625474
- 1 27.565314
1[] 2 items
- 0 -108.2066425
- 1 27.565314
2[] 2 items
- 0 -108.2066425
- 1 34.7446497
3[] 2 items
- 0 -115.3859782
- 1 34.7446497
4[] 2 items
- 0 -115.3859782
- 1 29.5649638
5[] 2 items
- 0 -115.3581305
- 1 28.0791503
6[] 2 items
- 0 -115.2179933
- 1 27.8869109
7[] 2 items
- 0 -115.1775691
- 1 27.8491816
8[] 2 items
- 0 -115.0014993
- 1 27.7117394
9[] 2 items
- 0 -114.7625474
- 1 27.565314
bbox[] 4 items
- 0 -115.3859782
- 1 27.565314
- 2 -108.2066425
- 3 34.7446497
properties
- datetime None
- proj:epsg 4326
proj:shape[] 2 items
- 0 7992
- 1 7992
- end_datetime "2018-12-31T23:59:59Z"
proj:transform[] 9 items
- 0 0.0008983152841195215
- 1 0.0
- 2 -115.38597824385106
- 3 0.0
- 4 -0.0008983152841195215
- 5 34.74464974521749
- 6 0.0
- 7 0.0
- 8 1.0
- start_datetime "2018-01-01T00:00:00Z"
links[] 5 items
0
- rel "collection"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
1
- rel "parent"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
2
- rel "root"
- href "https://planetarycomputer.microsoft.com/api/stac/v1"
- type "application/json"
- title "Microsoft Planetary Computer STAC API"
3
- rel "self"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity/items/bii_2018_34.74464974521749_-115.38597824385106_cog"
- type "application/geo+json"
4
- rel "preview"
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/map?collection=io-biodiversity&item=bii_2018_34.74464974521749_-115.38597824385106_cog"
- type "text/html"
- title "Map of item"
assets
data
- href "https://pcdata01euw.blob.core.windows.net/impact/bii-v1/bii_2018/bii_2018_34.74464974521749_-115.38597824385106_cog.tif?st=2024-12-12T00%3A25%3A30Z&se=2024-12-13T01%3A10%3A30Z&sp=rl&sv=2024-05-04&sr=c&skoid=9c8ff44a-6a2c-4dfb-b298-1c9212f64d9a&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2024-12-12T14%3A50%3A12Z&ske=2024-12-19T14%3A50%3A12Z&sks=b&skv=2024-05-04&sig=wSVIBt95DPkLDSnBaVv0RbfnRgEhYRCL4sEMwMxQE2c%3D"
- type "image/tiff; application=geotiff; profile=cloud-optimized"
- title "Biodiversity Intactness"
- description "Terrestrial biodiversity intactness at 100m resolution"
- version "v1"
raster:bands[] 1 items
0
- sampling "area"
- data_type "float32"
- spatial_resolution 100
roles[] 1 items
- 0 "data"
tilejson
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/tilejson.json?collection=io-biodiversity&item=bii_2018_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "application/json"
- title "TileJSON with default rendering"
roles[] 1 items
- 0 "tiles"
rendered_preview
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/preview.png?collection=io-biodiversity&item=bii_2018_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "image/png"
- title "Rendered preview"
- rel "preview"
roles[] 1 items
- 0 "overview"
- collection "io-biodiversity"
3
- type "Feature"
- stac_version "1.0.0"
stac_extensions[] 3 items
- 0 "https://stac-extensions.github.io/projection/v1.0.0/schema.json"
- 1 "https://stac-extensions.github.io/raster/v1.1.0/schema.json"
- 2 "https://stac-extensions.github.io/version/v1.1.0/schema.json"
- id "bii_2017_34.74464974521749_-115.38597824385106_cog"
geometry
- type "Polygon"
coordinates[] 1 items
0[] 10 items
0[] 2 items
- 0 -114.7616491
- 1 27.565314
1[] 2 items
- 0 -108.2066425
- 1 27.565314
2[] 2 items
- 0 -108.2066425
- 1 34.7446497
3[] 2 items
- 0 -115.3859782
- 1 34.7446497
4[] 2 items
- 0 -115.3859782
- 1 29.5649638
5[] 2 items
- 0 -115.3581305
- 1 28.0791503
6[] 2 items
- 0 -115.2036202
- 1 27.8662496
7[] 2 items
- 0 -115.0068892
- 1 27.7180276
8[] 2 items
- 0 -114.8469891
- 1 27.6174163
9[] 2 items
- 0 -114.7616491
- 1 27.565314
bbox[] 4 items
- 0 -115.3859782
- 1 27.565314
- 2 -108.2066425
- 3 34.7446497
properties
- datetime None
- proj:epsg 4326
proj:shape[] 2 items
- 0 7992
- 1 7992
- end_datetime "2017-12-31T23:59:59Z"
proj:transform[] 9 items
- 0 0.0008983152841195215
- 1 0.0
- 2 -115.38597824385106
- 3 0.0
- 4 -0.0008983152841195215
- 5 34.74464974521749
- 6 0.0
- 7 0.0
- 8 1.0
- start_datetime "2017-01-01T00:00:00Z"
links[] 5 items
0
- rel "collection"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
1
- rel "parent"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity"
- type "application/json"
2
- rel "root"
- href "https://planetarycomputer.microsoft.com/api/stac/v1"
- type "application/json"
- title "Microsoft Planetary Computer STAC API"
3
- rel "self"
- href "https://planetarycomputer.microsoft.com/api/stac/v1/collections/io-biodiversity/items/bii_2017_34.74464974521749_-115.38597824385106_cog"
- type "application/geo+json"
4
- rel "preview"
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/map?collection=io-biodiversity&item=bii_2017_34.74464974521749_-115.38597824385106_cog"
- type "text/html"
- title "Map of item"
assets
data
- href "https://pcdata01euw.blob.core.windows.net/impact/bii-v1/bii_2017/bii_2017_34.74464974521749_-115.38597824385106_cog.tif?st=2024-12-12T00%3A25%3A30Z&se=2024-12-13T01%3A10%3A30Z&sp=rl&sv=2024-05-04&sr=c&skoid=9c8ff44a-6a2c-4dfb-b298-1c9212f64d9a&sktid=72f988bf-86f1-41af-91ab-2d7cd011db47&skt=2024-12-12T14%3A50%3A12Z&ske=2024-12-19T14%3A50%3A12Z&sks=b&skv=2024-05-04&sig=wSVIBt95DPkLDSnBaVv0RbfnRgEhYRCL4sEMwMxQE2c%3D"
- type "image/tiff; application=geotiff; profile=cloud-optimized"
- title "Biodiversity Intactness"
- description "Terrestrial biodiversity intactness at 100m resolution"
- version "v1"
raster:bands[] 1 items
0
- sampling "area"
- data_type "float32"
- spatial_resolution 100
roles[] 1 items
- 0 "data"
tilejson
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/tilejson.json?collection=io-biodiversity&item=bii_2017_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "application/json"
- title "TileJSON with default rendering"
roles[] 1 items
- 0 "tiles"
rendered_preview
- href "https://planetarycomputer.microsoft.com/api/data/v1/item/preview.png?collection=io-biodiversity&item=bii_2017_34.74464974521749_-115.38597824385106_cog&assets=data&tile_format=png&colormap_name=io-bii&rescale=0%2C1&expression=0.97%2A%28data_b1%2A%2A3.84%29&format=png"
- type "image/png"
- title "Rendered preview"
- rel "preview"
roles[] 1 items
- 0 "overview"
- collection "io-biodiversity"
There are 4 items in my search, one raster for each year in the range 2017 - 2020.
BII Time Series Exploration
First, I’ll take a quick look at one of the pre-redered images from the catalog
# Select unique search items for 2020 and 2017
= items[0]
item2020 = items[3]
item2017
# Display a pre-rendered image
=item2020.assets['rendered_preview'].href, width=400) Image(url

Here, we have a raster showing biodiversity intactness. Darker green indicates higher intactness.
Let’s dive a litter deeper by importing the rasters for 2017 and 2020. Down the line, I will compare changes between these two rasters to show BII changes from 2017 to 2020!
# Access raster data from items
= rioxr.open_rasterio(item2020.assets['data'].href)
bii_2020 = rioxr.open_rasterio(item2017.assets['data'].href)
bii_2017
# Let's look at the data for 2020
bii_2020
<xarray.DataArray (band: 1, y: 7992, x: 7992)> Size: 255MB [63872064 values with dtype=float32] Coordinates: * band (band) int64 8B 1 * x (x) float64 64kB -115.4 -115.4 -115.4 ... -108.2 -108.2 -108.2 * y (y) float64 64kB 34.74 34.74 34.74 34.74 ... 27.57 27.57 27.57 spatial_ref int64 8B 0 Attributes: AREA_OR_POINT: Area scale_factor: 1.0 add_offset: 0.0
Notice that band is a dimension of length 1. We can go ahead and “squeeze” the raster to simplify it. I will do this for both my 2017 and my 2020 rasters:
# Remove length 1 dimension (band)
= bii_2020.squeeze().drop_vars('band')
bii_2020 = bii_2017.squeeze().drop_vars('band')
bii_2017
print("Sizes of dimensions, bii_2020:", dict(bii_2020.sizes))
print("Sizes of dimensions, bii_2017:", dict(bii_2017.sizes))
Sizes of dimensions, bii_2020: {'y': 7992, 'x': 7992}
Sizes of dimensions, bii_2017: {'y': 7992, 'x': 7992}
Lastly, I want to know the CRS of this data.
# Check raster CRS
bii_2020.rio.crs
CRS.from_epsg(4326)
The CRS of my rasters is EPSG:4326! I will keep this in mind as I explore my next geospatial object, the Phoenix Subdivision shapefile.
Access Data: Phoenix Subdivision Shapefile
# Read in the shapefile
= gpd.read_file('data/tl_2022_04_cousub.shp') arizona
Phoenix Subdivision Shapefile exploration
# Look at the head of the data
3) arizona.head(
STATEFP | COUNTYFP | COUSUBFP | COUSUBNS | GEOID | NAME | NAMELSAD | LSAD | CLASSFP | MTFCC | CNECTAFP | NECTAFP | NCTADVFP | FUNCSTAT | ALAND | AWATER | INTPTLAT | INTPTLON | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 04 | 005 | 91198 | 01934931 | 0400591198 | Flagstaff | Flagstaff CCD | 22 | Z5 | G4040 | None | None | None | S | 12231052883 | 44653332 | +35.1066114 | -111.3662497 | POLYGON ((-112.13370 35.85596, -112.13368 35.8... |
1 | 04 | 005 | 91838 | 01934953 | 0400591838 | Kaibab Plateau | Kaibab Plateau CCD | 22 | Z5 | G4040 | None | None | None | S | 7228864534 | 29327221 | +36.5991097 | -112.1368033 | POLYGON ((-112.66039 36.53941, -112.66033 36.5... |
2 | 04 | 005 | 91683 | 01934950 | 0400591683 | Hualapai | Hualapai CCD | 22 | Z5 | G4040 | None | None | None | S | 2342313339 | 3772690 | +35.9271665 | -113.1170408 | POLYGON ((-113.35416 36.04097, -113.35416 36.0... |
# Preliminary plot
arizona.plot()
The shapefile contains data for all of Arizona. Let’s do a bit of processing to clean the column names and subset to Phoenix.
# Convert column names to lowercase
= arizona.columns.str.lower()
arizona.columns
# Filter to Phoenix
= arizona[arizona.name == "Phoenix"]
phoenix
# Preliminary plot
phoenix.plot()
# Check the CRS
phoenix.crs
<Geographic 2D CRS: EPSG:4269>
Name: NAD83
Axis Info [ellipsoidal]:
- Lat[north]: Geodetic latitude (degree)
- Lon[east]: Geodetic longitude (degree)
Area of Use:
- name: North America - onshore and offshore: Canada - Alberta; British Columbia; Manitoba; New Brunswick; Newfoundland and Labrador; Northwest Territories; Nova Scotia; Nunavut; Ontario; Prince Edward Island; Quebec; Saskatchewan; Yukon. Puerto Rico. United States (USA) - Alabama; Alaska; Arizona; Arkansas; California; Colorado; Connecticut; Delaware; Florida; Georgia; Hawaii; Idaho; Illinois; Indiana; Iowa; Kansas; Kentucky; Louisiana; Maine; Maryland; Massachusetts; Michigan; Minnesota; Mississippi; Missouri; Montana; Nebraska; Nevada; New Hampshire; New Jersey; New Mexico; New York; North Carolina; North Dakota; Ohio; Oklahoma; Oregon; Pennsylvania; Rhode Island; South Carolina; South Dakota; Tennessee; Texas; Utah; Vermont; Virginia; Washington; West Virginia; Wisconsin; Wyoming. US Virgin Islands. British Virgin Islands.
- bounds: (167.65, 14.92, -40.73, 86.45)
Datum: North American Datum 1983
- Ellipsoid: GRS 1980
- Prime Meridian: Greenwich
The CRS of this GeoDataFrame
is EPSG:4269. Note that this is different from the BII rasters, and I will need to match them before they can interact.
# Match shapefile CRS to raster CRS
= phoenix.to_crs(bii_2020.rio.crs)
phoenix assert phoenix.crs == bii_2020.rio.crs
Map the Phoenix subdivision
To contextualize the Phoenix subdivision within its broader geographic context, I will include a basemap from the contextily
package. The simplest way to access contextily
and get a background map to go with your geographic data is to use the add_basemap
method. Because the method takes a matplotlib
axis, it is easy to integrate contextily with any other package for geospatial data that plots to matplotlib, such as rasterio
and geopandas
.
Setting the parameter source
within the add_basemap
method, I can choose the style of basemap. In this case, I am going to use CartoDB.Positron for its simplicity.
# Initialize plot
= plt.subplots(figsize = (9, 9))
fig, ax
# Remove axes for cleaner map
'off')
ax.axis(
# Add Phoenix subdivision
= ax, color = "none", edgecolor = "firebrick", linewidth = 2)
phoenix.plot(ax
# Add basemap
= phoenix.crs, source = cx.providers.CartoDB.PositronNoLabels)
cx.add_basemap(ax, crs = phoenix.crs, source = cx.providers.CartoDB.PositronOnlyLabels) # To bring labels above boundary outline
cx.add_basemap(ax, crs
# Create legend for Phoenix border
= mpatches.Patch(color = "#A83A34",
legend_border = 'Phoenix subdivision border')
label
# Plot legend
= [legend_border],
ax.legend(handles = 'upper left',
loc = 10,
fontsize = 'none',
facecolor = 'none')
edgecolor
# Plot title
'Map of study area: Phoenix subdivision border')
ax.set_title(
plt.show()
Calculate biodiversity intactness in the Phoenix subdivision
Next, I am interested in calculating the percentage of area of the Phoenix subdivision with a BII of at least 0.75 in both 2017 and 2020. I will use these to then find areas that lost BII over those 4 years.
First, taking a look at how the raster is located with respect to the Phoenix subdivision:
# Create GeoDataFrame from raster bounding box
= gpd.GeoDataFrame(geometry = [box(*bii_2020.rio.bounds())],
bii_bbox = bii_2020.rio.crs)
crs
# Plot raster boundary and Phoenix boundary
= plt.subplots()
fig, ax =ax, color='white', edgecolor ='black')
phoenix.plot(ax=ax, alpha=0.3)
bii_bbox.plot(ax
plt.show()
I see that I need to crop the BII rasters to the extent of the Phoenix subdivision. Clipping can be a costly operation for such a big raster relative to a detailed geometry. So we will perform the clipping in two steps:
- Clip the raster using the Phoenix subdivision bounding box using
rio.clip_box()
and then - Clip the simplified raster to the Phoenix subdivision using
rio.clip()
.
# Clip large raster to detailed geometry in two steps
= bii_2020.rio.clip_box(*phoenix.total_bounds)
bii_2020_step1 = bii_2020_step1.rio.clip(phoenix.geometry) bii_2020_clip
# Take a look at the clipped raster
bii_2020_clip.plot()
Now, the BII raster for 2020 is clipped to the Phoenix subdivision! I will repeat the process for the 2017 raster, and then move on to the next step.
# Clip large raster to detailed geometry in two steps
= bii_2017.rio.clip_box(*phoenix.total_bounds)
bii_2017_step1 = bii_2017_step1.rio.clip(phoenix.geometry) bii_2017_clip
Next, I want to select areas in each raster that have a BII of at least 0.75.
# Create a new object where if BII >= 0.75, the new value = True, and if BII < 0.75, the new value = False
= bii_2017_clip >= 0.75
bii_2017_intact bii_2017_intact.plot()
# Repeat for 2020
= bii_2020_clip >= 0.75
bii_2020_intact bii_2020_intact.plot()
Lastly, I can calculate the percentage of area in the Phoenix subdivision that has a BII of at least 0.75. To do so, I will calculate the total area in the subdivision and then divide the area where BII >= 0.75 by the total subdivision area. For ease of calculation, I will be using number of cells in the rasters to represent area.
First, I use np.unique()
to get the number of pixels that were < 0.75 and >= 0.75 in the cropped rasters for 2017 and 2020. By setting the parameter return_counts = True
, I can see both the unique values and their counts.
# Find the total count of values that are < 0.75 and >= 0.75 for 2017
= np.unique(bii_2017_intact, return_counts=True)
values_2017, counts_2017
# Create a data frame with the pixel counts for 2017
= pd.DataFrame({"above_threshold" : values_2017,
pix_counts_2017 "number_of_pix" : counts_2017})
# Find the total count of values that are < 0.75 and >= 0.75 for 2020
= np.unique(bii_2020_intact, return_counts=True)
values_2020, counts_2020
# Create a data frame with the pixel counts for 2020
= pd.DataFrame({"above_threshold" : values_2020,
pix_counts_2020 "number_of_pix" : counts_2020})
# Look at the 2017 data frame
pix_counts_2017
above_threshold | number_of_pix | |
---|---|---|
0 | False | 553037 |
1 | True | 24133 |
# Look at the 2020 data frame
pix_counts_2020
above_threshold | number_of_pix | |
---|---|---|
0 | False | 555184 |
1 | True | 21986 |
Next, I want to calculate the total number of pixels within the Phoenix subdivision. I use the same process as above to find the total area of the Phoenix subdivision using the 2020 clipped BII raster.
# Find areas where values in the bii_2020_clip raster are above 0 , representing all the pixels within the Phoenix subdivision as True
= bii_2020_clip > 0
total_area
# Find the count of pixels where the above returned true, representing the total number of pixels within Phoenix
= np.unique(total_area, return_counts=True)
values, counts
# Place the counts and values in a data frame
= pd.DataFrame({"above_zero" : values,
total_area "number_pixels" : counts})
total_area
above_zero | number_pixels | |
---|---|---|
0 | False | 238476 |
1 | True | 338694 |
# Access the value contianing the number of pixels within the Phoenix subdivision and save into a variable
= total_area.iloc[1,1]
total_area total_area
338694
Now, I can divide the number of pixels that are above the 0.75 threshold by the total number of pixels for 2017 and 2020 to find the percentage of area in the Phoenix subdivision that has a BII of at least 0.75.
# Calculate the percentage of area in Phoenix that has a BII of >= 0.75 in 2017 and 2020
= (pix_counts_2017.iloc[1, 1] / total_area) * 100
percent_intact_2017 = (pix_counts_2020.iloc[1, 1] / total_area) * 100
percent_intact_2020
print("The percentage of area for 2017 is: ", round(percent_intact_2017,2), "%")
print("The percentage of area for 2020 is: ", round(percent_intact_2020, 2), "%")
print("The percentage of area lost from 2017 to 2020 is: ", round(percent_intact_2017 - percent_intact_2020, 2), "%")
The percentage of area for 2017 is: 7.13 %
The percentage of area for 2020 is: 6.49 %
The percentage of area lost from 2017 to 2020 is: 0.63 %
Visualize the change in biodiversity intactness from 2017 to 2020
Now, I want to create a visualization showing the area with BII >= 0.75 in 2017 that was lost by 2020. I will need to create a raster that contains a value of 1 where BII was >= 0.75 in 2017 but not in 2020.
# Change the values in my BII rasters from True/False to 1 and 0
= bii_2017_intact.astype('int')
bii_2017_int = bii_2020_intact.astype('int')
bii_2020_int np.unique(bii_2017_int)
array([0, 1])
When I subtract the 2020 BII raster from the 2017 BII raster, I then have a new raster with a value of 1 in areas that had BII >= 0.75 in 2017 but not in 2020.
# Create raster with BII change from 2017 to 2020
= bii_2017_int - bii_2020_int
bii_change np.unique(bii_change)
array([-1, 0, 1])
Notice that we have 3 unique values, -1, 0, and 1. To see only areas that lost BII, I want to select the cells where bii_change == 1
# Select cells where BII was lost from 2017 to 2020
= bii_change.where(bii_change == 1)
bii_loss bii_loss.plot()
Now, I am ready to piece things together:
# Initialize figure
= plt.subplots(figsize = (7, 6))
fig, ax
# Remove axes for cleaner plot
'off')
ax.axis(
# Add 2020 BII raster
= ax,
bii_2020_clip.plot(ax = 'Greens',
cmap = {"location": "bottom",
cbar_kwargs "label": "BII for 2020",
"shrink" : 0.75})
# Add BII loss
= ax,
bii_loss.plot(ax = 'brg',
cmap = False)
add_colorbar
# Create legend for BII loss
= mpatches.Patch(color = "#E93323", label = 'Area with BIl ≥ 0.75 lost from 2017 to 2020')
legend_loss
# Plot legend
= [legend_loss],
ax.legend(handles = 'lower center',
loc =(0.5, -0.22),
bbox_to_anchor= 8,
fontsize = 'none',
facecolor = 'none')
edgecolor
# Add Phoenix boundary
= ax,
phoenix.plot(ax = "none",
color = "black",
edgecolor = 1)
linewidth
# Add plot title
"Biodiversity Intactness Index (BII) \n Phoenix subdivision", weight = 'bold', fontsize = '10')
ax.set_title(
plt.show()
This figure shows the Biodiversity Intactness Index (BII) change in the Phoenix subdivision from 2017 to 2020. In that time, 0.63% of the area with BII over 0.75 was lost, shown in red on the map.