import re import config import markup3dmodule as m3dm import polygon3dmodule as p3dm import json import numpy as np import open3d as o3d import open3d.core as o3c import os # Function to create triangles at each corner in 3D def create_corner_triangles(box_points, triangle_size=1): triangles = [] for i, point in enumerate(box_points): x, y, z = point if i == 0: # Bottom-left-front corner triangles.append([[x, y, z], [x + triangle_size, y, z], [x, y + triangle_size, z]]) elif i == 1: # Bottom-right-front corner triangles.append([[x, y, z], [x - triangle_size, y, z], [x, y + triangle_size, z]]) elif i == 2: # Top-left-front corner triangles.append([[x, y, z], [x + triangle_size, y, z], [x, y - triangle_size, z]]) elif i == 3: # Bottom-left-back corner triangles.append([[x, y, z], [x + triangle_size, y, z], [x, y + triangle_size, z]]) elif i == 4: # Top-right-back corner triangles.append([[x, y, z], [x - triangle_size, y, z], [x, y - triangle_size, z]]) elif i == 5: # Top-left-back corner triangles.append([[x, y, z], [x + triangle_size, y, z], [x, y - triangle_size, z]]) elif i == 6: # Bottom-right-back corner triangles.append([[x, y, z], [x - triangle_size, y, z], [x, y + triangle_size, z]]) elif i == 7: # Top-right-front corner triangles.append([[x, y, z], [x - triangle_size, y, z], [x, y - triangle_size, z]]) return triangles def addTranslationParameters(e, i, trans_param): # Convert lists to numpy arrays for easier manipulation e = np.array(e) # i = np.array(i) i_translated = [] trans_param = np.array(trans_param) # case distinction for empty translation parameters if len(trans_param) > 0: # Subtract the translation parameters from each point if len(e) > 0: e_translated = e - trans_param elif len(e) == 0: e_translated = np.asarray([]) # Iterate over all the different interior rings if len(i) > 0: for interior_ring in i: interior_ring = np.asarray(interior_ring) # Translate the interior ring interior_ring_translated = interior_ring - trans_param # Coollect the translated interior rings i_translated.append(interior_ring_translated.tolist()) else: i_translated = i return e_translated.tolist(), i_translated else: return e.tolist(), i def getBufferedBBoxPoints(b): # Schritt 1: identifying all wallsurfaces and roof surfaces of the building output = {} specifyVersion() # comprehensive list of semantic surfaces semanticSurfaces = ['GroundSurface', 'WallSurface', 'RoofSurface', 'ClosureSurface', 'CeilingSurface', ] for semanticSurface in semanticSurfaces: output[semanticSurface] = [] data = [] for cl in output: cls = [] for child in b.getiterator(): if child.tag == '{%s}%s' % (ns_bldg, cl): cls.append(child) for feature in cls: for p in feature.findall('.//{%s}Polygon' % ns_gml): e, i = m3dm.polydecomposer(p) epoints = m3dm.GMLpoints(e[0]) # -- Clean recurring points, except the last one last_ep = epoints[-1] epoints_clean = list(remove_reccuring(epoints)) epoints_clean.append(last_ep) for point in epoints_clean: data.append(point) # Schritt 2: Idetify the Bounding volume # 2.1 creating an open3d pointcloud from all the idetified vertex points pcd = o3d.t.geometry.PointCloud(o3c.Tensor(data, o3c.float32)) # 2.2 obtain the axis aligned boundign box of the point cloud axis_aligned_bb = pcd.get_axis_aligned_bounding_box() # Schritt 3: Construct small triangles that describe the boundign box sufficienly box_points = axis_aligned_bb.get_box_points().numpy().tolist() # Convert the list to a numpy array for easier manipulation box_points = np.array(box_points) # Calculate the min and max coordinates min_x, min_y, min_z = np.min(box_points, axis=0) max_x, max_y, max_z = np.max(box_points, axis=0) # Add a 3m buffer buffer = 3 min_x -= buffer min_y -= buffer min_z -= buffer max_x += buffer max_y += buffer max_z += buffer # Define the buffered bounding box points buffered_box_points = np.array([ [min_x, min_y, min_z], [max_x, min_y, min_z], [min_x, max_y, min_z], [min_x, min_y, max_z], [max_x, max_y, max_z], [min_x, max_y, max_z], [max_x, min_y, max_z], [max_x, max_y, min_z] ]) return buffered_box_points def obtainSRSInfo(root): specifyVersion() # obtain the envelope object envelopes = [] for envelope in root.getiterator('{%s}Envelope' % ns_gml): envelopes.append(envelope) # Extracting the srsName attribute from each Envelope srs_names = [envelope.get('srsName') for envelope in envelopes] srs_Dimensions = [envelope.get('srsDimension') for envelope in envelopes] return srs_names, srs_Dimensions # This function is used to create a corresponding json file defining the bbox of an object for each corresponding obj file def writeBBOXJSON(b, overall_counter, path, b_counter, trans_param): if len(trans_param) > 0: translation_parameters = { "d_x": str(trans_param[0]), "d_y": str(trans_param[1]), "d_z": str(trans_param[2]) } else: translation_parameters = { "d_x": str(0), "d_y": str(0), "d_z": str(0) } buffered_box_points_global = getBufferedBBoxPoints(b) # translate to the local coordinate system buffered_box_points, _ = addTranslationParameters(buffered_box_points_global, [], trans_param=trans_param) # From this set of points, obtain the minimum set that is necessary to describe the bounding box. min_point = buffered_box_points[0] max_point = buffered_box_points[4] # Construct the json file path json_file_path = str(path) + str(b_counter) + "_" + str(overall_counter) + "_bbox_" + ".json" # Write the bbox to a designated json file # Prüfen, ob die JSON-Datei existiert und laden if os.path.exists(json_file_path): with open(json_file_path, 'r') as json_file: axis_aligned_bbox = json.load(json_file) else: axis_aligned_bbox = {} # Neuen Identifier hinzufügen axis_aligned_bbox["axis_aligned_bbox"] = { "min_point": str(min_point), "max_point": str(max_point), "translation_parameters": translation_parameters } # Zuordnungen in JSON-Datei schreiben with open(json_file_path, 'w') as json_file: json.dump(axis_aligned_bbox, json_file, indent=4) return 0 # This function is used to add information about the used spatial reference system to the json file def addCRSToJSON(root, json_file_path): specifyVersion() # obtain the envelope object envelopes = [] for envelope in root.getiterator('{%s}Envelope' % ns_gml): envelopes.append(envelope) if envelopes: # Extracting the srsName attribute from each Envelope srs_names = [envelope.get('srsName') for envelope in envelopes] srs_Dimensions = [envelope.get('srsDimension') for envelope in envelopes] elif not envelopes: srs_names = "Unkown" srs_Dimensions = "Unkown" used_srs = srs_names[0] # Prüfen, ob die JSON-Datei existiert und laden if os.path.exists(json_file_path): with open(json_file_path, 'r') as json_file: crs_info = json.load(json_file) else: crs_info = {} # Neuen Identifier hinzufügen crs_info["CRS"] = { "srsName": used_srs, "srsDimensions": srs_Dimensions } # Zuordnungen in JSON-Datei schreiben with open(json_file_path, 'w') as json_file: json.dump(crs_info, json_file, indent=4) return 0 def claculateCornerTriangles(b, trans_param): buffered_box_points = getBufferedBBoxPoints(b) # Translate the Bounding box into the local coordinate system # buffered_box_points, _ = addTranslationParameters(buffered_box_points_global, [], trans_param=trans_param) # Create triangles at the corners of the buffered bounding box corner_triangles = create_corner_triangles(buffered_box_points) # Convert the triangles to lists corner_triangles = [np.array(triangle).tolist() for triangle in corner_triangles] print("\nCorner Triangles:") for i, triangle in enumerate(corner_triangles): print(f"Triangle {i + 1}: {triangle}") return corner_triangles # diese funktion dient dazu ein JSON file zu schreiben um die meta informationen über die einzelnen objekte zuspeichern def add_identifier_to_json(filename, tag, parentID, gmlID, json_file_path): """ Adds the identifier information for one .obj file to a JSON file. Parameters: - number (int): The number corresponding to the .obj file. - tag (str): The tag corresponding to the .obj file. - parentID (str): The parent ID corresponding to the .obj file. - gmlID (str): The gml ID corresponding to the .obj file. - json_file_path (str): Path to the JSON file where identifier information will be stored. """ # Prüfen, ob die JSON-Datei existiert und laden if os.path.exists(json_file_path): with open(json_file_path, 'r') as json_file: identifiers = json.load(json_file) else: identifiers = {} # Neuen Identifier hinzufügen identifiers[filename] = { 'tag': tag, 'parentID': parentID, 'gmlID': gmlID } # Zuordnungen in JSON-Datei schreiben with open(json_file_path, 'w') as json_file: json.dump(identifiers, json_file, indent=4) print(f"Zuordnung für {filename} wurde gespeichert.") def perturb_points(points, perturbation_scale=1e-6): """ Perturb the points slightly to avoid degenerate cases. Parameters: points (list of tuple of floats): A list where each element is a tuple (x, y, z) representing a 3D point. perturbation_scale (float): The maximum magnitude of the perturbation applied to each coordinate. Returns: list of list of floats: Perturbed list of points. """ points_array = np.array(points) perturbation = np.random.uniform(-perturbation_scale, perturbation_scale, points_array.shape) perturbed_points = points_array + perturbation return perturbed_points.tolist() def write_obj_file(surfaces, filename, tag, parentid, gmlid, counter, path, tr_1, translation_parameters): for triangle in tr_1: triangle_local, _ = addTranslationParameters(triangle, [], trans_param=translation_parameters) surfaces.append(triangle_local) with open(filename, 'w') as file: vertex_index = 1 for triangle in surfaces: for vertex in triangle: file.write(f"v {vertex[0]} {vertex[1]} {vertex[2]}\n") file.write(f"f {vertex_index} {vertex_index + 1} {vertex_index + 2}\n") vertex_index += 3 add_identifier_to_json(filename, tag, parentid, gmlid, (path + "index.json")) def remove_reccuring(list_vertices): """Removes recurring vertices, which messes up the triangulation. Inspired by http://stackoverflow.com/a/1143432""" # last_point = list_vertices[-1] list_vertices_without_last = list_vertices[:-1] found = set() for item in list_vertices_without_last: if str(item) not in found: yield item found.add(str(item)) def separate_string(s): # Define the regex pattern pattern = r'\{([^}]*)\}(.*)' # Search for the pattern in the input string match = re.search(pattern, s) if match: # Extract the parts inside_braces = match.group(1) outside_braces = match.group(2) return inside_braces, outside_braces else: return None, None def specifyVersion(): global ns_citygml global ns_gml global ns_bldg global ns_xsi global ns_xAL global ns_xlink global ns_dem global ns_con global ns_app global ns_pcl global ns_gen global ns_gss global ns_pfx0 global ns_gsr global ns_tran global ns_gmd global ns_gts global ns_veg global ns_frn global ns_tun global ns_wtr global nsmap if config.getVersion() == 1: # -- Name spaces for CityGML 2.0 ns_citygml = "http://www.opengis.net/citygml/1.0" ns_gml = "http://www.opengis.net/gml" ns_bldg = "http://www.opengis.net/citygml/building/1.0" ns_tran = "http://www.opengis.net/citygml/transportation/1.0" ns_veg = "http://www.opengis.net/citygml/vegetation/1.0" ns_gen = "http://www.opengis.net/citygml/generics/1.0" ns_xsi = "http://www.w3.org/2001/XMLSchema-instance" ns_xAL = "urn:oasis:names:tc:ciq:xsdschema:xAL:1.0" ns_xlink = "http://www.w3.org/1999/xlink" ns_dem = "http://www.opengis.net/citygml/relief/1.0" ns_frn = "http://www.opengis.net/citygml/cityfurniture/1.0" ns_tun = "http://www.opengis.net/citygml/tunnel/1.0" ns_wtr = "http://www.opengis.net/citygml/waterbody/1.0" ns_brid = "http://www.opengis.net/citygml/bridge/1.0" ns_app = "http://www.opengis.net/citygml/appearance/1.0" if config.getVersion() == 2: # -- Name spaces for CityGML 2.0 ns_citygml = "http://www.opengis.net/citygml/2.0" ns_gml = "http://www.opengis.net/gml" ns_bldg = "http://www.opengis.net/citygml/building/2.0" ns_xsi = "http://www.w3.org/2001/XMLSchema-instance" ns_xAL = "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" ns_xlink = "http://www.w3.org/1999/xlink" ns_dem = "http://www.opengis.net/citygml/relief/2.0" elif config.getVersion() == 3: # -- Name spaces for CityGML 3.0 ns_citygml = "http://www.opengis.net/citygml/3.0" ns_con = "http://www.opengis.net/citygml/construction/3.0" ns_xlink = "http://www.w3.org/1999/xlink" ns_gml = "http://www.opengis.net/gml/3.2" ns_bldg = "http://www.opengis.net/citygml/building/3.0" ns_app = "http://www.opengis.net/citygml/appearance/3.0" ns_pcl = "http://www.opengis.net/citygml/pointcloud/3.0" ns_gen = "http://www.opengis.net/citygml/generics/3.0" ns_gss = "http://www.isotc211.org/2005/gss" ns_pfx0 = "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" ns_gsr = "http://www.isotc211.org/2005/gsr" ns_xsi = "http://www.w3.org/2001/XMLSchema-instance" ns_tran = "http://www.opengis.net/citygml/transportation/3.0" ns_gmd = "http://www.isotc211.org/2005/gmd" ns_gts = "http://www.isotc211.org/2005/gts" ns_veg = "http://www.opengis.net/citygml/vegetation/3.0" ns_xAL = "urn:oasis:names:tc:ciq:xal:3" ns_dem = "http://www.opengis.net/citygml/relief/3.0" ns_frn = "http://www.opengis.net/citygml/cityfurniture/3.0" ns_tun = "http://www.opengis.net/citygml/tunnel/3.0" ns_wtr = "http://www.opengis.net/citygml/waterbody/3.0" nsmap = { None: ns_citygml, 'gml': ns_gml, 'bldg': ns_bldg, 'xsi': ns_xsi, 'xAL': ns_xAL, 'xlink': ns_xlink, 'dem': ns_dem } def compute_convex_hull(points): """ Computes the convex hull of a set of 3D points using Open3D, including triangulation of the hull's faces. Parameters: points (list of tuple of floats): A list where each element is a tuple (x, y, z) representing a 3D point. Returns: list: A list of faces, where each face is a list of vertex coordinates forming that face. """ # Convert the list of points to a NumPy array points_array = np.array(points) perturbed_points = perturb_points(points_array) # Create an Open3D PointCloud object pcd = o3d.geometry.PointCloud() pcd.points = o3d.utility.Vector3dVector(perturbed_points) # Compute the convex hull hull, triangles = pcd.compute_convex_hull() # Extract vertices and faces (triangles) hull_vertices = np.asarray(hull.vertices) hull_triangles = np.asarray(hull.triangles) # Prepare the faces in the desired format faces = [] for triangle in hull_triangles: face = [list(hull_vertices[vertex]) for vertex in triangle] faces.append(face) return faces def process_polygon(p, trans_param): e = p[0] i = p[1] e_trans, i_trans = addTranslationParameters(e, i, trans_param=trans_param) t = p3dm.triangulation(e_trans, i_trans) # print(f"t: {t}") return t def process_polygons_parallel(polys, trans_param): data = [] results = [] for poly in polys: e, i = m3dm.polydecomposer(poly) epoints = m3dm.GMLpoints(e[0]) # -- Clean recurring points, except the last one last_ep = epoints[-1] epoints_clean = list(remove_reccuring(epoints)) epoints_clean.append(last_ep) # -- LinearRing(s) forming the interior irings = [] for iring in i: ipoints = m3dm.GMLpoints(iring) # -- Clean them in the same manner as the exterior ring last_ip = ipoints[-1] ipoints_clean = list(remove_reccuring(ipoints)) ipoints_clean.append(last_ip) irings.append(ipoints_clean) if len(epoints_clean) > 4: t = process_polygon([epoints_clean, irings], trans_param=trans_param) results.append(t) # data.append(poly_components) if len(epoints_clean) == 4: epoints_clean_translated, _ = addTranslationParameters(epoints_clean, [], trans_param=trans_param) results.append([epoints_clean_translated]) # t = process_polygon(poly) # results.append(t) # cpu_cores = os.cpu_count() # print(f'Number of available CPU cores (using os): {cpu_cores}') # with ThreadPoolExecutor(max_workers=cpu_cores) as executor: # # Submitting all tasks # futures = [executor.submit(process_polygon, p) for p in data] # # Collecting results # for future in futures: # result = future.result() # This will re-raise any exception caught during the execution of the task # results.append(result) return results # this is an experimental method for parallelization def processOpening(o, path, buildingid, overall_counter, tr_1, trans_param, b_counter): for child in o.getiterator(): unique_identifier = child.xpath("@g:id", namespaces={'g': ns_gml}) if child.tag == '{%s}Window' % ns_bldg or child.tag == '{%s}Door' % ns_bldg: polys = m3dm.polygonFinder(o) t = process_polygons_parallel(polys, trans_param=trans_param) triangles = [] for poly in t: for tr in poly: triangles.append(tr) filename = path + str(b_counter) + "_" + str(overall_counter) + ".obj" write_obj_file(triangles, filename, str(child.tag), buildingid, unique_identifier, overall_counter, path, tr_1, trans_param) def getAllExteriorPoints(polys): data = [] for poly in polys: e, i = m3dm.polydecomposer(poly) epoints = m3dm.GMLpoints(e[0]) # -- Clean recurring points, except the last one last_ep = epoints[-1] epoints_clean = list(remove_reccuring(epoints)) epoints_clean.append(last_ep) for point in epoints_clean: data.append(point) return data def processWithApproximatedWindows(o, path, buildingid, overall_counter, tr_1, translation_parameters, b_counter): for child in o.getiterator(): unique_identifier = child.xpath("@g:id", namespaces={'g': ns_gml}) if child.tag == '{%s}Window' % ns_bldg or child.tag == '{%s}Door' % ns_bldg: polys = m3dm.polygonFinder(o) exterior_points = getAllExteriorPoints(polys) t_global = compute_convex_hull(exterior_points) _, t = addTranslationParameters(e=[], i=t_global, trans_param=translation_parameters) filename = path + str(b_counter) + "_" + str(overall_counter) + ".obj" write_obj_file(t, filename, str(child.tag), buildingid, unique_identifier, overall_counter, path, tr_1, translation_parameters=translation_parameters) # This function is used to im port a bounding box that is associated to the corresponding building component # It still has to be tested if it works def importBoundingBox(pathToBoundingBoxFile, trans_param): print("Hier") keys = ["_xmin", "_xmax", "_ymin", "_ymax", "_zmin", "_zmax"] values = {} with open(pathToBoundingBoxFile, 'r') as file: for _ in range(15): line = file.readline() for key in keys: match = re.search(f'"{key}"\s*:\s*([-0-9.]+)', line) if match: values[key] = float(match.group(1)) if not all(k in values for k in keys): raise ValueError("Missing bounding box values in the first 15 lines") min_x, max_x = values["_xmin"], values["_xmax"] min_y, max_y = values["_ymin"], values["_ymax"] min_z, max_z = values["_zmin"], values["_zmax"] print(f"min x: {min_x} min y: {min_y} , min z: {min_z}") print(f"max x: {max_x} max y: {max_y} , max z: {max_z}") box_points = np.array([ [min_x, min_y, min_z], [max_x, min_y, min_z], [min_x, max_y, min_z], [min_x, min_y, max_z], [max_x, max_y, max_z], [min_x, max_y, max_z], [max_x, min_y, max_z], [max_x, max_y, min_z] ]) # Create triangles at the corners of the buffered bounding box corner_triangles = create_corner_triangles(box_points) # Convert the triangles to lists corner_triangles = [np.array(triangle).tolist() for triangle in corner_triangles] print("\nCorner Triangles:") for i, triangle in enumerate(corner_triangles): print(f"Triangle {i + 1}: {triangle}") return corner_triangles def separateComponents(b, path, APPROXIMATEWINDOWS, ADDBOUNDINGBOX, ADDBOUNDINGBOXJSON, TRANSLATEBUILDINGS, IMPORTBOUNDINGBOX, b_counter): if TRANSLATEBUILDINGS: # Step 1: Obtain the axis oriented bounding box of the building bounding_box_points = getBufferedBBoxPoints(b) # Step 2 calculate the mean value of the points that the bbox points translation_parameters = np.mean(bounding_box_points, axis=0) if not TRANSLATEBUILDINGS: # todo: nocheinmal üerlegen ob man hier nicht vielleicht besser elif oder so nehmen sollte translation_parameters = [] if IMPORTBOUNDINGBOX != None: ADDBOUNDINGBOX = False # make sure that the bounding box is not calculated # Option to include the small triangles to mark the buffered bounding box if ADDBOUNDINGBOX: tr_1 = claculateCornerTriangles(b, trans_param=translation_parameters) elif not ADDBOUNDINGBOX: tr_1 = [] global overall_counter overall_counter = 0 output = {} specifyVersion() # comprehensive list of semantic surfaces semanticSurfaces = ['GroundSurface', 'WallSurface', 'RoofSurface', 'ClosureSurface', 'CeilingSurface', 'InteriorWallSurface', 'FloorSurface', 'OuterCeilingSurface', 'OuterFloorSurface', 'Door', "outerBuildingInstallation", 'Window', "BuildingInstallation", "BuildingConstructiveElement"] for semanticSurface in semanticSurfaces: output[semanticSurface] = [] # get the building id for the building buildingid = b.xpath("@g:id", namespaces={'g': ns_gml}) # Handle the case when a building has no gml:id - This should however never occur... if not buildingid: buildingid = b_counter # Todo: Muss noch implementiert werden # Import the bounding box if IMPORTBOUNDINGBOX != None: pathToBoundingBoxFile = IMPORTBOUNDINGBOX + "/" + str(buildingid) + ".json" tr_1 = importBoundingBox(pathToBoundingBoxFile=pathToBoundingBoxFile, trans_param=translation_parameters) if config.getVersion() != 3: openings = [] openingpolygons = [] for child in b.getiterator(): if child.tag == '{%s}opening' % ns_bldg: openings.append(child) for o in child.findall('.//{%s}Polygon' % ns_gml): openingpolygons.append(o) for o in openings: # print("approximate windows: ", APPROXIMATEWINDOWS) if APPROXIMATEWINDOWS: processWithApproximatedWindows(o, path, buildingid, overall_counter, tr_1=tr_1, translation_parameters=translation_parameters, b_counter=b_counter) if not APPROXIMATEWINDOWS: processOpening(o, path, buildingid, overall_counter, tr_1, trans_param=translation_parameters, b_counter=b_counter) if ADDBOUNDINGBOXJSON: writeBBOXJSON(b, overall_counter=overall_counter, path=path, b_counter=b_counter, trans_param=translation_parameters) overall_counter += 1 if config.getVersion() == 3: openingpolygons = [] print("Component separation for CityGML 3.0 is not implemented yet.") # todo: muss noch implementiert werden # -- Process other thematic boundaries for cl in output: cls = [] for child in b.getiterator(): if child.tag == '{%s}%s' % (ns_bldg, cl): cls.append(child) for feature in cls: # -- If it is the first feature, print the object identifier unique_identifier = feature.xpath("@g:id", namespaces={ 'g': ns_gml}) if str(unique_identifier) != "[]" or str(unique_identifier) == "[]": cleaned_filename = str(unique_identifier) # -- This is not supposed to happen, but just to be sure... if feature.tag == '{%s}Window' % ns_bldg or feature.tag == '{%s}Door' % ns_bldg: continue tag = feature.tag _, cleaned_tag = separate_string(tag) # -- Find all polygons in this semantic boundary hierarchy poly_t = [] t_ges = [] number_of_polygons = len(feature.findall('.//{%s}Polygon' % ns_gml)) pcounter = 0 print(f"there are {number_of_polygons} polygons there!") for p in feature.findall('.//{%s}Polygon' % ns_gml): found_opening = False for optest in openingpolygons: if p == optest: found_opening = True break # -- If there is an opening skip it if found_opening: pass else: # -- Decompose the polygon into exterior and interior e, i = m3dm.polydecomposer(p) # -- Points forming the exterior LinearRing epoints = m3dm.GMLpoints(e[0]) # -- Clean recurring points, except the last one last_ep = epoints[-1] epoints_clean = list(remove_reccuring(epoints)) epoints_clean.append(last_ep) # -- LinearRing(s) forming the interior irings = [] for iring in i: ipoints = m3dm.GMLpoints(iring) # -- Clean them in the same manner as the exterior ring last_ip = ipoints[-1] ipoints_clean = list(remove_reccuring(ipoints)) ipoints_clean.append(last_ip) irings.append(ipoints_clean) # Applying the translation parameters e_trans, i_trans = addTranslationParameters(e=epoints_clean, i=irings, trans_param=translation_parameters) try: if len(epoints_clean) > 4: t = p3dm.triangulation(e_trans, i_trans) # print("Nur drei Punkte") poly_t.append(t) if len(epoints_clean) == 4: t = e_trans[0:3] poly_t.append([t]) if len(epoints_clean) < 3: t = [] # print("Empty Surface!") except: t = [] for surfaces in poly_t: t_ges = t_ges + surfaces pcounter += 1 if pcounter % 100 == 0: print(pcounter) filename = path + str(b_counter) + "_" + str(overall_counter) + ".obj" if ADDBOUNDINGBOXJSON: writeBBOXJSON(b, overall_counter=overall_counter, path=path, b_counter=b_counter, trans_param=translation_parameters) write_obj_file(t_ges, filename, str(feature.tag), buildingid, cleaned_filename, overall_counter, path, tr_1, translation_parameters=translation_parameters) overall_counter += 1 print("Segmentation finished!") return 0