first attempt to new triangulate function in polyhedron.py

This commit is contained in:
Pilar 2021-01-13 16:41:45 -05:00
parent 14d4a1a4b8
commit 1fccac8f4c
5 changed files with 501 additions and 124 deletions

View File

@ -65,87 +65,62 @@ class Polyhedron:
def _point(coordinates): def _point(coordinates):
return coordinates[0], coordinates[1], coordinates[2] return coordinates[0], coordinates[1], coordinates[2]
@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:]])
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 _triangulate(self, surface): def _triangulate(self, surface):
print(surface.type)
triangles = [] triangles = []
complementary_surface = surface
triangles_count = len(surface.points) - 2 triangles_count = len(surface.points) - 2
points_list = surface.points_list points_list = surface.points_list
point_index = 0 normal = surface.normal
area = surface.area concave_points = []
while len(triangles) < triangles_count: convex_points = []
# get triangles and regions in both direction to find ears # case 1: first point as center ear
left_direction, right_direction = Polyhedron._get_regions(point_index, points_list) points = ' '.join(str(e) for e in [*points_list[len(points_list) - 3:], *points_list[0:6]])
# todo: use enum to describe triangle or rest instead 0 1 triangle = Surface(points, remove_last=False)
right_area = right_direction[0].area + right_direction[1].area if self._point_is_concave(normal, triangle):
left_area = left_direction[0].area + left_direction[1].area concave_points.append(points_list[0:3])
if self._geometry.almost_same_area(area, left_area) and self._geometry.almost_same_area(area, right_area):
# Both seems to be an ear, choose the more precise
if np.abs(left_area-area) < np.abs(right_area-area):
area = left_direction[1].area
point_index = 0
triangles.append(left_direction[0])
points_list = left_direction[1].points_list
complementary_surface = left_direction[1]
else: else:
area = right_direction[1].area convex_points.append(points_list[0:3])
point_index = 0 # case 2: all points except first and last
triangles.append(right_direction[0]) for i in range(0, int((len(points_list)-6)/3)):
points_list = right_direction[1].points_list points = ' '.join(str(e) for e in [*points_list[i*3:(i+3)*3]])
complementary_surface = right_direction[1] triangle = Surface(points, remove_last=False)
elif self._geometry.almost_same_area(area, left_area): if self._point_is_concave(normal, triangle):
area = left_direction[1].area concave_points.append(points_list[(i+1)*3:(i+2)*3])
point_index = 0
triangles.append(left_direction[0])
points_list = left_direction[1].points_list
complementary_surface = left_direction[1]
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
complementary_surface = right_direction[1]
else: else:
point_index = point_index + 3 convex_points.append(points_list[(i+1)*3:(i+2)*3])
if point_index >= len(points_list): # case 3: last point as center ear
return triangles points = ' '.join(str(e) for e in [*points_list[len(points_list) - 6:], *points_list[0:3]])
if len(points_list) == 9: triangle = Surface(points, remove_last=False)
# the rest point's are already a triangle if self._point_is_concave(normal, triangle):
triangles.append(complementary_surface) concave_points.append(points_list[len(points_list) - 3:])
else:
convex_points.append(points_list[len(points_list) - 3:])
# print('point list', points_list)
print('concave', concave_points)
print('convex', convex_points)
# todo: recursive function, not good solution
# for point in concave_points:
# is_ear_point = self._is_ear_point(point)
# if is_ear_point:
# ear = self._extract_ear()
# triangles.append(ear)
# self._remove_point()
# continue
return triangles return triangles
def _point_is_concave(self, normal, triangle) -> bool:
is_concave = False
accepted_error = 0.1
error_sum = 0
print('normal', normal)
print('normal triangle', triangle.normal)
for i in range(0, len(normal)):
error_sum += triangle.normal[i] - normal[i]
if np.abs(error_sum) < accepted_error:
is_concave = True
return is_concave
@property @property
def faces(self) -> [[int]]: def faces(self) -> [[int]]:

View File

@ -211,17 +211,17 @@ class Surface:
# New method to calculate area # New method to calculate area
if self._area is None: if self._area is None:
if len(self.points) < 3: if len(self.points) < 3:
sys.stderr.write('Warning: the area of a line or point cannot be calculated. Area = 0\n') sys.stderr.write('Warning: the area of a line or point cannot be calculated 1. Area = 0\n')
return 0 return 0
alpha = 0 alpha = 0
vec_1 = self.points[1] - self.points[0] vec_1 = self.points[1] - self.points[0]
for i in range(2, len(self.points)): for i in range(2, len(self.points)):
vec_2 = self.points[i] - self.points[0] vec_2 = self.points[i] - self.points[0]
alpha += self.angle_between_vectors(vec_1, vec_2) alpha += GeometryHelper.angle_between_vectors(vec_1, vec_2)
if alpha == 0: if alpha == 0:
sys.stderr.write('Warning: the area of a line or point cannot be calculated. Area = 0\n') sys.stderr.write('Warning: the area of a line or point cannot be calculated 2. Area = 0\n')
return 0 return 0
horizontal_points = self.rotate_surface_to_horizontal(self) horizontal_points = self.rotate_surface_to_horizontal
area = 0 area = 0
for i in range(0, len(horizontal_points)-1): for i in range(0, len(horizontal_points)-1):
point = horizontal_points[i] point = horizontal_points[i]
@ -233,48 +233,6 @@ class Surface:
self._area = abs(area) self._area = abs(area)
return self._area return self._area
@staticmethod
def rotate_surface_to_horizontal(surface):
z_vector = [0, 0, 1]
normal_vector = surface.normal
horizontal_points = []
x = normal_vector[0]
y = normal_vector[1]
if x == 0 and y == 0:
# Already horizontal
for point in surface.points:
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)
third_axis = np.cross(normal_vector, rotation_line)
w_1 = rotation_line / np.linalg.norm(rotation_line)
w_2 = normal_vector
w_3 = third_axis / np.linalg.norm(third_axis)
rotation_matrix = np.array([[1, 0, 0],
[0, math.cos(alpha), -math.sin(alpha)],
[0, math.sin(alpha), math.cos(alpha)]])
base_matrix = np.array([w_1, w_2, w_3])
rotation_base_matrix = np.matmul(base_matrix.transpose(), rotation_matrix.transpose())
rotation_base_matrix = np.matmul(rotation_base_matrix, base_matrix)
if rotation_base_matrix is None:
sys.stderr.write('Warning: rotation base matrix returned None\n')
else:
for point in surface.points:
new_point = np.matmul(rotation_base_matrix, point)
horizontal_points.append(new_point)
return horizontal_points
@staticmethod
def angle_between_vectors(vec_1, vec_2):
if np.linalg.norm(vec_1) == 0 or np.linalg.norm(vec_2) == 0:
sys.stderr.write("Warning: impossible to calculate angle between planes' normal. Return 0\n")
return 0
alpha = math.acos(np.dot(vec_1, vec_2) / np.linalg.norm(vec_1) / np.linalg.norm(vec_2))
return alpha
def _is_almost_same_terrain(self, terrain_points, ground_points): def _is_almost_same_terrain(self, terrain_points, ground_points):
equal = 0 equal = 0
for terrain_point in terrain_points: for terrain_point in terrain_points:
@ -320,7 +278,35 @@ class Surface:
""" """
if self._normal is None: if self._normal is None:
points = self.points points = self.points
cross_product = np.cross(points[1] - points[0], points[2] - points[0]) accepted_error = 0.01
cross_product = np.cross(points[len(points)-1] - points[len(points)-2],
points[0] - points[len(points)-2])
cross_product_next = np.cross(points[0] - points[len(points)-2],
points[1] - points[len(points)-2])
error_sum = 0
for j in range(0, 3):
error_sum += cross_product[j] - cross_product_next[j]
if np.abs(error_sum) < accepted_error:
clockwise = 1
counter_clockwise = 0
else:
clockwise = 0
counter_clockwise = 1
for i in range(0, len(points)-2):
cross_product_next = np.cross(points[i+1] - points[len(points)-2], points[i+2] - points[len(points)-2])
error_sum = 0
for j in range(0, 3):
error_sum += cross_product[j] - cross_product_next[j]
if np.abs(error_sum) < accepted_error:
clockwise += 1
else:
counter_clockwise += 1
if clockwise < counter_clockwise:
cross_product = np.cross(points[0] - points[len(points) - 2],
points[len(points) - 1] - points[len(points) - 2])
else:
cross_product = np.cross(points[len(points) - 1] - points[len(points) - 2],
points[0] - points[len(points) - 2])
self._normal = cross_product / np.linalg.norm(cross_product) self._normal = cross_product / np.linalg.norm(cross_product)
return self._normal return self._normal
@ -495,3 +481,38 @@ class Surface:
self._is_planar = False self._is_planar = False
break break
return self._is_planar return self._is_planar
@property
def rotate_surface_to_horizontal(self):
z_vector = [0, 0, 1]
normal_vector = self.normal
horizontal_points = []
x = normal_vector[0]
y = normal_vector[1]
if x == 0 and y == 0:
# Already horizontal
for point in self.points:
horizontal_points.append([point[0], point[1], 0])
else:
alpha = GeometryHelper.angle_between_vectors(normal_vector, z_vector)
rotation_line = np.cross(normal_vector, z_vector)
third_axis = np.cross(normal_vector, rotation_line)
w_1 = rotation_line / np.linalg.norm(rotation_line)
w_2 = normal_vector
w_3 = third_axis / np.linalg.norm(third_axis)
rotation_matrix = np.array([[1, 0, 0],
[0, math.cos(alpha), -math.sin(alpha)],
[0, math.sin(alpha), math.cos(alpha)]])
base_matrix = np.array([w_1, w_2, w_3])
rotation_base_matrix = np.matmul(base_matrix.transpose(), rotation_matrix.transpose())
rotation_base_matrix = np.matmul(rotation_base_matrix, base_matrix)
if rotation_base_matrix is None:
sys.stderr.write('Warning: rotation base matrix returned None\n')
else:
for point in self.points:
new_point = np.matmul(rotation_base_matrix, point)
horizontal_points.append(new_point)
return horizontal_points

View File

@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
Contributors Pilar Monsalvete pilar_monsalvete@yahoo.es Contributors Pilar Monsalvete pilar_monsalvete@yahoo.es
""" """
import sys
import math import math
import numpy as np import numpy as np
import open3d as o3d import open3d as o3d
@ -18,7 +19,7 @@ class GeometryHelper:
Geometry helper class Geometry helper class
""" """
def __init__(self, delta=0.5, area_delta=0.5): def __init__(self, delta=0, area_delta=0):
self._delta = delta self._delta = delta
self._area_delta = area_delta self._area_delta = area_delta
@ -252,3 +253,12 @@ class GeometryHelper:
power += math.pow(vertex2[dimension]-vertex1[dimension], 2) power += math.pow(vertex2[dimension]-vertex1[dimension], 2)
distance = math.sqrt(power) distance = math.sqrt(power)
return distance return distance
@staticmethod
def angle_between_vectors(vec_1, vec_2):
if np.linalg.norm(vec_1) == 0 or np.linalg.norm(vec_2) == 0:
sys.stderr.write("Warning: impossible to calculate angle between planes' normal. Return 0\n")
return 0
alpha = math.acos(np.dot(vec_1, vec_2) / np.linalg.norm(vec_1) / np.linalg.norm(vec_2))
return alpha

View File

@ -238,12 +238,10 @@ class TestGeometryFactory(TestCase):
self.assertIsNone(thermal_opening.thickness, 'thermal_opening thickness_m was initialized') self.assertIsNone(thermal_opening.thickness, 'thermal_opening thickness_m was initialized')
self.assertRaises(Exception, lambda: thermal_opening.u_value, 'thermal_opening u_value was initialized') self.assertRaises(Exception, lambda: thermal_opening.u_value, 'thermal_opening u_value was initialized')
def test_is_planar(self): def tests_with_building_bld100087(self):
building = 'bld100087.gml' building = 'bld100087.gml'
file_path = (self._example_path / building).resolve() file_path = (self._example_path / building).resolve()
city = GeometryFactory('citygml', file_path).city city = GeometryFactory('citygml', file_path).city
for building in city.buildings: for building in city.buildings:
for surface in building.surfaces:
print(surface.type, surface.area)
print('volume', building.volume) print('volume', building.volume)

373
tests_data/bld100087.gml Normal file
View File

@ -0,0 +1,373 @@
<?xml version="1.0" encoding="UTF-8"?>
<core:CityModel xmlns:brid="http://www.opengis.net/citygml/bridge/2.0" xmlns:tran="http://www.opengis.net/citygml/transportation/2.0" xmlns:frn="http://www.opengis.net/citygml/cityfurniture/2.0" xmlns:wtr="http://www.opengis.net/citygml/waterbody/2.0" xmlns:sch="http://www.ascc.net/xml/schematron" xmlns:veg="http://www.opengis.net/citygml/vegetation/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:tun="http://www.opengis.net/citygml/tunnel/2.0" xmlns:tex="http://www.opengis.net/citygml/texturedsurface/2.0" xmlns:gml="http://www.opengis.net/gml" xmlns:gen="http://www.opengis.net/citygml/generics/2.0" xmlns:dem="http://www.opengis.net/citygml/relief/2.0" xmlns:app="http://www.opengis.net/citygml/appearance/2.0" xmlns:luse="http://www.opengis.net/citygml/landuse/2.0" xmlns:xAL="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:smil20lang="http://www.w3.org/2001/SMIL20/Language" xmlns:pbase="http://www.opengis.net/citygml/profiles/base/2.0" xmlns:smil20="http://www.w3.org/2001/SMIL20/" xmlns:bldg="http://www.opengis.net/citygml/building/2.0" xmlns:core="http://www.opengis.net/citygml/2.0" xmlns:grp="http://www.opengis.net/citygml/cityobjectgroup/2.0">
<gml:boundedBy>
<gml:Envelope srsName="EPSG:26911" srsDimension="3">
<gml:lowerCorner>326011.03601000085 5526048.416990001 -1.6000000000058208</gml:lowerCorner>
<gml:upperCorner>329466.6600299999 5529018.72205 9.80000000000291</gml:upperCorner>
</gml:Envelope>
</gml:boundedBy>
<core:cityObjectMember>
<bldg:Building gml:id="BLD100087">
<gen:doubleAttribute name="gross_floor_area">
<gen:value>148</gen:value>
</gen:doubleAttribute>
<gen:stringAttribute name="gross_floor_raea_unit">
<gen:value>m2</gen:value>
</gen:stringAttribute>
<bldg:function>residential</bldg:function>
<bldg:yearOfConstruction>2019</bldg:yearOfConstruction>
<bldg:measuredHeight>4.4</bldg:measuredHeight>
<bldg:storeysAboveGround>1</bldg:storeysAboveGround>
<bldg:lod2Solid>
<gml:Solid srsName="EPSG:26911" srsDimension="3">
<gml:exterior>
<gml:CompositeSurface>
<gml:surfaceMember xlink:href="#UUID_198440d7-58b8-4ced-b98c-0779cb2ff55a"/>
<gml:surfaceMember xlink:href="#UUID_4d08aa29-161e-4783-b666-a9cf54f17ed2"/>
<gml:surfaceMember xlink:href="#UUID_6aa9fc26-81b7-4a4a-9eef-94c2e29dd117"/>
<gml:surfaceMember xlink:href="#UUID_8c56fb9d-6445-47f2-9ca3-91236124d4c9"/>
<gml:surfaceMember xlink:href="#UUID_aa36ef6d-d3ff-4ab5-8b8e-74c0d6fe6604"/>
<gml:surfaceMember xlink:href="#UUID_b167d715-c48e-4c25-a9f3-881b9f1d613b"/>
<gml:surfaceMember xlink:href="#UUID_90edce0c-608d-4503-955e-7c3c87bc7819"/>
<gml:surfaceMember xlink:href="#UUID_a467a3d2-a034-4a5d-9a39-6fce7fc1ab50"/>
<gml:surfaceMember xlink:href="#UUID_bdfea08d-fb5a-4327-9b48-f710ce6f6789"/>
<gml:surfaceMember xlink:href="#UUID_5d12563d-612c-425c-928a-15bd998f1dc8"/>
<gml:surfaceMember xlink:href="#UUID_2dfd3d1c-bb96-4b44-b2c1-c2ab92a0efdc"/>
<gml:surfaceMember xlink:href="#UUID_14a5ada6-1a80-4e54-8b06-2a12114e3d2f"/>
<gml:surfaceMember xlink:href="#UUID_523c07e3-aaae-4751-a365-47253cd8cb4b"/>
<gml:surfaceMember xlink:href="#UUID_55476b92-53bb-4752-82fe-242bc87a5964"/>
<gml:surfaceMember xlink:href="#UUID_bad2a114-dd33-4b7e-92b2-a784d1d9d219"/>
<gml:surfaceMember xlink:href="#UUID_80657db5-9c5a-4e12-a286-4b3e80dd2b0e"/>
<gml:surfaceMember xlink:href="#UUID_8f847375-a145-4d35-89ce-d68668b9c803"/>
<gml:surfaceMember xlink:href="#UUID_a7b817b0-47f3-4979-b24d-9b0ff0b913bc"/>
<gml:surfaceMember xlink:href="#UUID_604d603b-0f93-4e45-89ab-cdf9817d9c41"/>
</gml:CompositeSurface>
</gml:exterior>
</gml:Solid>
</bldg:lod2Solid>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_f6c5cc74-6058-48ff-aab8-f600313fba3c">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_198440d7-58b8-4ced-b98c-0779cb2ff55a">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327900.0092300009 5527617.35561 3.73359000000346 327903.1820100006 5527620.4350000005 2.8999999999941792 327896.92998999916 5527620.528000001 2.8999999999941792 327900.0092300009 5527617.35561 3.73359000000346</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_4fb5405f-f9b2-4293-a5d1-4deacd80c578">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_4d08aa29-161e-4783-b666-a9cf54f17ed2">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327903.6182000004 5527610.50165 4.076969999994617 327904.02800000086 5527606.081 2.8999999999941792 327907.9790000003 5527606.880999999 2.8999999999941792 327903.6182000004 5527610.50165 4.076969999994617</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_ad005b29-d30c-4672-a35f-606feee45db6">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_6aa9fc26-81b7-4a4a-9eef-94c2e29dd117">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327906.6085899994 5527617.37174 3.30688000000373 327908.15699999966 5527618.875 2.8999999999941792 327905.10500000045 5527618.92 2.8999999999941792 327906.6085899994 5527617.37174 3.30688000000373</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_fdcf81d1-64a4-46a0-ac85-488c0cb4b2a6">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_8c56fb9d-6445-47f2-9ca3-91236124d4c9">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327903.1820100006 5527620.4350000005 0 327903.1820100006 5527620.4350000005 2.8999999999941792 327903.13698999956 5527617.434 2.8999999999941792 327903.13698999956 5527617.434 0 327903.1820100006 5527620.4350000005 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_d0932daa-c1d4-407b-97cc-27dc52b5ccdc">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_aa36ef6d-d3ff-4ab5-8b8e-74c0d6fe6604">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327896.92998999916 5527620.528000001 0 327896.92998999916 5527620.528000001 2.8999999999941792 327903.1820100006 5527620.4350000005 2.8999999999941792 327903.1820100006 5527620.4350000005 0 327896.92998999916 5527620.528000001 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_f270f436-f031-4eba-aab1-e8103a8df58d">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_b167d715-c48e-4c25-a9f3-881b9f1d613b">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327896.716 5527606.1899999995 0 327896.716 5527606.1899999995 2.8999999999941792 327896.92998999916 5527620.528000001 2.8999999999941792 327896.92998999916 5527620.528000001 0 327896.716 5527606.1899999995 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_e5d2a4f3-c54b-4a37-a288-9e167998845b">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_90edce0c-608d-4503-955e-7c3c87bc7819">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327904.02800000086 5527606.081 0 327904.02800000086 5527606.081 2.8999999999941792 327896.716 5527606.1899999995 2.8999999999941792 327896.716 5527606.1899999995 0 327904.02800000086 5527606.081 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_7ad45bd0-a344-4552-919f-3e4f6d500eef">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_a467a3d2-a034-4a5d-9a39-6fce7fc1ab50">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327907.9790000003 5527606.880999999 0 327907.9790000003 5527606.880999999 2.8999999999941792 327904.02800000086 5527606.081 2.8999999999941792 327904.02800000086 5527606.081 0 327907.9790000003 5527606.880999999 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_970df158-d016-440a-bc72-a2c25165cfdf">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_bdfea08d-fb5a-4327-9b48-f710ce6f6789">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327908.15699999966 5527618.875 0 327908.15699999966 5527618.875 2.8999999999941792 327907.9790000003 5527606.880999999 2.8999999999941792 327907.9790000003 5527606.880999999 0 327908.15699999966 5527618.875 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_2ba08389-cfe0-4d06-a46c-9450fca21094">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_5d12563d-612c-425c-928a-15bd998f1dc8">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327905.10500000045 5527618.92 0 327905.10500000045 5527618.92 2.8999999999941792 327908.15699999966 5527618.875 2.8999999999941792 327908.15699999966 5527618.875 0 327905.10500000045 5527618.92 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_941c7caa-24ef-4627-8e80-090db037ff2d">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_2dfd3d1c-bb96-4b44-b2c1-c2ab92a0efdc">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327905.08299 5527617.404999999 0 327905.08299 5527617.404999999 2.8999999999941792 327905.10500000045 5527618.92 2.8999999999941792 327905.10500000045 5527618.92 0 327905.08299 5527617.404999999 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:WallSurface gml:id="UUID_937ae52c-71f0-47f6-b41d-eafec9a85460">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_14a5ada6-1a80-4e54-8b06-2a12114e3d2f">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327903.13698999956 5527617.434 0 327903.13698999956 5527617.434 2.8999999999941792 327905.08299 5527617.404999999 2.8999999999941792 327905.08299 5527617.404999999 0 327903.13698999956 5527617.434 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:WallSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:GroundSurface gml:id="UUID_df682005-4d33-413b-b383-462b9d351055">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_523c07e3-aaae-4751-a365-47253cd8cb4b">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327903.13698999956 5527617.434 0 327905.08299 5527617.404999999 0 327905.10500000045 5527618.92 0 327908.15699999966 5527618.875 0 327907.9790000003 5527606.880999999 0 327904.02800000086 5527606.081 0 327896.716 5527606.1899999995 0 327896.92998999916 5527620.528000001 0 327903.1820100006 5527620.4350000005 0 327903.13698999956 5527617.434 0</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:GroundSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_eac4bd2b-96ad-4eb4-afde-e8a6f9b12031">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_55476b92-53bb-4752-82fe-242bc87a5964">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327903.1820100006 5527620.4350000005 2.8999999999941792 327900.0092300009 5527617.35561 3.73359000000346 327899.9643300008 5527614.354800001 3.733559999993304 327903.13698999956 5527617.434 2.8999999999941792 327903.1820100006 5527620.4350000005 2.8999999999941792</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_c3f74405-f236-4b14-87a5-fda67af05d3e">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_bad2a114-dd33-4b7e-92b2-a784d1d9d219">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327896.716 5527606.1899999995 2.8999999999941792 327902.4248399995 5527611.731040001 4.399999999994179 327902.4261399992 5527611.81845 4.399999999994179 327899.9643300008 5527614.354800001 3.733559999993304 327900.0092300009 5527617.35561 3.73359000000346 327896.92998999916 5527620.528000001 2.8999999999941792 327896.716 5527606.1899999995 2.8999999999941792</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_9b5cbd81-2d58-4c14-bcd6-7b53f4a1cfd7">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_80657db5-9c5a-4e12-a286-4b3e80dd2b0e">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327904.02800000086 5527606.081 2.8999999999941792 327903.6182000004 5527610.50165 4.076969999994617 327902.4248399995 5527611.731040001 4.399999999994179 327896.716 5527606.1899999995 2.8999999999941792 327904.02800000086 5527606.081 2.8999999999941792</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_2a766b32-03e3-43b8-9ed4-d1ce04ebf6e6">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_8f847375-a145-4d35-89ce-d68668b9c803">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327908.15699999966 5527618.875 2.8999999999941792 327906.6085899994 5527617.37174 3.30688000000373 327906.58634999953 5527615.85674 3.3068099999945844 327902.4261399992 5527611.81845 4.399999999994179 327902.4248399995 5527611.731040001 4.399999999994179 327903.6182000004 5527610.50165 4.076969999994617 327907.9790000003 5527606.880999999 2.8999999999941792 327908.15699999966 5527618.875 2.8999999999941792</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_8401655f-eb98-41c1-b684-34cc31b3f5f6">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_a7b817b0-47f3-4979-b24d-9b0ff0b913bc">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327905.08299 5527617.404999999 2.8999999999941792 327906.58634999953 5527615.85674 3.3068099999945844 327906.6085899994 5527617.37174 3.30688000000373 327905.10500000045 5527618.92 2.8999999999941792 327905.08299 5527617.404999999 2.8999999999941792</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
<bldg:boundedBy>
<bldg:RoofSurface gml:id="UUID_6b1b15ff-d73d-4443-9d1a-eb9e3d33c8cf">
<bldg:lod2MultiSurface>
<gml:MultiSurface srsName="EPSG:26911" srsDimension="3">
<gml:surfaceMember>
<gml:Polygon gml:id="UUID_604d603b-0f93-4e45-89ab-cdf9817d9c41">
<gml:exterior>
<gml:LinearRing>
<gml:posList>327903.13698999956 5527617.434 2.8999999999941792 327899.9643300008 5527614.354800001 3.733559999993304 327902.4261399992 5527611.81845 4.399999999994179 327906.58634999953 5527615.85674 3.3068099999945844 327905.08299 5527617.404999999 2.8999999999941792 327903.13698999956 5527617.434 2.8999999999941792</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:MultiSurface>
</bldg:lod2MultiSurface>
</bldg:RoofSurface>
</bldg:boundedBy>
</bldg:Building>
</core:cityObjectMember>
</core:CityModel>