Boundary creation model

This notebook demonstrates how to use the boundary creation model

[1]:
from buildingmodel import data_path
from pathlib import Path
import geopandas as gpd
from buildingmodel.creation.boundary import run_creation
from buildingmodel.main import Parameters, load_data
import os
from shapely.geometry import Polygon
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Setting simulation parameters

[2]:
parameters = Parameters(
    districts="districts_test_sample_new.gpkg",
    district_level_census="district_level_census_test_sample.hdf",
    district_level_diagnosis="district_level_diagnosis_data_test_sample.hdf",
    date_format="%Y/%m/%d",
    gas_network_route="gas_network_route_sample.gpkg",
)

building_data = Path(data_path["gis"]) / "testing" / "Urban_Center.gpkg"
climate_data = Path(data_path["climate"]) / "Brest.epw"

Loading sample gis data

[3]:
building_init, climate, metadata = load_data(building_data, climate_data, parameters)
[4]:
building_init
[4]:
main_usage secondary_usage construction_date dwelling_count floor_count wall_material roof_material height altitude_min altitude_max ... has_annex district city city_group department region has_network_city_level has_network_grdf_data to_sim building_id
0 unknown NaN None 0.0 1.0 None None 13.0 79.2 NaN ... False None None None None None NaN NaN False 0
1 commercial NaN None 0.0 1.0 None None 13.0 71.0 72.1 ... False None None None None None NaN NaN False 1
2 residential commercial 1970/01/01 1.0 2.0 None None 5.0 76.3 76.9 ... False None None None None None NaN NaN True 2
3 residential commercial 1993/01/01 4.0 3.0 None None 9.0 68.3 71.6 ... False None None None None None NaN NaN True 3
4 residential commercial None 2.0 3.0 None None 8.0 70.0 71.0 ... False None None None None None NaN NaN True 4
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
648 residential NaN 1900/01/01 1.0 2.0 01 09 5.0 70.3 70.7 ... False None None None None None NaN NaN True 648
649 commercial NaN 1910/01/01 0.0 1.0 None None 5.0 70.0 71.0 ... False None None None None None NaN NaN False 649
650 residential NaN None 15.0 3.0 00 00 9.0 73.3 75.3 ... False None None None None None NaN NaN True 650
651 residential NaN 1800/01/01 1.0 3.0 10 10 6.0 71.5 72.1 ... False None None None None None NaN NaN True 651
652 residential NaN 1914/01/01 1.0 3.0 12 10 8.0 70.8 71.2 ... False None None None None None NaN NaN True 652

653 rows × 33 columns

[18]:
building_init.to_crs("EPSG:4326").explore()
[18]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Running the boundary model which performs the following operations :

  • Simplification of the geometries representing the footprints while preserving the topology using the Visvalingam-Whyatt algorithm. The tolerance for the simplification is an optional parameter of run_creation with a default value of 2 m² (see https://ignf.github.io/CartAGen/docs/algorithms/line/visvalingam.html for details)

  • Detection of adjacency (i.e. when a vertex is shared between two footprint polygons)

  • Calculation of boundaries (exterior walls, interior walls, roofs, floors) geometric characteristics (center, azimuth, area

[6]:
parameters.simplification_tolerance = 5.0
[7]:
boundaries, building = run_creation(building_init, parameters)
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)

plotting the district before and after simplification

[8]:
building_init["simplified"] = False
building["simplified"] = True

building_comparison = pd.concat([building_init, building], axis=0)
# fig,ax = plt.subplots(1,1,sharex=True,sharey=True,figsize=(15,15))

# building_init.plot(ax=ax,color='red', alpha=0.5)
building_comparison.to_crs("EPSG:4326").explore(
    column="simplified",
    cmap=["red", "blue"],
    tiles="CartoDB positron",
    tooltip=False,
    style_kwds={"stroke": False},
)
[8]:
Make this Notebook Trusted to load map: File -> Trust Notebook
[9]:
EXTERIOR_WALL = 0
INTERIOR_WALL = 1
ROOF = 2
FLOOR = 3
[10]:
boundaries.head()
[10]:
geometry height altitude center_x center_y center_z area type building_id azimuth floor_count adjacency_usage conventional_heating_set_point actual_heating_set_point
15 LINESTRING (706289.400 6857326.100, 706283.800... 5.0 76.60 706286.60 6.857325e+06 79.10 28.635642 0 2 347.905243 2.0 None 19.0 20.0
16 LINESTRING (706283.800 6857324.900, 706289.500... 5.0 76.60 706286.65 6.857311e+06 79.10 138.954129 0 2 258.164433 2.0 None 19.0 20.0
17 LINESTRING (706289.500 6857297.700, 706295.700... 5.0 76.60 706292.60 6.857297e+06 79.10 31.196955 0 2 186.441600 2.0 None 19.0 20.0
18 LINESTRING (706293.900 6857305.300, 706289.400... 5.0 76.60 706291.65 6.857316e+06 79.10 106.406062 0 2 77.792419 2.0 None 19.0 20.0
19 LINESTRING (706077.300 6856872.700, 706072.700... 9.0 69.95 706075.00 6.856873e+06 74.45 41.487950 0 3 3.731397 3.0 None 19.0 20.0
[11]:
boundaries.groupby("type")["area"].sum()
[11]:
type
0    162173.133763
1     23882.987142
2     59373.750000
3     59427.374312
Name: area, dtype: float64
[12]:
boundaries["type"].value_counts()
[12]:
type
0    2727
3     471
2     468
1     457
Name: count, dtype: int64

Parametric Study of the simplification tolerance

We compute below the numbers of boundaries and their surfaces by type for several values of the simplification tolerance

[13]:
simplification_tolerances = np.linspace(0.0, 7.0, 15)
results = pd.DataFrame(
    index=simplification_tolerances,
    columns=[
        "Exterior wall count",
        "Interior wall count",
        "Roof count",
        "Exterior wall area",
        "Interior wall area",
        "Roof area",
    ],
)
[14]:
for tol in simplification_tolerances:
    print(f"Running with tolerance of {tol}")
    parameters.simplification_tolerance = tol
    boundaries, building = run_creation(building_init, parameters)
    areas = boundaries.groupby("type")["area"].sum()
    counts = boundaries["type"].value_counts()
    results.loc[tol, "Exterior wall count"] = counts[0]
    results.loc[tol, "Interior wall count"] = counts[1]
    results.loc[tol, "Roof count"] = counts[2]
    results.loc[tol, "Exterior wall area"] = areas[0]
    results.loc[tol, "Interior wall area"] = areas[1]
    results.loc[tol, "Roof area"] = areas[2]
Running with tolerance of 0.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 0.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 1.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 1.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 2.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 2.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 3.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 3.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 4.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 4.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 5.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 5.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 6.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 6.5
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
Running with tolerance of 7.0
/home/yassine/miniconda3/envs/buildingmodel_dev/lib/python3.8/site-packages/topojson/core/cut.py:112: ShapelyDeprecationWarning: STRtree will be changed in 2.0.0 and will not be compatible with versions < 2.
  tree_splitter = STRtree(mp)
[15]:
results
[15]:
Exterior wall count Interior wall count Roof count Exterior wall area Interior wall area Roof area
0.0 3707 567 468 166099.873838 24268.291656 59495.02
0.5 3480 553 468 166040.567521 24258.515965 59492.23
1.0 3324 540 468 165689.699171 24249.121768 59482.985
1.5 3214 524 468 165393.680689 24193.443891 59468.86
2.0 3130 510 468 165038.436719 24168.03437 59443.385
2.5 3046 498 468 164513.119371 24104.93709 59442.065
3.0 2979 493 468 164113.240137 24090.555147 59436.87
3.5 2894 478 468 163410.242986 24024.159423 59433.13
4.0 2818 467 468 162836.36677 23922.75379 59384.57
4.5 2766 463 468 162496.187193 23901.00552 59381.265
5.0 2727 457 468 162173.133763 23882.987142 59373.75
5.5 2670 453 468 161230.186047 23855.323407 59438.365
6.0 2626 449 468 160844.980295 23814.914696 59477.51
6.5 2588 445 468 160474.103321 23798.182565 59443.5
7.0 2567 445 468 160350.079313 23798.182565 59423.44
[16]:
results["count exterior"] = results["Exterior wall count"] + 2 * results["Roof count"]
results["area exterior"] = results["Exterior wall area"] + 2 * results["Roof area"]
[17]:
fig, ax = plt.subplots(1, 1, sharex=True, figsize=(10, 10))


results.plot(ax=ax, y="count exterior")
ax2 = results.plot(ax=ax, y="area exterior", secondary_y=True)
ax.set_ylabel("Number of exterior boundaries")
ax2.set_ylabel("Total area of exterior boundaries in m²")
[17]:
Text(0, 0.5, 'Total area of exterior boundaries in m²')
../_images/notebooks_BoundaryModel_22_1.png