diff --git a/download_files_from_LGL_BW.py b/download_files_from_LGL_BW.py
index 66faabca47b7c6124396ec75b0d8305532838120..6971dfc769a3f2484200a4157c5fa69137e3df3a 100644
--- a/download_files_from_LGL_BW.py
+++ b/download_files_from_LGL_BW.py
@@ -4,16 +4,23 @@ LoD2 CityGML tiles are available for whole Baden-Württemberg, from LGL.
 
 https://opengeodata.lgl-bw.de/#/(sidenav:product/12)
 
-This script downloads the requires tiles for given regions
-(as WKT strings, Zipcode or Zipcodes, in *REGIONS* variable), and extracts the region.
+This script downloads the required tiles for given regions
+(as WKT strings, Zipcode or Zipcodes), and extracts the region.
+
+Usage:
+    python download_files_from_LGL_BW.py StuttgartCenter "POLYGON((9.175287 48.780916, 9.185501 48.777522, 9.181467 48.773704, 9.174429 48.768472, 9.168807 48.773902, 9.175287 48.780916))"
+    python download_files_from_LGL_BW.py Freiburg "79098,79102"
+    python download_files_from_LGL_BW.py MyRegion "70567" --download-only
+    python download_files_from_LGL_BW.py CustomPath "POLYGON(...)" --simstadt-folder "/path/to/SimStadt"
 
 Required:
 * Python
 * pyproj project (https://pypi.org/project/pyproj/)
-* SimStadt installed on the Desktop (for RegionChooser)
+* SimStadt installed on the Desktop (for RegionChooser) if extracting regions
 
 Eric Duminil, 2025
 """
+import argparse
 from pathlib import Path
 from math import floor
 import subprocess
@@ -21,6 +28,8 @@ import re
 import urllib.request
 import time
 import zipfile
+import logging
+import sys
 
 from pyproj import CRS
 from pyproj import Transformer
@@ -29,29 +38,23 @@ from shapely import wkt
 from shapely.ops import transform
 from shapely.geometry import Point
 
-# TODO: Write tests
-# TODO: Use logging
-
 from get_coordinates_by_zipcode import get_coordinates_by_zipcode
 
-COORDINATES_REGEX = re.compile(r"(\-?\d+\.\d*) (\-?\d+\.\d*)")
-
-###### User input ##########
-# Values can be either a WKT POLYGON or MULTIPOLYGON, a Zipcode, or Zipcodes separated by a comma.
-REGIONS = {
-    "StuttgartCenter": "POLYGON((9.175287 48.780916, 9.185501 48.777522, 9.181467 48.773704, 9.174429 48.768472, 9.168807 48.773902, 9.175287 48.780916))",
-    # "Freiburg": "79098,79102",
-    # "AnotherRegion": "Another WKT Polygon...",
-    # "YetAnotherRegion": "Another ZIP code",
-}
-# Should RegionChooser extract the regions from multiple CityGMLs?
-EXTRACT_REGIONS = True
-############################
+# Setup logging
+logging.basicConfig(
+    level=logging.INFO,
+    format='%(asctime)s - %(levelname)s - %(message)s',
+    handlers=[
+        logging.StreamHandler(sys.stdout)
+    ]
+)
+logger = logging.getLogger(__name__)
 
+COORDINATES_REGEX = re.compile(r"(\-?\d+\.\d*) (\-?\d+\.\d*)")
 
 CITYGML_SERVER = "https://opengeodata.lgl-bw.de/data/lod2"
 RASTER = 2  # [km]
-KILOMETER = 1000 # [m]
+KILOMETER = 1000  # [m]
 BUNDESLAND = 'bw'
 
 # UTM32N, used in BW. https://epsg.io/32632
@@ -65,16 +68,15 @@ SCRIPT_DIR = Path(__file__).parent
 WAIT_BETWEEN_DOWNLOADS = 5  # [s] Be nice to LGL Server.
 GML_GLOB = "LoD2_*/LoD2_*.gml"
 
-if EXTRACT_REGIONS:
+
+def find_simstadt_folder():
+    """Find SimStadt installation on desktop"""
     try:
-        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}")
+        simstadt_folder = next(x for x in Path.home().glob('Desktop/SimStadt*_0.*/') if x.is_dir())
+        logger.info(f"RegionChooser has been found in {simstadt_folder}")
+        return simstadt_folder
     except StopIteration:
-        exit("No SimStadt installation found!"
-             "\nPlease copy a SimStadt installation to the desktop,"
-             "\nset EXTRACT_REGIONS to False,"
-             "\nor set SIMSTADT_FOLDER manually: SIMSTADT_FOLDER = Path('/path/to/SimStadt')"
-             )
+        return None
 
 
 def coordinates_to_grid(longitude: float, latitude: float) -> tuple[int, int]:
@@ -87,12 +89,12 @@ def coordinates_to_grid(longitude: float, latitude: float) -> tuple[int, int]:
     return (x + 1, y)
 
 
-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: str) -> tuple[int, int, int, int]:
     """Returns (x, y) of lower-left and bottom-right tiles, containing a given region."""
-    if 'POLYGON' not in wkt:
+    if 'POLYGON' not in wkt_str:
         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_str)
 
     lons = [float(lon) for lon in coordinates[::2]]
     lats = [float(lat) for lat in coordinates[1::2]]
@@ -100,8 +102,8 @@ def wkt_polygon_to_grid_coords(location_name: str, wkt: str) -> tuple[int, int,
     min_lon, max_lon = min(lons), max(lons)
     min_lat, max_lat = min(lats), max(lats)
 
-    print("%s (%.3f°N %.3f°E -> %.3f°N %.3f°E)" %
-          (location_name, max_lat, min_lon, min_lat, max_lon))
+    logger.info("%s (%.3f°N %.3f°E -> %.3f°N %.3f°E)" %
+                (location_name, max_lat, min_lon, min_lat, max_lon))
 
     x1, y1 = coordinates_to_grid(min_lon, min_lat)
     x2, y2 = coordinates_to_grid(max_lon, max_lat)
@@ -123,43 +125,44 @@ def download_whole_region(output_dir: Path, wkt_region: str, x1: int, x2: int, y
             citygml_url = f"{CITYGML_SERVER}/{citygml_zip}"
             local_zip = output_dir / citygml_zip
             if local_zip.exists():
-                print(f"  {local_zip.name} already in {output_dir.name}/")
+                logger.info(f"  {local_zip.name} already in {output_dir.name}/")
             else:
-                print(f"  Download {citygml_zip} to {output_dir.name}/ ", end='')
+                logger.info(f"  Download {citygml_zip} to {output_dir.name}/ ")
                 try:
                     urllib.request.urlretrieve(citygml_url, local_zip)
+                    logger.info("✅ Download successful")
                 except urllib.error.HTTPError as e:
-                    print(f"❌ {e}")
+                    logger.error(f"❌ {e}")
                     continue
                 finally:
                     time.sleep(WAIT_BETWEEN_DOWNLOADS)
-                print("✅")
 
-                print(f"    Extract {citygml_zip} to {output_dir.name}/ ", end='')
-                print("✅")
-                print("")
+                logger.info(f"    Extract {citygml_zip} to {output_dir.name}/ ")
                 with zipfile.ZipFile(local_zip, "r") as zip_ref:
                     zip_ref.extractall(output_dir)
+                logger.info("✅ Extraction successful")
 
 
-def extract_region(output_dir: Path, location_name: str, wkt: str) -> None:
+def extract_region(output_dir: Path, location_name: str, wkt_str: str, simstadt_folder: Path) -> None:
     """Uses RegionChooser to extract a given region from all the CityGML files found in subfolder."""
     output_file = output_dir / (location_name + '.gml')
     if output_file.exists():
-        print(f"  {output_file} already exists. Not extracting.")
+        logger.info(f"  {output_file} already exists. Not extracting.")
         return
-    region_chooser_libs = Path(SIMSTADT_FOLDER).expanduser() / 'lib/*'
+
+    region_chooser_libs = simstadt_folder / 'lib/*'
     gml_inputs = list(output_dir.glob(GML_GLOB))
     if len(gml_inputs) == 0:
-        print("Error: No CityGML found. At least part of the region should be in Baden-Württemberg!")
+        logger.error(
+            "Error: No CityGML found. At least part of the region should be in Baden-Württemberg!")
         return
 
     params_path = output_dir / 'params.txt'
     wkt_path = output_dir / 'region.wkt'
 
-    local_wkt = convert_wkt_to_local(wkt)
+    local_wkt = convert_wkt_to_local(wkt_str)
 
-    print(f"  Extracting {output_file}.")
+    logger.info(f"  Extracting {output_file}.")
     with open(wkt_path, 'w') as f:
         f.write(local_wkt)
 
@@ -181,11 +184,11 @@ def extract_region(output_dir: Path, location_name: str, wkt: str) -> None:
                             capture_output=True,
                             check=False
                             )
-    if (result.stderr):
-        print(result.stderr)
+    if result.stderr:
+        logger.error(result.stderr)
     if result.returncode != 0:
         raise ValueError(f"RegionChooser failed with code {result.returncode}")
-    print("  DONE!")
+    logger.info("  DONE!")
 
 
 def get_wkt(wkt_or_zipcode: str) -> str:
@@ -200,30 +203,88 @@ def get_wkt(wkt_or_zipcode: str) -> str:
 
     return get_coordinates_by_zipcode(wkt_or_zipcode.split(','))
 
-def main(regions: dict[str, str]) -> None:
-    """Downloads ZIP files, extracts CityGML files, and selects desired region."""
-    for location_name, wkt_or_zipcode in regions.items():
-        if ' ' in location_name:
-            raise ValueError("Location name should not contain spaces: 'Some City' -> 'SomeCity'")
-        output_dir = SCRIPT_DIR / (location_name + '.proj')
-        output_dir.mkdir(parents=True, exist_ok=True)
-        wkt = get_wkt(wkt_or_zipcode)
-        x1, x2, y1, y2 = wkt_polygon_to_grid_coords(location_name, wkt)
-        download_whole_region(output_dir, wkt, x1, x2, y1, y2)
-        if EXTRACT_REGIONS:
-            extract_region(output_dir, location_name, wkt)
-        print()
-
 
 def convert_coordinates(match):
+    """Convert WGS84 coordinates to UTM32N"""
     longitude, latitude = match.groups()
     x, y = TO_LOCAL_CRS.transform(longitude, latitude)
     return f"{x} {y}"
 
 
-def convert_wkt_to_local(wkt):
-    return COORDINATES_REGEX.sub(convert_coordinates, wkt)
+def convert_wkt_to_local(wkt_str):
+    """Convert WKT from WGS84 to UTM32N"""
+    return COORDINATES_REGEX.sub(convert_coordinates, wkt_str)
+
+
+def parse_arguments():
+    """Parse command line arguments"""
+    parser = argparse.ArgumentParser(
+        description="Download LoD2 CityGML tiles from LGL Baden-Württemberg and extract specific regions",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        epilog="""
+Examples:
+  python download_files_from_LGL_BW.py StuttgartCenter "POLYGON((9.175287 48.780916, 9.185501 48.777522, 9.181467 48.773704, 9.174429 48.768472, 9.168807 48.773902, 9.175287 48.780916))"
+  python download_files_from_LGL_BW.py Freiburg "79098,79102"
+  python download_files_from_LGL_BW.py MyRegion "70567" --download-only
+"""
+    )
+
+    parser.add_argument('name', type=str,
+                        help='Name of the region (no spaces allowed). Output files will use this name.')
+
+    parser.add_argument('region', type=str,
+                        help='Region specification as WKT POLYGON/MULTIPOLYGON string or zipcode(s) (comma-separated).')
+
+    parser.add_argument('--download-only', action='store_true',
+                        help='Only download files without extracting the region (default: False).')
+
+    parser.add_argument('--simstadt-folder', type=Path, default=None,
+                        help='Path to SimStadt installation folder. By default, tries to find it on the Desktop.')
+
+    return parser.parse_args()
+
+
+def main():
+    """Main function to process arguments and run the download/extraction"""
+    args = parse_arguments()
+
+    location_name = args.name
+    wkt_or_zipcode = args.region
+    download_only = args.download_only
+    simstadt_folder = args.simstadt_folder
+
+    # Validate location name
+    if ' ' in location_name:
+        raise ValueError("Location name should not contain spaces: 'Some City' -> 'SomeCity'")
+
+    # Create output directory
+    output_dir = SCRIPT_DIR / (location_name + '.proj')
+    output_dir.mkdir(parents=True, exist_ok=True)
+
+    # Get WKT string
+    wkt_str = get_wkt(wkt_or_zipcode)
+
+    # Get grid coordinates
+    x1, x2, y1, y2 = wkt_polygon_to_grid_coords(location_name, wkt_str)
+
+    # Download region
+    download_whole_region(output_dir, wkt_str, x1, x2, y1, y2)
+
+    # Extract region if not download-only
+    if not download_only:
+        if not simstadt_folder:
+            simstadt_folder = find_simstadt_folder()
+            if not simstadt_folder:
+                logger.error(
+                    "No SimStadt installation found! Please provide --simstadt-folder or use --download-only.")
+                return
+
+        extract_region(output_dir, location_name, wkt_str, simstadt_folder)
+    else:
+        logger.info("Download-only mode: Skipping region extraction.")
+
+    logger.info(f"Processing of {location_name} complete!")
 
 
 if __name__ == '__main__':
-    main(REGIONS)
+    main()