tyler 0.4.1

Create tiles from 3D city objects encoded as CityJSONFeatures.
"""Copyright 2023 Balázs Dukai, Ravi Peters"""
import json
import argparse
from sys import argv
from pathlib import Path
from copy import deepcopy

from cjio.cityjson import CityJSON


def merge(cityjson_path, path_features_input_file: Path):
    lcount = 1
    # -- read first line
    with cityjson_path.open("r") as fo:
        j1 = json.load(fo)
    cm = CityJSON(j=j1)
    if "CityObjects" not in cm.j:
        cm.j["CityObjects"] = {}
    if "vertices" not in cm.j:
        cm.j["vertices"] = []
    with path_features_input_file.open("r") as input_file:
        for p in input_file:
            path = Path(p.strip("\n")).resolve()
            if path.suffix == ".jsonl":
                with path.open("r") as fo:
                    j1 = json.load(fo)
                if not ("type" in j1 and j1["type"] == 'CityJSONFeature'):
                    raise IOError(
                        "Line {} is not of type 'CityJSONFeature'.".format(lcount))
                cm.add_cityjsonfeature(j1)
            else:
                print(f"Not a .jsonl file {path}, suffix: {path.suffix}")
    path_features_input_file.unlink()
    return cm


parser=argparse.ArgumentParser()
parser.add_argument("--output_format", help="Format to convert to")
parser.add_argument("--output_file", help="Where to save the output")
parser.add_argument("--path_metadata", help="The main .city.json file with the transformation properties")
parser.add_argument("--path_features_input_file", help="File with the list of feature paths")
parser.add_argument("--min_x", help="Bounding box minimum x coordinate")
parser.add_argument("--min_y", help="Bounding box minimum y coordinate")
parser.add_argument("--min_z", help="Bounding box minimum z coordinate")
parser.add_argument("--max_x", help="Bounding box maximum x coordinate")
parser.add_argument("--max_y", help="Bounding box maximum y coordinate")
parser.add_argument("--max_z", help="Bounding box maximum z coordinate")
parser.add_argument("--cotypes", help="Comma separated list of CityObject types to include in the tile")
parser.add_argument("--metadata_class", help="The name of the metadata class to create (for EXT_structural_metadata).")
parser.add_argument("--attribute_spec", help="The CityObject attribute to include in the output.")
parser.add_argument("--geometric_error")

if __name__ == "__main__":
    args = parser.parse_args()
    # format to convert to
    supported_formats = ["cityjson", "3dtiles"]
    output_format = args.output_format
    if output_format not in supported_formats:
        raise ValueError(f"Output format {output_format} is not supported. Supported formats: {supported_formats}")
    # where to save the output
    output_file = Path(args.output_file)
    output_file.parent.mkdir(parents=True, exist_ok=True)
    # the main .city.json file with the transformation properties
    cityjson_path = Path(args.path_metadata).resolve()
    # a file with the list of feature paths
    path_features_input_file = Path(args.path_features_input_file).resolve()
    # tile bbox (used in the terrain splitter)
    bbox = [args.min_x, args.min_y, args.min_z, args.max_x, args.max_y, args.max_z]
    # CityObject types to use
    cotypes = args.cotypes.split(",")

    cm = merge(cityjson_path, path_features_input_file)
    nr_co_after_merge = len(cm.j["CityObjects"])
    types_after_merge = cm.get_info()
    if output_format == "cityjson":
        with output_file.open("w") as fo:
            fo.write(json.dumps(cm.j, separators=(',', ':')))
    elif output_format == "3dtiles":
        cm.reproject(4978)
        cm = cm.get_subset_cotype(cotypes)
        nr_co_after_subset = len(cm.j["CityObjects"])
        if nr_co_after_subset == 0:
            if nr_co_after_merge > 0:
                print(f"CityModel {output_file} does not contain any {cotypes} objects, but it did contain {nr_co_after_merge} objects of type {types_after_merge}.")
            else:
                print(
                    f"CityModel {output_file} does not contain any {cotypes} objects and it did not contain any objects at all after merge either.")
        glb = cm.export2glb(do_triangulate=False)
        glb.seek(0)
        with output_file.open("wb") as bo:
            bo.write(glb.getvalue())

        # # TODO: we are cheating here, because we know that the data has 3 LoD-s and we
        # #  also hardcoded the same into Tileset.from() in tyler. Same for the tile/file
        # #  names.
        # # lod_file_names = [("1.2", ""), ("1.3", "-0"), ("2.2", "-0-0")] # grid with multi-lod
        # # lod_file_names = [("2.2", "-0-0"),] # for grid with single lod
        # lod_file_names = [("2.2", ""),] # for quadtree
        # for lod, suffix in lod_file_names:
        #     cm_copy = deepcopy(cm)
        #     cm_copy.filter_lod(lod)
        #     glb = cm_copy.export2glb(do_triangulate=False)
        #     glb.seek(0)
        #     output_file_tile = (output_file.parent / (output_file.stem + suffix)).with_suffix(".glb")
        #     with output_file_tile.open("wb") as bo:
        #         bo.write(glb.getvalue())
    else:
        raise ValueError("unsupported format and we should have reached this branch anyway")

    # /home/bdukai/software/cjio/venv_38/lib/python3.8/site-packages/pyproj/transformer.py:197: UserWarning: Best transformation is not available due to missing Grid(short_name=nl_nsgi_nlgeo2018.tif, full_name=, package_name=, url=https://cdn.proj.org/nl_nsgi_nlgeo2018.tif, direct_download=True, open_license=True, available=False)