{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": true, "pycharm": { "name": "#%% md\n" } }, "source": [ "# Creating city models and objects\n", "\n", "In this tutorial we explore how to create new city models with using `cjio`'s\n", " API." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "from cjio import cityjson\n", "from cjio.models import CityObject, Geometry" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Set up paths for the tutorial.\n", " " ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "package_dir = Path(__name__).resolve().parent.parent.parent\n", "schema_dir = package_dir / 'cjio' / 'schemas'/ '1.0.0'\n", "data_dir = package_dir / 'tests' / 'data'" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Creating a single CityObject\n", "\n", "We are building a single CityObject of type *Building*. This building has an \n", "LoD2 geometry, thus it has Semantic Surfaces. The geometric shape of the \n", "building is a simple cube (size 10x10x10), which is sufficient for this \n", "demonstration.\n", "\n", "The idea is that we create empty containers for the CityModel, CityObjects and\n", "Geometries, then fill those up and add to the CityModel." ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We create an empty CityModel" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"cityjson_version\": \"1.0\",\n", " \"epsg\": null,\n", " \"bbox\": [\n", " 9000000000.0,\n", " 9000000000.0,\n", " 9000000000.0,\n", " -9000000000.0,\n", " -9000000000.0,\n", " -9000000000.0\n", " ],\n", " \"transform/compressed\": false,\n", " \"cityobjects_total\": 0,\n", " \"cityobjects_present\": [],\n", " \"materials\": false,\n", " \"textures\": false\n", "}\n" ] } ], "source": [ "cm = cityjson.CityJSON()\n", "print(cm)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "An empty CityObject. Note that the ID is required." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "co = CityObject(\n", " id='1'\n", ")" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We can also add attributes" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "co_attrs = {\n", " 'some_attribute': 42,\n", " 'other_attribute': 'bla bla'\n", "}\n", "co.attributes = co_attrs" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Let's see what do we have" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{\n", " \"id\": \"1\",\n", " \"type\": null,\n", " \"attributes\": {\n", " \"some_attribute\": 42,\n", " \"other_attribute\": \"bla bla\"\n", " },\n", " \"children\": [],\n", " \"parents\": [],\n", " \"geometry_type\": [],\n", " \"geometry_lod\": [],\n", " \"semantic_surfaces\": []\n", "}\n" ] } ], "source": [ "print(co)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Instantiate a Geometry without boundaries and semantics" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "geom = Geometry(type='Solid', lod=2)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We build the boundary Solid of the cube\n", "The surfaces are in this order: WallSurface, WallSurface, WallSurface, WallSurface, GroundSurface, RoofSurface" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "bdry = [\n", " [[(0.0, 0.0, 0.0), (10.0, 0.0, 0.0), (10.0, 0.0, 10.0), (0.0, 0.0, 10.0)]],\n", " [[(10.0, 0.0, 0.0), (10.0, 10.0, 0.0), (10.0, 10.0, 10.0), (10.0, 0.0, 10.0)]],\n", " [[(10.0, 10.0, 0.0), (0.0, 10.0, 0.0), (0.0, 10.0, 10.0), (10.0, 10.0, 10.0)]],\n", " [[(0.0, 10.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 10.0), (0.0, 10.0, 10.0)]],\n", " [[(0.0, 0.0, 0.0), (0.0, 10.0, 0.0), (10.0, 10.0, 0.0), (10.0, 0.0, 0.0)]],\n", " [[(10.0, 0.0, 10.0), (10.0, 10.0, 10.0), (0.0, 10.0, 10.0), (0.0, 0.0, 10.0)]]\n", "]" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Add the boundary to the Geometry" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "geom.boundaries.append(bdry)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We build the SemanticSurfaces for the boundary. The `surfaces` attribute must\n", "contain at least the `surface_idx` and `type` keys, optionally `attributes`.\n", "We have three semantic surface types, WallSurface, GroundSurface, RoofSurface." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "srf = {\n", " 0: {'surface_idx': [], 'type': 'WallSurface'},\n", " 1: {'surface_idx': [], 'type': 'GroundSurface'},\n", " 2: {'surface_idx': [], 'type': 'RoofSurface'}\n", "}" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We use the `surface_idx` to point to the surfaces of the boundary. Thus the\n", "index to a single boundary surface is composed as [Solid index, Shell index, Surface index].\n", "Consequently, in case of a CompositeSolid which first Solid, outer Shell,\n", "second Surface is a WallSurface, one element in the `surface_idx` would be\n", "`[0, 0, 1]`. Then assuming that there is only a single WallSurface in the\n", "mentioned CompositeSolid, the index to the WallSurfaces is composed as\n", "`{'surface_idx': [ [0, 0, 1] ], 'type': 'WallSurface'}`.\n", "In case of a Solid boundary type the *Solid index* is omitted from the elements\n", "of `surface_idx`. In case of a MultiSurface boundary type both the *Solid index*\n", "and *Shell index* are omitted from the elements of `surface_idx`.\n", "\n", "We create the surface index accordingly and assign them to the geometry." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "geom.surfaces[0] = {'surface_idx': [[0,0], [0,1], [0,2], [0,3]], 'type': 'WallSurface'}\n", "geom.surfaces[1] = {'surface_idx': [[0,4]], 'type': 'GroundSurface'}\n", "geom.surfaces[2] = {'surface_idx': [[0,5]], 'type': 'RoofSurface'}" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Then we test if it works." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "ground = geom.get_surfaces('groundsurface')\n", "ground_boundaries = []\n", "for g in ground.values():\n", " ground_boundaries.append(geom.get_surface_boundaries(g))" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We have a list of generators" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "res = list(ground_boundaries[0])" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "The generator creates a list of surfaces --> a MultiSurface" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "assert res[0] == bdry[4]\n", "\n", "# %%\n", "wall = geom.get_surfaces('wallsurface')\n", "wall_boundaries = []\n", "for w in wall.values():\n", " wall_boundaries.append(geom.get_surface_boundaries(w))" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "We put everything together, first filling up the CityObject" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "co.geometry.append(geom)\n", "co.type = 'Building'" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Then adding the CityObject to the CityModel." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "cm.cityobjects[co.id] = co" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Let's validate the citymodel before writing it to a file. However, first we \n", "need to index the geometry boundaries and create the vertex list, second we \n", "need to add the cityobject and vertices to the internal json-store of the \n", "citymodel so the `validate()` method can validate them.\n", "\n", "Note: CityJSON version 1.0.0 only accepts the Geometry `lod` as a numeric \n", "value and not a string." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" }, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[0.0, 0.0, 0.0, 10.0, 10.0, 10.0]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cityobjects, vertex_lookup = cm.reference_geometry()\n", "cm.add_to_j(cityobjects,vertex_lookup)\n", "cm.update_bbox()\n", "#cm.validate(folder_schemas=schema_dir)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{\n", " \"cityjson_version\": \"1.0\",\n", " \"epsg\": null,\n", " \"bbox\": [\n", " 0.0,\n", " 0.0,\n", " 0.0,\n", " 10.0,\n", " 10.0,\n", " 10.0\n", " ],\n", " \"transform/compressed\": false,\n", " \"cityobjects_total\": 1,\n", " \"cityobjects_present\": [\n", " \"Building\"\n", " ],\n", " \"materials\": false,\n", " \"textures\": false\n", "}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cm" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "Finally, we write the citymodel to a CityJSON file." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "pycharm": { "is_executing": false, "name": "#%%\n" } }, "outputs": [], "source": [ "outfile = data_dir / 'test_create.json'\n", "cityjson.save(cm, outfile)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.7" }, "pycharm": { "stem_cell": { "cell_type": "raw", "metadata": { "collapsed": false }, "source": [] } } }, "nbformat": 4, "nbformat_minor": 1 }