{
"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": [
"
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", "