• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

KU-Leuven-Geomatics / geomapi / 15188762246

22 May 2025 02:06PM UTC coverage: 46.454% (-0.6%) from 47.067%
15188762246

push

github

Jellevermandere
Documentaion and imageNode focallenght

3144 of 6768 relevant lines covered (46.45%)

0.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.44
geomapi/nodes/bimnode.py
1
"""
2
**BIMNode** is a Python Class to govern the data and metadata of BIM data. 
3
This node builds upon the [Open3D](https://www.open3d.org/) and [ifcopenshell](https://ifcopenshell.org/) API for the BIM definitions.
4
Be sure to check the properties defined in those abstract classes to initialise the Node.
5

6
This Node is an extension of the MeshNode so it inherits all mesh-based transformation and visualization functionality.
7

8
.. image:: ../../../docs/pics/graph_ifc1.png
9

10
**IMPORTANT**: The current BIMNode class is designed from a geospatial perspective to 
11
use in geometric analyses. As such, it's geometry is defined by Open3D.geometry.TriangleMesh objects 
12
and contains only a skeleton set of IFC Information. Users should use this class to conduct their analyses
13
and then combine it with the existing IFC files or IFCOWL RDF variants to integrate the results.
14

15
"""
16
#IMPORT PACKAGES
17
import os
1✔
18
from pathlib import Path
1✔
19
from typing import Optional
1✔
20
import open3d as o3d 
1✔
21
import numpy as np 
1✔
22
import ifcopenshell
1✔
23
import ifcopenshell.geom as geom
1✔
24
import ifcopenshell.util
1✔
25
import ifcopenshell.util.selector
1✔
26
import trimesh
1✔
27
import uuid
1✔
28
from rdflib import XSD, Graph, URIRef
1✔
29
from rdflib.namespace import RDF
1✔
30

31
#IMPORT MODULES
32
# from geomapi.nodes import GeometryNode
33
from geomapi.nodes.meshnode import MeshNode
1✔
34
import geomapi.utils as ut
1✔
35
from geomapi.utils import GEOMAPI_PREFIXES, rdf_property
1✔
36
import geomapi.utils.geometryutils as gmu
1✔
37

38
class BIMNode (MeshNode):
1✔
39
    def __init__(self,  
1✔
40
                subject: Optional[URIRef] = None,
41
                graph: Optional[Graph] = None,
42
                graphPath: Optional[Path] = None,
43
                name: Optional[str] = None,
44
                path: Optional[Path] = None,
45
                timestamp: Optional[str] = None,
46
                resource = None,
47
                cartesianTransform: Optional[np.ndarray] = None,
48
                orientedBoundingBox: Optional[o3d.geometry.OrientedBoundingBox] = None,
49
                convexHull: Optional[o3d.geometry.TriangleMesh] =None,
50
                loadResource: bool = False,
51
                ifcPath : Path = None,                        
52
                globalId : str = None,
53
                className : str = None,
54
                objectType : str = None,
55
                **kwargs): 
56
        """Creates a BIMNode. Overloaded function.
57
        
58
        This Node can be initialised from one or more of the inputs below.
59
        By default, no data is imported in the Node to speed up processing.
60
        If you also want the data, call node.get_resource() or set getResource() to True.
61
        
62
        **Warning**: never attach an IfcElement to a node directly as this is very unstable!
63

64
        Args:
65
            - subject (RDFlib URIRef) : subject to be used as the main identifier in the RDF Graph
66
            
67
            - graph (RDFlib Graph) : Graph with a single subject (if multiple subjects are present, only the first will be used to initialise the Node)
68
            
69
            - graphPath (Path) :  Graph file path with a single subject (if multiple subjects are present, only the first will be used to initialise the Node)
70
            
71
            - path (Path) : Path to mesh .obj or .ply file (data is not automatically loaded)
72
            
73
            - resource (o3d.geometry.TriangleMesh, ifcopenshell.entity_instance) : Open3D Triangle mesh data from trimesh, open3d or ifcopenshell. 
74
            
75
            - ifcPath (str|Path) : path to IFC file
76
            
77
            - globalId (str) : IFC globalId
78
            
79
            - className (str) : IFC className e.g. IfcWall, IfcBeam, IfcSlab, etc.
80
            
81
            - objectType (str) : IFC object type e.g.  i.e. Floor:232_FL_Concrete CIP 400mm
82
            
83
            - getResource (bool, optional= False) : If True, the node will search for its physical resource on drive 
84
                            
85
        Returns:
86
            BIMNode : A BIMNode with metadata 
87
        """           
88

89
        
90
        #set properties
91
        self.ifcPath=ifcPath
1✔
92
        self.globalId=globalId
1✔
93
        self.className=className
1✔
94
        self.objectType=objectType
1✔
95

96
        super().__init__(   subject = subject,
1✔
97
                            graph = graph,
98
                            graphPath = graphPath,
99
                            name = name,
100
                            path = path,
101
                            timestamp = timestamp,
102
                            resource = resource,
103
                            cartesianTransform = cartesianTransform,
104
                            orientedBoundingBox = orientedBoundingBox,
105
                            convexHull = convexHull,
106
                            loadResource = loadResource,
107
                            **kwargs) 
108
                     
109

110
#---------------------PROPERTIES----------------------------
111

112
    #---------------------ifcPath----------------------------
113
    @property
1✔
114
    @rdf_property(predicate= GEOMAPI_PREFIXES["ifc"].ifcPath ,datatype=XSD.string)
1✔
115
    def ifcPath(self): 
1✔
116
        """The path (Path) of the ifc file."""
117
        return self._ifcPath
1✔
118

119
    @ifcPath.setter
1✔
120
    def ifcPath(self,value:Path):
1✔
121
        if value is None:
1✔
122
           self._ifcPath = None
1✔
123
        elif Path(value).suffix.upper() in ut.get_node_resource_extensions(self):
1✔
124
            self._ifcPath=Path(value)
1✔
125
        else:
126
            raise ValueError('ifcPath invalid extension.')
×
127

128
    #---------------------globalId----------------------------
129
    @property
1✔
130
    @rdf_property(predicate= GEOMAPI_PREFIXES['ifc'].IfcGloballyUniqueId, datatype=XSD.string)
1✔
131
    def globalId(self): 
1✔
132
        """The GlobalId (str) of the node that originates from an ifc file."""
133
        #if self._globalId:
134
        #    pass 
135
        #elif self.ifcPath is not None and os.path.exists(self.ifcPath): # takes first element, not sure if this is good!
136
        #    ifc = ifcopenshell.open(self.ifcPath)
137
        #    print(ifc)
138
        #    ifcElement=next((ifcElement for ifcElement in ifcopenshell.util.selector.filter_elements(ifc,"IfcElement")),None)
139
        #    print(ifcElement)
140
        #    self._globalId=ifcElement.GlobalId if ifcElement else None
141
        #    print("checking if file exists")
142
        return self._globalId
1✔
143

144
    @globalId.setter
1✔
145
    def globalId(self,value:str):
1✔
146
        if value is None:
1✔
147
            self._globalId = None
1✔
148
        else:
149
            self._globalId=str(value)
1✔
150
            
151
    #---------------------className----------------------------
152
    @property
1✔
153
    @rdf_property(predicate= GEOMAPI_PREFIXES["ifc"].className)
1✔
154
    def className(self): 
1✔
155
        """The IFC className (str) of the node that originates from an ifc file. 
156
        
157
        **Note**: This must be a IFC formatted class name e.g. IfcWall, IfcBeam, IfcSlab, etc.
158
        """
159
       #if(self._className is None):
160
       #    self._className='IfcBuildingElement'
161
        return self._className
1✔
162

163
    @className.setter
1✔
164
    def className(self,value:str):
1✔
165
        if value is None:
1✔
166
            self._className = None
1✔
167
        else:
168
            self._className=str(value)
1✔
169
            
170
    #---------------------objectType----------------------------
171
    @property
1✔
172
    @rdf_property(predicate= GEOMAPI_PREFIXES['ifc'].objectType_IfcObject, datatype=XSD.string)
1✔
173
    def objectType(self): 
1✔
174
        """The IFC objectType (str) of the node that originates from an ifc file. 
175
        
176
        **Note**: In most software, this is the family or type name of the object, which contains information of the material composition i.e. Floor:232_FL_Concrete CIP 400mm
177
        """
178
        return self._objectType
1✔
179

180
    @objectType.setter
1✔
181
    def objectType(self,value:str):
1✔
182
        if value is None:
1✔
183
            self._objectType = None
1✔
184
        else:
185
            self._objectType=str(value)
1✔
186

187
#---------------------PROPERTY OVERRIDES----------------------------
188
    
189
    @MeshNode.resource.setter
1✔
190
    def resource(self,value):
1✔
191
        """Set self.resource (o3d.geometry.TriangleMesh) of the Node.
192

193
        Args:
194
            - o3d.geometry.TriangleMesh 
195
            - trimesh.base.Trimesh
196
            - ifcopenshell.entity_instance (this also sets the name, subject, etc.)
197

198
        Raises:
199
            ValueError: Resource must be ao3d.geometry.TriangleMesh, trimesh.base.Trimesh or ifcopenshell.entity_instance with len(mesh.triangles) >=1.
200
        """
201
        if(value is None):
1✔
202
            self._resource = None
1✔
203
        elif isinstance(value,o3d.geometry.TriangleMesh) and len(value.triangles) >=1:
1✔
204
            self._resource = value
1✔
205
        elif isinstance(value,trimesh.base.Trimesh):
1✔
206
            self._resource = value.as_open3d
×
207
        elif isinstance(value,ifcopenshell.entity_instance):
1✔
208
            self._resource= gmu.ifc_to_mesh(value)
1✔
209
            self.name=value.Name
1✔
210
            self.className=value.is_a()
1✔
211
            self.globalId=value.GlobalId
1✔
212
            self.objectType=value.ObjectType
1✔
213
            self.subject= self.name +'_'+self.globalId
1✔
214

215
        else:
216
            raise ValueError('Resource must be ao3d.geometry.TriangleMesh, trimesh.base.Trimesh or ifcopenshell.entity_instance with len(mesh.triangles) >=1')
1✔
217
    
218
#---------------------METHODS----------------------------
219

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc