-
Notifications
You must be signed in to change notification settings - Fork 9
2. Console with properties, quantities and type information
We start very similar as with the minimal console application, but expand the output to also return the names and values of property sets and individual properties, including their value.
import sys
import ifcopenshell
# Our Print Entity function (recursive)
def print_entity(entity, level):
print("{0}{1} [{2}]".format('. ' * level, entity, entity.is_a()))
# follow Containment relation
if hasattr(ifc_object, 'ContainsElements'):
for rel in ifc_object.ContainsElements:
for child in rel.RelatedElements:
print_entity(child, level + 1)
# follow Aggregation/Decomposition Relation
if hasattr(ifc_object, 'IsDecomposedBy'):
for rel in ifc_object.IsDecomposedBy:
for child in rel.RelatedObjects:
print_entity(child, level + 1)
# Our Main function
def main():
ifc_file = ifcopenshell.open(sys.argv[1])
for item in ifc_file.by_type('IfcProject'):
print_entity(item, 0)
if __name__ == "__main__":
main()
First we add a small convenience function which allows us to print the indents more easily, as we'll need this in a few places.
def indent(level):
spacer = '. '
for i in range(level):
print(spacer, end='')
Properties, quantities but also type definitions can all be retrieved via IfcRelDefines
relationships.
Within the print_element
method, we check if the attribute IsDefinedBy
is available, as this indicates the availability of that relationship.
def print_entity(entity, level):
indent(level), print('#' + str(entity.id()) + ' = ' + entity.is_a()
+ ' "' + str(entity.Name) + '" (' + entity.GlobalId + ')')
if hasattr(entity, 'IsDefinedBy'):
After that, we have to distinguish between the different specialisations of this relationship:
-
IfcRelDefinesByType
is the class the relates an element (actually all classes derived fromIfcObject
) with a type. In that case, we retrieve the type and recursively call theprint_entity
method. -
IfcRelDefinesByProperties
is another variant, which is used to link anIfcPropertySetDefinition
to anIfcObject
. This definition is than retrieved via the.RelatingPropertyDefinition
attribute.
if hasattr(entity, 'IsDefinedBy'):
for definition in entity.IsDefinedBy:
if definition.is_a('IfcRelDefinesByType'):
print_entity(definition.RelatingType, level + 1)
In the case of IfcRelDefinesByProperties
we need to make another distinction, as this can either by an IfcPropertySet
or a specialisation of this as an IfcElementQuantity
(which is actually just a special version of a property set, but containing quantities instead of properties.
if definition.is_a('IfcRelDefinesByProperties'):
related_data = definition.RelatingPropertyDefinition
# the individual properties/quantities
if related_data.is_a('IfcPropertySet'):
print_element_properties(related_data, level + 1)
elif related_data.is_a('IfcElementQuantity'):
print_element_quantities(related_data, level + 1)
You can notice that we will need a new method to print all properties and quantities. While we could expand our print_entity method, we want to keep it simple enough and a dedicated method seems easier to follow.
The method to display properties will first print out the .Name
attribute of the property, using the indent method to ensure it is nicely placed underneath the entity itself.
Then we follow the .HasProperties
attribute which returns the list of all properties, which we can traverse using a for
loop.
Finally, we display some information about this property:
- The
.Unit
attribute contains a string to show the unit type. As it is often empty, it will displayNone
. We should in that case go looking for the unit assignments at theIfcProject
instance. We also test if the attribute is actually available. - The value of the property depends on the property class and is located in different attributes. We initiate this as a string
'<not handled>'
and then check the class using the.is_a()
method. In most cases, properties will be found asIfcPropertySingleValue
and in that case, the.NominalValue.wrappedValue
returns the value, which we pack in a string.
We return an indented string and add the .Name
attribute along with the value and the unit.
# PropertySet
def print_element_properties(property_set, level):
indent(level), print(property_set.Name)
for prop in property_set.HasProperties:
unit = str(prop.Unit) if hasattr(prop, 'Unit') else ''
prop_value = '<not handled>'
if prop.is_a('IfcPropertySingleValue'):
prop_value = str(prop.NominalValue.wrappedValue)
indent(level + 1)
print(str('{0} = {1} [{2}]').format(prop.Name, prop_value, unit))
When we test the code now on the IfcOpenHouse.ifc
example file, we see that our site contains a property set called Pset_SiteCommon
with a single property called TotalArea
, a value of 523.31397507897 and a unit of None
.
#12 = IfcProject "IfcOpenHouse" (02NgJh73z9H9p1e8asU8xp)
. #18 = IfcSite "None" (3ouLTcA3r8P9cplyI5HVtV)
. . Pset_SiteCommon
. . . TotalArea = 523.31397507897 [None]
. . #25 = IfcBuilding "None" (2CLK0Xn_T2iBVjws$YeMlK)
. . . #32 = IfcBuildingStorey "None" (15VFoi2F54guzs2VUHeW$Z)
. . . . #34 = IfcWallStandardCase "South wall" (2eZ4QSizXEuRNpHMBut_dk)
. . . . #77 = IfcFooting "Footing" (2YmZTDa0T3h97PCthS2e$n)
(continued)
A very similar method is created to display quantities. It has the same structure, but has to look for different attributes. The set of quantities is like a property set, but is retrieved from the .Quantities
attribute, which can contain multiple quantities. We have prepared a series of tests to check the quantity class and distinguish between Length, Area, Volume and Count quantities, as their actual quantity value is always in a different attribute: .LengthValue .AreaValue .VolumeValue
and .CountValue
# QuantitySet
def print_element_quantities(quantity_set, level):
indent(level), print(quantity_set.Name)
# the individual quantities
for quantity in quantity_set.Quantities:
unit = str(quantity.Unit) if hasattr(quantity, 'Unit') else ''
quantity_value = '<not handled>'
if quantity.is_a('IfcQuantityLength'):
quantity_value = str(quantity.LengthValue)
elif quantity.is_a('IfcQuantityArea'):
quantity_value = str(quantity.AreaValue)
elif quantity.is_a('IfcQuantityVolume'):
quantity_value = str(quantity.VolumeValue)
elif quantity.is_a('IfcQuantityCount'):
quantity_value = str(quantity.CountValue)
indent(level + 1)
print(str('{0} = {1} [{2}]').format(quantity.Name, quantity_value, unit))
Running the script now returns a full listing of the Spatial hierarchy, with all defined types, property sets and quantity sets.
#12 = IfcProject "IfcOpenHouse" (02NgJh73z9H9p1e8asU8xp)
. #18 = IfcSite "None" (3ouLTcA3r8P9cplyI5HVtV)
. . Pset_SiteCommon
. . . TotalArea = 523.31397507897 [None]
. . #25 = IfcBuilding "None" (2CLK0Xn_T2iBVjws$YeMlK)
. . . #32 = IfcBuildingStorey "None" (15VFoi2F54guzs2VUHeW$Z)
. . . . #34 = IfcWallStandardCase "South wall" (2eZ4QSizXEuRNpHMBut_dk)
. . . . #77 = IfcFooting "Footing" (2YmZTDa0T3h97PCthS2e$n)
. . . . #187 = IfcRoof "Roof" (2BBRbyGfz0oAPIZfmijoFU)
. . . . . #188 = IfcSlab "South roof" (2N9x4fcqf7jPAPjLJiEAeu)
. . . . . #189 = IfcSlab "North roof" (0XohIE5VnFEeXM8xcyKinZ)
. . . . #219 = IfcWallStandardCase "North wall" (3MI1ZgnBDDDBiVLjK3Pjw6)
. . . . #292 = IfcWallStandardCase "East wall" (2BwdmwOJTA3AXuw7xnEWxI)
. . . . #299 = IfcWallStandardCase "West wall" (2NGpkeicv1wxe44d6RlRW4)
. . . . #2609 = IfcStairFlight "None" (3Kt_zAPyX1DPS4ICfjKmJe)
. . . . #2687 = IfcDoor "None" (2yg42GmAP2FPvawq2Wx0_t)
. . . . . #2697 = IfcDoorStyle "Door type" (3wdEz$iKfBfQfZvy43$ION)
. . . . #2756 = IfcWindow "None" (1I6DWQZ398jPufhllK9p9v)
. . . . . #2787 = IfcMember "None" (3AB2FCwEbFJRjTNrrzBzRx)
. . . . . #2793 = IfcMember "None" (1NOhpy0kH7GhQhwTkNsTkW)
. . . . . #2804 = IfcMember "None" (0wIkMD7fX4Gx1TviaIBg7B)
. . . . . #2810 = IfcMember "None" (3bbwac$Qz9XfP4$vwRZn_n)
. . . . . #2830 = IfcPlate "None" (3vj_YCbVX0xf$s9BJ1jfni)
. . . . #2837 = IfcWindow "None" (1CDtdlyp142Ra7nrHrHp3n)
. . . . . #2863 = IfcMember "None" (0bYSqmJFH9B9iQineRza0t)
. . . . . #2869 = IfcMember "None" (0iEQdvyZbCD9QwMyZcoWbe)
. . . . . #2875 = IfcMember "None" (0QnU_5Wv10efdpZOlt_PdH)
. . . . . #2881 = IfcMember "None" (2Rr$NdMOj5ehIfklzFoux3)
. . . . . #2901 = IfcPlate "None" (2vbNIVlI50GO0a4IjZdaws)
. . . . #2908 = IfcWindow "None" (3R3qWEaU905At4mDtVMMcL)
. . . . . #2934 = IfcMember "None" (1r8BKeb7D99xtMg_MHX9gH)
. . . . . #2940 = IfcMember "None" (1aOuLtEW9C1gu921pG7NmQ)
. . . . . #2946 = IfcMember "None" (2wcyJCbj99oA87M9J1$pwo)
. . . . . #2952 = IfcMember "None" (1Jfizzbvb9vOzFvdiixKEY)
. . . . . #2972 = IfcPlate "None" (2JKY$Y_Af9g9P3eOabCTlE)
. . . . #2979 = IfcWindow "None" (1rIP5j0CrBBgIVVNROt1U8)
. . . . . #3005 = IfcMember "None" (3IkQrRLgTEmgIYqQ6CYj5j)
. . . . . #3011 = IfcMember "None" (131EGjUgnE1eBcsIJj2NWq)
. . . . . #3017 = IfcMember "None" (0a$SU8COT7dQTEqIBq1iIm)
. . . . . #3023 = IfcMember "None" (0jHdJ0WUH0OPZ7Tu5W_b70)
. . . . . #3043 = IfcPlate "None" (1SDocEeHH9uBO_5OZEJJ9h)
. . . . #3050 = IfcWindow "None" (1dmSFt7FTBjwBVhxk9UIwv)
. . . . . #3076 = IfcMember "None" (1J1PQJXqz6oPlCr02UnY5V)
. . . . . #3082 = IfcMember "None" (39v3BUiu1EnAY67Ev89uee)
. . . . . #3088 = IfcMember "None" (0LIKNCiHr4pgJvgjnj77Ue)
. . . . . #3094 = IfcMember "None" (2wTCMQ71P1ovhnMbcvrSa8)
. . . . . #3114 = IfcPlate "None" (1QRDIGdcjDhxuxkcU7tT1Z)
The Open House example file does not contain quantities, but from another file we see that the script picks them up, here with the name BaseQuantities
:
. . . . #93324 = IfcCovering "41_Gevelafwerking_Balk-x" (1tfiwvX6LiHe$wvd7ntvew)
. . . . . #81427 = IfcCoveringType "Deksteen 50 x 5/9,5 500 x 92" (0vsMyV9dKq55fJB9JIkeqx)
. . . . . Pset_CoveringCommon
. . . . . . FireRating = n.v.t. [None]
. . . . . . FragilityRating = n.v.t. [None]
. . . . . . Material = Deksteen 50 x 5/9,5 [None]
. . . . . . Combustible = False [None]
. . . . . Pset_FireRatingProperties
. . . . . . FireResistanceRating = n.v.t. [None]
. . . . . Pset_ManufacturerOccurrence
. . . . . . BarCode = n.v.t. [None]
. . . . . . SerialNumber = n.v.t. [None]
. . . . . Pset_ManufacturerTypeInformation
. . . . . . ModelReference = n.v.t. [None]
. . . . . . Manufacturer = n.v.t. [None]
. . . . . . ProductionYear = DD/MM/YYYY [None]
. . . . . BaseQuantities
. . . . . . GrossArea = 6.48599996724 [None]
. . . . . . NetArea = 6.48599996724 [None]
A big thanks to the IfcOpenShell library and the many people contributing with code, but also examples.