Source code for geoips.plugins.modules.output_formatters.imagery_windbarbs
# # # Distribution Statement A. Approved for public release. Distribution unlimited.
# # #
# # # Author:
# # # Naval Research Laboratory, Marine Meteorology Division
# # #
# # # This program is free software: you can redistribute it and/or modify it under
# # # the terms of the NRLMMD License included with this program. This program is
# # # distributed WITHOUT ANY WARRANTY; without even the implied warranty of
# # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the included license
# # # for more details. If you did not receive the license, for more information see:
# # # https://github.com/U-S-NRL-Marine-Meteorology-Division/
"""Matplotlib-based windbarb annotated image output."""
import logging
import numpy
import cartopy.crs as crs
from geoips.image_utils.mpl_utils import (
create_figure_and_main_ax_and_mapobj,
save_image,
)
from geoips.image_utils.colormap_utils import set_matplotlib_colors_standard
from geoips.image_utils.mpl_utils import plot_image, plot_overlays, create_colorbar
from geoips.image_utils.mpl_utils import get_title_string_from_objects, set_title
LOG = logging.getLogger(__name__)
interface = "output_formatters"
family = "image_overlay"
name = "imagery_windbarbs"
[docs]def plot_barbs(main_ax, mapobj, mpl_colors_info, formatted_data_dict):
"""Plot windbarbs on matplotlib figure."""
# main_ax.extent = area_def.area_extent_ll
main_ax.set_extent(mapobj.bounds, crs=mapobj)
# main_ax.extent = mapobj.bounds
# NOTE this does not work if transform=mapobj.
# Something about transforming to PlateCarree projection, then
# reprojecting to mapobj. I don't fully understand it, but this
# works beautifully, and transform=mapobj puts all the vectors
# in the center of the image.
main_ax.scatter(
x=formatted_data_dict["lon"].data[formatted_data_dict["rain_inds"]],
y=formatted_data_dict["lat"].data[formatted_data_dict["rain_inds"]],
transform=crs.PlateCarree(),
marker="D",
color="k",
s=formatted_data_dict["rain_size"],
zorder=2,
)
main_ax.barbs(
formatted_data_dict["lon"].data,
formatted_data_dict["lat"].data,
formatted_data_dict["u"].data,
formatted_data_dict["v"].data,
formatted_data_dict["speed"].data,
transform=crs.PlateCarree(),
pivot="tip",
rounding=False,
cmap=mpl_colors_info["cmap"],
flip_barb=formatted_data_dict["flip_barb"],
# barb_increments=dict(half=10, full=20, flag=50),
sizes=formatted_data_dict["sizes_dict"],
length=formatted_data_dict["barb_length"],
linewidth=formatted_data_dict["line_width"],
norm=mpl_colors_info["norm"],
zorder=1,
)
[docs]def output_clean_windbarbs(
area_def,
clean_fnames,
mpl_colors_info,
image_datetime,
formatted_data_dict,
fig=None,
main_ax=None,
mapobj=None,
):
"""Plot and save "clean" windbarb imagery.
No background imagery, coastlines, gridlines, titles, etc.
Returns
-------
list of str
Full paths to all resulting output files.
"""
LOG.info("Starting clean_fname")
if fig is None and main_ax is None and mapobj is None:
# Create matplotlib figure and main axis, where the main image will be plotted
fig, main_ax, mapobj = create_figure_and_main_ax_and_mapobj(
area_def.x_size, area_def.y_size, area_def, noborder=True
)
plot_barbs(main_ax, mapobj, mpl_colors_info, formatted_data_dict)
success_outputs = []
if clean_fnames is not None:
for clean_fname in clean_fnames:
success_outputs += save_image(
fig, clean_fname, is_final=False, image_datetime=image_datetime
)
return success_outputs
[docs]def format_windbarb_data(xarray_obj, product_name):
"""Format windbarb data before plotting."""
# lat=xarray_obj['latitude'].to_masked_array()
# lon2=xarray_obj['longitude'].to_masked_array()
# direction=xarray_obj['wind_dir_deg_met'].to_masked_array()
# speed=xarray_obj['wind_speed_kts'].to_masked_array()
# u=speed * numpy.sin((direction+180)*3.1415926/180.0)
# v=speed * numpy.cos((direction+180)*3.1415926/180.0)
# u=speed * numpy.sin(direction*3.1415926/180.0)
# v=speed * numpy.cos(direction*3.1415926/180.0)
num_product_arrays = 1
if len(xarray_obj[product_name].shape) == 3:
num_product_arrays = xarray_obj[product_name].shape[2]
# This is 2-D, with only one array per variable (speed, direction,
# rain_flag) - meaning NO ambiguities
if len(xarray_obj[product_name].shape) == 3 and num_product_arrays == 3:
speed = xarray_obj[product_name].to_masked_array()[:, :, 0]
direction = xarray_obj[product_name].to_masked_array()[:, :, 1]
rain_flag = xarray_obj[product_name].to_masked_array()[:, :, 2]
# This is 2-D, with FOUR arrays per variable (speed, direction, rain_flag)
# - meaning 4 ambiguities
elif len(xarray_obj[product_name].shape) == 3 and num_product_arrays == 12:
speed = xarray_obj[product_name].to_masked_array()[:, :, 0:4]
direction = xarray_obj[product_name].to_masked_array()[:, :, 4:8]
rain_flag = xarray_obj[product_name].to_masked_array()[:, :, 8:12]
# This is 1-D, with one vector per variable - no ambiguities.
else:
speed = xarray_obj[product_name].to_masked_array()[:, 0]
direction = xarray_obj[product_name].to_masked_array()[:, 1]
rain_flag = xarray_obj[product_name].to_masked_array()[:, 2]
# These should probably be specified in the product dictionary.
# It will vary per-sensor / data type, these basically only currently work with
# ASCAT 25 km data.
# This would also avoid having the product names hard coded in the output
# module code.
prod_plugin = xarray_obj.attrs.get("product_plugin", {})
try:
barb_args = prod_plugin["spec"]["windbarb_plotter"]["plugin"]["arguments"]
except KeyError:
barb_args = {}
if barb_args:
# Thinning the data points to better display the windbards
thinning = barb_args["thinning"]
barblength = barb_args["length"]
linewidth = barb_args["width"]
sizes_dict = barb_args["sizes_dict"]
rain_size = barb_args["rain_size"]
elif product_name == "windbarbs":
# Thinning the data points to better display the windbards
thinning = 1 # skip data points
barblength = 5.0
linewidth = 1.5
sizes_dict = dict(height=0.7, spacing=0.3)
rain_size = 10
elif product_name == "wind-ambiguities" or "wind-ambiguities" in product_name:
# Thinning the data points to better display the windbards
thinning = 1 # skip data points
barblength = 5 # Length of individual barbs
linewidth = 2 # Width of individual barbs
rain_size = 10 # Marker size for rain_flag
sizes_dict = dict(
height=0,
spacing=0,
width=0, # flag width, relative to barblength
emptybarb=0.5,
)
else:
raise ValueError(f"Unknown product {product_name}")
lat = xarray_obj["latitude"].to_masked_array()
lon2 = xarray_obj["longitude"].to_masked_array()
u = speed * numpy.sin((direction + 180) * 3.1415926 / 180.0)
v = speed * numpy.cos((direction + 180) * 3.1415926 / 180.0)
# convert longitudes to (-180,180)
# lon=utils.wrap_longitudes(lon2)
# Must be 0-360 for barbs
lon = numpy.ma.where(lon2 < 0, lon2 + 360, lon2)
if len(lat.shape) == 2:
lat2 = lat[::thinning, ::thinning]
lon2 = lon[::thinning, ::thinning]
u2 = u[::thinning, ::thinning]
v2 = v[::thinning, ::thinning]
speed2 = speed[::thinning, ::thinning]
rain_flag2 = rain_flag[::thinning, ::thinning]
elif len(lat.shape) == 1:
lat2 = lat[::thinning]
lon2 = lon[::thinning]
u2 = u[::thinning]
v2 = v[::thinning]
speed2 = speed[::thinning]
rain_flag2 = rain_flag[::thinning]
if lat2.min() > 0:
flip_barb = False
elif lat2.max() < 0:
flip_barb = True
else:
flip_barb = numpy.ma.where(lat2 > 0, False, True).data
good_inds = numpy.ma.where(speed2)
return_dict = {}
if len(lon2.shape) != len(speed2.shape):
return_dict["lon"] = lon2[good_inds[0:2]]
else:
return_dict["lon"] = lon2[good_inds]
if len(lat2.shape) != len(speed2.shape):
return_dict["lat"] = lat2[good_inds[0:2]]
else:
return_dict["lat"] = lat2[good_inds]
if flip_barb is not True and flip_barb is not False:
if len(flip_barb.shape) != len(speed2.shape):
return_dict["flip_barb"] = flip_barb[good_inds[0:2]]
else:
return_dict["flip_barb"] = flip_barb[good_inds]
else:
return_dict["flip_barb"] = flip_barb
return_dict["u"] = u2[good_inds]
return_dict["v"] = v2[good_inds]
return_dict["speed"] = speed2[good_inds]
return_dict["rain_inds"] = numpy.ma.where(rain_flag2[good_inds])
return_dict["barb_length"] = barblength
return_dict["line_width"] = linewidth
return_dict["sizes_dict"] = sizes_dict
return_dict["rain_size"] = rain_size
return return_dict
[docs]def call(
area_def,
xarray_obj,
product_name,
output_fnames,
clean_fname=None,
product_name_title=None,
mpl_colors_info=None,
feature_annotator=None,
gridline_annotator=None,
product_datatype_title=None,
bg_data=None,
bg_mpl_colors_info=None,
bg_xarray=None,
bg_product_name_title=None,
bg_datatype_title=None,
remove_duplicate_minrange=None,
title_copyright=None,
title_formatter=None,
):
"""Plot annotated windbarbs on matplotlib figure."""
LOG.info("Startig imagery_windbarbs")
if product_name_title is None:
product_name_title = product_name
success_outputs = []
# Plot windbarbs
formatted_data_dict = format_windbarb_data(xarray_obj, product_name)
if clean_fname is not None:
success_outputs += output_clean_windbarbs(
area_def,
[clean_fname],
mpl_colors_info,
xarray_obj.start_datetime,
formatted_data_dict,
)
if output_fnames is not None:
LOG.info("Starting output_fnames")
# Create matplotlib figure and main axis, where the main image will be plotted
fig, main_ax, mapobj = create_figure_and_main_ax_and_mapobj(
area_def.x_size,
area_def.y_size,
area_def,
existing_mapobj=None,
noborder=False,
)
if bg_data is not None:
if not bg_mpl_colors_info:
bg_mpl_colors_info = set_matplotlib_colors_standard(
data_range=[bg_data.min(), bg_data.max()],
cmap_name="Greys",
cbar_label=None,
create_colorbar=False,
)
# Plot the background data on a map
plot_image(main_ax, bg_data, mapobj, mpl_colors_info=bg_mpl_colors_info)
plot_barbs(main_ax, mapobj, mpl_colors_info, formatted_data_dict)
# Set the title for final image
title_string = get_title_string_from_objects(
area_def,
xarray_obj,
product_name_title,
product_datatype_title=product_datatype_title,
bg_xarray=bg_xarray,
bg_product_name_title=bg_product_name_title,
bg_datatype_title=bg_datatype_title,
title_copyright=title_copyright,
title_formatter=title_formatter,
)
set_title(main_ax, title_string, area_def.y_size)
if mpl_colors_info["colorbar"] is True:
# Create the colorbar to match the mpl_colors
create_colorbar(fig, mpl_colors_info)
# Plot gridlines and feature overlays
plot_overlays(
mapobj,
main_ax,
area_def,
feature_annotator=feature_annotator,
gridline_annotator=gridline_annotator,
)
for annotated_fname in output_fnames:
# Save the final image
success_outputs += save_image(
fig,
annotated_fname,
is_final=True,
image_datetime=xarray_obj.start_datetime,
)
return success_outputs