From 9aa154bd048c02fb511e78b1dc4f9524ef09251b Mon Sep 17 00:00:00 2001 From: Koen van Greevenbroek Date: Thu, 25 Jan 2024 10:20:19 +0100 Subject: [PATCH 1/2] Add several map projection options --- config/config.default.yaml | 1 + doc/configtables/plotting.csv | 1 + doc/release_notes.rst | 2 ++ scripts/plot_network.py | 29 ++++++++++++++++++++++++----- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index 255e3869..ef410ad6 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -773,6 +773,7 @@ plotting: color_geomap: ocean: white land: white + projection: "EqualEarth" eu_node_location: x: -5.5 y: 46. diff --git a/doc/configtables/plotting.csv b/doc/configtables/plotting.csv index ed5d9c9f..656ed9be 100644 --- a/doc/configtables/plotting.csv +++ b/doc/configtables/plotting.csv @@ -1,6 +1,7 @@ ,Unit,Values,Description map,,, -- boundaries,°,"[x1,x2,y1,y2]",Boundaries of the map plots in degrees latitude (y) and longitude (x) +projection,--,"{EqualEarth, EuroPP, LambertAzimuthalEqualArea, LambertConformal, Orthographic}",Projection to use for maps; default is EqualEarth. LambertConformal is recommended by the European Environmental Agency. costs_max,bn Euro,float,Upper y-axis limit in cost bar plots. costs_threshold,bn Euro,float,Threshold below which technologies will not be shown in cost bar plots. energy_max,TWh,float,Upper y-axis limit in energy bar plots. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index f8f958cd..0386cf49 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -35,6 +35,8 @@ Upcoming Release * Add support for the linopy ``io_api`` option; set to ``"direct"`` to increase model reading and writing performance for the highs and gurobi solvers. +* Add several map projection options for plotting. + PyPSA-Eur 0.9.0 (5th January 2024) ================================== diff --git a/scripts/plot_network.py b/scripts/plot_network.py index 33312a44..c078e412 100644 --- a/scripts/plot_network.py +++ b/scripts/plot_network.py @@ -170,7 +170,7 @@ def plot_map( line_widths = line_widths.replace(line_lower_threshold, 0) link_widths = link_widths.replace(line_lower_threshold, 0) - fig, ax = plt.subplots(subplot_kw={"projection": ccrs.EqualEarth()}) + fig, ax = plt.subplots(subplot_kw={"projection": proj}) fig.set_size_inches(7, 6) n.plot( @@ -358,7 +358,6 @@ def plot_h2_map(network, regions): n.links.bus0 = n.links.bus0.str.replace(" H2", "") n.links.bus1 = n.links.bus1.str.replace(" H2", "") - proj = ccrs.EqualEarth() regions = regions.to_crs(proj.proj4_init) fig, ax = plt.subplots(figsize=(7, 6), subplot_kw={"projection": proj}) @@ -568,7 +567,7 @@ def plot_ch4_map(network): "biogas": "seagreen", } - fig, ax = plt.subplots(figsize=(7, 6), subplot_kw={"projection": ccrs.EqualEarth()}) + fig, ax = plt.subplots(figsize=(7, 6), subplot_kw={"projection": proj}) n.plot( bus_sizes=bus_sizes, @@ -679,7 +678,7 @@ def plot_map_without(network): # Drop non-electric buses so they don't clutter the plot n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True) - fig, ax = plt.subplots(figsize=(7, 6), subplot_kw={"projection": ccrs.EqualEarth()}) + fig, ax = plt.subplots(figsize=(7, 6), subplot_kw={"projection": proj}) # PDF has minimum width, so set these to zero line_lower_threshold = 200.0 @@ -993,7 +992,7 @@ def plot_map_perfect( link_widths[link_widths > line_upper_threshold] = line_upper_threshold for year in costs.columns: - fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()}) + fig, ax = plt.subplots(subplot_kw={"projection": proj}) fig.set_size_inches(7, 6) fig.suptitle(year) @@ -1082,6 +1081,26 @@ if __name__ == "__main__": if map_opts["boundaries"] is None: map_opts["boundaries"] = regions.total_bounds[[0, 2, 1, 3]] + [-1, 1, -1, 1] + proj_str = snakemake.params.plotting.get("projection", "EqualEarth") + central_coords = dict(central_longitude=10.0, central_latitude=50.0) + if proj_str == "EqualEarth": + # Equal area but large distortions towards the poles. + proj = ccrs.EqualEarth() + elif proj_str == "EuroPP": + # UTM Zone 32 projection + proj = ccrs.EuroPP() + elif proj_str == "LambertConformal": + # The European Environment Agency recommends using this + # projection for conformal pan-European mapping + proj = ccrs.LambertConformal(standard_parallels=(35, 65), **central_coords) + elif proj_str == "Orthographic": + proj = ccrs.Orthographic(**central_coords) + else: + logger.warning( + f"Plotting project {proj_str} not recognised; falling back on EqualEarth" + ) + proj = ccrs.EqualEarth() + if snakemake.params["foresight"] == "perfect": plot_map_perfect( n, From 03c914bef136bea7fe5b17fdb649ecd3239cf938 Mon Sep 17 00:00:00 2001 From: Koen van Greevenbroek Date: Thu, 25 Jan 2024 14:34:03 +0100 Subject: [PATCH 2/2] Make projection selection flexible by reading directly from config --- config/config.default.yaml | 8 +++++++- doc/configtables/plotting.csv | 4 +++- doc/release_notes.rst | 2 +- scripts/plot_network.py | 22 +++------------------- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/config/config.default.yaml b/config/config.default.yaml index ef410ad6..51080862 100644 --- a/config/config.default.yaml +++ b/config/config.default.yaml @@ -773,7 +773,13 @@ plotting: color_geomap: ocean: white land: white - projection: "EqualEarth" + projection: + name: "EqualEarth" + # See https://scitools.org.uk/cartopy/docs/latest/reference/projections.html for alternatives, for example: + # name: "LambertConformal" + # central_longitude: 10. + # central_latitude: 50. + # standard_parallels: [35, 65] eu_node_location: x: -5.5 y: 46. diff --git a/doc/configtables/plotting.csv b/doc/configtables/plotting.csv index 656ed9be..82fc203c 100644 --- a/doc/configtables/plotting.csv +++ b/doc/configtables/plotting.csv @@ -1,7 +1,9 @@ ,Unit,Values,Description map,,, -- boundaries,°,"[x1,x2,y1,y2]",Boundaries of the map plots in degrees latitude (y) and longitude (x) -projection,--,"{EqualEarth, EuroPP, LambertAzimuthalEqualArea, LambertConformal, Orthographic}",Projection to use for maps; default is EqualEarth. LambertConformal is recommended by the European Environmental Agency. +projection,,,, +-- name,--,"Valid Cartopy projection name","See https://scitools.org.uk/cartopy/docs/latest/reference/projections.html for list of available projections." +-- args,--,--,"Other entries under 'projection' are passed as keyword arguments to the projection constructor, e.g. ``central_longitude: 10.``." costs_max,bn Euro,float,Upper y-axis limit in cost bar plots. costs_threshold,bn Euro,float,Threshold below which technologies will not be shown in cost bar plots. energy_max,TWh,float,Upper y-axis limit in energy bar plots. diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 0386cf49..93d1a268 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -35,7 +35,7 @@ Upcoming Release * Add support for the linopy ``io_api`` option; set to ``"direct"`` to increase model reading and writing performance for the highs and gurobi solvers. -* Add several map projection options for plotting. +* Add the option to customise map projection in plotting config. PyPSA-Eur 0.9.0 (5th January 2024) diff --git a/scripts/plot_network.py b/scripts/plot_network.py index c078e412..13736d01 100644 --- a/scripts/plot_network.py +++ b/scripts/plot_network.py @@ -1081,25 +1081,9 @@ if __name__ == "__main__": if map_opts["boundaries"] is None: map_opts["boundaries"] = regions.total_bounds[[0, 2, 1, 3]] + [-1, 1, -1, 1] - proj_str = snakemake.params.plotting.get("projection", "EqualEarth") - central_coords = dict(central_longitude=10.0, central_latitude=50.0) - if proj_str == "EqualEarth": - # Equal area but large distortions towards the poles. - proj = ccrs.EqualEarth() - elif proj_str == "EuroPP": - # UTM Zone 32 projection - proj = ccrs.EuroPP() - elif proj_str == "LambertConformal": - # The European Environment Agency recommends using this - # projection for conformal pan-European mapping - proj = ccrs.LambertConformal(standard_parallels=(35, 65), **central_coords) - elif proj_str == "Orthographic": - proj = ccrs.Orthographic(**central_coords) - else: - logger.warning( - f"Plotting project {proj_str} not recognised; falling back on EqualEarth" - ) - proj = ccrs.EqualEarth() + proj_kwargs = snakemake.params.plotting.get("projection", dict(name="EqualEarth")) + proj_func = getattr(ccrs, proj_kwargs.pop("name")) + proj = proj_func(**proj_kwargs) if snakemake.params["foresight"] == "perfect": plot_map_perfect(