from argparse import ArgumentParser
from os import environ
import numpy as np
import xarray as xr
from parcels import Field
from parcels.plotting import cartopy_colorbar
from parcels.plotting import create_parcelsfig_axis
from parcels.plotting import plotfield
try:
import matplotlib.animation as animation
from matplotlib import rc
except:
anim = None
[docs]def plotTrajectoriesFile(filename, mode='2d', tracerfile=None, tracerfield='P',
tracerlon='x', tracerlat='y', recordedvar=None, movie_forward=True,
bins=20, show_plt=True, central_longitude=0):
"""Quick and simple plotting of Parcels trajectories
:param filename: Name of Parcels-generated NetCDF file with particle positions
:param mode: Type of plot to show. Supported are '2d', '3d', 'hist2d',
'movie2d' and 'movie2d_notebook'. The latter two give animations,
with 'movie2d_notebook' specifically designed for jupyter notebooks
:param tracerfile: Name of NetCDF file to show as background
:param tracerfield: Name of variable to show as background
:param tracerlon: Name of longitude dimension of variable to show as background
:param tracerlat: Name of latitude dimension of variable to show as background
:param recordedvar: Name of variable used to color particles in scatter-plot.
Only works in 'movie2d' or 'movie2d_notebook' mode.
:param movie_forward: Boolean whether to show movie in forward or backward mode (default True)
:param bins: Number of bins to use in `hist2d` mode. See also https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist2d.html
:param show_plt: Boolean whether plot should directly be show (for py.test)
:param central_longitude: Degrees East at which to center the plot
"""
environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
try:
pfile = xr.open_dataset(str(filename), decode_cf=True)
except:
pfile = xr.open_dataset(str(filename), decode_cf=False)
lon = np.ma.filled(pfile.variables['lon'], np.nan)
lat = np.ma.filled(pfile.variables['lat'], np.nan)
time = np.ma.filled(pfile.variables['time'], np.nan)
z = np.ma.filled(pfile.variables['z'], np.nan)
mesh = pfile.attrs['parcels_mesh'] if 'parcels_mesh' in pfile.attrs else 'spherical'
if(recordedvar is not None):
record = np.ma.filled(pfile.variables[recordedvar], np.nan)
pfile.close()
if tracerfile is not None and mode != 'hist2d':
tracerfld = Field.from_netcdf(tracerfile, tracerfield, {'lon': tracerlon, 'lat': tracerlat})
plt, fig, ax, cartopy = plotfield(tracerfld)
if plt is None:
return # creating axes was not possible
titlestr = ' and ' + tracerfield
else:
spherical = False if mode == '3d' or mesh == 'flat' else True
plt, fig, ax, cartopy = create_parcelsfig_axis(spherical=spherical, central_longitude=central_longitude)
if plt is None:
return # creating axes was not possible
titlestr = ''
if cartopy:
for p in range(lon.shape[1]):
lon[:, p] = [ln if ln < 180 else ln - 360 for ln in lon[:, p]]
if mode == '3d':
from mpl_toolkits.mplot3d import Axes3D # noqa
plt.clf() # clear the figure
ax = fig.gca(projection='3d')
for p in range(len(lon)):
ax.plot(lon[p, :], lat[p, :], z[p, :], '.-')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.set_zlabel('Depth')
ax.set_title('Particle trajectories')
elif mode == '2d':
if cartopy:
ax.plot(np.transpose(lon), np.transpose(lat), '.-', transform=cartopy.crs.Geodetic())
else:
ax.plot(np.transpose(lon), np.transpose(lat), '.-')
ax.set_title('Particle trajectories' + titlestr)
elif mode == 'hist2d':
_, _, _, cs = plt.hist2d(lon[~np.isnan(lon)], lat[~np.isnan(lat)], bins=bins)
cartopy_colorbar(cs, plt, fig, ax)
ax.set_title('Particle histogram')
elif mode in ('movie2d', 'movie2d_notebook'):
ax.set_xlim(np.nanmin((lon+central_longitude+180) % 360 - 180), np.nanmax((lon+central_longitude+180) % 360 - 180))
ax.set_ylim(np.nanmin(lat), np.nanmax(lat))
plottimes = np.unique(time)
if not movie_forward:
plottimes = np.flip(plottimes, 0)
if isinstance(plottimes[0], (np.datetime64, np.timedelta64)):
plottimes = plottimes[~np.isnat(plottimes)]
else:
try:
plottimes = plottimes[~np.isnan(plottimes)]
except:
pass
b = time == plottimes[0]
def timestr(plottimes, index):
if isinstance(plottimes[index], np.timedelta64):
if plottimes[-1] > np.timedelta64(1, 'h'):
return str(plottimes[index].astype('timedelta64[h]'))
elif plottimes[-1] > np.timedelta64(1, 's'):
return str(plottimes[index].astype('timedelta64[s]'))
else:
return str(plottimes[index])
if cartopy:
scat = ax.scatter(lon[b], lat[b], s=20, color='k', transform=cartopy.crs.Geodetic())
else:
scat = ax.scatter(lon[b], lat[b], s=20, color='k')
ttl = ax.set_title('Particles' + titlestr + ' at time ' + timestr(plottimes, 0))
frames = np.arange(0, len(plottimes))
def animate(t):
b = time == plottimes[t]
scat.set_offsets(np.vstack((lon[b], lat[b])).transpose())
ttl.set_text('Particle' + titlestr + ' at time ' + timestr(plottimes, t))
if recordedvar is not None:
scat.set_array(record[b])
return scat,
rc('animation', html='html5')
anim = animation.FuncAnimation(fig, animate, frames=frames, interval=100, blit=False)
else:
raise RuntimeError('mode %s not known' % mode)
if mode == 'movie2d_notebook':
plt.close()
return anim
else:
if show_plt:
plt.show()
return plt
if __name__ == "__main__":
p = ArgumentParser(description="""Quick and simple plotting of Parcels trajectories""")
p.add_argument('mode', choices=('2d', '3d', 'hist2d', 'movie2d', 'movie2d_notebook'), nargs='?',
default='movie2d', help='Type of display')
p.add_argument('-p', '--particlefile', type=str, default='MyParticle.nc',
help='Name of particle file')
p.add_argument('-f', '--tracerfile', type=str, default=None,
help='Name of tracer file to display underneath particle trajectories')
p.add_argument('-flon', '--tracerfilelon', type=str, default='x',
help='Name of longitude dimension in tracer file')
p.add_argument('-flat', '--tracerfilelat', type=str, default='y',
help='Name of latitude dimension in tracer file')
p.add_argument('-ffld', '--tracerfilefield', type=str, default='P',
help='Name of field in tracer file')
p.add_argument('-r', '--recordedvar', type=str, default=None,
help='Name of a variable recorded along trajectory')
p.add_argument('-bins', type=int, default=20,
help='Number of bins for mode=hist2d')
args = p.parse_args()
plotTrajectoriesFile(args.particlefile, mode=args.mode, tracerfile=args.tracerfile,
tracerfield=args.tracerfilefield, tracerlon=args.tracerfilelon,
tracerlat=args.tracerfilelat, recordedvar=args.recordedvar,
bins=args.bins, show_plt=True)