2022-09-16 13:04:04 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2023-02-16 10:50:55 +00:00
|
|
|
# SPDX-FileCopyrightText: : 2017-2023 The PyPSA-Eur Authors
|
2020-05-29 07:50:55 +00:00
|
|
|
#
|
2021-09-14 14:37:41 +00:00
|
|
|
# SPDX-License-Identifier: MIT
|
2020-05-29 07:50:55 +00:00
|
|
|
|
2019-08-08 13:02:28 +00:00
|
|
|
"""
|
2019-08-11 20:34:18 +00:00
|
|
|
Creates Voronoi shapes for each bus representing both onshore and offshore
|
|
|
|
regions.
|
2019-08-11 09:40:47 +00:00
|
|
|
|
|
|
|
Relevant Settings
|
|
|
|
-----------------
|
|
|
|
|
2019-08-11 11:17:36 +00:00
|
|
|
.. code:: yaml
|
|
|
|
|
|
|
|
countries:
|
|
|
|
|
2019-11-14 16:50:24 +00:00
|
|
|
.. seealso::
|
2019-08-13 08:03:46 +00:00
|
|
|
Documentation of the configuration file ``config.yaml`` at
|
|
|
|
:ref:`toplevel_cf`
|
2019-08-11 11:17:36 +00:00
|
|
|
|
2019-08-11 09:40:47 +00:00
|
|
|
Inputs
|
|
|
|
------
|
|
|
|
|
2019-08-11 20:34:18 +00:00
|
|
|
- ``resources/country_shapes.geojson``: confer :ref:`shapes`
|
|
|
|
- ``resources/offshore_shapes.geojson``: confer :ref:`shapes`
|
|
|
|
- ``networks/base.nc``: confer :ref:`base`
|
|
|
|
|
2019-08-11 09:40:47 +00:00
|
|
|
Outputs
|
|
|
|
-------
|
|
|
|
|
2019-08-11 20:34:18 +00:00
|
|
|
- ``resources/regions_onshore.geojson``:
|
|
|
|
|
2023-03-09 12:28:42 +00:00
|
|
|
.. image:: img/regions_onshore.png
|
2019-08-11 20:34:18 +00:00
|
|
|
:scale: 33 %
|
|
|
|
|
|
|
|
- ``resources/regions_offshore.geojson``:
|
|
|
|
|
2023-03-09 12:28:42 +00:00
|
|
|
.. image:: img/regions_offshore.png
|
2019-08-11 20:34:18 +00:00
|
|
|
:scale: 33 %
|
|
|
|
|
2019-08-11 09:40:47 +00:00
|
|
|
Description
|
|
|
|
-----------
|
2019-08-08 13:02:28 +00:00
|
|
|
"""
|
2019-11-28 07:22:52 +00:00
|
|
|
|
|
|
|
import logging
|
2022-09-16 13:04:04 +00:00
|
|
|
|
2018-01-29 21:28:33 +00:00
|
|
|
import geopandas as gpd
|
2022-03-28 13:17:55 +00:00
|
|
|
import numpy as np
|
|
|
|
import pandas as pd
|
2020-12-03 18:50:53 +00:00
|
|
|
import pypsa
|
2022-03-28 13:17:55 +00:00
|
|
|
from _helpers import REGION_COLS, configure_logging
|
|
|
|
from scipy.spatial import Voronoi
|
|
|
|
from shapely.geometry import Polygon
|
2020-12-03 18:50:53 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2022-06-07 13:01:18 +00:00
|
|
|
def voronoi_partition_pts(points, outline):
|
2022-03-28 13:17:55 +00:00
|
|
|
"""
|
|
|
|
Compute the polygons of a voronoi partition of `points` within the
|
|
|
|
polygon `outline`. Taken from
|
|
|
|
https://github.com/FRESNA/vresutils/blob/master/vresutils/graph.py
|
|
|
|
Attributes
|
|
|
|
----------
|
|
|
|
points : Nx2 - ndarray[dtype=float]
|
|
|
|
outline : Polygon
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
polygons : N - ndarray[dtype=Polygon|MultiPolygon]
|
|
|
|
"""
|
|
|
|
points = np.asarray(points)
|
|
|
|
|
|
|
|
if len(points) == 1:
|
|
|
|
polygons = [outline]
|
|
|
|
else:
|
|
|
|
xmin, ymin = np.amin(points, axis=0)
|
|
|
|
xmax, ymax = np.amax(points, axis=0)
|
|
|
|
xspan = xmax - xmin
|
|
|
|
yspan = ymax - ymin
|
|
|
|
|
|
|
|
# to avoid any network positions outside all Voronoi cells, append
|
|
|
|
# the corners of a rectangle framing these points
|
|
|
|
vor = Voronoi(
|
|
|
|
np.vstack(
|
2022-09-16 13:04:04 +00:00
|
|
|
(
|
2022-03-28 13:17:55 +00:00
|
|
|
points,
|
2022-09-16 13:04:04 +00:00
|
|
|
[
|
2022-03-28 13:17:55 +00:00
|
|
|
[xmin - 3.0 * xspan, ymin - 3.0 * yspan],
|
|
|
|
[xmin - 3.0 * xspan, ymax + 3.0 * yspan],
|
|
|
|
[xmax + 3.0 * xspan, ymin - 3.0 * yspan],
|
|
|
|
[xmax + 3.0 * xspan, ymax + 3.0 * yspan],
|
2022-09-16 13:04:04 +00:00
|
|
|
],
|
2022-03-28 13:17:55 +00:00
|
|
|
)
|
2022-09-16 13:04:04 +00:00
|
|
|
)
|
|
|
|
)
|
2022-03-28 13:17:55 +00:00
|
|
|
|
|
|
|
polygons = []
|
|
|
|
for i in range(len(points)):
|
|
|
|
poly = Polygon(vor.vertices[vor.regions[vor.point_region[i]]])
|
|
|
|
|
|
|
|
if not poly.is_valid:
|
|
|
|
poly = poly.buffer(0)
|
|
|
|
|
2023-03-07 07:58:07 +00:00
|
|
|
with np.errstate(invalid="ignore"):
|
|
|
|
poly = poly.intersection(outline)
|
2022-03-28 13:17:55 +00:00
|
|
|
|
|
|
|
polygons.append(poly)
|
|
|
|
|
2022-11-15 06:45:13 +00:00
|
|
|
return polygons
|
2022-03-28 13:17:55 +00:00
|
|
|
|
|
|
|
|
2019-08-08 13:02:28 +00:00
|
|
|
if __name__ == "__main__":
|
2019-12-09 20:29:15 +00:00
|
|
|
if "snakemake" not in globals():
|
|
|
|
from _helpers import mock_snakemake
|
2022-09-16 13:04:04 +00:00
|
|
|
|
2019-12-09 20:29:15 +00:00
|
|
|
snakemake = mock_snakemake("build_bus_regions")
|
2019-11-28 07:22:52 +00:00
|
|
|
configure_logging(snakemake)
|
2019-08-08 13:02:28 +00:00
|
|
|
|
|
|
|
countries = snakemake.config["countries"]
|
|
|
|
|
|
|
|
n = pypsa.Network(snakemake.input.base_network)
|
|
|
|
|
|
|
|
country_shapes = gpd.read_file(snakemake.input.country_shapes).set_index("name")[
|
|
|
|
"geometry"
|
|
|
|
]
|
2022-06-23 12:25:30 +00:00
|
|
|
offshore_shapes = gpd.read_file(snakemake.input.offshore_shapes)
|
|
|
|
offshore_shapes = offshore_shapes.reindex(columns=REGION_COLS).set_index("name")[
|
|
|
|
"geometry"
|
|
|
|
]
|
2019-08-08 13:02:28 +00:00
|
|
|
|
|
|
|
onshore_regions = []
|
|
|
|
offshore_regions = []
|
|
|
|
|
|
|
|
for country in countries:
|
|
|
|
c_b = n.buses.country == country
|
|
|
|
|
|
|
|
onshore_shape = country_shapes[country]
|
|
|
|
onshore_locs = n.buses.loc[c_b & n.buses.substation_lv, ["x", "y"]]
|
|
|
|
onshore_regions.append(
|
|
|
|
gpd.GeoDataFrame(
|
|
|
|
{
|
2019-10-24 14:22:57 +00:00
|
|
|
"name": onshore_locs.index,
|
2019-08-08 13:02:28 +00:00
|
|
|
"x": onshore_locs["x"],
|
|
|
|
"y": onshore_locs["y"],
|
|
|
|
"geometry": voronoi_partition_pts(
|
|
|
|
onshore_locs.values, onshore_shape
|
|
|
|
),
|
|
|
|
"country": country,
|
2019-10-24 14:22:57 +00:00
|
|
|
}
|
|
|
|
)
|
2022-09-16 13:04:04 +00:00
|
|
|
)
|
|
|
|
|
2019-08-08 13:02:28 +00:00
|
|
|
if country not in offshore_shapes.index:
|
|
|
|
continue
|
|
|
|
offshore_shape = offshore_shapes[country]
|
|
|
|
offshore_locs = n.buses.loc[c_b & n.buses.substation_off, ["x", "y"]]
|
|
|
|
offshore_regions_c = gpd.GeoDataFrame(
|
|
|
|
{
|
2019-10-24 14:22:57 +00:00
|
|
|
"name": offshore_locs.index,
|
2019-08-08 13:02:28 +00:00
|
|
|
"x": offshore_locs["x"],
|
|
|
|
"y": offshore_locs["y"],
|
|
|
|
"geometry": voronoi_partition_pts(offshore_locs.values, offshore_shape),
|
|
|
|
"country": country,
|
2020-03-18 08:56:47 +00:00
|
|
|
}
|
|
|
|
)
|
2019-08-08 13:02:28 +00:00
|
|
|
offshore_regions_c = offshore_regions_c.loc[offshore_regions_c.area > 1e-2]
|
|
|
|
offshore_regions.append(offshore_regions_c)
|
|
|
|
|
2022-06-23 12:25:30 +00:00
|
|
|
pd.concat(onshore_regions, ignore_index=True).to_file(
|
|
|
|
snakemake.output.regions_onshore
|
|
|
|
)
|
|
|
|
if offshore_regions:
|
|
|
|
pd.concat(offshore_regions, ignore_index=True).to_file(
|
|
|
|
snakemake.output.regions_offshore
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
offshore_shapes.to_frame().to_file(snakemake.output.regions_offshore)
|