python-awips/examples/notebooks/Watch_Warning_and_Advisory_Plotting.ipynb

693 lines
506 KiB
Text
Raw Permalink Normal View History

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a name=\"top\"></a>\n",
"<div style=\"width:1000 px\">\n",
"\n",
"<div style=\"float:right; width:98 px; height:98px;\">\n",
"<img src=\"https://docs.unidata.ucar.edu/images/logos/unidata_logo_vertical_150x150.png\" alt=\"Unidata Logo\" style=\"height: 98px;\">\n",
"</div>\n",
"\n",
"# Watch, Warning, and Advisory Plotting\n",
"**Python-AWIPS Tutorial Notebook**\n",
"\n",
"<div style=\"clear:both\"></div>\n",
"</div>\n",
"\n",
"---\n",
"\n",
"<div style=\"float:right; width:250 px\"><img src=\"../images/warnings_preview.png\" alt=\"Example Warning, Watch, Advisory plot\" style=\"height: 300px;\"></div>\n",
"\n",
"\n",
"# Objectives\n",
"\n",
"* Create a colorized plot with [Warnings, Watches, Advisories and Statements (WWAs)](https://weather.cod.edu/notes/criteria/)\n",
"* Use python-awips to connect to an EDEX server\n",
"* Create and filter the data request specifically for a warning data type\n",
"* Create and use accurate time filter for data requests\n",
"* Define and use functions\n",
"* Define and use dictionaries\n",
"* Colorize shapes based on a dictionary\n",
"* Overlay warnings, watches, and advisories with state and political maps\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {
"toc": true
},
"source": [
"<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
2022-04-28 13:23:07 -04:00
"<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Imports\" data-toc-modified-id=\"Imports-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>Imports</a></span></li><li><span><a href=\"#Function:-make_map()\" data-toc-modified-id=\"Function:-make_map()-2\"><span class=\"toc-item-num\">2&nbsp;&nbsp;</span>Function: make_map()</a></span></li><li><span><a href=\"#Function:-get_color()\" data-toc-modified-id=\"Function:-get_color()-3\"><span class=\"toc-item-num\">3&nbsp;&nbsp;</span>Function: get_color()</a></span></li><li><span><a href=\"#Function-get_title()\" data-toc-modified-id=\"Function-get_title()-4\"><span class=\"toc-item-num\">4&nbsp;&nbsp;</span>Function get_title()</a></span></li><li><span><a href=\"#Initial-Setup\" data-toc-modified-id=\"Initial-Setup-5\"><span class=\"toc-item-num\">5&nbsp;&nbsp;</span>Initial Setup</a></span><ul class=\"toc-item\"><li><span><a href=\"#EDEX-Connection\" data-toc-modified-id=\"EDEX-Connection-5.1\"><span class=\"toc-item-num\">5.1&nbsp;&nbsp;</span>EDEX Connection</a></span></li><li><span><a href=\"#Significance-(Sig)-Constants\" data-toc-modified-id=\"Significance-(Sig)-Constants-5.2\"><span class=\"toc-item-num\">5.2&nbsp;&nbsp;</span>Significance (Sig) Constants</a></span></li></ul></li><li><span><a href=\"#Filter-by-Time\" data-toc-modified-id=\"Filter-by-Time-6\"><span class=\"toc-item-num\">6&nbsp;&nbsp;</span>Filter by Time</a></span></li><li><span><a href=\"#Use-the-Data!\" data-toc-modified-id=\"Use-the-Data!-7\"><span class=\"toc-item-num\">7&nbsp;&nbsp;</span>Use the Data!</a></span><ul class=\"toc-item\"><li><span><a href=\"#Get-the-Data\" data-toc-modified-id=\"Get-the-Data-7.1\"><span class=\"toc-item-num\">7.1&nbsp;&nbsp;</span>Get the Data</a></span></li><li><span><a href=\"#Extract-Phensigs,-Geometries,-and-Times\" data-toc-modified-id=\"Extract-Phensigs,-Geometries,-and-Times-7.2\"><span class=\"toc-item-num\">7.2&nbsp;&nbsp;</span>Extract Phensigs, Geometries, and Times</a></span></li></ul></li><li><span><a href=\"#Plot-the-Data!\" data-toc-modified-id=\"Plot-the-Data!-8\"><span class=\"toc-item-num\">8&nbsp;&nbsp;</span>Plot the Data!</a></span><ul class=\"toc-item\"><li><span><a href=\"#Create-State-and-Political-Boundaries\" data-toc-modified-id=\"Create-State-and-Political-Boundaries-8.1\"><span class=\"toc-item-num\">8.1&nbsp;&nbsp;</span>Create State and Political Boundaries</a></span></li><li><span><a href=\"#Draw-the-Plot-and-Legend-for-WWAs\" data-toc-modified-id=\"Draw-the-Plot-and-Legend-for-WWAs-8.2\"><span class=\"toc-item-num\">8.2&nbsp;&nbsp;</span>Draw the Plot and Legend for WWAs</a></span></li></ul></li><li><span><a href=\"#See-Also\" data-toc-modified-id=\"See-Also-9\"><span class=\"toc-item-num\">9&nbsp;&nbsp;</span>See Also</a></span><ul class=\"toc-item\"><li><span><a href=\"#Related-Notebooks\" data-toc-modified-id=\"Related-Notebooks-9.1\"><span class=\"toc-item-num\">9.1&nbsp;&nbsp;</span>Related Notebooks</a></span></li><li><span><a href=\"#Additional-Documentation\" data-toc-modified-id=\"Additional-Documentation-9.2\"><span class=\"toc-item-num\">9.2&nbsp;&nbsp;</span>Additional Documentation</a></span></li></ul></li></ul></div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Imports\n",
"\n",
"The imports below are used throughout the notebook. The python-awips imports allow us to connect to an EDEX server, use the warning lookup dictionary, and define a TimeRange. The additional imports are for data manipulation and visualization. "
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from datetime import datetime, timedelta, UTC\n",
"\n",
"import numpy as np\n",
"import matplotlib.patches as mpatches\n",
"import matplotlib.pyplot as plt\n",
"import cartopy.crs as ccrs\n",
"import cartopy.feature as cfeature\n",
"from cartopy.feature import ShapelyFeature, NaturalEarthFeature\n",
"from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER\n",
"from shapely.geometry import MultiPolygon, Polygon\n",
"\n",
"from awips.dataaccess import DataAccessLayer\n",
"from awips.tables import vtec\n",
"from dynamicserialize.dstypes.com.raytheon.uf.common.time import TimeRange"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Function: make_map()\n",
"\n",
"In order to plot more than one image, it's easiest to define common logic in a function. However, for this notebook we only use it in one place. It is a function you will find in most of our example notebooks.\n",
"\n",
"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 lat/lon grids are added."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def make_map(bbox, projection=ccrs.PlateCarree()):\n",
" fig, ax = plt.subplots(figsize=(20,12),\n",
" subplot_kw=dict(projection=projection))\n",
" ax.set_extent(bbox)\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": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Function: get_color()\n",
"\n",
"Since we'll be needing to access the color using the vtec lookup table in several places, creating an easily recognizable function is useful."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def get_color(phensig):\n",
" return vtec[phensig]['color']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Function get_title()\n",
"\n",
"Similar to the color function just defined, accessing the full name for the phensig will also be necessary, so this function will be helpful."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def get_title(phensig):\n",
" return vtec[phensig]['hdln']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
2022-04-28 13:23:07 -04:00
"## Initial Setup"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### EDEX Connection\n",
"\n",
"First we establish a connection to Unidata's public EDEX server. With that connection made, we can create a [new data request object](http://unidata.github.io/python-awips/api/IDataRequest.html) and set the data type to ***warning***, and set the Parameters to ***phensig*** and ***sig***.\n",
"\n",
"<br>\n",
"<div class=\"alert-info\">\n",
"<b>Note:</b> Remember, to see all available parameters use the <a href=\"http://unidata.github.io/python-awips/api/DataAccessLayer.html#awips.dataaccess.DataAccessLayer.getAvailableParameters\"><b>DataAccess.getAvailableParameters()</b></a> method as shown in the <a href=\"http://unidata.github.io/python-awips/examples/generated/Grid_Levels_and_Parameters.html#get-available-parameters\"><b>Grid Levels and Parameters Notebook.</b></a>\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"DataAccessLayer.changeEDEXHost(\"edex-cloud.unidata.ucar.edu\")\n",
"request = DataAccessLayer.newDataRequest()\n",
"request.setDatatype(\"warning\")\n",
"params = [\"phensig\", \"sig\"]\n",
"request.setParameters(*(params))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Significance (Sig) Constants\n",
"\n",
"The two parameters we're requesting for our warning objects are ***phensig*** and ***sig*** where phensig is styled \"XX.Y\" and sig is \"Y\". Phen stands for \"Phenomena\" and sig stands for \"Significance\". [A more detailed description of phensigs and how they're used is provided with this NWS pamphlet](https://www.weather.gov/media/vtec/VTEC_explanation4-20.pdf).\n",
"\n",
"The constants in this section correlate the ***sig*** to what type of message it is (what significance it is). "
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"WATCH_SIG = 'A'\n",
"WARN_SIG = 'W'\n",
"ADVIS_SIG = 'Y'\n",
"STATEM_SIG = 'S'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Filter by Time\n",
"\n",
"Here we decide how much data we want to pull from EDEX. By default we'll request 12 hours, but that value can easily be modified by [adjusting the `timedelta(hours = 12)`](https://docs.python.org/3/library/datetime.html#timedelta-objects) in line `2`. The more data we request, the longer the next section will take to run."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# Get records from the last 12 hours\n",
"lastHourDateTime = datetime.now(UTC) - timedelta(hours = 12)\n",
"start = lastHourDateTime.strftime('%Y-%m-%d %H:%M:%S')\n",
"end = datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S')\n",
"\n",
"beginRange = datetime.strptime( start , \"%Y-%m-%d %H:%M:%S\")\n",
"endRange = datetime.strptime( end , \"%Y-%m-%d %H:%M:%S\")\n",
"timerange = TimeRange(beginRange, endRange)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Use the Data!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Get the Data\n",
"\n",
"Now that we have our `request` and TimeRange `timerange` objects ready, we're ready to get the data array from EDEX.\n",
"\n",
"<br>\n",
"<div class=\"alert-info\">\n",
"<b>Note:</b> Above we set <b>timerange</b> to be 12 hours worth of data. This can return on the order of ~2000 records and can take a little while to run.\n",
"</div>"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using 2330 records\n"
]
}
],
"source": [
"# Get response\n",
"response = DataAccessLayer.getGeometryData(request, timerange)\n",
"print(\"Using \" + str(len(response)) + \" records\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Extract Phensigs, Geometries, and Times\n",
"\n",
"In this section we start gathering all the information we'll need to properly display our data. First we create an array to keep track of unique phensigs. This is useful summary information and will be used to help create the legend which we'll display along with our plot.\n",
"\n",
"Next, we create arrays for each of the 4 types of significance a statement can have. We will group our records this way, so we can easily toggle which records to display or not.\n",
"\n",
"Then, we create two time variables to keep track of the earliest time from our records and the latest time, and will display that information in the title of our plot.\n",
"\n",
"This section has optional print statements at lines `65` and `85`. The first prints out the title, phensig, ref time, and shape for each unique phensig, and the second prints out a sum of how many unique phensigs there are.\n",
"\n",
"We cycle through all the data produced from our `response` object, access its geometries, and create a new [**ShapelyFeature**](https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.feature.ShapelyFeature.html) with the corresponding color. Then we place this new feature in the appropriate `shapes` array. During this process we also populate the phensigs array with all unique phensig entries.\n",
"\n",
"Finally, after we're done looping through all the `response` data, we create a mapping of phensigs to their corresponding titles. This will be used later to sort the legend alphabetically by titles (which differs from simply sorting by phensig). Ex. *Blizzard Warning (BZ.W)* would come before *Areal Flood Advisory (FA.Y)* if we simply sorted by phensig."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"28 Unique Phensigs\n"
]
}
],
"source": [
"# Keep track of unique phensigs, to use in legend\n",
"phensigs = []\n",
"\n",
"# Sort the geometries based on their sig\n",
"watch_shapes = []\n",
"warning_shapes = []\n",
"advisory_shapes = []\n",
"statement_shapes = []\n",
"\n",
"# Keep track of the earliest and latest reftime for title\n",
"# start with the first time from the first object in the response\n",
"time_str = str(response[0].getDataTime().getRefTime())\n",
"# truncate the decimal seconds for datetime parsing\n",
"time_str = time_str[:-4]\n",
"# convert to datetime object for easy comparison\n",
"first_time = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')\n",
"last_time = datetime.strptime(time_str, '%Y-%m-%d %H:%M:%S')\n",
"\n",
"for ob in response:\n",
" \n",
" # get the geometry for the object\n",
" poly = ob.getGeometry()\n",
" # get the reftime for the object\n",
" ref = ob.getDataTime().getRefTime()\n",
" \n",
" # do not plot if phensig is blank (SPS)\n",
" if ob.getString('phensig'):\n",
" \n",
" # look at the reftime\n",
" # convert reftime to a string and parse the decimal seconds\n",
" ref_str = str(ref)\n",
" ref_str = ref_str[:-4]\n",
" # convert reftime to a datetime object for comparison\n",
" ref_time = datetime.strptime(ref_str, '%Y-%m-%d %H:%M:%S')\n",
" # compare current time with first and last times and set if appropriate\n",
" if ref_time is not None:\n",
" if ref_time < first_time:\n",
" first_time = ref_time\n",
" elif ref_time > last_time:\n",
" last_time = ref_time\n",
" \n",
" # get the phensig and sig values from object\n",
" phensigString = ob.getString('phensig')\n",
" sig = ob.getString('sig')\n",
"\n",
" # set the geometries based on whether it's a MultiPolygon or Polygon\n",
" if poly.geom_type == 'MultiPolygon':\n",
" geometries = np.array([])\n",
" geometries = np.append(geometries,MultiPolygon(poly).geoms)\n",
" else:\n",
" geometries = np.array([])\n",
" geometries = np.append(geometries,Polygon(poly))\n",
"\n",
" for geom in geometries:\n",
" bounds = Polygon(geom)\n",
" intersection = bounds.intersection\n",
" geoms = (intersection(geom) for geom in geometries if bounds.intersects(geom))\n",
" \n",
" # Store the unique phensigs\n",
" if not phensigString in phensigs:\n",
" phensigs.append(phensigString)\n",
" # Optional printout of unique Phensigs\n",
"# print(get_title(phensigString) + \" (\" + phensigString + \")\n",
" \n",
" # get the corresponding color using the dictionary\n",
" color = get_color(phensigString)\n",
" # create a new shape feature for the object\n",
" shape_feature = ShapelyFeature(geoms,ccrs.PlateCarree(), \n",
" facecolor=color, edgecolor=color)\n",
" # store the shape feature in the correct array\n",
" if sig is WARN_SIG:\n",
" warning_shapes.append(shape_feature)\n",
" elif sig is WATCH_SIG:\n",
" watch_shapes.append(shape_feature)\n",
" elif sig is ADVIS_SIG:\n",
" advisory_shapes.append(shape_feature)\n",
" elif sig is STATEM_SIG:\n",
" statement_shapes.append(shape_feature)\n",
"\n",
"# Optional printout for the number of unique phensigs\n",
"print(len(phensigs), \" Unique Phensigs\")\n",
"\n",
"# Map phensigs to their titles (used for displaying alphabetically by\n",
"# title in legend)\n",
"phensig_titles = {}\n",
"for phensig in phensigs:\n",
" key = get_title(phensig)\n",
" phensig_titles[key] = phensig"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plot the Data!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Create State and Political Boundaries\n",
"\n",
"Define the state and political boundaries that we'll use in our plot to give more of a frame of reference. These objects are standard method calls in the [Cartopy Feature package, using the NaturalEarthFeature function](https://scitools.org.uk/cartopy/docs/v0.14/matplotlib/feature_interface.html#cartopy.feature.NaturalEarthFeature)."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"# Define the state and political boundaries for the plot \n",
"states_provinces = cfeature.NaturalEarthFeature(\n",
" category='cultural',\n",
" name='admin_1_states_provinces_lines',\n",
" scale='50m',\n",
" facecolor='none')\n",
"political_boundaries = cfeature.NaturalEarthFeature(category='cultural',\n",
" name='admin_0_boundary_lines_land',\n",
" scale='50m', facecolor='none')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Draw the Plot and Legend for WWAs\n",
"\n",
"Here is where we finally get to draw something! The very first few lines of this section are constants that we can manually \"switch on and off\" for what records we want displayed. By default we have all significance types drawn. If we want to \"turn off\" any of the significance records, simply set its corresponding constant to false, and re-run this cell to see how that plot compares.\n",
"\n",
"The next step involves creating the objects that are used to define the legend. We use the `phensig_titles` dictionary to loop through all the phensigs in alphabetical (by title) order. Then, we compare if the phensig will be displayed or not based on the display constants from the previous lines. If the significance will be drawn then we create a new [**Patch** object](https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Patch.html#matplotlib.patches.Patch) of the corresponding color with the corresponding label and add it to our `handles` array.\n",
"\n",
"After that we define our bounding box and create our new plot with its figure and axes.\n",
"\n",
"Our next step is to create our Title for our plot. We create a title based on the draw variables to accurately describe what is being drawn in our plot. Here is where we use the first and last times defined in a previous cell.\n",
"\n",
"Finally, we create and show our plot. We add the title to the plot, add all the features to the axes, and add the legend as well. "
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 2000x1200 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Set these variables for which records to draw\n",
"DRAW_ADVISORY = True\n",
"DRAW_WATCH = True\n",
"DRAW_WARNING = True\n",
"DRAW_STATEMENT = True\n",
" \n",
"# Create handles for legend and add items alphabetically by title and\n",
"# only display based on the display values above\n",
"handles = []\n",
"for title in sorted(phensig_titles):\n",
" phensig = phensig_titles[title]\n",
" # check draw booleans\n",
" if ( \".\"+ADVIS_SIG in phensig and DRAW_ADVISORY or \n",
" \".\"+WATCH_SIG in phensig and DRAW_WATCH or \n",
" \".\"+WARN_SIG in phensig and DRAW_WARNING or \n",
" \".\"+STATEM_SIG in phensig and DRAW_STATEMENT ):\n",
" entry = mpatches.Patch(color=get_color(phensig), label=title)\n",
" handles.append(entry)\n",
"\n",
"# Create the plot\n",
"bbox=[-127,-64,24,49]\n",
"fig, ax = make_map(bbox=bbox)\n",
"\n",
"# Add the title\n",
"# Construct the title based on which record types are being displayed\n",
"title_string = \"\"\n",
"if DRAW_WATCH:\n",
" title_string += \"Watches, \"\n",
"if DRAW_WARNING:\n",
" title_string += \"Warnings, \"\n",
"if DRAW_ADVISORY:\n",
" title_string += \"Advisories, \"\n",
"if DRAW_STATEMENT:\n",
" title_string += \"Statements, \"\n",
"# remove the last comma and space\n",
"title_string = title_string[:-2]\n",
"# add the time range\n",
"title_string += \" from \" + str(first_time)[:-3] + \" to \" + str(last_time)[:-3] + \" UTC\"\n",
"# set the title on the plot, give it a bigger font size, and increase\n",
"# the vertical padding between the title and the figure\n",
"plt.title(title_string, fontsize=20, pad=10)\n",
"\n",
"# Draw all features on the plot\n",
"ax.add_feature(cfeature.LAND)\n",
"ax.add_feature(cfeature.COASTLINE)\n",
"ax.add_feature(states_provinces, edgecolor='black')\n",
"ax.add_feature(political_boundaries, edgecolor='black')\n",
"\n",
"# Draw WWAs in order: Advisory -> Watch > Warning > Statement\n",
"if DRAW_ADVISORY:\n",
" for shape in advisory_shapes:\n",
" ax.add_feature(shape)\n",
"if DRAW_WATCH:\n",
" for shape in watch_shapes:\n",
" ax.add_feature(shape)\n",
"if DRAW_WARNING:\n",
" for shape in warning_shapes:\n",
" ax.add_feature(shape)\n",
"if DRAW_STATEMENT:\n",
" for shape in statement_shapes:\n",
" ax.add_feature(shape)\n",
" \n",
"# Draw the legend\n",
"# use the handles defined earlier for the color associations to\n",
"# phensig titles, set the location to the lower center, give it\n",
"# 5 columns so it uses all the horizonatal space, place it under\n",
"# the actual figure, and give it a larger fontsize\n",
"bottom = 0.12 + (len(handles)//5 *.04)\n",
"ax.legend(handles=handles, loc='lower center', ncol=5, bbox_to_anchor=(0.5, -bottom), fontsize=16)\n",
"\n",
"# Show the plot\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## See Also\n",
"\n",
"- [National Weather Service WWA Definitions (Baltimore Office)](https://www.weather.gov/lwx/warningsdefined#:~:text=A%20Winter%20Storm%20Warning%20is%20issued%20when%20a,combination%20of%20snow%20and%2For%20ice%20accumulation%20with%20wind.)\n",
"- [College of Dupage WWA Definitions](https://weather.cod.edu/notes/criteria/)\n",
"- [Phensig Explanation](https://www.weather.gov/media/vtec/VTEC_explanation4-20.pdf)\n",
"\n",
"### Related Notebooks\n",
"\n",
"- [Grid Levels and Parameters](http://unidata.github.io/python-awips/examples/generated/Grid_Levels_and_Parameters.html)\n",
"\n",
"### Additional Documentation\n",
"\n",
"**python-awips**\n",
"\n",
"- [DataAccessLayer.changeEDEXHost()](http://unidata.github.io/python-awips/api/DataAccessLayer.html#awips.dataaccess.DataAccessLayer.changeEDEXHost)\n",
"- [DataAccessLayer.newDataRequest()](http://unidata.github.io/python-awips/api/DataAccessLayer.html#awips.dataaccess.DataAccessLayer.newDataRequest)\n",
"- [DataAccessLayer.getAvailableParameters()](http://unidata.github.io/python-awips/api/DataAccessLayer.html#awips.dataaccess.DataAccessLayer.getAvailableParameters)\n",
"- [IDataRequest](http://unidata.github.io/python-awips/api/IDataRequest.html)\n",
"- [GeometryData](http://unidata.github.io/python-awips/api/PyGeometryData.html)\n",
"\n",
"**datetime**\n",
"\n",
"- [datetime.datetime](https://docs.python.org/3/library/datetime.html#datetime-objects)\n",
"- [datetime.timedelta](https://docs.python.org/3/library/datetime.html#timedelta-objects)\n",
"\n",
"**cartopy**\n",
"\n",
"- [cartopy feature interface](https://scitools.org.uk/cartopy/docs/v0.14/matplotlib/feature_interface.html)\n",
"- [cartopy.feature.ShaeplyFeature](https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.feature.ShapelyFeature.html)\n",
"\n",
"**matplotlib**\n",
"\n",
"- [matplotlib.pyplot()](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.html)\n",
"- [matplotlib.pyplot.legend()](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.legend.html)\n",
"- [matplotlib.pyplot.axes()](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axes.html)\n",
"- [matplotlib.pyplot.figure()](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.figure.html)\n",
"- [matplotlib.pyplot.title()](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.title.html)\n",
"- [matplotlib.pathes.Patch](https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Patch.html#matplotlib.patches.Patch)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a href=\"#top\">Top</a>\n",
"\n",
"---"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"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.13.1"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": true,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": true,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
}
},
"nbformat": 4,
"nbformat_minor": 4
}