working version after creating polygon.py
This commit is contained in:
parent
70a02b28d4
commit
74732355d4
|
@ -15,18 +15,27 @@ class Polygon:
|
||||||
Polygon class
|
Polygon class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, vertices):
|
def __init__(self, points):
|
||||||
self._vertices = vertices
|
|
||||||
self._area = None
|
self._area = None
|
||||||
self._points = None
|
self._points = points
|
||||||
|
self._points_list = None
|
||||||
self._normal = None
|
self._normal = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def points(self) -> np.ndarray:
|
def points(self) -> np.ndarray:
|
||||||
if self._points is None:
|
|
||||||
self._points = self._vertices
|
|
||||||
return self._points
|
return self._points
|
||||||
|
|
||||||
|
@property
|
||||||
|
def points_list(self) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Solid surface point coordinates list [x, y, z, x, y, z,...]
|
||||||
|
:return: np.ndarray
|
||||||
|
"""
|
||||||
|
if self._points_list is None:
|
||||||
|
s = self.points
|
||||||
|
self._points_list = np.reshape(s, len(s) * 3)
|
||||||
|
return self._points_list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def area(self):
|
def area(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,18 +9,17 @@ import numpy as np
|
||||||
from trimesh import Trimesh
|
from trimesh import Trimesh
|
||||||
from helpers.geometry_helper import GeometryHelper
|
from helpers.geometry_helper import GeometryHelper
|
||||||
from helpers.configuration_helper import ConfigurationHelper
|
from helpers.configuration_helper import ConfigurationHelper
|
||||||
from city_model_structure.attributes.surface import Surface
|
from city_model_structure.attributes.polygon import Polygon
|
||||||
|
from helpers.geometry_helper import GeometryHelper as gh
|
||||||
|
|
||||||
|
|
||||||
class Polyhedron:
|
class Polyhedron:
|
||||||
# todo: modify class to call polygons better than surfaces
|
|
||||||
"""
|
"""
|
||||||
Polyhedron class
|
Polyhedron class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, surfaces):
|
def __init__(self, polygons):
|
||||||
self._surfaces = list(surfaces)
|
self._polygons = polygons
|
||||||
self._polygons = [s.polygon for s in surfaces]
|
|
||||||
self._polyhedron = None
|
self._polyhedron = None
|
||||||
self._triangulated_polyhedron = None
|
self._triangulated_polyhedron = None
|
||||||
self._volume = None
|
self._volume = None
|
||||||
|
@ -52,7 +51,7 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._vertices is None:
|
if self._vertices is None:
|
||||||
vertices, self._vertices = [], []
|
vertices, self._vertices = [], []
|
||||||
_ = [vertices.extend(s.points) for s in self._surfaces]
|
_ = [vertices.extend(s.points) for s in self._polygons]
|
||||||
for vertex_1 in vertices:
|
for vertex_1 in vertices:
|
||||||
found = False
|
found = False
|
||||||
for vertex_2 in self._vertices:
|
for vertex_2 in self._vertices:
|
||||||
|
@ -65,18 +64,14 @@ class Polyhedron:
|
||||||
self._vertices = np.asarray(self._vertices)
|
self._vertices = np.asarray(self._vertices)
|
||||||
return self._vertices
|
return self._vertices
|
||||||
|
|
||||||
@staticmethod
|
def _triangulate(self, polygon) -> [Polygon]:
|
||||||
def _point(coordinates):
|
|
||||||
return coordinates[0], coordinates[1], coordinates[2]
|
|
||||||
|
|
||||||
def _triangulate(self, surface):
|
|
||||||
"""
|
"""
|
||||||
triangulates a polygon following the ear clipping methodology
|
triangulates a polygon following the ear clipping methodology
|
||||||
:param surface: surface
|
:param polygon: polygon
|
||||||
:return: list[triangles]
|
:return: list[triangles]
|
||||||
"""
|
"""
|
||||||
points_list = surface.points_list
|
points_list = polygon.points_list
|
||||||
normal = surface.normal
|
normal = polygon.normal
|
||||||
# are points concave or convex?
|
# are points concave or convex?
|
||||||
total_points_list, concave_points, convex_points = self._starting_lists(points_list, normal)
|
total_points_list, concave_points, convex_points = self._starting_lists(points_list, normal)
|
||||||
|
|
||||||
|
@ -87,7 +82,7 @@ class Polyhedron:
|
||||||
ear = self._triangle(points_list, total_points_list, concave_points[i])
|
ear = self._triangle(points_list, total_points_list, concave_points[i])
|
||||||
rest_points = []
|
rest_points = []
|
||||||
for p in total_points_list:
|
for p in total_points_list:
|
||||||
rest_points.append(list(surface.points[p]))
|
rest_points.append(list(polygon.points[p]))
|
||||||
if self._is_ear(ear, rest_points):
|
if self._is_ear(ear, rest_points):
|
||||||
ears.append(ear)
|
ears.append(ear)
|
||||||
point_to_remove = concave_points[i]
|
point_to_remove = concave_points[i]
|
||||||
|
@ -115,8 +110,8 @@ class Polyhedron:
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
if len(total_points_list) <= 3 and len(convex_points) > 0:
|
if len(total_points_list) <= 3 and len(convex_points) > 0:
|
||||||
sys.stderr.write(f'Not able to triangulate surface type {surface.type}\n')
|
sys.stderr.write(f'Not able to triangulate polygon\n')
|
||||||
return [surface]
|
return [polygon]
|
||||||
last_ear = self._triangle(points_list, total_points_list, concave_points[1])
|
last_ear = self._triangle(points_list, total_points_list, concave_points[1])
|
||||||
ears.append(last_ear)
|
ears.append(last_ear)
|
||||||
return ears
|
return ears
|
||||||
|
@ -215,16 +210,18 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
is_concave = False
|
is_concave = False
|
||||||
accepted_error = 0.1
|
accepted_error = 0.1
|
||||||
points = ' '.join(str(e) for e in [*previous_point[:], *point[:], *next_point[:]])
|
points = np.append(previous_point, point)
|
||||||
triangle = Surface(points, remove_last=False)
|
points = np.append(points, next_point)
|
||||||
|
points = gh.to_points_matrix(points)
|
||||||
|
triangle = Polygon(points)
|
||||||
error_sum = 0
|
error_sum = 0
|
||||||
for i in range(0, len(normal)):
|
for i in range(0, len(normal)):
|
||||||
error_sum += triangle.perimeter_surface.normal[i] - normal[i]
|
error_sum += triangle.normal[i] - normal[i]
|
||||||
if np.abs(error_sum) < accepted_error:
|
if np.abs(error_sum) < accepted_error:
|
||||||
is_concave = True
|
is_concave = True
|
||||||
return is_concave
|
return is_concave
|
||||||
|
|
||||||
def _triangle(self, points_list, total_points_list, point_position):
|
def _triangle(self, points_list, total_points_list, point_position) -> Polygon:
|
||||||
"""
|
"""
|
||||||
creates a triangular polygon out of three points
|
creates a triangular polygon out of three points
|
||||||
:param points_list: points_list
|
:param points_list: points_list
|
||||||
|
@ -234,10 +231,11 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
index = point_position * 3
|
index = point_position * 3
|
||||||
previous_point_index, next_point_index = self._enveloping_points_indices(point_position, total_points_list)
|
previous_point_index, next_point_index = self._enveloping_points_indices(point_position, total_points_list)
|
||||||
points = ' '.join(str(e) for e in [*points_list[previous_point_index:previous_point_index + 3],
|
points = points_list[previous_point_index:previous_point_index + 3]
|
||||||
*points_list[index:index + 3],
|
points = np.append(points, points_list[index:index + 3])
|
||||||
*points_list[next_point_index:next_point_index + 3]])
|
points = np.append(points, points_list[next_point_index:next_point_index + 3])
|
||||||
triangle = Surface(points, remove_last=False)
|
points = gh.to_points_matrix(points)
|
||||||
|
triangle = Polygon(points)
|
||||||
return triangle
|
return triangle
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -288,11 +286,11 @@ class Polyhedron:
|
||||||
def _is_ear(ear, points) -> bool:
|
def _is_ear(ear, points) -> bool:
|
||||||
"""
|
"""
|
||||||
finds whether a triangle is an ear of the polygon
|
finds whether a triangle is an ear of the polygon
|
||||||
:param ear: surface
|
:param ear: polygon
|
||||||
:param points: [point]
|
:param points: [point]
|
||||||
:return: boolean
|
:return: boolean
|
||||||
"""
|
"""
|
||||||
area_ear = ear.perimeter_surface.area
|
area_ear = ear.area
|
||||||
for point in points:
|
for point in points:
|
||||||
area_points = 0
|
area_points = 0
|
||||||
point_is_not_vertex = True
|
point_is_not_vertex = True
|
||||||
|
@ -303,11 +301,16 @@ class Polyhedron:
|
||||||
if point_is_not_vertex:
|
if point_is_not_vertex:
|
||||||
for i in range(0, 3):
|
for i in range(0, 3):
|
||||||
if i != 2:
|
if i != 2:
|
||||||
new_points = ' '.join(str(e) for e in [*ear.points[i][:], *ear.points[i + 1][:], *point[:]])
|
new_points = ear.points[i][:]
|
||||||
|
new_points = np.append(new_points, ear.points[i + 1][:])
|
||||||
|
new_points = np.append(new_points, point[:])
|
||||||
else:
|
else:
|
||||||
new_points = ' '.join(str(e) for e in [*ear.points[i][:], *point[:], *ear.points[0][:]])
|
new_points = ear.points[i][:]
|
||||||
new_triangle = Surface(new_points, remove_last=False)
|
new_points = np.append(new_points, point[:])
|
||||||
area_points += new_triangle.perimeter_surface.area
|
new_points = np.append(new_points, ear.points[0][:])
|
||||||
|
new_points = gh.to_points_matrix(new_points)
|
||||||
|
new_triangle = Polygon(new_points)
|
||||||
|
area_points += new_triangle.area
|
||||||
if abs(area_points - area_ear) < 1e-6:
|
if abs(area_points - area_ear) < 1e-6:
|
||||||
# point_inside_ear = True
|
# point_inside_ear = True
|
||||||
return False
|
return False
|
||||||
|
@ -324,17 +327,17 @@ class Polyhedron:
|
||||||
if self._faces is None:
|
if self._faces is None:
|
||||||
self._faces = []
|
self._faces = []
|
||||||
|
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
|
|
||||||
face = []
|
face = []
|
||||||
points = surface.points
|
points = polygon.points
|
||||||
if len(points) != 3:
|
if len(points) != 3:
|
||||||
sub_surfaces = self._triangulate(surface)
|
sub_polygons = self._triangulate(polygon)
|
||||||
# todo: I modified this! To be checked @Guille
|
# todo: I modified this! To be checked @Guille
|
||||||
if len(sub_surfaces) >= 1:
|
if len(sub_polygons) >= 1:
|
||||||
for sub_surface in sub_surfaces:
|
for sub_polygon in sub_polygons:
|
||||||
face = []
|
face = []
|
||||||
points = sub_surface.points
|
points = sub_polygon.points
|
||||||
for point in points:
|
for point in points:
|
||||||
face.append(self._position_of(point, face))
|
face.append(self._position_of(point, face))
|
||||||
self._faces.append(face)
|
self._faces.append(face)
|
||||||
|
@ -371,8 +374,8 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._max_z is None:
|
if self._max_z is None:
|
||||||
self._max_z = ConfigurationHelper().min_coordinate
|
self._max_z = ConfigurationHelper().min_coordinate
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
for point in surface.points:
|
for point in polygon.points:
|
||||||
if self._max_z < point[2]:
|
if self._max_z < point[2]:
|
||||||
self._max_z = point[2]
|
self._max_z = point[2]
|
||||||
return self._max_z
|
return self._max_z
|
||||||
|
@ -385,8 +388,8 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._max_y is None:
|
if self._max_y is None:
|
||||||
self._max_y = ConfigurationHelper().min_coordinate
|
self._max_y = ConfigurationHelper().min_coordinate
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
for point in surface.points:
|
for point in polygon.points:
|
||||||
if self._max_y < point[1]:
|
if self._max_y < point[1]:
|
||||||
self._max_y = point[1]
|
self._max_y = point[1]
|
||||||
return self._max_y
|
return self._max_y
|
||||||
|
@ -399,8 +402,8 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._max_x is None:
|
if self._max_x is None:
|
||||||
self._max_x = ConfigurationHelper().min_coordinate
|
self._max_x = ConfigurationHelper().min_coordinate
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
for point in surface.points:
|
for point in polygon.points:
|
||||||
self._max_x = max(self._max_x, point[0])
|
self._max_x = max(self._max_x, point[0])
|
||||||
return self._max_x
|
return self._max_x
|
||||||
|
|
||||||
|
@ -412,8 +415,8 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._min_z is None:
|
if self._min_z is None:
|
||||||
self._min_z = self.max_z
|
self._min_z = self.max_z
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
for point in surface.points:
|
for point in polygon.points:
|
||||||
if self._min_z > point[2]:
|
if self._min_z > point[2]:
|
||||||
self._min_z = point[2]
|
self._min_z = point[2]
|
||||||
return self._min_z
|
return self._min_z
|
||||||
|
@ -426,8 +429,8 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._min_y is None:
|
if self._min_y is None:
|
||||||
self._min_y = self.max_y
|
self._min_y = self.max_y
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
for point in surface.points:
|
for point in polygon.points:
|
||||||
if self._min_y > point[1]:
|
if self._min_y > point[1]:
|
||||||
self._min_y = point[1]
|
self._min_y = point[1]
|
||||||
return self._min_y
|
return self._min_y
|
||||||
|
@ -440,8 +443,8 @@ class Polyhedron:
|
||||||
"""
|
"""
|
||||||
if self._min_x is None:
|
if self._min_x is None:
|
||||||
self._min_x = self.max_x
|
self._min_x = self.max_x
|
||||||
for surface in self._surfaces:
|
for polygon in self._polygons:
|
||||||
for point in surface.points:
|
for point in polygon.points:
|
||||||
if self._min_x > point[0]:
|
if self._min_x > point[0]:
|
||||||
self._min_x = point[0]
|
self._min_x = point[0]
|
||||||
return self._min_x
|
return self._min_x
|
||||||
|
|
|
@ -27,6 +27,7 @@ class Surface:
|
||||||
self._holes_points = None
|
self._holes_points = None
|
||||||
self._holes_points_list = None
|
self._holes_points_list = None
|
||||||
self._perimeter_points = None
|
self._perimeter_points = None
|
||||||
|
self._perimeter_points_list = None
|
||||||
self._azimuth = None
|
self._azimuth = None
|
||||||
self._inclination = None
|
self._inclination = None
|
||||||
self._area_above_ground = None
|
self._area_above_ground = None
|
||||||
|
@ -37,9 +38,9 @@ class Surface:
|
||||||
self._min_z = None
|
self._min_z = None
|
||||||
self._shared_surfaces = []
|
self._shared_surfaces = []
|
||||||
self._global_irradiance = dict()
|
self._global_irradiance = dict()
|
||||||
self._perimeter_surface = None
|
self._perimeter_polygon = None
|
||||||
self._hole_surfaces = None
|
self._hole_polygons = None
|
||||||
self._solid_surface = None
|
self._solid_polygons = None
|
||||||
|
|
||||||
def parent(self, parent, surface_id):
|
def parent(self, parent, surface_id):
|
||||||
"""
|
"""
|
||||||
|
@ -103,6 +104,37 @@ class Surface:
|
||||||
self._holes_points.append(hole_points)
|
self._holes_points.append(hole_points)
|
||||||
return self._holes_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
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def points_list(self) -> np.ndarray:
|
def points_list(self) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
|
@ -128,6 +160,17 @@ class Surface:
|
||||||
np.add(self._holes_points_list, hole_points_list)
|
np.add(self._holes_points_list, hole_points_list)
|
||||||
return self._holes_points_list
|
return self._holes_points_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def perimeter_points_list(self) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Solid surface point coordinates list [x, y, z, x, y, z,...]
|
||||||
|
:return: np.ndarray
|
||||||
|
"""
|
||||||
|
if self._perimeter_points_list is None:
|
||||||
|
s = self.perimeter_points
|
||||||
|
self._perimeter_points_list = np.reshape(s, len(s) * 3)
|
||||||
|
return self._perimeter_points_list
|
||||||
|
|
||||||
def _min_coord(self, axis):
|
def _min_coord(self, axis):
|
||||||
if axis == 'x':
|
if axis == 'x':
|
||||||
axis = 0
|
axis = 0
|
||||||
|
@ -180,7 +223,7 @@ class Surface:
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
if self._area_above_ground is None:
|
if self._area_above_ground is None:
|
||||||
self._area_above_ground = self.perimeter_surface.area - self.area_below_ground
|
self._area_above_ground = self.perimeter_polygon.area - self.area_below_ground
|
||||||
return self._area_above_ground
|
return self._area_above_ground
|
||||||
|
|
||||||
# todo: to be implemented
|
# todo: to be implemented
|
||||||
|
@ -199,7 +242,7 @@ class Surface:
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
if self._azimuth is None:
|
if self._azimuth is None:
|
||||||
normal = self.perimeter_surface.normal
|
normal = self.perimeter_polygon.normal
|
||||||
self._azimuth = np.arctan2(normal[1], normal[0])
|
self._azimuth = np.arctan2(normal[1], normal[0])
|
||||||
return self._azimuth
|
return self._azimuth
|
||||||
|
|
||||||
|
@ -210,7 +253,7 @@ class Surface:
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
if self._inclination is None:
|
if self._inclination is None:
|
||||||
self._inclination = np.arccos(self.perimeter_surface.normal[2])
|
self._inclination = np.arccos(self.perimeter_polygon.normal[2])
|
||||||
return self._inclination
|
return self._inclination
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -236,7 +279,7 @@ class Surface:
|
||||||
:param intersection_area:
|
:param intersection_area:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
percent = intersection_area / self.perimeter_surface.area
|
percent = intersection_area / self.perimeter_polygon.area
|
||||||
self._shared_surfaces.append((percent, surface))
|
self._shared_surfaces.append((percent, surface))
|
||||||
|
|
||||||
# todo reimplement
|
# todo reimplement
|
||||||
|
@ -267,27 +310,27 @@ class Surface:
|
||||||
self._global_irradiance = value
|
self._global_irradiance = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def perimeter_surface(self) -> Polygon:
|
def perimeter_polygon(self) -> Polygon:
|
||||||
"""
|
"""
|
||||||
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_surface is None:
|
if self._perimeter_polygon is None:
|
||||||
self._perimeter_surface = Polygon(self.perimeter_points)
|
self._perimeter_polygon = Polygon(self.perimeter_points)
|
||||||
return self._perimeter_surface
|
return self._perimeter_polygon
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def solid_surface(self) -> Polygon:
|
def solid_polygon(self) -> Polygon:
|
||||||
"""
|
"""
|
||||||
solid surface
|
solid surface
|
||||||
:return: Polygon
|
:return: Polygon
|
||||||
"""
|
"""
|
||||||
if self._solid_surface is None:
|
if self._solid_polygons is None:
|
||||||
self._solid_surface = Polygon(self.points)
|
self._solid_polygons = Polygon(self.points)
|
||||||
return self._solid_surface
|
return self._solid_polygons
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hole_surfaces(self) -> [Polygon]:
|
def hole_polygons(self) -> [Polygon]:
|
||||||
"""
|
"""
|
||||||
hole surfaces, a list of hole polygons found in the surface
|
hole surfaces, a list of hole polygons found in the surface
|
||||||
:return: None, [] or [Polygon]
|
:return: None, [] or [Polygon]
|
||||||
|
@ -295,42 +338,11 @@ 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._hole_surfaces is None:
|
if self._hole_polygons is None:
|
||||||
if self.holes_points is None:
|
if self.holes_points is None:
|
||||||
self._hole_surfaces = None
|
self._hole_polygons = None
|
||||||
else:
|
else:
|
||||||
self._hole_surfaces = []
|
self._hole_polygons = []
|
||||||
for hole_points in self.holes_points:
|
for hole_points in self.holes_points:
|
||||||
self._hole_surfaces.append(Polygon(hole_points))
|
self._hole_polygons.append(Polygon(hole_points))
|
||||||
return self._hole_surfaces
|
return self._hole_polygons
|
||||||
|
|
||||||
@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
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ class ThermalBoundary:
|
||||||
self._surface = surface
|
self._surface = surface
|
||||||
self._delimits = delimits
|
self._delimits = delimits
|
||||||
# ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary, review for LOD3 and LOD4
|
# ToDo: up to at least LOD2 will be just one thermal opening per Thermal boundary, review for LOD3 and LOD4
|
||||||
self._thermal_openings = [ThermalOpening]
|
self._thermal_openings = [ThermalOpening()]
|
||||||
self._layers = None
|
self._layers = None
|
||||||
self._outside_solar_absorptance = None
|
self._outside_solar_absorptance = None
|
||||||
self._outside_thermal_absorptance = None
|
self._outside_thermal_absorptance = None
|
||||||
|
@ -83,7 +83,7 @@ class ThermalBoundary:
|
||||||
Thermal boundary area in square meters
|
Thermal boundary area in square meters
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
return self._surface.area
|
return self._surface.solid_polygon.area
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def area_above_ground(self):
|
def area_above_ground(self):
|
||||||
|
|
|
@ -57,7 +57,7 @@ class ThermalZone:
|
||||||
self._floor_area = 0
|
self._floor_area = 0
|
||||||
for s in self._surfaces:
|
for s in self._surfaces:
|
||||||
if s.type == 'Ground':
|
if s.type == 'Ground':
|
||||||
self._floor_area += s.area
|
self._floor_area += s.perimeter_polygon.area
|
||||||
return self._floor_area
|
return self._floor_area
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -19,7 +19,8 @@ class CityObject:
|
||||||
self._name = name
|
self._name = name
|
||||||
self._lod = lod
|
self._lod = lod
|
||||||
self._surfaces = surfaces
|
self._surfaces = surfaces
|
||||||
self._polyhedron = None
|
self._detailed_polyhedron = None
|
||||||
|
self._simplified_polyhedron = None
|
||||||
self._geometry = GeometryHelper()
|
self._geometry = GeometryHelper()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -37,17 +38,36 @@ class CityObject:
|
||||||
City object volume in cubic meters
|
City object volume in cubic meters
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
return self.polyhedron.volume
|
return self.detailed_polyhedron.volume
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def polyhedron(self):
|
def detailed_polyhedron(self):
|
||||||
"""
|
"""
|
||||||
City object polyhedron
|
City object polyhedron including details such as holes
|
||||||
:return: Polyhedron
|
:return: Polyhedron
|
||||||
"""
|
"""
|
||||||
if self._polyhedron is None:
|
if self._detailed_polyhedron is None:
|
||||||
self._polyhedron = Polyhedron(self.surfaces)
|
polygons = []
|
||||||
return self._polyhedron
|
for surface in self.surfaces:
|
||||||
|
polygons.append(surface.solid_polygon)
|
||||||
|
if surface.hole_polygons is not None:
|
||||||
|
for hole_polygon in surface.hole_polygons:
|
||||||
|
polygons.append(hole_polygon)
|
||||||
|
self._detailed_polyhedron = Polyhedron(polygons)
|
||||||
|
return self._detailed_polyhedron
|
||||||
|
|
||||||
|
@property
|
||||||
|
def simplified_polyhedron(self):
|
||||||
|
"""
|
||||||
|
City object polyhedron, just the simple lod2 representation
|
||||||
|
:return: Polyhedron
|
||||||
|
"""
|
||||||
|
if self._simplified_polyhedron is None:
|
||||||
|
polygons = []
|
||||||
|
for surface in self.surfaces:
|
||||||
|
polygons.append(surface.perimeter_polygon)
|
||||||
|
self._simplified_polyhedron = Polyhedron(polygons)
|
||||||
|
return self._simplified_polyhedron
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def surfaces(self) -> List[Surface]:
|
def surfaces(self) -> List[Surface]:
|
||||||
|
@ -74,32 +94,7 @@ class CityObject:
|
||||||
City object location
|
City object location
|
||||||
:return: [x,y,z]
|
:return: [x,y,z]
|
||||||
"""
|
"""
|
||||||
if self._polyhedron is None:
|
return self.simplified_polyhedron.center
|
||||||
self._polyhedron = Polyhedron(self.surfaces)
|
|
||||||
return self._polyhedron.center
|
|
||||||
|
|
||||||
def stl_export(self, path):
|
|
||||||
"""
|
|
||||||
Export the city object to stl file (city_object_name.stl) to the given path
|
|
||||||
:param path: str
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
# todo: this is a method just for debugging, it will be soon removed
|
|
||||||
if self._polyhedron is None:
|
|
||||||
self._polyhedron = Polyhedron(self.surfaces)
|
|
||||||
full_path = (Path(path) / (self._name + '.stl')).resolve()
|
|
||||||
self._polyhedron.stl_export(full_path)
|
|
||||||
|
|
||||||
def obj_export(self, path):
|
|
||||||
if self._polyhedron is None:
|
|
||||||
self._polyhedron = Polyhedron(self.surfaces)
|
|
||||||
full_path = (Path(path) / (self._name + '.obj')).resolve()
|
|
||||||
self._polyhedron.obj_export(full_path)
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
if self._polyhedron is None:
|
|
||||||
self._polyhedron = Polyhedron(self.surfaces)
|
|
||||||
self._polyhedron.show()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_height(self):
|
def max_height(self):
|
||||||
|
@ -107,6 +102,4 @@ class CityObject:
|
||||||
City object maximal height in meters
|
City object maximal height in meters
|
||||||
:return: float
|
:return: float
|
||||||
"""
|
"""
|
||||||
if self._polyhedron is None:
|
return self.simplified_polyhedron.max_z
|
||||||
self._polyhedron = Polyhedron(self.surfaces)
|
|
||||||
return self._polyhedron.max_z
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ class CityGml:
|
||||||
terrains = []
|
terrains = []
|
||||||
for curve in curves:
|
for curve in curves:
|
||||||
curve_points = np.fromstring(curve, dtype=float, sep=' ')
|
curve_points = np.fromstring(curve, dtype=float, sep=' ')
|
||||||
curve_points = self._geometry.to_points_matrix(curve_points, True)
|
curve_points = self._geometry.to_points_matrix(curve_points)
|
||||||
terrains.append(curve_points)
|
terrains.append(curve_points)
|
||||||
return terrains
|
return terrains
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ class CityGml:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _lod1_multi_surface(o):
|
def _lod1_multi_surface(o):
|
||||||
surfaces = [Surface(CityGml._remove_last_point((s['Polygon']['exterior']['LinearRing']['posList']))
|
surfaces = [Surface(CityGml._remove_last_point(s['Polygon']['exterior']['LinearRing']['posList']))
|
||||||
for s in o['Building']['lod1MultiSurface']['MultiSurface']['surfaceMember']]
|
for s in o['Building']['lod1MultiSurface']['MultiSurface']['surfaceMember']]
|
||||||
return surfaces
|
return surfaces
|
||||||
|
|
||||||
|
@ -178,4 +178,4 @@ class CityGml:
|
||||||
def _remove_last_point(points):
|
def _remove_last_point(points):
|
||||||
array = points.split(' ')
|
array = points.split(' ')
|
||||||
res = " "
|
res = " "
|
||||||
return res.join(array[0:len(array) -3])
|
return res.join(array[0:len(array) - 3])
|
||||||
|
|
|
@ -85,7 +85,6 @@ class TestGeometryFactory(TestCase):
|
||||||
print('volume', building.name, building.volume)
|
print('volume', building.name, building.volume)
|
||||||
if str(building.volume) == 'inf':
|
if str(building.volume) == 'inf':
|
||||||
counter += 1
|
counter += 1
|
||||||
building.stl_export(self._output_path)
|
|
||||||
print('total number of buildings with volume inf', counter)
|
print('total number of buildings with volume inf', counter)
|
||||||
|
|
||||||
def test_citygml_buildings(self):
|
def test_citygml_buildings(self):
|
||||||
|
@ -123,7 +122,6 @@ class TestGeometryFactory(TestCase):
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
city = self._get_citygml()
|
city = self._get_citygml()
|
||||||
print(city)
|
|
||||||
for building in city.buildings:
|
for building in city.buildings:
|
||||||
for surface in building.surfaces:
|
for surface in building.surfaces:
|
||||||
self.assertIsNotNone(surface.name, 'surface name is none')
|
self.assertIsNotNone(surface.name, 'surface name is none')
|
||||||
|
@ -139,8 +137,6 @@ class TestGeometryFactory(TestCase):
|
||||||
self.assertIsNotNone(surface.min_x, 'surface min_x is none')
|
self.assertIsNotNone(surface.min_x, 'surface min_x is none')
|
||||||
self.assertIsNotNone(surface.min_y, 'surface min_y is none')
|
self.assertIsNotNone(surface.min_y, 'surface min_y is none')
|
||||||
self.assertIsNotNone(surface.min_z, 'surface min_z is none')
|
self.assertIsNotNone(surface.min_z, 'surface min_z is none')
|
||||||
print('points', surface.points)
|
|
||||||
print('coordinates', surface._coordinates)
|
|
||||||
|
|
||||||
def test_citygml_thermal_zone(self):
|
def test_citygml_thermal_zone(self):
|
||||||
"""
|
"""
|
||||||
|
@ -257,10 +253,10 @@ class TestGeometryFactory(TestCase):
|
||||||
print(surface.holes_points)
|
print(surface.holes_points)
|
||||||
print('perimeter:', surface.perimeter_points)
|
print('perimeter:', surface.perimeter_points)
|
||||||
for i in range(0, len(holes_coordinates)):
|
for i in range(0, len(holes_coordinates)):
|
||||||
print(surface.hole_surfaces[i].area)
|
print(surface.hole_polygons[i].area)
|
||||||
print('perimeter:', surface.perimeter_surface.area)
|
print('perimeter:', surface.perimeter_polygon.area)
|
||||||
print('solid:', surface.solid_surface.area)
|
print('solid:', surface.solid_polygon.area)
|
||||||
for i in range(0, len(holes_coordinates)):
|
for i in range(0, len(holes_coordinates)):
|
||||||
print(surface.hole_surfaces[i].normal)
|
print(surface.hole_polygons[i].normal)
|
||||||
print('perimeter:', surface.perimeter_surface.normal)
|
print('perimeter:', surface.perimeter_polygon.normal)
|
||||||
print('solid:', surface.solid_surface.normal)
|
print('solid:', surface.solid_polygon.normal)
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user