{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "
\n", "\n", "
\n", "\"Unidata\n", "
\n", "\n", "

python-awips: Working with Satellite Data

\n", "

Unidata AMS 2021 Student Conference

\n", "\n", "
\n", "
\n", "\n", "---\n", "\n", "
\"Colorized
\n", "\n", "### Focuses\n", "\n", "* Investigate what satellite data is available from an EDEX server.\n", "* Define map properties in a function that can be used to plot multiple images.\n", "* Retreive Mesoscale GOES satellite grid data from an EDEX server.\n", "* Use matplotlib pcolormesh to plot the colorized images with a colorbar.\n", "\n", "\n", "### Objectives\n", "\n", "1. [Define Data Request](#1.-Define-Data-Request)\n", "1. [View Optional Identifiers](#2.-View-Optional-Identifiers)\n", "1. [View Sources](#3.-View-Sources)\n", "1. [View Creating Entities](#4.-View-Creating-Entities)\n", "1. [View Sector IDs](#5.-View-Sector-IDs)\n", "1. [Create a Satellite Product Tree](#6.-Create-a-Satellite-Product-Tree)\n", "1. [Define Map Properties](#7.-Define-Map-Properties)\n", "1. [Plot Image Data!](#8.-Plot-Image-Data!)\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Imports" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from awips.dataaccess import DataAccessLayer\n", "import cartopy.crs as ccrs\n", "import cartopy.feature as cfeat\n", "import matplotlib.pyplot as plt\n", "from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER\n", "import numpy as np\n", "import datetime" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## 1. Define Data Request\n", "\n", "If you read through the [python-awips: How to Access Data](https://nbviewer.jupyter.org/github/Unidata/pyaos-ams-2021/blob/master/notebooks/dataAccess/python-awips-HowToAccessData.ipynb) training, you will know that we need to set an EDEX url to access our server, and then we create a data request. In this example we use *satellite* as the data type to define our request." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create an EDEX data request\n", "DataAccessLayer.changeEDEXHost(\"edex-cloud.unidata.ucar.edu\")\n", "request = DataAccessLayer.newDataRequest()\n", "request.setDatatype(\"satellite\")\n", "\n", "# Take a look at our request\n", "print(request)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "\n", "## 2. View Optional Identifiers\n", "\n", "There can be many sources of satellite data stored in an EDEX server. First, let's take a look at the available \"identifiers\". We will then look a little more in depth at a few of the identifiers." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# get optional identifiers for satellite datatype\n", "identifiers = set(DataAccessLayer.getOptionalIdentifiers(request))\n", "\n", "print(\"Available Identifiers:\")\n", "for id in identifiers:\n", " if id.lower() == 'datauri':\n", " continue\n", " print(\" - \" + id)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## 3. View Sources\n", "\n", "Here, use *source* as the identifier, and take a look at the available sources are. The *source* is a parameter that is set in the netcdf file, and gives some insight to where the product is coming from." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Show available sources\n", "identifier = \"source\"\n", "sources = DataAccessLayer.getIdentifierValues(request, identifier)\n", "print(identifier + \":\")\n", "print(list(sources))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## 4. View Creating Entities\n", "\n", "Next, use *creatingEntity* as the identifier, and take a look at what creating entities are. The *creatingEntity* is related to what instrument or institute created the product." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Show available creatingEntities\n", "identifier = \"creatingEntity\"\n", "creatingEntities = DataAccessLayer.getIdentifierValues(request, identifier)\n", "print(identifier + \":\")\n", "print(list(creatingEntities))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## 5. View Sector IDs\n", "\n", "Next, use *sectorID* as the identifier and take a look at what sectors are available. The *sectorID* is what AWIPS uses to know what geographic section the data is related to." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Show available sectorIDs\n", "identifier = \"sectorID\"\n", "sectorIDs = DataAccessLayer.getIdentifierValues(request, identifier)\n", "print(identifier + \":\")\n", "print(list(sectorIDs))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## 6. Create a Satellite Product Tree\n", "\n", "By cycling through all the identifiers, a detailed overview of all available products can be created. In this example, first *creatingEntity* is used, and then *availableLocationNames*, and *availableParameters* are used to build the product list further. \n", "\n", "> Note: The identifieres *source* and *sectorID* are not used in this tree, but this is only one way to construct such a product overview." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Construct a full satellite product tree\n", "for entity in creatingEntities:\n", " print(entity)\n", " # Create a new request each time through so only one Identifer is set per request\n", " request = DataAccessLayer.newDataRequest(\"satellite\")\n", " request.addIdentifier(\"creatingEntity\", entity)\n", " # Group by available locations\n", " availableSectors = DataAccessLayer.getAvailableLocationNames(request)\n", " availableSectors.sort()\n", " for sector in availableSectors:\n", " print(\" - \" + sector)\n", " request.setLocationNames(sector)\n", " # Get all available products\n", " availableProducts = DataAccessLayer.getAvailableParameters(request)\n", " availableProducts.sort()\n", " for product in availableProducts:\n", " print(\" - \" + product)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## 7. Define Map Properties\n", "\n", "In order to plot more than one image, it's easiest to define common logic in a function. Here, a new function called **make_map** is defined. This function uses the [matplotlib.pyplot package (plt)](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.html) to create a figure and axis. The coastlines (continental boundaries) are added, along with lat/lon grids. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def make_map(bbox, projection=ccrs.PlateCarree()):\n", " fig, ax = plt.subplots(figsize=(10,12),\n", " subplot_kw=dict(projection=projection))\n", " if bbox[0] is not np.nan:\n", " ax.set_extent(bbox)\n", " ax.coastlines(resolution='50m')\n", " gl = ax.gridlines(draw_labels=True)\n", " gl.top_labels = gl.right_labels = False\n", " gl.xformatter = LONGITUDE_FORMATTER\n", " gl.yformatter = LATITUDE_FORMATTER\n", " return fig, ax" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## 8. Plot Image Data!\n", "\n", "For this example, use Channel 13 on the two mesoscale sectors from GOES-East satellite. Create a figure to contain the plot. Create a new *Data Request* for each sector and set the location and parameters on it. Limit the data to the most recently acquired image using the *getAvailableTimes* function. Then use pcolormesh to create a plot from the data in the response in the *GridData* object.\n", "\n", "> Note: You may see a warning appear with a red background, this is okay, and will go away with subsequent runs of the cell." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Specify the sectors - GOES East Mesocales\n", "sectors = [\"EMESO-1\",\"EMESO-2\"]\n", "# Create a figure to contain all subplots\n", "fig = plt.figure(figsize=(16,7*len(sectors)))\n", "\n", "# Cycle through the sectors to create and plot recent data from each one\n", "for i, sector in enumerate(sectors):\n", "\n", " # Create the Ch 13 data request\n", " request = DataAccessLayer.newDataRequest()\n", " request.setDatatype(\"satellite\")\n", " request.setLocationNames(sector)\n", " request.setParameters(\"CH-13-10.35um\")\n", "\n", " # Get the available times\n", " utc = datetime.datetime.utcnow()\n", " times = DataAccessLayer.getAvailableTimes(request)\n", "\n", " # Get the grid data using the latest time\n", " response = DataAccessLayer.getGridData(request, [times[-1]])\n", " grid = response[0]\n", " data = grid.getRawData()\n", " lons,lats = grid.getLatLonCoords()\n", " \n", " # Create the bounding box from the grid data\n", " bbox = [lons.min(), lons.max(), lats.min(), lats.max()]\n", "\n", " # Draw a new subplot based on the bounding box\n", " fig, ax = make_map(bbox=bbox)\n", " \n", " # Add state boundaries where available\n", " states = cfeat.NaturalEarthFeature(category='cultural', name='admin_1_states_provinces_lines', scale='50m', facecolor='none')\n", " ax.add_feature(states, linestyle=':')\n", " \n", " # Create the color scale\n", " cs = ax.pcolormesh(lons, lats, data, cmap='coolwarm')\n", " \n", " #Create the colorbar and add a label\n", " cbar = fig.colorbar(cs, shrink=0.6, orientation='horizontal')\n", " cbar.set_label(sector + \" \" + grid.getParameter() + \" \" \\\n", " + str(grid.getDataTime().getRefTime()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Top\n", "\n", "---\n", "## See also\n", "\n", "Documentation for:\n", "\n", "* [awips.DataAccessLayer](http://unidata.github.io/python-awips/api/DataAccessLayer.html#)\n", "* [awips.PyGridData](http://unidata.github.io/python-awips/api/PyGridData.html)\n", "* [matplotlib.pyplot](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.html)\n", "* [matplotlib.pyplot.pcolormesh](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.pcolormesh.html)\n", "* [matplotlib.pyplot.subplot](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.subplot.html)\n", "\n", "\n", "### Related Notebooks\n", "\n", "* [python-awips: How to Access Data](https://nbviewer.jupyter.org/github/Unidata/pyaos-ams-2021/blob/master/notebooks/dataAccess/python-awips-HowToAccessData.ipynb)\n", "\n", "Top" ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:pyaos-ams-2021]", "language": "python", "name": "conda-env-pyaos-ams-2021-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.1" } }, "nbformat": 4, "nbformat_minor": 1 }