COMPLETED Franklin notebook
Contents
COMPLETED Franklin notebook¶
Overview¶
In this notebook, you will create visualizations of tropical cyclone data, using Matplotlib and Cartopy.
Imports¶
Task
Import the necessary libraries in the cell below.
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
Part 1: Minimum central sea-level pressure and maximum wind speed of Hurricane Franklin (2023)¶
Search for Franklin’s central sea-level pressure and maximum wind speed, for at least eight distinct times since the storm was named. Use Matplotlib and create a single Figure with two subplots (i.e., Axes), one on top of the other:
On the top subplot, plot date and time on the x-axis, and central sea-level pressure in hPa on the y-axis.
On the bottom subplot, as in the top, but plot maximum sustained wind speed in mph on the y-axis.
Save your figure as a PNG
Task
Write your code in the cell below.
Manually create the following lists, corresponding to 2100 UTC data for the period 8/21 through 8/28:
Latitude (deg)
Longitude (deg)
Central SLP (hPa)
Maximum wind speed (kts)
Date and time (format: YYYY-MM-DD-HH; time zone: UTC) Source: NHC archived advisories for Franklin
lat = [14.3, 15.8, 19.8, 22.4, 21.9, 23.8, 25.9, 28.5]
lon = [-70.1, -71.4, -70.6, -68.9, -67.0, -67.5, -70.0, -71.0]
slp = [1002, 1002, 1004, 998, 1003, 982, 970, 937]
wspd = [45, 35, 35, 50, 45, 75, 85, 125]
dattim = ['2023-08-21-21', '2023-08-22-21', '2023-08-23-21', '2023-08-24-21',
'2023-08-25-21', '2023-08-26-21', '2023-08-27-21', '2023-08-28-21']
Create a Figure
with two subplots (aka, Axes
) and make two line graphs. On the top (bottom) Axes
, plot SLP (max wind) versus date/time.
fig = plt.figure(figsize=(12,9))
ax1 = fig.add_subplot (2,1,1)
ax1.plot(dattim, slp)
ax2 = fig.add_subplot(2,1,2)
ax2.plot(dattim, wspd)
[<matplotlib.lines.Line2D at 0x1468d7c308d0>]

A quick and easy way to make the plot better looking is to import and apply the Seaborn package.
import seaborn as sns
sns.set()
fig = plt.figure(figsize=(12,9))
ax1 = fig.add_subplot (2,1,1)
ax1.plot(dattim, slp)
ax2 = fig.add_subplot(2,1,2)
ax2.plot(dattim, wspd)
[<matplotlib.lines.Line2D at 0x1468c234b710>]

Add a title and axis labels
fig = plt.figure(figsize=(12,9))
fig.suptitle('Franklin (2023) 2100 UTC Min. SLP, Max. Wind, 8/21-8/28', fontsize=16)
ax1 = fig.add_subplot (2,1,1)
ax1.plot(dattim, slp)
ax1.set_xlabel('Date and time')
ax1.set_ylabel('SLP (hPa)')
ax2 = fig.add_subplot(2,1,2)
ax2.plot(dattim, wspd)
ax2.set_xlabel('Date and time')
ax2.set_ylabel('Windspeed (kts)');

A better visualization:
Rather than placing SLP and wind speed in their own subplots, let’s have them share a single subplot. We will need to add a legend, and also have a separate y-axis for each variable.
fig = plt.figure(figsize=(12,9))
fig.suptitle('Franklin (2023) 2100 UTC Min. SLP, Max. Wind, 8/21-8/28', fontsize=16)
ax1 = fig.add_subplot (1,1,1)
ax1_color='blue'
ax1.plot(dattim, slp, label='SLP', color=ax1_color)
ax1.set_xlabel('Date and time')
ax1.set_ylabel('SLP (hPa)', color=ax1_color)
ax1.tick_params(axis='y', labelcolor=ax1_color)
ax2_color='red'
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
ax2.plot(dattim, wspd, label='Windspeed', color=ax2_color)
ax2.set_ylabel('Windspeed (kts)', color=ax2_color) # we already handled the x-label with ax1
ax2.tick_params(axis='y', labelcolor=ax2_color);

There are still a few things we can improve:
Re-cast the x-axis from strings to
Datetime
objects, using a handy Pandas methodRe-format the x-axis labels, now that they are
Datetime
objectsDistinguish the y-axis gridlines
import pandas as pd
from matplotlib.dates import DateFormatter, AutoDateLocator,HourLocator,DayLocator,MonthLocator
dattim_dt = pd.to_datetime(dattim,format="%Y-%m-%d-%H")
fig = plt.figure(figsize=(12,9))
fig.suptitle('Franklin (2023) 2100 UTC Min. SLP, Max. Wind, 8/21-8/28', fontsize=16)
ax1 = fig.add_subplot (1,1,1)
ax1_color='blue'
ax1.set_ylim(900,1020)
ax1.plot(dattim_dt, slp, label='SLP', color=ax1_color)
ax1.set_xlabel('Date')
ax1.set_ylabel('SLP (hPa)', color=ax1_color)
ax1.tick_params(axis='y', labelcolor=ax1_color)
ax1.grid(color=ax1_color, axis='y', linestyle='dashed', linewidth=0.5)
ax1.xaxis.set_major_locator(DayLocator(interval=1))
dateFmt = DateFormatter('%b %d')
ax1.xaxis.set_major_formatter(dateFmt)
ax2_color='red'
ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis
ax2.set_ylim(0, 160)
ax2.plot(dattim_dt, wspd, label='Windspeed', color=ax2_color)
ax2.set_ylabel('Windspeed (kts)', color=ax2_color) # we already handled the x-label with ax1
ax2.tick_params(axis='y', labelcolor=ax2_color)
ax2.grid(color=ax2_color, axis='y', linestyle='dotted', linewidth=0.3)
# ask matplotlib for the plotted objects and their labels
lines, labels = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines + lines2, labels + labels2, loc=0);
#ax1.legend()

Save the figure as a PNG
fig.savefig('Franklin2023_TimeSeries_SLP_WSPD.png')
Part 2: Plot the center of Franklin on a map¶
Use Matplotlib and Cartopy to plot the locations of Franklin for the same time period you used in Part 1.
Save your figure as a PNG
Task
Write your code in the cell below.
Define an object pointing to the Cartopy coordinate reference system in which the dataset is based. Since the data is in lat-lon coordinates, we’ll use PlateCarree
.
projData = ccrs.PlateCarree()
Define the bounds over which to plot the data
mapBounds = [-90,-50,10,30]
Create the figure
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot (projection=projData)
ax.set_extent(mapBounds, crs=projData)
gl = ax.gridlines(
draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--'
)
ax.set_facecolor(cfeature.COLORS['water'])
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle='--')
ax.add_feature(cfeature.LAKES, alpha=0.5)
ax.add_feature(cfeature.STATES)
ax.add_feature(cfeature.RIVERS)
ax.set_xlabel('Latitude')
ax.set_title('Daily 2100 UTC locations of tropical cyclone Franklin, 8/21-8/28 2023');
ax.plot(lon,lat);

Let’s improve the look of the figure by making the track line more conspicuous:
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot (projection=projData)
ax.set_extent(mapBounds, crs=projData)
gl = ax.gridlines(
draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--'
)
ax.set_facecolor(cfeature.COLORS['water'])
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle='--')
ax.add_feature(cfeature.LAKES, alpha=0.5)
ax.add_feature(cfeature.STATES)
ax.add_feature(cfeature.RIVERS)
ax.set_xlabel('Latitude')
ax.set_title('Daily 2100 UTC locations of tropical cyclone Franklin, 8/21-8/28 2023');
ax.plot(lon,lat, color='darkgreen', marker='o', linestyle='dashed',
linewidth=2, markersize=12);

We’ll further improve the figure by using tropical cyclone symbols to mark the locations; the tcmarkers package works nicely!
import tcmarkers
fig = plt.figure(figsize=(12,9))
ax = fig.add_subplot (projection=projData)
ax.set_extent(mapBounds, crs=projData)
gl = ax.gridlines(
draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--'
)
ax.set_facecolor(cfeature.COLORS['water'])
ax.add_feature(cfeature.LAND)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle='--')
ax.add_feature(cfeature.LAKES, alpha=0.5)
ax.add_feature(cfeature.STATES)
ax.add_feature(cfeature.RIVERS)
ax.set_xlabel('Latitude')
ax.set_title('Daily 2100 UTC locations of tropical cyclone Franklin, 8/21-8/28 2023');
ax.plot(lon,lat, color='darkgreen')
marker_kwargs = {'s': 40, 'color':'darkgreen', 'edgecolor':'darkgreen'}
for idx, value in enumerate (wspd):
if (value < 34):
sym = tcmarkers.TD
if (lat[idx] < 0):
sym = tcmarkers.SH_TD
elif (value < 64):
sym = tcmarkers.TS
if (lat[idx] < 0):
sym = tcmarkers.SH_TS
else:
sym = tcmarkers.HU
if (lat[idx] < 0):
sym = tcmarkers.SH_HU
ax.scatter(lon[idx], lat[idx], marker=sym, **marker_kwargs);

Save the figure as a PNG
fig.savefig('Franklin2023_TrackMap.png')
REMINDER
Remember to save, close and shutdown your notebook when you are not actively developing it!