-
Lionel GUEZ authored
`settings.mk` in GNUmakefile. Bug fix in procedure `dispatch_snapshot`: an isolated eddy is a valid eddy. In script `plot_snapshot.py`, in function snaphot, replace argument ax with argument `new_figure` so the user does not have to call figure and `axes(projection = projection)`. Add argument light to function snapshot so we can call snapshot with light set to False for a second snapshot. Add a test to only read `h.nc` when we need it. In script `read_overlap.py`, print more information: number of nodes, number of edges, number of nodes with at least one successor, number of nodes with at least one predecessor, splitting events, merging events. In script `stat.py`, use the convention that shapefiles are grouped in a directory instead of identifying a set of shapefiles by the end of their basename. In main program `test_successive_overlap`, we know that `delta_in` for snapshot 1 and `delta_out` for snapshot 2 are `huge(0)` so do not test this. Print eddy identifiers in a single line. Add the printing of identifiers of isolated eddies.
Lionel GUEZ authored`settings.mk` in GNUmakefile. Bug fix in procedure `dispatch_snapshot`: an isolated eddy is a valid eddy. In script `plot_snapshot.py`, in function snaphot, replace argument ax with argument `new_figure` so the user does not have to call figure and `axes(projection = projection)`. Add argument light to function snapshot so we can call snapshot with light set to False for a second snapshot. Add a test to only read `h.nc` when we need it. In script `read_overlap.py`, print more information: number of nodes, number of edges, number of nodes with at least one successor, number of nodes with at least one predecessor, splitting events, merging events. In script `stat.py`, use the convention that shapefiles are grouped in a directory instead of identifying a set of shapefiles by the end of their basename. In main program `test_successive_overlap`, we know that `delta_in` for snapshot 1 and `delta_out` for snapshot 2 are `huge(0)` so do not test this. Print eddy identifiers in a single line. Add the printing of identifiers of isolated eddies.
plot_snapshot.py 7.45 KiB
#!/usr/bin/env python3
"""Plots outermost contours and max-speed contours.
Red for anti-cyclones, blue for cyclones.
Without the --light option. Empty circles for extrema
with a valid outermost contour, empty squares for other
extrema. Squares on outermost contour for a well-defined but invalid
outermost contour. Crosses on outermost contour for a valid outermost
contour but with no distinct max-speed contour. Filled circles on
outermost contour and max-speed contour for a valid outermost contour
with a distinct max-speed contour.
With the --light option. Crosses for extrema with a valid outermost
contour.
This scripts takes about 30 s of CPU for a 90° by 90° window, about 4
mn for a 180° by 180° window.
"""
import shapefile
import numpy as np
import matplotlib.pyplot as plt
import netCDF4
import math
import itertools
import cartopy.crs as ccrs
from os import path
def snaphot(directory, dashed = False, light = False, new_figure = False):
"""Plots extrema, outermost contours and max-speed contours.
directory: containing the three shapefiles.
dashed: boolean."""
reader_extr = shapefile.Reader(path.join(directory, "extremum"))
reader_outer = shapefile.Reader(path.join(directory, "outermost_contour"))
try:
reader_m_s = shapefile.Reader(path.join(directory, "max_speed_contour"))
except shapefile.ShapefileException:
print("Shapefile max_speed_contour not found. "
"Max-speed contours will not be plotted.")
m_s_iterShapes = itertools.repeat(None)
else:
m_s_iterShapes = reader_m_s.iterShapes()
if new_figure:
plt.figure()
ax = plt.axes(projection = projection)
else:
ax = plt.gca()
for shape_rec_extr, shape_outer, shape_m_s \
in zip(reader_extr, reader_outer.iterShapes(), m_s_iterShapes):
points = shape_rec_extr.shape.points[0]
if args.window:
points[0] += math.ceil((llcrnrlon - points[0]) / 360) * 360
# (in [llcrnrlon, llcrnrlon + 2 pi[)
if not args.window or (points[0] <= urcrnrlon
and llcrnrlat <= points[1] <= urcrnrlat):
if shape_rec_extr.record.cyclone == 0:
# Anti-cyclone
color = "red"
else:
color = "blue"
lines = ax.plot(points[0], points[1], markersize = 10,
color = color, fillstyle = "none",
transform = src_crs)
if shape_rec_extr.record.valid == 1:
if light:
lines[0].set_marker("+")
else:
lines[0].set_marker("o")
elif not light:
# Invalid outermost contour
lines[0].set_marker("s")
if not light:
ax.annotate(str(shape_rec_extr.record.eddy_index),
projection.transform_point(points[0], points[1],
src_crs),
xytext = (3, 3), textcoords = "offset points")
if shape_outer.shapeType != shapefile.NULL and not light \
or shape_rec_extr.record.valid:
points = np.array(shape_outer.points)
lines = ax.plot(points[:, 0], points[:, 1], color = color,
transform = src_crs)
if not light:
if shape_rec_extr.record.valid == 0:
# Invalid outermost contour
lines[0].set_marker("s")
lines[0].set_fillstyle("none")
elif shape_m_s == None \
or shape_m_s.shapeType == shapefile.NULL:
lines[0].set_marker("x")
else:
lines[0].set_marker("o")
if dashed: lines[0].set_linestyle("dashed")
if shape_m_s != None and shape_m_s.shapeType != shapefile.NULL:
points = np.array(shape_m_s.points)
if shape_rec_extr.record.cyclone == 0:
# Anti-cyclone
color = "magenta"
else:
color = "cyan"
lines = ax.plot(points[:, 0], points[:, 1], color = color,
transform = src_crs)
if not light: lines[0].set_marker("o")
if dashed: lines[0].set_linestyle("dashed")
ax.gridlines(draw_labels = True)
ax.coastlines()
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--velocity", help = "plot velocity field",
action = "store_true")
parser.add_argument("-s", "--scale", default = 20, type = float,
help = "scale of arrows for the velocity field")
parser.add_argument("-g", "--grid", help = "plot grid",
action = "store_true")
parser.add_argument("-w", "--window", help = "choose a limited plot window",
action = "store_true")
parser.add_argument("-l", "--light", help = "lighter plot",
action = "store_true")
parser.add_argument("-d", "--dashed", action = "store_true",
help = "dashed linestyle, useful for a second snapshot")
parser.add_argument("directory", help = "containing the three shapefiles")
args = parser.parse_args()
if args.grid or args.velocity:
with netCDF4.Dataset("h.nc") as f:
longitude = f.variables["longitude"][:]
latitude = f.variables["latitude"][:]
if args.window:
l = input("llcrnrlon, llcrnrlat, urcrnrlon, "
"urcrnrlat = ? ").split(sep = ",")
llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat = [float(v) for v in l]
if urcrnrlon - llcrnrlon > 360:
sys.exit("bad values of urcrnrlon and llcrnrlon")
if args.grid or args.velocity:
longitude += np.ceil((llcrnrlon - longitude) / 360) * 360
# (in [llcrnrlon, llcrnrlon + 2 pi[)
lon_mask = longitude <= urcrnrlon
lat_mask = np.logical_and(latitude >= llcrnrlat,
latitude <= urcrnrlat)
elif args.grid or args.velocity:
lon_mask = np.ones(len(longitude), dtype = bool)
lat_mask = np.ones(len(latitude), dtype = bool)
plt.figure()
src_crs = ccrs.PlateCarree()
projection = ccrs.PlateCarree()
##projection = ccrs.NorthPolarStereo()
ax = plt.axes(projection = projection)
if args.grid:
lon_2d, lat_2d = np.meshgrid(longitude[lon_mask], latitude[lat_mask])
ax.plot(lon_2d.reshape(-1), lat_2d.reshape(-1), transform = src_crs,
marker = "+", color = "gray", linestyle = "None")
snaphot(args.directory, args.dashed, args.light)
if args.velocity:
with netCDF4.Dataset("uv.nc") as f:
quiver_return = ax.quiver(longitude[lon_mask], latitude[lat_mask],
f["ugos"][0, lat_mask][:, lon_mask],
f["vgos"][0, lat_mask][:, lon_mask],
scale = args.scale, scale_units = "width",
transform = src_crs)
plt.quiverkey(quiver_return, 0.9, 0.9, 1, r"1 m s$^{-1}$",
coordinates = "figure")
plt.show()