forked from s_ranjbar/city_retrofit
Correct surface triangulation algorithms
This commit is contained in:
parent
b0ca46332c
commit
4c7eea5524
|
@ -69,6 +69,46 @@ class Polyhedron:
|
|||
for point in surface.points:
|
||||
print(point[0] - self.min_x, point[1] - self.min_y, point[2] - self.min_z)
|
||||
|
||||
@staticmethod
|
||||
def _get_regions(point_index, points_list):
|
||||
if point_index == 0:
|
||||
# first point in the polygon so the triangle is the points n-1, n, 0
|
||||
triangle_left = ' '.join(str(e) for e in [*points_list[len(points_list) - 6:], *points_list[0:3]])
|
||||
# remove point n
|
||||
rest_points_left = ' '.join(str(e) for e in [*points_list[:len(points_list) - 3]])
|
||||
elif point_index == 3:
|
||||
# second point in the polygon so the triangle is the points n, 0, 1
|
||||
triangle_left = ' '.join(str(e) for e in [*points_list[len(points_list) - 3:], *points_list[0:6]])
|
||||
# remove point 0
|
||||
rest_points_left = ' '.join(str(e) for e in [*points_list[3:]])
|
||||
else:
|
||||
# normal point index-2¸index-1, index
|
||||
triangle_left = ' '.join(str(e) for e in [*points_list[point_index - 6:point_index + 3]])
|
||||
# remove middle point (index - 1)
|
||||
rest_points_left = ' '.join(str(e) for e in [*points_list[0:point_index - 3], *points_list[point_index:]])
|
||||
if point_index < len(points_list) - 6:
|
||||
# normal point index, index+1, index+2
|
||||
triangle_right = ' '.join(str(e) for e in [*points_list[point_index:point_index + 9]])
|
||||
rest_points_right = ' '.join(str(e) for e in [*points_list[0:point_index + 3], *points_list[point_index + 6:]])
|
||||
elif point_index == (len(points_list) - 6):
|
||||
# last two points in the polygon so the triangle is the points n-1, n, 0
|
||||
triangle_right = ' '.join(str(e) for e in [*points_list[point_index:], *points_list[0:3]])
|
||||
rest_points_right = ' '.join(str(e) for e in [*points_list[:len(points_list - 3)]])
|
||||
else:
|
||||
# last point in the polygon so the triangle is n, 0, 1
|
||||
triangle_right = ' '.join(str(e) for e in [*points_list[len(points_list) - 3:], *points_list[0:6]])
|
||||
rest_points_right = ' '.join(str(e) for e in [*points_list[3:]])
|
||||
if len(triangle_right) < 3:
|
||||
print(f'error right!!!! {triangle_right}')
|
||||
if len(triangle_left) < 3:
|
||||
print(f'error right!!!! {triangle_left}')
|
||||
return (Surface(triangle_left, remove_last=False), Surface(rest_points_left, remove_last=False)), \
|
||||
(Surface(triangle_right, remove_last=False), Surface(rest_points_right, remove_last=False))
|
||||
|
||||
def _print(self, surface):
|
||||
for point in surface.points:
|
||||
print(point[0]-surface.min_x, point[1]-surface.min_y, point[2]-surface.min_z)
|
||||
|
||||
def _triangulate(self, surface):
|
||||
triangles = []
|
||||
triangles_count = len(surface.points) - 2
|
||||
|
@ -76,33 +116,43 @@ class Polyhedron:
|
|||
point_index = 0
|
||||
area = surface.area
|
||||
while len(triangles) < triangles_count:
|
||||
# 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
|
||||
rest_points = ' '.join(str(e) for e in [*points_list[0:point_index+3], *points_list[point_index+6:]])
|
||||
triangular_surface = Surface(triangle_points, remove_last=False)
|
||||
rest_surface = Surface(rest_points, remove_last=False)
|
||||
t_area = triangular_surface.area
|
||||
if t_area == 0 and self._geometry.almost_same_area(area, (t_area + rest_surface.area)):
|
||||
area = rest_surface.area
|
||||
triangles.append(triangular_surface)
|
||||
points_list = rest_surface.points_list
|
||||
if len(rest_surface.points) == 3:
|
||||
triangles.append(rest_surface)
|
||||
# get triangles and regions in both direction to find ears
|
||||
left_direction, right_direction = Polyhedron._get_regions(point_index, points_list)
|
||||
# todo: use enum to describe triangle or rest instead 0 1
|
||||
right_area = right_direction[0].area + right_direction[1].area
|
||||
left_area = left_direction[0].area + left_direction[1].area
|
||||
if self._geometry.almost_same_area(area, left_area):
|
||||
area = left_direction[1].area
|
||||
point_index = 0
|
||||
triangles.append(left_direction[0])
|
||||
points_list = left_direction[1].points_list
|
||||
elif self._geometry.almost_same_area(area, right_area):
|
||||
area = right_direction[1].area
|
||||
point_index = 0
|
||||
triangles.append(right_direction[0])
|
||||
points_list = right_direction[1].points_list
|
||||
else:
|
||||
point_index = point_index + 3
|
||||
if point_index > len(points_list):
|
||||
raise Exception('not ear found!')
|
||||
if len(points_list) == 9:
|
||||
# the rest point's are already a triangle
|
||||
triangles.append(Surface(points_list, remove_last=False))
|
||||
return triangles
|
||||
|
||||
@property
|
||||
def faces(self) -> [[int]]:
|
||||
|
||||
"""
|
||||
Polyhedron faces
|
||||
:return: [[int]]
|
||||
"""
|
||||
|
||||
if self._faces is None:
|
||||
self._faces = []
|
||||
|
||||
for surface in self._surfaces:
|
||||
|
||||
face = []
|
||||
points = surface.points
|
||||
if len(points) != 3:
|
||||
|
|
|
@ -198,19 +198,6 @@ class Surface:
|
|||
self._ground_polygon = None
|
||||
return self._ground_polygon
|
||||
|
||||
@property
|
||||
def area_geoms_class(self):
|
||||
"""
|
||||
Surface area in square meters
|
||||
:return: float
|
||||
"""
|
||||
if self._area is None:
|
||||
try:
|
||||
self._area = self.polygon.get_area()
|
||||
except AttributeError:
|
||||
self._area = 0
|
||||
return self._area
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
"""
|
||||
|
@ -230,15 +217,14 @@ class Surface:
|
|||
if alpha == 0:
|
||||
print('Warning: the area of a line or point cannot be calculated. Area = 0')
|
||||
return 0
|
||||
points_2d = self.rotate_surface_to_horizontal(self)
|
||||
polygon_2d = pn.Polygon(np.array(points_2d))
|
||||
horizontal_points = self.rotate_surface_to_horizontal(self)
|
||||
area = 0
|
||||
for i in range(0, len(polygon_2d.points)-1):
|
||||
point = polygon_2d.points[i]
|
||||
next_point = polygon_2d.points[i+1]
|
||||
for i in range(0, len(horizontal_points)-1):
|
||||
point = horizontal_points[i]
|
||||
next_point = horizontal_points[i+1]
|
||||
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
|
||||
next_point = polygon_2d.points[0]
|
||||
point = polygon_2d.points[len(polygon_2d.points)-1]
|
||||
next_point = horizontal_points[0]
|
||||
point = horizontal_points[len(horizontal_points)-1]
|
||||
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
|
||||
self._area = abs(area)
|
||||
return self._area
|
||||
|
@ -247,14 +233,14 @@ class Surface:
|
|||
def rotate_surface_to_horizontal(surface):
|
||||
z_vector = [0, 0, 1]
|
||||
normal_vector = surface.normal
|
||||
points_2d = []
|
||||
horizontal_points = []
|
||||
x = normal_vector[0]
|
||||
y = normal_vector[1]
|
||||
|
||||
if x == 0 and y == 0:
|
||||
# Already horizontal
|
||||
for point in surface.points:
|
||||
points_2d.append([point[0], point[1], 0])
|
||||
horizontal_points.append([point[0], point[1], 0])
|
||||
else:
|
||||
alpha = surface.angle_between_vectors(normal_vector, z_vector)
|
||||
rotation_line = np.cross(normal_vector, z_vector)
|
||||
|
@ -274,8 +260,8 @@ class Surface:
|
|||
else:
|
||||
for point in surface.points:
|
||||
new_point = np.matmul(rotation_base_matrix, point)
|
||||
points_2d.append(new_point)
|
||||
return points_2d
|
||||
horizontal_points.append(new_point)
|
||||
return horizontal_points
|
||||
|
||||
@staticmethod
|
||||
def angle_between_vectors(vec_1, vec_2):
|
||||
|
|
|
@ -68,6 +68,7 @@ class CityGml:
|
|||
:return: City
|
||||
"""
|
||||
if self._city is None:
|
||||
# todo: refactor this method to clearly choose the gml type
|
||||
self._city = City(self._lower_corner, self._upper_corner, self._srs_name)
|
||||
i = 0
|
||||
for o in self._gml['CityModel']['cityObjectMember']:
|
||||
|
@ -79,10 +80,11 @@ class CityGml:
|
|||
surfaces = CityGml._lod1_solid(o)
|
||||
elif 'lod1MultiSurface' in o['Building']:
|
||||
lod += 1
|
||||
surfaces = CityGml._lod1_multisurface(o)
|
||||
surfaces = CityGml._lod1_multi_surface(o)
|
||||
elif 'lod2MultiSurface' in o['Building']:
|
||||
# todo: check if this is a real case or a miss-formed citygml
|
||||
lod = 2
|
||||
surfaces = surfaces + CityGml._lod2_multisurface(o)
|
||||
surfaces = surfaces + CityGml._lod2_solid_multi_surface(o)
|
||||
else:
|
||||
for bound in o['Building']['boundedBy']:
|
||||
surface_type = next(iter(bound))
|
||||
|
@ -133,27 +135,41 @@ class CityGml:
|
|||
return surfaces
|
||||
|
||||
@staticmethod
|
||||
def _lod1_multisurface(o):
|
||||
def _lod1_multi_surface(o):
|
||||
surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList'])
|
||||
for s in o['Building']['lod1MultiSurface']['MultiSurface']['surfaceMember']]
|
||||
return surfaces
|
||||
|
||||
@staticmethod
|
||||
def _lod2_multisurface(o):
|
||||
def _lod2_solid_multi_surface(o):
|
||||
surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList'])
|
||||
for s in o['Building']['lod2MultiSurface']['MultiSurface']['surfaceMember']]
|
||||
return surfaces
|
||||
|
||||
@staticmethod
|
||||
def _lod2_composite_surface(s):
|
||||
surfaces = [Surface(sm['Polygon']['exterior']['LinearRing']['posList'])
|
||||
for sm in s['CompositeSurface']['surfaceMember']]
|
||||
return surfaces
|
||||
|
||||
@staticmethod
|
||||
def _lod2_multi_surface(s, surface_type):
|
||||
# todo: this need to be changed into surface bounded?
|
||||
try:
|
||||
surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList']['#text'],
|
||||
surface_type=GeometryHelper.gml_surface_to_libs(surface_type))]
|
||||
except TypeError:
|
||||
surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList'],
|
||||
surface_type=GeometryHelper.gml_surface_to_libs(surface_type))]
|
||||
return surfaces
|
||||
|
||||
@staticmethod
|
||||
def _lod2(bound):
|
||||
surfaces = []
|
||||
for surface_type in iter(bound):
|
||||
try:
|
||||
surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList']['#text'],
|
||||
surface_type=GeometryHelper.gml_surface_to_libs(surface_type))
|
||||
for s in bound[surface_type]['lod2MultiSurface']['MultiSurface']['surfaceMember']]
|
||||
except TypeError:
|
||||
surfaces = [Surface(s['Polygon']['exterior']['LinearRing']['posList'],
|
||||
surface_type=GeometryHelper.gml_surface_to_libs(surface_type))
|
||||
for s in bound[surface_type]['lod2MultiSurface']['MultiSurface']['surfaceMember']]
|
||||
for s in bound[surface_type]['lod2MultiSurface']['MultiSurface']['surfaceMember']:
|
||||
if 'CompositeSurface' in s:
|
||||
surfaces = surfaces + CityGml._lod2_composite_surface(s)
|
||||
else:
|
||||
surfaces = surfaces + CityGml._lod2_multi_surface(s, surface_type)
|
||||
return surfaces
|
||||
|
|
Loading…
Reference in New Issue
Block a user