Source code for pyflow_acdc.windfarm_loader
"""Wind-farm case loading.
Loads a bundled wind-farm case grid together with its GeoJSON geographic
context.
Owns: locating and loading bundled wind-farm case data.
Does not own: grid construction (delegates to ``grid_creator``).
"""
import json
import os
from pathlib import Path
from shapely.geometry import LineString, MultiPolygon, Polygon, shape
from .grid_creator import create_grid_from_pickle
__all__ = ['load_case_grid_and_geo']
def _file_stem(case_name):
return case_name.lower()
def _candidate_dirs():
current_file = Path(__file__).resolve()
pkg_root = current_file.parent
candidates = []
# Optional override for private/local datasets.
custom_data_dir = os.getenv("PYFLOW_WINDFARM_DATA_DIR")
if custom_data_dir:
candidates.append(Path(custom_data_dir))
# Default packaged wind-farm data location.
candidates.extend(
[
pkg_root / "example_grids" / "wind_farm_data",
]
)
return candidates
def _find_grid_pickle(case_name, source_tag="gebco"):
name = _file_stem(case_name)
tag = source_tag.lower()
for base in _candidate_dirs():
for candidate in (
base / f"{name}_{tag}.pkl.gz",
base / f"{name}.pkl.gz",
):
if candidate.exists():
return candidate
raise FileNotFoundError(f"Could not find grid pickle for case '{case_name}'.")
def _find_geojson(case_name):
name = _file_stem(case_name)
for base in _candidate_dirs():
candidate = base / f"{name}.geojson"
if candidate.exists():
return candidate
return None
def _parse_geojson_context(geojson_path):
if geojson_path is None:
return [], [], [], []
payload = json.loads(geojson_path.read_text(encoding="utf-8"))
dev_area_polygons = []
exclusion_polygons = []
soft_exclusion_polygons = []
export_lines = []
def _as_polygon_list(geom_obj):
if isinstance(geom_obj, Polygon):
return [geom_obj]
if isinstance(geom_obj, MultiPolygon):
return list(geom_obj.geoms)
return []
for feature in payload.get("features", []):
props = feature.get("properties", {})
geom = feature.get("geometry", {})
my_type = props.get("myType")
geom_type = geom.get("type")
coords = geom.get("coordinates")
if not coords:
continue
try:
geom_obj = shape(geom)
except Exception:
continue
if my_type == "pixel":
pixel_parts = _as_polygon_list(geom_obj)
dev_area_polygons.extend(pixel_parts)
# Match create_array_graph_from_geojson behavior: interior rings act as exclusions.
for poly in pixel_parts:
for interior in poly.interiors:
exclusion_polygons.append(Polygon(interior))
elif my_type == "exclusion_zone":
exclusion_polygons.extend(_as_polygon_list(geom_obj))
elif my_type == "exclusion_zone_soft":
soft_exclusion_polygons.extend(_as_polygon_list(geom_obj))
elif my_type == "export_cable":
if geom_type == "LineString":
export_lines.append(geom_obj if isinstance(geom_obj, LineString) else LineString(coords))
elif geom_type == "MultiLineString":
export_lines.extend(list(geom_obj.geoms))
return dev_area_polygons, export_lines, exclusion_polygons, soft_exclusion_polygons
[docs]
def load_case_grid_and_geo(case_name, source_tag="gebco"):
"""Load a bundled wind-farm grid and attach GeoJSON geographic context.
Locates ``{case_name.lower()}_{source_tag}.pkl.gz`` (or ``{case_name.lower()}.pkl.gz``)
under the packaged ``example_grids/wind_farm_data`` tree or ``PYFLOW_WINDFARM_DATA_DIR``.
Optionally loads ``{case_name.lower()}.geojson`` and attaches polygons/lines to ``grid``
(``dev_polygon``, ``export_cables``, ``exclusion_zones``, ``soft_exclusion_zones``).
Parameters
----------
case_name : str
Wind-farm case name (resolved to lowercase filenames under ``wind_farm_data/``).
source_tag : str, optional
Pickle filename tag (default ``"gebco"``).
Returns
-------
tuple
``(grid, res)`` from :func:`create_grid_from_pickle`.
Examples
--------
>>> import pyflow_acdc as pyf
>>> grid, res = pyf.windfarm_loader.load_case_grid_and_geo("moray_east")
>>> # Geometry context is attached for folium / array plotting:
>>> # grid.dev_polygon, grid.export_cables, grid.exclusion_zones, ...
"""
grid_pickle = _find_grid_pickle(case_name, source_tag=source_tag)
grid, res = create_grid_from_pickle(str(grid_pickle), use_dill=True)
geojson_path = _find_geojson(case_name)
dev_area_polygons, export_lines, exclusion_zones, soft_exclusion_zones = _parse_geojson_context(geojson_path)
# Attach geometry context for plotting helpers.
grid.dev_polygon = dev_area_polygons
grid.export_cables = export_lines
grid.exclusion_zones = exclusion_zones
grid.soft_exclusion_zones = soft_exclusion_zones
return grid, res