Custom extrusion and volume calculation for Geojson

This commit is contained in:
Guille Gutierrez 2023-03-15 14:21:38 -04:00
parent f0a07d9c0e
commit 20b88fbccb
5 changed files with 101 additions and 54 deletions

View File

@ -34,6 +34,7 @@ class CityObject:
self._max_y = ConfigurationHelper().min_coordinate self._max_y = ConfigurationHelper().min_coordinate
self._max_z = ConfigurationHelper().min_coordinate self._max_z = ConfigurationHelper().min_coordinate
self._centroid = None self._centroid = None
self._volume = None
self._external_temperature = dict() self._external_temperature = dict()
self._global_horizontal = dict() self._global_horizontal = dict()
self._diffuse = dict() self._diffuse = dict()
@ -63,7 +64,13 @@ class CityObject:
Get city object volume in cubic meters Get city object volume in cubic meters
:return: float :return: float
""" """
return self.simplified_polyhedron.volume if self._volume is None:
self._volume = self.simplified_polyhedron.volume
return self._volume
@volume.setter
def volume(self, value):
self._volume = value
@property @property
def detailed_polyhedron(self) -> Polyhedron: def detailed_polyhedron(self) -> Polyhedron:

View File

@ -6,6 +6,7 @@ Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca
""" """
import json import json
import numpy as np
import trimesh.creation import trimesh.creation
from pyproj import Transformer from pyproj import Transformer
@ -64,8 +65,10 @@ class Geojson:
buildings = [] buildings = []
for zone, surface_coordinates in enumerate(surfaces_coordinates): for zone, surface_coordinates in enumerate(surfaces_coordinates):
points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates)) points = igh.points_from_string(igh.remove_last_point_from_string(surface_coordinates))
# geojson provides the roofs, need to be transform into grounds
points = igh.invert_points(points)
polygon = Polygon(points) polygon = Polygon(points)
surfaces.append(Surface(polygon, polygon, surface_type=cte.GROUND)) surfaces.append(Surface(polygon, polygon))
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)) buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
return buildings return buildings
@ -74,22 +77,41 @@ class Geojson:
lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates) lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates)
surfaces = [] surfaces = []
buildings = [] buildings = []
for zone, lod0_building in enumerate(lod0_buildings): for zone, lod0_building in enumerate(lod0_buildings):
for surface in lod0_building.surfaces: for surface in lod0_building.grounds:
shapely_polygon = ShapelyPolygon(surface.solid_polygon.coordinates) volume = surface.solid_polygon.area * height
if not shapely_polygon.is_valid:
print(surface.solid_polygon.area)
print('error?', name, surface_coordinates)
continue
mesh = trimesh.creation.extrude_polygon(shapely_polygon, height)
for face in mesh.faces:
points = []
for vertex_index in face:
points.append(mesh.vertices[vertex_index])
polygon = Polygon(points)
surface = Surface(polygon, polygon)
surfaces.append(surface) surfaces.append(surface)
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)) roof_coordinates = []
# adding a roof means invert the polygon coordinates and change the Z value
for coordinate in surface.solid_polygon.coordinates:
roof_coordinate = np.array([coordinate[0], coordinate[1], height])
# insert the roof rotated already
roof_coordinates.insert(0, roof_coordinate)
polygon = Polygon(roof_coordinates)
roof = Surface(polygon, polygon)
surfaces.append(roof)
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
coordinates_length = len(roof.solid_polygon.coordinates)
for i, coordinate in enumerate(roof.solid_polygon.coordinates):
j = i + 1
if j == coordinates_length:
j = 0
next_coordinate = roof.solid_polygon.coordinates[j]
wall_coordinates = [
np.array([coordinate[0], coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], 0.0]),
np.array([next_coordinate[0], next_coordinate[1], next_coordinate[2]]),
np.array([coordinate[0], coordinate[1], coordinate[2]])
]
polygon = Polygon(wall_coordinates)
wall = Surface(polygon, polygon)
surfaces.append(wall)
building = Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)
building.volume = volume
buildings.append(building)
return buildings return buildings
def _get_polygons(self, polygons, coordinates): def _get_polygons(self, polygons, coordinates):
@ -119,6 +141,8 @@ class Geojson:
def _store_shared_percentage_to_walls(self, city, city_mapped): def _store_shared_percentage_to_walls(self, city, city_mapped):
for building in city.buildings: for building in city.buildings:
if building.name not in city_mapped.keys():
continue
building_mapped = city_mapped[building.name] building_mapped = city_mapped[building.name]
for wall in building.walls: for wall in building.walls:
percentage = 0 percentage = 0
@ -207,7 +231,7 @@ class Geojson:
self._city.add_city_object(building) self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod self._city.level_of_detail.geometry = lod
if lod == 1: if lod == 1:
lines_information = GeometryHelper.city_mapping(self._city) lines_information = GeometryHelper.city_mapping(self._city, plot=True)
self._store_shared_percentage_to_walls(self._city, lines_information) self._store_shared_percentage_to_walls(self._city, lines_information)
if len(missing_functions) > 0: if len(missing_functions) > 0:
print(f'There are unknown functions {missing_functions}') print(f'There are unknown functions {missing_functions}')

View File

@ -45,3 +45,10 @@ class GeometryHelper:
array = points.split(' ') array = points.split(' ')
res = " " res = " "
return res.join(array[0:len(array) - 3]) return res.join(array[0:len(array) - 3])
@staticmethod
def invert_points(points):
res = []
for point in points:
res.insert(0,point)
return res

View File

@ -132,13 +132,14 @@ class TestGeometryFactory(TestCase):
""" """
Test geojson import Test geojson import
""" """
file = 'neighbours.geojson' file = 'concordia.geojson'
city = self._get_city(file, 'geojson', city = self._get_city(file, 'geojson',
height_field='citygml_me', height_field='citygml_me',
year_of_construction_field='ANNEE_CONS', year_of_construction_field='ANNEE_CONS',
function_field='CODE_UTILI') function_field='CODE_UTILI')
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export() for building in city.buildings:
print(building.volume)
self.assertEqual(207, len(city.buildings), 'wrong number of buildings') self.assertEqual(207, len(city.buildings), 'wrong number of buildings')
self._check_buildings(city) self._check_buildings(city)
for building in city.buildings: for building in city.buildings:
@ -150,13 +151,21 @@ class TestGeometryFactory(TestCase):
Test neighbours map creation Test neighbours map creation
""" """
file = 'neighbours.geojson' file = 'neighbours.geojson'
city = self._get_city(file, 'geojson',
year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT')
info_lod0 = GeometryHelper.city_mapping(city, plot=False)
city = self._get_city(file, 'geojson', city = self._get_city(file, 'geojson',
height_field='citygml_me', height_field='citygml_me',
year_of_construction_field='ANNEE_CONS', year_of_construction_field='ANNEE_CONS',
function_field='LIBELLE_UT') function_field='LIBELLE_UT')
print(GeometryHelper.city_mapping(city, plot=True)) info_lod1 = GeometryHelper.city_mapping(city, plot=False)
self.assertEqual(info_lod0, info_lod1)
for building in city.buildings: for building in city.buildings:
self.assertEqual(2, len(building.neighbours)) self.assertEqual(2, len(building.neighbours))
print(building.volume)
self.assertEqual('2_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[0].name) self.assertEqual('2_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[0].name)
self.assertEqual('3_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[1].name) self.assertEqual('3_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[1].name)

View File

@ -12,18 +12,18 @@
-73.580414175680588, -73.580414175680588,
45.497641136608358 45.497641136608358
], ],
[
-73.581414175680588,
45.497641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[ [
-73.580414175680588, -73.580414175680588,
45.498641136608358 45.498641136608358
], ],
[
-73.581414175680588,
45.498641136608358
],
[
-73.581414175680588,
45.497641136608358
],
[ [
-73.580414175680588, -73.580414175680588,
45.497641136608358 45.497641136608358
@ -204,19 +204,20 @@
[ [
-73.581414175680588, -73.581414175680588,
45.497641136608358 45.497641136608358
]
,
[
-73.581414175680588,
45.498441136608358
],
[
-73.582214175680588,
45.498441136608358
], ],
[ [
-73.582214175680588, -73.582214175680588,
45.497641136608358 45.497641136608358
], ],
[
-73.582214175680588,
45.498441136608358
],
[
-73.581414175680588,
45.498441136608358
],
[ [
-73.581414175680588, -73.581414175680588,
45.497641136608358 45.497641136608358
@ -399,31 +400,30 @@
-73.581914175680588, -73.581914175680588,
45.498441136608358 45.498441136608358
], ],
[
-73.581914175680588,
45.499641136608358
],
[
-73.580914175680588,
45.499641136608358
],
[
-73.580914175680588,
45.498641136608358
],
[
-73.581414175680588,
45.498641136608358
],
[ [
-73.581414175680588, -73.581414175680588,
45.498441136608358 45.498441136608358
], ],
[
-73.581414175680588,
45.498641136608358
],
[
-73.580914175680588,
45.498641136608358
],
[
-73.580914175680588,
45.499641136608358
],
[
-73.581914175680588,
45.499641136608358
],
[ [
-73.581914175680588, -73.581914175680588,
45.498441136608358 45.498441136608358
] ]
] ]
] ]
}, },