{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Cartopy 2: NYS Mesonet Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### In this notebook, we'll use Cartopy, Matplotlib, Datetime, and Pandas to visualize data from the New York State Mesonet, headquartered right here at UAlbany." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "from cartopy import crs as ccrs\n", "from cartopy import feature as cfeature\n", "from datetime import datetime" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create a regional map, centered over NYS, and add in some geographic features.\n", "#### Be patient: this may take a minute or so to plot, depending on the resolution of the Natural Earth shapefile features you are adding! \n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create the figure\n", "#### For a quick look, let's just choose the coarsest (110,000,000:1) Natural Earth shapefiles set." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "# Set the domain for defining the plot region.\n", "latN = 45.2\n", "latS = 40.2\n", "lonW = -80.0\n", "lonE = -71.5\n", "cLat = (latN + latS)/2\n", "cLon = (lonW + lonE )/2\n", "\n", "\n", "proj = ccrs.LambertConformal(central_longitude=cLon, central_latitude=cLat)\n", "\n", "res = '110m' # Coarsest and quickest to display; other options are '10m' (slowest) and '50m'.\n", "\n", "fig = plt.figure(figsize=(11,8.5),dpi=125)\n", "ax = plt.subplot(1,1,1,projection=proj)\n", "ax.set_extent ([lonW,lonE,latS,latN])\n", "ax.add_feature(cfeature.COASTLINE.with_scale(res))\n", "ax.add_feature (cfeature.STATES.with_scale(res));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Plot some data on the map. We'll use Pandas to read in the file containing the most recent NYS Mesonet obs.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "df = pd.read_csv('http://www.atmos.albany.edu/products/nysm/nysm_latest.csv')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### View the first and last five lines of this `DataFrame`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Examine the column names." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.columns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create objects pointing to some columns of interest. In Pandas, we can refer to columns by using a \".\" in addtion to \"[]\" in most circumstances, though not if a column name starts with a number, nor if there are spaces in the column name." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "stid = df.station\n", "lat = df.lat\n", "lon = df.lon\n", "tmp2 = df['temp_2m [degC]'] # Use brackets due to the presence of a space in the column name\n", "tmp9 = df['temp_9m [degC]']\n", "time = df.time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will plot one of the variables on a map later on in this notebook. In case we want to go back later and pick a different variable to plot (e.g. 9 m temperature), let's define a generic object name. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "param = tmp2 # replace with tmp9, e.g., if you want to plot 9 m temperature" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "param" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's look at the time object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "time" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The times are the same for all stations, so let's just pull out one of them, and then create some formatted datetime strings out of it." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "timeString = time[0]\n", "timeString" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The `timeString` above could make for a perfectly fine part of an informative title for a map, but let's create a string of the form \"Month Day, Year, HourMin UTC\" (e.g., *Mar 30, 2021, 0120 UTC* )\n", "First, make a `datetime` object from the string, using `strptime` with a format that matches that of the string." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "timeObj = datetime.strptime(timeString,\"%Y-%m-%d %H:%M:%S\")\n", "timeObj" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, make a `string` object from the `datetime` object, using `strftime` with a format that matches what we want to use in the map's title." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "titleString = datetime.strftime(timeObj,\"%B %d %Y, %H%M UTC\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "titleString" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create a scatterplot to show the locations of each NYS Mesonet site using Matplotlib's `scatter` method. This method accepts an entire array of lon-lat values." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax.set_title ('New York State Mesonet Site Locations')\n", "ax.scatter(lon,lat,s=9,c='r',edgecolor='black',alpha=0.75,transform=ccrs.PlateCarree())\n", "# Plot the figure, now with the sites plotted\n", "fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Did you notice the `transform` argument? Since we are plotting on a Lambert Conformal-projected map, which uses a Cartesian x-y coordinate system where each point is equally separated in meters, we need to convert, or *transform*, the lat-lon coordinates into their equivalent coordinates in our chosen projection. We use the `transform` argument, and assign its value to the coordinate system that our lat-lon array is derived from." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Next, plot the site IDs, using Matplotlib's `text` method. This method only accepts a single value for its x and y coordinates, so we need to loop over all the values in the arrays." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall that in a Python `list`, one can use the `enumerate` function to set a numerical value to be used as a counter. We can use the same technique on a Pandas `series` object:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for count, site in enumerate(stid):\n", " print (count, site)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll repeat the enumeration below; instead of printing out the counter value and its associated list element name, we'll pass them in directly to Matplotlib's `ax.text` function:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for count, site in enumerate(stid):\n", " ax.text(lon[count],lat[count],site,horizontalalignment='right',transform=ccrs.PlateCarree(),fontsize=7)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's attempt to plot the site locations again, but this time we'll **omit** the `transform` argument in `ax.scatter` and `ax.text`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(11,8.5),dpi=125)\n", "ax = plt.subplot(1,1,1,projection=proj)\n", "ax.set_extent ([lonW,lonE,latS,latN])\n", "ax.add_feature(cfeature.COASTLINE.with_scale(res))\n", "ax.add_feature (cfeature.STATES.with_scale(res))\n", "ax.set_title ('New York State Mesonet Site Locations')\n", "ax.scatter(lon,lat,s=9,c='r',edgecolor='black',alpha=0.75)\n", "for count, site in enumerate(stid):\n", " ax.text(lon[count],lat[count],site,horizontalalignment='right',fontsize=7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### What do you think happened here?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Exercise

\n", " Create a new figure which plots the current 2m temperature at the NYSM sites. Include the current date/time to the title.\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "# %load '/spare11/atm350/common/mar07/mar07.py'\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### We could plot additional variables, and in so doing create a standard surface station plot, but we would find it challenging to align all the variables around the points corresponding to the NYS Mesonet sites. Fortunately, Metpy's `StationPlot` method takes care of this, and we will be using it in upcoming notebooks!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Sneak preview: [Latest NYSM Surface Map](http://www.atmos.albany.edu/facstaff/ktyle/nysm/sfcmap.png)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 Jan. 2024 Environment", "language": "python", "name": "jan24" }, "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.11.7" } }, "nbformat": 4, "nbformat_minor": 4 }