# Import required libraries
import earthaccess
import warnings
import requests
import pandas as pd
import os
'USE_PYGEOS'] = '0'
os.environ[import geopandas
import folium
import folium.plugins
import seaborn as sns
import glob
import numpy as np
import matplotlib.pyplot as plt
import branca.colormap as cm
from folium import Map, TileLayer
from branca.element import Figure
from pystac_client import Client
from pyproj import Geod
from shapely import wkt
from datetime import datetime
from folium.plugins import MousePosition
Interactive Session: Human Anthropogenic Emissions (ODIAC)
About the Data
The Open-Data Inventory for Anthropogenic Carbon dioxide (ODIAC) is a high-spatial resolution global emission data product of CO₂ emissions from fossil fuel combustion (Oda and Maksyutov, 2011). ODIAC pioneered the combined use of space-based nighttime light data and individual power plant emission/location profiles to estimate the global spatial extent of fossil fuel CO₂ emissions. With the innovative emission modeling approach, ODIAC achieved the fine picture of global fossil fuel CO₂ emissions at a 1x1km.
Requirements
- Set up Python Environment - See setup_instructions.md in the
/setup/
folder
Learning Objectives
- How to use
U.S. GHG Center STAC Catalog
to access ‘ODIAC Fossil Fuel CO₂ Emissions’ andOCO-2 GEOS Column CO₂ Concentrations
data. - How to use
VEDA STAC Catalog
to accessNO₂ Emissions
data. - How to visualize datasets using
folium
and perfom zonal statistics over South Africa. - How to plot time series plot for
ODIAC
and analyze the results.
Approach
- Identify available dates and temporal frequency of observations for the given collection using the GHGC API
/stac
endpoint. Collection processed in this notebook is ODIAC CO₂ emissions version 2022. - Pass the STAC item into raster API
/stac/tilejson.json
endpoint - We’ll visualize two tiles (side-by-side) allowing for comparison of each of the time points using
folium.plugins.DualMap
- After the visualization, we’ll perform zonal statistics for a given polygon.
Setup
Import the required Python libraries.
Querying the STAC API
Search for ODIAC Fossil Fuel Co2 Emissions, OCO-2 and NO2 Emissions dataset
# Provide STAC and RASTER API endpoints
= "http://ghg.center/api/stac"
STAC_API_URL = "https://ghg.center/api/raster"
RASTER_API_URL
= "https://staging-stac.delta-backend.com"
STAC_API_URL_veda = "https://staging-raster.delta-backend.com"
RASTER_API_URL_veda
#Please use the collection name similar to the one used in STAC collection.
# Name of the collection for ODIAC dataset.
= "odiac-ffco2-monthgrid-v2022"
collection_name_odiac = "oco2geos-co2-daygrid-v10r"
collection_name_oco = "no2-monthly" collection_name_no2
# Fetching the collection from STAC collections using appropriate endpoint.
= requests.get(f"{STAC_API_URL}/collections/{collection_name_odiac}").json()
collection_odiac
collection_odiac
= requests.get(f"{STAC_API_URL}/collections/{collection_name_oco}").json()
collection_oco2
collection_oco2
= requests.get(f"{STAC_API_URL_veda}/collections/{collection_name_no2}").json()
collection_no2 collection_no2
Examining the contents of our collection
under summaries
we see that the data is available from January 2000 to December 2021. By looking at the dashboard:time density
we observe that the periodic frequency of these observations is monthly.
# Create a function that would search for the number of items in above data collection in the STAC API
def get_item_count(STAC_API_URL, collection_id):
= 0
count = f"{STAC_API_URL}/collections/{collection_id}/items"
items_url
while True:
= requests.get(items_url)
response
if not response.ok:
print("error getting items")
exit()
= response.json()
stac += int(stac["context"].get("returned", 0))
count next = [link for link in stac["links"] if link["rel"] == "next"]
if not next:
break
= next[0]["href"]
items_url
return count
# Check total number of items available
= get_item_count(STAC_API_URL,collection_name_odiac)
number_of_items_odiac = requests.get(f"{STAC_API_URL}/collections/{collection_name_odiac}/items?limit={number_of_items_odiac}").json()["features"]
items_odiac print(f"Found {len(items_odiac)} odiac items")
= get_item_count(STAC_API_URL,collection_name_oco)
number_of_items_oco2 = requests.get(f"{STAC_API_URL}/collections/{collection_name_oco}/items?limit={number_of_items_oco2}").json()["features"]
items_oco2 print(f"Found {len(items_oco2)} oco2 items")
= get_item_count(STAC_API_URL_veda,collection_name_no2)
number_of_items_no2 = requests.get(f"{STAC_API_URL_veda}/collections/{collection_name_no2}/items?limit={number_of_items_no2}").json()["features"]
items_no2 print(f"Found {len(items_no2)} no2 items")
This makes sense as there are 22 years between 2000 - 2021, with 12 months per year, meaning 264 records in total.
Below, we are entering the minimum and maximum values to provide our upper and lower bounds in rescale_values
.
Exploring Changes in Carbon Dioxide (CO₂) levels using the Raster API
We will explore changes in fossil fuel emissions in urban egions. In this notebook, we’ll explore the impacts of these emissions and explore these changes over time. We’ll then visualize the outputs on a map using folium
.
# to access the year value from each item more easily, this will let us query more explicity by year and month (e.g., 2020-02)
# Set the asset values to the appropriate parameter
= {item["properties"]["start_datetime"][:7]: item for item in items_odiac}
items_odiac = "co2-emissions"
asset_name
= {item["properties"]["datetime"][:10]: item for item in items_oco2}
items_oco2 = "xco2"
asset_name1
= {item["properties"]["start_datetime"][:10]: item for item in items_no2} items_no2
# Below, we are entering the minimum and maximum values to provide our upper and lower bounds
= {"max":items_odiac[list(items_odiac.keys())[0]]["assets"][asset_name]["raster:bands"][0]["histogram"]["max"], "min":items_odiac[list(items_odiac.keys())[0]]["assets"][asset_name]["raster:bands"][0]["histogram"]["min"]}
rescale_values_odiac = {'max':415 , 'min': 412}
rescale_values_oco2 = {'max': 9050373673124971, 'min': 0} rescale_values_no2
Now we will pass the item id, collection name, and rescaling_factor
to the Raster API
endpoint. We will do this twice, once for January 2020 and again for January 2000, so that we can visualize each event independently.
Opening and Exploring Data Using the Raster API
In this notebook, we will explore the temporal impacts of methane emissions. We will visualize the outputs on a map using ‘folium’.
# Set a colormap for the granule
# Please refer to matplotlib library if you'd prefer choosing a different color ramp (https://matplotlib.org/stable/users/explain/colors/colormaps.html)
= "rainbow" # please select the color ramp from matplotlib library.
color_map
# Extract and display the January 2020 tile using the appropriate ID, colormap, rescale values, and datetime (items['2020-01'])
= requests.get(
january_2020_tile f"{RASTER_API_URL}/stac/tilejson.json?collection={items_odiac['2020-01']['collection']}&item={items_odiac['2020-01']['id']}"
f"&assets={asset_name}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map}"
f"&rescale={rescale_values_odiac['min']},{rescale_values_odiac['max']}",
).json()
january_2020_tile
= "magma"
color_map1 # Extract and display the OCO-2 January 2020 tile using the appropriate ID, colormap, rescale values, and datetime (items['2020-01-20'])
= requests.get(
oco2_1 f"{RASTER_API_URL}/stac/tilejson.json?collection={items_oco2['2020-01-20']['collection']}&item={items_oco2['2020-01-20']['id']}"
f"&assets={asset_name1}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map1}"
f"&rescale={rescale_values_oco2['min']},{rescale_values_oco2['max']}",
).json()
oco2_1
# Extract and display the NO-2 January 2020 tile using the appropriate ID, colormap, rescale values, and datetime (items['2020-01-01'])
= requests.get(
no2_1 f"{RASTER_API_URL_veda}/stac/tilejson.json?collection={items_no2['2020-01-01']['collection']}&item={items_no2['2020-01-01']['id']}"
f"&assets=cog_default"
f"&color_formula=gamma+r+1.05&colormap_name={color_map1}"
f"&rescale={rescale_values_no2['min']},{rescale_values_no2['max']}",
).json() no2_1
Define Spatial Region of Interest
For this example, our spatial region of interest (ROI) will be the a region in South Africa.
= {
sa_aoi "type": "FeatureCollection",
"features": [
{"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
[25.519052777398997,
-24.8470086420499
],
[25.519052777398997,
-28.145634397543844
],
[30.29637465013832,
-28.145634397543844
],
[30.29637465013832,
-24.8470086420499
],
[25.519052777398997,
-24.8470086420499
]
]
],"type": "Polygon"
}
}
] }
Visualizing CO₂ emissions
= folium.Map(location=(-25.943840, 29.789560), zoom_start=7)
map_
= TileLayer(
map_layer_2020 =january_2020_tile["tiles"][0],
tiles="GHG",
attr="ODIAC",
name =0.5,
opacity
)= TileLayer(
map_layer_2020_2 =no2_1["tiles"][0],
tiles="GHG",
attr= "NO2",
name =0.5,
opacity
)= folium.plugins.SideBySideLayers(layer_left=map_layer_2020, layer_right=map_layer_2020_2)
sbs
map_layer_2020.add_to(map_)
map_layer_2020_2.add_to(map_)
# Load the GeoJSON file for South Africa
="South Africa").add_to(map_)
folium.GeoJson(sa_aoi, name
sbs.add_to(map_)
MousePosition().add_to(map_)# visualising the map
map_
# Set initial zoom and center of map
= folium.Map(location=(-25.943840, 29.789560), zoom_start=7)
map_
# January 2020
= TileLayer(
map_layer_2020_odiac =january_2020_tile["tiles"][0],
tiles="GHG",
attr="ODIAC",
name =0.5,
opacity
)
map_layer_2020_odiac.add_to(map_)
# OCO-2 2020
= TileLayer(
map_layer_2020_oco2 =oco2_1["tiles"][0],
tiles="GHG",
attr= "OCO2",
name =0.5,
opacity
)
map_layer_2020_oco2.add_to(map_)
# NO-2 2020
= TileLayer(
map_layer_2020_no2 =no2_1["tiles"][0],
tiles="GHG",
attr= "NO2",
name =0.5,
opacity
)
map_layer_2020_no2.add_to(map_)
="South Africa").add_to(map_)
folium.GeoJson(sa_aoi, name=False,position='bottomleft').add_to(map_)
folium.LayerControl(collapsed
# visualising the map
map_
Generate Statistics and Time Series Lineplots for CO2 Emission
# Check total number of items available
= requests.get(
items f"{STAC_API_URL}/collections/{collection_name_odiac}/items?limit={number_of_items}"
"features"]
).json()[print(f"Found {len(items)} items")
# Explore one item to see what it contains
items
# the bounding box should be passed to the geojson param as a geojson Feature or FeatureCollection
def generate_stats(item, geojson):
= requests.post(
result f"{RASTER_API_URL}/cog/statistics",
={"url": item["assets"][asset_name]["href"]},
params=geojson,
json'features']
).json()[return {
**result[0]["properties"],
"start_datetime": item["properties"]["start_datetime"][:7],
}
With the function above we can generate the statistics for the AOI.
%%time
= [generate_stats(item,sa_aoi) for item in items] stats
0] stats[
# Manipulating and cleaning the stats output from the previous cell
def clean_stats(stats_json) -> pd.DataFrame:
= pd.json_normalize(stats_json)
df = [col.replace("statistics.b1.", "") for col in df.columns]
df.columns "date"] = pd.to_datetime(df["start_datetime"])
df[return df
= clean_stats(stats)
df 5) df.head(
Visualizing the Data as a Time Series
We can now explore the ODIAC fossil fuel emission time series available (January 2000 -December 2021) for the Texas, Dallas area of USA. We can plot the data set using the code below:
import matplotlib.pyplot as plt
= plt.figure(figsize=(20, 10))
fig
# Set the plot elements
plt.plot("date"],
df["max"],
df[="red",
color="-",
linestyle=0.5,
linewidth="Max monthly CO₂ emissions",
label
)
# Set the labels for the X and Y axis and assign a title for the plot
plt.legend()"Years")
plt.xlabel("CO2 emissions gC/m2/d")
plt.ylabel("CO2 emission Values for South Africa (2000-2021)") plt.title(