Files
GeoData/geodata_to_unity.py

216 lines
7.8 KiB
Python

#!/usr/bin/env python3
"""CLI entrypoint to export GeoData assets for Unity."""
from __future__ import annotations
import argparse
import os
import sys
from typing import Iterable
from geodata_download import run_download
from geodata_pipeline.config import Config, DEFAULT_CONFIG_PATH, ensure_default_config
from geodata_pipeline.buildings import export_buildings
from geodata_pipeline.buildings_enhanced import export_buildings_enhanced
from geodata_pipeline.heightmaps import export_heightmaps
from geodata_pipeline.heightmaps_enhanced import export_heightmaps_enhanced
from geodata_pipeline.lpolpg_split import split_lpolpg
from geodata_pipeline.orthophotos import export_orthophotos
from geodata_pipeline.setup_helpers import ensure_directories, materialize_archives
from geodata_pipeline.street_furniture import export_street_furniture
from geodata_pipeline.trees import export_trees
from geodata_pipeline.trees_enhanced import export_trees_enhanced
def parse_args(argv: Iterable[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Export GeoData assets for Unity.")
parser.add_argument(
"--config",
default=DEFAULT_CONFIG_PATH,
help="Path to config TOML (created on --setup if missing).",
)
parser.add_argument(
"--export",
choices=[
"heightmap", "textures", "buildings", "trees", "all",
"heightmap-enhanced", "buildings-enhanced", "trees-enhanced",
"street-furniture", "all-enhanced"
],
default=None,
help="Which assets to export. Enhanced options use point cloud data.",
)
parser.add_argument("--raw-dgm1-path", dest="raw_dgm1_path", help="Override raw DGM1 directory.")
parser.add_argument("--raw-dop20-path", dest="raw_dop20_path", help="Override raw DOP20 JP2 directory.")
parser.add_argument(
"--build-from-archive",
action="store_true",
help="Populate raw inputs from archives (unzips zips, leaves dop20 filelist in archive).",
)
parser.add_argument(
"--setup",
action="store_true",
help="Create default directory structure and config if missing; does not export.",
)
parser.add_argument(
"--force-vrt",
action="store_true",
help="Rebuild VRTs even if present (useful after moving raw data).",
)
parser.add_argument(
"--download",
action="store_true",
help="Download raw datasets before export using geodata_download.toml.",
)
parser.add_argument(
"--download-config",
default="geodata_download.toml",
help="Path to download config TOML.",
)
parser.add_argument(
"--download-datasets",
help="Comma-separated dataset keys to download (default: enabled datasets).",
)
parser.add_argument(
"--download-start",
nargs=2,
type=int,
metavar=("X", "Y"),
help="Override tile range start for download (x y).",
)
parser.add_argument(
"--download-end",
nargs=2,
type=int,
metavar=("X", "Y"),
help="Override tile range end for download (x y).",
)
parser.add_argument(
"--clean-downloads",
action="store_true",
help="Delete selected dataset folders before downloading.",
)
parser.add_argument(
"--download-ca-bundle",
help="Override CA bundle path for downloads.",
)
parser.add_argument(
"--split-lpolpg",
action="store_true",
help="Split combined lpolpg LAZ into LPG/LPO outputs.",
)
parser.add_argument(
"--split-lpolpg-formats",
default="laz,xyz",
help="Comma-separated output formats for lpolpg split (laz,xyz).",
)
parser.add_argument(
"--split-lpolpg-ground-classes",
default="2",
help="Comma-separated LAS classification codes treated as ground.",
)
parser.add_argument(
"--split-lpolpg-overwrite",
action="store_true",
help="Overwrite existing LPG/LPO outputs when splitting lpolpg.",
)
return parser.parse_args(argv)
def load_config(args: argparse.Namespace) -> Config:
if args.setup:
cfg = Config.default()
cfg.save(args.config)
return cfg.with_overrides(raw_dgm1_path=args.raw_dgm1_path, raw_dop20_path=args.raw_dop20_path)
if os.path.exists(args.config):
cfg = Config.load(args.config)
else:
cfg = Config.default()
cfg.save(args.config)
return cfg.with_overrides(raw_dgm1_path=args.raw_dgm1_path, raw_dop20_path=args.raw_dop20_path)
def main(argv: Iterable[str] | None = None) -> int:
args = parse_args(argv)
cfg = load_config(args)
target_export = None
if args.export is not None or not (args.download or args.split_lpolpg):
target_export = args.export or "all"
if args.setup:
ensure_directories(cfg)
print(f"Directories ensured. Config at {args.config}.")
if args.build_from_archive:
materialize_archives(cfg)
if args.export is None and not args.download:
return 0
if args.build_from_archive and not args.setup:
materialize_archives(cfg)
if args.download:
datasets = (
[name.strip() for name in args.download_datasets.split(",")]
if args.download_datasets
else None
)
start = tuple(args.download_start) if args.download_start else None
end = tuple(args.download_end) if args.download_end else None
if (start is None) != (end is None):
raise SystemExit("--download-start and --download-end must be provided together.")
download_exit = run_download(
config_path=args.download_config,
requested_datasets=datasets,
start_override=start,
end_override=end,
clean_downloads=args.clean_downloads,
ca_bundle_override=args.download_ca_bundle,
)
if download_exit != 0:
return download_exit
if args.split_lpolpg:
formats = [fmt.strip() for fmt in args.split_lpolpg_formats.split(",") if fmt.strip()]
ground = [int(val) for val in args.split_lpolpg_ground_classes.split(",") if val.strip()]
split_exit = split_lpolpg(
cfg,
formats=formats,
ground_classes=ground,
overwrite=args.split_lpolpg_overwrite,
)
if split_exit != 0:
return split_exit
if target_export is None:
return 0
exit_codes = []
# Standard exports
if target_export in ("heightmap", "all"):
exit_codes.append(export_heightmaps(cfg, force_vrt=args.force_vrt))
if target_export in ("textures", "all"):
exit_codes.append(export_orthophotos(cfg, force_vrt=args.force_vrt))
if target_export in ("buildings", "all"):
exit_codes.append(export_buildings(cfg))
if target_export in ("trees", "all"):
exit_codes.append(export_trees(cfg, force_vrt=args.force_vrt))
# Enhanced exports (use point cloud data)
# Order matters: heightmap-enhanced creates tile_index.csv needed by others
# street-furniture must run before trees-enhanced (for exclusion mask)
if target_export in ("heightmap-enhanced", "all-enhanced"):
exit_codes.append(export_heightmaps_enhanced(cfg))
if target_export in ("all-enhanced",):
exit_codes.append(export_orthophotos(cfg, force_vrt=args.force_vrt))
if target_export in ("street-furniture", "all-enhanced"):
exit_codes.append(export_street_furniture(cfg))
if target_export in ("buildings-enhanced", "all-enhanced"):
exit_codes.append(export_buildings_enhanced(cfg))
if target_export in ("trees-enhanced", "all-enhanced"):
exit_codes.append(export_trees_enhanced(cfg))
return max(exit_codes) if exit_codes else 0
if __name__ == "__main__":
sys.exit(main())