pypsa-eur/scripts/retrieve_osm_data.py
Bobby Xiong 0c36de9bf8
Introducing OpenStreetMap high-voltage grid to PyPSA-Eur (#1079)
* Implemented  which uses the overpass API to download power features for individual countries.

* Extended  rule by input.

* Bug fixes and improvements to clean_osm_data.py. Added  in retrieve_osm_data.py.

* Updated clean_osm_data and retrieve_osm_data to create clean substations.

* Finished clean_osm_data function.

* Added check whether line is a circle. If so, drop it.

* Extended build_electricity.smk by build_osm_network.py

* Added build_osm_network

* Working osm-network-fast

* Bug fixes.

* Finalised and cleaned  including docstrings.

* Added try catch to retrieve_osm_data. Allows for parallelisation of downloads.

* Updated cleaning process.

* Set maximum number of threads for retrieving to 4, wrt. fair usage policy and potential request errors.

* Intermediate update on clean_osm_data.py. Added docstrings.

* Bug fix.

* Bug fix.

* Bug fixes in data types out of clean_osm_data

* Significant improvements to retrieve_osm_data, clean_osm_data. Cleaned code. Speed improvements

* Cleaned config.

* Fixes.

* Bug fixes.

* Updated default config

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Removed overpass from required packages. Not needed anymore.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added links_relations (route = power, frequency = 0) to retrieval. This will change how HVDC links are extracted in the near future.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Work-in-progress clean_osm_data

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Added clean links output to clean_osm_data. Script uses OSM relations to retrieve clean HVDC links.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* New code for integrating HVDC links. Using relations. Base network implementation functioning.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* removed manual line dropping.

* Updated clean script

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* reverted Snakefile to default: sync settings

* added prebuilt functionality.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Updated build_electricity.smk to work with scenario management.

* removed commented-out code.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* removed commented-out code.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fixed bug in pdf export by substituting pdf export with svg.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Bug-fix Snakefile

* dropped not needed columns from build_osm_network.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Updated build_shapes, config.default and clean_osm_data.

* pre-commit changes.

* test

* Added initial prepare_osm_network_release.py script

* Finalised prepare_osm_network_release script to build clean and stable OSM base_network input files.

* Added new rules/development.smk

* Updated clean_osm_data to add substation_centroid to linestrings

* Updated clean_osm_data to add substation_centroid to linestrings

* Updated clean_osm_data to add substation_centroid to linestrings

* Updated clean_osm_data to add substation_centroid to linestrings

* Added osm-prebuilt functionality and zenodo sandbox repository.

* Updated clean_osm_data to geopandas v.1.01

* Made base_network and build_osm_network function more robust for empty links.

* Made base_network and build_osm_network function more robust for empty links.

* Bug fix in base_network. Voltage level null is now kept (relevant e.g. for Corsica)

* Merge with hcanges in upstream PR 1146. Fixing UA and MD.

* Updated Zenodo and fixed prepare_osm_network_release

* Updated osm network release.

* Updated prepare osm network release.

* Updated MD, UA scripts.

* Cleaned determine_availability_matrix_MD_UA.py, removed redundant code

* Bug fixes.

* Bug fixes for UA MD scripts.

* Rename of build script.

* Bug fix: only distribute load to buses with substation.

* Updated zenodo sandbox repository.

* Updated config.default

* Cleaned config.default.yaml: Related settings grouped together and redundant voltage settings aggregated.

* Cleaned config.default.yaml: Related settings grouped together and redundant voltage settings aggregated. Added release notes.

* Updated Zenodo repositories for OSM-prebuilt to offcial publication.

* Updated configtables

* Updated links.csv: Under_construction lines to in commission.

* Updated link 8394 and parameter_corrections: Continuation of North-Sea-Link.

* Major update: fix simplify_network, fix Corsica, updated build_osm_network to include lines overpassing nodes.

* remove config backup

* Bug fix: Carrier type of all supernodes corrected to 'AC'

* Bug fix: Carrier type of all supernodes corrected to 'AC'

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Updated rules and base_network for compatibility with TYNDP projects.

* Updated Zenodo repository and prebuilt network to include 150 kV HVDC connections.

* Removed outdated config backup.

* Implemented all comments from PR #1079. Cleaned up OSM implementation.

* Bug fix: Added all voltages, 200 kV-750 kV, to default config.

* Cleaning and bugfixes.

* Updated Zenodo repository to https://zenodo.org/records/13358976. Added converter voltages, 'underground' property for DC lines/cables, and included Konti-Skan HVDC (DK-SE). Added compatibility with https://github.com/PyPSA/pypsa-eur/pull/1079 and https://github.com/PyPSA/pypsa-eur/pull/1085

* Apply suggestions from code review

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* simplify_network: handle complicated transformer topologies

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* syntax fix

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Fabian Neumann <fabian.neumann@outlook.de>
2024-08-22 15:01:20 +02:00

153 lines
5.1 KiB
Python

# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
#
# SPDX-License-Identifier: MIT
"""
Retrieve OSM data for the specified country using the overpass API and save it
to the specified output files.
Note that overpass requests are based on a fair
use policy. `retrieve_osm_data` is meant to be used in a way that respects this
policy by fetching the needed data once, only.
"""
import json
import logging
import os
import time
import requests
from _helpers import ( # set_scenario_config,; update_config_from_wildcards,; update_config_from_wildcards,
configure_logging,
set_scenario_config,
)
logger = logging.getLogger(__name__)
def retrieve_osm_data(
country,
output,
features=[
"cables_way",
"lines_way",
"links_relation",
"substations_way",
"substations_relation",
],
):
"""
Retrieve OSM data for the specified country and save it to the specified
output files.
Parameters
----------
country : str
The country code for which the OSM data should be retrieved.
output : dict
A dictionary mapping feature names to the corresponding output file
paths. Saving the OSM data to .json files.
features : list, optional
A list of OSM features to retrieve. The default is [
"cables_way",
"lines_way",
"substations_way",
"substations_relation",
].
"""
# Overpass API endpoint URL
overpass_url = "https://overpass-api.de/api/interpreter"
features_dict = {
"cables_way": 'way["power"="cable"]',
"lines_way": 'way["power"="line"]',
"links_relation": 'relation["route"="power"]["frequency"="0"]',
"substations_way": 'way["power"="substation"]',
"substations_relation": 'relation["power"="substation"]',
}
wait_time = 5
for f in features:
if f not in features_dict:
logger.info(
f"Invalid feature: {f}. Supported features: {list(features_dict.keys())}"
)
raise ValueError(
f"Invalid feature: {f}. Supported features: {list(features_dict.keys())}"
)
retries = 3
for attempt in range(retries):
logger.info(
f" - Fetching OSM data for feature '{f}' in {country} (Attempt {attempt+1})..."
)
# Build the overpass query
op_area = f'area["ISO3166-1"="{country}"]'
op_query = f"""
[out:json];
{op_area}->.searchArea;
(
{features_dict[f]}(area.searchArea);
);
out body geom;
"""
try:
# Send the request
response = requests.post(overpass_url, data=op_query)
response.raise_for_status() # Raise HTTPError for bad responses
data = response.json()
filepath = output[f]
parentfolder = os.path.dirname(filepath)
if not os.path.exists(parentfolder):
os.makedirs(parentfolder)
with open(filepath, mode="w") as f:
json.dump(response.json(), f, indent=2)
logger.info(" - Done.")
break # Exit the retry loop on success
except (json.JSONDecodeError, requests.exceptions.RequestException) as e:
logger.error(f"Error for feature '{f}' in country {country}: {e}")
logger.debug(
f"Response text: {response.text if response else 'No response'}"
)
if attempt < retries - 1:
wait_time += 15
logger.info(f"Waiting {wait_time} seconds before retrying...")
time.sleep(wait_time)
else:
logger.error(
f"Failed to retrieve data for feature '{f}' in country {country} after {retries} attempts."
)
except Exception as e:
# For now, catch any other exceptions and log them. Treat this
# the same as a RequestException and try to run again two times.
logger.error(
f"Unexpected error for feature '{f}' in country {country}: {e}"
)
if attempt < retries - 1:
wait_time += 10
logger.info(f"Waiting {wait_time} seconds before retrying...")
time.sleep(wait_time)
else:
logger.error(
f"Failed to retrieve data for feature '{f}' in country {country} after {retries} attempts."
)
if __name__ == "__main__":
if "snakemake" not in globals():
from _helpers import mock_snakemake
snakemake = mock_snakemake("retrieve_osm_data", country="BE")
configure_logging(snakemake)
set_scenario_config(snakemake)
# Retrieve the OSM data
country = snakemake.wildcards.country
output = snakemake.output
retrieve_osm_data(country, output)