Source code for tropycal.utils.cartopy_utils

r"""Adds Tropycal functionality to Cartopy GeoAxes."""

import types
import numpy as np
import scipy.ndimage as ndimage
import cartopy.crs as ccrs
import cartopy.feature as cfeature

[docs]def add_tropycal(ax): r""" Adds Tropycal plotting capability to a matplotlib.pyplot axes instance with a Cartopy projection. This axes instance must have already had a Cartopy projection added (e.g., ``projection=ccrs.PlateCarree()``). Parameters ---------- ax : cartopy.mpl.geoaxes.GeoAxes Instance of a matplotlib axes with a Cartopy projection added. Returns ------- ax The same axes instance is returned, with tropycal plotting functions from `tropycal.utils.cartopy_utils` added to it as methods. Notes ----- This function appends Tropycal plotting capability to an existing axes with a Cartopy projection. Below is an example of how to use this functionality: .. code-block:: python #Import necessary packages from tropycal import tracks, utils import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.pyplot as plt #Retrieve North Atlantic HURDATv2 dataset and store in basin variable basin = tracks.TrackDataset('north_atlantic') #Create a PlateCarree Cartopy projection proj = ccrs.PlateCarree() #Create an instance of figure and axes fig = plt.figure(figsize=(9,6),dpi=150) ax = plt.axes(projection=proj) #Plot coastlines and boundaries states = ax.add_feature(cfeature.STATES.with_scale('50m'),linewidths=0.5,linestyle='solid',edgecolor='k') countries = ax.add_feature(cfeature.BORDERS.with_scale('50m'),linewidths=1.0,linestyle='solid',edgecolor='k') coastlines = ax.add_feature(cfeature.COASTLINE.with_scale('50m'),linewidths=1.0,linestyle='solid',edgecolor='k') #Add Tropycal functionality to axes ax = utils.add_tropycal(ax) #Use the "plot_storm()" method to plot Hurricane Michael's track storm = basin.get_storm(('michael',2018)) ax.plot_storm(storm,'-',color='k') #Zoom in over the Gulf Coast ax.set_extent([-100,-70,18,37]) #Show plot and close plt.show() plt.close() """ ax.plot_storm = types.MethodType(plot_storm, ax) ax.plot_two = types.MethodType(plot_two, ax) ax.plot_cone = types.MethodType(plot_cone, ax) return ax
[docs]def plot_storm(self, storm, *args, **kwargs): r""" Plot a Storm object on the axes instance. Parameters ---------- storm : tropycal.tracks.Storm Instance of a Storm object to be plotted. Notes ----- Besides the parameters listed above, this function behaves identically to matplotlib's default `plot()` function. It is not necessary to pass a "transform" keyword argument, as this is already assumed to be ccrs.PlateCarree(). This function is already appended to an axes instance if ``ax = utils.add_tropycal(ax)`` is run beforehand. This allows this method to be called simply via ``ax.plot_storm(...)`` the same way one would call ``ax.plot(...)``. """ # Pass arguments to ax plot method self.plot(storm.lon, storm.lat, transform=ccrs.PlateCarree(), *args, **kwargs)
[docs]def plot_two(self, two_dict, days=7, **kwargs): r""" Plot a NHC Tropical Weather Outlook (TWO) on the axes instance. Parameters ---------- two_dict : dict Dictionary containing TWO areas and points. This dictionary can be retrieved from ``utils.get_two_current()`` or ``utils.get_two_archive()``. days : int Forecast range of TWO. Can be 2, 5 or 7 days. Default is 7. Other Parameters ---------------- ms : int or float, optional Marker size label for invest areas. Default is 15. linewidth : int or float, optional Linewidth of TWO area. Default is 1.5. alpha : int or float, optional Opacity of TWO area fill. Default is 0.3. zorder : int, optional Optional display order on axes of TWO areas and labels. Notes ----- It is not necessary to pass a "transform" keyword argument, as this is already assumed to be ccrs.PlateCarree(). This function is already appended to an axes instance if ``ax = utils.add_tropycal(ax)`` is run beforehand. This allows this method to be called simply via ``ax.plot_two(...)``. """ # Retrieve kwargs ms = kwargs.pop('ms', 15) alpha = kwargs.pop('alpha', 0.3) linewidth = kwargs.pop('linewidth', 1.5) zorder = kwargs.pop('zorder', None) # Format kwargs for zorder functions kwargs = {} if zorder is not None: kwargs = {'zorder': zorder} # Consistency check if days not in [2, 5, 7]: raise ValueError("'days' must have a value of 2, 5 or 7") # Store TWO colors for reference color_base = {'Low': 'yellow', 'Medium': 'orange', 'High': 'red'} # Plot areas if two_dict['areas'] is not None: for record, geom in zip(two_dict['areas'].records(), two_dict['areas'].geometries()): keys = record.attributes.keys() # Read relevant data if 'RISK2DAY' in keys or 'RISK5DAY' in keys or 'RISK7DAY' in keys: if days == 2: color = color_base.get(record.attributes['RISK2DAY'], 'yellow') elif 'RISK5DAY' in record.attributes.keys(): color = color_base.get(record.attributes['RISK5DAY'], 'yellow') else: color = color_base.get(record.attributes['RISK7DAY'], 'yellow') else: color = color_base.get(record.attributes['GENCAT'], 'yellow') # Plot area self.add_feature(cfeature.ShapelyFeature([geom], ccrs.PlateCarree()), facecolor=color, edgecolor=color, alpha=alpha, linewidth=linewidth, **kwargs) # Plot hatching self.add_feature(cfeature.ShapelyFeature([geom], ccrs.PlateCarree()), facecolor='none', edgecolor='k', linewidth=linewidth*1.5, **kwargs) self.add_feature(cfeature.ShapelyFeature([geom], ccrs.PlateCarree()), facecolor='none', edgecolor=color, linewidth=linewidth, **kwargs) # Plot points if two_dict['points'] is not None: for record, point in zip(two_dict['points'].records(), two_dict['points'].geometries()): lon = (list(point.coords)[0][0]) lat = (list(point.coords)[0][1]) # Determine if 5 or 7 day outlook exists prob_2day = record.attributes['PROB2DAY'].replace(" ", "") risk_2day = record.attributes['RISK2DAY'].replace(" ", "") if 'PROB5DAY' in record.attributes.keys(): prob_5day = record.attributes['PROB5DAY'].replace(" ", "") risk_5day = record.attributes['RISK5DAY'].replace(" ", "") else: prob_5day = record.attributes['PROB7DAY'].replace(" ", "") risk_5day = record.attributes['RISK7DAY'].replace(" ", "") # Label area if days == 2: color = color_base.get(risk_2day, 'yellow') text = prob_2day else: color = color_base.get(risk_5day, 'yellow') text = prob_5day self.plot(lon, lat, 'X', ms=ms, color=color, mec='k', mew=1.5*(ms/15.0), transform=ccrs.PlateCarree(), **kwargs)
[docs]def plot_cone(self, cone, plot_center_line=False, **kwargs): r""" Plots a Tropycal derived National Hurricane Center (NHC) cone of uncertainty. Parameters ---------- cone : dict or xarray.Dataset Cone of uncertainty generated from ``utils.generate_nhc_cone()``. plot_center_line : bool Determine whether to plot cone center line. Default is False. Other Parameters ---------------- fillcolor : str Color to fill the cone in. Default is 'white'. linecolor : str Color of outer edge of cone. Default is 'black'. linewidth : int or float Linewidth of outer edge of cone. Default is 1.0. alpha : int or float Fill opacity of cone. Default is 0.6. zorder : int or float Optional display order on axes of the cone and center line. center_linecolor : str Color of center line. Default is 'black'. Ignored if plot_center_line is False. center_linewidth : int or float Linewidth of center line. Default is 2.0. Ignored if plot_center_line is False. center_linestyle : str Linestyle of center line. Default is 'solid'. Ignored if plot_center_line is False. Notes ----- It is not necessary to pass a "transform" keyword argument, as this is already assumed to be ccrs.PlateCarree(). This function is already appended to an axes instance if ``ax = utils.add_tropycal(ax)`` is run beforehand. This allows this method to be called simply via ``ax.plot_cone(...)``. """ # Retrieve kwargs fillcolor = kwargs.pop('fillcolor', 'w') linecolor = kwargs.pop('linecolor', 'k') linewidth = kwargs.pop('linewidth', 1.0) alpha = kwargs.pop('alpha', 0.6) zorder = kwargs.pop('zorder', None) center_linecolor = kwargs.pop('center_linecolor', 'k') center_linewidth = kwargs.pop('center_linewidth', 2.0) center_linestyle = kwargs.pop('center_linestyle', 'solid') # Format kwargs for zorder functions kwargs = {} if zorder is not None: kwargs = {'zorder': zorder} # Contour fill cone cone_lon_2d = cone['lon2d'] if 'lon2d' in cone.keys() else cone['grid_lon'] cone_lat_2d = cone['lat2d'] if 'lat2d' in cone.keys() else cone['grid_lat'] cone_2d = cone['cone'] cone_2d = ndimage.gaussian_filter(cone_2d, sigma=0.5, order=0) self.contourf(cone_lon_2d, cone_lat_2d, cone_2d, [0.99, 1.1], colors=[fillcolor, fillcolor], alpha=alpha, transform=ccrs.PlateCarree(), **kwargs) self.contour(cone_lon_2d, cone_lat_2d, cone_2d, [0.99], linewidths=linewidth, colors=linecolor, transform=ccrs.PlateCarree(), **kwargs) # Plot center line if plot_center_line: self.plot(cone['center_lon'], cone['center_lat'], color=center_linecolor, linewidth=center_linewidth, transform=ccrs.PlateCarree(), linestyle=center_linestyle,**kwargs)