# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + [markdown] pycharm={"name": "#%% md\n"}
# # Creating city models and objects
#
# In this tutorial we explore how to create new city models with using `cjio`'s
#  API.

# + pycharm={"is_executing": false, "name": "#%%\n"}
from pathlib import Path

from cjio import cityjson
from cjio.models import CityObject, Geometry

# + [markdown] pycharm={"name": "#%% md\n"}
# Set up paths for the tutorial.
#     

# + pycharm={"is_executing": false, "name": "#%%\n"}
package_dir = Path(__name__).resolve().parent.parent.parent
schema_dir = package_dir / 'cjio' / 'schemas'/ '1.0.0'
data_dir = package_dir / 'tests' / 'data'

# + [markdown] pycharm={"name": "#%% md\n"}
# ## Creating a single CityObject
#
# We are building a single CityObject of type *Building*. This building has an 
# LoD2 geometry, thus it has Semantic Surfaces. The geometric shape of the 
# building is a simple cube (size 10x10x10), which is sufficient for this 
# demonstration.
#
# The idea is that we create empty containers for the CityModel, CityObjects and
# Geometries, then fill those up and add to the CityModel.

# + [markdown] pycharm={"name": "#%% md\n"}
# We create an empty CityModel

# + pycharm={"is_executing": false, "name": "#%%\n"}
cm = cityjson.CityJSON()
print(cm)

# + [markdown] pycharm={"name": "#%% md\n"}
# An empty CityObject. Note that the ID is required.

# + pycharm={"is_executing": false, "name": "#%%\n"}
co = CityObject(
    id='1'
)

# + [markdown] pycharm={"name": "#%% md\n"}
# We can also add attributes

# + pycharm={"is_executing": false, "name": "#%%\n"}
co_attrs = {
    'some_attribute': 42,
    'other_attribute': 'bla bla'
}
co.attributes = co_attrs

# + [markdown] pycharm={"name": "#%% md\n"}
# Let's see what do we have

# + pycharm={"is_executing": false, "name": "#%%\n"}
print(co)

# + [markdown] pycharm={"name": "#%% md\n"}
# Instantiate a Geometry without boundaries and semantics

# + pycharm={"is_executing": false, "name": "#%%\n"}
geom = Geometry(type='Solid', lod=2)

# + [markdown] pycharm={"name": "#%% md\n"}
# We build the boundary Solid of the cube
# The surfaces are in this order: WallSurface, WallSurface, WallSurface, WallSurface, GroundSurface, RoofSurface

# + pycharm={"is_executing": false, "name": "#%%\n"}
bdry = [
    [[(0.0, 0.0, 0.0), (10.0, 0.0, 0.0), (10.0, 0.0, 10.0), (0.0, 0.0, 10.0)]],
    [[(10.0, 0.0, 0.0), (10.0, 10.0, 0.0), (10.0, 10.0, 10.0), (10.0, 0.0, 10.0)]],
    [[(10.0, 10.0, 0.0), (0.0, 10.0, 0.0), (0.0, 10.0, 10.0), (10.0, 10.0, 10.0)]],
    [[(0.0, 10.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 10.0), (0.0, 10.0, 10.0)]],
    [[(0.0, 0.0, 0.0), (0.0, 10.0, 0.0), (10.0, 10.0, 0.0), (10.0, 0.0, 0.0)]],
    [[(10.0, 0.0, 10.0), (10.0, 10.0, 10.0), (0.0, 10.0, 10.0), (0.0, 0.0, 10.0)]]
]

# + [markdown] pycharm={"name": "#%% md\n"}
# Add the boundary to the Geometry

# + pycharm={"is_executing": false, "name": "#%%\n"}
geom.boundaries.append(bdry)

# + [markdown] pycharm={"name": "#%% md\n"}
# We build the SemanticSurfaces for the boundary. The `surfaces` attribute must
# contain at least the `surface_idx` and `type` keys, optionally `attributes`.
# We have three semantic surface types, WallSurface, GroundSurface, RoofSurface.

# + pycharm={"is_executing": false, "name": "#%%\n"}
srf = {
    0: {'surface_idx': [], 'type': 'WallSurface'},
    1: {'surface_idx': [], 'type': 'GroundSurface'},
    2: {'surface_idx': [], 'type': 'RoofSurface'}
}

# + [markdown] pycharm={"name": "#%% md\n"}
# We use the `surface_idx` to point to the surfaces of the boundary. Thus the
# index to a single boundary surface is composed as [Solid index, Shell index, Surface index].
# Consequently, in case of a CompositeSolid which first Solid, outer Shell,
# second Surface is a WallSurface, one element in the `surface_idx` would be
# `[0, 0, 1]`. Then assuming that there is only a single WallSurface in the
# mentioned CompositeSolid, the index to the WallSurfaces is composed as
# `{'surface_idx': [ [0, 0, 1] ], 'type': 'WallSurface'}`.
# In case of a Solid boundary type the *Solid index* is omitted from the elements
# of `surface_idx`. In case of a MultiSurface boundary type both the *Solid index*
# and *Shell index* are omitted from the elements of `surface_idx`.
#
# We create the surface index accordingly and assign them to the geometry.

# + pycharm={"is_executing": false, "name": "#%%\n"}
geom.surfaces[0] = {'surface_idx': [[0,0], [0,1], [0,2], [0,3]], 'type': 'WallSurface'}
geom.surfaces[1] = {'surface_idx': [[0,4]], 'type': 'GroundSurface'}
geom.surfaces[2] = {'surface_idx': [[0,5]], 'type': 'RoofSurface'}

# + [markdown] pycharm={"name": "#%% md\n"}
# Then we test if it works.

# + pycharm={"is_executing": false, "name": "#%%\n"}
ground = geom.get_surfaces('groundsurface')
ground_boundaries = []
for g in ground.values():
    ground_boundaries.append(geom.get_surface_boundaries(g))

# + [markdown] pycharm={"name": "#%% md\n"}
# We have a list of generators

# + pycharm={"is_executing": false, "name": "#%%\n"}
res = list(ground_boundaries[0])

# + [markdown] pycharm={"name": "#%% md\n"}
# The generator creates a list of surfaces --> a MultiSurface

# + pycharm={"is_executing": false, "name": "#%%\n"}
assert  res[0] == bdry[4]

# %%
wall = geom.get_surfaces('wallsurface')
wall_boundaries = []
for w in wall.values():
    wall_boundaries.append(geom.get_surface_boundaries(w))

# + [markdown] pycharm={"name": "#%% md\n"}
# We put everything together, first filling up the CityObject

# + pycharm={"is_executing": false, "name": "#%%\n"}
co.geometry.append(geom)
co.type = 'Building'

# + [markdown] pycharm={"name": "#%% md\n"}
# Then adding the CityObject to the CityModel.

# + pycharm={"is_executing": false, "name": "#%%\n"}
cm.cityobjects[co.id] = co

# + [markdown] pycharm={"name": "#%% md\n"}
# Let's validate the citymodel before writing it to a file. However, first we 
# need to index the geometry boundaries and create the vertex list, second we 
# need to add the cityobject and vertices to the internal json-store of the 
# citymodel so the `validate()` method can validate them.
#
# Note: CityJSON version 1.0.0 only accepts the Geometry `lod` as a numeric 
# value and not a string.

# + pycharm={"is_executing": false, "name": "#%%\n"}
cityobjects, vertex_lookup = cm.reference_geometry()
cm.add_to_j(cityobjects,vertex_lookup)
cm.update_bbox()
#cm.validate(folder_schemas=schema_dir)
# -

cm

# + [markdown] pycharm={"name": "#%% md\n"}
# Finally, we write the citymodel to a CityJSON file.

# + pycharm={"is_executing": false, "name": "#%%\n"}
outfile = data_dir / 'test_create.json'
cityjson.save(cm, outfile)

StarcoderdataJupyterScriptsDedupFiltered