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]:
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]:
[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²')
