# ifccityjson - Python CityJSON to IFC converter # Copyright (C) 2021 Laurens J.N. Oostwegel # # This file is part of ifccityjson. # # ifccityjson is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ifccityjson is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ifccityjson. If not, see . import warnings class GeometryIO: def __init__(self): self.vertices = {} def build_vertices(self, IFC_model, coords, scale=None): for coord in coords: if scale: IFC_vertex = tuple([float(xyz) * coord_scale for xyz, coord_scale in zip(coord, scale)]) else: IFC_vertex = [float(xyz) for xyz in coord] IFC_cartesian_point = IFC_model.create_entity("IfcCartesianPoint", IFC_vertex) self.vertices[tuple(coord)] = IFC_cartesian_point # See for CityJSON geometries: # https://www.cityjson.org/dev/geom-arrays/ # https://www.cityjson.org/specs/1.0.3/#geometry-objects def create_IFC_geometry(self, IFC_model, geometry): IFC_Geometry = None geometry_type = 'brep' if geometry.type in ["MultiPoint"]: IFC_geometry = self.create_IFC_cartesian_point_list3D(IFC_model, geometry) geometry_type = 'PointCloud' elif geometry.type in ["MultiLineString"]: IFC_geometry = self.create_IFC_composite_curve(IFC_model, geometry) geometry_type = 'Curve3D' elif geometry.type in ["CompositeSurface", "MultiSurface"]: IFC_geometry = self.create_IFC_surface(IFC_model, geometry) elif geometry.type == "Solid": IFC_geometry = self.create_IFC_closed_shell(IFC_model, geometry) elif geometry.type in ["CompositeSolid", "MultiSolid"]: IFC_geometry = self.create_IFC_composite_closed_shell(IFC_model, geometry) elif geometry.type in ["GeometryInstance"]: warnings.warn("GeometryInstance is not supported.") return None, None else: warnings.warn("Custom CityJSON geometries are not supported.") return None, None return IFC_geometry, geometry_type def create_IFC_cartesian_point_list3D(self, IFC_model, geometry): # https://www.cityjson.org/dev/geom-arrays/ # https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcgeometricmodelresource/lexical/ifccartesianpointlist3d.htm IFC_geometry = IFC_model.create_entity("IfcCartesianPointList3D", geometry.boundaries) return IFC_geometry def create_IFC_composite_curve(self, IFC_model, geometry): # https://www.cityjson.org/dev/geom-arrays/ # https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcgeometryresource/lexical/ifccompositecurve.htm # https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/schema/ifcgeometryresource/lexical/ifccompositecurvesegment.htm IFC_geometry = [] for line in geometry.boundaries: vertices = [] for vertex in line: vertices.append(self.vertices[tuple(vertex)]) polyline = IFC_model.create_entity("IfcPolyLine", vertices) IFC_geometry.append(polyline) return IFC_geometry def create_IFC_composite_closed_shell(self, IFC_model, geometry): shells = [] for shell in geometry.boundaries: # exterior shell outershell = shell[0] faces = [] for face in outershell: faces.append(self.create_IFC_face(IFC_model, face)) shells.append(IFC_model.create_entity("IfcClosedShell", faces)) IFC_geometry = IFC_model.create_entity("IfcShellBasedSurfaceModel", shells) return IFC_geometry def create_IFC_closed_shell(self, IFC_model, geometry): # exterior shell outershell = geometry.boundaries[0] faces = [] for face in outershell: faces.append(self.create_IFC_face(IFC_model, face)) if len(geometry.boundaries) == 1: shell = IFC_model.create_entity("IfcClosedShell", faces) IFC_geometry = IFC_model.create_entity("IfcShellBasedSurfaceModel", [shell]) return IFC_geometry # TODO: INTERIOR SHELL warnings.warn("Solid interior shell not yet supported") return # for boundary in geometry.boundaries[1:]: # interior shells # for face in boundary: # for triangle in face: # print(triangle) def create_IFC_surface(self, IFC_model, geometry, surface_id=None): faces = None if surface_id is not None: face_ids = geometry.surfaces[surface_id]["surface_idx"] faces = list(map(lambda face_id: geometry.boundaries[face_id[0]][face_id[1]], face_ids)) else: faces = geometry.boundaries IFC_faces = [] for face in faces: IFC_faces.append(self.create_IFC_face(IFC_model, face)) shell = IFC_model.create_entity("IfcOpenShell", IFC_faces) IFC_geometry = IFC_model.create_entity("IfcShellBasedSurfaceModel", [shell]) return IFC_geometry def create_IFC_face(self, IFC_model, face): # exterior face vertices = [] for vertex in face[0]: vertices.append(self.vertices[tuple(vertex)]) polyloop = IFC_model.create_entity("IfcPolyLoop", vertices) outerbound = IFC_model.create_entity("IfcFaceOuterBound", polyloop, True) # return if only exterior face if len(face) == 1: return IFC_model.create_entity("IfcFace", [outerbound]) # interior face innerbounds = [] for interior_face in face[1:]: for vertex in interior_face: vertices.append(self.vertices[tuple(vertex)]) polyloop = IFC_model.create_entity("IfcPolyLoop", vertices) innerbounds.append(IFC_model.create_entity("IfcFaceBound", polyloop, True)) return IFC_model.create_entity("IfcFace", [outerbound] + innerbounds)