{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Using Python to Access NCEI Archived NEXRAD Level 2 Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook shows how to access the THREDDS Data Server (TDS) instance that is serving up archived NEXRAD Level 2 data hosted on Amazon S3. The TDS provides a mechanism to query for available data files, as well as provides access to the data as native volume files, through OPeNDAP, and using its own CDMRemote protocol. Since we're using Python, we can take advantage of Unidata's Siphon package, which provides an easy API for talking to THREDDS servers.\n", "\n", "**NOTE:** Due to data charges, the TDS instance in AWS only allows access to .edu domains. For other users interested in using Siphon to access radar data, you can access recent (2 weeks') data by changing the server URL below to: http://thredds.ucar.edu/thredds/radarServer/nexrad/level2/IDD/\n", "\n", "**But first!**\n", "Bookmark these resources for when you want to use Siphon later!\n", "+ [latest Siphon documentation](http://siphon.readthedocs.org/en/latest/)\n", "+ [Siphon github repo](https://github.com/Unidata/siphon)\n", "+ [TDS documentation](http://www.unidata.ucar.edu/software/thredds/current/tds/TDS.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Just a bit of initial set-up to import necessary libraries, use inline figures and quiet some warnings." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import matplotlib\n", "import warnings\n", "from datetime import datetime, timedelta\n", "from siphon.cdmr import Dataset\n", "import matplotlib.pyplot as plt\n", "import cartopy.crs as ccrs\n", "import cartopy.feature as cf\n", "import cartopy.io.shapereader as shprd\n", "import time\n", "import sys\n", "\n", "warnings.filterwarnings(\"ignore\", category=matplotlib.cbook.MatplotlibDeprecationWarning)\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "MetPy is a Python package for meteorology (Documentation: http://metpy.readthedocs.org and GitHub: http://github.com/MetPy/MetPy). We import MetPy and use it to get the colortable and value mapping information for the NWS Reflectivity data." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from metpy.plots import ctables # For NWS colortable\n", "ref_norm, ref_cmap = ctables.registry.get_with_steps('NWSReflectivity', 5, 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First we'll create an instance of RadarServer to point to the appropriate radar server access URL." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from siphon.radarserver import RadarServer\n", "rs = RadarServer('http://thredds-aws.unidata.ucar.edu/thredds/radarServer/nexrad/level2/S3/')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define some helper functions to make working with the data easier. One takes the raw data and converts it to floating point values with the missing data points appropriately marked. The other helps with converting the polar coordinates (azimuth and range) to Cartesian (x and y)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true, "run_control": { "marked": false } }, "outputs": [], "source": [ "import numpy as np\n", "def raw_to_masked_float(var, data):\n", " # Values come back signed. If the _Unsigned attribute is set, we need to convert\n", " # from the range [-127, 128] to [0, 255].\n", " if var._Unsigned:\n", " data = data & 255\n", "\n", " # Mask missing points\n", " data = np.ma.array(data, mask=data==0)\n", "\n", " # Convert to float using the scale and offset\n", " return data * var.scale_factor + var.add_offset\n", "\n", "def polar_to_cartesian(az, rng):\n", " az_rad = np.deg2rad(az)[:, None]\n", " x = rng * np.sin(az_rad)\n", " y = rng * np.cos(az_rad)\n", " return x, y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The CDMRemote reader provides an interface that is almost identical to the usual python NetCDF interface. We pull out the variables we need for azimuth and range, as well as the data itself." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then convert the raw data to floating point values and the polar coordinates to Cartesian." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "MetPy is a Python package for meteorology (Documentation: http://metpy.readthedocs.org and GitHub: http://github.com/MetPy/MetPy). We import MetPy and use it to get the colortable and value mapping information for the NWS Reflectivity data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we plot them up using matplotlib and cartopy. We create a helper function for making a map to keep things simpler later." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def new_map(fig, lon, lat):\n", " # Create projection centered on the radar. This allows us to use x\n", " # and y relative to the radar.\n", " proj = ccrs.LambertConformal(central_longitude=lon, central_latitude=lat)\n", "\n", " # New axes with the specified projection\n", " ax = fig.add_subplot(1, 1, 1, projection=proj)\n", "\n", " # Add coastlines\n", " ax.coastlines('50m', 'black', linewidth=2, zorder=2)\n", "\n", " # Grab state borders\n", " state_borders = cf.NaturalEarthFeature(\n", " category='cultural', name='admin_1_states_provinces_lines',\n", " scale='50m', facecolor='none')\n", " ax.add_feature(state_borders, edgecolor='black', linewidth=1, zorder=3)\n", " \n", " return ax" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use the function to make a new map and plot a colormapped view of the data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Download a collection of historical data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Make a query based on a longitude, latitude point and using a time range." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "time_start=2017-04-30T12%3A00%3A00&time_end=2017-04-30T14%3A00%3A00&latitude=37.76&longitude=-99.95" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "query = rs.query()\n", "dt = datetime(2017, 4, 30, 12) # Our specified time\n", "query.lonlat_point(-99.95, 37.76).time_range(dt, dt + timedelta(hours=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Make the request to the THREDDS Data Server, which returns an instance of TDSCatalog; this handles parsing the returned XML information.\n", "\n", "The TDS helpfully finds the closest station to that point specified by the lat/lon pair. The time range we request is also queried for data files occurring during the time window. We will likely obtain multiple datasets." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "OrderedDict([('KDDC20170430_120002_V06',\n", " ),\n", " ('KDDC20170430_120607_V06',\n", " ),\n", " ('KDDC20170430_121212_V06',\n", " ),\n", " ('KDDC20170430_121817_V06',\n", " ),\n", " ('KDDC20170430_122422_V06',\n", " ),\n", " ('KDDC20170430_123026_V06',\n", " ),\n", " ('KDDC20170430_123631_V06',\n", " ),\n", " ('KDDC20170430_124237_V06',\n", " ),\n", " ('KDDC20170430_124842_V06',\n", " ),\n", " ('KDDC20170430_125447_V06',\n", " ),\n", " ('KDDC20170430_130051_V06',\n", " ),\n", " ('KDDC20170430_130655_V06',\n", " ),\n", " ('KDDC20170430_131301_V06',\n", " ),\n", " ('KDDC20170430_131852_V06',\n", " ),\n", " ('KDDC20170430_132444_V06',\n", " ),\n", " ('KDDC20170430_133021_V06',\n", " ),\n", " ('KDDC20170430_133559_V06',\n", " ),\n", " ('KDDC20170430_134124_V06',\n", " ),\n", " ('KDDC20170430_134634_V06',\n", " ),\n", " ('KDDC20170430_135145_V06',\n", " ),\n", " ('KDDC20170430_135655_V06',\n", " )])" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat = rs.get_catalog(query)\n", "cat.datasets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Grab the first dataset so that we can get the longitude and latitude of the station and make a map for plotting. We'll go ahead and specify some longitude and latitude bounds for the map." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loaded dataset successfully\n" ] }, { "data": { "image/png": "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\no8VeOlLEAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ds = list(cat.datasets.values())[0]\n", "#print (ds)\n", "process = 0\n", "ok = 0\n", "while ok == 0:\n", " ncount = 1\n", " while ncount <= 5:\n", " try:\n", " data = Dataset(ds.access_urls['CdmRemote'])\n", " except: \n", " print \"Caught an exception; will try URL again in 5 secs\"\n", " sys.stdout.flush()\n", " time.sleep (5)\n", " ncount = ncount + 1\n", " else:\n", " ok = 1\n", " ncount = 10\n", " if (ncount == 6):\n", " print \"Exceeded # of attempts to connect to remote server\"\n", " sys.stdout.flush()\n", " process = 0\n", " ok = 1\n", " else:\n", " print \"Loaded dataset successfully\"\n", " process = 1\n", "\n", "if process: \n", " fig = plt.figure(figsize=(10, 10))\n", " ax = new_map(fig, -99.22, 37.88)\n", "\n", "## Modify this to fit your case\n", "# Set limits in lat/lon space\n", " ax.set_extent([-102, -98, 36, 39])\n", "\n", "# Add ocean and land background\n", " ocean = cf.NaturalEarthFeature('physical', 'ocean', scale='50m',\n", " edgecolor='face',\n", " facecolor=cf.COLORS['water'])\n", " land = cf.NaturalEarthFeature('physical', 'land', scale='50m',\n", " edgecolor='face',\n", " facecolor=cf.COLORS['land'])\n", " ax.add_feature(ocean, zorder=-1)\n", " ax.add_feature(land, zorder=-1) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can loop over the collection of returned datasets and plot them. As we plot, we collect the returned plot objects so that we can use them to make an animated plot. We also add a timestamp for each plot." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "\n", "Loaded dataset successfully\n", "done\n" ] } ], "source": [ "meshes = []\n", "for item in sorted(cat.datasets.items()):\n", " # After looping over the list of sorted datasets, pull the actual Dataset object out\n", " # of our list of items and access over CDMRemote\n", " ds = item[1]\n", " print (ds)\n", " process = 0\n", " ok = 0\n", " while ok == 0:\n", " ncount = 1\n", " while ncount <= 5:\n", " try:\n", " data = Dataset(ds.access_urls['CdmRemote'])\n", " except: \n", " print \"Caught an exception; will try URL again in 5 secs\"\n", " sys.stdout.flush()\n", " time.sleep (5)\n", " ncount = ncount + 1\n", " else:\n", " ok = 1\n", " ncount = 10\n", " if (ncount == 6):\n", " print \"Exceeded # of attempts to connect to remote server\"\n", " sys.stdout.flush()\n", " process = 0\n", " ok = 1\n", " else:\n", " print \"Loaded dataset successfully\"\n", " process = 1\n", "\n", " if process: \n", " # Pull out the data of interest\n", " sweep = 0\n", " rng = data.variables['distanceR'][:]\n", " az = data.variables['azimuthR'][sweep]\n", " ref_var = data.variables['Reflectivity']\n", "\n", " # Convert data to float and coordinates to Cartesian\n", " ref = raw_to_masked_float(ref_var, ref_var[sweep])\n", " x, y = polar_to_cartesian(az, rng)\n", "\n", " # Plot the data and the timestamp\n", " mesh = ax.pcolormesh(x, y, ref, cmap=ref_cmap, norm=ref_norm, zorder=0)\n", " text = ax.text(0.65, 0.03, data.time_coverage_start, transform=ax.transAxes,\n", " fontdict={'size':16})\n", " \n", " # Collect the things we've plotted so we can animate\n", " meshes.append((mesh, text))\n", "print ('done')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using matplotlib, we can take a collection of ``Artists`` that have been plotted and turn them into an animation. With matplotlib , this animation can be converted to HTML5 video viewable in the notebook. This may take a minute or two." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Set up matplotlib to do the conversion to HTML5 video\n", "import matplotlib\n", "matplotlib.rcParams['animation.html'] = 'html5'\n", "#matplotlib.rcParams['animation.ffmpeg_path'] = '/usr/local/bin/ffmpeg'\n", "\n", "\n", "# Create an animation\n", "from matplotlib.animation import ArtistAnimation\n", "ArtistAnimation(fig, meshes)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.13" } }, "nbformat": 4, "nbformat_minor": 1 }