mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
Implement IDS property facet measure conversions
This commit is contained in:
@@ -23,6 +23,7 @@ import operator
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
import ifcopenshell.util.unit
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
import ifcopenshell.util.classification
|
import ifcopenshell.util.classification
|
||||||
@@ -824,7 +825,16 @@ class property(facet):
|
|||||||
message = "%(location)sproperty '%(name)s' in '%(propertySet)s' with a value %(value)s"
|
message = "%(location)sproperty '%(name)s' in '%(propertySet)s' with a value %(value)s"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(propertySet="Property_Set", name="PropertyName", value=None, location="any", measure=None, uri=None, use=None, instructions=None):
|
def create(
|
||||||
|
propertySet="Property_Set",
|
||||||
|
name="PropertyName",
|
||||||
|
value=None,
|
||||||
|
location="any",
|
||||||
|
measure=None,
|
||||||
|
uri=None,
|
||||||
|
use=None,
|
||||||
|
instructions=None,
|
||||||
|
):
|
||||||
"""Create a property facet that can be added to applicability or requirements of IDS specification.
|
"""Create a property facet that can be added to applicability or requirements of IDS specification.
|
||||||
|
|
||||||
:param location: Where to check for the parameter. One of "any"|"instance"|"type", defaults to "any"
|
:param location: Where to check for the parameter. One of "any"|"instance"|"type", defaults to "any"
|
||||||
@@ -914,6 +924,35 @@ class property(facet):
|
|||||||
is_pass = False
|
is_pass = False
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if self.measure:
|
||||||
|
pset_entity = inst.wrapped_data.file.by_id(pset_props["id"])
|
||||||
|
for prop_entity in pset_entity.HasProperties:
|
||||||
|
if (
|
||||||
|
prop_entity.Name not in props[pset_name].keys()
|
||||||
|
or not prop_entity.is_a("IfcPropertySingleValue")
|
||||||
|
or prop_entity.NominalValue is None
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
data_type = prop_entity.NominalValue.is_a().replace("Ifc", "").replace("Measure", "")
|
||||||
|
|
||||||
|
if data_type != self.measure:
|
||||||
|
is_pass = False
|
||||||
|
break
|
||||||
|
|
||||||
|
unit = ifcopenshell.util.unit.get_property_unit(prop_entity, inst.wrapped_data.file)
|
||||||
|
|
||||||
|
props[pset_name][prop_entity.Name] = ifcopenshell.util.unit.convert(
|
||||||
|
prop_entity.NominalValue.wrappedValue,
|
||||||
|
getattr(unit, "Prefix", None),
|
||||||
|
unit.Name,
|
||||||
|
None,
|
||||||
|
ifcopenshell.util.unit.si_type_names[unit.UnitType],
|
||||||
|
)
|
||||||
|
|
||||||
|
if not is_pass:
|
||||||
|
break
|
||||||
|
|
||||||
if self.value:
|
if self.value:
|
||||||
if any([v != self.value for v in props[pset_name].values()]):
|
if any([v != self.value for v in props[pset_name].values()]):
|
||||||
is_pass = False
|
is_pass = False
|
||||||
|
|||||||
@@ -633,6 +633,13 @@ class TestIdsAuthoring(unittest.TestCase):
|
|||||||
|
|
||||||
def test_filtering_using_a_property_facet(self):
|
def test_filtering_using_a_property_facet(self):
|
||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
|
ifc.createIfcProject()
|
||||||
|
# Milli prefix used to check measurement conversions
|
||||||
|
lengthunit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", name="METRE", prefix="MILLI")
|
||||||
|
areaunit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="AREAUNIT", name="SQUARE_METRE", prefix="MILLI")
|
||||||
|
volumeunit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="VOLUMEUNIT", name="CUBIC_METRE", prefix="MILLI")
|
||||||
|
timeunit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="TIMEUNIT", name="SECOND")
|
||||||
|
ifcopenshell.api.run("unit.assign_unit", ifc, units=[lengthunit, areaunit, volumeunit, timeunit])
|
||||||
|
|
||||||
# A name check by itself only checks that a property is non-null and non empty string
|
# A name check by itself only checks that a property is non-null and non empty string
|
||||||
# The logic is that unfortunately most BIM users cannot differentiate between the two.
|
# The logic is that unfortunately most BIM users cannot differentiate between the two.
|
||||||
@@ -719,6 +726,34 @@ class TestIdsAuthoring(unittest.TestCase):
|
|||||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": False})
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": False})
|
||||||
assert bool(facet(element)) is False
|
assert bool(facet(element)) is False
|
||||||
|
|
||||||
|
# When measure is not specified, no unit conversion is done and only primitives are checked
|
||||||
|
restriction = ids.restriction.create(options=[42.12], type="enumeration", base="decimal")
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foobar", value=restriction)
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": 42.12})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
|
||||||
|
# Measure may be used to specify an IFC data type
|
||||||
|
restriction = ids.restriction.create(options=[2], type="enumeration", base="decimal")
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", value=restriction, measure="Time")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcMassMeasure(2)})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcTimeMeasure(2)})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
|
||||||
|
# Measure also implies that a unit matters, and so a conversion shall take place to SI units
|
||||||
|
restriction = ids.restriction.create(options=[2], type="enumeration", base="decimal")
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", value=restriction, measure="Length")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcLengthMeasure(2)})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcLengthMeasure(2000)})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
|
||||||
# Location instance only checks on the instance, even if the instance is a type. Yes, weird, I know.
|
# Location instance only checks on the instance, even if the instance is a type. Yes, weird, I know.
|
||||||
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
|||||||
Reference in New Issue
Block a user