def main(): # Define the simulations to be validated r_config = RunConfig( data_path= "/RESCUE/skynet3_rech1/huziy/hdf_store/quebec_0.1_crcm5-hcd-r.hdf5", start_year=1990, end_year=2010, label="CRCM5-L1") r_config_list = [r_config] r_config = RunConfig( data_path= "/RESCUE/skynet3_rech1/huziy/hdf_store/quebec_0.1_crcm5-r.hdf5", start_year=1990, end_year=2010, label="CRCM5-NL") r_config_list.append(r_config) bmp_info = analysis.get_basemap_info_from_hdf(file_path=r_config.data_path) bmp_info.should_draw_grey_map_background = True bmp_info.should_draw_basin_boundaries = False bmp_info.map_bg_color = "0.75" station_ids = ["104001", "093806", "093801", "081002", "081007", "080718"] # get river network information used in the model flow_directions = analysis.get_array_from_file( r_config.data_path, var_name=infovar.HDF_FLOW_DIRECTIONS_NAME) accumulation_area_km2 = analysis.get_array_from_file( path=r_config.data_path, var_name=infovar.HDF_ACCUMULATION_AREA_NAME) cell_manager = CellManager(flow_dirs=flow_directions, lons2d=bmp_info.lons, lats2d=bmp_info.lats, accumulation_area_km2=accumulation_area_km2) # Get the list of stations to indicate on the bias map stations = cehq_station.read_station_data(start_date=None, end_date=None, selected_ids=station_ids) """:type : list[Station]""" xx, yy = bmp_info.get_proj_xy() station_to_modelpoint = cell_manager.get_model_points_for_stations( station_list=stations) upstream_edges = cell_manager.get_upstream_polygons_for_points( model_point_list=station_to_modelpoint.values(), xx=xx, yy=yy) bmp_info.draw_colorbar_for_each_subplot = True # Validate temperature, precip and swe obs_path_anusplin = "/home/huziy/skynet3_rech1/anusplin_links" obs_path_swe = "data/swe_ross_brown/swe.nc" model_var_to_obs_path = OrderedDict([("TT", obs_path_anusplin), ("I5", obs_path_swe)]) model_var_to_season = OrderedDict([ ("TT", OrderedDict([("Spring", range(3, 6))])), ("I5", OrderedDict([("Winter", [1, 2, 12])])) ]) vname_to_obs_data = {} # parameters that won't change in the loop over variable names params_const = dict(rconfig=r_config, bmp_info=bmp_info) for vname, obs_path in model_var_to_obs_path.items(): season_to_obs_data = get_seasonal_clim_obs_data( vname=vname, obs_path=obs_path, season_to_months=model_var_to_season[vname], **params_const) # Comment swe over lakes, since I5 calculated only for land if vname in [ "I5", ]: for season in season_to_obs_data: season_to_obs_data[season] = maskoceans( bmp_info.lons, bmp_info.lats, season_to_obs_data[season], inlands=True) vname_to_obs_data[vname] = season_to_obs_data # Plotting plot_all_vars_in_one_fig = True fig = None gs = None if plot_all_vars_in_one_fig: plot_utils.apply_plot_params(font_size=12, width_pt=None, width_cm=25, height_cm=20) fig = plt.figure() ncols = len(model_var_to_obs_path) + 1 gs = GridSpec(len(r_config_list), ncols, width_ratios=(ncols - 1) * [ 1., ] + [ 0.05, ]) else: plot_utils.apply_plot_params(font_size=12, width_pt=None, width_cm=25, height_cm=25) station_x_list = [] station_y_list = [] mvarname_to_cs = {} for row, r_config in enumerate(r_config_list): for col, mname in enumerate(model_var_to_obs_path): row_axes = [ fig.add_subplot(gs[row, col]), ] mvarname_to_cs[mname] = compare_vars( vname_model=mname, vname_to_obs=vname_to_obs_data, r_config=r_config, season_to_months=model_var_to_season[mname], bmp_info_agg=bmp_info, axes_list=row_axes) # -1 in order to exclude colorbars for the_ax in row_axes: the_ax.set_title(the_ax.get_title() + ", {}".format( infovar.get_long_display_label_for_var(mname))) # Need titles only for the first row if row > 0: the_ax.set_title("") if col == 0: the_ax.set_ylabel(r_config.label) else: the_ax.set_ylabel("") draw_upstream_area_bounds(the_ax, upstream_edges, color="g") if len(station_x_list) == 0: for the_station in stations: xst, yst = bmp_info.basemap(the_station.longitude, the_station.latitude) station_x_list.append(xst) station_y_list.append(yst) bmp_info.basemap.scatter(station_x_list, station_y_list, c="g", ax=the_ax, s=20, zorder=10, alpha=0.5) # Save the figure if necessary if plot_all_vars_in_one_fig: if not img_folder.is_dir(): img_folder.mkdir(parents=True) fig_path = img_folder.joinpath("{}.png".format( "_".join(model_var_to_obs_path))) with fig_path.open("wb") as figfile: fig.savefig(figfile, format="png", bbox_inches="tight") plt.close(fig)
def main(): season_to_months = DEFAULT_SEASON_TO_MONTHS r_config = RunConfig( data_path="/RESCUE/skynet3_rech1/huziy/hdf_store/quebec_0.1_crcm5-hcd-r.hdf5", start_year=1990, end_year=2010, label="CRCM5-L" ) bmp_info = analysis.get_basemap_info_from_hdf(file_path=r_config.data_path) bmp_info.should_draw_grey_map_background = True bmp_info.should_draw_basin_boundaries = False bmp_info.map_bg_color = "0.75" station_ids = [ "104001", "093806", "093801", "081002", "081007", "080718" ] # get river network information used in the model flow_directions = analysis.get_array_from_file(r_config.data_path, var_name=infovar.HDF_FLOW_DIRECTIONS_NAME) accumulation_area_km2 = analysis.get_array_from_file(path=r_config.data_path, var_name=infovar.HDF_ACCUMULATION_AREA_NAME) cell_manager = CellManager(flow_dirs=flow_directions, lons2d=bmp_info.lons, lats2d=bmp_info.lats, accumulation_area_km2=accumulation_area_km2) # Get the list of stations to indicate on the bias map stations = cehq_station.read_station_data( start_date=None, end_date=None, selected_ids=station_ids ) """:type : list[Station]""" xx, yy = bmp_info.get_proj_xy() station_to_modelpoint = cell_manager.get_model_points_for_stations(station_list=stations) upstream_edges = cell_manager.get_upstream_polygons_for_points( model_point_list=station_to_modelpoint.values(), xx=xx, yy=yy) # Validate temperature, precip and swe obs_path_anusplin = "/home/huziy/skynet3_rech1/anusplin_links" obs_path_swe = "data/swe_ross_brown/swe.nc" model_var_to_obs_path = OrderedDict([ ("TT", obs_path_anusplin), # ("PR", obs_path_anusplin), ("I5", obs_path_swe) ]) vname_to_obs_data = {} # parameters that won't change in the loop over variable names params_const = dict(rconfig=r_config, bmp_info=bmp_info, season_to_months=season_to_months) for vname, obs_path in model_var_to_obs_path.items(): season_to_obs_data = get_seasonal_clim_obs_data(vname=vname, obs_path=obs_path, **params_const) # Comment swe over lakes, since I5 calculated only for land if vname in ["I5", ]: for season in season_to_obs_data: season_to_obs_data[season] = maskoceans(bmp_info.lons, bmp_info.lats, season_to_obs_data[season], inlands=True) vname_to_obs_data[vname] = season_to_obs_data # Plotting plot_all_vars_in_one_fig = True fig = None gs = None row_axes = [] ncols = None if plot_all_vars_in_one_fig: plot_utils.apply_plot_params(font_size=12, width_pt=None, width_cm=25, height_cm=20) fig = plt.figure() ncols = len(season_to_months) + 1 gs = GridSpec(len(model_var_to_obs_path), ncols, width_ratios=(ncols - 1) * [1., ] + [0.05, ]) else: plot_utils.apply_plot_params(font_size=12, width_pt=None, width_cm=25, height_cm=25) row = 0 station_x_list = [] station_y_list = [] for mname in model_var_to_obs_path: if plot_all_vars_in_one_fig: row_axes = [fig.add_subplot(gs[row, col]) for col in range(ncols)] compare_vars(vname_model=mname, vname_to_obs=vname_to_obs_data, r_config=r_config, season_to_months=season_to_months, bmp_info_agg=bmp_info, axes_list=row_axes) # -1 in order to exclude colorbars for the_ax in row_axes[:-1]: # Need titles only for the first row if row > 0: the_ax.set_title("") draw_upstream_area_bounds(the_ax, upstream_edges) if len(station_x_list) == 0: for the_station in stations: xst, yst = bmp_info.basemap(the_station.longitude, the_station.latitude) station_x_list.append(xst) station_y_list.append(yst) bmp_info.basemap.scatter(station_x_list, station_y_list, c="g", ax=the_ax, s=5, zorder=10, alpha=0.5) # Hide fall swe if mname in ["I5"]: row_axes[-2].set_visible(False) row += 1 # Save the figure if necessary if plot_all_vars_in_one_fig: fig_path = img_folder.joinpath("{}.png".format("_".join(model_var_to_obs_path))) with fig_path.open("wb") as figfile: fig.savefig(figfile, format="png", bbox_inches="tight") plt.close(fig)
def plot_basin_outlets(shape_file=BASIN_BOUNDARIES_FILE, bmp_info=None, directions=None, accumulation_areas=None, lake_fraction_field=None): assert isinstance(bmp_info, BasemapInfo) driver = ogr.GetDriverByName("ESRI Shapefile") print(driver) ds = driver.Open(shape_file, 0) assert isinstance(ds, ogr.DataSource) layer = ds.GetLayer() assert isinstance(layer, ogr.Layer) print(layer.GetFeatureCount()) latlong_proj = osr.SpatialReference() latlong_proj.ImportFromEPSG(4326) utm_proj = layer.GetSpatialRef() # create Coordinate Transformation coord_transform = osr.CoordinateTransformation(latlong_proj, utm_proj) utm_coords = coord_transform.TransformPoints( list(zip(bmp_info.lons.flatten(), bmp_info.lats.flatten()))) utm_coords = np.asarray(utm_coords) x_utm = utm_coords[:, 0].reshape(bmp_info.lons.shape) y_utm = utm_coords[:, 1].reshape(bmp_info.lons.shape) basin_mask = np.zeros_like(bmp_info.lons) cell_manager = CellManager(directions, accumulation_area_km2=accumulation_areas, lons2d=bmp_info.lons, lats2d=bmp_info.lats) index = 1 basins = [] basin_names = [] basin_name_to_mask = {} for feature in layer: assert isinstance(feature, ogr.Feature) # print feature["FID"] geom = feature.GetGeometryRef() assert isinstance(geom, ogr.Geometry) basins.append(ogr.CreateGeometryFromWkb(geom.ExportToWkb())) basin_names.append(feature["abr"]) accumulation_areas_temp = accumulation_areas.copy() lons_out, lats_out = [], [] basin_names_out = [] name_to_ij_out = OrderedDict() min_basin_area = min(b.GetArea() * 1.0e-6 for b in basins) while len(basins): fm = np.max(accumulation_areas_temp) i, j = np.where(fm == accumulation_areas_temp) i, j = i[0], j[0] p = ogr.CreateGeometryFromWkt("POINT ({} {})".format( x_utm[i, j], y_utm[i, j])) b_selected = None name_selected = None for name, b in zip(basin_names, basins): assert isinstance(b, ogr.Geometry) assert isinstance(p, ogr.Geometry) if b.Contains(p.Buffer(2000 * 2**0.5)): # Check if there is an upstream cell from the same basin the_mask = cell_manager.get_mask_of_upstream_cells_connected_with_by_indices( i, j) # Save the mask of the basin for future use basin_name_to_mask[name] = the_mask # if is_part_of_points_in(b, x_utm[the_mask == 1], y_utm[the_mask == 1]): # continue b_selected = b name_selected = name # basin_names_out.append(name) lons_out.append(bmp_info.lons[i, j]) lats_out.append(bmp_info.lats[i, j]) name_to_ij_out[name] = (i, j) basin_mask[the_mask == 1] = index index += 1 break if b_selected is not None: basins.remove(b_selected) basin_names.remove(name_selected) outlet_index_in_basin = 1 current_basin_name = name_selected while current_basin_name in basin_names_out: current_basin_name = name_selected + str(outlet_index_in_basin) outlet_index_in_basin += 1 basin_names_out.append(current_basin_name) print(len(basins), basin_names_out) accumulation_areas_temp[i, j] = -1 plot_utils.apply_plot_params(font_size=12, width_pt=None, width_cm=20, height_cm=20) gs = GridSpec(2, 2, width_ratios=[1.0, 0.5], wspace=0.01) fig = plt.figure() ax = fig.add_subplot(gs[1, 0]) xx, yy = bmp_info.get_proj_xy() bmp_info.basemap.drawcoastlines(linewidth=0.5, ax=ax) bmp_info.basemap.drawrivers(zorder=5, color="0.5", ax=ax) upstream_edges = cell_manager.get_upstream_polygons_for_points( model_point_list=[ ModelPoint(ix=i, jy=j) for (i, j) in name_to_ij_out.values() ], xx=xx, yy=yy) upstream_edges_latlon = cell_manager.get_upstream_polygons_for_points( model_point_list=[ ModelPoint(ix=i, jy=j) for (i, j) in name_to_ij_out.values() ], xx=bmp_info.lons, yy=bmp_info.lats) plot_utils.draw_upstream_area_bounds(ax, upstream_edges=upstream_edges, color="r", linewidth=0.6) plot_utils.save_to_shape_file(upstream_edges_latlon, in_proj=None) xs, ys = bmp_info.basemap(lons_out, lats_out) bmp_info.basemap.scatter(xs, ys, c="0.75", s=30, zorder=10) bmp_info.basemap.drawparallels(np.arange(-90, 90, 5), labels=[True, False, False, False], linewidth=0.5) bmp_info.basemap.drawmeridians(np.arange(-180, 180, 5), labels=[False, False, False, True], linewidth=0.5) cmap = cm.get_cmap("rainbow", index - 1) bn = BoundaryNorm(list(range(index + 1)), index - 1) # basin_mask = np.ma.masked_where(basin_mask < 0.5, basin_mask) # bmp_info.basemap.pcolormesh(xx, yy, basin_mask, norm=bn, cmap=cmap, ax=ax) xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() print(xmin, xmax, ymin, ymax) dx = xmax - xmin dy = ymax - ymin step_y = 0.1 step_x = 0.12 y0_frac = 0.75 y0_frac_bottom = 0.02 x0_frac = 0.35 bname_to_text_coords = { "RDO": (xmin + x0_frac * dx, ymin + y0_frac_bottom * dy), "STM": (xmin + (x0_frac + step_x) * dx, ymin + y0_frac_bottom * dy), "SAG": (xmin + (x0_frac + 2 * step_x) * dx, ymin + y0_frac_bottom * dy), "BOM": (xmin + (x0_frac + 3 * step_x) * dx, ymin + y0_frac_bottom * dy), "MAN": (xmin + (x0_frac + 4 * step_x) * dx, ymin + y0_frac_bottom * dy), "MOI": (xmin + (x0_frac + 5 * step_x) * dx, ymin + y0_frac_bottom * dy), "ROM": (xmin + (x0_frac + 5 * step_x) * dx, ymin + (y0_frac_bottom + step_y) * dy), "NAT": (xmin + (x0_frac + 5 * step_x) * dx, ymin + (y0_frac_bottom + 2 * step_y) * dy), ###### "CHU": (xmin + (x0_frac + 5 * step_x) * dx, ymin + y0_frac * dy), "GEO": (xmin + (x0_frac + 5 * step_x) * dx, ymin + (y0_frac + step_y) * dy), "BAL": (xmin + (x0_frac + 5 * step_x) * dx, ymin + (y0_frac + 2 * step_y) * dy), "PYR": (xmin + (x0_frac + 4 * step_x) * dx, ymin + (y0_frac + 2 * step_y) * dy), "MEL": (xmin + (x0_frac + 3 * step_x) * dx, ymin + (y0_frac + 2 * step_y) * dy), "FEU": (xmin + (x0_frac + 2 * step_x) * dx, ymin + (y0_frac + 2 * step_y) * dy), "ARN": (xmin + (x0_frac + 1 * step_x) * dx, ymin + (y0_frac + 2 * step_y) * dy), ###### "CAN": (xmin + 0.1 * dx, ymin + 0.80 * dy), "GRB": (xmin + 0.1 * dx, ymin + (0.80 - step_y) * dy), "LGR": (xmin + 0.1 * dx, ymin + (0.80 - 2 * step_y) * dy), "RUP": (xmin + 0.1 * dx, ymin + (0.80 - 3 * step_y) * dy), "WAS": (xmin + 0.1 * dx, ymin + (0.80 - 4 * step_y) * dy), "BEL": (xmin + 0.1 * dx, ymin + (0.80 - 5 * step_y) * dy), } # bmp_info.basemap.readshapefile(".".join(BASIN_BOUNDARIES_FILE.split(".")[:-1]).replace("utm18", "latlon"), "basin", # linewidth=1.2, ax=ax, zorder=9) for name, xa, ya, lona, lata in zip(basin_names_out, xs, ys, lons_out, lats_out): ax.annotate(name, xy=(xa, ya), xytext=bname_to_text_coords[name], textcoords='data', ha='right', va='bottom', bbox=dict(boxstyle='round,pad=0.4', fc='white'), arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0', linewidth=0.25), font_properties=FontProperties(size=8), zorder=20) print(r"{} & {:.0f} \\".format( name, accumulation_areas[name_to_ij_out[name]])) # Plot zonally averaged lake fraction ax = fig.add_subplot(gs[1, 1]) ydata = range(lake_fraction_field.shape[1]) ax.plot(lake_fraction_field.mean(axis=0) * 100, ydata, lw=2) ax.fill_betweenx(ydata, lake_fraction_field.mean(axis=0) * 100, alpha=0.5) ax.set_xlabel("Lake fraction (%)") ax.set_ylim(min(ydata), max(ydata)) ax.xaxis.set_tick_params(direction='out', width=1) ax.yaxis.set_tick_params(direction='out', width=1) ax.xaxis.set_ticks_position("bottom") ax.yaxis.set_ticks_position("none") ax.spines["top"].set_visible(False) ax.spines["right"].set_visible(False) for tl in ax.yaxis.get_ticklabels(): tl.set_visible(False) # plot elevation, buffer zone, big lakes, grid cells ax = fig.add_subplot(gs[0, :]) geophy_file = "/RESCUE/skynet3_rech1/huziy/from_guillimin/geophys_Quebec_0.1deg_260x260_with_dd_v6" r = RPN(geophy_file) elev = r.get_first_record_for_name("ME") lkfr = r.get_first_record_for_name("LKFR") fldr = r.get_first_record_for_name("FLDR") params = r.get_proj_parameters_for_the_last_read_rec() lons, lats = r.get_longitudes_and_latitudes_for_the_last_read_rec() rll = RotatedLatLon(**params) bsmp = rll.get_basemap_object_for_lons_lats(lons2d=lons, lats2d=lats, resolution="l") xx, yy = bsmp(lons, lats) dx = (xx[0, 0] - xx[-1, 0]) / xx.shape[0] dy = (yy[0, 0] - yy[0, -1]) / yy.shape[1] xx_ll_crnrs = xx - dx / 2 yy_ll_crnrs = yy - dy / 2 xx_ur_crnrs = xx + dx / 2 yy_ur_crnrs = yy + dy / 2 ll_lon, ll_lat = bsmp(xx_ll_crnrs[0, 0], yy_ll_crnrs[0, 0], inverse=True) ur_lon, ur_lat = bsmp(xx_ur_crnrs[-1, -1], yy_ur_crnrs[-1, -1], inverse=True) crnr_lons = np.array([[ll_lon, ll_lon], [ur_lon, ur_lon]]) crnr_lats = np.array([[ll_lat, ll_lat], [ur_lat, ur_lat]]) bsmp = rll.get_basemap_object_for_lons_lats(lons2d=crnr_lons, lats2d=crnr_lats) # plot elevation levs = [0, 100, 200, 300, 500, 700, 1000, 1500, 2000, 2800] norm = BoundaryNorm(levs, len(levs) - 1) the_cmap = my_colormaps.get_cmap_from_ncl_spec_file( path="colormap_files/OceanLakeLandSnow.rgb", ncolors=len(levs) - 1) lons[lons > 180] -= 360 me_to_plot = maskoceans(lons, lats, elev, resolution="l") im = bsmp.contourf(xx, yy, me_to_plot, cmap=the_cmap, levels=levs, norm=norm, ax=ax) bsmp.colorbar(im) bsmp.drawcoastlines(linewidth=0.5, ax=ax) # show large lake points gl_lakes = np.ma.masked_where((lkfr < 0.6) | (fldr <= 0) | (fldr > 128), lkfr) gl_lakes[~gl_lakes.mask] = 1.0 bsmp.pcolormesh(xx, yy, gl_lakes, cmap=cm.get_cmap("Blues"), ax=ax, vmin=0, vmax=1, zorder=3) # show free zone border margin = 20 x1 = xx_ll_crnrs[margin, margin] x2 = xx_ur_crnrs[-margin, margin] y1 = yy_ll_crnrs[margin, margin] y2 = yy_ur_crnrs[margin, -margin] pol_corners = ((x1, y1), (x2, y1), (x2, y2), (x1, y2)) ax.add_patch(Polygon(xy=pol_corners, fc="none", ls="solid", lw=3, zorder=5)) # show blending zone border (with halo zone) margin = 10 x1 = xx_ll_crnrs[margin, margin] x2 = xx_ur_crnrs[-margin, margin] y1 = yy_ll_crnrs[margin, margin] y2 = yy_ur_crnrs[margin, -margin] pol_corners = ((x1, y1), (x2, y1), (x2, y2), (x1, y2)) ax.add_patch( Polygon(xy=pol_corners, fc="none", ls="dashed", lw=3, zorder=5)) # show the grid step = 20 xx_ll_crnrs_ext = np.zeros([n + 1 for n in xx_ll_crnrs.shape]) yy_ll_crnrs_ext = np.zeros([n + 1 for n in yy_ll_crnrs.shape]) xx_ll_crnrs_ext[:-1, :-1] = xx_ll_crnrs yy_ll_crnrs_ext[:-1, :-1] = yy_ll_crnrs xx_ll_crnrs_ext[:-1, -1] = xx_ll_crnrs[:, -1] yy_ll_crnrs_ext[-1, :-1] = yy_ll_crnrs[-1, :] xx_ll_crnrs_ext[-1, :] = xx_ur_crnrs[-1, -1] yy_ll_crnrs_ext[:, -1] = yy_ur_crnrs[-1, -1] bsmp.pcolormesh(xx_ll_crnrs_ext[::step, ::step], yy_ll_crnrs_ext[::step, ::step], np.ma.masked_all_like(xx_ll_crnrs_ext)[::step, ::step], edgecolors="0.6", ax=ax, linewidth=0.05, zorder=4, alpha=0.5) ax.set_title("Elevation (m)") # plt.show() fig.savefig("qc_basin_outlets_points.png", bbox_inches="tight") # plt.show() plt.close(fig) return name_to_ij_out, basin_name_to_mask
def plot_station_positions(directions_file_path: Path, stations: list, ax: Axes, grid_config: GridConfig=default_domains.bc_mh_044, save_upstream_boundaries_to_shp=False): with Dataset(str(directions_file_path)) as ds: flow_dirs = ds.variables["flow_direction_value"][:] flow_acc_area = ds.variables["accumulation_area"][:] lons_2d, lats_2d = [ds.variables[k][:] for k in ["lon", "lat"]] basemap, reg_of_interest = grid_config.get_basemap_using_shape_with_polygons_of_interest(lons_2d, lats_2d, shp_path=default_domains.MH_BASINS_PATH, resolution="i") cell_manager = CellManager(flow_dirs, lons2d=lons_2d, lats2d=lats_2d, accumulation_area_km2=flow_acc_area) station_to_model_point = cell_manager.get_model_points_for_stations(station_list=stations, nneighbours=8) ##### xx, yy = basemap(lons_2d, lats_2d) upstream_edges = cell_manager.get_upstream_polygons_for_points( model_point_list=list(station_to_model_point.values()), xx=xx, yy=yy) upstream_edges_latlon = cell_manager.get_upstream_polygons_for_points( model_point_list=list(station_to_model_point.values()), xx=lons_2d, yy=lats_2d) plot_utils.draw_upstream_area_bounds(ax, upstream_edges=upstream_edges, color="r", linewidth=0.6) if save_upstream_boundaries_to_shp: plot_utils.save_to_shape_file(upstream_edges_latlon, folder_path="mh/engage_report/upstream_stations_areas/mh_{}".format(grid_config.dx), in_proj=None) basemap.drawrivers(linewidth=0.2) basemap.drawstates(linewidth=0.1) basemap.drawcountries(linewidth=0.1) basemap.drawcoastlines(linewidth=0.2) pos_ids, lons_pos, lats_pos = [], [], [] pos_labels = [] legend_lines = [] for i, (s, mp) in enumerate(sorted(station_to_model_point.items(), key=lambda p: p[0].latitude, reverse=True), start=1): pos_ids.append(s.id) pos_labels.append(i) lons_pos.append(mp.longitude) lats_pos.append(mp.latitude) legend_lines.append("{}: {}".format(i, s.id)) xm, ym = basemap(lons_pos, lats_pos) ax.scatter(xm, ym, c="g", s=20) for txt, x1, y1, pos_label in zip(pos_ids, xm, ym, pos_labels): ax.annotate(pos_label, xy=(x1, y1)) at = AnchoredText("\n".join(legend_lines), prop=dict(size=8), frameon=True, loc=1) at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") ax.add_artist(at)