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 show_obfo_rain_24(grid_fo,sta_ob,filename = None): fig = plt.figure(figsize=(6,3.6)) # 平面对比图 rect1 = [0.01,0.01,0.98,0.98] # 左下宽高 datacrs = ccrs.PlateCarree() ax = plt.axes(rect1,projection=datacrs) # 设置地图背景 map_extent = [grid_fo.slon,grid_fo.elon,grid_fo.slat,grid_fo.elat] ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=0.3) #省界 add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=0.3) #河流 #绘制格点预报场 x = np.arange(grid_fo.nlon) * grid_fo.dlon + grid_fo.slon y = np.arange(grid_fo.nlat) * grid_fo.dlat + grid_fo.slat clevs = [0.1, 10, 25, 50, 100, 250, 1000] colors_grid = ["#E0EEFA", "#B4D3E9", "#6FB0D7", "#3787C0", "#105BA4", "#07306B","#07306B"] plot_grid = ax.contourf(x, y, grid_fo.dat, clevs, colors = colors_grid, transform=datacrs) #填色图 colorbar_position_grid = fig.add_axes([0.035, 0.94, 0.25, 0.015]) # 位置[左,下,宽,高] plt.colorbar(plot_grid,cax = colorbar_position_grid,orientation='horizontal') plt.text(0.035, 0.955, "model accumulated precipition(mm)", fontsize=10) # 绘制填色站点值 sta_ob_in = bt.ssf.get_sta_in_grid(sta_ob, grid=grid_fo.grid) colors_sta = ['#FFFFFF','#0055FF','#00FFB4','#F4FF00','#FE1B00','#910000','#B800BA'] dat = sta_ob_in.values[:, 2] dat[dat>1000] = 0 clevs = [0,0.1, 10, 25, 50, 100, 250, 1000] cleves_name = ["0","0.1-10","10-25","25-50","50-100","100-250",">=250"] for i in range(len(clevs)-1): index0 = np.where((dat>=clevs[i])&(dat<clevs[i+1])) if (len(index0[0]) > 0): x = np.squeeze(sta_ob_in.values[index0, 0]) y = np.squeeze(sta_ob_in.values[index0, 1]) if (len(index0) == 1): x = np.array([x]) y = np.array([y]) if(i >0): ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=3,label=cleves_name[i],linewidths=0.3,edgecolor='k') else: ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=1, label=cleves_name[i]) ax.legend(facecolor ='whitesmoke',title = "observation",loc = "lower left",edgecolor = 'whitesmoke') #图片显示或保存 if(filename is None): plt.show() else: plt.savefig(filename,dpi = 300) plt.close() return
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_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_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_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 rain_24h_comprehensive_sg(sta_ob, grd_fo, filename=None): ''' #绘制24小时降水实况与预报综合对比检验图,画幅中央为预报实况的对比,左右两侧为各类检验指标 :param grd_fo: 输入的网格数据,包含一个平面的网格场 :param sta_ob: 输入的站点数据,包含一个时刻的站点数据列表 :param filename: 图片输出路径,缺省时会以调试窗口形式弹出 :return:无返回值 ''' grid_fo = nmc_verification.nmc_vf_base.get_grid_of_data(grd_fo) #通过经纬度范围设置画幅 hight = 5.6 title_hight = 0.3 legend_hight = 0.6 left_plots_width = 3 right_plots_width = 2.3 width = ( hight - title_hight - legend_hight ) * grid_fo.nlon / grid_fo.nlat + left_plots_width + right_plots_width map_width = width - left_plots_width - right_plots_width fig = plt.figure(figsize=(width, hight)) # 设置画幅的布局方式, rect1 = [ left_plots_width / width, 0.12, (width - right_plots_width - left_plots_width) / width, 0.84 ] # 左下宽高,中央对比图 ylabelwidth = 0.52 / width rect2 = [ ylabelwidth, 0.08, left_plots_width / width - ylabelwidth - 0.005, 0.40 ] # 左下宽高,散点回归图 ylabelwidth = 0.65 / width rect3 = [ ylabelwidth, 0.60, left_plots_width / width - ylabelwidth - 0.005, 0.18 ] # 左下宽高,频谱统计柱状图 rect4 = [0.01, 0.79, left_plots_width / width - 0.045, 0.15] # 左下宽高,左侧文字 rect5 = [(width - right_plots_width) / width + 0.005, -0.035, right_plots_width / width - 0.01, 0.90] # 左下宽高,右侧文字 width_ob_fo_str = 0.3 if (map_width < 3.5): width_bar = 2.1 #根据中间地图的宽度,来确定预报colorbar的尺寸 sta_legend_size = 5 #根据中间地图的宽度,来确定观测legend的size else: sta_legend_size = 7 width_bar = 2.9 rect6 = [(left_plots_width + 0.5 * map_width - 0.5 * width_bar + 0.5 * width_ob_fo_str) / width, 0.04, width_bar / width, 0.02] #预报colorbar rect7 = [(left_plots_width + 0.5 * map_width - 0.5 * width_bar - 0.5 * width_ob_fo_str) / width, 0.04, width_ob_fo_str / width, 0.3] #观测文字 #rect8 = [left_plots_width / width, 0.04, width_ob_fo_str / width, 0.3] # 预报文字 datacrs = ccrs.PlateCarree() ax = plt.axes(rect1, projection=datacrs) # 设置地图背景 map_extent = [grid_fo.slon, grid_fo.elon, grid_fo.slat, grid_fo.elat] ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=0.3) # 河流 # 绘制格点预报场 x = np.arange(grid_fo.nlon) * grid_fo.dlon + grid_fo.slon y = np.arange(grid_fo.nlat) * grid_fo.dlat + grid_fo.slat clevs = [0.1, 10, 25, 50, 100, 250, 1000] colors_grid = [ "#D0DEEA", "#B4D3E9", "#6FB0D7", "#3787C0", "#105BA4", "#07306B", "#07306B" ] dat = grd_fo.values.squeeze() #print(x) #print(y) #print(dat) plot_grid = ax.contourf(x, y, dat, clevs, colors=colors_grid, transform=datacrs) # 填色图 time_str = nmc_verification.nmc_vf_base.tool.time_tools.time_to_str( grid_fo.gtime[0]) dati_str = time_str[0:4] + "年" + time_str[4:6] + "月" + time_str[ 6:8] + "日" + time_str[8:10] + "时" if type(grid_fo.members[0]) == str: model_name = grid_fo.members[0] else: model_name = str(grid_fo.members[0]) if map_width < 3: title = model_name + " " + dati_str + "起报" + str( grid_fo.dtimes[0]) + "H时效预报和观测" ax.set_title(title, fontsize=7) elif map_width < 4: title = model_name + " " + dati_str + "起报" + str( grid_fo.dtimes[0]) + "H时效预报和观测" ax.set_title(title, fontsize=10) else: title = model_name + " " + dati_str + "起报" + str( grid_fo.dtimes[0]) + "H时效预报和观测" ax.set_title(title, fontsize=11) colorbar_position_grid = fig.add_axes(rect6) # 位置[左,下,宽,高] cb = plt.colorbar(plot_grid, cax=colorbar_position_grid, orientation='horizontal') cb.ax.tick_params(labelsize=8) # 设置色标刻度字体大小。 #plt.text(0, 0, "预报(mm)", fontsize=8) # 绘制填色站点值 sta_ob_in = nmc_verification.nmc_vf_base.function.get_from_sta_data.sta_in_grid_xy( sta_ob, grid=grid_fo) colors_sta = [ '#FFFFFF', '#0055FF', '#00FFB4', '#F4FF00', '#FE1B00', '#910000', '#B800BA' ] dat = sta_ob_in.values[:, -1] dat[dat > 1000] = 0 clevs = [0, 0.1, 10, 25, 50, 100, 250, 1000] cleves_name = [ "0", "0.1-10", "10-25", "25-50", "50-100", "100-250", ">=250" ] for i in range(len(clevs) - 1): index0 = np.where((dat >= clevs[i]) & (dat < clevs[i + 1])) if (len(index0[0]) > 0): x = np.squeeze(sta_ob_in["lon"].values[index0]) y = np.squeeze(sta_ob_in["lat"].values[index0]) if (len(index0) == 1): x = np.array([x]) y = np.array([y]) if (i > 0): ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=3, label=cleves_name[i], linewidths=0.3, edgecolor='k') else: ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=1, label=cleves_name[i], linewidths=0.1, edgecolor="k") ax.legend(facecolor='whitesmoke', loc="lower center", ncol=4, edgecolor='whitesmoke', prop={'size': sta_legend_size}, bbox_to_anchor=(0.5 + 0.5 * width_ob_fo_str / map_width, -0.08)) ax7 = plt.axes(rect7) ax7.axes.set_axis_off() plt.text(0, 0.00, "观测\n\n预报", fontsize=7) #ax.legend(loc="lower right", ncol=4, facecolor='whitesmoke', title="观测", edgecolor='whitesmoke', fontsize=9, # bbox_to_anchor=(0, -0.32)) # 散点回归图 ax2 = plt.axes(rect2) sta_fo = nmc_verification.nmc_vf_base.function.gxy_sxy.interpolation_linear( grd_fo, sta_ob_in) #print(sta_fo) data_name = nmc_verification.nmc_vf_base.get_data_names(sta_ob_in) ob = sta_ob_in[data_name[0]].values data_name = nmc_verification.nmc_vf_base.get_data_names(sta_fo) fo = sta_fo[data_name[0]].values ax2.plot(ob, fo, '.', color='k') # 绘制比例线 rate = np.sum(fo) / (np.sum(ob) + 1e-30) ob_line = np.arange(0, np.max(ob), np.max(ob) / 30) fo_rate = ob_line * rate ax2.plot(ob_line[0:20], fo_rate[0:20], 'r') # 绘制回归线 X = np.zeros((len(ob), 1)) X[:, 0] = ob[:] clf = LinearRegression().fit(X, fo) X = np.zeros((len(ob_line), 1)) X[:, 0] = ob_line[:] fo_rg = clf.predict(X) ax2.plot(ob_line, fo_rg, color='b', linestyle='dashed') cor = np.corrcoef(ob, fo) rg_text1 = "R = " + '%.2f' % (cor[0, 1]) rg_text2 = "y = " + '%.2f' % (clf.coef_[0]) + "* x + " + '%.2f' % ( clf.intercept_) maxy = max(np.max(ob), np.max(fo)) + 5 plt.xlim(0, maxy) plt.ylim(0, maxy) plt.text(0.05 * maxy, 0.9 * maxy, rg_text1, fontsize=10) plt.text(0.05 * maxy, 0.8 * maxy, rg_text2, fontsize=10) maxy = max(np.max(ob), np.max(fo)) ax2.set_xlabel("观测", fontsize=9) ax2.set_ylabel("预报", fontsize=9) ax2.set_title("Obs.vs Pred. Scatter plot", fontsize=12) # 设置次刻度间隔 xmi = 2 if (np.max(ob) > 100): xmi = 5 ymi = 2 if (np.max(fo) > 100): ymi = 5 xmajorLocator = mpl.ticker.MultipleLocator(10 * xmi) # 将x主刻度标签设置为次刻度10倍 ymajorLocator = mpl.ticker.MultipleLocator(10 * ymi) # 将y主刻度标签设置为次刻度10倍 ax2.xaxis.set_major_locator(xmajorLocator) ax2.yaxis.set_major_locator(ymajorLocator) xminorLocator = mpl.ticker.MultipleLocator(xmi) # 将x轴次刻度标签设置xmi yminorLocator = mpl.ticker.MultipleLocator(ymi) # 将y轴次刻度标签设置ymi ax2.xaxis.set_minor_locator(xminorLocator) ax2.yaxis.set_minor_locator(yminorLocator) # 绘制频率柱状图 p_ob = np.zeros(6) p_fo = np.zeros(6) x = np.arange(6) + 1 for i in range(1, len(clevs) - 1, 1): index0 = np.where((ob >= clevs[i]) & (ob < clevs[i + 1])) p_ob[i - 1] = len(index0[0]) index0 = np.where((fo >= clevs[i]) & (fo < clevs[i + 1])) p_fo[i - 1] = len(index0[0]) ax3 = plt.axes(rect3) ax3.bar(x - 0.25, p_ob, width=0.2, facecolor="r", label="Obs") ax3.bar(x + 0.05, p_fo, width=0.2, facecolor="b", label="Pred") ax3.legend(loc="upper right") ax3.set_xlabel("precipitation threshold", fontsize=10) ax3.set_xticks(x) ax3.set_xticklabels( ["0.1-10", "10-25", "25-50", "50-100", "100-250", ">=250"], fontsize=9) ax3.set_ylabel("point number", fontsize=10) ax3.yaxis.set_minor_locator(mpl.ticker.MultipleLocator(100)) # 绘制降水站点实况预报统计表 ax4 = plt.axes(rect4) ax4.axes.set_axis_off() ob_has = ob[ob >= 0.01] fo_has = fo[fo >= 0.01] text = "降水站点实况和预报 n=" + str(len(ob)) + "\n" text += "=============================================\n" text += " 观测 预报\n" text += "---------------------------------------------\n" text += "有降水站点数(>=0.01) " + "%4d" % len( ob_has) + " %4d" % len(fo_has) + "\n" text += "有降水站点数百分比% " + "%6.1f" % (len(ob_has) / len(ob)) + "%15.1f" % ( len(fo_has) / len(fo)) + "\n" text += "平均降水量(排除无降水) " + "%6.1f" % (np.mean(ob_has)) + "%15.1f" % ( np.mean(fo_has)) + "\n" text += "最大降水量 " + "%6.1f" % (np.max(ob_has)) + "%15.1f" % ( np.max(fo_has)) + "\n" text += "---------------------------------------------" plt.text(0, 0, text, fontsize=9) # 绘制统计检验结果 ax5 = plt.axes(rect5) ax5.axes.set_axis_off() mae = nmc_verification.nmc_vf_method.continuous.score.mae(ob, fo) me = nmc_verification.nmc_vf_method.continuous.score.me(ob, fo) mse = nmc_verification.nmc_vf_method.continuous.score.mse(ob, fo) rmse = nmc_verification.nmc_vf_method.continuous.score.rmse(ob, fo) bias_c = nmc_verification.nmc_vf_method.continuous.score.bias(ob, fo) cor = nmc_verification.nmc_vf_method.continuous.score.corr(ob, fo) hit, mis, fal, co = nmc_verification.nmc_vf_method.yes_or_no.score.hmfn( ob, fo, clevs[1:]) ts = nmc_verification.nmc_vf_method.yes_or_no.score.ts(ob, fo, clevs[1:]) ets = nmc_verification.nmc_vf_method.yes_or_no.score.ets(ob, fo, clevs[1:]) bias = nmc_verification.nmc_vf_method.yes_or_no.score.bias( ob, fo, clevs[1:]) hit_rate = nmc_verification.nmc_vf_method.yes_or_no.score.hit_rate( ob, fo, clevs[1:]) mis_rate = nmc_verification.nmc_vf_method.yes_or_no.score.mis_rate( ob, fo, clevs[1:]) fal_rate = nmc_verification.nmc_vf_method.yes_or_no.score.fal_rate( ob, fo, clevs[1:]) text = str(len(ob)) + "评分站点预报检验统计量\n" text += "Mean absolute error:" + "%6.2f" % mae + "\n" text += "Mean error:" + "%6.2f" % me + "\n" text += "Mean-squared error:" + "%6.2f" % mse + "\n" text += "Root mean-squared error:" + "%6.2f" % rmse + "\n" text += "Bias:" + "%6.2f" % bias_c + "\n" text += "Correctlation coefficiant:" + "%6.2f" % cor + "\n\n\n" leves_name = [ "0.1-10-", "10-25--", "25-50--", "50-100-", "100-250", ">=250-" ] for i in range(len(leves_name)): text += ":" + leves_name[i] + "---------------------------\n" text += "正确:" + "%-4d" % hit[i] + " 空报:" + "%-4d" % fal[ i] + " 漏报:" + "%-4d" % mis[i] + "\n" text += "TS:" + "%5.3f" % ts[ i] + " ETS:" + "%5.3f" % ets[i] + "\n" text += "Hit rate:" + "%5.3f" % hit_rate[ i] + " Miss rate: " + "%5.3f" % mis_rate[i] + "\n" text += "False alarm ratio:" + "%5.3f" % fal_rate[ i] + " Bias:" + "%5.3f" % bias[i] + "\n\n" plt.text(0, 0.00, text, fontsize=9) # 图片显示或保存 if (filename is None): plt.show() print() else: plt.savefig(filename, dpi=300) plt.close() return
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_composite_map(date_obj, t850, u200, v200, u500, v500, mslp, gh500, u850, v850, pwat): """ Draw synoptic composite map. Args: map_subset (int, optional): [description]. Defaults to 1. map_region (list, optional): [description]. Defaults to [70, 140, 20, 60]. """ #Get lat and lon arrays for this dataset: lat = t850.lat.values lon = t850.lon.values #======================================================================================================== # Create a Basemap plotting figure and add geography #======================================================================================================== #Create a Plate Carree projection object proj_ccrs = ccrs.Miller(central_longitude=0.0) #Create figure and axes for main plot and colorbars fig = plt.figure(figsize=(18, 12), dpi=125) gs = gridspec.GridSpec(12, 36, figure=fig) #[ytop:ybot, xleft:xright] ax = plt.subplot(gs[:, :-1], projection=proj_ccrs) #main plot ax.set_xticklabels([]) ax.set_yticklabels([]) ax2 = plt.subplot(gs[:4, -1]) #top plot ax2.set_xticklabels([]) ax2.set_yticklabels([]) ax3 = plt.subplot(gs[4:8, -1]) #bottom plot ax3.set_xticklabels([]) ax3.set_yticklabels([]) ax4 = plt.subplot(gs[8:, -1]) #bottom plot ax4.set_xticklabels([]) ax4.set_yticklabels([]) #Add political boundaries and coastlines ax.add_feature(cfeature.COASTLINE.with_scale('50m'), linewidths=1.2) ax.add_feature(cfeature.BORDERS.with_scale('50m'), linewidths=1.2) ax.add_feature(cfeature.STATES.with_scale('50m'), linewidths=0.5) #Add land/lake/ocean masking land_mask = cfeature.NaturalEarthFeature('physical', 'land', '50m', edgecolor='face', facecolor='#e6e6e6') sea_mask = cfeature.NaturalEarthFeature('physical', 'ocean', '50m', edgecolor='face', facecolor='#ffffff') lake_mask = cfeature.NaturalEarthFeature('physical', 'lakes', '50m', edgecolor='face', facecolor='#ffffff') ax.add_feature(sea_mask, zorder=0) ax.add_feature(land_mask, zorder=0) ax.add_feature(lake_mask, zorder=0) #======================================================================================================== # Fill contours #======================================================================================================== #-------------------------------------------------------------------------------------------------------- # 850-hPa temperature #-------------------------------------------------------------------------------------------------------- #Specify contour settings clevs = np.arange(-40, 40, 1) cmap = plt.cm.jet extend = "both" #Contour fill this variable norm = col.BoundaryNorm(clevs, cmap.N) cs = ax.contourf(lon, lat, t850, clevs, cmap=cmap, norm=norm, extend=extend, transform=proj_ccrs, alpha=0.1) #-------------------------------------------------------------------------------------------------------- # PWAT #-------------------------------------------------------------------------------------------------------- #Specify contour settings clevs = np.arange(20, 71, 0.5) #Define a color gradient for PWAT pwat_colors = gradient([[(255, 255, 255), 0.0], [(255, 255, 255), 20.0]], [[(205, 255, 205), 20.0], [(0, 255, 0), 34.0]], [[(0, 255, 0), 34.0], [(0, 115, 0), 67.0]]) cmap = pwat_colors.get_cmap(clevs) extend = "max" #Contour fill this variable norm = col.BoundaryNorm(clevs, cmap.N) cs = ax.contourf(lon, lat, pwat, clevs, cmap=cmap, norm=norm, extend=extend, transform=proj_ccrs, alpha=0.9) #Add a color bar cbar = plt.colorbar(cs, cax=ax2, shrink=0.75, pad=0.01, ticks=[20, 30, 40, 50, 60, 70]) #-------------------------------------------------------------------------------------------------------- # 250-hPa wind #-------------------------------------------------------------------------------------------------------- #Get the data for this variable wind = calc.wind_speed(u200, v200) #Specify contour settings clevs = [40, 50, 60, 70, 80, 90, 100, 110] cmap = col.ListedColormap([ '#99E3FB', '#47B6FB', '#0F77F7', '#AC97F5', '#A267F4', '#9126F5', '#E118F3', '#E118F3' ]) extend = "max" #Contour fill this variable norm = col.BoundaryNorm(clevs, cmap.N) cs = ax.contourf(lon, lat, wind, clevs, cmap=cmap, norm=norm, extend=extend, transform=proj_ccrs) #Add a color bar cbar = plt.colorbar(cs, cax=ax3, shrink=0.75, pad=0.01, ticks=clevs) #-------------------------------------------------------------------------------------------------------- # 500-hPa smoothed vorticity #-------------------------------------------------------------------------------------------------------- #Get the data for this variable dx, dy = calc.lat_lon_grid_deltas(lon, lat) vort = calc.vorticity(u500, v500, dx, dy) smooth_vort = smooth(vort, 5.0) * 10**5 #Specify contour settings clevs = np.arange(2, 20, 1) cmap = plt.cm.autumn_r extend = "max" #Contour fill this variable norm = col.BoundaryNorm(clevs, cmap.N) cs = ax.contourf(lon, lat, smooth_vort, clevs, cmap=cmap, norm=norm, extend=extend, transform=proj_ccrs, alpha=0.3) #Add a color bar cbar = plt.colorbar(cs, cax=ax4, shrink=0.75, pad=0.01, ticks=clevs[::2]) #======================================================================================================== # Contours #======================================================================================================== #-------------------------------------------------------------------------------------------------------- # MSLP #-------------------------------------------------------------------------------------------------------- #Specify contour settings clevs = np.arange(960, 1040 + 4, 4) style = 'solid' #Plot solid lines color = 'red' #Plot lines as gray width = 0.8 #Width of contours 0.25 #Contour this variable cs = ax.contour(lon, lat, mslp, clevs, colors=color, linewidths=width, linestyles=style, transform=proj_ccrs, alpha=0.9) #Include value labels ax.clabel(cs, inline=1, fontsize=9, fmt='%d') #-------------------------------------------------------------------------------------------------------- # Geopotential heights #-------------------------------------------------------------------------------------------------------- #Get the data for this variable gh500 = gh500 / 10.0 #Specify contour settings clevs = np.arange(480, 612, 4) style = 'solid' #Plot solid lines color = 'black' #Plot lines as gray width = 2.0 #Width of contours #Contour this variable cs = ax.contour(lon, lat, gh500, clevs, colors=color, linewidths=width, linestyles=style, transform=proj_ccrs) #Include value labels ax.clabel(cs, inline=1, fontsize=12, fmt='%d') #-------------------------------------------------------------------------------------------------------- # Surface barbs #-------------------------------------------------------------------------------------------------------- #Plot wind barbs quivers = ax.quiver(lon, lat, u850.values, v850.values, transform=proj_ccrs, regrid_shape=(38, 30), scale=820, alpha=0.5) #-------------------------------------------------------------------------------------------------------- # Label highs & lows #-------------------------------------------------------------------------------------------------------- #Label highs and lows add_mslp_label(ax, proj_ccrs, mslp, lat, lon) #======================================================================================================== # Step 6. Add map boundary, legend, plot title, then save image and close #======================================================================================================== #Add china province boundary add_china_map_2cartopy(ax, name='province') #Add custom legend from matplotlib.lines import Line2D custom_lines = [ Line2D([0], [0], color='#00A123', lw=5), Line2D([0], [0], color='#0F77F7', lw=5), Line2D([0], [0], color='#FFC000', lw=5), Line2D([0], [0], color='k', lw=2), Line2D([0], [0], color='k', lw=0.1, marker=r'$\rightarrow$', ms=20), Line2D([0], [0], color='r', lw=0.8), ] ax.legend(custom_lines, [ 'PWAT (mm)', '200-hPa Wind (m/s)', '500-hPa Vorticity', '500-hPa Height (dam)', '850-hPa Wind (m/s)', 'MSLP (hPa)' ], loc=2, prop={'size': 12}) #Format plot title title = "Synoptic Composite \nValid: " + dt.datetime.strftime( date_obj, '%Y-%m-%d %H%M UTC') st = plt.suptitle(title, fontweight='bold', fontsize=16) st.set_y(0.92) #Return figuration return (fig)
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
def cref_uv850(initial_time, fhour=0, model='ShangHai', map_center=(117, 39), map_width=12, draw_wind=False): """ Analysis composite reflectivity and 850hPa wind. Arguments: initial_time {string or datetime}} -- initial time, string or datetime ojbect. like '18042008' or datetime(2018, 4, 20, 8). Keyword Arguments: fhour {int} -- forecast hour (default: {0}) model {str} -- model name (default: {'ShangHai'}) map_center {tuple} -- map center (default: {(117, 39)}) map_width {int} -- map width (default: {12}) draw_wind {bool} -- draw 850hPa wind or not (default: {False}) Raises: ValueError -- [description] """ # micaps data directory data_dirs = { 'SHANGHAI': ['SHANGHAI_HR/COMPOSITE_REFLECTIVITY/ENTIRE_ATMOSPHERE', 'SHANGHAI_HR/UGRD/850', 'SHANGHAI_HR/VGRD/850'], 'BEIJING': ['BEIJING_MR/COMPOSITE_REFLECTIVITY/ENTIRE_ATMOSPHERE', 'BEIJING_MR/UGRD/850', 'BEIJING_MR/VGRD/850'], 'GRAPES_MESO': ['GRAPES_MESO_HR/RADAR_COMBINATION_REFLECTIVITY', 'GRAPES_MESO_HR/UGRD/850', 'GRAPES_MESO_HR/VGRD/850'], 'GRAPES_3KM': ['GRAPES_3KM/RADAR_COMBINATION_REFLECTIVITY', 'GRAPES_3KM/UGRD/850', 'GRAPES_3KM/VGRD/850']} try: data_dir = data_dirs[model.strip().upper()] except KeyError: raise ValueError('Unknown model, choose ShangHai, BeiJing, Grapes_meso of Grapes_3km.') # get filename filename = model_filename(initial_time, fhour) # retrieve data from micaps server cref = get_model_grid(data_dir[0], filename=filename) if cref is None: return init_time = cref.coords['init_time'].values[0] if draw_wind: u850 = get_model_grid(data_dir[1], filename=filename) if u850 is None: return v850 = get_model_grid(data_dir[2], filename=filename) if v850 is None: return # prepare data data = np.ma.masked_array(cref.values) data[data == 9999] = np.ma.masked data[data < 10] = np.ma.masked cref_data = {'lon':cref.coords['lon'].values, 'lat':cref.coords['lat'].values, 'data':np.squeeze(data)} if draw_wind: uv850 = {'lon': u850.coords['lon'].values, 'lat': u850.coords['lat'].values, 'udata': np.squeeze(u850.values), 'vdata': np.squeeze(v850.values)} # set up map projection datacrs = ccrs.PlateCarree() plotcrs = ccrs.AlbersEqualArea( central_latitude=map_center[1], central_longitude=map_center[0], standard_parallels=[30., 60.]) # set up figure fig = plt.figure(figsize=(12, 9)) gs = mpl.gridspec.GridSpec( 1, 2, width_ratios=[1, .03], bottom=.01, top=.99, hspace=0.01, wspace=0.01) ax = plt.subplot(gs[0], projection=plotcrs) # add model title add_model_title( 'CREF (dBz), 850-hPa Winds', init_time, model=model, fhour=fhour, fontsize=18, multilines=True) # add map background map_extent = ( map_center[0] - map_width/2.0, map_center[0] + map_width/2.0, map_center[1] - map_width/3.0, map_center[1] + map_width/3.0) ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy( ax, name='province', edgecolor='black', lw=2, zorder=100) # draw composite reflectivity x, y = np.meshgrid(cref_data['lon'], cref_data['lat']) norm, cmap = colortables.get_with_steps('NWSReflectivity', 12, 4) pm = ax.pcolormesh(x, y, cref_data['data'], norm=norm, cmap=cmap, transform=datacrs) cax = plt.subplot(gs[1]) cb = plt.colorbar(pm, cax=cax, orientation='vertical', extendrect='True') cb.set_label('Composite reflectivity', size=12) # draw wind vector if draw_wind: x, y = np.meshgrid(uv850['lon'], uv850['lat']) ax.quiver(x, y, uv850['udata'], uv850['vdata'], transform=datacrs, regrid_shape=25) # show figure gs.tight_layout(fig) plt.show()
def cref_uv850_compare(initial_time, fhour=0, map_center=(117, 39), map_width=12, draw_wind=False): """ Compare mesoscale model's composite reflectivity. Arguments: initial_time {string or datetime}} -- initial time, string or datetime ojbect. like '18042008' or datetime(2018, 4, 20, 8). Keyword Arguments: fhour {int} -- forecast hour (default: {0}) map_center {tuple} -- map center (default: {(117, 39)}) map_width {int} -- map width (default: {12}) draw_wind {bool} -- draw 850hPa wind or not (default: {False}) """ # micaps data directory data_dirs = {'SHANGHAI': ['SHANGHAI_HR/COMPOSITE_REFLECTIVITY/ENTIRE_ATMOSPHERE', 'SHANGHAI_HR/UGRD/850', 'SHANGHAI_HR/VGRD/850'], 'BEIJING': ['BEIJING_MR/COMPOSITE_REFLECTIVITY/ENTIRE_ATMOSPHERE', 'BEIJING_MR/UGRD/850', 'BEIJING_MR/VGRD/850'], 'GRAPES_MESO': ['GRAPES_MESO_HR/RADAR_COMBINATION_REFLECTIVITY', 'GRAPES_MESO_HR/UGRD/850', 'GRAPES_MESO_HR/VGRD/850'], 'GRAPES_3KM': ['GRAPES_3KM/RADAR_COMBINATION_REFLECTIVITY', 'GRAPES_3KM/UGRD/850', 'GRAPES_3KM/VGRD/850']} # get filename filename = model_filename(initial_time, fhour) # set up map projection datacrs = ccrs.PlateCarree() plotcrs = ccrs.AlbersEqualArea( central_latitude=map_center[1], central_longitude=map_center[0], standard_parallels=[30., 60.]) # set up figure fig = plt.figure(figsize=(16, 12)) axes_class = (GeoAxes, dict(map_projection=plotcrs)) grid = AxesGrid(fig, 111, axes_class=axes_class, nrows_ncols=(2, 2), axes_pad=0.05, cbar_location='right', cbar_mode='single', cbar_pad=0.05, label_mode='') # loop every data directory for index, key in enumerate(data_dirs): # get axis and data directory ax = grid[index] data_dir = data_dirs[key] # retrieve data from micaps server cref = get_model_grid(data_dir[0], filename=filename) if cref is None: return init_time = cref.coords['init_time'].values[0] if draw_wind: u850 = get_model_grid(data_dir[1], filename=filename) if u850 is None: return v850 = get_model_grid(data_dir[2], filename=filename) if v850 is None: return # add title if index == 0: initial_str, fhour_str, valid_str = get_model_time_stamp(init_time, fhour) fig.suptitle('CREF (dBz), 850-hPa Winds ' + initial_str + '; ' + fhour_str + '; ' + valid_str, x=0.5, y=0.9, fontsize=16) # prepare data data = np.ma.masked_array(cref.values) data[data == 9999] = np.ma.masked data[data < 10] = np.ma.masked cref_data = {'lon':cref.coords['lon'].values, 'lat':cref.coords['lat'].values, 'data':np.squeeze(data)} if draw_wind: uv850 = {'lon': u850.coords['lon'].values, 'lat': u850.coords['lat'].values, 'udata': np.squeeze(u850.values), 'vdata': np.squeeze(v850.values)} # add map background map_extent = ( map_center[0] - map_width/2.0, map_center[0] + map_width/2.0, map_center[1] - map_width/3.0, map_center[1] + map_width/3.0) ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy( ax, name='province', edgecolor='black', lw=2, zorder=100) # draw composite reflectivity x, y = np.meshgrid(cref_data['lon'], cref_data['lat']) norm, cmap = colortables.get_with_steps('NWSReflectivity', 12, 4) pm = ax.pcolormesh(x, y, cref_data['data'], norm=norm, cmap=cmap, transform=datacrs) # draw wind vector if draw_wind: x, y = np.meshgrid(uv850['lon'], uv850['lat']) ax.quiver(x, y, uv850['udata'], uv850['vdata'], transform=datacrs, regrid_shape=25) # add title add_titlebox(ax, key) # add color bar cbar = grid.cbar_axes[0].colorbar(pm) # show figure plt.show()
def temper_comprehensive_gg(grd_ob, grd_fo, filename=None): ob_min = np.min(grd_ob.values) fo_min = np.min(grd_fo.values) ob_max = np.max(grd_ob.values) fo_max = np.max(grd_fo.values) ob_fo_max = max(ob_max, fo_max) ob_fo_min = min(ob_min, fo_min) clevs_temp, cmap_temp = nmc_verification.nmc_vf_base.tool.color_tools.get_clev_and_cmap_by_element_name( "temp") clevs, cmap = nmc_verification.nmc_vf_base.tool.color_tools.get_part_clev_and_cmap( clevs_temp, cmap_temp, ob_fo_max, ob_fo_min) width = 9 #整个画面的宽度 width_colorbar = 0.6 height_title = 0.3 height_veri_plot = 3 grid0 = nmc_verification.nmc_vf_base.get_grid_of_data(grd_fo) if (grid0.nlon <= grid0.nlat * 0.5): #采用3*1布局 width_map = (width - 2 * width_colorbar) / 3 height_map = (grid0.nlat / grid0.nlon) * width_map height = height_map + height_title + height_veri_plot rect1 = [ width_colorbar / width, height_veri_plot / height, width_map / width, height_map / height ] # 实况 rect2 = [(1 * width_map + width_colorbar) / width, height_veri_plot / height, width_map / width, height_map / height] # 预报 rect3 = [(2 * width_map + width_colorbar + 0.05) / width, height_veri_plot / height, width_map / width, height_map / height] # 误差 ob_fo_colorbar_box = [ 0.02, height_veri_plot / height, 0.015, height_map / height ] error_colorbar_box = [(3 * width_map + width_colorbar + 0.05) / width, height_veri_plot / height, 0.015, height_map / height] else: #采用1*2 + 1 布局 width_map = (width - 2 * width_colorbar) / 1.5 height_map = (grid0.nlat / grid0.nlon) * width_map height = height_map + height_title + height_veri_plot rect1 = [(width_colorbar - 0.03) / width, (height_veri_plot + 0.5 * height_map) / height, 0.5 * width_map / width, 0.5 * height_map / height] # 实况 rect2 = [(width_colorbar - 0.03) / width, height_veri_plot / height, 0.5 * width_map / width, 0.5 * height_map / height] # 预报 rect3 = [(0.5 * width_map + width_colorbar) / width, height_veri_plot / height, width_map / width, height_map / height] # 误差 ob_fo_colorbar_box = [ 0.02, height_veri_plot / height, 0.01, height_map / height ] error_colorbar_box = [ (1.5 * width_map + width_colorbar + 0.05) / width, height_veri_plot / height, 0.01, height_map / height ] rect4 = [0.05, 0.06, 0.26, height_veri_plot / height - 0.1] # 散点回归图,左下宽高 rect5 = [0.38, 0.06, 0.28, height_veri_plot / height - 0.1] # 频率柱状图, 左下宽高 rect6 = [0.67, 0.01, 0.3, height_veri_plot / height - 0.03] # 左下宽高 rect_title = [ width_colorbar / width, (height_veri_plot + height_map) / height, 1 - 2 * width_colorbar / width, 0.001 ] fig = plt.figure(figsize=(width, height)) # 平面对比图1 datacrs = ccrs.PlateCarree() ax1 = plt.axes(rect1, projection=datacrs) ax2 = plt.axes(rect2, projection=datacrs) ax3 = plt.axes(rect3, projection=datacrs) # 设置地图背景 map_extent = [grid0.slon, grid0.elon, grid0.slat, grid0.elat] ax1.set_extent(map_extent, crs=datacrs) ax2.set_extent(map_extent, crs=datacrs) ax3.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax1, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax1, name='river', edgecolor='blue', lw=0.3) # 河流 add_china_map_2cartopy(ax2, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax2, name='river', edgecolor='blue', lw=0.3) # 河流 add_china_map_2cartopy(ax3, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax3, name='river', edgecolor='blue', lw=0.3) # 河流 # 绘制格点预报场 x = np.arange(grid0.nlon) * grid0.dlon + grid0.slon y = np.arange(grid0.nlat) * grid0.dlat + grid0.slat #clevs = [-10,0,15,20,22,24,26,28,30,32,34,35] #colors_grid = ["#00AAAA","#009500","#808000", "#BFBF00","#FFFF00","#FFD400","#FFAA00","#FF7F00","#FF0000","#FF002A","#FF0055","#FF0055"] plot_grid = ax1.contourf(x, y, grd_ob.values.squeeze(), levels=clevs, cmap=cmap, transform=datacrs) # 填色图 ax1.set_title("实况", fontsize=12, loc="left", y=0.0) colorbar_position_grid = fig.add_axes(ob_fo_colorbar_box) # 位置[左,下,宽,高] plt.colorbar(plot_grid, cax=colorbar_position_grid, orientation='vertical') plt.title("温度(℃)", fontsize=8, verticalalignment='bottom') ax2.contourf(x, y, grd_fo.values.squeeze(), levels=clevs, cmap=cmap, transform=datacrs) # 填色图 ax2.set_title("预报", fontsize=12, loc="left", y=0.0) clevs1 = [-5, -4, -3, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 3, 4, 5] error = grd_fo.values.squeeze() - grd_ob.values.squeeze() plot_grid1 = ax3.contourf(x, y, error, clevs1, cmap="bwr", transform=datacrs) # 填色图 colorbar_position_grid1 = fig.add_axes(error_colorbar_box) # 位置[左,下,宽,高] ax3.set_title("预报 - 实况", fontsize=12, loc="left", y=0.0) plt.colorbar(plot_grid1, cax=colorbar_position_grid1, orientation='vertical') plt.title("误差(℃)", fontsize=8, verticalalignment='bottom') time_str = nmc_verification.nmc_vf_base.tool.time_tools.time_to_str( grid0.gtime[0]) dati_str = time_str[0:4] + "年" + time_str[4:6] + "月" + time_str[ 6:8] + "日" + time_str[8:10] + "时" if type(grid0.members[0]) == str: model_name = grid0.members[0] else: model_name = str(grid0.members[0]) title = model_name + " " + dati_str + "起报" + str( grid0.dtimes[0]) + "H时效预报和实况对比及误差" ax_title = plt.axes(rect_title) ax_title.axes.set_axis_off() ax_title.set_title(title) # 散点回归图 ax2 = plt.axes(rect4) # 保证这两个值的正确性 ob = grd_ob.values.flatten() fo = grd_fo.values.flatten() print(len(ob), len(fo)) ax2.plot(ob, fo, '.', color='k') print(np.sum(fo), np.sum(ob)) # 绘制比例线 rate = np.sum(fo) / np.sum(ob) ob_line = np.arange(0, np.max(ob), np.max(ob) / 30) fo_rate = ob_line * rate ax2.plot(ob_line[0:20], fo_rate[0:20], 'r') # 绘制回归线 X = np.zeros((len(ob), 1)) X[:, 0] = ob[:] #绘制回归线 clf = LinearRegression().fit(X, fo) X = np.zeros((len(ob_line), 1)) X[:, 0] = ob_line[:] fo_rg = clf.predict(X) ax2.plot(ob_line, fo_rg, color='b', linestyle='dashed') rg_text1 = "R = " + '%.2f' % (np.corrcoef(ob, fo)[0, 1]) rg_text2 = "y = " + '%.2f' % (clf.coef_[0]) + "* x + " + '%.2f' % ( clf.intercept_) maxy = max(np.max(ob), np.max(fo)) + 5 plt.xlim(0, maxy) plt.ylim(0, maxy) plt.text(0.05 * maxy, 0.9 * maxy, rg_text1, fontsize=10) plt.text(0.05 * maxy, 0.8 * maxy, rg_text2, fontsize=10) # maxy = max(np.max(ob),np.max(fo)) ax2.set_xlabel("实况", fontsize=10) ax2.set_ylabel("预报", fontsize=10) ax2.set_title("散点回归图", fontsize=10) # 设置次刻度间隔 xmi = 1 if (np.max(ob) > 100): xmi = 2 ymi = 1 if (np.max(fo) > 100): ymi = 2 xmajorLocator = mpl.ticker.MultipleLocator(10 * xmi) # 将x主刻度标签设置为次刻度10倍 ymajorLocator = mpl.ticker.MultipleLocator(10 * ymi) # 将y主刻度标签设置为次刻度10倍 ax2.xaxis.set_major_locator(xmajorLocator) ax2.yaxis.set_major_locator(ymajorLocator) xminorLocator = mpl.ticker.MultipleLocator(xmi) # 将x轴次刻度标签设置xmi yminorLocator = mpl.ticker.MultipleLocator(ymi) # 将y轴次刻度标签设置ymi ax2.xaxis.set_minor_locator(xminorLocator) ax2.yaxis.set_minor_locator(yminorLocator) #折线图 # 绘制频率柱状图 p_ob = np.zeros(len(clevs) - 1) p_fo = np.zeros(len(clevs) - 1) x = clevs[0:-1] for i in range(len(clevs) - 1): index0 = np.where((ob >= clevs[i]) & (ob < clevs[i + 1])) p_ob[i] = len(index0[0]) index0 = np.where((fo >= clevs[i]) & (fo < clevs[i + 1])) p_fo[i] = len(index0[0]) ax5 = plt.axes(rect5) # ax5.plot(p_ob, color='r',label="Obs") # ax5.plot(p_fo, color='b',label="Pred") ax5.bar(x + 0.5, p_ob, width=0.4, color='r', label="实况") ax5.bar(x + 1.1, p_fo, width=0.4, color="b", label="预报") ax5.legend(loc="upper right") ax5.set_xlabel("等级", fontsize=10) ax5.set_ylabel("站点数", fontsize=10) ax5.set_title("频率统计图", fontsize=10) ax5.yaxis.set_minor_locator(mpl.ticker.MultipleLocator(1000)) maxy = max(np.max(p_fo), np.max(p_ob)) * 1.4 ax5.set_ylim(0, maxy) #检验效果图 # 绘制降水站点实况预报统计表 ax6 = plt.axes(rect6) ax6.axes.set_axis_off() ob_mean = np.mean(grd_ob.values) fo_mean = np.mean(grd_fo.values) maee = nmc_verification.nmc_vf_method.continuous.score.mae(ob, fo) mee = nmc_verification.nmc_vf_method.continuous.score.me(ob, fo) msee = nmc_verification.nmc_vf_method.continuous.score.mse(ob, fo) rmsee = nmc_verification.nmc_vf_method.continuous.score.rmse(ob, fo) bias_ce = nmc_verification.nmc_vf_method.continuous.score.bias(ob, fo) cor = nmc_verification.nmc_vf_method.continuous.score.corr(ob, fo) ob_has = ob[ob >= 0.01] fo_has = fo[fo >= 0.01] text = "格点检验统计量 n=" + str(len(ob)) + "\n" text += "==============================================\n" text += " 实况 预报 \n" text += "----------------------------------------------\n" text += "平均温度 " + "%8.2f" % (ob_mean) + "%20.2f" % ( fo_mean) + "\n\n" text += "温度范围 " + "%8.2f" % (ob_min) + "~%6.2f" % ( ob_max) + "%12.2f" % (fo_min) + "~%6.2f" % (fo_max) + "\n\n" text += "==============================================\n\n" text += "Mean absolute error:" + "%6.2f" % maee + "\n\n" text += "Mean error:" + "%6.2f" % mee + "\n\n" text += "Mean-squared error:" + "%6.2f" % msee + "\n\n" text += "Root mean-squared error:" + "%6.2f" % rmsee + "\n\n" text += "Bias:" + "%6.2f" % bias_ce + "\n\n" text += "Correctlation coefficiant:" + "%6.2f" % cor + "\n" plt.text(0, 0, text, fontsize=9) # 图片显示或保存 if (filename is None): plt.show() print() else: plt.savefig(filename, dpi=300) plt.close()
def temper_gg(grd_ob, grd_fo, filename=None): ob_min = np.min(grd_ob.values) fo_min = np.min(grd_fo.values) ob_max = np.max(grd_ob.values) fo_max = np.max(grd_fo.values) ob_fo_max = max(ob_max, fo_max) ob_fo_min = min(ob_min, fo_min) clevs_temp, cmap_temp = nmc_verification.nmc_vf_base.tool.color_tools.get_clev_and_cmap_by_element_name( "temp") clevs, cmap = nmc_verification.nmc_vf_base.tool.color_tools.get_part_clev_and_cmap( clevs_temp, cmap_temp, ob_fo_max, ob_fo_min) width = 9 #整个画面的宽度 width_colorbar = 0.6 height_title = 0.3 height_veri_plot = 0.5 grid0 = nmc_verification.nmc_vf_base.get_grid_of_data(grd_fo) if (grid0.nlon <= grid0.nlat * 0.5): #采用3*1布局 width_map = (width - 2 * width_colorbar) / 3 height_map = (grid0.nlat / grid0.nlon) * width_map height = height_map + height_title + height_veri_plot rect1 = [ width_colorbar / width, height_veri_plot / height, width_map / width, height_map / height ] # 实况 rect2 = [(1 * width_map + width_colorbar) / width, height_veri_plot / height, width_map / width, height_map / height] # 预报 rect3 = [(2 * width_map + width_colorbar + 0.05) / width, height_veri_plot / height, width_map / width, height_map / height] # 误差 ob_fo_colorbar_box = [ 0.02, height_veri_plot / height, 0.015, height_map / height ] error_colorbar_box = [(3 * width_map + width_colorbar + 0.05) / width, height_veri_plot / height, 0.015, height_map / height] else: #采用1*2 + 1 布局 width_map = (width - 2 * width_colorbar) / 1.5 height_map = (grid0.nlat / grid0.nlon) * width_map height = height_map + height_title + height_veri_plot rect1 = [(width_colorbar - 0.03) / width, (height_veri_plot + 0.5 * height_map) / height, 0.5 * width_map / width, 0.5 * height_map / height] # 实况 rect2 = [(width_colorbar - 0.03) / width, height_veri_plot / height, 0.5 * width_map / width, 0.5 * height_map / height] # 预报 rect3 = [(0.5 * width_map + width_colorbar) / width, height_veri_plot / height, width_map / width, height_map / height] # 误差 ob_fo_colorbar_box = [ 0.02, height_veri_plot / height, 0.01, height_map / height ] error_colorbar_box = [ (1.5 * width_map + width_colorbar + 0.05) / width, height_veri_plot / height, 0.01, height_map / height ] rect4 = [0.05, 0.06, 0.26, height_veri_plot / height - 0.1] # 散点回归图,左下宽高 rect5 = [0.38, 0.06, 0.28, height_veri_plot / height - 0.1] # 频率柱状图, 左下宽高 rect6 = [0.67, 0.01, 0.3, height_veri_plot / height - 0.03] # 左下宽高 rect_title = [ width_colorbar / width, (height_veri_plot + height_map) / height, 1 - 2 * width_colorbar / width, 0.001 ] fig = plt.figure(figsize=(width, height)) # 平面对比图1 datacrs = ccrs.PlateCarree() ax1 = plt.axes(rect1, projection=datacrs) ax2 = plt.axes(rect2, projection=datacrs) ax3 = plt.axes(rect3, projection=datacrs) # 设置地图背景 map_extent = [grid0.slon, grid0.elon, grid0.slat, grid0.elat] ax1.set_extent(map_extent, crs=datacrs) ax2.set_extent(map_extent, crs=datacrs) ax3.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax1, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax1, name='river', edgecolor='blue', lw=0.3) # 河流 add_china_map_2cartopy(ax2, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax2, name='river', edgecolor='blue', lw=0.3) # 河流 add_china_map_2cartopy(ax3, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax3, name='river', edgecolor='blue', lw=0.3) # 河流 # 绘制格点预报场 x = np.arange(grid0.nlon) * grid0.dlon + grid0.slon y = np.arange(grid0.nlat) * grid0.dlat + grid0.slat #clevs = [-10,0,15,20,22,24,26,28,30,32,34,35] #colors_grid = ["#00AAAA","#009500","#808000", "#BFBF00","#FFFF00","#FFD400","#FFAA00","#FF7F00","#FF0000","#FF002A","#FF0055","#FF0055"] plot_grid = ax1.contourf(x, y, grd_ob.values.squeeze(), levels=clevs, cmap=cmap, transform=datacrs) # 填色图 ax1.set_title("实况", fontsize=12, loc="left", y=0.0) colorbar_position_grid = fig.add_axes(ob_fo_colorbar_box) # 位置[左,下,宽,高] plt.colorbar(plot_grid, cax=colorbar_position_grid, orientation='vertical') plt.title("温度(℃)", fontsize=8, verticalalignment='bottom') ax2.contourf(x, y, grd_fo.values.squeeze(), levels=clevs, cmap=cmap, transform=datacrs) # 填色图 ax2.set_title("预报", fontsize=12, loc="left", y=0.0) clevs1 = [-5, -4, -3, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 3, 4, 5] error = grd_fo.values.squeeze() - grd_ob.values.squeeze() plot_grid1 = ax3.contourf(x, y, error, clevs1, cmap="bwr", transform=datacrs) # 填色图 colorbar_position_grid1 = fig.add_axes(error_colorbar_box) # 位置[左,下,宽,高] ax3.set_title("预报 - 实况", fontsize=12, loc="left", y=0.0) plt.colorbar(plot_grid1, cax=colorbar_position_grid1, orientation='vertical') plt.title("误差(℃)", fontsize=8, verticalalignment='bottom') time_str = nmc_verification.nmc_vf_base.tool.time_tools.time_to_str( grid0.gtime[0]) dati_str = time_str[0:4] + "年" + time_str[4:6] + "月" + time_str[ 6:8] + "日" + time_str[8:10] + "时" if type(grid0.members[0]) == str: model_name = grid0.members[0] else: model_name = str(grid0.members[0]) title = model_name + " " + dati_str + "起报" + str( grid0.dtimes[0]) + "H时效预报和实况对比及误差" ax_title = plt.axes(rect_title) ax_title.axes.set_axis_off() ax_title.set_title(title) # 图片显示或保存 if (filename is None): plt.show() print() else: plt.savefig(filename, dpi=300) plt.close()
def pcolormesh_2d_grid(grd, title=None, filename=None, clevs=None, cmap=None): x = grd['lon'].values y = grd['lat'].values rlon = x[-1] - x[0] rlat = y[-1] - y[0] height = 5 width = height * rlon / rlat + 1 fig = plt.figure(figsize=(width, height)) datacrs = ccrs.PlateCarree() ax = plt.axes(projection=datacrs) map_extent = [x[0], x[-1], y[0], y[-1]] ax.set_extent(map_extent) if title is not None: plt.title(title) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=0.3) #省界 add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=0.3) #河流 if clevs is None: vmax = np.max(grd.values) vmin = np.min(grd.values) if vmax - vmin < 1e-10: vmax = vmin + 1.1 dif = (vmax - vmin) / 10.0 inte = math.pow(10, math.floor(math.log10(dif))) #用基本间隔,将最大最小值除于间隔后小数点部分去除,最后把间隔也整数化 r = dif / inte if r < 3 and r >= 1.5: inte = inte * 2 elif r < 4.5 and r >= 3: inte = inte * 4 elif r < 5.5 and r >= 4.5: inte = inte * 5 elif r < 7 and r >= 5.5: inte = inte * 6 elif r >= 7: inte = inte * 8 vmin = inte * ((int)(vmin / inte)) vmax = inte * ((int)(vmax / inte) + 1) clevs = np.arange(vmin, vmax, inte) if cmap is None: cmap = plt.get_cmap("rainbow") norm = BoundaryNorm(clevs, ncolors=cmap.N, clip=True) im = ax.pcolormesh(x, y, np.squeeze(grd.values), cmap=cmap, norm=norm, transform=datacrs) left_low = (width - 1) / width colorbar_position = fig.add_axes([left_low, 0.11, 0.03, 0.77]) # 位置[左,下,宽,高] plt.colorbar(im, cax=colorbar_position) vmax = x[-1] vmin = x[0] r = rlon if r <= 1: inte = 0.1 elif r <= 5 and r > 1: inte = 1 elif r <= 10 and r > 5: inte = 2 elif r < 20 and r >= 10: inte = 4 elif r <= 30 and r >= 20: inte = 5 else: inte = 10 vmin = inte * ((int)(vmin / inte)) vmax = inte * ((int)(vmax / inte) + 1) xticks = np.arange(vmin, vmax, inte) xticks_label = [] for x in range(len(xticks)): xticks_label.append(str(xticks[x])) xticks_label[-1] += "°E" ax.set_xticks(xticks) ax.set_xticklabels(xticks_label) vmax = y[-1] vmin = y[0] r = rlat if r <= 1: inte = 0.1 elif r <= 5 and r > 1: inte = 1 elif r <= 10 and r > 5: inte = 2 elif r < 20 and r >= 10: inte = 4 elif r <= 30 and r >= 20: inte = 5 else: inte = 10 vmin = inte * ((int)(vmin / inte)) vmax = inte * ((int)(vmax / inte) + 1) yticks = np.arange(vmin, vmax, inte) yticks_label = [] for y in range(len(yticks)): yticks_label.append(str(yticks[y])) yticks_label[-1] += "°N" ax.set_yticks(yticks) ax.set_yticklabels(yticks_label) if (filename is None): plt.show() else: file1, extension = os.path.splitext(filename) extension = extension[1:] plt.savefig(filename, format=extension) plt.close()
def qpf_24h(initial_time, fhour=0, model='ECMWF', map_center=(117, 39), map_width=12): """ Draw 24h accumulated QPF. Arguments: initial_time {string or datetime object} -- model initital time, like '18042008' or datetime(2018, 4, 20, 8). Keyword Arguments: fhour {int} -- model initial time (default: {0}) model {str} -- model name (default: {'ECMWF'}) """ # micaps data directory data_dirs = {'ECMWF': ['ECMWF_HR/RAIN24']} try: data_dir = data_dirs[model.strip().upper()] except KeyError: raise ValueError('Unknown model, choose ECMWF, GRAPES or NCEP.') # get file name filename = model_filename(initial_time, fhour) # retrieve data from micaps server rain24 = get_model_grid(data_dir[0], filename=filename) if rain24 is None: print('Can not retrieve {} from Micaps server.'.format(filename)) return init_time = rain24.coords['init_time'].values[0] rain24 = { 'lon': rain24.coords['lon'].values, 'lat': rain24.coords['lat'].values, 'data': np.squeeze(rain24.values) } # set up map projection datacrs = ccrs.PlateCarree() plotcrs = ccrs.AlbersEqualArea(central_latitude=map_center[1], central_longitude=map_center[0], standard_parallels=[30., 60.]) # set up figure fig = plt.figure(figsize=(10, 8)) ax = fig.add_axes([0, 0, 1, 1], projection=plotcrs) # add model title add_model_title('24h accumulated QPF', init_time, model=model, fhour=fhour, fontsize=18, multilines=True, atime=24) # add map background map_extent = (map_center[0] - map_width / 2.0, map_center[0] + map_width / 2.0, map_center[1] - map_width / 2.0, map_center[1] + map_width / 2.0) ax.set_extent(map_extent, crs=datacrs) land_50m = cfeature.NaturalEarthFeature('physical', 'land', '50m', edgecolor='face', facecolor=cfeature.COLORS['land']) ax.add_feature(land_50m) add_china_map_2cartopy(ax, name='province', edgecolor='darkcyan', lw=1, zorder=100) # draw QPF clevs = [0.1, 10, 25, 50, 100, 250] colors = ["#88F492", "#00A929", "#2AB8FF", "#1202FC", "#FF04F4", "#850C3E"] cmap, norm = mpl.colors.from_levels_and_colors(clevs, colors, extend='max') ax.pcolormesh(rain24['lon'], rain24['lat'], rain24['data'], norm=norm, cmap=cmap, transform=datacrs, zorder=2) # add custom legend legend_elements = [ Patch(facecolor=colors[0], label='0.1~10mm'), Patch(facecolor=colors[1], label='10~25mm'), Patch(facecolor=colors[2], label='25~50mm'), Patch(facecolor=colors[3], label='50~100mm'), Patch(facecolor=colors[4], label='100~250mm'), Patch(facecolor=colors[5], label='>250mm') ] ax.legend(handles=legend_elements, loc='lower right', fontsize=16) # add logo add_logo(fig, alpha=0.7) # show figure ax.set_adjustable('datalim') plt.show()
def draw_veri_rain_24(grid_fo, sta_ob, filename=None): fig = plt.figure(figsize=(10, 7)) # 平面对比图 rect1 = [0.00, 0.42, 0.7, 0.55] # 左下宽高 datacrs = ccrs.PlateCarree() ax = plt.axes(rect1, projection=datacrs) # 设置地图背景 map_extent = [grid_fo.slon, grid_fo.elon, grid_fo.slat, grid_fo.elat] ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=0.3) # 河流 # 绘制格点预报场 x = np.arange(grid_fo.nlon) * grid_fo.dlon + grid_fo.slon y = np.arange(grid_fo.nlat) * grid_fo.dlat + grid_fo.slat clevs = [0.1, 10, 25, 50, 100, 250, 1000] colors_grid = [ "#E0EEFA", "#B4D3E9", "#6FB0D7", "#3787C0", "#105BA4", "#07306B", "#07306B" ] plot_grid = ax.contourf(x, y, grid_fo.dat, clevs, colors=colors_grid, transform=datacrs) # 填色图 colorbar_position_grid = fig.add_axes([0.035, 0.94, 0.25, 0.015]) # 位置[左,下,宽,高] plt.colorbar(plot_grid, cax=colorbar_position_grid, orientation='horizontal') plt.text(0.035, 0.955, "model accumulated precipition(mm)", fontsize=10) # 绘制填色站点值 sta_ob_in = function.get_from_sta_data.sta_in_grid_xy(sta_ob, grid=grid_fo.grid) colors_sta = [ '#FFFFFF', '#0055FF', '#00FFB4', '#F4FF00', '#FE1B00', '#910000', '#B800BA' ] dat = sta_ob_in.values[:, 2] dat[dat > 1000] = 0 clevs = [0, 0.1, 10, 25, 50, 100, 250, 1000] cleves_name = [ "0", "0.1-10", "10-25", "25-50", "50-100", "100-250", ">=250" ] for i in range(len(clevs) - 1): index0 = np.where((dat >= clevs[i]) & (dat < clevs[i + 1])) if (len(index0[0]) > 0): x = np.squeeze(sta_ob_in.values[index0, 0]) y = np.squeeze(sta_ob_in.values[index0, 1]) if (len(index0) == 1): x = np.array([x]) y = np.array([y]) if (i > 0): ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=3, label=cleves_name[i], linewidths=0.3, edgecolor='k') else: ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=1, label=cleves_name[i]) ax.legend(facecolor='whitesmoke', title="observation", loc="lower left", edgecolor='whitesmoke') # 散点回归图 rect2 = [0.07, 0.07, 0.21, 0.30] # 左下宽高 ax2 = plt.axes(rect2) sta_fo = function.gxy_sxy.interpolation_linear(grid_fo, sta_ob_in) ob = sta_ob_in.values[:, 2] fo = sta_fo.values[:, 2] ax2.plot(ob, fo, '.', color='k') # 绘制比例线 rate = np.sum(fo) / np.sum(ob) ob_line = np.arange(0, np.max(ob), np.max(ob) / 30) fo_rate = ob_line * rate ax2.plot(ob_line[0:20], fo_rate[0:20], 'r') # 绘制回归线 X = np.zeros((len(ob), 1)) X[:, 0] = ob[:] clf = LinearRegression().fit(X, fo) X = np.zeros((len(ob_line), 1)) X[:, 0] = ob_line[:] fo_rg = clf.predict(X) ax2.plot(ob_line, fo_rg, color='b', linestyle='dashed') rg_text1 = "R = " + '%.2f' % (np.corrcoef(ob, fo)[0, 1]) rg_text2 = "y = " + '%.2f' % (clf.coef_[0]) + "* x + " + '%.2f' % ( clf.intercept_) maxy = max(np.max(ob), np.max(fo)) + 5 plt.xlim(0, maxy) plt.ylim(0, maxy) plt.text(0.05 * maxy, 0.9 * maxy, rg_text1, fontsize=10) plt.text(0.05 * maxy, 0.8 * maxy, rg_text2, fontsize=10) maxy = max(np.max(ob), np.max(fo)) ax2.set_xlabel("observation", fontsize=12) ax2.set_ylabel("precipitation", fontsize=12) ax2.set_title("Obs.vs Pred. Scatter plot", fontsize=12) # 设置次刻度间隔 xmi = 1 if (np.max(ob) > 100): xmi = 2 ymi = 1 if (np.max(fo) > 100): ymi = 2 xmajorLocator = mpl.ticker.MultipleLocator(10 * xmi) # 将x主刻度标签设置为次刻度10倍 ymajorLocator = mpl.ticker.MultipleLocator(10 * ymi) # 将y主刻度标签设置为次刻度10倍 ax2.xaxis.set_major_locator(xmajorLocator) ax2.yaxis.set_major_locator(ymajorLocator) xminorLocator = mpl.ticker.MultipleLocator(xmi) # 将x轴次刻度标签设置xmi yminorLocator = mpl.ticker.MultipleLocator(ymi) # 将y轴次刻度标签设置ymi ax2.xaxis.set_minor_locator(xminorLocator) ax2.yaxis.set_minor_locator(yminorLocator) # 绘制频率柱状图 p_ob = np.zeros(6) p_fo = np.zeros(6) x = np.arange(6) + 1 for i in range(1, len(clevs) - 1, 1): index0 = np.where((ob >= clevs[i]) & (ob < clevs[i + 1])) p_ob[i - 1] = len(index0[0]) index0 = np.where((fo >= clevs[i]) & (fo < clevs[i + 1])) p_fo[i - 1] = len(index0[0]) rect3 = [0.35, 0.07, 0.325, 0.17] # 左下宽高 ax3 = plt.axes(rect3) ax3.bar(x - 0.25, p_ob, width=0.2, facecolor="r", label="Obs") ax3.bar(x + 0.05, p_fo, width=0.2, facecolor="b", label="Pred") ax3.legend(loc="upper right") ax3.set_xlabel("precipitation threshold", fontsize=10) ax3.set_xticks(x) ax3.set_xticklabels( ["0.1-10", "10-25", "25-50", "50-100", "100-250", ">=250"], fontsize=9) ax3.set_ylabel("point number", fontsize=10) ax3.yaxis.set_minor_locator(mpl.ticker.MultipleLocator(100)) # 绘制降水站点实况预报统计表 rect4 = [0.325, 0.255, 0.4, 0.10] # 左下宽高 ax4 = plt.axes(rect4) ax4.axes.set_axis_off() ob_has = ob[ob >= 0.01] fo_has = fo[fo >= 0.01] text = "制降水站点实况预报 n=" + str(len(ob)) + "\n" text += "=======================================================\n" text += " observation Predication\n" text += "-------------------------------------------------------\n" text += "有降水站点数(>=0.01) " + "%4d" % len( ob_has) + " %4d" % len(fo_has) + "\n" text += "有降水站点数百分比% " + "%8.2f" % ( len(ob_has) / len(ob)) + "%20.2f" % (len(fo_has) / len(fo)) + "\n" text += "平均降水量(排除无降水) " + "%8.2f" % (np.mean(ob_has)) + "%20.2f" % ( np.mean(fo_has)) + "\n" text += "最大降水量 " + "%8.2f" % (np.max(ob_has)) + "%20.2f" % ( np.max(fo_has)) plt.text(0, 0, text, fontsize=9) # 绘制统计检验结果 rect5 = [0.705, 0.01, 0.28, 0.97] # 左下宽高 ax5 = plt.axes(rect5) ax5.axes.set_axis_off() mae = continuous.score.mae(ob, fo) me = continuous.score.me(ob, fo) mse = continuous.score.mse(ob, fo) rmse = continuous.score.rmse(ob, fo) bias_c = continuous.score.bias(ob, fo) cor = continuous.score.corr(ob, fo) hit, mis, fal, co = yes_or_no.score.hmfn(ob, fo, clevs[1:]) ts = yes_or_no.score.ts(ob, fo, clevs[1:]) ets = yes_or_no.score.ets(ob, fo, clevs[1:]) bias = yes_or_no.score.bias(ob, fo, clevs[1:]) hit_rate = yes_or_no.score.hit_rate(ob, fo, clevs[1:]) mis_rate = yes_or_no.score.mis_rate(ob, fo, clevs[1:]) fal_rate = yes_or_no.score.fal_rate(ob, fo, clevs[1:]) text = str(len(ob)) + "评分站点预报检验统计量\n" text += "Mean absolute error:" + "%6.2f" % mae + "\n" text += "Mean error:" + "%6.2f" % me + "\n" text += "Mean-squared error:" + "%6.2f" % mse + "\n" text += "Root mean-squared error:" + "%6.2f" % rmse + "\n" text += "Bias:" + "%6.2f" % bias_c + "\n" text += "Correctlation coefficiant:" + "%6.2f" % cor + "\n\n\n" leves_name = [ "0.1-10-", "10-25--", "25-50--", "50-100-", "100-250", ">=250-" ] for i in range(len(leves_name)): text += ":" + leves_name[i] + "----------------------------\n" text += "正确:" + "%-4d" % hit[i] + " 空报:" + "%-4d" % fal[ i] + " 漏报:" + "%-4d" % mis[i] + "\n" text += "TS:" + "%5.3f" % ts[ i] + " ETS:" + "%5.3f" % ets[i] + "\n" text += "Hit rate:" + "%5.3f" % hit_rate[ i] + " Miss rate: " + "%5.3f" % mis_rate[i] + "\n" text += "False alarm ratio:" + "%5.3f" % fal_rate[ i] + " Bias:" + "%5.3f" % bias[i] + "\n\n" plt.text(0, 0.00, text, fontsize=11) # 图片显示或保存 if (filename is None): plt.show() else: plt.savefig(filename, dpi=300) plt.close() return
def rain_24h_sg(sta_ob, grd_fo, filename=None): ''' #绘制24小时降水实况与预报对比图 :param grd_fo: 输入的网格数据,包含一个平面的网格场 :param sta_ob: 输入的站点数据,包含一个时刻的站点数据列表 :param filename: 图片输出路径,缺省时会以调试窗口形式弹出 :return: 无返回值 ''' grid_fo = nmc_verification.nmc_vf_base.get_grid_of_data(grd_fo) # 通过经纬度范围设置画幅 hight = 5.6 title_hight = 0.3 legend_hight = 0.6 left_plots_width = 0 right_plots_width = 0 width = ( hight - title_hight - legend_hight ) * grid_fo.nlon / grid_fo.nlat + left_plots_width + right_plots_width map_width = width - left_plots_width - right_plots_width fig = plt.figure(figsize=(width, hight)) # 设置画幅的布局方式, rect1 = [ left_plots_width / width, 0.12, (width - right_plots_width - left_plots_width) / width, 0.84 ] # 左下宽高,中央对比图 ylabelwidth = 0.52 / width rect2 = [ ylabelwidth, 0.08, left_plots_width / width - ylabelwidth - 0.005, 0.40 ] # 左下宽高,散点回归图 ylabelwidth = 0.65 / width rect3 = [ ylabelwidth, 0.60, left_plots_width / width - ylabelwidth - 0.005, 0.18 ] # 左下宽高,频谱统计柱状图 rect4 = [0.01, 0.79, left_plots_width / width - 0.045, 0.15] # 左下宽高,左侧文字 rect5 = [(width - right_plots_width) / width + 0.005, -0.035, right_plots_width / width - 0.01, 0.90] # 左下宽高,右侧文字 width_ob_fo_str = 0.3 if (map_width < 3.5): width_bar = 2.1 # 根据中间地图的宽度,来确定预报colorbar的尺寸 sta_legend_size = 5 # 根据中间地图的宽度,来确定观测legend的size else: sta_legend_size = 7 width_bar = 2.9 rect6 = [(left_plots_width + 0.5 * map_width - 0.5 * width_bar + 0.5 * width_ob_fo_str) / width, 0.04, width_bar / width, 0.02] # 预报colorbar rect7 = [(left_plots_width + 0.5 * map_width - 0.5 * width_bar - 0.5 * width_ob_fo_str) / width, 0.04, width_ob_fo_str / width, 0.3] # 观测文字 datacrs = ccrs.PlateCarree() ax = plt.axes(rect1, projection=datacrs) # 设置地图背景 map_extent = [grid_fo.slon, grid_fo.elon, grid_fo.slat, grid_fo.elat] ax.set_extent(map_extent, crs=datacrs) add_china_map_2cartopy(ax, name='province', edgecolor='k', lw=0.3) # 省界 add_china_map_2cartopy(ax, name='river', edgecolor='blue', lw=0.3) # 河流 # 绘制格点预报场 x = np.arange(grid_fo.nlon) * grid_fo.dlon + grid_fo.slon y = np.arange(grid_fo.nlat) * grid_fo.dlat + grid_fo.slat clevs = [0.1, 10, 25, 50, 100, 250, 1000] colors_grid = [ "#D0DEEA", "#B4D3E9", "#6FB0D7", "#3787C0", "#105BA4", "#07306B", "#07306B" ] dat = grd_fo.values.squeeze() # print(x) # print(y) # print(dat) plot_grid = ax.contourf(x, y, dat, clevs, colors=colors_grid, transform=datacrs) # 填色图 time_str = nmc_verification.nmc_vf_base.tool.time_tools.time_to_str( grid_fo.gtime[0]) dati_str = time_str[0:4] + "年" + time_str[4:6] + "月" + time_str[ 6:8] + "日" + time_str[8:10] + "时" if type(grid_fo.members[0]) == str: model_name = grid_fo.members[0] else: model_name = str(grid_fo.members[0]) if map_width < 3: title = model_name + " " + dati_str + "起报" + str( grid_fo.dtimes[0]) + "H时效预报和观测" ax.set_title(title, fontsize=7) elif map_width < 4: title = model_name + " " + dati_str + "起报" + str( grid_fo.dtimes[0]) + "H时效预报和观测" ax.set_title(title, fontsize=10) else: title = model_name + " " + dati_str + "起报" + str( grid_fo.dtimes[0]) + "H时效预报和观测" ax.set_title(title, fontsize=11) colorbar_position_grid = fig.add_axes(rect6) # 位置[左,下,宽,高] cb = plt.colorbar(plot_grid, cax=colorbar_position_grid, orientation='horizontal') cb.ax.tick_params(labelsize=8) # 设置色标刻度字体大小。 # plt.text(0, 0, "预报(mm)", fontsize=8) # 绘制填色站点值 sta_ob_in = nmc_verification.nmc_vf_base.function.get_from_sta_data.sta_in_grid_xy( sta_ob, grid=grid_fo) colors_sta = [ '#FFFFFF', '#0055FF', '#00FFB4', '#F4FF00', '#FE1B00', '#910000', '#B800BA' ] dat = sta_ob_in.values[:, -1] dat[dat > 1000] = 0 clevs = [0, 0.1, 10, 25, 50, 100, 250, 1000] cleves_name = [ "0", "0.1-10", "10-25", "25-50", "50-100", "100-250", ">=250" ] for i in range(len(clevs) - 1): index0 = np.where((dat >= clevs[i]) & (dat < clevs[i + 1])) if (len(index0[0]) > 0): x = np.squeeze(sta_ob_in["lon"].values[index0]) y = np.squeeze(sta_ob_in["lat"].values[index0]) if (len(index0) == 1): x = np.array([x]) y = np.array([y]) if (i > 0): ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=3, label=cleves_name[i], linewidths=0.3, edgecolor='k') else: ax.scatter(x, y, c=colors_sta[i], transform=ccrs.PlateCarree(), s=1, label=cleves_name[i], linewidths=0.1, edgecolor="k") ax.legend(facecolor='whitesmoke', loc="lower center", ncol=4, edgecolor='whitesmoke', prop={'size': sta_legend_size}, bbox_to_anchor=(0.5 + 0.5 * width_ob_fo_str / map_width, -0.08)) ax7 = plt.axes(rect7) ax7.axes.set_axis_off() plt.text(0, 0.00, "观测\n\n预报", fontsize=7) # 图片显示或保存 if (filename is None): plt.show() print() else: plt.savefig(filename, dpi=300) plt.close() return