def map_add_currents(ax, currents, coarsen=None, scale=None, headwidth=None, headlength=None, headaxislength=None): """ Add currents to map :param dsd: dataset :param sub: amount to downsample by :return: """ scale = scale or 90 headwidth = headwidth or 2.75 headlength = headlength or 2.75 headaxislength = headaxislength or 2.5 coarsen = coarsen or 2 try: qds = currents.coarsen(lon=coarsen, boundary='pad').mean().coarsen( lat=coarsen, boundary='pad').mean() mesh = True except ValueError: qds = currents.coarsen(X=coarsen, boundary='pad').mean().coarsen( Y=coarsen, boundary='pad').mean() mesh = False angle, speed = uv2spdir(qds['u'], qds['v']) # convert u/v to angle and speed u, v = spdir2uv( # convert angle and speed back to u/v, normalizing the arrow sizes np.ones_like(speed), angle, deg=True) qargs = {} qargs['scale'] = scale qargs['headwidth'] = headwidth qargs['headlength'] = headlength qargs['headaxislength'] = headaxislength qargs['transform'] = ccrs.PlateCarree() if mesh: lons, lats = np.meshgrid(qds['lon'], qds['lat']) q = ax.quiver(lons, lats, u, v, **qargs) else: q = ax.quiver(qds.lon.squeeze().data, qds.lat.squeeze().data, u.squeeze(), v.squeeze(), **qargs) return q
fig = plt.figure() tds = ds.squeeze() u = tds['u'].data v = tds['v'].data lon = tds.coords['lon'].data lat = tds.coords['lat'].data # time = tds.coords['time'].data u = ma.masked_invalid(u) v = ma.masked_invalid(v) angle, speed = uv2spdir(u, v) us, vs = spdir2uv(np.ones_like(speed), angle, deg=True) lons, lats = np.meshgrid(lon, lat) speed_clipped = np.clip(speed[::sub, ::sub], velocity_min, velocity_max).squeeze() fig, ax = plt.subplots(figsize=(11, 8), subplot_kw=dict(projection=ccrs.PlateCarree())) # Plot title plt.title('{}\n{}'.format(title_str, str(ds.time.values[0]))) # plot arrows over pcolor h = ax.quiver(lons[::sub, ::sub], lats[::sub, ::sub],
label=ndbc[0]) ax6.plot(df2['datetime'], df2['wind_speed'], color='tab:blue', label=ndbc[1]) # ax6.plot(df['datetime'], df['wind_gust'], color='tab:red', linestyle='--', alpha=0.3, label=ndbc[0]) # ax6b.plot(df['datetime'], df['wind_direction'], color='tab:red', linestyle='-', alpha=0.3, label=ndbc[0]) # ax6b.plot(df2['datetime'], df2['wind_direction'], color='tab:blue', linestyle='-', alpha=0.3, label=ndbc[1]) ax6.set_ylabel('Wind Speed [m/s]', fontsize=18, fontweight='bold') # ax6b.set_ylabel('Wind Direction', fontsize=10, fontweight='bold') ax6.legend(fontsize=16) ax6.grid(True, linestyle='--', linewidth=0.5) ax6.tick_params(axis='both', labelsize=18) ax6.xaxis.set_minor_locator(AutoMinorLocator(12)) # Stick plot u1, v1 = spdir2uv(df['wind_speed'], df['wind_direction'], deg=True) u2, v2 = spdir2uv(df2['wind_speed'], df2['wind_direction'], deg=True) q = stick_plot(df['datetime'], -u1, -v1, ax=ax7) ref = 10 qk = plt.quiverkey( q, 0.1, 0.85, ref, "{} {}".format(ref, 'm/s'), labelpos="N", coordinates="axes", )
def surface_map_glider_track(ds, region, bathy=None, argo=None, gliders=None, transform=None, model=None, save_dir=None, dpi=None, custom_transect=None, current_glider_loc=None): """ Written by Mike Smith Modified by Lori Garzio """ bathy = bathy or None transform = transform or ccrs.PlateCarree() argo = argo or False gliders = gliders or False save_dir = save_dir or os.getcwd() model = model or 'rtofs' dpi = dpi or 150 custom_transect = custom_transect or None current_glider_loc = current_glider_loc or None limits = region[1] extent = limits['lonlat'] save_dir_maps = os.path.join(save_dir, 'surface_maps', region[1]["code"]) os.makedirs(save_dir_maps, exist_ok=True) glider_name = gliders.deployment_name.split('-')[0] glidert0 = np.nanmin(gliders.time.values) glidert1 = np.nanmax(gliders.time.values) glidert0_str = pd.to_datetime(glidert0).strftime('%Y-%m-%dT%H:%M') glidert1_str = pd.to_datetime(glidert1).strftime('%Y-%m-%dT%H:%M') if model in ['gofs', 'cmems']: t1 = pd.to_datetime(ds.time.data) elif model == 'rtofs': t1 = pd.to_datetime(ds.time.data[0]) else: return 'Incorrect model type. Please enter "gofs", "rtofs" or "cmems"' for k, v in limits.items(): if k in ['lonlat', 'code', 'currents']: continue if k == 'salinity': var_str = 'Sea Surface Salinity' elif k == 'temperature': var_str = 'Sea Surface Temperature' for item in v: depth = item['depth'] if depth > 0: # only plot sea surface continue try: dsd = ds.sel(depth=depth) except KeyError: dsd = ds.sel(depth=slice(0, 1)) if len(dsd.depth) > 1: raise ValueError('More than one depth between 0-1m') title = f'{glider_name} track: {glidert0_str} to {glidert1_str}\n' \ f'{model.upper()} {var_str} at {str(t1)} UTC' sname = f'{glider_name}_{region[1]["code"]}_{model}_{k}_{t1.strftime("%Y-%m-%dT%H%M%SZ")}' save_file = os.path.join(save_dir_maps, sname) vargs = {} vargs['vmin'] = item['limits'][0] vargs['vmax'] = item['limits'][1] vargs['transform'] = transform vargs['cmap'] = sp.cmaps(ds[k].name) vargs['extend'] = 'both' if k == 'sea_surface_height': vargs['levels'] = np.arange(vargs['vmin'], vargs['vmax'], item['limits'][2]) limits['currents'] = True else: vargs['levels'] = np.arange(vargs['vmin'], vargs['vmax'], item['limits'][2]) try: vargs.pop('vmin'), vargs.pop('vmax') except KeyError: pass fig, ax = plt.subplots( figsize=(11, 8), subplot_kw=dict(projection=ccrs.Mercator()) ) h = plt.contourf(dsd['lon'], dsd['lat'], dsd[k].squeeze(), **vargs) if k == 'sea_surface_height': sub = 6 qds = dsd.coarsen(lon=sub, boundary='pad').mean().coarsen(lat=sub, boundary='pad').mean() angle, speed = uv2spdir(qds['u'], qds['v']) # convert u/v to angle and speed u, v = spdir2uv( # convert angle and speed back to u/v, normalizing the arrow sizes np.ones_like(speed), angle, deg=True ) qargs = {} # qargs['norm'] = Normalize(vmin=velocity_min, vmax=velocity_max, clip=True) qargs['scale'] = 90 # qargs['headwidth'] = 2.5 # qargs['headlength'] = 4 # qargs['headaxislength'] = 4 qargs['headwidth'] = 2.75 qargs['headlength'] = 2.75 qargs['headaxislength'] = 2.5 qargs['transform'] = ccrs.PlateCarree() # qargs['pivot'] = 'mid' # qargs['units'] = 'inches' # sub = 3 lons, lats = np.meshgrid(qds['lon'], qds['lat']) q = plt.quiver(lons, lats, u, v, **qargs) if bathy: levels = np.arange(-100, 0, 50) bath_lat = bathy.variables['lat'][:] bath_lon = bathy.variables['lon'][:] bath_elev = bathy.variables['elevation'][:] CS = plt.contour(bath_lon, bath_lat, bath_elev, levels, linewidths=.75, alpha=.5, colors='k', transform=ccrs.PlateCarree()) ax.clabel(CS, [-100], inline=True, fontsize=7, fmt=sp.fmt) # plt.contourf(bath_lon, bath_lat, bath_elev, np.arange(-9000,9100,100), cmap=cmocean.cm.topo, transform=ccrs.PlateCarree()) sp.map_add_features(ax, extent) sp.map_add_ticks(ax, extent) if argo: atimes = [] for i in argo: ax.plot(i.lon, i.lat, marker='o', markersize=7, color='w', markeredgecolor='black', label=i.name, transform=ccrs.PlateCarree()) atimes.append(pd.to_datetime(i.time)) title = '{}\n Argo floats (circles): {} to {} '.format(title, pd.to_datetime(np.min(atimes)).strftime('%Y-%m-%dT%H:%M'), pd.to_datetime(np.max(atimes)).strftime('%Y-%m-%dT%H:%M')) #ax.legend(loc='upper right', fontsize=6) # plot full glider track ax.plot(gliders.longitude.values, gliders.latitude.values, color='white', linewidth=1.5, label='Glider Track', transform=ccrs.PlateCarree()) ax.legend(loc='upper left') if current_glider_loc: ax.plot(gliders.longitude.values[-1], gliders.latitude.values[-1], color='white', marker='^', markeredgecolor='black', markersize=8.5, transform=ccrs.PlateCarree()) if custom_transect: ax.plot(custom_transect['lon'], custom_transect['lat'], color='magenta', linewidth=1.5, label='Model Comparison', transform=ccrs.PlateCarree()) ax.legend(loc='upper left') # Plot title plt.title(title) # Set colorbar height equal to plot height divider = make_axes_locatable(ax) cax = divider.new_horizontal(size='5%', pad=0.05, axes_class=plt.Axes) fig.add_axes(cax) # generate colorbar cbar = plt.colorbar(h, cax=cax) cbar.set_label(ds[k].units) plt.savefig(save_file, dpi=dpi, bbox_inches='tight', pad_inches=0.1) plt.close()
def plot_radials(dataset, *, plot_type='velocity', output_file=None, extent=None, lon_ticks=None, lat_ticks=None, scale=True, sub=2, cbar_step=10, velocity_min=None, velocity_max=None, markers=None, prim_filter=False, title='HF Radar'): """ param dataset: a file-path to an xarray compatible object or an xarray Dataset object """ try: ds = xr.open_dataset(dataset) closing = ds.close except AttributeError: if isinstance(dataset, xr.Dataset): ds = dataset closing = int # dummy func to close nothing else: raise tds = ds.squeeze() if prim_filter: if 'PRIM' in list(tds.keys()): tds = tds.where(tds.PRIM == 1).squeeze() title = title + ' - QC' else: logging.warning('PRIM flag not found. Bypassing quality control filters') time = ds.time.values[0] lon = tds.coords['lon'].data lat = tds.coords['lat'].data u = tds['u'].data v = tds['v'].data u = ma.masked_invalid(u) v = ma.masked_invalid(v) if scale: angle, speed = uv2spdir(u, v) # convert u/v to angle and speed u, v = spdir2uv( # convert angle and speed back to u/v, normalizing the arrow sizes np.ones_like(speed), angle, deg=True ) velocity_min = velocity_min or -40 velocity_max = velocity_max or 40 kwargs = dict(extent=extent, lon_ticks=lon_ticks, lat_ticks=lat_ticks, output_file=output_file, sub=sub, markers=markers, title=title, meshgrid=False, scale=120, headwidth=2.5, headlength=4, headaxislength=4) if 'velocity' in plot_type: """ Velocity displays the direction and magnitude of the radials """ kwargs['colorbar'] = True kwargs['cmap'] = cmocean.cm.balance # Define arrow colors. Limited by velocity_min and velocity_max kwargs['color_clipped'] = np.clip( tds.velocity.data[::sub], velocity_min, velocity_max ).squeeze() kwargs['offset'] = Normalize(vmin=velocity_min, vmax=velocity_max, clip=True) kwargs['ticks'] = np.append(np.arange(velocity_min, velocity_max, cbar_step), velocity_max) elif 'motion' in plot_type: """ Motion displays the direction (towards or away) from radar """ kwargs['colorbar'] = False kwargs['cmap'] = 'bwr' velocity = tds.velocity velocity_temp = velocity.where(velocity > 0, other=-1) # Going away from radar kwargs['color_clipped'] = velocity_temp.where(velocity < 0, other=1).data # Going towards radar kwargs['offset'] = TwoSlopeNorm(vmin=-1, vcenter=0, vmax=1) elif 'qc_pass_fail' in plot_type: kwargs['colorbar'] = False kwargs['cmap'] = colors.ListedColormap(['limegreen', 'red']) if prim_filter: tds = ds.squeeze() kwargs['color_clipped'] = tds.PRIM.where(tds.PRIM != 1, other=-1).data # PRIM == 1 where vectors pass qc kwargs['offset'] = TwoSlopeNorm(vmin=-1, vcenter=0, vmax=1) closing() plot_common(time, lon, lat, u, v, **kwargs)
def plot_totals(dataset, *, output_file=None, extent=None, scale=True, sub=2, cbar_step=20, velocity_min=None, velocity_max=None, markers=None, title='HF Radar'): """ param dataset: a file-path to an xarray compatible object or an xarray Dataset object """ try: ds = xr.open_dataset(dataset) closing = ds.close except AttributeError: if isinstance(dataset, xr.Dataset): ds = dataset closing = int # dummy func to close nothing else: raise tds = ds.squeeze() u = tds['u'].data v = tds['v'].data lon = tds.coords['lon'].data lat = tds.coords['lat'].data try: time = str(ds.time.values[0]) except IndexError: time = ds.time.values closing() if scale: angle, speed = uv2spdir(u, v) # convert u/v to angle and speed u, v = spdir2uv( # convert angle and speed back to u/v, normalizing the arrow sizes np.ones_like(speed), angle, deg=True ) velocity_min = velocity_min or np.int32(np.nanmin(speed)) or 0 velocity_max = velocity_max or np.int32(np.nanmax(speed)) or 15 kwargs = dict(output_file=output_file, sub=sub, markers=markers, title=title, meshgrid=True, scale=60, headwidth=3, headlength=5, headaxislength=4.5) kwargs['color_clipped'] = np.clip( speed[::sub], velocity_min, velocity_max ).squeeze() kwargs['offset'] = Normalize(vmin=velocity_min, vmax=velocity_max, clip=True) kwargs['ticks'] = np.append(np.arange(velocity_min, velocity_max, cbar_step), velocity_max) kwargs['extent'] = extent plt = plot_common( time, lon, lat, u, v, **kwargs ) if output_file is None: return plt
def plot_common(time, lon, lat, u, v, *, output_file=None, meshgrid=True, sub=2, velocity_min=None, velocity_max=None, markers=None, title='HF Radar'): """ param markers: a list of 3-tuple/lists containng [lon, lat, marker kwargs] as should be passed into ax.plot() eg. [ [-74.6, 38.5, dict(marker='o', markersize=8, color='r')], [-70.1, 35.2, dict(marker='o', markersize=8, color='b')] ] """ markers = markers or [] fig = plt.figure() u = ma.masked_invalid(u) v = ma.masked_invalid(v) angle, speed = uv2spdir(u, v) us, vs = spdir2uv(np.ones_like(speed), angle, deg=True) if meshgrid is True: lons, lats = np.meshgrid(lon, lat) else: lons, lats = lon, lat velocity_min = velocity_min or 0 velocity_max = velocity_max or np.nanmax(speed) or 15 speed_clipped = np.clip(speed[::sub, ::sub], velocity_min, velocity_max).squeeze() fig, ax = plt.subplots(figsize=(11, 8), subplot_kw=dict(projection=ccrs.PlateCarree())) # Plot title plt.title('{}\n{}'.format(title, time)) # plot arrows over pcolor h = ax.quiver(lons[::sub, ::sub], lats[::sub, ::sub], us[::sub, ::sub], vs[::sub, ::sub], speed_clipped, cmap='jet', scale=60) divider = make_axes_locatable(ax) cax = divider.new_horizontal(size='5%', pad=0.05, axes_class=plt.Axes) fig.add_axes(cax) # generate colorbar ticks = np.linspace(velocity_min, velocity_max, 5) cb = plt.colorbar(h, cax=cax, ticks=ticks) cb.ax.set_yticklabels([f'{s:.2f}' for s in ticks]) cb.set_label('cm/s') for m in markers: ax.plot(m[0], m[1], **m[2]) # Gridlines and grid labels gl = ax.gridlines(draw_labels=True, linewidth=1, color='black', alpha=0.5, linestyle='--') gl.xlabels_top = gl.ylabels_right = False gl.xlabel_style = {'size': 10, 'color': 'gray'} gl.ylabel_style = {'size': 10, 'color': 'gray'} gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER # Axes properties and features ax.set_extent([lon.min() - 1, lon.max() + 1, lat.min() - 1, lat.max() + 1]) ax.add_feature(LAND, zorder=0, edgecolor='black') ax.add_feature(cfeature.LAKES) ax.add_feature(cfeature.BORDERS) ax.add_feature(state_lines, edgecolor='black') fig_size = plt.rcParams["figure.figsize"] fig_size[0] = 12 fig_size[1] = 8.5 plt.rcParams["figure.figsize"] = fig_size if output_file is not None: create_dir(str(Path(output_file).parent)) resoluton = 150 # plot resolution in DPI plt.savefig(output_file, dpi=resoluton) plt.close('all') else: return plt
def plot_ruv(radial_file, save_path=None, fname=None, speed_display='color', redblue=True, plotflag=None, scale=50, vlims=(-100, 100)): """ Main function to plot radial files. Args: radial_file (str or Path): Path to radial file or a Radial object save_path (str or Path): Path to save figures fname (str): Output file name. If not specified, the radial object filename is used, Defaults to None speed_display (str, optional): 'color' or 'arrowlength' to specify whether current speed is depicted by color or arrow length, Defaults to color redblue (bool, optional): If True, colorbar scheme is redblue, Defaults to True plotflag (str, optional): QARTOD QC test code, fail and suspect flags for that test will be highlighted, Defaults to None scale (int, optional): Scaling factor for drawing the vectors, Default = 50 vlims (tuple, optional): Velocity limits for the colorbar, Default = (-100,100) """ if not isinstance(radial_file, Radial): r = Radial(radial_file) else: r = radial_file if not r.is_valid(): return if r._iscorrupt: return if fname == None: fname = r.file_name[0:-4] # Adjust some standard plotting settings to make them the size of a sheet of paper fig_size = plt.rcParams["figure.figsize"] fig_size[0] = 12 fig_size[1] = 8 plt.rcParams["figure.figsize"] = fig_size # Set colors of the land. edgecolor = 'black' landcolor = 'tan' LAND = cfeature.NaturalEarthFeature('physical', 'land', '10m', edgecolor='face', facecolor='tan') state_lines = cfeature.NaturalEarthFeature( category='cultural', name='admin_1_states_provinces_lines', scale='50m', facecolor='none') bf = 0.3 #degrees extent = [ r.data.LOND.min() - bf, r.data.LOND.max() + bf, r.data.LATD.min() - bf, r.data.LATD.max() + bf ] # Split out everything into seperate variables in order to pass them easier to the plotting functions time = r.time lon = r.data.LOND.to_numpy() lat = r.data.LATD.to_numpy() u = r.data.VELU.to_numpy() v = r.data.VELV.to_numpy() velocity = r.data.VELO.to_numpy() sitename = r.metadata['Site'][0:4] ptype = r.metadata['PatternType'] # Mask nans just in case there are any u = ma.masked_invalid(u) v = ma.masked_invalid(v) # convert U and V component velocities to angle and speed angle, speed = uv2spdir(u, v) # convert angle and speed right back back to U and V component velocities, # Passing speed as an array of 1's allows for the normalizing of the arrow sizes # if we pass the correct u, v = spdir2uv(np.ones_like(speed), angle, deg=True) # Get the receiver location receiver_location = [float(x) for x in r.metadata['Origin'].split(' ')] receiver_location.reverse() # Intialize an empty subplot using cartopy fig, ax = plt.subplots(figsize=(11, 8), subplot_kw=dict(projection=ccrs.Mercator())) #plt.quiver(lon, lat, u, v, transform=ccrs.PlateCarree()) plt.plot(receiver_location[0], receiver_location[1], 'o', markersize=10, markeredgecolor='black', color='red', transform=ccrs.PlateCarree()) map_features(ax, extent, LAND, edgecolor, landcolor, state_lines) # The next lines specify the arrow shapes. You can customize this to your preference, usually by trial and error. # scale = 50 headwidth = 2.5 headlength = 4 headaxislength = 4 sub = 1 # if user requested speed displayed as arrow length if speed_display == 'arrowlength': scale_units = 'width' width = 0.005 if not plotflag == None: fail = r.data[plotflag] == 4 suspect = r.data[plotflag] == 3 noteval = r.data[plotflag] == 2 away = r.data.VELO > 0 plt.quiver(lon, lat, u, v, transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='lightpink') plt.quiver(lon[away], lat[away], u[away], v[away], transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='lightblue') #plt.quiver(lon, lat, u, v, transform=ccrs.PlateCarree(), scale=scale, color='white') plt.quiver(lon[fail], lat[fail], u[fail], v[fail], transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='red') plt.quiver(lon[suspect], lat[suspect], u[suspect], v[suspect], transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='gold') plt.quiver(lon[noteval], lat[noteval], u[noteval], v[noteval], transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='gray') plt.title( f'{sitename} {ptype} {plotflag}\nFail(red) Suspect(yellow) Not Evaluated(grey)\n{time}' ) plt.savefig(save_path + '/' + fname + '_' + plotflag) plt.close('all') elif redblue: away = r.data.VELO > 0 plt.quiver(lon, lat, u, v, transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='red') plt.quiver(lon[away], lat[away], u[away], v[away], transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='blue') plt.title(f'{sitename} {ptype}\n{time}') plt.savefig(save_path + '/' + fname + '_rb') plt.close('all') else: plt.quiver(lon, lat, u, v, transform=ccrs.PlateCarree(), scale=scale, scale_units=scale_units, width=width, color='wheat') plt.title(f'{sitename} {ptype}\n{time}') plt.savefig(save_path + '/' + fname) plt.close('all') # if user requested speed displayed as color else: if not plotflag == None: test = r.data[plotflag] velocity[np.where(test >= 0)] = 1 velocity[np.where(test == 4)] = -1 color_clipped = np.clip(r.data.VELO[::sub], -1, 1).squeeze() offset = TwoSlopeNorm(vmin=-1, vcenter=0, vmax=1) cmap = colors.ListedColormap(['red', 'wheat']) plt.title(f'{sitename} {ptype} {plotflag} Fail\n{time}') qargs = dict(cmap=cmap, scale=scale, headwidth=headwidth, headlength=headlength, headaxislength=headaxislength) qargs['transform'] = ccrs.PlateCarree() qargs['norm'] = offset # plot arrows over pcolor h = ax.quiver(lon[::sub], lat[::sub], u[::sub], v[::sub], color_clipped, **qargs) plt.savefig(save_path + '/' + fname + '_' + plotflag + '_fail') plt.close('all') elif redblue: cmap = 'bwr' # velocity_temp = velocity.where(velocity > 0, other=-1) # Going away from radar velocity[np.where(velocity < 0)] = -1 velocity[np.where(velocity >= 0)] = 1 # We will create temporary variable of velocities that sets any velocity less than 0 to 1 # color_clipped = velocity_temp.where(velocity < 0, other=1) # Going towards radar # Define arrow colors. Limited by velocity_min and velocity_max color_clipped = np.clip(r.data.VELO[::sub], -1, 1).squeeze() offset = TwoSlopeNorm(vmin=-1, vcenter=0, vmax=1) plt.title(f'{sitename} {ptype}\n{time}') qargs = dict(cmap=cmap, scale=scale, headwidth=headwidth, headlength=headlength, headaxislength=headaxislength) qargs['transform'] = ccrs.PlateCarree() qargs['norm'] = offset # plot arrows over pcolor h = ax.quiver(lon[::sub], lat[::sub], u[::sub], v[::sub], color_clipped, **qargs) # map_features(ax, extent, LAND, edgecolor, landcolor, state_lines) plt.savefig(save_path + '/' + fname + '_rb') plt.close('all') else: plt.title(f'{sitename} {ptype}\n{time}') cmap = cmocean.cm.balance # Colorbar options velocity_min = vlims[ 0] # The minimum speed that should be displayed on the colorbar velocity_max = vlims[ 1] # The maximum speed that should be displayed on the colorbar cbar_step = 10 # The step between each colorbar tick offset = Normalize(vmin=velocity_min, vmax=velocity_max, clip=True) # Define arrow colors. Limited by velocity_min and velocity_max color_clipped = np.clip(r.data.VELO[::sub], velocity_min, velocity_max).squeeze() ticks = np.append(np.arange(velocity_min, velocity_max, cbar_step), velocity_max) qargs = dict(cmap=cmap, scale=scale, headwidth=headwidth, headlength=headlength, headaxislength=headaxislength) qargs['transform'] = ccrs.PlateCarree() qargs['norm'] = offset # plot arrows over pcolor h = ax.quiver(lon[::sub], lat[::sub], u[::sub], v[::sub], color_clipped, **qargs) # map_features(ax, extent, LAND, edgecolor, landcolor, state_lines) # generate colorbar divider = make_axes_locatable(ax) cax = divider.new_horizontal(size='5%', pad=0.05, axes_class=plt.Axes) fig.add_axes(cax) cb = plt.colorbar(h, cax=cax, ticks=ticks) cb.ax.set_yticklabels([f'{s:d}' for s in ticks]) cb.set_label('cm/s') plt.savefig(save_path + '/' + fname) plt.close('all')