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:

  1. On the top subplot, plot date and time on the x-axis, and central sea-level pressure in hPa on the y-axis.

  2. On the bottom subplot, as in the top, but plot maximum sustained wind speed in mph on the y-axis.

  3. 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:

  1. Latitude (deg)

  2. Longitude (deg)

  3. Central SLP (hPa)

  4. Maximum wind speed (kts)

  5. 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>]
../../_images/55b0421b93f69a0adc3509ebb7395b4957576516e22ffd566aa9337c6777cdfb.png

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>]
../../_images/b617599293d9e78420189f4b3d00460f8d55ecafa5c0c4a9cdc48cec258e6562.png

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)');
../../_images/4284b3dca96ce5af7b854aafa06b45e6b74b929d2c3039680de53f3480c08f42.png

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);
../../_images/b8b5cd7ed2023650f10b30222bd248c4dddb88d1733741b20cba53a0bce0794f.png

There are still a few things we can improve:

  1. Re-cast the x-axis from strings to Datetime objects, using a handy Pandas method

  2. Re-format the x-axis labels, now that they are Datetime objects

  3. Distinguish 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()
../../_images/a2144133287406afa6710df9131cd5850199f4856bda4a808d3c7d14980a089b.png

Save the figure as a PNG

fig.savefig('Franklin2023_TimeSeries_SLP_WSPD.png')

Part 2: Plot the center of Franklin on a map

  1. Use Matplotlib and Cartopy to plot the locations of Franklin for the same time period you used in Part 1.

  2. 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);
../../_images/8708d5d588c3bf9ccdec3c82df1fa6925262274140da2231e6f06037cfc60a88.png

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);
../../_images/756bc9635d7f56e0369e804bfde2c9157e21a6ba643d2728574348b0e2efc163.png

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);
../../_images/3137beea553819ebd812a8308c2afbb610e43bf432d5f1842f7ab7b05bc98657.png

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!