-"""
-@author: Hamid Ali Syed
-@email: hamidsyed37[at]gmail[dot]com
-"""
-
-import os
-
-import cartopy.crs as ccrs
-import cartopy.feature as feat
-import cmweather ## noqa
-import matplotlib.pyplot as plt
-import numpy as np
-import pyart ##noqa
-from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER
-from matplotlib.ticker import NullFormatter
-from netCDF4 import num2date
-
-# from . import register_colormap
-
-# Cmaps config
-CAPPI_CMAPS = {
- "REF": {
- "cmap": "SyedSpectral",
- "vmin": -20,
- "vmax": 70,
- "title": "Max-Z",
- "unit": "dBZ",
- "keywords": [
- "REF",
- "DBZH",
- "reflectivity",
- "DBZ",
- "ref",
- "DBZV",
- "DBZC",
- "refh",
- "Z",
- ],
- },
- "VEL": {
- "cmap": cmweather.cm.NWSVel,
- "vmin": -50,
- "vmax": 50,
- "title": "Max-V",
- "unit": "m/s",
- "keywords": [
- "VELH",
- "velocity",
- "VEL",
- "V",
- "radial_velocity",
- "VELOCITY",
- "VELV",
- "vel",
- "velh",
- ],
- },
- "WIDTH": {
- "cmap": cmweather.cm.NWS_SPW,
- "vmin": 0,
- "vmax": 4,
- "title": "Max-W",
- "unit": "m/s",
- "keywords": [
- "WIDTH",
- "WIDTHH",
- "SPW",
- "W",
- "spectrum_width",
- "width",
- "spectrum_width",
- "WIDTHV",
- ],
- },
-}
-
-
-def get_cmap_params(keyword):
- for k, v in CAPPI_CMAPS.items():
- if any([kw in keyword for kw in v["keywords"]]):
- v["name"] = k
- return v
- else:
- raise KeyError(f"'{keyword}' does not match the defined grid variable")
-
-
-def plot_crosshair(ax_xy):
- background_color = ax_xy.get_facecolor()
- if sum(background_color[:3]) / 3 > 0.5:
- color = "k"
- else:
- color = "w"
- ax_xy.plot([0, 0], [-10e3, 10e3], color=color)
- ax_xy.plot([-10e3, 10e3], [0, 0], color=color)
-
-
-
-
[docs]
-
def plot_cappi(
-
grid,
-
moment,
-
cmap=None,
-
vmin=None,
-
vmax=None,
-
title=None,
-
colorbar=True,
-
range_rings=True,
-
crosshair=True,
-
dpi=100,
-
show_progress=True,
-
savedir=None,
-
show_figure=True,
-
**kwargs,
-
):
-
"""Plots CAPPI
-
grid: pyart grid object,
-
moment(str): radar moment e.g., "REF", "VEL", "WIDTH"
-
cmap: matplotlib colormap, optional
-
vmin: minimum value for color scaling, optional
-
vmax: maximum value for color scaling, optional
-
title: plot title, optional
-
colorbar: bool, plot colorbar or not, (default: True), optional
-
range_rings: bool, (50 km interval), (default: True), optional
-
crosshair: bool, (default: True), optional
-
dpi: int, (default: 100), optional
-
show_progress: bool, (default: True)
-
savedir: string, path to save the plot, optional
-
"""
-
-
try:
-
param = get_cmap_params(keyword=moment)
-
max_c = grid.fields[param["name"]]["data"].max(axis=0)
-
max_x = grid.fields[param["name"]]["data"].max(axis=1)
-
max_y = grid.fields[param["name"]]["data"].max(axis=2).T
-
except KeyError:
-
print(f"Error: '{moment}' does not match the defined moment")
-
-
trgx = grid.x["data"]
-
trgy = grid.y["data"]
-
trgz = grid.z["data"]
-
-
max_height = int(np.floor(grid.z["data"].max()) / 1e3)
-
sideticks = np.arange(max_height / 4, max_height + 1, max_height / 4).astype(int)
-
-
if cmap is None:
-
cmap = param["cmap"]
-
# cmap.set_under(color="none")
-
if vmin is None:
-
vmin = param["vmin"]
-
if vmax is None:
-
vmax = param["vmax"]
-
if title is None:
-
title = f"Max-{moment.upper()}"
-
-
def plot_range_rings(ax_xy):
-
background_color = ax_xy.get_facecolor()
-
if sum(background_color[:3]) / 3 > 0.5:
-
color = "k"
-
else:
-
color = "w"
-
[
-
ax_xy.plot(
-
r * np.cos(np.arange(0, 360) * np.pi / 180),
-
r * np.sin(np.arange(0, 360) * np.pi / 180),
-
color=color,
-
ls="-",
-
linewidth=0.5,
-
alpha=0.5,
-
)
-
for r in np.arange(5e4, np.floor(trgx.max()) + 1, 5e4)
-
]
-
-
if show_progress:
-
print("...............................")
-
figtime = num2date(grid.time["data"], grid.time["units"])[0].strftime(
-
"%Y%m%d%H%M%S"
-
)
-
print(f"Plotting {title} {figtime}")
-
print("...............................\n")
-
else:
-
None
-
-
lat_0 = grid.origin_latitude["data"][0]
-
lon_0 = grid.origin_longitude["data"][0]
-
proj = ccrs.LambertAzimuthalEqualArea(lon_0, lat_0)
-
-
# FIG
-
fig = plt.figure(figsize=[10.3, 10], tight_layout=True)
-
left, bottom, width, height = 0.1, 0.1, 0.6, 0.2
-
ax_xy = plt.axes((left, bottom, width, width), projection=proj)
-
ax_x = plt.axes((left, bottom + width, width, height))
-
ax_y = plt.axes((left + width, bottom, height, width))
-
ax_cnr = plt.axes((left + width, bottom + width, left + left, height))
-
if colorbar:
-
ax_cb = plt.axes((left + width + height + 0.02, bottom, 0.02, width))
-
-
# set axis label formatters
-
ax_x.xaxis.set_major_formatter(NullFormatter())
-
ax_y.yaxis.set_major_formatter(NullFormatter())
-
ax_cnr.yaxis.set_major_formatter(NullFormatter())
-
ax_cnr.xaxis.set_major_formatter(NullFormatter())
-
ax_x.set_ylabel("Height AMSL (km)", size=13)
-
ax_y.set_xlabel("Height AMSL (km)", size=13)
-
-
# draw CAPPI
-
plt.sca(ax_xy)
-
xy = ax_xy.pcolormesh(trgx, trgy, max_c, cmap=cmap, vmin=vmin, vmax=vmax, **kwargs)
-
-
def map_features(ax_xy):
-
background_color = ax_xy.get_facecolor()
-
if sum(background_color[:3]) / 3 > 0.5:
-
color = "k"
-
else:
-
color = "w"
-
gl = ax_xy.gridlines(
-
crs=ccrs.PlateCarree(),
-
linewidth=1,
-
alpha=0.5,
-
linestyle="--",
-
draw_labels=True,
-
)
-
gl.xlabels_top = False
-
gl.ylabels_left = True
-
gl.ylabels_bottom = True
-
gl.ylabels_right = False
-
gl.xlines = False
-
gl.ylines = False
-
gl.xformatter = LONGITUDE_FORMATTER
-
gl.yformatter = LATITUDE_FORMATTER
-
gl.xlabel_style = {
-
"color": color,
-
}
-
gl.ylabel_style = {
-
"color": color,
-
}
-
-
# ax_xy.add_feature(shape_feature)
-
ax_xy.add_feature(feat.COASTLINE, alpha=0.8, lw=1, ec=color)
-
ax_xy.add_feature(feat.BORDERS, alpha=0.7, lw=0.7, ls="--", ec=color)
-
ax_xy.add_feature(
-
feat.STATES.with_scale("10m"), alpha=0.6, lw=0.5, ls=":", ec=color
-
)
-
-
map_features(ax_xy)
-
-
if range_rings:
-
plot_range_rings(ax_xy)
-
-
ax_xy.set_xlim(trgx.min(), trgx.max())
-
ax_xy.set_ylim(trgx.min(), trgx.max())
-
-
# plot crosshair
-
if crosshair:
-
plot_crosshair(ax_xy)
-
-
# draw colorbar
-
if colorbar:
-
cb = plt.colorbar(xy, cax=ax_cb)
-
cb.set_label(param["unit"], size=15)
-
-
plt.sca(ax_x)
-
plt.pcolormesh(trgx / 1e3, trgz / 1e3, max_x, cmap=cmap, vmin=vmin, vmax=vmax)
-
# plt.ylim(0,20)
-
plt.yticks(sideticks)
-
# plt.grid(axis='y')
-
ax_x.set_xlim(trgx.min() / 1e3, trgx.max() / 1e3)
-
-
plt.sca(ax_y)
-
plt.pcolormesh(trgz / 1e3, trgy / 1e3, max_y, cmap=cmap, vmin=vmin, vmax=vmax)
-
# plt.xlim(0,20)
-
-
ax_y.set_xticks(sideticks)
-
# plt.grid(axis='x')
-
ax_y.set_ylim(trgx.min() / 1e3, trgx.max() / 1e3)
-
-
plt.sca(ax_cnr)
-
plt.tick_params(
-
axis="both", # changes apply to the x-axis
-
which="both", # both major and minor ticks are affected
-
bottom=False, # ticks along the bottom edge are off
-
top=False, # ticks along the top edge are off
-
left=False,
-
right=False,
-
labelbottom=False,
-
)
-
-
# labels along the bottom edge are off
-
plt.text(0.32, 0.8, title, size=14, weight="bold")
-
plt.text(0.09, 0.65, f"Max Range: {np.floor(trgx.max()/1e3)} km", size=12)
-
plt.text(0.12, 0.5, f"Max Height: {np.floor(trgz.max()/1e3)} km", size=12)
-
plt.text(
-
0.15,
-
0.3,
-
num2date(grid.time["data"], grid.time["units"])[0].strftime("%H:%M:%S Z"),
-
weight="bold",
-
size=17,
-
)
-
plt.text(
-
0.06,
-
0.15,
-
num2date(grid.time["data"], grid.time["units"])[0].strftime("%d %b, %Y UTC"),
-
size=14,
-
)
-
-
ax_xy.set_aspect("auto")
-
-
if savedir is not None:
-
radar_name = grid.metadata["instrument_name"]
-
figtime = num2date(grid.time["data"], grid.time["units"])[0].strftime(
-
"%Y%m%d%H%M%S"
-
)
-
figname = f"{savedir}{os.sep}{title}_{radar_name}_{figtime}.png"
-
plt.savefig(fname=figname, dpi=dpi, bbox_inches="tight")
-
print(f"Figure(s) saved as {figname}")
-
if show_figure:
-
fig.show()
-
else:
-
plt.close()
-
-
-
-