diff --git a/city_model_structure/attributes/polyhedron.py b/city_model_structure/attributes/polyhedron.py index 9452e8fc..27970eeb 100644 --- a/city_model_structure/attributes/polyhedron.py +++ b/city_model_structure/attributes/polyhedron.py @@ -4,12 +4,13 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ import numpy as np -import matplotlib.pyplot as plt from trimesh import Trimesh from helpers.geometry_helper import GeometryHelper +from helpers.configuration_helper import ConfigurationHelper from city_model_structure.attributes.surface import Surface + class Polyhedron: """ Polyhedron class @@ -25,6 +26,11 @@ class Polyhedron: self._mesh = None self._centroid = None self._max_z = None + self._max_y = None + self._max_x = None + self._min_z = None + self._min_y = None + self._min_x = None self._geometry = GeometryHelper() def _position_of(self, point): @@ -59,36 +65,13 @@ class Polyhedron: def _point(coordinates): return coordinates[0], coordinates[1], coordinates[2] - @staticmethod - def _ground_weird_shape(points, original, triangle=False): - x = [] - y = [] - xo = [] - yo = [] - for o in original: - xo.append(o[0]) - yo.append(o[1]) - for point in points: - x.append(point[0]) - y.append(point[1]) - x = [val - min(xo) for val in x] - y = [val - min(yo) for val in y] - if triangle: - print(len(points)) - for point in range(0, len(x)): - print('point', x[point], y[point]) - def _triangulate(self, surface): - Polyhedron._ground_weird_shape(surface.points, surface.points) triangles = [] triangles_count = len(surface.points) - 2 points_list = surface.points_list - print(points_list) point_index = 0 area = surface.area - print(f'area is {area}') while len(triangles) < triangles_count: - print(f'try vertex {point_index}') # select a triangle starting at point index triangle_points = ' '.join(str(e) for e in [*points_list[point_index:point_index + 9]]) # remove the middle vertex from previous triangle @@ -97,22 +80,12 @@ class Polyhedron: rest_surface = Surface(rest_points, remove_last=False) if self._geometry.almost_same_area(area, (triangular_surface.area + rest_surface.area)): area = rest_surface.area - print(f'ok! new area is {area}') - print('Triangle-----------------------------------------') - Polyhedron._ground_weird_shape(triangular_surface.points, surface.points) - print('rest---------------------------------------------') - Polyhedron._ground_weird_shape(rest_surface.points, surface.points) triangles.append(triangular_surface) points_list = rest_surface.points_list if len(rest_surface.points) == 3: triangles.append(rest_surface) point_index = 0 else: - print(f'nok! area {(triangular_surface.area + rest_surface.area)} is not {area}') - print('Triangle-----------------------------------------') - Polyhedron._ground_weird_shape(triangular_surface.points, surface.points) - print('rest---------------------------------------------') - Polyhedron._ground_weird_shape(rest_surface.points, surface.points) point_index = point_index + 3 return triangles @@ -127,39 +100,23 @@ class Polyhedron: for surface in self._surfaces: face = [] points = surface.points - if len(points) != 3: # three vertices - print(f'Number of vertex {len(points)}') + if len(points) != 3: sub_surfaces = self._triangulate(surface) - print(f'Transformed {len(points)} vertex into {len(sub_surfaces)} triangles') - print(f'Total area: {surface.area}') - sum_area = 0.0 for sub_surface in sub_surfaces: - sum_area = sum_area + sub_surface.area points = sub_surface.points for point in points: face.append(self._position_of(point)) self._faces.append(face) - print(f'Accumulated area: {sum_area}') else: for point in points: face.append(self._position_of(point)) self._faces.append(face) return self._faces - @property - def _cloud_mesh(self): - if self._mesh is None: - self._mesh = GeometryHelper.create_mesh(self._surfaces) - return self._mesh - @property def _polyhedron_mesh(self): if self._mesh is None: - try: - print("create mesh") - self._mesh = Trimesh(vertices=self.vertices, faces=self.faces) - except SyntaxError: - self._mesh = self._cloud_mesh + self._mesh = Trimesh(vertices=self.vertices, faces=self.faces) return self._mesh @property @@ -181,9 +138,82 @@ class Polyhedron: Polyhedron maximal z value :return: float """ - bounds = self._polyhedron_mesh.bounds - z_max = max(bounds[:, 2]) - return z_max + if self._max_z is None: + self._max_z = ConfigurationHelper().min_coordinate + for surface in self._surfaces: + for point in surface.points: + if self._max_z < point[2]: + self._max_z = point[2] + return self._max_z + + @property + def max_y(self): + """ + Polyhedron maximal y value + :return: float + """ + if self._max_y is None: + self._max_y = ConfigurationHelper().min_coordinate + for surface in self._surfaces: + for point in surface.points: + if self._max_y < point[1]: + self._max_y = point[1] + return self._max_y + + @property + def max_x(self): + """ + Polyhedron maximal x value + :return: float + """ + if self._max_x is None: + self._max_x = ConfigurationHelper().min_coordinate + for surface in self._surfaces: + for point in surface.points: + self._max_x = max(self._max_x, point[0]) + return self._max_x + + @property + def min_z(self): + """ + Polyhedron minimal z value + :return: float + """ + if self._min_z is None: + self._min_z = self.max_z + for surface in self._surfaces: + for point in surface.points: + if self._min_z > point[2]: + self._min_z = point[2] + return self._min_z + + @property + def min_y(self): + """ + Polyhedron minimal y value + :return: float + """ + if self._min_y is None: + self._min_y = self.max_y + for surface in self._surfaces: + for point in surface.points: + if self._min_y > point[1]: + self._min_y = point[1] + return self._min_y + + @property + def min_x(self): + """ + Polyhedron minimal x value + :return: float + """ + if self._min_x is None: + self._min_x = self.max_x + for surface in self._surfaces: + for point in surface.points: + if self._min_x > point[0]: + self._min_x = point[0] + return self._min_x @property def centroid(self): @@ -191,7 +221,8 @@ class Polyhedron: Polyhedron centroid :return: [x,y,z] """ - return self._polyhedron_mesh.centroid + return [self.max_x - self.min_x, self.max_y - self.min_y, self.max_z - self.min_z] + def stl_export(self, full_path): """ diff --git a/city_model_structure/attributes/surface.py b/city_model_structure/attributes/surface.py index 399b2053..e2515a1f 100644 --- a/city_model_structure/attributes/surface.py +++ b/city_model_structure/attributes/surface.py @@ -205,7 +205,10 @@ class Surface: :return: float """ if self._area is None: - self._area = self.polygon.get_area() + try: + self._area = self.polygon.get_area() + except AttributeError: + self._area = 0 return self._area @property @@ -481,3 +484,7 @@ class Surface: except Exception as err: print('Error', err) return None + + @property + def convex(self): + return pn.Polygon.is_convex(self.polygon.points) diff --git a/config/configuration.ini b/config/configuration.ini index 840206f1..d63fb74c 100644 --- a/config/configuration.ini +++ b/config/configuration.ini @@ -19,4 +19,5 @@ shortwave_reflectance = 0.8 min_air_change = 0.5 [buildings] -max_location_distance_for_shared_walls = 100.0 \ No newline at end of file +max_location_distance_for_shared_walls = 100.0 +min_coordinate = -1.7976931348623157e+308 \ No newline at end of file diff --git a/factories/geometry_feeders/city_gml.py b/factories/geometry_feeders/city_gml.py index 14bf99f2..92028b41 100644 --- a/factories/geometry_feeders/city_gml.py +++ b/factories/geometry_feeders/city_gml.py @@ -39,7 +39,7 @@ class CityGml: 'http://www.opengis.net/citygml/relief/2.0 http://schemas.opengis.net/citygml/relief/2.0/relief.xsd" ' 'xmlns="http://www.opengis.net/citygml/2.0': None, 'http://www.opengis.net/citygml/2.0': None - }, force_list=('cityObjectMember', 'curveMember', 'boundedBy', 'surfaceMember', 'CompositeSurface')) + }, force_list=('cityObjectMember', 'curveMember', 'boundedBy', 'surfaceMember')) self._city_objects = None self._geometry = GeometryHelper() for bound in self._gml['CityModel']['boundedBy']: @@ -80,6 +80,9 @@ class CityGml: elif 'lod1MultiSurface' in o['Building']: lod += 1 surfaces = CityGml._lod1_multisurface(o) + elif 'lod2MultiSurface' in o['Building']: + lod = 2 + surfaces = surfaces + CityGml._lod2_multisurface(o) else: for bound in o['Building']['boundedBy']: surface_type = next(iter(bound)) @@ -135,6 +138,12 @@ class CityGml: for s in o['Building']['lod1MultiSurface']['MultiSurface']['surfaceMember']] return surfaces + @staticmethod + def _lod2_multisurface(o): + surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList']) + for s in o['Building']['lod2MultiSurface']['MultiSurface']['surfaceMember']] + return surfaces + @staticmethod def _lod2(bound): surfaces = [] diff --git a/helpers/configuration_helper.py b/helpers/configuration_helper.py index c1a43256..879e9903 100644 --- a/helpers/configuration_helper.py +++ b/helpers/configuration_helper.py @@ -100,8 +100,15 @@ class ConfigurationHelper: @property def max_location_distance_for_shared_walls(self): """ - Configured maximal distance between - attributes to consider that they may share walls in meters + Configured maximal distance between attributes to consider that they may share walls in meters :return: float """ return self._config.getfloat('buildings', 'max_location_distance_for_shared_walls') + + @property + def min_coordinate(self) -> float: + """ + Configured minimal coordinate value + :return: float + """ + return self._config.getfloat('buildings', 'min_coordinate') diff --git a/helpers/geometry_helper.py b/helpers/geometry_helper.py index 4f5cf323..52c80603 100644 --- a/helpers/geometry_helper.py +++ b/helpers/geometry_helper.py @@ -28,7 +28,6 @@ class GeometryHelper: :param location2: :return: Boolean """ - print("check adjacency") max_distance = ConfigurationHelper().max_location_distance_for_shared_walls x = location1[0] - location2[0] y = location1[1] - location2[1] diff --git a/tests/test_geometry_factory.py b/tests/test_geometry_factory.py index 2b5f2e27..3e810831 100644 --- a/tests/test_geometry_factory.py +++ b/tests/test_geometry_factory.py @@ -24,7 +24,7 @@ class TestGeometryFactory(TestCase): def _get_citygml(self): if self._city_gml is None: - file_path = (self._example_path / 'EngHT_Flat_model_lod1.gml').resolve() + file_path = (self._example_path / 'lod2_buildings.gml').resolve() self._city_gml = GeometryFactory('citygml', file_path).city self.assertIsNotNone(self._city_gml, 'city is none') return self._city_gml diff --git a/tests_data/building_lod2.gml b/tests_data/building_lod2.gml new file mode 100644 index 00000000..2e1dfbf0 --- /dev/null +++ b/tests_data/building_lod2.gml @@ -0,0 +1,453 @@ + + + + + 326011.03601000085 5526362.34802 0 + 329466.6600299999 5529018.72205 8.5 + + + + + + 809144 + + + 7 + + + SFD + + + 148.17 + + + m2 + + + 189 + + + m2 + + + residential + + + 2019 + + + 2019 + + residential + 2019 + flat + 5.5 + 1 + + + + + + + 327918.65699999966 5527620.1219999995 0 327918.65699999966 5527620.1219999995 5.5 327918.5720000006 5527617.433 5.5 327918.5720000006 5527617.433 0 327918.65699999966 5527620.1219999995 0 + + + + + + + + + 327912.1970099993 5527620.328 0 327912.1970099993 5527620.328 5.5 327918.65699999966 5527620.1219999995 5.5 327918.65699999966 5527620.1219999995 0 327912.1970099993 5527620.328 0 + + + + + + + + + 327911.7700100001 5527606.959000001 0 327911.7700100001 5527606.959000001 5.5 327912.1970099993 5527620.328 5.5 327912.1970099993 5527620.328 0 327911.7700100001 5527606.959000001 0 + + + + + + + + + 327923.7589999996 5527606.57701 0 327923.7589999996 5527606.57701 5.5 327911.7700100001 5527606.959000001 5.5 327911.7700100001 5527606.959000001 0 327923.7589999996 5527606.57701 0 + + + + + + + + + 327924.1219900008 5527617.968 0 327924.1219900008 5527617.968 5.5 327923.7589999996 5527606.57701 5.5 327923.7589999996 5527606.57701 0 327924.1219900008 5527617.968 0 + + + + + + + + + 327920.45799 5527618.085000001 0 327920.45799 5527618.085000001 5.5 327924.1219900008 5527617.968 5.5 327924.1219900008 5527617.968 0 327920.45799 5527618.085000001 0 + + + + + + + + + 327918.5720000006 5527617.433 0 327918.5720000006 5527617.433 5.5 327920.45799 5527618.085000001 5.5 327920.45799 5527618.085000001 0 327918.5720000006 5527617.433 0 + + + + + + + + + 327918.65699999966 5527620.1219999995 5.5 327912.1970099993 5527620.328 5.5 327911.7700100001 5527606.959000001 5.5 327923.7589999996 5527606.57701 5.5 327924.1219900008 5527617.968 5.5 327920.45799 5527618.085000001 5.5 327918.5720000006 5527617.433 5.5 327918.65699999966 5527620.1219999995 5.5 + + + + + + + + + + + + 809157 + + + 7 + + + SFD + + + 147.58 + + + m2 + + + 201 + + + m2 + + + residential + + + 2019 + + + 2019 + + residential + 2019 + flat + 7 + 2 + + + + + + + 327903.1820100006 5527620.4350000005 0 327903.1820100006 5527620.4350000005 7 327903.13698999956 5527617.434 7 327903.13698999956 5527617.434 0 327903.1820100006 5527620.4350000005 0 + + + + + + + + + 327896.92998999916 5527620.528000001 0 327896.92998999916 5527620.528000001 7 327903.1820100006 5527620.4350000005 7 327903.1820100006 5527620.4350000005 0 327896.92998999916 5527620.528000001 0 + + + + + + + + + 327896.716 5527606.1899999995 0 327896.716 5527606.1899999995 7 327896.92998999916 5527620.528000001 7 327896.92998999916 5527620.528000001 0 327896.716 5527606.1899999995 0 + + + + + + + + + 327904.02800000086 5527606.081 0 327904.02800000086 5527606.081 7 327896.716 5527606.1899999995 7 327896.716 5527606.1899999995 0 327904.02800000086 5527606.081 0 + + + + + + + + + 327907.9790000003 5527606.880999999 0 327907.9790000003 5527606.880999999 7 327904.02800000086 5527606.081 7 327904.02800000086 5527606.081 0 327907.9790000003 5527606.880999999 0 + + + + + + + + + 327908.15699999966 5527618.875 0 327908.15699999966 5527618.875 7 327907.9790000003 5527606.880999999 7 327907.9790000003 5527606.880999999 0 327908.15699999966 5527618.875 0 + + + + + + + + + 327905.10500000045 5527618.92 0 327905.10500000045 5527618.92 7 327908.15699999966 5527618.875 7 327908.15699999966 5527618.875 0 327905.10500000045 5527618.92 0 + + + + + + + + + 327905.08299 5527617.404999999 0 327905.08299 5527617.404999999 7 327905.10500000045 5527618.92 7 327905.10500000045 5527618.92 0 327905.08299 5527617.404999999 0 + + + + + + + + + 327903.13698999956 5527617.434 0 327903.13698999956 5527617.434 7 327905.08299 5527617.404999999 7 327905.08299 5527617.404999999 0 327903.13698999956 5527617.434 0 + + + + + + + + + 327903.1820100006 5527620.4350000005 7 327896.92998999916 5527620.528000001 7 327896.716 5527606.1899999995 7 327904.02800000086 5527606.081 7 327907.9790000003 5527606.880999999 7 327908.15699999966 5527618.875 7 327905.10500000045 5527618.92 7 327905.08299 5527617.404999999 7 327903.13698999956 5527617.434 7 327903.1820100006 5527620.4350000005 7 + + + + + + + + + + + + 182978 + + + 47 + + + MURB + + + 646.8 + + + m2 + + + 1940 + + + m2 + + + residential + + + 1974 + + + 1974 + + residential + 1974 + flat + 8.5 + 3 + + + + + + + 328066.44198999926 5528221.52698 0 328066.44198999926 5528221.52698 8.5 328066.37298999913 5528218.87403 8.5 328066.37298999913 5528218.87403 0 328066.44198999926 5528221.52698 0 + + + + + + + + + 328062.64898999967 5528221.625979999 0 328062.64898999967 5528221.625979999 8.5 328066.44198999926 5528221.52698 8.5 328066.44198999926 5528221.52698 0 328062.64898999967 5528221.625979999 0 + + + + + + + + + 328062.5769999996 5528218.872020001 0 328062.5769999996 5528218.872020001 8.5 328062.64898999967 5528221.625979999 8.5 328062.64898999967 5528221.625979999 0 328062.5769999996 5528218.872020001 0 + + + + + + + + + 328054.4609999992 5528219.083009999 0 328054.4609999992 5528219.083009999 8.5 328062.5769999996 5528218.872020001 8.5 328062.5769999996 5528218.872020001 0 328054.4609999992 5528219.083009999 0 + + + + + + + + + 328054.00298999995 5528198.384 0 328054.00298999995 5528198.384 8.5 328054.4609999992 5528219.083009999 8.5 328054.4609999992 5528219.083009999 0 328054.00298999995 5528198.384 0 + + + + + + + + + 328054.8479900006 5528192.8129900005 0 328054.8479900006 5528192.8129900005 8.5 328054.00298999995 5528198.384 8.5 328054.00298999995 5528198.384 0 328054.8479900006 5528192.8129900005 0 + + + + + + + + + 328053.9679899998 5528187.125 0 328053.9679899998 5528187.125 8.5 328054.8479900006 5528192.8129900005 8.5 328054.8479900006 5528192.8129900005 0 328053.9679899998 5528187.125 0 + + + + + + + + + 328062.03800000064 5528186.91501 0 328062.03800000064 5528186.91501 8.5 328053.9679899998 5528187.125 8.5 328053.9679899998 5528187.125 0 328062.03800000064 5528186.91501 0 + + + + + + + + + 328061.9790100008 5528184.657989999 0 328061.9790100008 5528184.657989999 8.5 328062.03800000064 5528186.91501 8.5 328062.03800000064 5528186.91501 0 328061.9790100008 5528184.657989999 0 + + + + + + + + + 328065.91500999965 5528184.55601 0 328065.91500999965 5528184.55601 8.5 328061.9790100008 5528184.657989999 8.5 328061.9790100008 5528184.657989999 0 328065.91500999965 5528184.55601 0 + + + + + + + + + 328065.9790100008 5528187.00299 0 328065.9790100008 5528187.00299 8.5 328065.91500999965 5528184.55601 8.5 328065.91500999965 5528184.55601 0 328065.9790100008 5528187.00299 0 + + + + + + + + + 328074.1480100006 5528186.79001 0 328074.1480100006 5528186.79001 8.5 328065.9790100008 5528187.00299 8.5 328065.9790100008 5528187.00299 0 328074.1480100006 5528186.79001 0 + + + + + + + + + 328073.75799999945 5528197.550000001 0 328073.75799999945 5528197.550000001 8.5 328074.1480100006 5528186.79001 8.5 328074.1480100006 5528186.79001 0 328073.75799999945 5528197.550000001 0 + + + + + + + + + 328074.43600999936 5528201.869999999 0 328074.43600999936 5528201.869999999 8.5 328073.75799999945 5528197.550000001 8.5 328073.75799999945 5528197.550000001 0 328074.43600999936 5528201.869999999 0 + + + + + + + + + 328074.7179899998 5528218.65601 0 328074.7179899998 5528218.65601 8.5 328074.43600999936 5528201.869999999 8.5 328074.43600999936 5528201.869999999 0 328074.7179899998 5528218.65601 0 + + + + + + + + + 328066.37298999913 5528218.87403 0 328066.37298999913 5528218.87403 8.5 328074.7179899998 5528218.65601 8.5 328074.7179899998 5528218.65601 0 328066.37298999913 5528218.87403 0 + + + + + + + + + 328066.44198999926 5528221.52698 8.5 328062.64898999967 5528221.625979999 8.5 328062.5769999996 5528218.872020001 8.5 328054.4609999992 5528219.083009999 8.5 328054.00298999995 5528198.384 8.5 328054.8479900006 5528192.8129900005 8.5 328053.9679899998 5528187.125 8.5 328062.03800000064 5528186.91501 8.5 328061.9790100008 5528184.657989999 8.5 328065.91500999965 5528184.55601 8.5 328065.9790100008 5528187.00299 8.5 328074.1480100006 5528186.79001 8.5 328073.75799999945 5528197.550000001 8.5 328074.43600999936 5528201.869999999 8.5 328074.7179899998 5528218.65601 8.5 328066.37298999913 5528218.87403 8.5 328066.44198999926 5528221.52698 8.5 + + + + + + + + + \ No newline at end of file diff --git a/tests_data/lod2_buildings.gml b/tests_data/lod2_buildings.gml index 13bfa209..c4c50c93 100644 --- a/tests_data/lod2_buildings.gml +++ b/tests_data/lod2_buildings.gml @@ -795,20 +795,6 @@ - - - - - 296846.760109 5041420.154931 125.439000 296846.760109 5041420.154931 125.439074 - - - - - 296845.859131 5041420.726167 125.439000 296846.760109 5041420.154931 125.439000 296845.859131 5041420.726167 125.439000 - - - - @@ -3434,6 +3420,8 @@ + 2050 + W4 @@ -10207,6 +10195,8 @@ + 2050 + W4