From ba735fb53d1b0783d8fc4234f02ee0f6d6793d97 Mon Sep 17 00:00:00 2001 From: Pilar Date: Tue, 6 Apr 2021 13:48:18 -0400 Subject: [PATCH] changes for new definition of surfaces (by polygons) and buildings (by polyhedrons) --- city_model_structure/attributes/surface.py | 96 ++++------------------ city_model_structure/building.py | 20 +++-- imports/geometry_feeders/citygml.py | 79 +++++++++++++++++- 3 files changed, 108 insertions(+), 87 deletions(-) diff --git a/city_model_structure/attributes/surface.py b/city_model_structure/attributes/surface.py index c52039d4..eaec993c 100644 --- a/city_model_structure/attributes/surface.py +++ b/city_model_structure/attributes/surface.py @@ -8,7 +8,6 @@ contributors Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca from __future__ import annotations import numpy as np import uuid -from helpers.geometry_helper import GeometryHelper as gh from city_model_structure.attributes.polygon import Polygon @@ -16,15 +15,13 @@ class Surface: """ Surface class """ - def __init__(self, coordinates, holes_coordinates=None, surface_type=None, name=None, swr=None): - self._coordinates = coordinates - self._holes_coordinates = holes_coordinates + def __init__(self, solid_polygon, perimeter_polygon, holes_polygons=None, surface_type=None, name=None, + swr=None, id_number=None): self._type = surface_type self._name = name + self._id = id_number self._swr = swr - self._points = None - self._holes_points = None - self._perimeter_points = None + self._azimuth = None self._inclination = None self._area_above_ground = None @@ -34,9 +31,9 @@ class Surface: self._envelope_upper_corner = None self._shared_surfaces = [] self._global_irradiance = dict() - self._perimeter_polygon = None - self._holes_polygons = None - self._solid_polygons = None + self._perimeter_polygon = perimeter_polygon + self._holes_polygons = holes_polygons + self._solid_polygons = solid_polygon def parent(self, parent, surface_id): """ @@ -58,6 +55,14 @@ class Surface: self._name = uuid.uuid4() return self._name + @property + def id(self): + """ + Surface id + :return: str + """ + return self._id + @property def swr(self): """ @@ -75,62 +80,6 @@ class Surface: """ self._swr = value - @property - def points(self) -> np.ndarray: - """ - Solid surface point matrix [[x, y, z],[x, y, z],...] - :return: np.ndarray - """ - if self._points is None: - self._points = np.fromstring(self._coordinates, dtype=float, sep=' ') - self._points = gh.to_points_matrix(self._points) - return self._points - - @property - def holes_points(self) -> [np.ndarray]: - """ - Holes surfaces point matrices [[[x, y, z],[x, y, z],...]] - :return: np.ndarray - """ - if self._holes_coordinates is not None: - self._holes_points = [] - for hole_coordinates in self._holes_coordinates: - hole_points = np.fromstring(hole_coordinates, dtype=float, sep=' ') - hole_points = gh.to_points_matrix(hole_points) - self._holes_points.append(hole_points) - return self._holes_points - - @property - def perimeter_points(self) -> np.ndarray: - """ - Matrix of points of the perimeter in the same order as in coordinates [[x, y, z],[x, y, z],...] - :return: np.ndarray - """ - if self._perimeter_points is None: - if self.holes_points is None: - self._perimeter_points = self.points - else: - _perimeter_coordinates = self._coordinates - for hole_points in self.holes_points: - _hole = np.append(hole_points, hole_points[0]) - _closed_hole = ' '.join(str(e) for e in [*_hole[:]]) - # add a mark 'M' to ensure that the recombination of points does not provoke errors in finding holes - _perimeter_coordinates = _perimeter_coordinates.replace(_closed_hole, 'M') - _perimeter_coordinates = _perimeter_coordinates.replace('M', '') - self._perimeter_points = np.fromstring(_perimeter_coordinates, dtype=float, sep=' ') - self._perimeter_points = gh.to_points_matrix(self._perimeter_points) - # remove duplicated points - pv = np.array([self._perimeter_points[0]]) - for point in self._perimeter_points: - duplicated_point = False - for p in pv: - if gh().almost_equal(0.0, p, point): - duplicated_point = True - if not duplicated_point: - pv = np.append(pv, [point], axis=0) - self._perimeter_points = pv - return self._perimeter_points - def _max_coord(self, axis): if axis == 'x': axis = 0 @@ -139,7 +88,7 @@ class Surface: else: axis = 2 max_coordinate = '' - for point in self.points: + for point in self.perimeter_polygon.points: if max_coordinate == '': max_coordinate = point[axis] elif max_coordinate < point[axis]: @@ -154,7 +103,7 @@ class Surface: else: axis = 2 min_coordinate = '' - for point in self.points: + for point in self.perimeter_polygon.points: if min_coordinate == '': min_coordinate = point[axis] elif min_coordinate > point[axis]: @@ -272,8 +221,6 @@ class Surface: total surface defined by the perimeter, merging solid and holes :return: Polygon """ - if self._perimeter_polygon is None: - self._perimeter_polygon = Polygon(self.perimeter_points) return self._perimeter_polygon @property @@ -282,8 +229,6 @@ class Surface: solid surface :return: Polygon """ - if self._solid_polygons is None: - self._solid_polygons = Polygon(self.points) return self._solid_polygons @property @@ -295,11 +240,4 @@ class Surface: [] -> no holes in the surface [Polygon] -> one or more holes in the surface """ - if self._holes_polygons is None: - if self.holes_points is None: - self._holes_polygons = None - else: - self._holes_polygons = [] - for hole_points in self.holes_points: - self._holes_polygons.append(Polygon(hole_points)) return self._holes_polygons diff --git a/city_model_structure/building.py b/city_model_structure/building.py index fe5ff362..54917a0f 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -24,7 +24,8 @@ class Building(CityObject): """ Building(CityObject) class """ - def __init__(self, name, lod, surfaces, terrains, year_of_construction, function, city_lower_corner): + def __init__(self, name, lod, surfaces, zones_surfaces_ids, terrains, year_of_construction, function, + city_lower_corner): super().__init__(lod, surfaces, name, city_lower_corner) self._basement_heated = None self._attic_heated = None @@ -44,11 +45,14 @@ class Building(CityObject): self._grounds = [] self._roofs = [] self._walls = [] - # ToDo: Check this for LOD4 + self._internal_walls = [] + self._thermal_zones = [] - if self.lod < 4: - # for lod under 4 is just one thermal zone - self._thermal_zones.append(ThermalZone(self.surfaces)) + for zone_surfaces_ids in zones_surfaces_ids: + zone_surfaces = [] + for surface_id in zone_surfaces_ids: + zone_surfaces.append(self.surface(surface_id)) + self._thermal_zones.append(ThermalZone(zone_surfaces)) for t_zones in self._thermal_zones: t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces] @@ -60,8 +64,10 @@ class Building(CityObject): self._grounds.append(surface) elif surface.type == 'Wall': self._walls.append(surface) - else: + elif surface.type == 'Roof': self._roofs.append(surface) + else: + self._internal_walls.append(surface) @property def grounds(self) -> [Surface]: @@ -287,7 +293,7 @@ class Building(CityObject): if self._eave_height is None: self._eave_height = 0 for wall in self.walls: - self._eave_height = max(self._eave_height, wall.max_z) + self._eave_height = max(self._eave_height, wall.envelope_upper_corner[2]) return self._eave_height @property diff --git a/imports/geometry_feeders/citygml.py b/imports/geometry_feeders/citygml.py index c8112234..18b5d9cc 100644 --- a/imports/geometry_feeders/citygml.py +++ b/imports/geometry_feeders/citygml.py @@ -5,12 +5,13 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc """ import numpy as np import xmltodict +import time from city_model_structure.city import City from city_model_structure.building import Building from city_model_structure.attributes.surface import Surface from helpers.geometry_helper import GeometryHelper -import time +from city_model_structure.attributes.polygon import Polygon class CityGml: @@ -182,3 +183,79 @@ class CityGml: array = points.split(' ') res = " " return res.join(array[0:len(array) - 3]) + + @staticmethod + def _solid_points(coordinates) -> np.ndarray: + """ + Solid surface point matrix [[x, y, z],[x, y, z],...] + :parameter coordinates: string from file + :return: np.ndarray + """ + solid_points = np.fromstring(coordinates, dtype=float, sep=' ') + solid_points = GeometryHelper.to_points_matrix(solid_points) + return solid_points + + @staticmethod + def _holes_points(holes_coordinates) -> [np.ndarray]: + """ + Holes surfaces point matrices [[[x, y, z],[x, y, z],...]] + :parameter holes_coordinates: strings from file + :return: [np.ndarray] + """ + holes_points = [] + for hole_coordinates in holes_coordinates: + hole_points = np.fromstring(hole_coordinates, dtype=float, sep=' ') + hole_points = GeometryHelper.to_points_matrix(hole_points) + holes_points.append(hole_points) + return holes_points + + @staticmethod + def _perimeter_points(coordinates, solid_points, holes_points) -> np.ndarray: + """ + Matrix of points of the perimeter in the same order as in coordinates [[x, y, z],[x, y, z],...] + :parameter coordinates: string from file + :parameter solid_points: np.ndarray points that define the solid part of a surface + :parameter holes_points: [np.ndarray] points that define each the holes in a surface + :return: np.ndarray + """ + if holes_points is None: + perimeter_points = solid_points + else: + _perimeter_coordinates = coordinates + for hole_points in holes_points: + _hole = np.append(hole_points, hole_points[0]) + _closed_hole = ' '.join(str(e) for e in [*_hole[:]]) + # add a mark 'M' to ensure that the recombination of points does not provoke errors in finding holes + _perimeter_coordinates = _perimeter_coordinates.replace(_closed_hole, 'M') + _perimeter_coordinates = _perimeter_coordinates.replace('M', '') + perimeter_points = np.fromstring(_perimeter_coordinates, dtype=float, sep=' ') + perimeter_points = GeometryHelper.to_points_matrix(perimeter_points) + # remove duplicated points + pv = np.array([perimeter_points[0]]) + for point in perimeter_points: + duplicated_point = False + for p in pv: + if GeometryHelper().almost_equal(0.0, p, point): + duplicated_point = True + if not duplicated_point: + pv = np.append(pv, [point], axis=0) + perimeter_points = pv + return perimeter_points + + @staticmethod + def _holes_polygons(holes_points) -> [Polygon]: + """ + hole surfaces, a list of hole polygons found in a surface + :parameter holes_points: [np.ndarray] that define each of the holes + :return: None, [] or [Polygon] + None -> not known whether holes exist in reality or not due to low level of detail of input data + [] -> no holes in the surface + [Polygon] -> one or more holes in the surface + """ + if holes_points is None: + holes_polygons = None + else: + holes_polygons = [] + for hole_points in holes_points: + holes_polygons.append(Polygon(hole_points)) + return holes_polygons