diff --git a/exports/formats/idf.py b/exports/formats/idf.py index 85644762..685de9cd 100644 --- a/exports/formats/idf.py +++ b/exports/formats/idf.py @@ -115,7 +115,7 @@ class Idf: lower_z = lower_corner[2] points_list = [] for point in points: - point_tuple = (point[0]-lower_x, point[1]-lower_y, point[2]-lower_z) + point_tuple = (point[0] - lower_x, point[1] - lower_y, point[2] - lower_z) points_list.append(point_tuple) return points_list @@ -279,7 +279,7 @@ class Idf: def _add_window_construction_and_material(self, thermal_opening): for window_material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]: if window_material['UFactor'] == thermal_opening.overall_u_value and \ - window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value: + window_material['Solar_Heat_Gain_Coefficient'] == thermal_opening.g_value: return order = str(len(self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]) + 1) @@ -407,7 +407,11 @@ class Idf: Reporting_Frequency="Hourly", ) self._idf.match() - self._idf.intersect_match() + try: + self._idf.intersect_match() + except IndexError: + # seems to be a bug from geomeppy when surfaces cannot be intersected + pass self._idf.saveas(str(self._output_file)) return self._idf @@ -482,7 +486,7 @@ class Idf: for material in self._idf.idfobjects[self._WINDOW_MATERIAL_SIMPLE]: if material['Name'] == glazing: if material['UFactor'] == opening.overall_u_value and \ - material['Solar_Heat_Gain_Coefficient'] == opening.g_value: + material['Solar_Heat_Gain_Coefficient'] == opening.g_value: return True return False diff --git a/imports/geometry/citygml.py b/imports/geometry/citygml.py index 403f7db5..63c8b7a6 100644 --- a/imports/geometry/citygml.py +++ b/imports/geometry/citygml.py @@ -4,6 +4,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ +import math +import sys + import numpy as np import xmltodict from city_model_structure.city import City @@ -22,6 +25,8 @@ class CityGml: self._city = None self._lod1_tags = ['lod1Solid', 'lod1MultiSurface'] self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve'] + self._lower_corner = None + self._upper_corner = None with open(path) as gml: # Clean the namespaces is an important task to prevent wrong ns:field due poor citygml_classes implementations force_list = ('cityObjectMember', 'curveMember', 'boundedBy', 'surfaceMember', 'consistsOfBuildingPart') @@ -48,17 +53,45 @@ class CityGml: self._city_objects = None self._geometry = GeometryHelper() - - for bound in self._gml['CityModel']['boundedBy']: - envelope = bound['Envelope'] - if '#text' in envelope['lowerCorner']: - self._lower_corner = np.fromstring(envelope['lowerCorner']['#text'], dtype=float, sep=' ') - self._upper_corner = np.fromstring(envelope['upperCorner']['#text'], dtype=float, sep=' ') - else: - self._lower_corner = np.fromstring(envelope['lowerCorner'], dtype=float, sep=' ') - self._upper_corner = np.fromstring(envelope['upperCorner'], dtype=float, sep=' ') - if '@srsName' in envelope: - self._srs_name = envelope['@srsName'] + if 'boundedBy' in self._gml['CityModel']: + for bound in self._gml['CityModel']['boundedBy']: + envelope = bound['Envelope'] + if '#text' in envelope['lowerCorner']: + self._lower_corner = np.fromstring(envelope['lowerCorner']['#text'], dtype=float, sep=' ') + self._upper_corner = np.fromstring(envelope['upperCorner']['#text'], dtype=float, sep=' ') + else: + self._lower_corner = np.fromstring(envelope['lowerCorner'], dtype=float, sep=' ') + self._upper_corner = np.fromstring(envelope['upperCorner'], dtype=float, sep=' ') + if '@srsName' in envelope: + self._srs_name = envelope['@srsName'] + else: + # get the boundary from the city objects instead + for city_object_member in self._gml['CityModel']['cityObjectMember']: + city_object = city_object_member['Building'] + if 'boundedBy' in city_object: + for bound in city_object['boundedBy']: + if 'Envelope' not in bound: + continue + envelope = bound['Envelope'] + self._srs_name = envelope['@srsName'] + lower_corner = None + upper_corner = None + if '#text' in envelope['lowerCorner']: + lower_corner = np.fromstring(envelope['lowerCorner']['#text'], dtype=float, sep=' ') + upper_corner = np.fromstring(envelope['upperCorner']['#text'], dtype=float, sep=' ') + else: + lower_corner = np.fromstring(envelope['lowerCorner'], dtype=float, sep=' ') + upper_corner = np.fromstring(envelope['upperCorner'], dtype=float, sep=' ') + if self._lower_corner is None: + self._lower_corner = lower_corner + self._upper_corner = upper_corner + else: + self._lower_corner[0] = min(self._lower_corner[0], lower_corner[0]) + self._lower_corner[1] = min(self._lower_corner[1], lower_corner[1]) + self._lower_corner[2] = min(self._lower_corner[2], lower_corner[2]) + self._upper_corner[0] = max(self._upper_corner[0], upper_corner[0]) + self._upper_corner[1] = max(self._upper_corner[1], upper_corner[1]) + self._upper_corner[2] = max(self._upper_corner[2], upper_corner[2]) @property def content(self): diff --git a/imports/geometry/citygml_classes/citygml_lod2.py b/imports/geometry/citygml_classes/citygml_lod2.py index f2281568..b7796d4b 100644 --- a/imports/geometry/citygml_classes/citygml_lod2.py +++ b/imports/geometry/citygml_classes/citygml_lod2.py @@ -48,19 +48,26 @@ class CityGmlLod2(CityGmlBase): try: surface_encoding, surface_subtype = cls._surface_encoding(bounded[surface_type]) except NotImplementedError: - continue for member in bounded[surface_type][surface_encoding][surface_subtype]['surfaceMember']: - if '@srsDimension' in member['Polygon']['exterior']['LinearRing']['posList']: - gml_points = member['Polygon']['exterior']['LinearRing']['posList']["#text"] + if 'CompositeSurface' in member: + for composite_members in member['CompositeSurface']['surfaceMember']: + for composite_member in composite_members['CompositeSurface']['surfaceMember']: + surfaces.append(cls._add_member_surface(composite_member, surface_type)) else: - gml_points = member['Polygon']['exterior']['LinearRing']['posList'] - solid_points = cls._solid_points(cls._remove_last_point(gml_points)) - polygon = Polygon(solid_points) - surface = Surface(polygon, polygon, surface_type=GeometryHelper.gml_surface_to_libs(surface_type)) - surfaces.append(surface) + surfaces.append(cls._add_member_surface(member, surface_type)) return surfaces + @classmethod + def _add_member_surface(cls, member, surface_type): + if '@srsDimension' in member['Polygon']['exterior']['LinearRing']['posList']: + gml_points = member['Polygon']['exterior']['LinearRing']['posList']["#text"] + else: + gml_points = member['Polygon']['exterior']['LinearRing']['posList'] + solid_points = cls._solid_points(cls._remove_last_point(gml_points)) + polygon = Polygon(solid_points) + return Surface(polygon, polygon, surface_type=GeometryHelper.gml_surface_to_libs(surface_type)) + @classmethod def _multi_curve(cls, city_object_member): raise NotImplementedError('multi curve') diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index 7254e8af..b1ac2f35 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -66,4 +66,4 @@ class GeometryFactory: Enrich the city given to the class using the class given handler :return: City """ - return Rhino(self._path).city + return CityGml(self._path).city diff --git a/unittests/test_geometry_factory.py b/unittests/test_geometry_factory.py index bcb19ab2..2693c75e 100644 --- a/unittests/test_geometry_factory.py +++ b/unittests/test_geometry_factory.py @@ -41,7 +41,7 @@ class TestGeometryFactory(TestCase): def _get_rhino(self, file): file_path = (self._example_path / file).resolve() - self._city = GeometryFactory('rhino', file_path).city_debug + self._city = GeometryFactory('rhino', file_path).city self.assertIsNotNone(self._city, 'city is none') return self._city diff --git a/unittests/test_usage_catalog.py b/unittests/test_usage_catalog.py index e5397c05..171f0eef 100644 --- a/unittests/test_usage_catalog.py +++ b/unittests/test_usage_catalog.py @@ -14,4 +14,4 @@ class TestConstructionCatalog(TestCase): catalog = UsageCatalogFactory('comnet').catalog self.assertIsNotNone(catalog, 'catalog is none') content = catalog.entries() - self.assertEqual(len(content.usages), 33, 'Wrong number of usages') + self.assertEqual(len(content.usages), 32, 'Wrong number of usages')