Create Graph from existing IFC models
Hi,
I would like to contribute to topologicpy and share a bit of code, but i have some question. I have put all the code at the end, for info.
My Problem
I want to create graph with existing model,, without relationship. The models doesn't contain relationship and they are split in different model. I have a model with all the building Space I have a model wit all the door I have models with all the electrical equipements, etc...
I haven't found a way to link object from ifc file directly.
Use Case
My main use case is to link IfcDoor to IfcSpace, in order to reconstruct the building Geometry, in an easy graph to read. As well, i can link object with the space there are in. For example, i can link two object together. The door is linked to the badge reader. The badge reader is linked to a camera, etc.. There is 3 cases:
- Door to Spaces (1 object linked to 2 objects)
- Object to Space (1 object linked to 1 object)
- Object to Object (1 object linked to 1 object)
My question
Is there an existing way, with topologicpy to achieve the same result ?
Are you interested by this piece of code ? Is there rule to contribute ?
In Graph.ByIFCFile there is already several sub-function to mix Topology and Ifc File like vertexByIFCObject, and the edge info between to IfcObject in edgesByIFCRelationships.
Should it be extracted from this function in order to make it more generic behaviour ?
My code is kinda decoraleted from Topologicpy but i am willing to integrate with all the function.
The search function is a little bit messy, with empiric check, but that's the only way i have found to make it work. Is the empiric check good ?
My code
def Graph_ObjectB_in_ObjetA(IfcFile_A,IfcFile_B,Objects_A,Objects_B,SearchNumber=[1],Graph=None,CoeffK=1):
"""
This function create a graph to link all the Object A and the Objects B by geometry distances. (The closest one).
:param IfcFile_A: An IfcOpenShell File, for the A object.
:param IfcFile_B: An IfcOpenShell File, for the B object.
:param Objects_A: A list of IfcOpenshell Object, from the A file
:param Objects_B: A list of IfcOpenshell Object, from the B file
:param SearchNumber: Numbre of edge to seach for. A door is linked to 2 space, a desk is linked to a single chair.
:param Graph: Networkx Graph to append the data
:param CoeffK: A value to change the precision and the size of the different metthod of search.
:return: Graph with new edge and new node
"""
import ifcopenshell
import networkx as nx
def Result_Analyze(Results,GUID_List,SearchNumber):
"""
:param Results: The result of the clash search
:param GUID_List: GUID list to ignore
:param SearchNumber: Number of result expected
:return: Bool, to determine if the search is succesful or not.
"""
NumberFound=0
for One_Result in Results:
if One_Result.GlobalId in GUID_List:
continue
else:
NumberFound=NumberFound+1
if NumberFound in SearchNumber:
return True
else:
return False
def Filter_Result(Results,GUID_List):
"""
You need to filter only the good result and ignore everything not expected.
:param LesResultat: List of result
:param GUID_List: List of GUID to ignore
:return: A filtered list of good result.
"""
Return_List=[]
for One_Result in Results:
if One_Result.GlobalId in GUID_List:
continue
else:
Return_List.append(One_Result)
return Return_List
def CreationNoeud(IfcObjects, G):
"""
Create all the node in the graph, by default for all the Objects A and Objects B.
:param IfcObjects: IfcObject to add to the node
:param G: The exist Graph
:return: The updated Graph
"""
for UnObjet in IfcObjects:
Dic = {"GUID": UnObjet.GlobalId, "IfcClass": UnObjet.is_a(), "IfcName": UnObjet.Name}
G.add_node(UnObjet.GlobalId, **Dic)
return G
#We create a graph if it does not exist yet.
if Graph is None:
G=nx.Graph()
else:
G=Graph
# Initialize the geometric settings
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_WORLD_COORDS, True)
tree_UB = ifcopenshell.geom.tree()
G = CreationNoeud(Objects_A, G) #I create all my nodes, from the A files.
G = CreationNoeud(Objects_B, G) #I create all my nodes, from the B files.
iterator_A = ifcopenshell.geom.iterator(settings, num_threads=8, file_or_filename=IfcFile_A, include=Objects_A)
if iterator_A.initialize():
while True:
#We append all the A objects in the tree. We do not need all the B object in the tree (for now)
tree_UB.add_element(iterator_A.get_native())
if not iterator_A.next():
break
Liste_Lien = []
GUID_List=[]
for B_Object in Objects_B:
GUID_List.append(B_Object.GlobalId)
Temp_Tree_UB = tree_UB #We duplicate a tempory tree to focus our search between one B object versus all A objects.
print("==============================================")
print(B_Object)
print()
# ============================== Select Object Placement, extend by 0.1
Placement = ifcopenshell.util.placement.get_local_placement(B_Object.ObjectPlacement)
X = float(Placement[0][3])
Y = float(Placement[1][3])
Z = float(Placement[2][3])
Tuple = (X, Y, Z)
Results = Temp_Tree_UB.select(Tuple, extend=0.1*CoeffK) #Does the object placement alone is enough to find a match.
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("I find with the 0.1 with the placement.")
FilteredResult = Filter_Result(Results, GUID_List) # I need to filter the result to only get meaningfull search.
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult]) #I append to my list of edge.
continue
# ============================== Select Object Placement, extend by 0.25
Placement = ifcopenshell.util.placement.get_local_placement(B_Object.ObjectPlacement)
X = float(Placement[0][3])
Y = float(Placement[1][3])
Z = float(Placement[2][3])
Tuple = (X, Y, Z)
Results = Temp_Tree_UB.select(Tuple, extend=0.25*CoeffK)
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("I find with the 0.25 with the placement.")
FilteredResult = Filter_Result(Results, GUID_List)
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult])
continue
# ============================== Select Object Placement with 1m tolerance, extend by 1.
Placement = ifcopenshell.util.placement.get_local_placement(B_Object.ObjectPlacement)
X = float(Placement[0][3])
Y = float(Placement[1][3])
Z = float(Placement[2][3])
Tuple = (X, Y, Z)
Results = Temp_Tree_UB.select(Tuple, extend=1*CoeffK)
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("I find with the 1 with the placement.")
FilteredResult = Filter_Result(Results, GUID_List)
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult])
continue
#We lunch the iterator to do analyse the geometry itself. The local placement may not be enough.
#We start with the bouding box, and next move to the more precise precise method, but more time consumming.
iterator = ifcopenshell.geom.iterator(settings, num_threads=4, file_or_filename=IfcFile_B, include=[B_Object])
if iterator.initialize():
while True:
# Use triangulation to build a BVH tree
# tree.add_element(iterator.get())
# Alternatively, use this code to build an unbalanced binary tree
Temp_Tree_UB.add_element(iterator.get_native())
shape = iterator.get()
if not iterator.next():
break
#============================== Select Box
Results = Temp_Tree_UB.select_box(B_Object)
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("J'ai trouvé, en box")
FilteredResult = Filter_Result(Results, GUID_List)
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult])
continue
# ============================== Select Box with extend
Results = Temp_Tree_UB.select_box(B_Object, extend=0.2*CoeffK)
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("J'ai trouvé, en box entend 0.2")
FilteredResult = Filter_Result(Results, GUID_List)
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult])
continue
# ============================== Select Box with negative Extend
Results = Temp_Tree_UB.select_box(B_Object, extend=-0.2*CoeffK)
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("J'ai trouvé, en box entend -0.2")
FilteredResult = Filter_Result(Results, GUID_List)
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult])
continue
# ============================== Select Object
Results = Temp_Tree_UB.select(B_Object)
Analyse_Resultat = Result_Analyze(Results, GUID_List, SearchNumber)
print(Results)
if Analyse_Resultat:
print("J'ai trouvé, en select object")
FilteredResult = Filter_Result(Results, GUID_List)
for One_FilteredResult in FilteredResult:
Liste_Lien.append([B_Object, One_FilteredResult])
continue
print("Je n'ai rien trouvé")
#We add all the edge to the graph.
for x in Liste_Lien:
G.add_edge(x[0].GlobalId, x[1].GlobalId)
return G
My wait to use it
def MainCreation():
CheminEspace = r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1801_17_EXE_MOD_000177_01_H_0810P_GEN_2x3-Finale.ifc"
CheminTrappons = r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1801_17_EXE_MOD_000283_01_C_0810P_ SOE_2x3_Trappons.ifc"
CheminTrappes = r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1801_17_EXE_MOD_001446_01_A_0810P_SOE_2x3_Trappes_A.ifc"
CheminPortes = r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1801_17_EXE_MOD_000246_1_C_0810P_SOE_2x3_portes.ifc"
CheminCFA=r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1801_17_EXE_MOD_000073_01_K_0810P_G20_2X3.ifc"
CheminTra=r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1630_65_EXE_BIM_000427_02_E_0810P_TRA_V20_T1.ifc"
CheminHTBT = r"G:\Mon Drive\Programmation\Topologic\Model_IFC\PN1629_64_EXE_MOD_000154_03_E_0810P_G10_V20.ifc"
IfcFile_A=ifcopenshell.open(CheminEspace)
Objects_A = IfcFile_A.by_type("IfcSpace")
IfcFile_B=ifcopenshell.open(CheminCFA)
Objects_B=ifcopenshell.util.selector.filter_elements(IfcFile_B, "IfcElement,!IfcFlowSegment,!IfcFlowFitting,!IfcDistributionPort")
LeGraph=nx.Graph()
LeGraph = Graph_ObjectB_in_ObjetA(IfcFile_A, IfcFile_B, Objects_A, Objects_B, SearchNumber=[1], Graph=LeGraph, CoeffK=1)
IfcFile_B=ifcopenshell.open(CheminPortes)
Objects_B=ifcopenshell.util.selector.filter_elements(IfcFile_B, "IfcDoor,!IfcFlowSegment,!IfcFlowFitting,!IfcDistributionPort")
LeGraph = Graph_ObjectB_in_ObjetA_Complex_Geo(IfcFile_A, IfcFile_B, Objects_A, Objects_B, SearchNumber=[2], Graph=LeGraph, CoeffK=1)
IfcFile_B=ifcopenshell.open(CheminTra)
Objects_B=ifcopenshell.util.selector.filter_elements(IfcFile_B, "IfcElement,!IfcFlowSegment,!IfcFlowFitting,!IfcDistributionPort")
LeGraph = Graph_ObjectB_in_ObjetA(IfcFile_A, IfcFile_B, Objects_A, Objects_B, SearchNumber=[1], Graph=LeGraph, CoeffK=1)
IfcFile_B=ifcopenshell.open(CheminHTBT)
Objects_B=ifcopenshell.util.selector.filter_elements(IfcFile_B, "IfcElement,!IfcFlowSegment,!IfcFlowFitting,!IfcDistributionPort")
LeGraph = Graph_ObjectB_in_ObjetA(IfcFile_A, IfcFile_B, Objects_A, Objects_B, SearchNumber=[1], Graph=LeGraph, CoeffK=1)
IfcFile_A=ifcopenshell.open(CheminPortes)
Objects_A = IfcFile_A.by_type("IfcDoor")
IfcFile_B=ifcopenshell.open(CheminPortes)
Objects_B=ifcopenshell.util.selector.filter_elements(IfcFile_B, "IfcElement,Name*=LECB")
LeGraph = Graph_ObjectB_in_ObjetA_Complex_Geo(IfcFile_A, IfcFile_B, Objects_A, Objects_B, SearchNumber=[1], Graph=LeGraph, CoeffK=2)
nx.write_gexf(LeGraph,"Graph_IFC_Total.gexf")