diff --git a/hub/city_model_structure/city_object.py b/hub/city_model_structure/city_object.py index ab50250c..549a3362 100644 --- a/hub/city_model_structure/city_object.py +++ b/hub/city_model_structure/city_object.py @@ -34,6 +34,7 @@ class CityObject: self._max_y = ConfigurationHelper().min_coordinate self._max_z = ConfigurationHelper().min_coordinate self._centroid = None + self._volume = None self._external_temperature = dict() self._global_horizontal = dict() self._diffuse = dict() @@ -63,7 +64,13 @@ class CityObject: Get city object volume in cubic meters :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 def detailed_polyhedron(self) -> Polyhedron: diff --git a/hub/imports/geometry/geojson.py b/hub/imports/geometry/geojson.py index 1adf95d1..f526bebd 100644 --- a/hub/imports/geometry/geojson.py +++ b/hub/imports/geometry/geojson.py @@ -6,6 +6,7 @@ Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca """ import json +import numpy as np import trimesh.creation from pyproj import Transformer @@ -64,8 +65,10 @@ class Geojson: buildings = [] for zone, surface_coordinates in enumerate(surfaces_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) - 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)) return buildings @@ -74,22 +77,41 @@ class Geojson: lod0_buildings = Geojson._create_buildings_lod0(name, year_of_construction, function, surface_coordinates) surfaces = [] buildings = [] + for zone, lod0_building in enumerate(lod0_buildings): - for surface in lod0_building.surfaces: - shapely_polygon = ShapelyPolygon(surface.solid_polygon.coordinates) - 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) - buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function)) + for surface in lod0_building.grounds: + volume = surface.solid_polygon.area * height + surfaces.append(surface) + 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 def _get_polygons(self, polygons, coordinates): @@ -119,6 +141,8 @@ class Geojson: def _store_shared_percentage_to_walls(self, city, city_mapped): for building in city.buildings: + if building.name not in city_mapped.keys(): + continue building_mapped = city_mapped[building.name] for wall in building.walls: percentage = 0 @@ -207,7 +231,7 @@ class Geojson: self._city.add_city_object(building) self._city.level_of_detail.geometry = lod 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) if len(missing_functions) > 0: print(f'There are unknown functions {missing_functions}') diff --git a/hub/imports/geometry/helpers/geometry_helper.py b/hub/imports/geometry/helpers/geometry_helper.py index b9384801..9c72d855 100644 --- a/hub/imports/geometry/helpers/geometry_helper.py +++ b/hub/imports/geometry/helpers/geometry_helper.py @@ -45,3 +45,10 @@ class GeometryHelper: array = points.split(' ') res = " " return res.join(array[0:len(array) - 3]) + + @staticmethod + def invert_points(points): + res = [] + for point in points: + res.insert(0,point) + return res diff --git a/hub/unittests/test_geometry_factory.py b/hub/unittests/test_geometry_factory.py index 04c958e4..69860803 100644 --- a/hub/unittests/test_geometry_factory.py +++ b/hub/unittests/test_geometry_factory.py @@ -132,13 +132,14 @@ class TestGeometryFactory(TestCase): """ Test geojson import """ - file = 'neighbours.geojson' + file = 'concordia.geojson' city = self._get_city(file, 'geojson', height_field='citygml_me', year_of_construction_field='ANNEE_CONS', 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._check_buildings(city) for building in city.buildings: @@ -150,13 +151,21 @@ class TestGeometryFactory(TestCase): Test neighbours map creation """ 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', height_field='citygml_me', year_of_construction_field='ANNEE_CONS', 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: 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('3_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[1].name) diff --git a/hub/unittests/tests_data/neighbours.geojson b/hub/unittests/tests_data/neighbours.geojson index 65149ee9..437bf551 100644 --- a/hub/unittests/tests_data/neighbours.geojson +++ b/hub/unittests/tests_data/neighbours.geojson @@ -12,18 +12,18 @@ -73.580414175680588, 45.497641136608358 ], - [ - -73.581414175680588, - 45.497641136608358 - ], - [ - -73.581414175680588, - 45.498641136608358 - ], [ -73.580414175680588, 45.498641136608358 ], + [ + -73.581414175680588, + 45.498641136608358 + ], + [ + -73.581414175680588, + 45.497641136608358 + ], [ -73.580414175680588, 45.497641136608358 @@ -204,19 +204,20 @@ [ -73.581414175680588, 45.497641136608358 + ] + , + [ + -73.581414175680588, + 45.498441136608358 + ], + [ + -73.582214175680588, + 45.498441136608358 ], [ -73.582214175680588, 45.497641136608358 ], - [ - -73.582214175680588, - 45.498441136608358 - ], - [ - -73.581414175680588, - 45.498441136608358 - ], [ -73.581414175680588, 45.497641136608358 @@ -399,31 +400,30 @@ -73.581914175680588, 45.498441136608358 ], - [ - -73.581914175680588, - 45.499641136608358 - ], - [ - -73.580914175680588, - 45.499641136608358 - ], - [ - -73.580914175680588, - 45.498641136608358 - ], - [ - -73.581414175680588, - 45.498641136608358 - ], [ -73.581414175680588, 45.498441136608358 ], + [ + -73.581414175680588, + 45.498641136608358 + ], + [ + -73.580914175680588, + 45.498641136608358 + ], + [ + -73.580914175680588, + 45.499641136608358 + ], + [ + -73.581914175680588, + 45.499641136608358 + ], [ -73.581914175680588, 45.498441136608358 ] - ] ] },