# Import the following libraries
import requests
import folium
import folium.plugins
from folium import Map, TileLayer
from pystac_client import Client
import branca
import pandas as pd
import matplotlib.pyplot as plt
OCO-2 GEOS Column CO₂ Concentrations
Approach
- Identify available dates and temporal frequency of observations for the given collection using the GHGC API
/stac
endpoint. The collection processed in this notebook is the OCO-2 GEOS Column CO₂ Concentrations data product. - Pass the STAC item into the raster API
/stac/tilejson.json
endpoint. - Using
folium.plugins.DualMap
, visualize two tiles (side-by-side), allowing time point comparison. - After the visualization, perform zonal statistics for a given polygon.
About the Data
In July 2014, NASA successfully launched the first dedicated Earth remote sensing satellite to study atmospheric carbon dioxide (CO₂) from space. The Orbiting Carbon Observatory-2 (OCO-2) is an exploratory science mission designed to collect space-based global measurements of atmospheric CO₂ with the precision, resolution, and coverage needed to characterize sources and sinks (fluxes) on regional scales (≥1000 km). This dataset provides global gridded, daily column-averaged carbon dioxide (XCO₂) concentrations from January 1, 2015 - February 28, 2022. The data are derived from OCO-2 observations that were input to the Goddard Earth Observing System (GEOS) Constituent Data Assimilation System (CoDAS), a modeling and data assimilation system maintained by NASA’s Global Modeling and Assimilation Office (GMAO). Concentrations are measured in moles of carbon dioxide per mole of dry air (mol CO₂/mol dry) at a spatial resolution of 0.5° x 0.625°. Data assimilation synthesizes simulations and observations, adjusting modeled atmospheric constituents like CO₂ to reflect observed values. With the support of NASA’s Carbon Monitoring System (CMS) Program and the OCO Science Team, this dataset was produced as part of the OCO-2 mission which provides the highest quality space-based XCO₂ retrievals to date.
For more information regarding this dataset, please visit the OCO-2 GEOS Column CO₂ Concentrations data overview page.
Install the Required Libraries
Required libraries are pre-installed on the GHG Center Hub. If you need to run this notebook elsewhere, please install them with this line in a code cell:
%pip install requests folium rasterstats pystac_client pandas matplotlib –quiet
Querying the STAC API
First, we are going to import the required libraries. Once imported, they allow better executing a query in the GHG Center Spatio Temporal Asset Catalog (STAC) Application Programming Interface (API) where the granules for this collection are stored.
# Provide STAC and RASTER API endpoints
= "http://ghg.center/api/stac"
STAC_API_URL = "https://ghg.center/api/raster"
RASTER_API_URL
# Please use the collection name similar to the one used in STAC collection.
# Name of the collection for OCO-2 GEOS Column CO₂ Concentrations.
= "oco2geos-co2-daygrid-v10r" collection_name
# Fetching the collection from STAC collections using appropriate endpoint.
= requests.get(f"{STAC_API_URL}/collections/{collection_name}").json()
collection collection
Examining the contents of our collection
under the temporal
variable, we see that the data is available from January 2015 to February 2022. By looking at the dashboard:time density
, we can see that these observations are collected daily.
def get_item_count(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(collection_name)
number_of_items = requests.get(f"{STAC_API_URL}/collections/{collection_name}/items?limit={number_of_items}").json()["features"]
items print(f"Found {len(items)} items")
# Examining the first item in the collection
0] items[
Below, we enter minimum and maximum values to provide our upper and lower bounds in rescale_values
.
Exploring Changes in Column-Averaged XCO₂ Concentrations Levels Using the Raster API
In this notebook, we will explore the temporal impacts of CO₂ emissions. We will visualize the outputs on a map using folium.
# To access the year value from each item more easily, this will let us query more explicitly by year and month (e.g., 2020-02)
= {item["properties"]["datetime"]: item for item in items}
items = "xco2" #fossil fuel asset_name
# Fetching the min and max values for a specific item
= {"max":items[list(items.keys())[0]]["assets"][asset_name]["raster:bands"][0]["histogram"]["max"], "min":items[list(items.keys())[0]]["assets"][asset_name]["raster:bands"][0]["histogram"]["min"]} rescale_values
Now, we will pass the item id, collection name, and rescaling_factor
to the Raster API
endpoint. We will do this twice, once for 2022-02-08 and again for 2022-01-27, so that we can visualize each event independently.
= "magma"
color_map = requests.get(
oco2_1 f"{RASTER_API_URL}/stac/tilejson.json?collection={items[list(items.keys())[0]]['collection']}&item={items[list(items.keys())[0]]['id']}"
f"&assets={asset_name}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map}"
f"&rescale={rescale_values['min']},{rescale_values['max']}",
).json() oco2_1
= requests.get(
oco2_2 f"{RASTER_API_URL}/stac/tilejson.json?collection={items[list(items.keys())[1]]['collection']}&item={items[list(items.keys())[1]]['id']}"
f"&assets={asset_name}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map}"
f"&rescale={rescale_values['min']},{rescale_values['max']}",
).json() oco2_2
Visualizing Daily Column-Averaged XCO₂ Concentrations
# Set initial zoom and center of map for XCO₂ Layer
# Centre of map [latitude,longitude]
= folium.plugins.DualMap(location=(34, -118), zoom_start=6)
map_
= TileLayer(
map_layer_2020 =oco2_1["tiles"][0],
tiles="GHG",
attr=0.5,
opacity
)
map_layer_2020.add_to(map_.m1)
= TileLayer(
map_layer_2019 =oco2_2["tiles"][0],
tiles="GHG",
attr=0.5,
opacity
)
map_layer_2019.add_to(map_.m2)
# visualising the map
map_
Calculating Zonal Statistics
To perform zonal statistics, first we need to create a polygon. In this use case we are creating a polygon in Texas (USA).
# Texas, USA
= {
texas_aoi "type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[-95, 29],
[-95, 33],
[-104, 33],
[-104,29],
[-95, 29]
[
]
],"type": "Polygon",
}, }
# We will plug in the coordinates for a location inside the the polygon and a zoom level
= Map(
aoi_map ="OpenStreetMap",
tiles=[
location30,-100
],=6,
zoom_start
)
="Texas, USA").add_to(aoi_map)
folium.GeoJson(texas_aoi, name aoi_map
# Check total number of items available
= requests.get(
items f"{STAC_API_URL}/collections/{collection_name}/items?limit=600"
"features"]
).json()[print(f"Found {len(items)} items")
# Explore the first item
0] 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
).json()print(result)
return {
**result["properties"],
"datetime": item["properties"]["datetime"],
}
for item in items:
print(item["properties"]["datetime"])
break
With the function above we can generate the statistics for the AOI.
%%time
= [generate_stats(item, texas_aoi) for item in items] stats
0] stats[
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["datetime"])
df[return df
= clean_stats(stats)
df 5) df.head(
Visualizing the Data as a Time Series
We can now explore the XCO₂ concentrations time series (January 1, 2015 - February 28, 2022) available for the Dallas, Texas area of the U.S. We can plot the data set using the code below:
= plt.figure(figsize=(20, 10))
fig
plt.plot("datetime"],
df["max"],
df[="red",
color="-",
linestyle=0.5,
linewidth="CO₂ concentrations",
label
)
plt.legend()"Years")
plt.xlabel("CO2 concentrations ppm")
plt.ylabel("CO₂ concentrations Values for Texas, Dallas (Jan 2015- Feb 2022)") plt.title(
print(items[2]["properties"]["datetime"])
= requests.get(
oco2_3 f"{RASTER_API_URL}/stac/tilejson.json?collection={items[2]['collection']}&item={items[2]['id']}"
f"&assets={asset_name}"
f"&color_formula=gamma+r+1.05&colormap_name={color_map}"
f"&rescale={rescale_values['min']},{rescale_values['max']}",
).json() oco2_3
# Use bbox initial zoom and map
# Set up a map located w/in event bounds
= Map(
aoi_map_bbox ="OpenStreetMap",
tiles=[
location30,-100
],=6.8,
zoom_start
)
= TileLayer(
map_layer =oco2_3["tiles"][0],
tiles="GHG", opacity = 0.7
attr
)
map_layer.add_to(aoi_map_bbox)
aoi_map_bbox
Summary
In this notebook, we have successfully explored, analyzed, and visualized the STAC collection for OCO-2 GEOS Column CO₂ Concentrations.
- Install and import the necessary libraries
- Fetch the collection from STAC collections using the appropriate endpoints
- Count the number of existing granules within the collection
- Map and compare the Column-Averaged XCO₂ Concentrations Levels for two distinctive months/years
- Generate zonal statistics for the area of interest (AOI)
- Visualizing the Data as a Time Series
If you have any questions regarding this user notebook, please contact us using the feedback form.