Commit b951138d authored by Eric Duminil's avatar Eric Duminil
Browse files

Format

parent ed51d96f
...@@ -16,27 +16,24 @@ Data license: https://www.govdata.de/dl-de/by-2-0 ...@@ -16,27 +16,24 @@ Data license: https://www.govdata.de/dl-de/by-2-0
Eric Duminil, 2025 Eric Duminil, 2025
""" """
from pathlib import Path
from math import floor
import subprocess
import re import re
import urllib.request import subprocess
import time import time
import urllib.request
import zipfile import zipfile
from math import floor
from pathlib import Path
from pyproj import CRS from pyproj import CRS, Transformer
from pyproj import Transformer
from shapely import wkt from shapely import wkt
from shapely.ops import transform
from shapely.geometry import Point from shapely.geometry import Point
from shapely.ops import transform
# TODO: Write tests from german_laender_wkt import german_laender
# TODO: Use logging
from get_coordinates_by_zipcode import get_coordinates_by_zipcode from get_coordinates_by_zipcode import get_coordinates_by_zipcode
from german_laender_wkt import german_laender # TODO: Write tests
# TODO: Use logging
COORDINATES_REGEX = re.compile(r"(\-?\d+\.\d*) (\-?\d+\.\d*)") COORDINATES_REGEX = re.compile(r"(\-?\d+\.\d*) (\-?\d+\.\d*)")
...@@ -44,7 +41,7 @@ COORDINATES_REGEX = re.compile(r"(\-?\d+\.\d*) (\-?\d+\.\d*)") ...@@ -44,7 +41,7 @@ COORDINATES_REGEX = re.compile(r"(\-?\d+\.\d*) (\-?\d+\.\d*)")
###### User input ########## ###### User input ##########
# Values can be either a WKT POLYGON or MULTIPOLYGON, a Zipcode, or Zipcodes separated by a comma. # Values can be either a WKT POLYGON or MULTIPOLYGON, a Zipcode, or Zipcodes separated by a comma.
REGIONS = { REGIONS = {
"BW": german_laender['Baden-Württemberg'], "BW": "POLYGON((9.211240 48.769258, 9.209040 48.770220, 9.208547 48.770708, 9.207206 48.771295, 9.206390 48.771521, 9.204834 48.771528, 9.204405 48.771775, 9.204019 48.772249, 9.203708 48.772907, 9.203981 48.774104, 9.203949 48.774485, 9.204378 48.774994, 9.206169 48.774994, 9.207478 48.775843, 9.208637 48.775899, 9.213776 48.776981, 9.215085 48.775610, 9.216072 48.773736, 9.217350 48.770910, 9.217801 48.769326, 9.214365 48.767755, 9.211240 48.769258))"
# "Freiburg": "79098,79102", # "Freiburg": "79098,79102",
# "AnotherRegion": "Another WKT Polygon...", # "AnotherRegion": "Another WKT Polygon...",
# "YetAnotherRegion": "Another ZIP code", # "YetAnotherRegion": "Another ZIP code",
...@@ -56,13 +53,11 @@ EXTRACT_REGIONS = True ...@@ -56,13 +53,11 @@ EXTRACT_REGIONS = True
CITYGML_SERVER = "https://opengeodata.lgl-bw.de/data/lod2" CITYGML_SERVER = "https://opengeodata.lgl-bw.de/data/lod2"
RASTER = 2 # [km] RASTER = 2 # [km]
KILOMETER = 1000 # [m] KILOMETER = 1000 # [m]
BUNDESLAND = 'bw' BUNDESLAND = "bw"
# UTM32N, used in BW. https://epsg.io/32632 # UTM32N, used in BW. https://epsg.io/32632
TO_LOCAL_CRS = Transformer.from_crs(CRS.from_epsg(4326), TO_LOCAL_CRS = Transformer.from_crs(CRS.from_epsg(4326), CRS.from_epsg(32632), always_xy=True)
CRS.from_epsg(32632),
always_xy=True)
UTM = 32 UTM = 32
...@@ -72,14 +67,15 @@ GML_GLOB = "LoD2_*/LoD2_*.gml" ...@@ -72,14 +67,15 @@ GML_GLOB = "LoD2_*/LoD2_*.gml"
if EXTRACT_REGIONS: if EXTRACT_REGIONS:
try: try:
SIMSTADT_FOLDER = next(x for x in Path.home().glob('Desktop/SimStadt*_0.*/') if x.is_dir()) SIMSTADT_FOLDER = next(x for x in Path.home().glob("Desktop/SimStadt*_0.*/") if x.is_dir())
print(f"RegionChooser has been found in {SIMSTADT_FOLDER}") print(f"RegionChooser has been found in {SIMSTADT_FOLDER}")
except StopIteration: except StopIteration:
exit("No SimStadt installation found!" exit(
"\nPlease copy a SimStadt installation to the desktop," "No SimStadt installation found!"
"\nset EXTRACT_REGIONS to False," "\nPlease copy a SimStadt installation to the desktop,"
"\nor set SIMSTADT_FOLDER manually: SIMSTADT_FOLDER = Path('/path/to/SimStadt')" "\nset EXTRACT_REGIONS to False,"
) "\nor set SIMSTADT_FOLDER manually: SIMSTADT_FOLDER = Path('/path/to/SimStadt')"
)
def coordinates_to_grid(longitude: float, latitude: float) -> tuple[int, int]: def coordinates_to_grid(longitude: float, latitude: float) -> tuple[int, int]:
...@@ -94,10 +90,10 @@ def coordinates_to_grid(longitude: float, latitude: float) -> tuple[int, int]: ...@@ -94,10 +90,10 @@ def coordinates_to_grid(longitude: float, latitude: float) -> tuple[int, int]:
def wkt_polygon_to_grid_coords(location_name: str, wkt: str) -> tuple[int, int, int, int]: def wkt_polygon_to_grid_coords(location_name: str, wkt: str) -> tuple[int, int, int, int]:
"""Returns (x, y) of lower-left and bottom-right tiles, containing a given region.""" """Returns (x, y) of lower-left and bottom-right tiles, containing a given region."""
if 'POLYGON' not in wkt: if "POLYGON" not in wkt:
raise ValueError(f"wkt for {location_name} should be a WKT POLYGON or MULTIPOLYGON") raise ValueError(f"wkt for {location_name} should be a WKT POLYGON or MULTIPOLYGON")
coordinates = re.findall(r'\-?\d+\.\d+', wkt) coordinates = re.findall(r"\-?\d+\.\d+", wkt)
lons = [float(lon) for lon in coordinates[::2]] lons = [float(lon) for lon in coordinates[::2]]
lats = [float(lat) for lat in coordinates[1::2]] lats = [float(lat) for lat in coordinates[1::2]]
...@@ -105,8 +101,7 @@ def wkt_polygon_to_grid_coords(location_name: str, wkt: str) -> tuple[int, int, ...@@ -105,8 +101,7 @@ def wkt_polygon_to_grid_coords(location_name: str, wkt: str) -> tuple[int, int,
min_lon, max_lon = min(lons), max(lons) min_lon, max_lon = min(lons), max(lons)
min_lat, max_lat = min(lats), max(lats) min_lat, max_lat = min(lats), max(lats)
print("%s (%.3f°N %.3f°E -> %.3f°N %.3f°E)" % print("%s (%.3f°N %.3f°E -> %.3f°N %.3f°E)" % (location_name, max_lat, min_lon, min_lat, max_lon))
(location_name, max_lat, min_lon, min_lat, max_lon))
x1, y1 = coordinates_to_grid(min_lon, min_lat) x1, y1 = coordinates_to_grid(min_lon, min_lat)
x2, y2 = coordinates_to_grid(max_lon, max_lat) x2, y2 = coordinates_to_grid(max_lon, max_lat)
...@@ -131,7 +126,7 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y ...@@ -131,7 +126,7 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y
if local_zip.exists(): if local_zip.exists():
print(f" {local_zip.name} already in {output_dir.name}/") print(f" {local_zip.name} already in {output_dir.name}/")
else: else:
print(f" Download {citygml_zip} to {output_dir.name}/ ", end='') print(f" Download {citygml_zip} to {output_dir.name}/ ", end="")
try: try:
urllib.request.urlretrieve(citygml_url, local_zip) urllib.request.urlretrieve(citygml_url, local_zip)
except urllib.error.HTTPError as e: except urllib.error.HTTPError as e:
...@@ -141,7 +136,7 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y ...@@ -141,7 +136,7 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y
time.sleep(WAIT_BETWEEN_DOWNLOADS) time.sleep(WAIT_BETWEEN_DOWNLOADS)
print("✅") print("✅")
print(f" Extract {citygml_zip} to {output_dir.name}/ ", end='') print(f" Extract {citygml_zip} to {output_dir.name}/ ", end="")
print("✅") print("✅")
print("") print("")
with zipfile.ZipFile(local_zip, "r") as zip_ref: with zipfile.ZipFile(local_zip, "r") as zip_ref:
...@@ -150,44 +145,48 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y ...@@ -150,44 +145,48 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y
def extract_region(output_dir: Path, location_name: str, wkt: str) -> None: def extract_region(output_dir: Path, location_name: str, wkt: str) -> None:
"""Uses RegionChooser to extract a given region from all the CityGML files found in subfolder.""" """Uses RegionChooser to extract a given region from all the CityGML files found in subfolder."""
output_file = output_dir / (location_name + '.gml') output_file = output_dir / (location_name + ".gml")
if output_file.exists(): if output_file.exists():
print(f" {output_file} already exists. Not extracting.") print(f" {output_file} already exists. Not extracting.")
return return
region_chooser_libs = Path(SIMSTADT_FOLDER).expanduser() / 'lib/*' region_chooser_libs = Path(SIMSTADT_FOLDER).expanduser() / "lib/*"
gml_inputs = list(output_dir.glob(GML_GLOB)) gml_inputs = list(output_dir.glob(GML_GLOB))
if len(gml_inputs) == 0: if len(gml_inputs) == 0:
print("Error: No CityGML found. At least part of the region should be in Baden-Württemberg!") print("Error: No CityGML found. At least part of the region should be in Baden-Württemberg!")
return return
params_path = output_dir / 'params.txt' params_path = output_dir / "params.txt"
wkt_path = output_dir / 'region.wkt' wkt_path = output_dir / "region.wkt"
local_wkt = convert_wkt_to_local(wkt) local_wkt = convert_wkt_to_local(wkt)
print(f" Extracting {output_file}.") print(f" Extracting {output_file}.")
with open(wkt_path, 'w') as f: with open(wkt_path, "w") as f:
f.write(local_wkt) f.write(local_wkt)
with open(params_path, 'w') as f: with open(params_path, "w") as f:
f.write("--input\n") f.write("--input\n")
f.write(','.join(f"{gml.as_posix()}" for gml in gml_inputs)) f.write(",".join(f"{gml.as_posix()}" for gml in gml_inputs))
f.write("\n") f.write("\n")
f.write("--output\n") f.write("--output\n")
f.write(f'"{output_file.as_posix()}"\n') f.write(f'"{output_file.as_posix()}"\n')
f.write('--local\n') f.write("--local\n")
f.write("--wkt\n") f.write("--wkt\n")
f.write(f'"{wkt_path.as_posix()}"\n') f.write(f'"{wkt_path.as_posix()}"\n')
result = subprocess.run(['java', '-classpath', f'{region_chooser_libs}', result = subprocess.run(
'eu.simstadt.regionchooser.RegionChooserCLI', [
f'@{params_path}' "java",
], "-classpath",
text=True, f"{region_chooser_libs}",
capture_output=True, "eu.simstadt.regionchooser.RegionChooserCLI",
check=False f"@{params_path}",
) ],
if (result.stderr): text=True,
capture_output=True,
check=False,
)
if result.stderr:
print(result.stderr) print(result.stderr)
if result.returncode != 0: if result.returncode != 0:
raise ValueError(f"RegionChooser failed with code {result.returncode}") raise ValueError(f"RegionChooser failed with code {result.returncode}")
...@@ -201,17 +200,18 @@ def get_wkt(wkt_or_zipcode: str) -> str: ...@@ -201,17 +200,18 @@ def get_wkt(wkt_or_zipcode: str) -> str:
"70567" "70567"
"70567,70569" "70567,70569"
""" """
if 'POLYGON' in wkt_or_zipcode: if "POLYGON" in wkt_or_zipcode:
return wkt_or_zipcode return wkt_or_zipcode
return get_coordinates_by_zipcode(wkt_or_zipcode.split(',')) return get_coordinates_by_zipcode(wkt_or_zipcode.split(","))
def main(regions: dict[str, str]) -> None: def main(regions: dict[str, str]) -> None:
"""Downloads ZIP files, extracts CityGML files, and selects desired region.""" """Downloads ZIP files, extracts CityGML files, and selects desired region."""
for location_name, wkt_or_zipcode in regions.items(): for location_name, wkt_or_zipcode in regions.items():
if ' ' in location_name: if " " in location_name:
raise ValueError("Location name should not contain spaces: 'Some City' -> 'SomeCity'") raise ValueError("Location name should not contain spaces: 'Some City' -> 'SomeCity'")
output_dir = SCRIPT_DIR / (location_name + '.proj') output_dir = SCRIPT_DIR / (location_name + ".proj")
output_dir.mkdir(parents=True, exist_ok=True) output_dir.mkdir(parents=True, exist_ok=True)
wkt = get_wkt(wkt_or_zipcode) wkt = get_wkt(wkt_or_zipcode)
x1, x2, y1, y2 = wkt_polygon_to_grid_coords(location_name, wkt) x1, x2, y1, y2 = wkt_polygon_to_grid_coords(location_name, wkt)
...@@ -231,5 +231,5 @@ def convert_wkt_to_local(wkt): ...@@ -231,5 +231,5 @@ def convert_wkt_to_local(wkt):
return COORDINATES_REGEX.sub(convert_coordinates, wkt) return COORDINATES_REGEX.sub(convert_coordinates, wkt)
if __name__ == '__main__': if __name__ == "__main__":
main(REGIONS) main(REGIONS)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment