New utility function to get storey elevations with elevation attribute fallback for more compliant sorting of building storey elevations.

This commit is contained in:
Dion Moult
2021-10-18 12:39:04 +11:00
parent a9a48bdd1d
commit 699ccddda3
4 changed files with 75 additions and 46 deletions
@@ -24,3 +24,10 @@ def get_local_placement(plc):
else:
parent = get_local_placement(plc.PlacementRelTo)
return np.dot(parent, get_axis2placement(plc.RelativePlacement))
def get_storey_elevation(storey):
if storey.ObjectPlacement:
matrix = get_local_placement(storey.ObjectPlacement)
return matrix[2][3]
return getattr(storey, "Elevation", 0.0) or 0.0
@@ -1,26 +1,26 @@
import pytest
import test.bootstrap
import ifcopenshell.api
import ifcopenshell.util.element
import ifcopenshell.util.element as subject
class TestGetPsetsIFC4(test.bootstrap.IFC4):
def test_getting_the_psets_of_a_product_as_a_dictionary(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
assert ifcopenshell.util.element.get_psets(element) == {}
assert subject.get_psets(element) == {}
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="name")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a": "b"})
assert ifcopenshell.util.element.get_psets(element) == {"name": {"a": "b"}}
assert subject.get_psets(element) == {"name": {"a": "b"}}
def test_getting_the_psets_of_a_product_type_as_a_dictionary(self):
type_element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
assert ifcopenshell.util.element.get_psets(type_element) == {}
assert subject.get_psets(type_element) == {}
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=type_element, name="name")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"x": "y"})
assert ifcopenshell.util.element.get_psets(type_element) == {"name": {"x": "y"}}
assert subject.get_psets(type_element) == {"name": {"x": "y"}}
def test_getting_psets_from_an_element_which_cannot_have_psets(self):
assert ifcopenshell.util.element.get_psets(self.file.create_entity("IfcPerson")) == {}
assert subject.get_psets(self.file.create_entity("IfcPerson")) == {}
class TestGetPropertyDefinitionIFC4(test.bootstrap.IFC4):
@@ -28,18 +28,18 @@ class TestGetPropertyDefinitionIFC4(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="name")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a": "b"})
assert ifcopenshell.util.element.get_property_definition(pset) == {"a": "b"}
assert subject.get_property_definition(pset) == {"a": "b"}
def test_getting_the_properties_of_a_qto(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
qto = ifcopenshell.api.run("pset.add_qto", self.file, product=element, name="name")
ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={"x": 42})
assert ifcopenshell.util.element.get_property_definition(qto) == {"x": 42}
assert subject.get_property_definition(qto) == {"x": 42}
def test_getting_the_properties_of_a_predefined_pset(self):
pset = self.file.create_entity("IfcDoorLiningProperties", ifcopenshell.guid.new())
pset.LiningDepth = 42
assert ifcopenshell.util.element.get_property_definition(pset) == {"LiningDepth": 42}
assert subject.get_property_definition(pset) == {"LiningDepth": 42}
class TestGetQuantitiesIFC4(test.bootstrap.IFC4):
@@ -47,20 +47,20 @@ class TestGetQuantitiesIFC4(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
qto = ifcopenshell.api.run("pset.add_qto", self.file, product=element, name="name")
ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={"x": 42})
assert ifcopenshell.util.element.get_quantities(qto.Quantities) == {"x": 42}
assert subject.get_quantities(qto.Quantities) == {"x": 42}
class TestGetPropertiesIFC4(test.bootstrap.IFC4):
def test_getting_no_properties_when_none_are_available(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="name")
assert ifcopenshell.util.element.get_properties(pset.HasProperties) == {}
assert subject.get_properties(pset.HasProperties) == {}
def test_getting_single_properties_from_a_list_of_properties(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="name")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a": "b"})
assert ifcopenshell.util.element.get_properties(pset.HasProperties) == {"a": "b"}
assert subject.get_properties(pset.HasProperties) == {"a": "b"}
def test_getting_complex_properties_from_a_list_of_properties(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -68,7 +68,7 @@ class TestGetPropertiesIFC4(test.bootstrap.IFC4):
complex_property = self.file.createIfcComplexProperty(Name="prop", UsageName="usage_name")
ifcopenshell.api.run("pset.edit_pset", self.file, pset=complex_property, properties={"a": "b"})
pset.HasProperties = [complex_property]
assert ifcopenshell.util.element.get_properties(pset.HasProperties) == {
assert subject.get_properties(pset.HasProperties) == {
"prop": {
"UsageName": "usage_name",
"id": 4,
@@ -83,8 +83,8 @@ class TestGetTypeIFC4(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
ifcopenshell.api.run("type.assign_type", self.file, related_object=element, relating_type=element_type)
assert ifcopenshell.util.element.get_type(element) == element_type
assert ifcopenshell.util.element.get_type(element_type) == element_type
assert subject.get_type(element) == element_type
assert subject.get_type(element_type) == element_type
class TestGetTypeIFC2X3(test.bootstrap.IFC2X3):
@@ -92,8 +92,8 @@ class TestGetTypeIFC2X3(test.bootstrap.IFC2X3):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
element_type = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType")
ifcopenshell.api.run("type.assign_type", self.file, related_object=element, relating_type=element_type)
assert ifcopenshell.util.element.get_type(element) == element_type
assert ifcopenshell.util.element.get_type(element_type) == element_type
assert subject.get_type(element) == element_type
assert subject.get_type(element_type) == element_type
class TestGetMaterial(test.bootstrap.IFC4):
@@ -101,7 +101,7 @@ class TestGetMaterial(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
material = ifcopenshell.api.run("material.add_material", self.file)
ifcopenshell.api.run("material.assign_material", self.file, product=element, material=material)
assert ifcopenshell.util.element.get_material(element) == material
assert subject.get_material(element) == material
def test_getting_a_material_list_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -109,31 +109,31 @@ class TestGetMaterial(test.bootstrap.IFC4):
rel = ifcopenshell.api.run(
"material.assign_material", self.file, product=element, type="IfcMaterialList", material=material
)
assert ifcopenshell.util.element.get_material(element) == rel.RelatingMaterial
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_layer_set_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run("material.assign_material", self.file, product=element, type="IfcMaterialLayerSet")
assert ifcopenshell.util.element.get_material(element) == rel.RelatingMaterial
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_profile_set_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run("material.assign_material", self.file, product=element, type="IfcMaterialProfileSet")
assert ifcopenshell.util.element.get_material(element) == rel.RelatingMaterial
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_layer_set_usage_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run(
"material.assign_material", self.file, product=element, type="IfcMaterialLayerSetUsage"
)
assert ifcopenshell.util.element.get_material(element) == rel.RelatingMaterial
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_profile_set_usage_of_a_product(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
rel = ifcopenshell.api.run(
"material.assign_material", self.file, product=element, type="IfcMaterialProfileSetUsage"
)
assert ifcopenshell.util.element.get_material(element) == rel.RelatingMaterial
assert subject.get_material(element) == rel.RelatingMaterial
def test_getting_a_material_layer_set_indirectly_from_an_assigned_usage(self):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -141,7 +141,7 @@ class TestGetMaterial(test.bootstrap.IFC4):
"material.assign_material", self.file, product=element, type="IfcMaterialLayerSetUsage"
)
assert (
ifcopenshell.util.element.get_material(element, should_skip_usage=True) == rel.RelatingMaterial.ForLayerSet
subject.get_material(element, should_skip_usage=True) == rel.RelatingMaterial.ForLayerSet
)
def test_getting_a_material_profile_set_indirectly_from_an_assigned_usage(self):
@@ -150,7 +150,7 @@ class TestGetMaterial(test.bootstrap.IFC4):
"material.assign_material", self.file, product=element, type="IfcMaterialProfileSetUsage"
)
assert (
ifcopenshell.util.element.get_material(element, should_skip_usage=True)
subject.get_material(element, should_skip_usage=True)
== rel.RelatingMaterial.ForProfileSet
)
@@ -160,7 +160,7 @@ class TestGetMaterial(test.bootstrap.IFC4):
ifcopenshell.api.run("type.assign_type", self.file, related_object=element, relating_type=element_type)
material = ifcopenshell.api.run("material.add_material", self.file)
ifcopenshell.api.run("material.assign_material", self.file, product=element_type, material=material)
assert ifcopenshell.util.element.get_material(element) == material
assert subject.get_material(element) == material
class TestGetContainerIFC4(test.bootstrap.IFC4):
@@ -168,7 +168,7 @@ class TestGetContainerIFC4(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
ifcopenshell.api.run("spatial.assign_container", self.file, product=element, relating_structure=building)
assert ifcopenshell.util.element.get_container(element) == building
assert subject.get_container(element) == building
def test_getting_an_indirect_spatial_container_of_an_element(self):
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -176,7 +176,7 @@ class TestGetContainerIFC4(test.bootstrap.IFC4):
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
ifcopenshell.api.run("spatial.assign_container", self.file, product=element, relating_structure=building)
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
assert ifcopenshell.util.element.get_container(subelement) == building
assert subject.get_container(subelement) == building
def test_getting_nothing_if_we_enforce_only_getting_direct_spatial_containers(self):
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
@@ -184,7 +184,7 @@ class TestGetContainerIFC4(test.bootstrap.IFC4):
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
ifcopenshell.api.run("spatial.assign_container", self.file, product=element, relating_structure=building)
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
assert ifcopenshell.util.element.get_container(subelement, should_get_direct=True) is None
assert subject.get_container(subelement, should_get_direct=True) is None
class TestGetDecompositionIFC4(test.bootstrap.IFC4):
@@ -194,7 +194,7 @@ class TestGetDecompositionIFC4(test.bootstrap.IFC4):
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
ifcopenshell.api.run("spatial.assign_container", self.file, product=element, relating_structure=building)
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
results = ifcopenshell.util.element.get_decomposition(building)
results = subject.get_decomposition(building)
assert element in results
assert subelement in results
@@ -204,13 +204,13 @@ class TestGetAggregateIFC4(test.bootstrap.IFC4):
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcCovering")
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
assert ifcopenshell.util.element.get_aggregate(subelement) == element
assert subject.get_aggregate(subelement) == element
class TestReplaceAttributeIFC4(test.bootstrap.IFC4):
def test_replacing_an_elements_attribute(self):
element = self.file.createIfcWall("foo")
ifcopenshell.util.element.replace_attribute(element, "foo", "bar")
subject.replace_attribute(element, "foo", "bar")
assert element.GlobalId == "bar"
def test_replacing_a_value_in_a_list(self):
@@ -218,7 +218,7 @@ class TestReplaceAttributeIFC4(test.bootstrap.IFC4):
new = self.file.createIfcWall()
rel = self.file.createIfcRelAggregates()
rel.RelatedObjects = [old]
ifcopenshell.util.element.replace_attribute(rel, old, new)
subject.replace_attribute(rel, old, new)
assert rel.RelatedObjects == (new,)
@@ -228,15 +228,15 @@ class TestHasElementReferenceIFC4(test.bootstrap.IFC4):
new = self.file.createIfcWall()
rel = self.file.createIfcRelAggregates()
rel.RelatedObjects = [old]
assert ifcopenshell.util.element.has_element_reference(rel.RelatedObjects, old) is True
assert ifcopenshell.util.element.has_element_reference(rel.RelatedObjects, new) is False
assert subject.has_element_reference(rel.RelatedObjects, old) is True
assert subject.has_element_reference(rel.RelatedObjects, new) is False
class TestRemoveDeepIFC4(test.bootstrap.IFC4):
def test_removing_an_element_along_with_all_direct_attributes_recursively(self):
owner = self.file.createIfcOwnerHistory()
element = self.file.createIfcWall(GlobalId="id", OwnerHistory=owner)
ifcopenshell.util.element.remove_deep(self.file, element)
subject.remove_deep(self.file, element)
with pytest.raises(RuntimeError):
self.file.by_id(1)
self.file.by_id(2)
@@ -245,7 +245,7 @@ class TestRemoveDeepIFC4(test.bootstrap.IFC4):
owner = self.file.createIfcOwnerHistory()
element = self.file.createIfcWall(GlobalId="id1", OwnerHistory=owner)
element2 = self.file.createIfcWall(GlobalId="id2", OwnerHistory=owner)
ifcopenshell.util.element.remove_deep(self.file, element)
subject.remove_deep(self.file, element)
with pytest.raises(RuntimeError):
self.file.by_guid("id1")
assert self.file.by_id(1)
@@ -255,7 +255,7 @@ class TestRemoveDeepIFC4(test.bootstrap.IFC4):
class TestCopyIFC4(test.bootstrap.IFC4):
def test_copying_an_element(self):
element = self.file.createIfcWall(GlobalId="id", Name="name")
element2 = ifcopenshell.util.element.copy(self.file, element)
element2 = subject.copy(self.file, element)
assert element.is_a() == element2.is_a()
assert element.GlobalId != element2.GlobalId
assert element.Name == element2.Name
@@ -266,7 +266,7 @@ class TestCopyDeepIFC4(test.bootstrap.IFC4):
owner = self.file.createIfcOwnerHistory()
owner.State = "READWRITE"
element = self.file.createIfcWall(GlobalId="id", Name="name", OwnerHistory=owner)
element2 = ifcopenshell.util.element.copy_deep(self.file, element)
element2 = subject.copy_deep(self.file, element)
assert element.OwnerHistory != element2.OwnerHistory
assert element.OwnerHistory.State == element2.OwnerHistory.State
@@ -274,6 +274,6 @@ class TestCopyDeepIFC4(test.bootstrap.IFC4):
element = self.file.createIfcWall(Name="name")
rel = self.file.createIfcRelAggregates()
rel.RelatedObjects = [element]
rel2 = ifcopenshell.util.element.copy_deep(self.file, rel)
rel2 = subject.copy_deep(self.file, rel)
assert rel.RelatedObjects != rel2.RelatedObjects
assert rel.RelatedObjects[0].Name == rel2.RelatedObjects[0].Name
@@ -0,0 +1,25 @@
import ifcopenshell
import test.bootstrap
import ifcopenshell.util.placement as subject
class TestGetStoreyElevationIFC4(test.bootstrap.IFC4):
def test_run(self):
storey = self.file.createIfcBuildingStorey()
placement = self.file.createIfcLocalPlacement()
placement.RelativePlacement = self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint((0.0, 0.0, 3.0))
)
storey.ObjectPlacement = placement
assert subject.get_storey_elevation(storey) == 3.0
def test_getting_the_elevation_if_no_z_location(self):
storey = self.file.createIfcBuildingStorey()
storey.Elevation = 3.0
assert subject.get_storey_elevation(storey) == 3.0
def test_returning_0_as_a_fallback(self):
storey = self.file.createIfcBuildingStorey()
assert subject.get_storey_elevation(storey) == 0.0
building = self.file.createIfcBuilding()
assert subject.get_storey_elevation(building) == 0.0