def fig_uo_coverage_grid(lad20cd: str, uo_sensors: gpd.GeoDataFrame, theta: int, save_dir: Path): """Calculate the coverage the Urban Observatory sensor network provides on a square grid. Figure name: urb_obs_coverage_grid_theta_{theta}_nsensors_{n_sensors}.png Parameters ---------- lad20cd : str Local authority code uo_sensors : gpd.GeoDataFrame Urban Observatory sensor locations theta : int Coverage distance to use save_dir : Path Directory to save figure """ fig, ax = get_fig_grid(nrows_ncols=(1, 1)) ax = ax[0] cmap = "Greens" plot_uo_coverage_grid(lad20cd, uo_sensors=uo_sensors, ax=ax, legend=False, cmap=cmap) add_colorbar(ax, cmap=cmap, label="Coverage") add_scalebar(ax) t_str = f"theta_{theta}" n_str = f"nsensors_{len(uo_sensors)}" save_fig(fig, f"urb_obs_coverage_grid_{t_str}_{n_str}.png", save_dir)
def fig_single_obj(thetas: list, n_sensors: list, results: dict, all_groups: dict, save_dir: Path): """Save figures showing optimised networks with different parameters (number of sensors and coverage distance, theta) for each objective. Names of saved figures: "{plot_obj}_{thetas}_{n_sensors}.png", where `plot_obj` is the objective name from all_groups. Parameters ---------- thetas : list Theta (coverage distance) values to plot (must exist in results) n_sensors : list Network sizes (number of sensors) to plot (must exist in results) results : dict Previous optimisation results (e.g. from networks_single_obj.make_single_obj_networks) all_groups : dict Short name (keys) and long title (values) for each objective to plot save_dir : Path Directory to save figures in. """ n_figs = len(thetas) * len(n_sensors) n_rows = floor(sqrt(n_figs)) n_cols = ceil(n_figs / n_rows) for plot_obj in all_groups.keys(): fig, grid = get_fig_grid(nrows_ncols=(n_rows, n_cols)) i = 0 for t in thetas: for s in n_sensors: r = results[plot_obj][f"theta{t}"][f"{s}sensors"] plot_optimisation_result( r, title= f"n = {s}, $\\theta$ = {t} m, c = {r['total_coverage']:.2f}", ax=grid[i], show=False, legend=False, sensor_size=50, sensor_color="green", sensor_edgecolor="yellow", sensor_linewidth=1.5, ) if i == 1: add_scalebar(grid[i]) i += 1 add_colorbar(grid[-1], label="Coverage", cmap="Greens") fig.suptitle(all_groups[plot_obj]["title"], y=0.87, fontsize=20) t_str = "theta" + "_".join(str(t) for t in thetas) n_str = "nsensors" + "_".join(str(n) for n in n_sensors) save_fig(fig, f"{plot_obj}_{t_str}_{n_str}.png", save_dir)
def fig_importance( lad20cd: str, groups: dict, oa_weights: dict, theta: float, save_dir: Path, vmax: float = 0.06, ): """Save a figure showing the "importance" of each output area for each objective, where importance is the overall coverage of the whole local authority provided by a network with a single sensor placed in that output area. Name of figure: demographics_importance.png Parameters ---------- lad20cd : str Local authority code to generate results for groups : dict Name and title of each population group/objective oa_weights : dict Weight for each output area for each group/objective theta : float Coverage distance save_dir : Path Directory to save figure in vmax : float, optional Max value for colour scale, by default 0.06 """ fig, grid = get_fig_grid() for i, g in enumerate(groups.items()): name = g[0] title = g[1]["title"] plot_oa_importance( lad20cd, oa_weights[name], theta=theta, vmax=vmax, ax=grid[i], show=False, legend=False, title=title, ) if i == 1: add_scalebar(grid[i]) add_colorbar(grid[-1], vmax=vmax, label="Importance") save_fig(fig, "demographics_importance.png", save_dir)
def fig_density( lad20cd: str, oa: gpd.GeoDataFrame, all_groups: dict, save_dir: Path, vmax: float = 6, ): """Save a figure showing the density of each demographic variable/objective for each output area (OA), measured as the fraction of the population in that OA divided by the area of the OA. Name of figure: demographics_density.png Parameters ---------- lad20cd : str Local authority code to generate results for oa : gpd.GeoDataFrame Data frame with stats for each variable in each output area (e.g. from calc_oa_density) all_groups : dict Short name (keys) and long title (values) for each objective save_dir : Path Directory to save figure in vmax : float, optional Max value for colour scale, by default 6 """ fig, grid = get_fig_grid() for i, g in enumerate(all_groups.items()): name = g[0] title = g[1]["title"] plot_oa_weights( lad20cd, 100 * oa[f"{name}_reld"], title=title, vmax=vmax, ax=grid[i], legend=False, show=False, ) if i == 1: add_scalebar(grid[i]) add_colorbar(grid[-1], vmax=vmax, label=r"Density [% / $\mathrm{km}^2$]") save_fig(fig, "demographics_density.png", save_dir)
def fig_uo_sensor_locations(lad20cd: str, uo_sensors: gpd.GeoDataFrame, save_dir: Path): """Show the location of all sensors in the Urban Observatory network (points only). Figure nane: urb_obs_sensors_nsensors_{N}.png (where {N} is the no. sensors in the UO network) Parameters ---------- lad20cd : str Local authority code uo_sensors : gpd.GeoDataFrame Urban Observatory sensor locations save_dir : Path Directory to save figure """ fig, ax = plt.subplots(1, 1, figsize=(15, 15)) plot_sensors(lad20cd, uo_sensors, centroids=False, ax=ax) add_scalebar(ax) save_fig(fig, f"urb_obs_sensors_nsensors_{len(uo_sensors)}.png", save_dir)
def fig_uo_coverage_oa(uo_coverage: dict, theta: float, all_groups: dict, save_dir: Path): """Plot the coverage of each output area provide by the Urban Observatory sensors. Figure name: urb_obs_coverage_oa_theta_{theta}_nsensors_{n_sensors}.png Parameters ---------- uo_coverage : dict Coverage of each output area with the Urban Observatory network (e.g. from get_uo_coverage_oa) theta : float Coverage distance to use all_groups : dict Short name (keys) and long title (values) for each objective save_dir : Path Directory to save figure """ title = f"Urban Observatory: Coverage with $\\theta$ = {theta} m\n" for name, params in all_groups.items(): title += f"{params['title']}: {uo_coverage[name]['total_coverage']:.2f}, " title = title[:-2] fig, ax = get_fig_grid(nrows_ncols=(1, 1)) plot_optimisation_result( uo_coverage[name], title=title, ax=ax[0], show=False, legend=False, sensor_size=50, sensor_color="green", sensor_edgecolor="yellow", sensor_linewidth=1.5, ) add_scalebar(ax[0]) add_colorbar(ax[0], cmap="Greens", label="Coverage") t_str = f"theta_{theta}" n_str = f"nsensors_{uo_coverage['n_sensors']}" save_fig(fig, f"urb_obs_coverage_oa_{t_str}_{n_str}.png", save_dir)
def fig_uo_coverage_oa_diff( lad20cd: str, uo_coverage: dict, theta: float, all_groups: dict, networks: dict, save_dir: Path, ): """Show the coverage difference of each outupt area between the Urban Observatory network and networks optimised for the coverage of single objectives (single population sub-groups). Figure name: urb_obs_coverage_difference_oa_theta_{theta}_nsensors_{n_sensors}.png Parameters ---------- lad20cd : str Local authority code uo_sensors : gpd.GeoDataFrame Urban Observatory sensor locations theta : float Coverage distance to use all_groups : dict Short name (keys) and long title (values) for each objective networks : dict Previous optimisation results (e.g. from networks_single_obj.make_single_obj_networks) save_dir : Path Directory to save figure """ fig, grid = get_fig_grid() cmap = get_diff_cmap() n_uo_oa = uo_coverage["n_sensors"] for i, (name, params) in enumerate(all_groups.items()): uo_cov = uo_coverage[name]["oa_coverage"] uo_cov = pd.DataFrame(uo_cov).set_index("oa11cd") uo_cov.rename(columns={"coverage": "urb_obs"}, inplace=True) greedy_cov = networks[name][f"theta{theta}"][f"{n_uo_oa}sensors"][ "oa_coverage"] greedy_cov = pd.DataFrame(greedy_cov).set_index("oa11cd") greedy_cov.rename(columns={"coverage": "greedy"}, inplace=True) compare_nets = uo_cov.join(greedy_cov) compare_nets["diff"] = compare_nets["greedy"] - compare_nets["urb_obs"] compare_nets["diff"].describe() oa_shapes = get_oa_shapes(lad20cd) oa_shapes = oa_shapes.join(compare_nets["diff"]) vmin = -1 vmax = 1 oa_shapes.plot(column="diff", alpha=0.85, cmap=cmap, ax=grid[i], vmin=vmin, vmax=vmax) ctx.add_basemap( grid[i], source="http://a.tile.stamen.com/toner/{z}/{x}/{y}.png", crs=oa_shapes.crs.to_epsg(), ) grid[i].set_axis_off() grid[i].set_title(params["title"]) add_scalebar(grid[1]) add_colorbar(grid[-1], cmap=cmap, label="Coverage Difference", vmin=vmin, vmax=vmax) fig.suptitle( f"Comparisons with Urban Observatory Network (n = {n_uo_oa}, $\\theta$ = {theta} m)", y=0.87, fontsize=20, ) t_str = f"theta_{theta}" n_str = f"nsensors_{n_uo_oa}" save_fig(fig, f"urb_obs_coverage_difference_oa_{t_str}_{n_str}.png", save_dir)
def fig_uo_coverage_grid_diff( lad20cd: str, uo_sensors: gpd.GeoDataFrame, theta: float, all_groups: dict, networks: dict, save_dir: Path, ): """Show the coverage difference, on a square grid, between the Urban Observatory network and networks optimised for the coverage of single objectives (single population sub-groups). Figure name: urb_obs_coverage_difference_grid_theta_{theta}_nsensors_{n_sensors}.png Parameters ---------- lad20cd : str Local authority code uo_sensors : gpd.GeoDataFrame Urban Observatory sensor locations theta : float Coverage distance to use all_groups : dict Short name (keys) and long title (values) for each objective networks : dict Previous optimisation results (e.g. from networks_single_obj.make_single_obj_networks) save_dir : Path Directory to save figure """ uo_sensors_xy = np.array( [uo_sensors["geometry"].x.values, uo_sensors["geometry"].y.values]).T oa = get_oa_shapes(lad20cd) bounds = oa["geometry"].bounds xlim = (bounds["minx"].min(), bounds["maxx"].max()) ylim = (bounds["miny"].min(), bounds["maxy"].max()) grid_size = 100 uo_cov = coverage_grid(uo_sensors_xy, xlim, ylim, theta=theta, grid_size=grid_size) n_uo_oa = uo_sensors["oa11cd"].nunique() fig, grid = get_fig_grid() cmap = get_diff_cmap() vmin = -1 vmax = 1 for i, (name, params) in enumerate(all_groups.items()): greedy_sensors = pd.DataFrame( networks[name][f"theta{theta}"][f"{n_uo_oa}sensors"]["sensors"]) greedy_sensors = np.array(greedy_sensors[["x", "y"]]) greedy_cov = coverage_grid(greedy_sensors, xlim, ylim, theta=theta, grid_size=grid_size) cov_diff = uo_cov.copy() cov_diff["coverage"] = greedy_cov["coverage"] - uo_cov["coverage"] plot_coverage_grid( lad20cd, cov_diff, ax=grid[i], vmin=vmin, vmax=vmax, legend=False, cmap=cmap, title=params["title"], ) add_scalebar(grid[1]) add_colorbar(grid[-1], cmap=cmap, vmin=vmin, vmax=vmax, label="Coverage Difference") fig.suptitle( f"Comparisons with Urban Observatory Network (n = {n_uo_oa}, $\\theta$ = {theta} m)", y=0.87, fontsize=20, ) t_str = f"theta_{theta}" n_str = f"nsensors_{n_uo_oa}" save_fig(fig, f"urb_obs_coverage_difference_grid_{t_str}_{n_str}.png", save_dir)
def fig_max_min_coverage( lad20cd: str, scores: np.ndarray, objs: list, theta: float, n_sensors: int, solutions: np.ndarray, inputs: dict, save_dir: float, ): """Plot the network that maximises the minimum coverage across all objectives. Parameters ---------- lad20cd : str Local authority code scores : np.ndarray Coverage values for each objective in each candidate network objs : list Names of objectives in the order they appear in scores theta : float Coverage distance to use n_sensors : int No. of sensors in the network solutions : np.ndarray Sensor output area indices for each candidate network inputs : dict Optimisation inputs from networks_multi_objs.get_multi_obj_inputs save_dir : float Directory to save figure """ # find network that has the maximum minimum coverage across all objectives # (i.e. find network where all objectives have a coverage of at least t, for # highest possible t) t = scores.min() delta = 0.001 remaining = len(scores) while remaining > 0: t += delta remaining = (scores > t).all(axis=1).sum() t -= delta best_idx = (scores > t).all(axis=1).argmax() sensor_idx = solutions[best_idx].astype(int) sensor_dict = [{ "oa11cd": inputs["oa11cd"][idx], "x": inputs["oa_x"][idx], "y": inputs["oa_y"][idx], } for idx in sensor_idx] coverage = calc_coverage(lad20cd, sensor_dict, oa_weight=inputs["oa_weight"]["pop_total"], theta=theta) coverage["sensors"] = sensor_dict coverage["lad20cd"] = lad20cd title = "".join(f"{obj} = {score:.2f}, " for obj, score in zip(objs, scores[best_idx])) title = title[:-2] title += f"\n(n = {n_sensors}, $\\theta$ = {theta} m)" fig, ax = get_fig_grid(nrows_ncols=(1, 1)) plot_optimisation_result( coverage, title=title, ax=ax[0], show=False, legend=False, sensor_size=50, sensor_color="green", sensor_edgecolor="yellow", sensor_linewidth=1.5, ) add_scalebar(ax[0]) add_colorbar(ax[0], cmap="Greens", label="Coverage") save_fig( fig, f"multiobj_compromise_theta{theta}_{n_sensors}sensors_cov{round(t, 3)}.png", save_dir, )
def fig_max_child_work_above_threshold( lad20cd: str, scores: np.ndarray, objs: list, threshold: float, theta: float, n_sensors: int, solutions: np.ndarray, inputs: dict, work_name: str, child_name: str, save_dir: Path, ): """From a set of candidate networks, plot the one that maximsies coverage of children whlist also keeping the coverage of worklplaces above a minimum threshold. Figure name: multiobj_wplace{work_cov}_child{child_cov}_theta{theta}_{n_sensors}sensors.png Parameters ---------- lad20cd : str Local authority code scores : np.ndarray Coverage values for each objective in each candidate network objs : list Names of objectives in the order they appear in scores threshold : float Only consider networks with at least this coverage of workplaces theta : float Coverage distance to use n_sensors : int No. of sensors in the network solutions : np.ndarray Sensor output area indices for each candidate network inputs : dict Optimisation inputs from networks_multi_objs.get_multi_obj_inputs work_name : str Name of the workplace objective child_name : str Name of the coverage of children objective save_dir : Path Directory to save the figure """ work_idx = objs.index(work_name) child_idx = objs.index(child_name) if (scores[:, work_idx] < threshold).all(): print( f"No networks with workplace coverage > {threshold}, skipping figure" ) return network_idx = scores[scores[:, work_idx] > threshold, child_idx].argmax() sensor_idx = solutions[ scores[:, work_idx] > threshold][network_idx].astype(int) cov = scores[scores[:, work_idx] > threshold][network_idx] sensor_dict = [{ "oa11cd": inputs["oa11cd"][idx], "x": inputs["oa_x"][idx], "y": inputs["oa_y"][idx], } for idx in sensor_idx] # select weight for 1st objective # (weights don't matter for calculating coverage of each OA, only for calculating # overrall coverage) w = list(inputs["oa_weight"].values())[0] coverage = calc_coverage(lad20cd, sensor_dict, oa_weight=w, theta=theta) coverage["sensors"] = sensor_dict coverage["lad20cd"] = lad20cd title = "".join(f"{obj} = {score:.2f}, " for obj, score in zip(objs, cov)) title = title[:-2] title += f"\n(n = {n_sensors}, $\\theta$ = {theta} m)" fig, ax = get_fig_grid(nrows_ncols=(1, 1)) plot_optimisation_result( coverage, title=title, ax=ax[0], show=False, legend=False, sensor_size=50, sensor_color="green", sensor_edgecolor="yellow", sensor_linewidth=1.5, ) add_scalebar(ax[0]) add_colorbar(ax[0], cmap="Greens", label="Coverage") save_fig( fig, f"multiobj_wplace{round(cov[3], 2)}_child{round(cov[1], 2)}_theta{theta}_{n_sensors}sensors.png", save_dir, )
def fig_two_objs_spectrum( lad20cd: str, plot_objs: list, scores: np.ndarray, solutions: np.ndarray, inputs: dict, all_groups: dict, theta: float, n_sensors: int, save_dir: Path, ): """Save a figure showing a range of networks varying from maximal coverage of one objective to maximal coverage of another, showing the trade-offs between the two. Figure name: 2obj_spectrum_theta{theta}_{n_sensors}sensors.png Parameters ---------- lad20cd : str Local authority code plot_objs : list Names of the two objectives to plot scores : np.ndarray Coverage values for each objective in each candidate network solutions : np.ndarray Output area indices for each sensor in each candidate network inputs : dict Optimisation inputs from networks_two_objs.get_two_obj_inputs all_groups : dict Short name (keys) and long title (values) for each objective theta : float Coverage distance to use n_sensors : int Number of sensors in the candidate networks save_dir : Path Directory to save the figure """ population_size = len(scores) rank_idx = scores[:, 0].argsort() ranks_to_plot = np.linspace(0, population_size - 1, 4).astype(int) fig, ax = get_fig_grid() for i, rank in enumerate(ranks_to_plot): idx = rank_idx[rank] sensor_idx = solutions[idx].astype(int) sensor_dict = [{ "oa11cd": inputs["oa11cd"][idx], "x": inputs["oa_x"][idx], "y": inputs["oa_y"][idx], } for idx in sensor_idx] coverage = calc_coverage( lad20cd, sensor_dict, oa_weight=inputs["oa_weight"][plot_objs[0]], theta=theta, ) coverage["sensors"] = sensor_dict coverage["lad20cd"] = lad20cd title = "".join( f"{all_groups[obj]['title']} = {score:.2f}, " for obj, score in zip(["pop_elderly", "workplace"], scores[idx])) title = title[:-2] plot_optimisation_result( coverage, title=title, ax=ax[i], show=False, legend=False, sensor_size=50, sensor_color="green", sensor_edgecolor="yellow", sensor_linewidth=1.5, ) add_scalebar(ax[1]) add_colorbar(ax[-1], cmap="Greens", label="Coverage") fig.suptitle(f"n = {n_sensors}, $\\theta$ = {theta} m", y=0.87, fontsize=20) save_fig(fig, f"2obj_spectrum_theta{theta}_{n_sensors}sensors.png", save_dir)