Last active
December 31, 2024 15:52
-
-
Save akhuff157/ab8daeadf316feeb8c92062990d1adc5 to your computer and use it in GitHub Desktop.
agu_python_workshop_2024.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "view-in-github", | |
| "colab_type": "text" | |
| }, | |
| "source": [ | |
| "<a href=\"https://colab.research.google.com/gist/akhuff157/ab8daeadf316feeb8c92062990d1adc5/agu_python_workshop_2024.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Snrb6KjKajti" | |
| }, | |
| "source": [ | |
| "# **Python for Satellite Remote Sensing: Analysis and Visualization for Earth Scientists**\n", | |
| "\n", | |
| "Authors: Dr. Rebekah Esmaili (rebekah.esmaili@gmail.com) and Dr. Amy Huff (amy.huff@noaa.gov)\n", | |
| "\n", | |
| "This tutorial was written in December 2024 for the AGU24 Fall Meeting \"Pre-Conference Workshop\" PREWS3.\n", | |
| "\n", | |
| "<font color='red'>**If you use any of the Python code in Sections 1 or 4 in your research, please credit the NOAA/NESDIS/STAR Aerosols & Atmospheric Composition Science Team.**</font>\n", | |
| "\n", | |
| "---\n", | |
| "\n", | |
| "Topics covered in **Section 1 (Search for & Download Satellite Files)**:\n", | |
| "- Access online NOAA and NASA data archives\n", | |
| "- Search for available files for a given satellite/sensor/product & date/time\n", | |
| " - NASA Earthdata:\n", | |
| " - [TEMPO Level 3 (gridded) Nitrogen Dioxide (NO2)](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/images/2024/tempo_tropospheric_no2_20241113_scan009_1822.png)\n", | |
| " - [GPM IMERG rain rate](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/images/2024/gpm_imerg_20240401.png)\n", | |
| " - NOAA Open Data Dissemination (NODD) on AWS:\n", | |
| " - [AVHRR Optimum Interpolation Sea Surface Temperature (OISST)](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/images/2024/avhrr_oisst_2020926.png)\n", | |
| " - [NOAA-20 VIIRS Level 3 (gridded) Aerosol Optical Depth (AOD)](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/images/2024/noaa20_viirs_aod_gridded_20200821.png)\n", | |
| "- Select & download satellite files\n", | |
| " - [Link to zip file with data files](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/documents/2024a/agu2024_python_workshop_data_files.zip) (back-up only, in case you encounter trouble with downloading files in Section 1)\n", | |
| "\n", | |
| "Topics covered in **Section 2 (Open & Understand Satellite Files)**:\n", | |
| "- Understand satellite data filenames\n", | |
| "- Open satellite files that are/are not organized using \"groups\"\n", | |
| "- Identify data variables and read their metadata\n", | |
| "- Extract data variables\n", | |
| "- Process satellite data variables using quality/diagnostic flags\n", | |
| "\n", | |
| "Topics covered in **Section 3 (Simple Plots of Satellite Data)**:\n", | |
| "- Check satellite variable & coordinate dimensions and ensure they are consistent\n", | |
| "- Make a simple plot of satellite data\n", | |
| "\n", | |
| "Topics covered in **Section 4 (Plotting Satellite Data on a Map)**:\n", | |
| "- Set up a figure with a map projection\n", | |
| "- Set the geographic domain of the map\n", | |
| "- Change the appearance (colors) of borderlines, land & water polygons\n", | |
| "- Add latitude/longitude gridlines & labels\n", | |
| "- Pull information out of the data file name and use it to automatically make a plot title & name for saved image file\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **More Information on Satellite Data Terminology**\n", | |
| "\n", | |
| "In this tutorial, we will be working with Level 3 (L3 or gridded) satellite data, and we refer to Level 2 (L2 or granule) satellite data. In addition, the VIIRS gridded AOD data file we will use is generated from reprocessed L2 data, while the other products are generated from operational L2 data. And the TEMPO NO2 data are beta-maturity, while the other products are full/validated maturity.\n", | |
| "\n", | |
| "If you aren't familiar with these terms, the NOAA Aerosols and Atmospheric Composition Science Team's [satellite training website](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/index.php) has definitions and examples of:\n", | |
| "- [Level 1b, Level 2, and Level 3 satellite data processing levels](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/satellite_data_processing_levels.php)\n", | |
| "- [Operational vs. reprocessed data latency](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/satellite_data_operational_reprocessed.php)\n", | |
| "- [Beta, Provisional, and Full/Validated product maturity levels](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/satellite_data_maturity_levels.php)" | |
| ], | |
| "metadata": { | |
| "id": "BtToSuEqU_QJ" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **Read the Satellite Data Documentation!**\n", | |
| "\n", | |
| "Before working with a satellite product, users should read the product documentation from the science team, such as the users' guide, \"readme\", or algorithm theoretical basis document (ATBD). These documents provide important information about satellite products, including valid data ranges, data screening using data quality/diagnostic flags, and known issues.\n", | |
| "\n", | |
| "Here are links to documentation for the satellite products used in today's tutorial:\n", | |
| "\n", | |
| "- [TEMPO Trace Gas and Cloud Level 2 and Level 3 Data Products: User's Guide](https://asdc.larc.nasa.gov/documents/tempo/guide/TEMPO_Level-2-3_trace_gas_clouds_user_guide_V1.1.pdf) (September 18, 2024)\n", | |
| "-[GPM IMERG V07 Release Notes](https://gpm.nasa.gov/sites/default/files/2024-02/IMERG_V07_ReleaseNotes_240221.pdf) (February 21, 2023)\n", | |
| "-[VIIRS Aerosol Optical Depth EDR (granules) User's Guide](https://www.star.nesdis.noaa.gov/atmospheric-composition-training/documents/VIIRS_AOD_Users_Guide.pdf) (February 2020)\n", | |
| " - VIIRS gridded AOD files are generated by gridding high quality AOD EDR (granules) data on a regular 0.25° x 0.25° equal-angle grid (~28 km x 28 km at the equator), for global coverage\n", | |
| "- [OISST ATBD](https://www.ncei.noaa.gov/pub/data/sds/cdr/CDRs/Sea_Surface_Temperature_Optimum_Interpolation/AlgorithmDescription_01B-09.pdf) (September 13, 2013)\n" | |
| ], | |
| "metadata": { | |
| "id": "MggXxnrWgyTt" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **Section 0: Set up Google Colab**" | |
| ], | |
| "metadata": { | |
| "id": "x_amqa-K5S87" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "[Google Colab](https://colab.google/) is a hosted Jupyter Notebook service that requires no setup to use and provides free access to computing resources.\n", | |
| "\n", | |
| "[Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/notebook.html) is an open-source web application that supports >40 programming languages, including Python. It allows code to be broken into “blocks” that run independently, which makes it ideal for learning. Any output from the code in a \"block\" will appear underneath it.\n", | |
| "\n", | |
| "**The Python code demonstrated in this training is universal**. Specific lines of code or functions will run in any Python IDE (e.g., Spyder, Visual Studio Code), Jupyter Notebook, or the Python interpreter." | |
| ], | |
| "metadata": { | |
| "id": "xWSzp7qCARPl" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "###**Example of how to run Jupyter Notebook code blocks**\n", | |
| "\n", | |
| "To see how Jupyter Notebook works, let's run the Python code to print \"Hello world!\"\n", | |
| "\n", | |
| "Place your cursor over the grey code block below, then click the little black circle with the white arrow inside, located on the far left side of the block." | |
| ], | |
| "metadata": { | |
| "id": "8RQO-i-i4TM2" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "print('Hello world!')" | |
| ], | |
| "metadata": { | |
| "id": "kKCQc4nl2vuN" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **Limitations of using Google Colab**\n", | |
| "\n", | |
| "Colab is free, powerful, and easy to use, but it has limitations:\n", | |
| "\n", | |
| "\n", | |
| "1. **Colab sessions are temporary.** A session will expire after 12 hours of continuous use or after 90 minutes of idle time. <font color='red'>**All output, including downloaded or generated files, will be lost after the session expires.**</font> Therefore, any files users want to save must be downloaded to the user's local computer or Google Drive account.\n", | |
| "2. **Colab cannot be configured to use a virtual or conda environment.** Therefore, in general, users must work with the existing, current Colab configuration.\n", | |
| "3. **The Colab configuration changes frequently, with the addition of new packages and updates to existing packages.** So code that runs today may give an error in the future after an update to the Colab configuration." | |
| ], | |
| "metadata": { | |
| "id": "NnfX517t1yxB" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "d3404f3a" | |
| }, | |
| "source": [ | |
| "### **Import Python modules and packages**\n", | |
| "\n", | |
| "- [pathlib](https://docs.python.org/3/library/pathlib.html): module to set file system paths\n", | |
| "- [datetime](https://docs.python.org/3/library/datetime.html): module to manipulate dates and times\n", | |
| "- [S3Fs](https://s3fs.readthedocs.io/en/latest/): library to set up a file system interface with AWS Simple Storage Service (S3) buckets\n", | |
| "- [earthaccess](https://earthaccess.readthedocs.io/en/stable/): library to search for, download, or stream NASA Earth science data\n", | |
| "- [Xarray](https://docs.xarray.dev/en/stable/index.html): library to work with labeled multi-dimensional arrays\n", | |
| "- [NumPy](https://numpy.org/doc/stable/user/index.html): library to perform array operations\n", | |
| "- [Matplotlib](https://matplotlib.org/stable/index.html): library to make plots\n", | |
| "- [cartopy](https://scitools.org.uk/cartopy/docs/latest/index.html): library to make maps\n", | |
| "\n", | |
| "Used by `xarray` but not imported:\n", | |
| "- [netCDF4](https://unidata.github.io/netcdf4-python/): library to read and write netCDF files\n", | |
| "\n", | |
| "---\n", | |
| "---\n", | |
| "The Colab configuration does not include the `earthaccess`, `s3fs`, `netcdf4`, or `cartopy` packages, so they need to be installed.\n", | |
| "\n", | |
| "**Ignore any error messages about package dependency conflicts; they will not impact the training.**" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Install missing packages in Colab quietly (no progress notifications)\n", | |
| "!pip install --quiet earthaccess s3fs netcdf4 cartopy" | |
| ], | |
| "metadata": { | |
| "id": "RWkGnKW1cGIy" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Import modules and packages\n", | |
| "\n", | |
| "from pathlib import Path\n", | |
| "\n", | |
| "import datetime\n", | |
| "from datetime import date\n", | |
| "\n", | |
| "import earthaccess\n", | |
| "\n", | |
| "import s3fs\n", | |
| "\n", | |
| "import xarray as xr\n", | |
| "\n", | |
| "import numpy as np\n", | |
| "\n", | |
| "import matplotlib as mpl\n", | |
| "from matplotlib import pyplot as plt\n", | |
| "import matplotlib.ticker as ticker\n", | |
| "\n", | |
| "import cartopy.feature as cfeature\n", | |
| "from cartopy import crs as ccrs" | |
| ], | |
| "metadata": { | |
| "id": "FvJF3LSTdVbh" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **Save a copy of today's Colab Python configuration**\n", | |
| "\n", | |
| "Google updates the Colab package configuration on a monthly basis. If you plan to run today's notebook again in the future, or to use any of the provided code in a script or notebook of your own, you will want to have a record of the packages & their versions used today.\n", | |
| "\n", | |
| "You can install a [Conda environment](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html) on your local computer with the key packages and their versions to run Python code with the same configuration as today's Colab.\n", | |
| "\n", | |
| "Run the blocks below to:\n", | |
| "\n", | |
| "1. Print the current version of Python installed in Colab & save it as a text file (`colab_version.txt`)\n", | |
| "2. Print the list of packages and their versions in the current Colab configuration & save them in a text file (`colab_packages.txt`)\n" | |
| ], | |
| "metadata": { | |
| "id": "S3UG28bVjAA-" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "3iXLZ493VOij" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Find version of Python installed in Colab\n", | |
| "!python --version" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Save installed version of Python to a text file\n", | |
| "!python --version > colab_version.txt" | |
| ], | |
| "metadata": { | |
| "id": "6gqPdgPXXIEO" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# List all packages & their versions in the Colab configuration\n", | |
| "!pip list -v" | |
| ], | |
| "metadata": { | |
| "id": "AbfYeIs_WzYr" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Save list of all installed packages & their versions to a text file\n", | |
| "!pip freeze > colab_packages.txt" | |
| ], | |
| "metadata": { | |
| "id": "RzBNBBxXTLOr" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **Section 1: Search for & Download Satellite Files**" | |
| ], | |
| "metadata": { | |
| "id": "nf6-3lhb6jTY" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **1.1 NASA Earthdata: TEMPO and GPM IMERG Files**" | |
| ], | |
| "metadata": { | |
| "id": "KMkENZN9mz0O" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "####**1.1.1 Connect to Earthdata Login**\n", | |
| "\n", | |
| "The data archive for TEMPO and GPM IMERG files is [NASA Earthdata](https://search.earthdata.nasa.gov/).\n", | |
| "\n", | |
| "We can connect to [NASA Earthdata Login](https://urs.earthdata.nasa.gov/) using the `earthaccess` package. In this tutorial, we are using the `strategy='interactive'` option so you can manually enter your username and password for your Earthdata Login.\n", | |
| "\n", | |
| "If you don't already have an account with Earthdata, register [here](https://urs.earthdata.nasa.gov/users/new) (it's free!).\n", | |
| "\n", | |
| "Useful `earthaccess` functions covered in this tutorial include:\n", | |
| "- [earthaccess.login()](https://earthaccess.readthedocs.io/en/stable/user-reference/api/api/#earthaccess.api.login): authenticates with Earthdata Login credentials\n", | |
| "- [earthaccess.search_data()](https://earthaccess.readthedocs.io/en/stable/user-reference/api/api/#earthaccess.api.search_data): queries for dataset granules on NASA Earthdata\n", | |
| "- [DataGranule.data_links()](https://earthaccess.readthedocs.io/en/stable/user-reference/granules/granules/#earthaccess.results.DataGranule.data_links): returns the URL link to a granule file\n", | |
| "- [earthaccess.download()](https://earthaccess.readthedocs.io/en/stable/user-reference/api/api/#earthaccess.api.download): downloads granules files to local directory" | |
| ], | |
| "metadata": { | |
| "id": "UkAhHni0h35z" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Connect to NASA Earthdata Login with user credentials\n", | |
| "auth = earthaccess.login(strategy='interactive')" | |
| ], | |
| "metadata": { | |
| "id": "rln4q4_ncQt4" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.1.2 Search NASA Earthdata for TEMPO NO2 Level 3 (gridded) files**\n", | |
| "\n", | |
| "The `earthaccess.search_data()` function has a number of arguments that can be defined to query Earthdata, including `short_name`, `temporal`, `bounding_box`, and `granule_name`. You can use some or all of these parameters to search for satellite data files.\n", | |
| "\n", | |
| "Let's use these search parameters to find the available TEMPO NO2 L3 files spanning the full TEMPO field of regard (FOR) for November 13, 2024. The FOR extends from Mexico City and the Yucatan Peninsula to the Canadian oil sands in the north-south direction, and from the Atlantic Ocean to the Pacific Ocean in the east-west direction. TEMPO scans its FOR from east to west; a full scan takes about 1 hour, with shorter scan times in the morning and evening (~40 minutes)." | |
| ], | |
| "metadata": { | |
| "id": "Oh7t1_VvixKq" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Earthdata search settings\n", | |
| "\n", | |
| "short_name = 'TEMPO_NO2_L3' # Product abbreviation (string)\n", | |
| "observation_start = '2024-11-13 12:00:00' # Observation start date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "observation_end = '2024-11-13 23:59:59' # Observation end date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "domain = (-130, 10, -50, 60) # Geographic bounding box (integers: W_lon, S_lat, E_lon, N_lat)\n", | |
| "scan_wildcard = '*S*' # Wildcard string for scan number (use '*S*' to find all scans)" | |
| ], | |
| "metadata": { | |
| "id": "lCKEtyRQXbU2" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Search NASA Earthdata\n", | |
| "\n", | |
| "results = earthaccess.search_data(short_name=short_name,\n", | |
| " temporal=(observation_start, observation_end),\n", | |
| " bounding_box=domain,\n", | |
| " granule_name=scan_wildcard)" | |
| ], | |
| "metadata": { | |
| "id": "g06qjMmWXZBg" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.1.3 Review NASA Earthdata search results**\n", | |
| "\n", | |
| "Using [list comprehensions](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions) in conjunction with the the `earthaccess` `DataGranule.data_links()` function, we can print the list of satellite file names returned from the NASA Earthdata search.\n", | |
| "\n", | |
| "---\n", | |
| "---\n", | |
| "\n", | |
| "**Pro tip:** When printing directory paths, for simplicity I prefer to print only the final path component using the `str.split` [method](https://docs.python.org/3/library/stdtypes.html#str.split). To see why this is a simpler option, we can also print the full directory path for the first product, for comparison." | |
| ], | |
| "metadata": { | |
| "id": "O_SybN7al5yg" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Print list of available file names\n", | |
| "\n", | |
| "available_files = [(granule.data_links(access=\"external\")) for granule in results]\n", | |
| "for available_file in available_files:\n", | |
| " print(available_file[0].split('/')[-1])" | |
| ], | |
| "metadata": { | |
| "id": "1GxHR-C3Ybwx" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "16fba075" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Print the full directory path for the first product\n", | |
| "# For NASA Earthdata, this is a web link\n", | |
| "\n", | |
| "print(available_files[0])" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.1.4 Modify search settings to find file corresponding to Scan 9**\n", | |
| "\n", | |
| "Our initial search returned 13 files for November 13, 2024, corresponding to TEMPO Scans 2 (`S002`) through 14 (`S014`). \n", | |
| "\n", | |
| "Let's modify the `Earthdata search settings` to search for only Scan 9 (`S009`), and then re-run the code block to `Search NASA Earthdata`." | |
| ], | |
| "metadata": { | |
| "id": "fcLP9smkXuSx" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Earthdata search settings\n", | |
| "\n", | |
| "short_name = 'TEMPO_NO2_L3' # Product abbreviation (string)\n", | |
| "observation_start = '2024-11-13 12:00:00' # Observation start date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "observation_end = '2024-11-13 23:59:59' # Observation end date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "domain = (-130, 10, -50, 60) # Geographic bounding box (integers: W_lon, S_lat, E_lon, N_lat)\n", | |
| "scan_wildcard = '*S009*' # Wildcard string for scan number (use '*S*' to find all scans)" | |
| ], | |
| "metadata": { | |
| "id": "DHDwt4HNWspi" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Search NASA Earthdata\n", | |
| "\n", | |
| "results = earthaccess.search_data(short_name=short_name,\n", | |
| " temporal=(observation_start, observation_end),\n", | |
| " bounding_box=domain,\n", | |
| " granule_name=scan_wildcard)" | |
| ], | |
| "metadata": { | |
| "id": "3VHFb1S9Wspk" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Print list of available file names\n", | |
| "# Should be only one file, corresponding to Scan 9 (S009)\n", | |
| "\n", | |
| "available_files = [(granule.data_links(access=\"external\")) for granule in results]\n", | |
| "for available_file in available_files:\n", | |
| " print(available_file[0].split('/')[-1])" | |
| ], | |
| "metadata": { | |
| "id": "lscCU8z--Y9Y" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.1.5 Download the TEMPO NO2 file for Scan 9 on November 13, 2024**\n", | |
| "\n", | |
| "Use the `earthaccess.download()` function to download the one TEMPO NO2 L3 (gridded) data file. Downloaded files are saved to the Colab instance; click on the `Files` icon in the menu panel on the left side of the Colab window.\n", | |
| "\n", | |
| "---\n", | |
| "---\n", | |
| "\n", | |
| "\n", | |
| "**Pro tip:** I recommend using the `pathlib` [module](https://docs.python.org/3/library/pathlib.html) to set directory paths because it has a lot of very handy features, including automatically using the correct format for the user's operating system. This helps avoid errors in situations when more than one person is using the same code file, because Windows uses back slashes in directory paths, while MacOS and Linux use forward slashes.\n", | |
| "\n", | |
| "The `pathlib` syntax to set a directory path is `Path('directory_name')`, for example `Path('C:/Users/Jane.Smith/Desktop')`. To set the current working directory, use `Path.cwd()`." | |
| ], | |
| "metadata": { | |
| "id": "y00x4CCsbdGR" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Download file from NASA Earthdata\n", | |
| "\n", | |
| "earthaccess.download(results, local_path=Path.cwd())" | |
| ], | |
| "metadata": { | |
| "id": "v0wygewEaaBy" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "##### <b><font color=\"blue\" size=\"5\">Exercise 1-1: Search for GPM IMERG rain rate files for April 1, 2024 & download file for the 12:00 UTC scan</b></font>\n", | |
| "\n", | |
| "Fill in the code blocks below, following the same procedure used for the TEMPO NO2 data:\n", | |
| "\n", | |
| "1. Change the `Earthdata search settings` to search for all of the available IMERG rain rate files for April 1, 2024, as follows:\n", | |
| "\n", | |
| "```\n", | |
| "short_name = 'GPM_3IMERGHH' # Product abbreviation (string)\n", | |
| "observation_start = '2024-04-01 00:00:00' # Observation start date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "observation_end = '2024-04-01 23:59:59' # Observation end date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "domain = (180, -90, -180, 90) # Geographic bounding box (integers: W_lon, S_lat, E_lon, N_lat)\n", | |
| "scan_wildcard = '*S*' # # Wildcard string for scan number (use '*S*' to find all scans)\n", | |
| "```\n", | |
| "\n", | |
| "2. Run the code block to `Search NASA Earthdata`.\n", | |
| " - Your search should return 48 files.\n", | |
| " - Check the list of file names with the answers (below).\n", | |
| "\n", | |
| "3. Modify the `Earthdata search settings` to search for **only the 12:00 UTC scan** & re-run the the code block to `Search NASA Earthdata`.\n", | |
| " - Your search should return 1 file.\n", | |
| " - Check the file name with the answers (below).\n", | |
| "\n", | |
| "4. Run the code block to `Download file from NASA Earthdata`.\n", | |
| " - **You should download 1 file.**\n", | |
| " - Check that the downloaded file appears in the `Files` menu panel.\n" | |
| ], | |
| "metadata": { | |
| "id": "yQYcjXYfnn-0" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Earthdata search settings\n", | |
| "\n", | |
| "short_name = '' # Product abbreviation (string)\n", | |
| "observation_start = 'YYYY-MM-DD 12:00:00' # Observation start date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "observation_end = 'YYYY-MM-DD 23:59:59' # Observation end date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "domain = () # Geographic bounding box (integers: W_lon, S_lat, E_lon, N_lat)\n", | |
| "scan_wildcard = '*S*' # Wildcard string for scan number (use '*S*' to find all scans)" | |
| ], | |
| "metadata": { | |
| "id": "bNH3D-FjW2Xn" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Search NASA Earthdata\n", | |
| "\n", | |
| "results =" | |
| ], | |
| "metadata": { | |
| "id": "Dfm4c6OJW2Xo" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Print list of available file names\n", | |
| "\n" | |
| ], | |
| "metadata": { | |
| "id": "sZ3hSaI6XSj-" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Download file from NASA Earthdata for 12:00 UTC scan on April 1, 2024\n", | |
| "\n" | |
| ], | |
| "metadata": { | |
| "id": "uzZ_HAzaXjJv" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "<details><summary><b><font color=\"blue\" size=5>Answers to Exercise 1-1</font></b></summary>\n", | |
| "<p></p>\n", | |
| "\n", | |
| "```\n", | |
| "# Earthdata search settings for all files on April 1, 2024\n", | |
| "\n", | |
| "short_name = 'GPM_3IMERGHH' # Product abbreviation (string)\n", | |
| "observation_start = '2024-04-01 00:00:00' # Observation start date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "observation_end = '2024-04-01 23:59:59' # Observation end date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "domain = (180, -90, -180, 90) # Geographic bounding box (integers: W_lon, S_lat, E_lon, N_lat)\n", | |
| "scan_wildcard = '*S*' # # Wildcard string for scan number (use '*S*' to find all scans)\n", | |
| "```\n", | |
| "\n", | |
| "```\n", | |
| "# Search NASA Earthdata\n", | |
| "\n", | |
| "results = earthaccess.search_data(short_name=short_name,\n", | |
| " temporal=(observation_start, observation_end),\n", | |
| " bounding_box=domain,\n", | |
| " granule_name=scan_wildcard)\n", | |
| "```\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "\n", | |
| "---\n", | |
| "\n", | |
| "**List of all of the available IMERG rain rate files for April 1, 2024**\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S000000-E002959.0000.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S003000-E005959.0030.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S010000-E012959.0060.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S013000-E015959.0090.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S020000-E022959.0120.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S023000-E025959.0150.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S030000-E032959.0180.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S033000-E035959.0210.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S040000-E042959.0240.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S043000-E045959.0270.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S050000-E052959.0300.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S053000-E055959.0330.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S060000-E062959.0360.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S063000-E065959.0390.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S070000-E072959.0420.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S073000-E075959.0450.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S080000-E082959.0480.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S083000-E085959.0510.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S090000-E092959.0540.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S093000-E095959.0570.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S100000-E102959.0600.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S103000-E105959.0630.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S110000-E112959.0660.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S113000-E115959.0690.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S120000-E122959.0720.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S123000-E125959.0750.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S130000-E132959.0780.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S133000-E135959.0810.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S140000-E142959.0840.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S143000-E145959.0870.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S150000-E152959.0900.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S153000-E155959.0930.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S160000-E162959.0960.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S163000-E165959.0990.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S170000-E172959.1020.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S173000-E175959.1050.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S180000-E182959.1080.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S183000-E185959.1110.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S190000-E192959.1140.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S193000-E195959.1170.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S200000-E202959.1200.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S203000-E205959.1230.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S210000-E212959.1260.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S213000-E215959.1290.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S220000-E222959.1320.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S223000-E225959.1350.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S230000-E232959.1380.V07B.HDF5\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S233000-E235959.1410.V07B.HDF5\n", | |
| "\n", | |
| "```\n", | |
| "# Modified Earthdata search settings for the 12:00 UTC scan\n", | |
| "\n", | |
| "short_name = 'GPM_3IMERGHH' # Product abbreviation (string)\n", | |
| "observation_start = '2024-04-01 00:00:00' # Observation start date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "observation_end = '2024-04-01 23:59:59' # Observation end date/time (string): 'YYYY-MM-DD HH:MM:SS'\n", | |
| "domain = (180, -90, -180, 90) # Geographic bounding box (integers: W_lon, S_lat, E_lon, N_lat)\n", | |
| "scan_wildcard = '*S1200*' # # Wildcard string for scan number (use '*S*' to find all scans)\n", | |
| "```\n", | |
| "\n", | |
| "---\n", | |
| "\n", | |
| "**IMERG file for the 12:00 UTC scan on April 1, 2024**\n", | |
| "\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S120000-E122959.0720.V07B.HDF5\n", | |
| "\n", | |
| "---\n" | |
| ], | |
| "metadata": { | |
| "id": "6AyYSX39mU9v" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **1.2 NOAA Open Data Dissemination (NODD): OISST and VIIRS AOD Files**" | |
| ], | |
| "metadata": { | |
| "id": "zg7BsB4XnDl5" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.2.1 Connect to NODD on Amazon Web Services (AWS)**\n", | |
| "The [NODD Program](https://www.noaa.gov/information-technology/open-data-dissemination) provides public access to NOAA's open data via commercial cloud platforms.\n", | |
| "\n", | |
| "The NODD platform for VIIRS gridded AOD and AVHRR OISST files is Amazon Web Services (AWS). We can connect to AWS using the `s3fs` package with an anonymous connection (`annon=True`). Files on AWS are stored in directories called Simple Storage Service (S3) buckets. The `s3fs` package allows users to access AWS S3 buckets as if they are file system (`fs`) directories.\n", | |
| "\n", | |
| "Useful `s3fs` functions covered in this training include:\n", | |
| "- [fs.get()](https://s3fs.readthedocs.io/en/latest/api.html#s3fs.core.S3FileSystem.get): downloads files to local directory\n", | |
| "- [fs.ls()](https://s3fs.readthedocs.io/en/latest/api.html#s3fs.core.S3FileSystem.ls): lists file paths (directories & files) at source\n", | |
| "- [fs.size()](https://s3fs.readthedocs.io/en/latest/api.html#s3fs.core.S3FileSystem.size): returns the approximate size (in bytes) of the file\n", | |
| "\n", | |
| "**You do not need an AWS cloud computing account to access NOAA data!** Think of the NODD on AWS as a data archive that just happens to be in the cloud instead of hosted on a physical server." | |
| ], | |
| "metadata": { | |
| "id": "VvjEmgss8anY" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Connect to AWS S3 anonymously\n", | |
| "fs = s3fs.S3FileSystem(anon=True)" | |
| ], | |
| "metadata": { | |
| "id": "-gsEKs-goIRJ" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.2.2 Navigating the NODD S3 buckets on AWS**\n", | |
| "\n", | |
| "Each NOAA program has its own S3 buckets for NODD data. The NODD also has a web interface, which allows users to easily see the organizational structure for the various S3 buckets. Data files can be downloaded manually via the web interface.\n", | |
| "\n", | |
| "The NOAA Oceanic Climate Data Records (CDRs) program includes the AVHRR OISST data:\n", | |
| "- [NODD on AWS Oceanic CDRs homepage](https://registry.opendata.aws/noaa-cdr-oceanic/)\n", | |
| "- OISST S3 bucket name: `noaa-cdr-sea-surface-temp-optimum-interpolation-pds`\n", | |
| "- OISST S3 bucket [web interface](https://noaa-cdr-sea-surface-temp-optimum-interpolation-pds.s3.amazonaws.com/index.html)\n", | |
| "\n", | |
| "The NOAA Joint Polar Satellite Series (JPSS) program includes the VIIRS gridded AOD data:\n", | |
| "- [NODD on AWS JPSS homepage](https://registry.opendata.aws/noaa-jpss/)\n", | |
| "- JPSS Development Data S3 bucket name: `noaa-jpss`\n", | |
| "- JPSS Development Data S3 bucket [web interface](https://noaa-jpss.s3.amazonaws.com/index.html)\n", | |
| "\n", | |
| "\n", | |
| "---\n", | |
| "\n", | |
| "Find a specific data file on the NODD by setting the full S3 bucket directory path to the file; for example:\n", | |
| "\n", | |
| "`noaa-cdr-sea-surface-temp-optimum-interpolation-pds/data/v2.1/avhrr/202409/oisst-avhrr-v02r01.20240926.nc`\n", | |
| "\n", | |
| "`noaa-jpss/NOAA20/VIIRS/NOAA20_VIIRS_Aerosol_Optical_Depth_Gridded_Reprocessed/0.25_Degrees_Daily/2020/viirs_eps_noaa20_aod_0.250_deg_20200821.nc`" | |
| ], | |
| "metadata": { | |
| "id": "MbzrzIKnTjvB" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "7acd94bd" | |
| }, | |
| "source": [ | |
| "#### **1.2.3 Browse the AVHRR OISST S3 bucket**\n", | |
| "\n", | |
| "To find the full S3 bucket directory paths to OISST files, we start by browsing the top organizational level of the AVHRR OISST S3 bucket; these are subdirectories labeled with the 6-digit year and month of the observations, in the format `YYYYMM`.\n", | |
| "\n", | |
| "Use the `fs.ls()` function to find the available product subdirectory year-month paths in the OISST S3 bucket." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Find & print the subdirectory paths in the S3 bucket\n", | |
| "\n", | |
| "year_months = fs.ls('noaa-cdr-sea-surface-temp-optimum-interpolation-pds/data/v2.1/avhrr/')\n", | |
| "\n", | |
| "for year_month in year_months:\n", | |
| " print(year_month.split('/')[-1])" | |
| ], | |
| "metadata": { | |
| "id": "r-KDC3ki34-j" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "e15a0b14" | |
| }, | |
| "source": [ | |
| "#### **1.2.4 Find all of the OISST files for September 2024**\n", | |
| "\n", | |
| "Let's define a `data_path` variable to set the directory path for AVHRR OISST files for September 2024, and then use the `fs.ls()` function to list the full path names for the individual data files.\n", | |
| "\n", | |
| "**Coding Note:** The `fs.ls()` function takes the source directory path argument `data_path` as a string, which is why the `year` and `month` variables, entered as integers, are converted to strings. The Python `str.zfill(width)` [method](https://docs.python.org/3/library/stdtypes.html#str.zfill) ensures the `month` string in the `data_path` is 2 digits; `str.zfill(width)` returns a copy of the string left-filled with ASCII '0' digits to make a string of length `width`. This way, the `data_path` syntax is correct for `month` variable integers <10." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Find all the OISST files for September 2024\n", | |
| "# Print total number of files in directory & first 10 file names\n", | |
| "\n", | |
| "bucket = 'noaa-cdr-sea-surface-temp-optimum-interpolation-pds/data/v2.1/avhrr/'\n", | |
| "year = 2024\n", | |
| "month = 9\n", | |
| "\n", | |
| "data_path = (bucket + str(year) + str(month).zfill(2) + '/')\n", | |
| "\n", | |
| "files = fs.ls(data_path)\n", | |
| "\n", | |
| "print('Total number of files:', len(files), '\\n')\n", | |
| "\n", | |
| "for file in files[:10]:\n", | |
| " print(file.split('/')[-1])" | |
| ], | |
| "metadata": { | |
| "id": "lAOGiJ9v34D1" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Print the full directory path for the first data file\n", | |
| "\n", | |
| "files[0]" | |
| ], | |
| "metadata": { | |
| "id": "o3u8gvZE6dQZ" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "78c34997" | |
| }, | |
| "source": [ | |
| "#### **1.2.5 Select the OISST file for September 26, 2024**\n", | |
| "\n", | |
| "Most satellite file names contain information about the observation date and, if relevant, observation times. You will learn more about filename conventions in Section 2.\n", | |
| "\n", | |
| "Using Python [slicing](https://stackoverflow.com/questions/509211/how-slicing-in-python-works) and [list comprehension](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions), we can select OISST file paths on the S3 bucket using the observation date in `YYYYMMDD` format in the file names.\n", | |
| "\n", | |
| "Let's select the file corresponding to September 26.\n", | |
| "\n", | |
| "---\n", | |
| "---\n", | |
| "\n", | |
| "**Pro tip**: Before downloading, I recommend printing the selected satellite file names to confirm they are the files you want, and checking the approximate size of each file using the `fs.size()` function.\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Select the file for September 26, 2024\n", | |
| "\n", | |
| "matches = [file for file in files if (file.split('/')[-1].split('.')[1][6:9] == '26')]\n", | |
| "\n", | |
| "# Print file name and approximate file size\n", | |
| "for match in matches:\n", | |
| " print(match.split('/')[-1])\n", | |
| " print('Approximate file size (MB):', round((fs.size(match)/1.0E6), 2))" | |
| ], | |
| "metadata": { | |
| "id": "x6fp4N096lPs" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.2.6 Download the selected OISST file**\n", | |
| "\n", | |
| "Use the `fs.get()` function to download the one OISST file corresponding to the selected file directory path (`matches`). Downloaded files are saved to the Colab instance; click on the `Files` icon in the menu panel on the left side of the Colab window.\n", | |
| "\n", | |
| "**Coding Notes:** The `pathlib` module uses `/` to join the directory path with the file name to get the full path for the file, e.g., `Path.cwd() / match.split('/')[-1]`. The `fs.get()` [function](https://s3fs.readthedocs.io/en/latest/api.html#s3fs.core.S3FileSystem.get) takes the downloaded file directory argument as a string, so the `pathlib` `PurePath.as_posix()` [method](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.as_posix) is used to return a string representation of the full path (with forward slashes) for the downloaded file." | |
| ], | |
| "metadata": { | |
| "id": "B3hFIcqJu1O7" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Download file from AWS NODD\n", | |
| "\n", | |
| "for match in matches:\n", | |
| " fs.get(match, (Path.cwd() / match.split('/')[-1]).as_posix())" | |
| ], | |
| "metadata": { | |
| "id": "P6sM2McG8lM-" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "##### <b><font color=\"blue\" size=\"5\">Exercise 1-2: Search for NOAA-20 VIIRS gridded AOD files (daily at 0.250° resolution) for 2020</b></font>\n", | |
| "\n", | |
| "\n", | |
| "1. Fill in the missing code in the block below, to find all of the VIIRS gridded AOD files (daily at 0.250° resolution) for 2020.\n", | |
| "\n", | |
| "2. Run the code block to print the total number of files in the directory and the first 10 file names.\n", | |
| " - Your search should return 366 files.\n", | |
| " - Check the list of the first 10 file names with the answers (below).\n", | |
| "\n", | |
| "\n" | |
| ], | |
| "metadata": { | |
| "id": "oavqh-AVh4-g" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Find all the NOAA-20 VIIRS gridded AOD files (daily at 0.250° resolution) for 2020\n", | |
| "# Print total number of files in directory & first 10 file names\n", | |
| "\n", | |
| "bucket = 'noaa-jpss/NOAA20/VIIRS/NOAA20_VIIRS_Aerosol_Optical_Depth_Gridded_Reprocessed/'\n", | |
| "resolution = '0.25_Degrees_Daily/'\n", | |
| "year =\n", | |
| "\n", | |
| "data_path = ()\n", | |
| "\n", | |
| "files = fs.ls()\n", | |
| "\n", | |
| "print('Total number of files:', len(files), '\\n')\n", | |
| "\n", | |
| "for\n" | |
| ], | |
| "metadata": { | |
| "id": "pY7YHeTPhkF3" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "<details><summary><b><font color=\"blue\" size=5>Answers to Exercise 1-2</font></b></summary>\n", | |
| "<p></p>\n", | |
| "\n", | |
| "```\n", | |
| "# Find all the VIIRS gridded AOD files (daily at 0.250° resolution) for 2020\n", | |
| "# Print total number of files in directory & first 10 file names\n", | |
| "\n", | |
| "bucket = 'noaa-jpss/NOAA20/VIIRS/NOAA20_VIIRS_Aerosol_Optical_Depth_Gridded_Reprocessed/'\n", | |
| "resolution = '0.25_Degrees_Daily/'\n", | |
| "year = 2020\n", | |
| "\n", | |
| "data_path = (bucket + resolution + str(year) + '/')\n", | |
| "\n", | |
| "files = fs.ls(data_path)\n", | |
| "\n", | |
| "print('Total number of files:', len(files), '\\n')\n", | |
| "\n", | |
| "for file in files[:10]:\n", | |
| " print(file.split('/')[-1])\n", | |
| "```\n", | |
| "---\n", | |
| "\n", | |
| "Total number of files: 366\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200101.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200102.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200103.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200104.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200105.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200106.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200107.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200108.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200109.nc\n", | |
| "\n", | |
| "viirs_eps_noaa20_aod_0.250_deg_20200110.nc\n" | |
| ], | |
| "metadata": { | |
| "id": "E2s6b3W3h4-h" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "kUpIyMK6qQrh" | |
| }, | |
| "source": [ | |
| "#### **1.2.7 Select the NOAA-20 VIIRS gridded AOD file for August 21, 2024**\n", | |
| "\n", | |
| "Like we did with the OISST files, we can select the VIIRS gridded AOD file paths on the S3 bucket using the observation date in `YYYYMMDD` format in the file names.\n", | |
| "\n", | |
| "Let's select the file corresponding to August 21, print the file name and the approximate size of the file.\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Select the file for August 21, 2020\n", | |
| "\n", | |
| "matches = [file for file in files if (file.split('/')[-1].split('_')[6][4:8] == '0821')]\n", | |
| "\n", | |
| "# Print file name and approximate file size\n", | |
| "for match in matches:\n", | |
| " print(match.split('/')[-1])\n", | |
| " print('Approximate file size (MB):', round((fs.size(match)/1.0E6), 2))" | |
| ], | |
| "metadata": { | |
| "id": "CGVs-mprqQri" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **1.2.8 Download the selected AOD file**\n", | |
| "\n", | |
| "Like we did with the OISST file, use the `fs.get()` function to download the one AOD file corresponding to the selected file directory path (`matches`). Downloaded files are saved to the Colab instance; click on the `Files` icon in the menu panel on the left side of the Colab window." | |
| ], | |
| "metadata": { | |
| "id": "qA-PcMMyqQri" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Download file from AWS NODD\n", | |
| "\n", | |
| "for match in matches:\n", | |
| " fs.get(match, (Path.cwd() / match.split('/')[-1]).as_posix())" | |
| ], | |
| "metadata": { | |
| "id": "uoEi8OJIqQri" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **Section 2: Open & Understand Satellite Files**" | |
| ], | |
| "metadata": { | |
| "id": "ItJikCNIrTh8" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "WGFW2OPjajti" | |
| }, | |
| "source": [ | |
| "### **2.1 Filenames Conventions**\n", | |
| "\n", | |
| "NetCDF and HDF are [self-describing formats](https://ntrs.nasa.gov/api/citations/20120008476/downloads/20120008476.pdf), which are structured binary data files. These formats are community standards for Earth Science because they:\n", | |
| "\n", | |
| "1. Are faster to read when dealing with binary-based datasets compared to text formats\n", | |
| "2. Are compact files and thus cost-effective for long-term data storage\n", | |
| "3. Include metadata that provides information about the data source and the available variables\n", | |
| "\n", | |
| "NetCDF files are a type of HDF file, they share many of the same tools and workflows to read, extract, and write.\n", | |
| "\n", | |
| "In Section 1, we downloaded four files that we will work with:\n", | |
| "\n", | |
| "* TEMPO gridded NO2\n", | |
| "* GPM IMERG rain rate\n", | |
| "* VIIRS gridded Aerosol Optical Depth (AOD)\n", | |
| "* AVHRR Optimum Interpolation Sea Surface Temperature (OISST)\n", | |
| "\n", | |
| "Many environmental dataset names are quite long and can vary in structure when produced by different organizations. However, the filenames follow similar conventions and contain useful information about their contents. For instance, the following filename is from the VIIRS Level 2 (granule) AOD product from the NOAA JPSS program:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "" | |
| ], | |
| "metadata": { | |
| "id": "ZUhhl02qAi_4" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "\n", | |
| "```\n", | |
| "JRR-AOD_v2r3_j01_s202009152044026_e202009152045271_c202009152113150.nc\n", | |
| "```\n", | |
| "\n", | |
| "* Prefix indicates the mission (`JRR` for JPSS Risk Reduction)\n", | |
| "* Product (`AOD` for Aerosol Optical Depth)\n", | |
| "* Algorithm version and revision number (`v2r3` for version 2 revision 3)\n", | |
| "* Satellite source (`j01` for JPSS-1/NOAA-20)\n", | |
| "* Start (`s`), end (`e`), and creation (`c`) time, `YYYYMMDDSSS`(seconds are to one decimal place) in UTC\n", | |
| "* The extension `.nc` means that it's a NetCDF file\n", | |
| "\n", | |
| "According to the start and end `HHMM`, file begin at `20:44` and ends at `20:45`, it's roughly a one-minute scan (85 seconds).\n", | |
| "\n", | |
| "Let's explore another file. This file was generated by NASA and has a different filename system, but follows similar conventions:" | |
| ], | |
| "metadata": { | |
| "id": "oBajNTydAjoa" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "" | |
| ], | |
| "metadata": { | |
| "id": "k4mQx9YFAjIi" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "\n", | |
| "```\n", | |
| "3B-HHR.MS.MRG.3IMERG.20240401-S120000-E122959.0720.V07B.HDF5\n", | |
| "```\n", | |
| "\n", | |
| "* Prefix indicates that this is a Level 3B (`3B`) and half-hourly dataset (`HRR`). `MS` is for multi-satellite, `MRG` is multi-instrument\n", | |
| "* `3IMERG` is for Level 3 IMERG, which is the algorithm name\n", | |
| "* The calendar date is in `YYYYMMDD` format (`20240401`)\n", | |
| "* The start time (`S`) is `120000` for 12:00:00 in UTC\n", | |
| "* The end time (`E`) is `122959` for 12:29:59 in UTC\n", | |
| "* The version number is `.V07B` for version 7b\n", | |
| "* The extension `.HDF` indicates an HDF file\n", | |
| "\n", | |
| "You can learn more about the [Precipitation Product File Naming Convention Document](https://gpm.nasa.gov/sites/default/files/2020-02/FileNamingConventionForPrecipitationProductsForGPMMission.pdf)." | |
| ], | |
| "metadata": { | |
| "id": "R-lj-pFEFKxb" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### <b><font color=\"blue\" size=\"5\">Exercise 2-1: Understanding filenames</b></font>\n", | |
| "\n", | |
| "For the file:\n", | |
| "\n", | |
| "```\n", | |
| "oisst-avhrr-v02r01.20240901.nc\n", | |
| "```\n", | |
| "\n", | |
| "1. What does `v02r01` refer to?\n", | |
| "2. What is the algorithm name?\n", | |
| "3. What sensor does this dataset use?\n", | |
| "\n", | |
| "For the file:\n", | |
| "\n", | |
| "```\n", | |
| "TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc\n", | |
| "```\n", | |
| "\n", | |
| "4. What sensor does this dataset use?\n", | |
| "5. What is the format of the date in the filename?\n", | |
| "6. If you don't know the filename structure where can you go to learn more?\n", | |
| "---\n", | |
| "\n", | |
| "Your Answer:\n" | |
| ], | |
| "metadata": { | |
| "id": "dtGUmrxqD4G6" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [], | |
| "metadata": { | |
| "id": "sgu4w9F9IijX" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "<details><summary><b><font color=\"blue\" size=5>Answers to Exercise 2-1</font></b></summary>\n", | |
| "<p></p>\n", | |
| "\n", | |
| "\n", | |
| "```\n", | |
| "oisst-avhrr-v02r01.20240901.nc\n", | |
| "```\n", | |
| "\n", | |
| "1. What does `v02r01` refer to?\n", | |
| "\n", | |
| "<font color=\"blue\">Algorithm version 2, revision 1</font>\n", | |
| "\n", | |
| "2. What is the algorithm name?\n", | |
| "\n", | |
| "<font color=\"blue\">OISST</font>\n", | |
| "\n", | |
| "3. What sensor does this dataset use?\n", | |
| "\n", | |
| "<font color=\"blue\">AVHRR</font>\n", | |
| "\n", | |
| "For the file:\n", | |
| "\n", | |
| "```\n", | |
| "TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc\n", | |
| "```\n", | |
| "\n", | |
| "4. What instrument is this from?\n", | |
| "\n", | |
| "<font color=\"blue\">TEMPO</font>\n", | |
| "\n", | |
| "5. What is the format of the date in the filename?\n", | |
| "\n", | |
| "`YYYYMMDD` which is separated with a `T` followed by `HHMMSS`. The Z indicated \"Zulu\" or UTC. </font>\n", | |
| "\n", | |
| "6. If you don't know the filename structure, how can you go to find out?\n", | |
| "\n", | |
| "<font color=\"blue\">Google! You can find \"Level 2\" and \"Level 3\" product user guides, READMEs, Algorithm Theoretical Basis Documents (ATBDs) at NASA, NOAA, EUMETSAT, JAXA, etc. for technical information about the data. </font>\n", | |
| "\n", | |
| "[TEMPO Trace Gas and Cloud Level 2 and Level 3 Data Products: User's Guide](https://asdc.larc.nasa.gov/documents/tempo/guide/TEMPO_Level-2-3_trace_gas_clouds_user_guide_V1.1.pdf)" | |
| ], | |
| "metadata": { | |
| "id": "oZOc9z_iEPxj" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **2.2 Opening NetCDF files**" | |
| ], | |
| "metadata": { | |
| "id": "FYgVJLYeBzRr" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Xrpbrvdeajtj" | |
| }, | |
| "source": [ | |
| "We can use the [xarray](http://xarray.pydata.org/en/stable/io.html) package to open self-describing formats and work with large, nested arrays. The [h5netcdf](https://github.com/h5netcdf/h5netcdf) reader engine is useful because it can open `netcdf4` and `HDF` files. Other useful support packages are the [netcdf4](https://unidata.github.io/netcdf4-python/) and [h5py](https://www.h5py.org/) packages." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Nhg0I-nEajtj" | |
| }, | |
| "source": [ | |
| "\n", | |
| "Use the `xr.open_dataset()` function to import the above dataset. The engine option (`engine=<name>`) is used to read the files. Some possible file readers are `netcdf4`, `scipy`, `pydap`, `h5netcdf`, `pynio`, `cfgrib`, `pseudonetcdf`, `zarr` but you also must have the packages installed.\n", | |
| "\n", | |
| "Note: If you need to get the path of the file in Google Colab, you can click on the `Files` folder icon on the left, and right click on the file of interest, and then select \"Copy Path.\"" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "" | |
| ], | |
| "metadata": { | |
| "id": "gjGfOVseXFDI" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "4hoPDQ4Bajtj" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "fname='/content/viirs_eps_noaa20_aod_0.250_deg_20200821.nc'\n", | |
| "noaa20_viirs_aod_file_id = xr.open_dataset(fname, engine='h5netcdf')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "8UjwQn3Oajtj" | |
| }, | |
| "source": [ | |
| "Printing `noaa20_viirs_aod_file_id` will reveal a long list of the variables, dimensions, indices, and global attributes:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "iuLTYbQXajtk" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "noaa20_viirs_aod_file_id" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "l9HpClKQajtk" | |
| }, | |
| "source": [ | |
| "The output above is interactive - click on the arrows to show the metadata for:\n", | |
| "\n", | |
| "* __Dimensions__: The dimensions are named `lon` and `lat`, which are respectively 1440 and 720.\n", | |
| "\n", | |
| "* __Coordinates__: The coordinates are `lon` and `lat`. These have the same name as the dimensions, but this is not always true.\n", | |
| "\n", | |
| "* __Variables__: This file contains several variables, we'll use `AOD550`. It's dimensions are also lat and lon.\n", | |
| "\n", | |
| "* __Attributes__: netCDF4 [CF-1.5 conventions](https://cfconventions.org/). Some of the information that we saw in the file name is also present: this product is the \"NOAA Enterprise L3 Aerosol Optical Depth\" (`title`) it's a NOAA Level 3 product (`processing_level`) and the data was collected from the NOAA-20 (`satellite_name`) VIIRS instrument (`instrument_name)`.\n", | |
| "\n", | |
| "Always inspect netCDF file header contents when working with new data." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **2.3 Opening NetCDF files that have 'Groups'**" | |
| ], | |
| "metadata": { | |
| "id": "hitaK79S8NQm" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "_XKEU6lnajtk" | |
| }, | |
| "source": [ | |
| "Let's look at a dataset that has column totals of nitrogen dioxide (NO2) in the troposphere. NO2 is a harmful air pollutant.\n", | |
| "\n", | |
| "We'll open the `TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc`, inspect the header, and find the `vertical_column_troposphere` variable." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "fname='/content/TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc'\n", | |
| "tempo_no2_file_id = xr.open_dataset(fname, engine='h5netcdf')\n", | |
| "tempo_no2_file_id" | |
| ], | |
| "metadata": { | |
| "id": "qkKIjJ52M1M7" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "In the file above, we can find the dimensions (`longitude` and `latitude`) and coordinates, but there is one data variable (`weight`) but `vertical_column_troposphere` is not listed. That does not mean that data are not in the file, but may be inside a [group](https://docs.xarray.dev/en/stable/user-guide/terminology.html#term-Group). Groups are a commonly used organizational structure in HDF, NetCDF, and Zarr formats.\n", | |
| "\n", | |
| "Some files use a nested [group structure](https://docs.h5py.org/en/stable/high/group.html) to organize their variables, which is also called a [DataTree](https://docs.xarray.dev/en/stable/user-guide/terminology.html#term-DataTree). Groups are also called [subtrees](https://xarray-datatree.readthedocs.io/en/latest/terminology.html#term-Subtree)." | |
| ], | |
| "metadata": { | |
| "id": "8HD7O5Ts6wEb" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "" | |
| ], | |
| "metadata": { | |
| "id": "ihQWTE00PwGm" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "We can open the groups, but we will need to open the file as a [data tree](https://docs.xarray.dev/en/stable/generated/xarray.open_datatree.html#xarray-open-datatree) and use the syntax `xr.open_datatree()`. The syntax looks similar to `xr.open_dataset()`:" | |
| ], | |
| "metadata": { | |
| "id": "W_zJdcmq6G5x" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "fname='/content/TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc'\n", | |
| "tempo_no2_file_id = xr.open_datatree(fname, engine='h5netcdf')\n", | |
| "tempo_no2_file_id" | |
| ], | |
| "metadata": { | |
| "id": "Ke62XdKyPsvh" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "In the output above, you will see there are four groups (`products`, `qa_statistics`, `geolocation`, and `support_data `). You can also print them as a list using `.groups`:" | |
| ], | |
| "metadata": { | |
| "id": "eEKnfxtYRX-e" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "2_BCIXeRajtk" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_no2_file_id.groups" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "The `vertical_column_troposphere` is contained in the `product` group. You can confirm this by inspecting the contents of the group:" | |
| ], | |
| "metadata": { | |
| "id": "2ejlDlm2-WyL" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "tempo_no2_file_id['/product']" | |
| ], | |
| "metadata": { | |
| "id": "pZIKtphK-jJr" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Now you can see the `vertical_column_troposphere` variable is in the file, we can begin extracting the data we need to make a plot." | |
| ], | |
| "metadata": { | |
| "id": "um0FUbh7AFq-" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **2.4 Extracting Data**" | |
| ], | |
| "metadata": { | |
| "id": "M2SPFPKb8njD" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Now that we have inspected the file, we can begin extracting the `latitude`, `longitude`, and `vertical_column_troposphere` variables. Once extracted, we can perform analysis and make plots with the data.\n", | |
| "\n", | |
| "The syntax for extracting variables that aren't in a group is just the variable name:\n", | |
| "\n", | |
| "```\n", | |
| "\"<variable name>\"\n", | |
| "```\n", | |
| "\n", | |
| "The `latitude` and `longitude` are outside of the group, so we use the following syntax:" | |
| ], | |
| "metadata": { | |
| "id": "SK2tVLnVZ9pY" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "4B-JTdJ0ajtk" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_no2_lat = tempo_no2_file_id['latitude']\n", | |
| "tempo_no2_lon = tempo_no2_file_id['longitude']" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "To extract the tropospheric column total NO2, we reference the full path of the group and variable using the following syntax:\n", | |
| "\n", | |
| "```\n", | |
| "\"<group name>/<variable name>\"\n", | |
| "```\n", | |
| "\n", | |
| "Below we extract the variable `vertical_column_troposphere` from the `product` group:" | |
| ], | |
| "metadata": { | |
| "id": "Ac1N1k8EaSon" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "jSRuQOOtajtk" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_no2_vc_trop = tempo_no2_file_id['/product/vertical_column_troposphere']" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Af-1RMK5ajtk" | |
| }, | |
| "source": [ | |
| "Let's print column total NO2 (`tempo_no2_vc_trop`) below:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "Udo7g6UJajtk" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_no2_vc_trop" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Sm9Kh_f0ajtk" | |
| }, | |
| "source": [ | |
| "The `xarray` package uses `numpy` as a dependency so we can use many of the numpy functions like `.mean()`. You can confirm this fact if you check the type of `tempo_no2_vc_trop.values`. Below, you can see the type is `numpy.ndarray.`" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "69z0HVCFajtk" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "type(tempo_no2_vc_trop.values)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "QObwJqj1ajtl" | |
| }, | |
| "source": [ | |
| "Xarray handles missing data automatically - that is, if we do statistics on arrays with `nan` values in them, they will be ignored implicitly:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "Vgsi49Rfajtl" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Check if there are any nan values\n", | |
| "print(np.isnan(tempo_no2_vc_trop).any())\n", | |
| "\n", | |
| "# Compute the average\n", | |
| "avgNO2 = np.mean(tempo_no2_vc_trop)\n", | |
| "print(avgNO2)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Also, let's check the dimensions. Are they the same as the latitude and longitude size? If not, we have to address this when we make plots." | |
| ], | |
| "metadata": { | |
| "id": "75j7MyQmbVVh" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "tempo_no2_vc_trop.shape, tempo_no2_lat.shape, tempo_no2_lon.shape" | |
| ], | |
| "metadata": { | |
| "id": "YrYLdQARbcX9" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "ixrEF0TDajtl" | |
| }, | |
| "source": [ | |
| "#### <b><font color=\"blue\" size=\"5\">Exercise 2-2: Importing an HDF5 file</b></font>\n", | |
| "\n", | |
| "1. Open the IMERG file using the xarray library using `xr.open_datatree()`, save as `gpm_imerg_file_id`\n", | |
| "2. Are there any groups? If yes, what are their name(s)?\n", | |
| "3. Print the variable names - what are the coordinate names?\n", | |
| "3. What are the dimensions of the `latitude` and `longitude` coordinates?\n", | |
| "---\n", | |
| "\n", | |
| "Your Answer:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [], | |
| "metadata": { | |
| "id": "GuZ9bQIeZvmw" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [], | |
| "metadata": { | |
| "id": "iyC34qdRhKmc" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "<details><summary><b><font color=\"blue\" size=5>Answers to Exercise 2-2</font></b></summary>\n", | |
| "<p></p>\n", | |
| "\n", | |
| "1. Open the file\n", | |
| "```\n", | |
| "fname='/content/3B-HHR.MS.MRG.3IMERG.20240401-S120000-E122959.0720.V07B.HDF5'\n", | |
| "gpm_imerg_file_id = xr.open_datatree(fname, engine='h5netcdf')\n", | |
| "gpm_imerg_file_id\n", | |
| "```\n", | |
| "2. Yes there is one group (`Grid`)\n", | |
| "\n", | |
| "3. The coordinates are `lat`, `lon`, and `time`. You can find them in the `'/Grid'` group.\n", | |
| "```\n", | |
| "gpm_imerg_group_id = gpm_imerg_file_id['/Grid']\n", | |
| "gpm_imerg_group_id\n", | |
| "```\n", | |
| "\n", | |
| "4. The `lat` and `lon` coordinates are 1800 and 3600, respectively.\n" | |
| ], | |
| "metadata": { | |
| "id": "FJzkumpKhMcF" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **2.5 Processing Data using Quality/Diagnostic Flags**\n", | |
| "\n", | |
| "Many satellite data files - especially L2 files - include data quality flags or diagnostic flags. These flags are used to screen out satellite observations with known errors or uncertainty (e.g., clouds, sunglint) or select pixels with specific characteristics (e.g., over land, over water).\n", | |
| "\n", | |
| "**The onus is on you as the satellite data user to correctly process satellite variables using quality/diagnostic flags.** How do you know what processing is required, and what quality/diagnostic flags to use? **You must read the satellite product documentation provided by the science team!!**\n", | |
| "\n", | |
| "In this tutorial, the only satellite product that requires processing is the TEMPO gridded (L3) NO2 data." | |
| ], | |
| "metadata": { | |
| "id": "IYCGzZj4eDNh" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **2.5.1 Required processing of TEMPO gridded NO2 data**\n", | |
| "\n", | |
| "From reading the [TEMPO Trace Gas and Cloud Level 2 and Level 3 Data Products: User's Guide](https://asdc.larc.nasa.gov/documents/tempo/guide/TEMPO_Level-2-3_trace_gas_clouds_user_guide_V1.1.pdf) (p. 17-18), we know that we need to apply the following processing to the TEMPO NO2 L3 data variables:\n", | |
| "- Set `product/main_data_quality_flag` == 0\n", | |
| "- Exclude pixels with `support_data/eff_cloud_fraction` > 0.2\n", | |
| "\n", | |
| "Let's read in the two flag variables we need." | |
| ], | |
| "metadata": { | |
| "id": "NvD3Arzcm7vV" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "bSAhtvVdiwSx" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_main_data_qf = tempo_no2_file_id['/product/main_data_quality_flag']\n", | |
| "tempo_eff_cloud_frac = tempo_no2_file_id['/support_data/eff_cloud_fraction']" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "ueSf9igbiwSy" | |
| }, | |
| "source": [ | |
| "Let's print the flag variables below:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "zaprNQlliwSz" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_main_data_qf" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "4iGpCOMHjotL" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "tempo_eff_cloud_frac" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "We can use the `xarray.DataArray.where()` [function](https://docs.xarray.dev/en/stable/generated/xarray.DataArray.where.html) to select the tropospheric column NO2 (`tempo_no2_vc_trop`) pixels that have `tempo_main_data_qf == 0` and `tempo_eff_cloud_frac < 0.2`." | |
| ], | |
| "metadata": { | |
| "id": "yJcX2b4fj9jl" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "processed_no2 = tempo_no2_vc_trop.where((tempo_main_data_qf == 0) & (tempo_eff_cloud_frac < 0.2))" | |
| ], | |
| "metadata": { | |
| "id": "K8zpgvWplNTu" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Notice that the average of the processed NO2 (below) is less than that of the original, unprocessed NO2 you calculated previously. This is because the pixels in the original NO2 DataArray that have `tempo_main_data_qf != 0` and `tempo_eff_cloud_frac > 0.2` were screened out.\n", | |
| "\n", | |
| "**You should use the `processed_no2` variable for analysis and plotting!!!**" | |
| ], | |
| "metadata": { | |
| "id": "9ft0w9nFmqV_" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Compute the average of the processed NO2\n", | |
| "\n", | |
| "processed_avgNO2 = np.mean(processed_no2)\n", | |
| "print(processed_avgNO2)" | |
| ], | |
| "metadata": { | |
| "id": "IYZdC_2ylNMB" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "CnsUOciTajtl" | |
| }, | |
| "source": [ | |
| "Now that we know how to import & process multidimensional data, you will make some plots in the next section." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **Section 3: Simple Plots of Satellite Data**" | |
| ], | |
| "metadata": { | |
| "id": "MPjiVr46rpy9" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "0qhu6T0Zajtl" | |
| }, | |
| "source": [ | |
| "In the homework, we made line and scatter plots from tabular data. The `Matplotlib` package also supports plotting spatial datasets. However, we often have to do perform several array operations to ensure the `x`, `y`, and `z` coordinates are the same shape. Let's work with GPM IMERG dataset in the next example and make a 3D plot." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **3.1 Ensuring Consistent Variable Dimensions**\n", | |
| "\n" | |
| ], | |
| "metadata": { | |
| "id": "BI2YCBMN0Sn3" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "1LEdowACajtl" | |
| }, | |
| "source": [ | |
| "From Exercise 2-1, we learned that this file only contains one group (`Grid`). We can open the group directly so that we don't have to add `'/Grid'` every time we want to access a variable inside that group." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "zSgbdyPYajtl" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "gpm_imerg_group_id = gpm_imerg_file_id['/Grid']\n", | |
| "gpm_imerg_group_id" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "1LgKW5Beajtl" | |
| }, | |
| "source": [ | |
| "From the printed information above, we can see the following:\n", | |
| "\n", | |
| "* __Dimensions__: The dimensions are named `time`, `lon`, and `lat`, which each have the size of 1, 3600, and 1800.\n", | |
| "\n", | |
| "* __Coordinates__: Are also `time`, `lon`, and `lat`\n", | |
| "\n", | |
| "* __Variables__: Has seven variables, we'll examine `precipitation` in this lesson." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "3oLamTAYajtl" | |
| }, | |
| "source": [ | |
| "Let's import the rain rate (`precipitation`). We also need `lat` and `lon` to know where the data are." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "6bp2VCfMajtl" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "gpm_imerg_rr = gpm_imerg_group_id['precipitation']\n", | |
| "gpm_imerg_lat = gpm_imerg_group_id['lat']\n", | |
| "gpm_imerg_lon = gpm_imerg_group_id['lon']" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "Let's inspect the shape and see if the data are already formatted for plotting:" | |
| ], | |
| "metadata": { | |
| "id": "m9vdnH3kcMZj" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "gpm_imerg_lat.shape, gpm_imerg_lon.shape, gpm_imerg_rr.shape" | |
| ], | |
| "metadata": { | |
| "id": "dmtCNswZcLbr" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "At this point, we can make a simple plot of the rain rates using [imshow](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html#matplotlib.pyplot.imshow) from matplotlib. Because the dimensions are `(time, lon, lat)`, we can remove time by subsetting the data using the syntax `[0,:,:]`. We will use the first (and only) time index and keep all the `lon` and `lat` data We'll set the min and max values to 0 and 2 using `vmin` and `vmax`." | |
| ], | |
| "metadata": { | |
| "id": "psR7KyuYzAeu" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "plt.figure()\n", | |
| "plt.imshow(gpm_imerg_rr[0,:,:], vmin=0, vmax=2)\n", | |
| "plt.show()" | |
| ], | |
| "metadata": { | |
| "id": "CYdleQ3pzD8V" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "tN4ujMvuajtl" | |
| }, | |
| "source": [ | |
| "A problem with the above plot is that we do not know where these data are located - for that we need to plot the data with the `lat` and `lon` coordinates." | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "yMntbeAuajtl" | |
| }, | |
| "source": [ | |
| "Mesh plots, such as [pcolormesh](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.pcolormesh.html), are useful for plotting 3D data. Creating a mesh requires that the `X`, `Y`, and `Z` variables have the same 2D shape.\n", | |
| "\n", | |
| "The `.shapes` illustrate that the data all have different shapes. Here are the steps to address this:\n", | |
| "1. `gpm_imerg_rr` has a time dependency, which the latitude and longitude don't. We'll need to reduce this to 2D data.\n", | |
| "2. `gpm_imerg_lat` and `gpm_imerg_lon` are 1D. We can use `np.meshgrid()` to project the 1-dimensional x and y coordinates into two dimensions\n", | |
| "\n", | |
| "The first problem can be solved by using the xarray `.isel` [function](https://docs.xarray.dev/en/latest/generated/xarray.DataArray.isel.html#xarray.DataArray.isel) to select the only available timestep. The index for this is `0`." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "lOViOoQ6ajtl" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "gpm_imerg_rr = gpm_imerg_rr.isel(time=0)\n", | |
| "gpm_imerg_rr" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "ppPKZjXVajtl" | |
| }, | |
| "source": [ | |
| "The `np.meshgrid` function will help with problem #2 above. The function is a little confusing at first, so let's start with a simple example. Suppose you have two simple, 1D arrays:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "PBMQ0Ostajtl" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "old_x = [1,2]\n", | |
| "old_y = [3,4,5]" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "lnvK_Hc4ajtl" | |
| }, | |
| "source": [ | |
| "`old_x` has two elements and `old_y` has three. If you create a mesh of the two variables, there will be two variables, both with 2 rows and 3 columns:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "Nap4SyMIajtl" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "new_x, new_y = np.meshgrid(old_x, old_y, indexing='ij')\n", | |
| "print(new_x)\n", | |
| "print(new_y)" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "" | |
| ], | |
| "metadata": { | |
| "id": "hjdjdya4zOaG" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "The image above depicts how the resulting mesh will look like.\n", | |
| "\n", | |
| "We strongly recommend using the `indexing='ij'` option. This will yield arrays with dimensions in the same order that the indexes are passed into `np.meshgrid()`.\n", | |
| "\n", | |
| "For example:\n", | |
| "* Dim `old_x` --> `2`\n", | |
| "* Dim `old_y` --> `3`\n", | |
| "\n", | |
| "Dim `new_x`, `new_y` --> `(2,3)`\n", | |
| "\n", | |
| "If you use the default (`indexing='xy'`) the resulting mesh will be `(3, 2)`." | |
| ], | |
| "metadata": { | |
| "id": "nzOUOqjaw3Qd" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **3.2 Creating a Plot**" | |
| ], | |
| "metadata": { | |
| "id": "RgmJdmPO0dKK" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "cW3GgW8iajtl" | |
| }, | |
| "source": [ | |
| "\n", | |
| "\n", | |
| "Returning to the the GPM IMERG dataset, below is the meshgrid of the 1-dimensional latitude and longitude coordinates:\n", | |
| "\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "X_gpm_imerg, Y_gpm_imerg = np.meshgrid(gpm_imerg_lon, gpm_imerg_lat, indexing='ij')" | |
| ], | |
| "metadata": { | |
| "id": "ldXd0sOnprn9" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "TCXfOrxIajtm" | |
| }, | |
| "source": [ | |
| "We want the dimensions of the output mesh to match `gpm_imerg_lon` dims, which are `(3600, 1800)`. Note the order:\n", | |
| "\n", | |
| "* Dim `gpm_imerg_lon` --> `3600`\n", | |
| "* Dim `gpm_imerg_lat` --> `1800`\n", | |
| "\n", | |
| "\n", | |
| "To ensure the dimensions of `X_gpm_imerg`, `Y_gpm_imerg` --> `(3600,1800)`, the order needs to use the syntax:\n", | |
| "\n", | |
| "```\n", | |
| "new_x, new_y = np.meshgrid(old_x, old_y, indexing='ij')\n", | |
| "```\n", | |
| "\n", | |
| "We'll see in the next exercise that the NO2 dimensions are ordered lat, lon!" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "0H4_kGMOajtm" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "gpm_imerg_rr.shape, X_gpm_imerg.shape, Y_gpm_imerg.shape" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "dRwFSRGyajtm" | |
| }, | |
| "source": [ | |
| "\n", | |
| "Let's plot the data!\n", | |
| "\n", | |
| "The basic syntax is:\n", | |
| "\n", | |
| "```\n", | |
| "fig = plt.figure()\n", | |
| "ax = plt.subplot(111)\n", | |
| "rr_plot = ax.pcolormesh(X_gpm_imerg, Y_gpm_imerg, gpm_imerg_rr)\n", | |
| "plt.show()\n", | |
| "```\n", | |
| "\n", | |
| "We'll add a few enhancements to make the plot look nicer:\n", | |
| "\n", | |
| "1. Rain rates have a large range (sometimes 50 mm/hour!) but are usually less than 2 mm/hr. We can set the data ranges using the keyword options `vmin=0` and `vmax=2` in `plt.pcolormesh()`\n", | |
| "2. It would be helpful to have a colorbar! To set a colorbar, we need to save the plotted data object as a variable (`rr_plot`) so that we can tell the colorbar command what we're making a colorbar for (`fig.colorbar(rr_plot)`) The `orientation='horizontal'` keyword option can control where the colorbar is positioned." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": { | |
| "id": "PfCKfVN4ajtm" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "fig = plt.figure()\n", | |
| "ax = plt.subplot(111)\n", | |
| "rr_plot = ax.pcolormesh(X_gpm_imerg, Y_gpm_imerg, gpm_imerg_rr, vmin=0, vmax=2)\n", | |
| "fig.colorbar(rr_plot, orientation='horizontal')\n", | |
| "plt.show()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "xkNwRFSgajtm" | |
| }, | |
| "source": [ | |
| "---\n", | |
| "\n", | |
| "#### <b><font color=\"blue\" size=\"5\">Exercise 3-1: Manipulate variable dimensions and make a plot</b></font>\n", | |
| "\n", | |
| "Plot `tempo_no2_lon`, `tempo_no2_lat`, and `processed_no2` (we imported & processed in Section 2):\n", | |
| "\n", | |
| "1. Check the dimensions for all variables using `.shape`.\n", | |
| "2. Do you need to `.isel()` or `.meshgrid()`?\n", | |
| "3. Create a pcolormesh plot. Some useful aesthetics:\n", | |
| " * `processed_no2/1e14`\n", | |
| " * set the `vmin=0` and `vmax=50`\n", | |
| " * add a colorbar\n", | |
| "\n", | |
| "---\n", | |
| "Your answer:" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [], | |
| "metadata": { | |
| "id": "93AKVUDVrvmH" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "<details><summary><b><font color=\"blue\" size=5>Answers to Exercise 3-1</font></b></summary>\n", | |
| "<p></p>\n", | |
| "\n", | |
| "Plot `tempo_no2_lon`, `tempo_no2_lat`, and `processed_no2` (we imported & processed in Section 2):\n", | |
| "\n", | |
| "1. Check the dimensions for all variables using `.shape`.\n", | |
| "\n", | |
| "```\n", | |
| "tempo_no2_lat.shape, tempo_no2_lon.shape, processed_no2.shape\n", | |
| "```\n", | |
| "*((2950,), (7750,), (1, 2950, 7750))*\n", | |
| "\n", | |
| "2. Do you need to `.isel()` or `.meshgrid()`?\n", | |
| "\n", | |
| "Yes to both! `processed_no2` has a time dimension and `tempo_no2_lat`, `tempo_no2_lon` are one dimensional.\n", | |
| "\n", | |
| "Reducing `processed_no2` is straightforward:\n", | |
| "\n", | |
| "```\n", | |
| "processed_no2=processed_no2.isel(time=0)\n", | |
| "```\n", | |
| "\n", | |
| "We want the dimensions of the output mesh to match the `processed_no2` dimensions, which are `(7750, 2970)`. Note the order is lat then lon:\n", | |
| "\n", | |
| "* Dim `tempo_no2_lat` --> `2970`\n", | |
| "* Dim `tempo_no2_lon` --> `7750`\n", | |
| "\n", | |
| "To ensure the dimensions of `tempo_no2_Y`, `tempo_no2_X` --> `(7750,2970)`, the order needs to be lat then lon:\n", | |
| "\n", | |
| "```\n", | |
| "tempo_no2_Y, tempo_no2_X = np.meshgrid(tempo_no2_lat, tempo_no2_lon, indexing='ij')\n", | |
| "```\n", | |
| "\n", | |
| "print the shape to check:\n", | |
| "\n", | |
| "```\n", | |
| "tempo_no2_X.shape, tempo_no2_Y.shape\n", | |
| "\n", | |
| "```\n", | |
| "*((2950, 7750), (2950, 7750))*\n", | |
| "\n", | |
| "\n", | |
| "This matches the shape of tempo_no2_vc_trop after it was reduce using `.isel`.\n", | |
| "\n", | |
| "3. Create a pcolormesh plot. Some useful aesthetics:\n", | |
| " * `processed_no2/1e14`\n", | |
| " * set the `vmin=0` and `vmax=50`\n", | |
| " * add a colorbar\n", | |
| "\n", | |
| "```\n", | |
| "fig = plt.figure()\n", | |
| "ax = plt.subplot(111)\n", | |
| "no2_plot = ax.pcolormesh(tempo_no2_X, tempo_no2_Y, processed_no2/1e14, vmin=0, vmax=50)\n", | |
| "fig.colorbar(no2_plot, orientation='horizontal')\n", | |
| "plt.show()\n", | |
| "```\n", | |
| "\n", | |
| "" | |
| ], | |
| "metadata": { | |
| "id": "FxRD67GHuAxc" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "## **Section 4: Plotting Satellite Data on Maps**" | |
| ], | |
| "metadata": { | |
| "id": "SCIix0nlsAcD" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "The `xarray` package has some [basic built-in plotting functions](https://docs.xarray.dev/en/stable/user-guide/plotting.html) that can be useful for making a quick plot, especially if you are exploring a new satellite dataset. And in Section 3, you learned how to make a simple plot using the `Matplotlib` package.\n", | |
| "\n", | |
| "To make a figure suitable for a presentation or report, however, you will want to plot satellite data on a map using the [cartopy](https://scitools.org.uk/cartopy/docs/latest/index.html) package." | |
| ], | |
| "metadata": { | |
| "id": "W2pFFNYPCCW9" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **4.1 Select a map projection using `cartopy`**\n", | |
| "\n", | |
| "The`cartopy` package has many [map projection options](https://scitools.org.uk/cartopy/docs/latest/reference/projections.html), each with its own strengths and limitations.\n", | |
| "\n", | |
| "Choose a map projection that highlights/emphasizes the satellite data with which you are working. This tutorial demonstrates two different map projections: `Plate Carree` and `Geostationary`." | |
| ], | |
| "metadata": { | |
| "id": "VSaR6rQmA9Vv" | |
| } | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "#### **4.1.1 : Plate Carree map projection**\n", | |
| "\n", | |
| "To make a map, we set up a figure in `Matplotlib` and add `geoaxes` with a map projection using `cartopy`.\n", | |
| "\n", | |
| "The first example of a map projection is the [Plate Carree](https://scitools.org.uk/cartopy/docs/latest/reference/projections.html#platecarree) equidistant cylindrical (equirectangular) projection: `projection=ccrs.PlateCarree()`.\n", | |
| "\n", | |
| "We add [coastlines](https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.mpl.geoaxes.GeoAxes.html#cartopy.mpl.geoaxes.GeoAxes.coastlines) to the map, so we can see how the Earth is represented by this projection. You can ignore the `DownloadWarning` message; the first time you access a [Natural Earth Feature shapefile](https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.io.shapereader.natural_earth.html#cartopy.io.shapereader.natural_earth) via `cartopy`, you will see this warning message." | |
| ], | |
| "metadata": { | |
| "id": "8JQY6jxSDefH" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Set up figure in matplotlib\n", | |
| "fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| "# Set Plate Carree map projection using cartopy\n", | |
| "ax = plt.axes(projection=ccrs.PlateCarree())\n", | |
| "\n", | |
| "# Add coastlines\n", | |
| "ax.coastlines()\n", | |
| "\n", | |
| "# Show plot\n", | |
| "plt.show()" | |
| ], | |
| "metadata": { | |
| "id": "cLhoZEyvDd8B" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "1d8958d6" | |
| }, | |
| "source": [ | |
| "#### **4.1.2 : Geostationary map projection**\n", | |
| "\n", | |
| "When plotting geostationary satellite data, like TEMPO data, many end users prefer to use the native [Geostationary](https://scitools.org.uk/cartopy/docs/latest/reference/projections.html#geostationary) projection.\n", | |
| "\n", | |
| "A number of constants are used to define the `geo_projection`:\n", | |
| "- TEMPO flies on the Intelsat-40e satellite:\n", | |
| " - `satellite_height`: height of satellite above ellipsoid (Earth)\n", | |
| " - `central_longitude`: longitude where satellite is centered; 91° W\n", | |
| "- Ellipsoid (Earth):\n", | |
| " - `semi_major_axis`: semi-major axis of Earth using Geodetic Reference System of 1980 (GRS80); same as for GOES-R ABI data\n", | |
| " - `semi_minor_axis`: semi-minor axis of Earth using Geodetic Reference System of 1980 (GRS80); same as for GOES-R ABI data\n", | |
| " - `inverse_flattening`: reciprocal of Geodetic Reference System of 1980 (GRS80) flattening factor; same as for GOES-R ABI data" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Define constants for Intelsat-40e geostationary orbit & Earth reference system\n", | |
| "satellite_height = 35786023.0 # height of satellite in meters\n", | |
| "central_longitude = -91.0 # longitude of satellite in degrees\n", | |
| "semi_major_axis = 6378137.0 # GRS80 semi-major axis of earth in meters\n", | |
| "semi_minor_axis = 6356752.31414 # GRS80 semi-minor axis of earth in meters\n", | |
| "inverse_flattening = 298.257222096 # Reciprocal of GRS80 flattening factor\n", | |
| "\n", | |
| "# Define geostationary map projection using cartopy\n", | |
| "globe = ccrs.Globe(semimajor_axis=semi_major_axis, semiminor_axis=semi_minor_axis,\n", | |
| " inverse_flattening=inverse_flattening)\n", | |
| "geo_projection = ccrs.Geostationary(central_longitude=central_longitude,\n", | |
| " satellite_height=satellite_height, globe=globe)" | |
| ], | |
| "metadata": { | |
| "id": "iPVxDKi6atRo" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Set up figure in matplotlib\n", | |
| "fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| "# Set geostationary map projection using cartopy\n", | |
| "ax = plt.axes(projection=geo_projection)\n", | |
| "\n", | |
| "# Add coastlines\n", | |
| "ax.coastlines()\n", | |
| "\n", | |
| "# Show plot\n", | |
| "plt.show()" | |
| ], | |
| "metadata": { | |
| "id": "wcPEvAKqFWms" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **4.2 Set the geographic domain of the map**\n", | |
| "\n", | |
| "Use the `set_extent([bounding_box_corners])` [function](https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.mpl.geoaxes.GeoAxes.html#cartopy.mpl.geoaxes.GeoAxes.set_extent) to set the geographic domain of the map, where the `bounding_box_corners` are the `[western_longitude, eastern_longitude, southern_latitude, northern_latitude]` of the zoomed-in domain in degrees; negative values indicate °S latitude or °W longitude.\n", | |
| "\n", | |
| "The argument `crs=ccrs.PlateCarree()` must be used with the `set_extent()` function because we are entering the `bounding_box_corners` in geographic coordinates (latitude and longitude).\n", | |
| "\n", | |
| "Let's zoom into the TEMPO field of regard (FOR) with the `geostationary` map projection. As before, you can ignore the `DownloadWarning` message related to downloading the Natural Earth Feature shapefile via `cartopy`." | |
| ], | |
| "metadata": { | |
| "id": "CHU_sPJeDrx8" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Set up figure in matplotlib\n", | |
| "fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| "# Set geostationary map projection using cartopy\n", | |
| "ax = plt.axes(projection=geo_projection)\n", | |
| "\n", | |
| "# Set geographic domain of map: [W_lon, E_lon, S_lat, N_lat]\n", | |
| "# °E longitude > 0 > °W longitude, °N latitude > 0 > °S latitude\n", | |
| "ax.set_extent([-121, -64, 17, 59], crs=ccrs.PlateCarree())\n", | |
| "\n", | |
| "# Add coastlines\n", | |
| "ax.coastlines()\n", | |
| "\n", | |
| "# Show plot\n", | |
| "plt.show()" | |
| ], | |
| "metadata": { | |
| "id": "3T1l1u6UCMmM" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **4.3 Add borders and shade land & water polygons**\n", | |
| "\n", | |
| "The `cartopy` package has a number of options for adding borders to a map. We've already seen how to add `coastlines()`. For more control over borders as well as land & water polygons, use the `cartopy` [Feature interface](https://scitools.org.uk/cartopy/docs/latest/matplotlib/feature_interface.html), which defines seven common features.\n", | |
| "\n", | |
| "The Feature Interface borders plot as black lines with `linewidth=1` by default. You can modify the default Features interface settings to change the color and/or width of the coastlines and borderlines, and to shade land, ocean, and lakes polygons using `Matplotlib` [built-in named colors](https://matplotlib.org/stable/gallery/color/named_colors.html). \n", | |
| "\n", | |
| "Working with our TEMPO FOR `geostationary` projection map, let's add coastlines, international borders, US state & Canada province borders, and large lakes from the Features interface. And let's color the ocean and lakes `facecolor='lightblue'` and the land `facecolor='wheat'`, and change the widths of the coast/border lines.\n", | |
| "\n", | |
| "As before, you can ignore the `DownloadWarning` messages related to downloading Natural Earth Feature shapefiles via `cartopy`." | |
| ], | |
| "metadata": { | |
| "id": "3bc3Wz5nHo-W" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Set up figure in matplotlib\n", | |
| "fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| "# Set geostationary map projection using cartopy\n", | |
| "ax = plt.axes(projection=geo_projection)\n", | |
| "\n", | |
| "# Set geographic domain of map: [W_lon, E_lon, S_lat, N_lat]\n", | |
| "# °E longitude > 0 > °W longitude, °N latitude > 0 > °S latitude\n", | |
| "ax.set_extent([-121, -64, 17, 59], crs=ccrs.PlateCarree())\n", | |
| "\n", | |
| "# Add coastlines & borders, shade land & water polygons\n", | |
| "ax.add_feature(cfeature.COASTLINE, linewidth=0.5)\n", | |
| "ax.add_feature(cfeature.BORDERS, linewidth=0.5)\n", | |
| "ax.add_feature(cfeature.LAKES, facecolor='lightblue')\n", | |
| "ax.add_feature(cfeature.STATES, linewidth=0.25)\n", | |
| "ax.add_feature(cfeature.LAND, facecolor='wheat')\n", | |
| "ax.add_feature(cfeature.OCEAN, facecolor='lightblue')\n", | |
| "\n", | |
| "# Show plot\n", | |
| "plt.show()" | |
| ], | |
| "metadata": { | |
| "id": "m7rXeDrxK2cp" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **4.4 Add latitude/longitude gridlines & labels using `cartopy`**\n", | |
| "\n", | |
| "Latitude and longitude gridlines and labels can be added using the `cartopy` [gridlines function](https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.mpl.geoaxes.GeoAxes.html#cartopy.mpl.geoaxes.GeoAxes.gridlines), conventionally abbreviated as `gl`. Examples of how to apply the gridlines settings are shown in [cartopy's gridlines tutorial](https://scitools.org.uk/cartopy/docs/latest/matplotlib/gridliner.html).\n", | |
| "\n", | |
| "The appearance of gridlines and labels will be different for different map projections. The easiest approach is to begin with the default settings to add gridlines and labels `gl=ax.gridlines(draw_labels=True)`, and then use parameters in the `gridlines()` function to customize the grid labels and the gridlines.\n", | |
| "\n", | |
| "Let's see how to:\n", | |
| "- Add `'navy'` colored dashed gridlines with `linewidth=0.5`\n", | |
| "- Set tick mark intervals `lon_ticks` and `lat_ticks` for the latitude/longitude grid\n", | |
| "- Assign the locations of the gridlines using the `matplotlib` `ticker.FixedLocator()` [function](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.FixedLocator)\n", | |
| "- Remove the labels along the bottom and right sides of map\n", | |
| "- Make the `'size'` of the labels smaller (8-point font)." | |
| ], | |
| "metadata": { | |
| "id": "uoozbd48Nm71" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Set up figure in matplotlib\n", | |
| "fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| "# Set geostationary map projection using cartopy\n", | |
| "ax = plt.axes(projection=geo_projection)\n", | |
| "\n", | |
| "# Set geographic domain of map: [W_lon, E_lon, S_lat, N_lat]\n", | |
| "# °E longitude > 0 > °W longitude, °N latitude > 0 > °S latitude\n", | |
| "ax.set_extent([-121, -64, 17, 59], crs=ccrs.PlateCarree())\n", | |
| "\n", | |
| "# Format lat/lon gridlines using cartopy\n", | |
| "lon_ticks = [-120, -100, -80, -60]\n", | |
| "lat_ticks = [20, 30, 40, 50]\n", | |
| "gl = ax.gridlines(draw_labels=True, linewidth=0.5, color='navy', ls='--')\n", | |
| "gl.xlocator = ticker.FixedLocator(lon_ticks)\n", | |
| "gl.ylocator = ticker.FixedLocator(lat_ticks)\n", | |
| "gl.right_labels = False\n", | |
| "gl.bottom_labels = False\n", | |
| "gl.xlabel_style = {'size': 8}\n", | |
| "gl.ylabel_style = {'size': 8}\n", | |
| "\n", | |
| "# Add coastlines & borders, shade land & water polygons\n", | |
| "ax.add_feature(cfeature.COASTLINE, linewidth=0.5)\n", | |
| "ax.add_feature(cfeature.BORDERS, linewidth=0.5)\n", | |
| "ax.add_feature(cfeature.LAKES, facecolor='lightblue')\n", | |
| "ax.add_feature(cfeature.STATES, linewidth=0.25)\n", | |
| "ax.add_feature(cfeature.LAND, facecolor='wheat')\n", | |
| "ax.add_feature(cfeature.OCEAN, facecolor='lightblue')\n", | |
| "\n", | |
| "# Show plot\n", | |
| "plt.show()" | |
| ], | |
| "metadata": { | |
| "id": "N7mP-7PNOI6I" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **4.5 Pull information from the satellite file name for plot title & name of saved image file**\n", | |
| "\n", | |
| "Next, we're going to plot TEMPO NO2 and VIIRS AOD data on maps. For each map file we make, we could manually assign the text of the plot title and the saved image file name, but that is tedious and prone to error. A better approach is to use the information in a satellite data file name to generate the plot title and file save name automatically.\n", | |
| "\n", | |
| "Let's see how to do this by making a title for a plot of the TEMPO gridded NO2 data. We set the full directory path to the file on the Colab instance using the `pathlib` module.\n", | |
| "- Recall from Section 1 that the `pathlib` module uses `/` to join the directory path with the file name to get the full path for the file, e.g., `Path.cwd() / <file_name>`.\n", | |
| "- Appending `.name` to a `pathlib` object (e.g., `no2_file.name`) extracts a string representing the final path component, in this case, the file name.\n", | |
| "\n", | |
| "We can extract parts of the satellite data file name using the Python `str.split()` [built-in type](https://docs.python.org/3/library/stdtypes.html#str.split) to split the file name using the `_` delimiter, in conjunction with Python [slicing](https://stackoverflow.com/questions/509211/how-slicing-in-python-works).\n", | |
| "\n", | |
| "For satellite data plot titles, I like to specify the observation date and time. I recommend reformatting the observation date using the `datetime` module's [datetime format codes](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes). This way, we can reformat the date in the satellite file name, which is in **YYYMMDD** format, to something more readable for a plot title, such as **DD Mon YYYY** format (e.g., \"13 Nov 2024\"); this format avoids any confusion with the order of date abbreviations used in the US (MM/DD) and in Europe (DD/MM).\n", | |
| "\n", | |
| "**Coding Notes:** The text formatting `'$_{2}$'` will appear as a subscripted `2` when we add the plot title with `matplotlib`. Python [f-strings](https://docs.python.org/3/tutorial/inputoutput.html#tut-f-strings) are used to join the defined variables and strings to make the `plot_title`." | |
| ], | |
| "metadata": { | |
| "id": "1dC3poi3Uk8w" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Set full directory path to file\n", | |
| "no2_file = Path.cwd() / 'TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc'\n", | |
| "\n", | |
| "# Print the string representing the final path component\n", | |
| "print(no2_file.name)\n", | |
| "print(type(no2_file.name))" | |
| ], | |
| "metadata": { | |
| "id": "MF-kmAFguQ2v" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Pull info out of TEMPO file name string, reformat & make plot title automatically\n", | |
| "file_date = no2_file.name.split('_')[4][:8]\n", | |
| "title_date = datetime.datetime.strptime(file_date, '%Y%m%d').date().strftime('%d %b %Y')\n", | |
| "file_time = no2_file.name.split('_')[4][9:13]\n", | |
| "scan = no2_file.name.split('_')[5][1:4]\n", | |
| "plot_title = (f'TEMPO Tropospheric Column NO$_{2}$ {title_date} Scan {scan}'\n", | |
| " f' {file_time} UTC')\n", | |
| "\n", | |
| "# Print the automatically generated plot title (to check)\n", | |
| "print(plot_title)" | |
| ], | |
| "metadata": { | |
| "id": "jmXLbuO8CMr4" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "### **4.6 Putting it all together: Plot processed TEMPO NO2 on a map**\n", | |
| "\n", | |
| "The code below builds on concepts presented previously in the tutorial to make a professional-looking plot of processed TEMPO gridded (L3) NO2 data on a map.\n", | |
| "\n", | |
| "**There is always more than one way to do something with Python!** The code below presents a different approach than Section 3 for three aspects of the data analysis/plotting:\n", | |
| "\n", | |
| "1. The extra `time` dimension for variables in the `product` and `support_data` groups is removed using the `xarray.DataArray.squeeze` [function](https://docs.xarray.dev/en/stable/generated/xarray.DataArray.squeeze.html) instead of the `xarray.DataArray.isel` [function](https://docs.xarray.dev/en/latest/generated/xarray.DataArray.isel.html#xarray.DataArray.isel).\n", | |
| "2. The processed TEMPO NO2 data (`processed_no2`) are multiplied by 1x10E-15 and plotted on a scale of 0-10 to conform with the convention for displaying satellite NO2 data.\n", | |
| "3. The `latitude` and `longitude` 1-D arrays are used directly in the `matplotlib` `pyplot.pcolormesh()` [plotting function](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.pcolormesh.html) without converting them to 2-D arrays using the `numpy.meshgrid` [function](https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html)\n", | |
| " - `pcolormesh` can handle its `X` and `Y` input arrays (i.e., `latitude` and `longitude`) as 1-D arrays\n", | |
| " - If `X` and/or `Y` are 1-D arrays or column vectors they will be expanded as needed into the appropriate 2-D arrays, making a rectangular grid\n", | |
| "---\n", | |
| "**Additional notes on the map plotting code:**\n", | |
| "\n", | |
| "We open the data file utilizing the Python `with` [statement](https://www.geeksforgeeks.org/with-statement-in-python/), which automatically closes the file when we're done with it.\n", | |
| "\n", | |
| "I changed the colors of the gridlines and land & water polygons to provide a neutral background for the plotted satellite data.\n", | |
| "\n", | |
| " For plotting NO2 data, select any of the sequential colormaps from the `matplotlib` [built-in colormaps](https://matplotlib.org/stable/tutorials/colors/colormaps.html). Note that adding `_r` to the end of any colormap name reverses it (e.g., `'plasma_r'`). We [set a unique color for data > vmax](https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.Colormap.html#matplotlib.colors.Colormap.with_extremes) using the `matplotlib` [built-in named colors](https://matplotlib.org/stable/gallery/color/named_colors.html). This highlights areas on the map with high NO2.\n", | |
| "\n", | |
| "The `matplotlib` `pyplot.pcolormesh()` [plotting function](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.pcolormesh.html) argument `transform=ccrs.PlateCarree()` tells `cartopy` that the plotted satellite data are in geographic coordinates (lat/lon). This argument **must** be included when plotting satellite data that are in geographic coordinates, or the data will not plot correctly on the map projection.\n", | |
| "\n", | |
| "We add a colorbar and format it using the `matplotlib.colorbar.colorbar()` [function](https://matplotlib.org/stable/api/colorbar_api.html).\n", | |
| "\n", | |
| "We save the map image plot to the Colab instance using the the `matplotlib` `fig.savefig()` [function](https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.savefig).\n", | |
| "\n", | |
| "- Change the resolution of the saved image file using the `dpi=` argument in `fig.savefig()`. The higher the dpi, the higher the figure resolution, but the larger the file size and the longer it will take to save the file. The default is `dpi=100`. I typically use `dpi=300` for general use (e.g., posting images on social media). Try `dpi=600` for presentations and `dpi=1000` for journal articles.\n", | |
| "- I recommend setting `bbox_inches='tight'` to minimize empty space around the figure (to zoom in \"tight\" on the plot)." | |
| ], | |
| "metadata": { | |
| "id": "lXLuj6WBLfw1" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Process & plot tropospheric TEMPO L3 NO2 on a geostationary map projection\n", | |
| "\n", | |
| "# Set full directory path to file\n", | |
| "no2_file = Path.cwd() / 'TEMPO_NO2_L3_V03_20241113T182249Z_S009.nc'\n", | |
| "\n", | |
| "# Open file using xarray datatree (automatically closes file when done)\n", | |
| "with xr.open_datatree(no2_file, engine='netcdf4') as dt:\n", | |
| "\n", | |
| " # Read in & process tropospheric NO2 using quality/diagnostic flags\n", | |
| " # \"dataarray.squeeze()\" removes \"time\" dimension & converts 3D arrays to 2D\n", | |
| " trop_no2 = dt['/product/vertical_column_troposphere'].squeeze(dim='time')\n", | |
| " main_data_qf = dt['/product/main_data_quality_flag'].squeeze(dim='time')\n", | |
| " eff_cloud_frac = dt['/support_data/eff_cloud_fraction'].squeeze(dim='time')\n", | |
| " processed_no2 = trop_no2.where((main_data_qf == 0) & (eff_cloud_frac < 0.2))\n", | |
| "\n", | |
| " # Scale NO2 by factor of 1x10E15 (for plotting)\n", | |
| " processed_no2 = processed_no2*1.0E-15\n", | |
| "\n", | |
| " # Read in latitude and longitude\n", | |
| " longitude = dt['longitude']\n", | |
| " latitude = dt['latitude']\n", | |
| "\n", | |
| " # Set up figure in matplotlib\n", | |
| " fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| " # Set map projection using cartopy\n", | |
| " # Use TEMPO's geostationary projection\n", | |
| " ax = plt.axes(projection=geo_projection)\n", | |
| "\n", | |
| " # Set geographic domain of map: [W_lon, E_lon, S_lat, N_lat]\n", | |
| " # °E longitude > 0 > °W longitude, °N latitude > 0 > °S latitude\n", | |
| " ax.set_extent([-121, -64, 17, 59], crs=ccrs.PlateCarree())\n", | |
| "\n", | |
| " # Format lat/lon gridlines using cartopy\n", | |
| " lon_ticks = [-120, -100, -80, -60]\n", | |
| " lat_ticks = [20, 30, 40, 50]\n", | |
| " gl = ax.gridlines(draw_labels=True, linewidth=0.3, color='silver')\n", | |
| " gl.xlocator = ticker.FixedLocator(lon_ticks)\n", | |
| " gl.ylocator = ticker.FixedLocator(lat_ticks)\n", | |
| " gl.right_labels = False\n", | |
| " gl.bottom_labels = False\n", | |
| " gl.xlabel_style = {'size': 8}\n", | |
| " gl.ylabel_style = {'size': 8}\n", | |
| "\n", | |
| " # Add coastlines & borders, shade land & water polygons\n", | |
| " ax.add_feature(cfeature.COASTLINE, linewidth=0.5)\n", | |
| " ax.add_feature(cfeature.BORDERS, linewidth=0.5)\n", | |
| " ax.add_feature(cfeature.LAKES, facecolor='lightgrey')\n", | |
| " ax.add_feature(cfeature.STATES, linewidth=0.25)\n", | |
| " ax.add_feature(cfeature.LAND, facecolor='grey')\n", | |
| " ax.add_feature(cfeature.OCEAN, facecolor='lightgrey')\n", | |
| "\n", | |
| " # Format & add plot title\n", | |
| " # Pull information from file name string & reformat\n", | |
| " file_date = no2_file.name.split('_')[4][:8]\n", | |
| " title_date = datetime.datetime.strptime(file_date, '%Y%m%d').date().strftime('%d %b %Y')\n", | |
| " file_time = no2_file.name.split('_')[4][9:13]\n", | |
| " scan = no2_file.name.split('_')[5][1:4]\n", | |
| " plot_title = (f'TEMPO Tropospheric Column NO$_{2}$ {title_date} Scan {scan}'\n", | |
| " f' {file_time} UTC')\n", | |
| " plt.title(plot_title, pad=10, size=8, weight='bold')\n", | |
| "\n", | |
| " # Set colormap with unique color for data > vmax\n", | |
| " cmap = plt.get_cmap('rainbow').with_extremes(over='darkred')\n", | |
| "\n", | |
| " # Plot tropospheric NO2 data\n", | |
| " # \"transform=ccrs.PlateCarree()\" argument needed b/c data are in geographic coordinates (lat/lon)\n", | |
| " plot = ax.pcolormesh(longitude, latitude, processed_no2, cmap=cmap, vmin=0,\n", | |
| " vmax=10, transform=ccrs.PlateCarree())\n", | |
| "\n", | |
| " # Add colorbar\n", | |
| " cb = fig.colorbar(plot, orientation='horizontal', fraction=0.2, pad=0.02,\n", | |
| " shrink=0.5, ticks=[0, 2, 4, 6, 8, 10], extend='max')\n", | |
| " cb.set_label(label='Tropospheric Column NO$_{2}$ (10$\\mathregular{^{15}}$ cm$\\mathregular{^{-2}}$)',\n", | |
| " size=8, weight='bold')\n", | |
| "\n", | |
| " # Show plot\n", | |
| " plt.show()\n", | |
| "\n", | |
| " # Save image file\n", | |
| " save_name = f'tempo_tropospheric_no2_{file_date}_scan{scan}_{file_time}'\n", | |
| " fig.savefig(Path.cwd() / save_name, dpi=300, bbox_inches='tight')\n", | |
| "\n", | |
| " # Close plot\n", | |
| " plt.close()" | |
| ], | |
| "metadata": { | |
| "id": "WuP-sonW4kwY" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "##### <b><font color=\"blue\" size=\"5\">Exercise 4-1: Plot NOAA-20 VIIRS gridded AOD data on global map</b></font>\n", | |
| "\n", | |
| "In the empty block below, write the code to plot NOAA-20 VIIRS gridded AOD data on a global map with a Plate Carree map projection. Use the code to plot processed TEMPO gridded NO2 data as a starting point.\n", | |
| "\n", | |
| "**Hints:**\n", | |
| "- Review Section 2 to see how you opened the AOD file, and what the contents are.\n", | |
| " - Are there any groups in this file?\n", | |
| "- You will need to extract the `AOD550`, `lon`, and `lat` variables.\n", | |
| "- No processing of the `AOD550` data is required! The file was generated using high quality AOD data.\n", | |
| "- For a Plate Carree map projection, set `projection=ccrs.PlateCarree()`\n", | |
| "- For a global domain, use:\n", | |
| " - `ax.set_extent([180, -180, -90, 90], crs=ccrs.PlateCarree())`\n", | |
| " - `lon_ticks = [0, 60, 120, 180, -180, -120, -60]`\n", | |
| " - `lat_ticks = [-60, -30, 0, 30, 60]`\n", | |
| "- Plot AOD data with a range of `vmin=0` and `vmax=1`.\n", | |
| "- Select a sequential colormap and set a unique color for AOD >1.\n", | |
| "\n", | |
| "\n", | |
| "\n" | |
| ], | |
| "metadata": { | |
| "id": "WZUgEzQXoMCX" | |
| } | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "# Plot VIIRS gridded AOD on a Plate Carree map projection\n", | |
| "\n" | |
| ], | |
| "metadata": { | |
| "id": "m6Qm6iberUvR" | |
| }, | |
| "execution_count": null, | |
| "outputs": [] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "source": [ | |
| "<details><summary><b><font color=\"blue\" size=5>An answer for Exercise 4-1</font></b></summary>\n", | |
| "<p></p>\n", | |
| "\n", | |
| "```\n", | |
| "# Plot VIIRS gridded AOD on a Plate Carree map projection\n", | |
| "\n", | |
| "# Set full directory path to file\n", | |
| "aod_file = Path.cwd() / 'viirs_eps_noaa20_aod_0.250_deg_20200821.nc'\n", | |
| "\n", | |
| "# Open file using xarray dataset (automatically closes file when done)\n", | |
| "with xr.open_dataset(aod_file, engine='netcdf4') as ds:\n", | |
| "\n", | |
| " # Read in AOD550, latitude, and longitude\n", | |
| " aod = ds['AOD550']\n", | |
| " lon = ds['lon']\n", | |
| " lat = ds['lat']\n", | |
| "\n", | |
| " # Set up figure in matplotlib\n", | |
| " fig = plt.figure(figsize=(8, 10))\n", | |
| "\n", | |
| " # Set map projection using cartopy\n", | |
| " # Use Plate Carree projection\n", | |
| " ax = plt.axes(projection=ccrs.PlateCarree())\n", | |
| "\n", | |
| " # Set geographic domain of map: [W_lon, E_lon, S_lat, N_lat]\n", | |
| " # °E longitude > 0 > °W longitude, °N latitude > 0 > °S latitude\n", | |
| " ax.set_extent([180, -180, -90, 90], crs=ccrs.PlateCarree())\n", | |
| "\n", | |
| " # Format lat/lon gridlines using cartopy\n", | |
| " lon_ticks = [0, 60, 120, 180, -180, -120, -60]\n", | |
| " lat_ticks = [-60, -30, 0, 30, 60]\n", | |
| " gl = ax.gridlines(draw_labels=True, linewidth=0.3, color='silver')\n", | |
| " gl.xlocator = ticker.FixedLocator(lon_ticks)\n", | |
| " gl.ylocator = ticker.FixedLocator(lat_ticks)\n", | |
| " gl.right_labels = False\n", | |
| " gl.top_labels = False\n", | |
| " gl.xlabel_style = {'size': 8}\n", | |
| " gl.ylabel_style = {'size': 8}\n", | |
| "\n", | |
| " # Add coastlines & borders, shade land & water polygons\n", | |
| " ax.add_feature(cfeature.COASTLINE, linewidth=0.75)\n", | |
| " ax.add_feature(cfeature.BORDERS, linewidth=0.75)\n", | |
| " ax.add_feature(cfeature.LAKES, facecolor='lightgrey')\n", | |
| " ax.add_feature(cfeature.STATES, linewidth=0.5)\n", | |
| " ax.add_feature(cfeature.LAND, facecolor='grey')\n", | |
| " ax.add_feature(cfeature.OCEAN, facecolor='lightgrey')\n", | |
| "\n", | |
| " # Format & add plot title\n", | |
| " # Pull information from file name & reformat\n", | |
| " satellite = aod_file.name.split('_')[2]\n", | |
| " if satellite == 'npp':\n", | |
| " satellite_name = 'SNPP'\n", | |
| " elif satellite == 'noaa20':\n", | |
| " satellite_name = 'NOAA-20'\n", | |
| " resolution = aod_file.name.split('_')[4][:4]\n", | |
| " file_date = aod_file.name.split('_')[6][:8]\n", | |
| " title_date = datetime.datetime.strptime(file_date, '%Y%m%d').date().strftime('%d %b %Y')\n", | |
| " plot_title = (f'{satellite_name}/VIIRS Aerosol Optical Depth ({resolution}\\N{DEGREE SIGN} resolution)'\n", | |
| " f' {title_date}')\n", | |
| " plt.title(plot_title, pad=10, size=8, weight='bold')\n", | |
| "\n", | |
| " # Set colormap with unique color for data > vmax\n", | |
| " cmap = plt.get_cmap('rainbow').with_extremes(over='darkred')\n", | |
| "\n", | |
| " # Plot VIIRS AOD data\n", | |
| " # \"transform=ccrs.PlateCarree()\" argument needed b/c data are in geographic coordinates (lat/lon)\n", | |
| " plot = ax.pcolormesh(lon, lat, aod, cmap=cmap, vmin=0, vmax=1,\n", | |
| " transform=ccrs.PlateCarree())\n", | |
| "\n", | |
| " # Add colorbar\n", | |
| " cb = fig.colorbar(plot, orientation='horizontal', fraction=0.2, pad=0.05,\n", | |
| " shrink=0.5, ticks=[0, 0.25, 0.5, 0.75, 1], extend='max')\n", | |
| " cb.set_label(label='Aerosol Optical Depth at 550nm', size=8, weight='bold')\n", | |
| "\n", | |
| " # Show plot\n", | |
| " plt.show()\n", | |
| "\n", | |
| " # Save image file\n", | |
| " save_name = f'{satellite}_viirs_aod_gridded_{file_date}'\n", | |
| " fig.savefig(Path.cwd() / save_name, dpi=300, bbox_inches='tight')\n", | |
| "\n", | |
| " # Close plot\n", | |
| " plt.close()\n", | |
| "```\n", | |
| "\n", | |
| "![noaa20_viirs_aod_gridded_20200821.png](data:image/png;base64,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3n//favpCoUCv/76K/z9/a3mSWWVAIzji0uRyuhga2zjouO4d+zY0WZPWlNvtOIEQcDSpUstpv3555+iY8O2atWqXHuyOVvt2rWxcOFCLF261GYPCMD4ef7666+SYz/acvLkSXTs2NEqG0BZLV68WLSHu2koA5NevXqJjvu4fv16pKeni9a9evVq0QwaU6ZMQVhYmNV0d3d3jB492mp6dna2aOpxKR07diwxrbbU5/j4449jwIABVtMjIyPx4Ycf2lWX2D4cFxdncygCpVKJbt264YUXXrCaV3SfLOqdd94R3Xek3oNYW11J6jzk6nOQmPbt2+O5556zmt6iRQsMHTpUdJl//vnH7vqd7Z9//hFNe+zl5YW//voLISEhkst269YNjz76qNV00/Hp9u3b+OabbzBgwADz0BIeHh6QyWTml9QY55Vl/FxH+Pj4SM4ruo1u3rxZ9DMfM2YM6tatazVdJpNZXbMBxrTTjuxnzz33nOgY2E899RS6desmukx5bpsvvfQSHnnkEYtparUavXv3Fi0vliHKUVJp2kNDQ20uJ3WtZOs6CSjdtZIzffDBB/j++++tpnt7e2Px4sUlth8wbud9+vTB9OnTsWrVKmzbtg3r1q3D9OnTRbdfk48++ggpKSllar+r1apVC19++aXNLCiu3H/FzumXLl2yORSBWq1G3759RfcTqXP6J598IrqNDxs2DK1bt7aartfrsWHDBsk2/Je48prOljp16ohebz777LOi5e09Poqd48Wys1QGzjh+VRZlvYexZMkSu4e3cLZNmzaJTn/rrbdEvyt89NFHZc4aVFal2XYqw3n+8OHDeOqpp0SHSfvggw8kh9EqTdsr+vqEiIgABhEQEVGlM3bsWAQFBVlM+/XXX0XT7tpiGr9UjK0H0mKkxm8Ue7h85MgRnDt3zmp6eHi41Zj3ffv2FU3PvmbNGptBEACwbt060QeXrVu3RrVq1SymiaWeBqQfngLA7t277Xo58hALMI59PGLECNEbj/PmzZNMU2nroZutB5NS87y9vUWnnzhxwuIGl62hDEwGDx4sOn3x4sU2/y5p+fuBXC5Hu3bt0KlTJ7uXef7553Hy5En069fP4RtrWq0WQ4YMQVpamqNNlWTvDTM/Pz/R7TM/Px/Lly8XrUNsWBPAOHxF0QeWRV9DhgwRXebff/+19TYsjBgxosQy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IiIiIiIiIiIADCIgIiIiIiIiIiIiIiIiIiKiQgwiICIiIiIiIiIiIiIiIiIiIgAMIiAiIiIiIiIiIiIiIiIiIqJCDCIgIiIiIiIiIiIiIiIiIiIiAAwiICIiIiIiIiIiIiIiIiIiokIMIiAiIiIiIiIiIiIiIiIiIiIADCIgIiIiIiIiIiIiIiIiIiKiQgwiICIiIiIiIiIiIiIiIiIiIgAMIiAiIiIiIiIiIiIiIiIiIqJCDCIgIiIiIiIiIiIiIiIiIiIiAAwiICIiIiIiIiIiIiIiIiIiokIMIiAiIiIiIiIiIiIiIiIiIiIAgLKiG0CVj0ajwZUrVyq6GURERERERERERERERERELlezZk2o1eqKbkalwSACsnLlyhU0bNiwoptBRERERERERERERERERORyp0+fRoMGDSq6GZUGhzMgIiIiIiIiIiIiIiIiIiIiAAwiICIiIiIiIiIiIiIiIiIiokIczoBK9OWXX6JatWoV3QwiojLR6/XIysqCj48PFApFRTeHiKjMeFwjogcNj2tE9KDhcY2IHjQ8rhHRg+TGjRt48803K7oZlRaDCKhE1apVQ61atSq6GUREZaLX65Geng5/f39+ySGiBwKPa0T0oOFxjYgeNDyuEdGDhsc1IqL/Dg5nQERERERERERERERERERERAAYREBERERERERERERERERERESFGERAREREREREREREREREREREABhEQERERERERERERERERERERIUYREBEREREREREREREREREREQAGERAREREREREREREREREREREhRhEQERERERERERERERERERERAAYREBERERERERERERERERERESFGERAREREREREREREREREREREABhEQERERERERERERERERERERIUYREBEREREREREREREREREREQAGERAREREREREREREREREREREhRhEQERERERERERERERERERERAAYREBERERERERERERERERERESFGERAREREREREREREREREREREABhEQERERERERERERERERERERIUYREBEREREREREREREREREREQAGERAREREREREREREREREREREhRhEQERERERERERERERERERERAAYREBERERERERERERERERERESFGERAREREREREREREREREREREABhEQERERERERERERERERERERIUYREBEREREREREREREREREREQAGERAREREREREREREREREREREhRhEQERERERERERERERERERERAAYREBERERERERERERERERERESFGERAREREREREREREREREREREAABlRTeAiIiIiIjov0IQBGRlZUGj0aCgoAAFBQXQarVWvxedJlbO9LdcLodCoYBSqYRCobD63da80pSTWsb0UyaTVfRHTEREREREREREZcQgAiIiIiIiIheZPrI+xsw5gkOHDmHXrl3YtWsXEhISKrpZLiMWYODj44PIyEhERkYiIiLC/HtkZCSCgoIgl1dsgrxNDS4BAObO7YL1na5VaFuI6MHQfUc0AGDjo9dcup5XQxTIylUBABbn5OGXBrfhJjMAAJ4/HQkAeCg5FIdCkkSXf/+9egCAaZ+ew+S3jb/rVIBBYVlu+sfnXNF8IiIiIiKqxBhEQEREREROIQgCdDodDAYD9Hq91ctgMMDHxwdeXl4l1qXVapGVlYXs7GzzT9PvOTk5UCgUUKlU5pe7uzvc3Nzg7u5uNb3o3yqVqsIfWFLllJeXhytXriAuLg46nQ5yudzcy9/0Kvq36Xe5XA6lUmnxt0KhgMFgwLFjx7D97DGs7LAd+fn5Ff0Wy4Vpfy8qPT0dN2/eFC3v7u6O8PBwySCDgIAAZjcgIiIiIiIiIipnDCIgIiIi+g/Lzs7GrVu3cOfOHaSmppof1GdmZlo8xDe9cnNzJYMEBEGwa53R0dGoV68e1Gq1aJBAdna2Sx+4urm5lRh4UFJAQvEynp6e8Pb2hre3N3x9fc2/e3h48AFoJWMwGBAfH49Lly7h7WMJeOzWcVy8eBE3btywext2JlMQgtjQACX9VCgU5vfkrJcp4MdgMECrVUEQdAA0MBgMLvl88vPzce3aNVy7dk10voeHhzm4ICIiAlFRURaBBr6+vmXex544E2v8ySwEROQkrs5AYPJ9sh5AnvnvF8+EW5WRykIAGDMQmByfEw8AWK3Psijzzsd1rZbrsqc6ZAYZUsIKAAC+aUpEXVbBI9MYqBlyQ2Gu+61PjcvPeu+8RR0fja2PyXPOSraNiIiIiIgqFoMIiIiIiP4DBEHAli1bcPjwYdy6dQu3b9/GrVu3kJWVVfLCTmbrgWF50Gq10Gq1yMnJcfm6FAoFvL294ePjAx8fH3h7eyM4OBjR0dGo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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment