diff --git a/.gitignore b/.gitignore index d3518d355a353013596f4afb7eb86a5575068b75..ba6095ae06c7157a6eeba9ba15dc707dfbcb97cd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ *.txt *.pdf cache -input \ No newline at end of file +input +plz/plz-5stellig.geojson diff --git a/get_coordinates_by_zipcode.py b/get_coordinates_by_zipcode.py new file mode 100644 index 0000000000000000000000000000000000000000..60081440664cbbc9a35ef42e80d8f5cd62451ca0 --- /dev/null +++ b/get_coordinates_by_zipcode.py @@ -0,0 +1,108 @@ +""" +For a given German Zipcode, returns the corresponding WKT Polygon or Multipolygon. +If pyperclip is installed, the WKT gets copied to the clipboard, +e.g. for RegionChooser or download_files_from_LGL_BW.py. + +Also accepts multiple Zipcodes, or Zipcode prefix. + + python get_coordinates_by_zipcode.py --help + usage: get_coordinates_by_zipcode.py [-h] PLZ [PLZ ...] + + Get WKT geometry for desired PLZs + + positional arguments: + PLZ desired PLZs + + options: + -h, --help show this help message and exit + +> python get_coordinates_by_zipcode.py 70174 +> python get_coordinates_by_zipcode.py 70567 70569 +> python get_coordinates_by_zipcode.py 70 +""" + +import argparse +import json +import re +from pathlib import Path +from shapely.geometry import shape +from shapely.ops import unary_union + +INPUT_FOLDER = Path('plz') +PLZ_FILENAME = 'plz-5stellig.geojson' +PLZ_SHAPES = INPUT_FOLDER / PLZ_FILENAME + + +def download_if_needed(): + if not PLZ_SHAPES.exists(): + from tqdm import tqdm + import requests + URL = "https://downloads.suche-postleitzahl.org/v2/public/" + PLZ_FILENAME + response = requests.get(URL, stream=True) + INPUT_FOLDER.mkdir(exist_ok=True) + with open(PLZ_SHAPES, "wb") as handle: + for data in tqdm(response.iter_content(chunk_size=1024), unit='kB'): + handle.write(data) + + +def parse_data(): + print("Parsing %s..." % PLZ_FILENAME) + download_if_needed() + try: + with open(PLZ_SHAPES) as f: + print(' Done') + return json.load(f) + except json.decoder.JSONDecodeError: + PLZ_SHAPES.unlink() + raise AttributeError(f"{PLZ_FILENAME} seems to be damaged. Removing it. Please try again!") + + +def get_plz(data, plz_patterns): + geometries = [] + for plz_pattern in plz_patterns: + found = False + for plz_geojson in data['features']: + if re.match(plz_pattern, plz_geojson['properties']['plz']): + found = True + + properties = plz_geojson['properties'] + + print('## %s' % properties['note']) + print('Population : %d' % properties['einwohner']) + print('Area : %.2f km²' % properties['qkm']) + # NOTE : Geometry can be either a polygon, + # a MultiPolygon : 98694 Ilmenau + # or a polygon with holes : 31860 Emmerthal + print('WKT Polygon : ') + + geometries.append(shape(plz_geojson['geometry'])) + + if not found: + raise AttributeError(f"Sorry, no information could be found for PLZ={plz_pattern}") + + merged = unary_union(geometries) + wkt_polygon = merged.simplify(0.0001).wkt + print(wkt_polygon) + try: + import pyperclip + pyperclip.copy(wkt_polygon) + print("WKT Polygon copied to clipboard.") + except ModuleNotFoundError: + pass + + print() + print("Done!") + return wkt_polygon + + +def main(plzs): + data = parse_data() + get_plz(data, plzs) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Get WKT geometry for desired PLZs') + parser.add_argument('plzs', metavar='PLZ', type=str, nargs='+', + help='desired PLZs') + args = parser.parse_args() + main(args.plzs) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..49ddecbfcd33182c7f7e44e15ad0dd19a681746f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pyproj +requests +pyperclip