changes for new definition of surfaces (by polygons) and buildings (by polyhedrons)

This commit is contained in:
Pilar 2021-04-06 13:48:18 -04:00
parent bf0cda131e
commit ba735fb53d
3 changed files with 108 additions and 87 deletions

View File

@ -8,7 +8,6 @@ contributors Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
from __future__ import annotations from __future__ import annotations
import numpy as np import numpy as np
import uuid import uuid
from helpers.geometry_helper import GeometryHelper as gh
from city_model_structure.attributes.polygon import Polygon from city_model_structure.attributes.polygon import Polygon
@ -16,15 +15,13 @@ class Surface:
""" """
Surface class Surface class
""" """
def __init__(self, coordinates, holes_coordinates=None, surface_type=None, name=None, swr=None): def __init__(self, solid_polygon, perimeter_polygon, holes_polygons=None, surface_type=None, name=None,
self._coordinates = coordinates swr=None, id_number=None):
self._holes_coordinates = holes_coordinates
self._type = surface_type self._type = surface_type
self._name = name self._name = name
self._id = id_number
self._swr = swr self._swr = swr
self._points = None
self._holes_points = None
self._perimeter_points = None
self._azimuth = None self._azimuth = None
self._inclination = None self._inclination = None
self._area_above_ground = None self._area_above_ground = None
@ -34,9 +31,9 @@ class Surface:
self._envelope_upper_corner = None self._envelope_upper_corner = None
self._shared_surfaces = [] self._shared_surfaces = []
self._global_irradiance = dict() self._global_irradiance = dict()
self._perimeter_polygon = None self._perimeter_polygon = perimeter_polygon
self._holes_polygons = None self._holes_polygons = holes_polygons
self._solid_polygons = None self._solid_polygons = solid_polygon
def parent(self, parent, surface_id): def parent(self, parent, surface_id):
""" """
@ -58,6 +55,14 @@ class Surface:
self._name = uuid.uuid4() self._name = uuid.uuid4()
return self._name return self._name
@property
def id(self):
"""
Surface id
:return: str
"""
return self._id
@property @property
def swr(self): def swr(self):
""" """
@ -75,62 +80,6 @@ class Surface:
""" """
self._swr = value 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): def _max_coord(self, axis):
if axis == 'x': if axis == 'x':
axis = 0 axis = 0
@ -139,7 +88,7 @@ class Surface:
else: else:
axis = 2 axis = 2
max_coordinate = '' max_coordinate = ''
for point in self.points: for point in self.perimeter_polygon.points:
if max_coordinate == '': if max_coordinate == '':
max_coordinate = point[axis] max_coordinate = point[axis]
elif max_coordinate < point[axis]: elif max_coordinate < point[axis]:
@ -154,7 +103,7 @@ class Surface:
else: else:
axis = 2 axis = 2
min_coordinate = '' min_coordinate = ''
for point in self.points: for point in self.perimeter_polygon.points:
if min_coordinate == '': if min_coordinate == '':
min_coordinate = point[axis] min_coordinate = point[axis]
elif 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 total surface defined by the perimeter, merging solid and holes
:return: Polygon :return: Polygon
""" """
if self._perimeter_polygon is None:
self._perimeter_polygon = Polygon(self.perimeter_points)
return self._perimeter_polygon return self._perimeter_polygon
@property @property
@ -282,8 +229,6 @@ class Surface:
solid surface solid surface
:return: Polygon :return: Polygon
""" """
if self._solid_polygons is None:
self._solid_polygons = Polygon(self.points)
return self._solid_polygons return self._solid_polygons
@property @property
@ -295,11 +240,4 @@ class Surface:
[] -> no holes in the surface [] -> no holes in the surface
[Polygon] -> one or more 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 return self._holes_polygons

View File

@ -24,7 +24,8 @@ class Building(CityObject):
""" """
Building(CityObject) class 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) super().__init__(lod, surfaces, name, city_lower_corner)
self._basement_heated = None self._basement_heated = None
self._attic_heated = None self._attic_heated = None
@ -44,11 +45,14 @@ class Building(CityObject):
self._grounds = [] self._grounds = []
self._roofs = [] self._roofs = []
self._walls = [] self._walls = []
# ToDo: Check this for LOD4 self._internal_walls = []
self._thermal_zones = [] self._thermal_zones = []
if self.lod < 4: for zone_surfaces_ids in zones_surfaces_ids:
# for lod under 4 is just one thermal zone zone_surfaces = []
self._thermal_zones.append(ThermalZone(self.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: for t_zones in self._thermal_zones:
t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces] t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces]
@ -60,8 +64,10 @@ class Building(CityObject):
self._grounds.append(surface) self._grounds.append(surface)
elif surface.type == 'Wall': elif surface.type == 'Wall':
self._walls.append(surface) self._walls.append(surface)
else: elif surface.type == 'Roof':
self._roofs.append(surface) self._roofs.append(surface)
else:
self._internal_walls.append(surface)
@property @property
def grounds(self) -> [Surface]: def grounds(self) -> [Surface]:
@ -287,7 +293,7 @@ class Building(CityObject):
if self._eave_height is None: if self._eave_height is None:
self._eave_height = 0 self._eave_height = 0
for wall in self.walls: 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 return self._eave_height
@property @property

View File

@ -5,12 +5,13 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
""" """
import numpy as np import numpy as np
import xmltodict import xmltodict
import time
from city_model_structure.city import City from city_model_structure.city import City
from city_model_structure.building import Building from city_model_structure.building import Building
from city_model_structure.attributes.surface import Surface from city_model_structure.attributes.surface import Surface
from helpers.geometry_helper import GeometryHelper from helpers.geometry_helper import GeometryHelper
import time from city_model_structure.attributes.polygon import Polygon
class CityGml: class CityGml:
@ -182,3 +183,79 @@ class CityGml:
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 _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