def draw_precipitation_nws(ax, prep, map_extent=(73, 136, 17, 54), gridlines=True): """ Draw NWS-style precipitation map. http://jjhelmus.github.io/blog/2013/09/17/plotting-nsw-precipitation-data/ :param ax: `matplotlib.axes.Axes`, the `Axes` instance used for plotting. :param prep: precipitation, dictionary: {'lon': 1D array, 'lat': 1D array, 'data': 2D array} :param map_extent: (lonmin, lonmax, latmin, latmax), longitude and latitude range. :param gridlines: bool, draw grid lines or not. :return: plots dictionary. :Example: >>> plt.ioff() >>> fig = plt.figure(figsize=(8.6, 6.2)) >>> fig.clf() >>> ax = plt.axes((0.1, 0.08, 0.85, 0.92), projection=ccrs.PlateCarree()) >>> prep = {'lon': lon, 'lat': lat, 'data': rain} >>> plots = draw_precipitation_nws(ax, prep, map_extent=[73, 136, 17, 54]) >>> ax.set_title('24h Accumulated Precipitation', fontsize=18, loc='left') >>> cax = fig.add_axes([0.16, 0.08, 0.7, 0.03]) >>> cb = plt.colorbar(plots['prep'], cax=cax, orientation='horizontal', >>> ticks=plots['prep'].norm.boundaries) >>> cb.set_label('Precipitation (mm)', fontsize=10) >>> cb.ax.tick_params(labelsize=10) >>> plt.savefig('D:/plot.png') """ # set data projection datacrs = ccrs.PlateCarree() # plot map background ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=1) add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=1) # plots container plots = {} # draw precipitation map x, y = np.meshgrid(prep['lon'], prep['lat']) cmap, norm = cm_precipitation_nws() plots['prep'] = ax.pcolormesh(x, y, np.squeeze(prep['data']), norm=norm, cmap=cmap, transform=datacrs) # add grid lines if gridlines: add_gridlines(ax) # return return plots
def draw_qpf_nmc(ax, prep, stations=None, map_extent=(107., 123, 28, 43.)): """ Draw filled-contour QPF. :param ax: ax: `matplotlib.axes.Axes`, the `Axes` instance. :param prep: precipitation, dictionary: necessary, {'lon': 1D array, 'lat': 1D array, 'data': 2D array} optional, {'clevs': 1D array} :param stations: station locations, dictionary: necessary, {'lon': 1D array, 'lat': 1D array} :param map_extent: [lonmin, lonmax, latmin, latmax], longitude and latitude range. :return: plots dictionary. """ # set data projection datacrs = ccrs.PlateCarree() # plot map background ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=1) add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=1) # plots container plots = {} # draw precipitation map x, y = np.meshgrid(prep['lon'], prep['lat']) if prep.get('clevs') is None: clevs = [0.1, 10, 25, 50, 100, 250, 600] cmap = plt.get_cmap("YlGnBu") norm = mpl.colors.BoundaryNorm(clevs, cmap.N) plots['prep'] = ax.contourf(x, y, np.squeeze(prep['data']), clevs, norm=norm, cmap=cmap, transform=datacrs) # add station points if stations is not None: plots['stations'] = ax.scatter(stations['lon'], stations['lat'], transform=ccrs.PlateCarree()) # add grid lines add_gridlines(ax) # return return plots
def draw_total_precipitation(prep, map_extent=(107., 112, 23.2, 26.5), back_image='terrain-background', back_image_zoom=8, title="降水量实况图", draw_station=True, station_info='cities', station_size=22, just_contourf=False): """ 该程序用于显示多日的累积降水量分布特征, 2020/6/7按业务要求制作. Args: ax (matplotlib.axes.Axes): the `Axes` instance used for plotting. prep (dictionary): precipitation, dictionary: {'lon': 1D array, 'lat': 1D array, 'data': 2D array} map_extent (tuple, optional): (lonmin, lonmax, latmin, latmax),. Defaults to (107., 112, 23.2, 26.5). back_image (str, opional): the background image name. Default is stamen 'terrain-background', else is arcgis map server 'World_Physical_Map' (max zoom level is 8) back_image_zoom (int, optional): the zoom level for background image. Defaults to 8. draw_station (bool, optional): draw station name. Defaults to True. station_info (str, optional): station information, 'cities' is 260 city names, or province captial shows. station_size (int, optional): station font size. Defaults to 22. title (str, optional): title string. Defaults to "降水量实况图". Example: import pandas as pd from nmc_met_graphics.plot.precipitation import draw_total_precipitation from nmc_met_io.retrieve_micaps_server import get_model_grids # read data times = pd.date_range(start = pd.to_datetime('2020-06-02 08:00'), end = pd.to_datetime('2020-06-07 08:00'), freq='1H') dataset = get_model_grids("CLDAS/RAIN01_TRI_DATA_SOURCE", times.strftime("%y%m%d%H.000")) data = dataset.sum(dim="time") data['data'].values[data['data'].values > 2400.0] = np.nan prep = {'lon': data['lon'].values, 'lat': data['lat'].values, 'data': data['data'].values} # draw the figure draw_total_precipitation(prep); """ # set figure size fig = plt.figure(figsize=(16, 14.5)) # set map projection datacrs = ccrs.PlateCarree() mapcrs = ccrs.LambertConformal(central_longitude=np.mean(map_extent[0:1]), central_latitude=np.mean(map_extent[2:3]), standard_parallels=(30, 60)) ax = plt.axes((0.1, 0.08, 0.85, 0.92), projection=mapcrs) ax.set_extent(map_extent, crs=datacrs) # add map background add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=1) add_china_map_2cartopy(ax, name='river', edgecolor='cyan', lw=1) if back_image == 'terrain-background': stamen_terrain = cimg.Stamen('terrain-background') ax.add_image(stamen_terrain, back_image_zoom) else: image = cimg.GoogleTiles( url= "https://server.arcgisonline.com/arcgis/rest/services/World_Physical_Map/MapServer/tile/{z}/{y}/{x}.jpg" ) ax.add_image(image, back_image_zoom) # set colors and levels clevs = [50, 100, 200, 300, 400, 500, 600] colors = [ '#6ab4f1', '#0001f6', '#f405ee', '#ffa900', '#fc6408', '#e80000', '#9a0001' ] linewidths = [1, 1, 2, 2, 3, 4, 4] cmap, norm = mpl.colors.from_levels_and_colors(clevs, colors, extend='max') # draw precipitation contour map x, y = np.meshgrid(prep['lon'], prep['lat']) if just_contourf: _ = ax.contourf(x, y, np.squeeze(prep['data']), clevs, norm=norm, cmap=cmap, transform=datacrs, extend='max', alpha=0.5) else: _ = ax.contourf(x, y, np.squeeze(prep['data']), clevs, norm=norm, cmap=cmap, transform=datacrs, extend='max', alpha=0.1) con2 = ax.contour(x, y, np.squeeze(prep['data']), clevs, norm=norm, cmap=cmap, transform=datacrs, linewidths=linewidths) # add path effects plt.setp(con2.collections, path_effects=[ path_effects.SimpleLineShadow(), path_effects.Normal() ]) # add title and legend font = FontProperties(family='Microsoft YaHei', size=32) ax.set_title('降水量实况图(累计降水: 6月02日—6月06日)', loc='center', fontproperties=font) font = FontProperties(family='Microsoft YaHei', size=16) plt.legend([mpatches.Patch(color=b) for b in colors], [ '50~100 毫米', '100~200 毫米', '200-300 毫米', '300~400 毫米', '400~500 毫米', '500~600 毫米', '>=600毫米' ], prop=font) # add city information if draw_station: if station_info == 'cities': cities = pd.read_csv(pkg_resources.resource_filename( 'nmc_met_graphics', "resources/stations/cma_city_station_info.dat"), delimiter=r"\s+") else: cities = pd.read_csv( pkg_resources.resource_filename( 'nmc_met_graphics', "resources/stations/provincial_capital.csv")) font = FontProperties(family='SimHei', size=22, weight='bold') geodetic_transform = ccrs.Geodetic()._as_mpl_transform(ax) for _, row in cities.iterrows(): text_transform = offset_copy(geodetic_transform, units='dots', x=-5) ax.plot(row['lon'], row['lat'], marker='o', color='white', markersize=8, alpha=0.7, transform=datacrs) ax.text(row['lon'], row['lat'], row['city_name'], clip_on=True, verticalalignment='center', horizontalalignment='right', transform=text_transform, fontproperties=font, color='white', path_effects=[ path_effects.Stroke(linewidth=1, foreground='black'), path_effects.Normal() ]) return fig
def draw_theta_on_pv(ax, lon, lat, theta, mslp=None, gh500=None, map_extent=(73, 136, 18, 54), theta_clev=np.arange(300, 400, 4), alpha=0, mslp_clev=np.arange(960, 1060, 4), gh500_clev=np.arange(480, 600, 2), cax=None, left_title="850hPa wind", right_title=None, add_china=True, coastline_color='black'): """ Draw potential temperature on pv surface. :param ax: matplotlib axes. :param lon: longitude coordinates. :param lat: latitude coordinates. :param theta: potential temperature. :param mslp: mean sea level pressure. :param gh500: geopotential height 500hpa. :param map_extent: map extent. :param theta_clev: potential temperature. :param alpha: theta contour transparency. :param mslp_clev: mean sea level contour levels. :param gh500_clev: geopotential height 500 contour levels. :param cax: color bar axes. :param left_title: left title. :param right_title: right title. :param add_china: draw china province map or not. :param coastline_color: coast lines color. :return: potential temperature filled contour cf object. """ # set data projection, should be longitude and latitude. datacrs = ccrs.PlateCarree() # clear figure ax.clear # set map extent ax.set_extent(map_extent) # add map boundary ax.coastlines('50m', edgecolor=coastline_color) if add_china: add_china_map_2cartopy( ax, name='province', edgecolor='darkcyan', lw=4) # draw potential temperature x, y = np.meshgrid(lon, lat) cmap = guide_cmaps("27") cf = ax.contourf( x, y, theta, theta_clev, cmap=cmap, alpha=alpha, antialiased=True, transform=datacrs) if cax is not None: cb = plt.colorbar( cf, cax=cax, orientation='horizontal', extendrect=True, ticks=theta_clev) cb.set_label( 'Potential Temperature [K]', size='large', fontsize=16) cb.ax.tick_params(labelsize=16) # draw mean sea level pressure if mslp is not None: x, y = np.meshgrid(mslp[0], mslp[1]) cs1 = ax.contour( x, y, mslp[2], mslp_clev, colors='k', linewidth=1.0, linestyles='solid', transform=datacrs) plt.clabel( cs1, fontsize=10, inline=1, inline_spacing=10, fmt='%i', rightside_up=True, use_clabeltext=True) # draw 500hPa geopotential height if gh500 is not None: x, y = np.meshgrid(gh500[0], gh500[1]) cs2 = ax.contour( x, y, gh500[2], gh500_clev, colors='w', linewidth=1.0, linestyles='dashed', transform=datacrs) plt.clabel( cs2, fontsize=10, inline=1, inline_spacing=10, fmt='%i', rightside_up=True, use_clabeltext=True) # add grid lines gl = ax.gridlines( crs=ccrs.PlateCarree(), draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--') gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER gl.xlabels_top = False gl.ylabels_right = False gl.xlabel_style = {'size': 16} gl.ylabel_style = {'size': 16} # add title ax.set_title(left_title, loc='left', fontsize=18) if right_title is not None: ax.set_title(right_title, loc='right', fontsize=18) # return plot return cf
def draw_gh500_uv850_mslp(ax, gh500=None, uv850=None, mslp=None, map_extent=(50, 150, 0, 65), add_china=True, regrid_shape=20): """ Draw 500-hPa geopotential height contours, 850-hPa wind barbs and mean sea level pressure filled contours. :param ax: `matplotlib.axes.Axes`, the `Axes` instance used for plotting. :param gh500: 500-hPa gh, dictionary: necessary, {'lon': 1D array, 'lat': 1D array, 'data': 2D array} optional, {'clevs': 1D array} :param uv850: 850-hPa u-component and v-component wind, dictionary: necessary, {'lon': 1D array, 'lat': 1D array, 'udata': 2D array, 'vdata': 2D array} :param mslp: MSLP, dictionary: necessary, {'lon': 1D array, 'lat': 1D array, 'data': 2D array} optional, {'clevs': 1D array} :param map_extent: [lonmin, lonmax, latmin, latmax], longitude and latitude range. :param add_china: add china map or not. :param regrid_shape: control the wind barbs density. :return: plots dictionary. :Examples: See dk_tool_weather_map.synoptic.gh500_uv850_mslp """ # set data projection datacrs = ccrs.PlateCarree() # plot map background ax.set_extent(map_extent, crs=datacrs) ax.add_feature(cfeature.LAND, facecolor='0.6') ax.coastlines('50m', edgecolor='black', linewidth=0.75, zorder=100) if add_china: add_china_map_2cartopy( ax, name='province', edgecolor='darkcyan', lw=1, zorder=100) # define return plots plots = {} # draw mean sea level pressure if mslp is not None: x, y = np.meshgrid(mslp['lon'], mslp['lat']) clevs = mslp.get('clevs') if clevs is None: clevs = np.arange(960, 1065, 5) cmap = guide_cmaps(26) plots['mslp'] = ax.contourf( x, y, np.squeeze(mslp['data']), clevs, cmap=cmap, alpha=0.8, zorder=10, transform=datacrs) # draw 850-hPa wind bards if uv850 is not None: x, y = np.meshgrid(uv850['lon'], uv850['lat']) u = np.squeeze(uv850['udata']) * 2.5 v = np.squeeze(uv850['vdata']) * 2.5 plots['uv850'] = ax.barbs( x, y, u, v, length=6, regrid_shape=regrid_shape, transform=datacrs, fill_empty=False, sizes=dict(emptybarb=0.05), zorder=20) # draw 500-hPa geopotential height if gh500 is not None: x, y = np.meshgrid(gh500['lon'], gh500['lat']) clevs = gh500.get('clevs') if clevs is None: clevs = np.append(np.arange(480, 584, 8), np.arange(580, 604, 4)) plots['gh500'] = ax.contour( x, y, np.squeeze(gh500['data']), clevs, colors='purple', linewidths=2, transform=datacrs, zorder=30) plt.clabel(plots['gh500'], inline=1, fontsize=16, fmt='%.0f') # grid lines gl = ax.gridlines( crs=datacrs, linewidth=2, color='gray', alpha=0.5, linestyle='--') gl.xlocator = mpl.ticker.FixedLocator(np.arange(0, 360, 15)) gl.ylocator = mpl.ticker.FixedLocator(np.arange(-90, 90, 15)) # return plots return plots
def draw_wind850(ax, lon, lat, u, v, mslp=None, gh500=None, thetae850=None, map_extent=(73, 136, 18, 54), wspeed_clev=np.arange(4, 40, 4), mslp_clev=np.arange(960, 1060, 4), wind_cmap=None, gh500_clev=np.arange(480, 600, 2), thetae850_clev=np.arange(280, 360, 4), draw_barbs=True, left_title="850hPa wind", right_title=None, add_china=True, coastline_color='black', title_font=None, cax=None, cb_title='850hPa wind speed (m/s)', cb_font=None): """ Draw 850hPa wind field. :param ax: matplotlib axes. :param lon: longitude coordinates. :param lat: latitude coordinates. :param u: u wind. :param v: v wind. :param mslp: mean sea level pressure, [lon, lat, mslp_data] :param gh500: 500hPa geopotential height, [lon, lat, gh500_data] :param thetae850: 850hPa equivalent potential temperature, [lon, lat, thetae850_data] :param map_extent: map extent :param wspeed_clev: wind speed filled contour levels. :param mslp_clev: mean sea level contour levels. :param wind_cmap: wind filled contour color map. :param gh500_clev: geopotential height 500 contour levels. :param thetae850_clev: 850hPa theta contour levels. :param draw_barbs: flag for drawing wind barbs. :param cax: color bar axes, if None, no color bar will be draw. :param left_title: left title. :param right_title: right title. :param add_china: draw china province boundary or not. :param coastline_color: coast line color. :param title_font: title font properties, like: title_font = mpl.font_manager.FontProperties( fname='C:/Windows/Fonts/SIMYOU.TTF') :param cb_title: color bar title :param cb_font: color bar title font properties :return: wind filled contour cf and barbs bb object. """ # set data projection, should be longitude and latitude. datacrs = ccrs.PlateCarree() # clear figure ax.clear # set map extent ax.set_extent(map_extent) # add map boundary ax.coastlines('50m', edgecolor=coastline_color) if add_china: add_china_map_2cartopy(ax, name='province', edgecolor='darkcyan') # draw 850hPa wind speed x, y = np.meshgrid(lon, lat) if wind_cmap is None: wind_cmap = guide_cmaps("2") cf = ax.contourf( x, y, np.sqrt(u*u + v*v), wspeed_clev, cmap=wind_cmap, transform=datacrs) if cax is not None: cb = plt.colorbar( cf, cax=cax, orientation='horizontal', extendrect=True, ticks=wspeed_clev) cb.set_label( cb_title, size='large', fontsize=16, fontproperties=cb_font) cb.ax.tick_params(labelsize=16) # draw wind barbs if draw_barbs: bb = ax.barbs( x, y, u, v, length=7, regrid_shape=15, transform=datacrs, sizes=dict(emptybarb=0.05)) # draw mean sea level pressure if mslp is not None: x, y = np.meshgrid(mslp[0], mslp[1]) cs1 = ax.contour( x, y, mslp[2], mslp_clev, colors='k', linewidth=1.0, linestyles='solid', transform=datacrs) plt.clabel( cs1, fontsize=10, inline=1, inline_spacing=10, fmt='%i', rightside_up=True, use_clabeltext=True) # draw 500hPa geopotential height if gh500 is not None: x, y = np.meshgrid(gh500[0], gh500[1]) cs2 = ax.contour( x, y, gh500[2], gh500_clev, colors='w', linewidth=1.0, linestyles='dashed', transform=datacrs) plt.clabel( cs2, fontsize=10, inline=1, inline_spacing=10, fmt='%i', rightside_up=True, use_clabeltext=True) # draw 850hPa equivalent potential temperature if thetae850 is not None: x, y = np.meshgrid(thetae850[0], thetae850[1]) cmap = plt.get_cmap("hsv") cs3 = ax.contour( x, y, thetae850[2], thetae850_clev, cmap=cmap, linewidth=0.8, linestyles='solid', transform=datacrs) plt.clabel( cs3, fontsize=10, inline=1, inline_spacing=10, fmt='%i', rightside_up=True, use_clabeltext=True) # add grid lines gl = ax.gridlines( crs=ccrs.PlateCarree(), draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--') gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER gl.xlabels_top = False gl.ylabels_right = False gl.xlabel_style = {'size': 16} gl.ylabel_style = {'size': 16} # add title ax.set_title( left_title, loc='left', fontsize=18, fontproperties=title_font) if right_title is not None: ax.set_title( right_title, loc='right', fontsize=18, fontproperties=title_font) # return plot if draw_barbs: return cf, bb else: return cf
def draw_uv850(ax, uv850=None, gh850=None, map_extent=(73, 136, 18, 54), add_china=True, regrid_shape=15): """ Draw 850-hPa wind field. :param ax: `matplotlib.axes.Axes`, the `Axes` instance used for plotting. :param uv850: 850-hPa u-component and v-component wind, dictionary: necessary, {'lon': 1D array, 'lat': 1D array, 'udata': 2D array, 'vdata': 2D array} optional, {'clevs': 1D array speed contour levels} :param gh850: optional, {'clevs': 1D array} :param map_extent: [lonmin, lonmax, latmin, latmax], longitude and latitude range. :param add_china: add china map or not. :param regrid_shape: control the wind barbs density. :return: plots dictionary. """ # set data projection datacrs = ccrs.PlateCarree() # plot map background ax.set_extent(map_extent, crs=datacrs) ax.add_feature(cfeature.LAND, facecolor='0.6') ax.coastlines('50m', edgecolor='black', linewidth=0.75, zorder=100) if add_china: add_china_map_2cartopy( ax, name='province', edgecolor='darkcyan', lw=1, zorder=100) # define return plots plots = {} # draw 850hPa wind speed and barbs if uv850 is not None: x, y = np.meshgrid(uv850['lon'], uv850['lat']) u = np.squeeze(uv850['udata']) v = np.squeeze(uv850['vdata']) clevs = uv850.get('clevs') if clevs is None: clevs = np.arange(4, 40, 4) cmaps = guide_cmaps("2") plots['uv850_cf'] = ax.contourf( x, y, np.sqrt(u * u + v * v), clevs, cmap=cmaps, transform=datacrs) plots['uv850_bb'] = ax.barbs( x, y, u*2.5, v*2.5, length=7, regrid_shape=regrid_shape, transform=datacrs, sizes=dict(emptybarb=0.05)) # draw 850hPa geopotential height if gh850 is not None: x, y = np.meshgrid(gh850['lon'], gh850['lat']) clevs = gh850.get('clevs') if clevs is None: clevs = np.arange(80, 180, 4) plots['gh850'] = ax.contour( x, y, np.squeeze(gh850['data']), clevs, colors='purple', linewidths=2, transform=datacrs, zorder=30) plt.clabel(plots['gh850'], inline=1, fontsize=16, fmt='%.0f') # add grid lines gl = ax.gridlines( crs=ccrs.PlateCarree(), draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--') gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER gl.xlabels_top = False gl.ylabels_right = False gl.xlabel_style = {'size': 16} gl.ylabel_style = {'size': 16} # return return plots
def fig_cldas_temp(indata, figsize=12, map_extent=(100, 125, 25, 45), gridlines=False, outfile=None, title="CLDAS Temperature", time=None): """Produce CLDAS temperature map figure. Arguments: indata {dictionary or xarray dataset} -- {'lon': 1D array, 'lat': 1D array, 'data': 2D array} Keyword Arguments: figsize {tuple or int} -- figure size (default: {12}) map_extent {tuple} -- (lonmin, lonmax, latmin, latmax) (default: {(100, 125, 25, 45)}) gridlines {bool} -- bool, draw grid lines or not. (default: {False}) outfile {string} -- save figure to outfile (default: {None}) title {string} -- figure title. time {datetime} -- analysis time. """ # check data type if isinstance(indata, xr.core.dataarray.DataArray): data = { 'lon': indata.coords['lon'].values, 'lat': indata.coords['lat'].values, 'data': np.squeeze(indata.values) } else: data = indata # set data projection datacrs = ccrs.PlateCarree() plotcrs = ccrs.AlbersEqualArea( central_latitude=(map_extent[2] + map_extent[3]) / 2.0, central_longitude=(map_extent[0] + map_extent[1]) / 2.0, standard_parallels=[30., 60.]) # set figure if isinstance(figsize, int): ratio = (map_extent[3] - map_extent[2]) / (map_extent[1] - map_extent[0]) figsize = (figsize, figsize * ratio * 0.8) fig = plt.figure(figsize=figsize) gs = mpl.gridspec.GridSpec(1, 2, width_ratios=[1, .02], bottom=.07, top=.99, hspace=0.01, wspace=0.01) ax = plt.subplot(gs[0], projection=plotcrs) # plot map background ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=1) add_china_map_2cartopy(ax, name='river', edgecolor='darkcyan', lw=1) # set color maps pos = np.array( [-45, -30, -20, -10, -5, 0, 0, 5, 5, 10, 20, 20, 30, 30, 40, 45]) colormap = cm_temperature_nws(pos) # draw CLDAS temperature x, y = np.meshgrid(data['lon'], data['lat']) pm = ax.pcolormesh(x, y, np.squeeze(data['data']), cmap=colormap, vmin=pos.min(), vmax=pos.max(), transform=datacrs) # add title plt.title(title, loc='left', fontsize=18) if time is not None: plt.title(time.strftime("%Y-%m-%dT%H"), loc='right', fontsize=18) # add grid lines if gridlines: add_gridlines(ax) # add color bar cax = plt.subplot(gs[1]) cb = plt.colorbar(pm, cax=cax, orientation='vertical', extendrect='True') cb.set_label('Temperature', size=12) # return if outfile is not None: fig.savefig(outfile) plt.close(fig) return None