Hi ALL,
I’m trying to finalize a script to upload via a python script an IFC file stored on one of my desktop folders.
I’m able now to upload the file according to the file size limit of the server, but, when i see the commit on the stream no 3D is available…and more…i can see the IDs of each element stored on the file but the hieararchy it self of the file is it not correctly visible on the scene explorer.
I tryied to use powerbi to get the data, i can obtein correctly all the ids objects but i’m not able to see the properties of my file, not even possible to see that on the web.
Here I post the script based on specklepy and python.
from specklepy.api.client import SpeckleClient
from specklepy.transports.server import ServerTransport
from specklepy.api import operations
from specklepy.objects import Base
import ifcopenshell
# Speckle server information
speckle_server_url = "https://app.speckle.systems/" # Speckle URL
stream_id = "your_stream_id" # Stream ID
branch_name = "test_branch" # Branch name for the commit
# Speckle Token
token = "your_speckle_token"
# Initialization
client = SpeckleClient(host=speckle_server_url)
client.authenticate_with_token(token=token)
# Path to the IFC file
ifc_file_path = 'path_to_your_ifc_file.ifc'
# Open the IFC file
ifc_file = ifcopenshell.open(ifc_file_path)
# Speckle Object creation
speckle_base = Base()
# Create separate base objects for IfcSite, IfcBuilding, and IfcBuildingStorey
site = None
building = None
storeys = {}
# List of main objects to add to the scene
main_objects = []
# Step 1: Iterate through all elements and categorize them
for element in ifc_file.by_type("IfcProduct"):
element_base = Base()
element_base.name = element.Name if hasattr(element, 'Name') else "Unnamed"
element_base.category = element.is_a()
element_base.global_id = element.GlobalId
# Step 2: Classify and organize elements by their hierarchy
if element.is_a("IfcSite"):
site = Base()
site.name = element.Name if element.Name else "Unnamed Site"
site["GlobalId"] = element.GlobalId
speckle_base["@site"] = site # Set as site in Speckle Base
elif element.is_a("IfcBuilding"):
building = Base()
building.name = element.Name if element.Name else "Unnamed Building"
building["GlobalId"] = element.GlobalId
if site:
site["@building"] = building # Attach building to the site
speckle_base["@building"] = building # Add building to base
elif element.is_a("IfcBuildingStorey"):
storey_base = Base()
storey_base.name = element.Name if element.Name else "Unnamed Storey"
storey_base["GlobalId"] = element.GlobalId
if building:
building["@{}".format(element.GlobalId)] = storey_base # Attach storey to the building
storeys[element.GlobalId] = storey_base # Track storeys for further element associations
# Step 3: Link elements (components) to their respective storey
if hasattr(element, "ContainedInStructure"):
for rel in element.ContainedInStructure:
if rel.is_a("IfcRelContainedInSpatialStructure") and rel.RelatingStructure:
related_storey = rel.RelatingStructure.GlobalId
if related_storey in storeys:
storeys[related_storey][element.GlobalId] = element_base # Attach element to the respective storey
# Serialize the Speckle objects and send to the server
serializer = BaseObjectSerializer()
hash_id, obj_dict = serializer.traverse_base(speckle_base)
# Transport objects
transport = ServerTransport(client=client, stream_id=stream_id)
# Send objects to the Speckle server
object_id = operations.send(base=speckle_base, transports=[transport])
print(f"Object ID sent: {object_id}")
# Create commit in the specified branch
commit_id = client.commit.create(
stream_id=stream_id,
object_id=object_id,
message="Upload IFC file with structured hierarchy",
branch_name=branch_name
)
print(f"Commit ID: {commit_id}")
To summarise, you’ve got the basics down with parsing the IFC file using ifcopenshell, restructuring elements like IfcSite, IfcBuilding, and IfcProduct, and sending it to Speckle. But I get that there are still some hiccups with the 3D view, the hierarchy in the Scene Explorer, and property access in Power BI.
I don’t have time to run your script with my IFC file, but if you could share a sample project where you see these issues, that’d help a lot. I can usually grok the resultant hierarchy just from code but seeing an actual result you already have would help a bunch.
Also:
Are there specific properties you expected to see but aren’t?
Have you tried other IFC files, and do they show the same issues?
Yes I should see all the properties like in the link with the ifc just draged and droped.
Yes i tested different files and I have always the same result like in the last picture.
In this picture you can see on the left part one element of the model and in to the right part of the screen you can see the same element globalid and properties but without visualization in 3d.
Ok - I’m not sure what you get from ifcopenshell; it is years since I last used it as a data interrogation tool, but in order for your Python script to bear visual fruit, you will need at least to upload a mesh representation of each element in a displayValue prop.
For a basic structure if you are looking for a hierarchical structure look into the Collection object.
This has a property collectionType and expects sub-elements in an elements prop as an array.
Collection (collectionType = "model", name = "IFC Filename"):
elements: [
Base (ifcType = "IfcPart"):
displayValue = [Mesh, Mesh, ...],
Base (ifcType = "IfcPart"):
displayValue = [Mesh, Mesh, ...],
]
Hi and thanks, i’ve updated my script following your suggestions.
Now i can retriews objects in storey levels but i’m still not able to see 3D elements in the viewer.
I share again the updated code
Hi Alex, i’m not able to change the Speckle type using Ifc categories.
I’m trying since 2 days whitout success.
I share there my last code.
I’m able right now to upload correctly an IFC file and see storey levels and all the elements of an ifc file and some properties, but still not able to see the model in 3D.
I share too a screenshot of the result printed in my terminal where it shows that are presents 3 meshes.
Here the code:
from specklepy.api.client import SpeckleClient
from specklepy.transports.server import ServerTransport
from specklepy.api import operations
from specklepy.objects import Base
import ifcopenshell
import ifcopenshell.geom
speckle_server_url = "xxxx/"
stream_id = "x"
client = SpeckleClient(host=speckle_server_url)
client.authenticate_with_token("xxxxx")
ifc_path = "C:/Users/xxxx/Desktop/TESTIFC/SimpleWall.ifc"
ifc_file = ifcopenshell.open(ifc_path)
speckle_base = Base()
speckle_base.name = "IFC Model"
speckle_base["collectionType"] = "model"
project = Base()
project.name = "IFC Project"
project.speckle_type = "IFCProject"
site = Base()
site.name = "Site"
site.speckle_type = "IFCSite"
building = Base()
building.name = "Unnamed Building"
building.speckle_type = "IFCBuilding"
storeys = {}
def extract_properties(ifc_element):
props = Base()
props["global_id"] = ifc_element.GlobalId
props["name"] = ifc_element.Name if hasattr(ifc_element, 'Name') else "Unnamed Element"
props["ifcType"] = ifc_element.is_a()
props["ObjectType"] = ifc_element.ObjectType if hasattr(ifc_element, 'ObjectType') else "N/A"
props["Tag"] = ifc_element.Tag if hasattr(ifc_element, 'Tag') else "N/A"
props["expressID"] = ifc_element.id()
if hasattr(ifc_element, "HasPropertySets"):
props["PropertySets"] = {}
for pset in ifc_element.HasPropertySets:
if hasattr(pset, "Name") and hasattr(pset, "HasProperties"):
pset_name = pset.Name
props["PropertySets"][pset_name] = {}
for prop in pset.HasProperties:
prop_name = prop.Name
prop_value = prop.NominalValue.wrappedValue if hasattr(prop.NominalValue, 'wrappedValue') else prop.NominalValue
props["PropertySets"][pset_name][prop_name] = prop_value
if hasattr(ifc_element, "IsDefinedBy"):
props["BaseQuantities"] = {}
for rel in ifc_element.IsDefinedBy:
if rel.is_a("IfcRelDefinesByProperties"):
prop_set = rel.RelatingPropertyDefinition
if prop_set.is_a("IfcElementQuantity"):
for quantity in prop_set.Quantities:
quantity_name = quantity.Name
if hasattr(quantity, "LengthValue"):
quantity_value = quantity.LengthValue
elif hasattr(quantity, "AreaValue"):
quantity_value = quantity.AreaValue
elif hasattr(quantity, "VolumeValue"):
quantity_value = quantity.VolumeValue
elif hasattr(quantity, "CountValue"):
quantity_value = quantity.CountValue
elif hasattr(quantity, "WeightValue"):
quantity_value = quantity.WeightValue
elif hasattr(quantity, "TimeValue"):
quantity_value = quantity.TimeValue
else:
quantity_value = None
props["BaseQuantities"][quantity_name] = quantity_value
return props
def extract_geometry(ifc_element):
settings = ifcopenshell.geom.settings()
settings.set(settings.USE_WORLD_COORDS, True)
try:
shape = ifcopenshell.geom.create_shape(settings, ifc_element)
geometry = shape.geometry
vertices = geometry.verts
faces = geometry.faces
if len(vertices) == 0 or len(faces) == 0:
print(f"No geometry for element {ifc_element.GlobalId}")
return None
speckle_faces = []
for i in range(0, len(faces), 3):
speckle_faces.append(f"3 {faces[i]} {faces[i+1]} {faces[i+2]}")
mesh = Base()
mesh.name = f"Mesh for {ifc_element.GlobalId}"
mesh.speckle_type = "Objects.Geometry.Mesh"
mesh["vertices"] = list(vertices)
mesh["faces"] = speckle_faces
mesh["units"] = "millimeters"
print(f"Mesh for element {ifc_element.GlobalId}: vertices={len(vertices)}, faces={len(faces)//3}")
return mesh
except Exception as e:
print(f"Error extracting geometry for {ifc_element.GlobalId}: {e}")
return None
for element in ifc_file.by_type("IfcProduct"):
element_base = Base()
properties = extract_properties(element)
for key in properties.get_dynamic_member_names():
element_base[key] = properties[key]
element_base.speckle_type = properties["ifcType"]
geometry = extract_geometry(element)
if geometry:
element_base["displayValue"] = [geometry]
else:
print(f"Element {element.GlobalId} does not have valid geometry, but properties were added.")
if hasattr(element, "ContainedInStructure"):
for rel in element.ContainedInStructure:
if rel.is_a("IfcRelContainedInSpatialStructure") and rel.RelatingStructure.is_a("IfcBuildingStorey"):
storey_id = rel.RelatingStructure.GlobalId
if storey_id not in storeys:
storey_base = Base()
storey_base.name = rel.RelatingStructure.Name if rel.RelatingStructure.Name else "Unnamed Storey"
storey_base.speckle_type = "IFCBuildingStorey"
storey_base["global_id"] = storey_id
storey_base["elements"] = []
storeys[storey_id] = storey_base
storeys[storey_id]["elements"].append(element_base)
building["elements"] = list(storeys.values())
site["elements"] = [building]
project["elements"] = [site]
speckle_base["elements"] = [project]
transport = ServerTransport(client=client, stream_id=stream_id)
object_id = operations.send(base=speckle_base, transports=[transport])
print(f"Object ID sent: {object_id}")
commit_id = client.commit.create(
stream_id=stream_id,
object_id=object_id,
message="Uploaded IFC model with correct speckle types and geometry"
)
print(f"Commit ID: {commit_id}")
but it is not finished!
Why I can not see colors correctly?
I can fix the code to extract all the pset too.
But do you think it is possible to show color materials as we can see in the original ifc files?
In order for the colors to show, the objects either need to have a proper material with said colors, or alternatively the mesh objects can have per vertex colors