mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-16 13:46:54 +00:00
Completely untested refactor of #5257.
Removes dependency of file to util (util should depend on file, not vice versa). Removes auxiliary method.
This commit is contained in:
@@ -33,7 +33,6 @@ from typing import Union
|
|||||||
|
|
||||||
from . import ifcopenshell_wrapper
|
from . import ifcopenshell_wrapper
|
||||||
from .entity_instance import entity_instance
|
from .entity_instance import entity_instance
|
||||||
from .util.unit import get_measure_unit_type
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
@@ -223,7 +222,7 @@ class file:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
wrapped_data: ifcopenshell_wrapper.file
|
wrapped_data: ifcopenshell_wrapper.file
|
||||||
_units: file_units | None = None
|
units: dict[str: ifcopenshell.entity_instance] = None
|
||||||
history_size: int = 64
|
history_size: int = 64
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -463,28 +462,6 @@ class file:
|
|||||||
version.append(int(number.group(1)) if number else 0)
|
version.append(int(number.group(1)) if number else 0)
|
||||||
return tuple(version)
|
return tuple(version)
|
||||||
|
|
||||||
@property
|
|
||||||
def units(self) -> file_units:
|
|
||||||
"""
|
|
||||||
A class which dynamically stores unit information
|
|
||||||
and provides methods to compute unit-entities associated to types.
|
|
||||||
"""
|
|
||||||
if self._units is None:
|
|
||||||
self._units = file_units(self)
|
|
||||||
return self._units
|
|
||||||
|
|
||||||
def unit_by_measure_class(
|
|
||||||
self,
|
|
||||||
measure_class: str | None = None,
|
|
||||||
/,
|
|
||||||
) -> ifcopenshell.entity_instance | None:
|
|
||||||
"""
|
|
||||||
Helper method to obtain unit-entity either directly
|
|
||||||
or indirectly via measure class.
|
|
||||||
"""
|
|
||||||
if measure_class is not None:
|
|
||||||
return self.units.by_measure_type(measure_class)
|
|
||||||
|
|
||||||
def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]:
|
def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]:
|
||||||
if attr[0:6] == "create":
|
if attr[0:6] == "create":
|
||||||
return functools.partial(self.create_entity, attr[6:])
|
return functools.partial(self.create_entity, attr[6:])
|
||||||
@@ -724,39 +701,3 @@ class file:
|
|||||||
|
|
||||||
def to_string(self) -> str:
|
def to_string(self) -> str:
|
||||||
return self.wrapped_data.to_string()
|
return self.wrapped_data.to_string()
|
||||||
|
|
||||||
|
|
||||||
class file_units:
|
|
||||||
"""
|
|
||||||
A class which provides methods to compute unit-entities associated to types.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_unit_assignment: dict[str, entity_instance] = {}
|
|
||||||
|
|
||||||
def __init__(self, ifc_file: file):
|
|
||||||
self._load_unit_assignment(ifc_file)
|
|
||||||
|
|
||||||
def _load_unit_assignment(self, ifc_file: file, /):
|
|
||||||
"""
|
|
||||||
A mapping as dictionary which associates
|
|
||||||
to basic UnitTypes a unit-entity in the IFC file.
|
|
||||||
"""
|
|
||||||
entities = self.by_type("IfcUnitAssignment")
|
|
||||||
base = next(iter(entities), None)
|
|
||||||
units = getattr(base, "Units", None) or ()
|
|
||||||
units_and_types = [(u, getattr(u, "UnitType", None)) for u in units if isinstance(u, entity_instance)]
|
|
||||||
self._unit_assignment = {t: u for u, t in units_and_types if isinstance(t, str)}
|
|
||||||
return
|
|
||||||
|
|
||||||
def by_type(self, t: str, /) -> entity_instance | None:
|
|
||||||
"""
|
|
||||||
Returns the unit entity associated to a basic type
|
|
||||||
"""
|
|
||||||
return self._unit_assignment.get(t, None)
|
|
||||||
|
|
||||||
def by_measure_type(self, t: str, /) -> entity_instance | None:
|
|
||||||
"""
|
|
||||||
Returns the unit entity associated to a measure type
|
|
||||||
"""
|
|
||||||
t = get_measure_unit_type(t)
|
|
||||||
return self.by_type(t)
|
|
||||||
|
|||||||
@@ -412,7 +412,19 @@ def get_unit_assignment(ifc_file: ifcopenshell.file) -> Union[ifcopenshell.entit
|
|||||||
return ifc_file.by_type("IfcProject")[0].UnitsInContext
|
return ifc_file.by_type("IfcProject")[0].UnitsInContext
|
||||||
|
|
||||||
|
|
||||||
def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcopenshell.entity_instance, None]:
|
def cache_units(ifc_file: ifcopenshell.file) -> None:
|
||||||
|
ifc_file.units = {}
|
||||||
|
if assignment := get_unit_assignment(ifc_file):
|
||||||
|
ifc_file.units = {u.UnitType: u for u in assignment.Units if getattr(u, "UnitType", None)}
|
||||||
|
|
||||||
|
|
||||||
|
def clear_unit_cache(ifc_file: ifcopenshell.file) -> None:
|
||||||
|
ifc_file.units = {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_project_unit(
|
||||||
|
ifc_file: ifcopenshell.file, unit_type: str, use_cache: bool = False
|
||||||
|
) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
"""Get the default project unit of a particular unit type
|
"""Get the default project unit of a particular unit type
|
||||||
|
|
||||||
:param ifc_file: The IFC file.
|
:param ifc_file: The IFC file.
|
||||||
@@ -421,6 +433,10 @@ def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcop
|
|||||||
:return: The IFC unit entity, or nothing if there is no default project
|
:return: The IFC unit entity, or nothing if there is no default project
|
||||||
unit defined.
|
unit defined.
|
||||||
"""
|
"""
|
||||||
|
if use_cache and not ifc_file.units:
|
||||||
|
cache_units(ifc_file)
|
||||||
|
if units := ifc_file.units:
|
||||||
|
return units.get(unit_type, None)
|
||||||
if unit_assignment := get_unit_assignment(ifc_file):
|
if unit_assignment := get_unit_assignment(ifc_file):
|
||||||
for unit in unit_assignment.Units or []:
|
for unit in unit_assignment.Units or []:
|
||||||
if getattr(unit, "UnitType", None) == unit_type:
|
if getattr(unit, "UnitType", None) == unit_type:
|
||||||
@@ -428,8 +444,7 @@ def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcop
|
|||||||
|
|
||||||
|
|
||||||
def get_property_unit(
|
def get_property_unit(
|
||||||
prop: ifcopenshell.entity_instance,
|
prop: ifcopenshell.entity_instance, ifc_file: ifcopenshell.file, use_cache: bool = False
|
||||||
ifc_file: ifcopenshell.file
|
|
||||||
) -> Union[ifcopenshell.entity_instance, None]:
|
) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
"""Gets the unit definition of a property or quantity
|
"""Gets the unit definition of a property or quantity
|
||||||
|
|
||||||
@@ -437,53 +452,43 @@ def get_property_unit(
|
|||||||
This unit may be defined at the property itself explicitly, or if not
|
This unit may be defined at the property itself explicitly, or if not
|
||||||
specified, fallback to the project default.
|
specified, fallback to the project default.
|
||||||
|
|
||||||
:param prop: The property instance. You can fetch this via the instance ID
|
:param prop: The IfcProperty instance. You can fetch this via the instance
|
||||||
if doing :func:`ifcopenshell.util.element.get_psets` with
|
ID if doing :func:`ifcopenshell.util.element.get_psets` with
|
||||||
``verbose=True``.
|
``verbose=True``.
|
||||||
:param ifc_file: The IFC file being used. This is necessary to check
|
:param ifc_file: The IFC file being used. This is necessary to check
|
||||||
default project units.
|
default project units.
|
||||||
:return: The IFC unit entity, or nothing if there is no default project
|
:return: The IFC unit entity, or nothing if there is no default project
|
||||||
unit defined.
|
unit defined.
|
||||||
"""
|
"""
|
||||||
unit = prop.Unit
|
if unit := getattr(prop, "Unit", None):
|
||||||
if isinstance(unit, ifcopenshell.entity_instance):
|
|
||||||
return unit
|
return unit
|
||||||
|
|
||||||
value = None
|
value = None
|
||||||
measure_class = None
|
measure_class = None
|
||||||
|
|
||||||
# DEV-NOTE: Using .is_a() is wrong, as it tells us nothing about super class membership
|
|
||||||
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
||||||
# get underlying object
|
|
||||||
entity = prop.wrapped_data.declaration().as_entity()
|
entity = prop.wrapped_data.declaration().as_entity()
|
||||||
# extract measure class
|
|
||||||
measure_class = entity.attribute_by_index(3).type_of_attribute().declared_type().name()
|
measure_class = entity.attribute_by_index(3).type_of_attribute().declared_type().name()
|
||||||
|
|
||||||
elif prop.is_a("IfcPropertySingleValue"):
|
elif prop.is_a("IfcPropertySingleValue"):
|
||||||
value = prop.NominalValue
|
measure_class = prop.NominalValue.is_a()
|
||||||
|
|
||||||
elif prop.is_a("IfcPropertyEnumeratedValue"):
|
elif prop.is_a("IfcPropertyEnumeratedValue"):
|
||||||
unit = prop.EnumerationReference.Unit
|
if unit := prop.EnumerationReference.Unit:
|
||||||
value = next(iter(prop.EnumerationValues or ()), None)
|
return unit
|
||||||
|
if value := next(iter(prop.EnumerationValues or ()), None):
|
||||||
|
measure_class = value.is_a()
|
||||||
elif prop.is_a("IfcPropertyListValue"):
|
elif prop.is_a("IfcPropertyListValue"):
|
||||||
value = next(iter(prop.ListValues or ()), None)
|
if value := next(iter(prop.ListValues or ()), None):
|
||||||
|
measure_class = value.is_a()
|
||||||
elif prop.is_a("IfcPropertyBoundedValue"):
|
elif prop.is_a("IfcPropertyBoundedValue"):
|
||||||
value = prop.UpperBoundValue or prop.LowerBoundValue or prop.SetPointValue
|
if value := (prop.UpperBoundValue or prop.LowerBoundValue or prop.SetPointValue):
|
||||||
|
measure_class = value.is_a()
|
||||||
|
|
||||||
unit = _auxiliary_method_compute_unit(
|
if measure_class and (unit_type := get_measure_unit_type(measure_class)):
|
||||||
ifc_file,
|
return get_project_unit(ifc_file, unit_type)
|
||||||
unit=unit,
|
|
||||||
value=value,
|
|
||||||
measure_class=measure_class,
|
|
||||||
)
|
|
||||||
return unit
|
|
||||||
|
|
||||||
|
|
||||||
def get_property_table_unit(
|
def get_property_table_unit(
|
||||||
prop: ifcopenshell.entity_instance,
|
prop: ifcopenshell.entity_instance, ifc_file: ifcopenshell.file, use_cache: bool = False
|
||||||
ifc_file: ifcopenshell.file
|
|
||||||
) -> Dict[str, Union[ifcopenshell.entity_instance, None]]:
|
) -> Dict[str, Union[ifcopenshell.entity_instance, None]]:
|
||||||
"""
|
"""
|
||||||
Gets the unit definition of a property table
|
Gets the unit definition of a property table
|
||||||
@@ -503,25 +508,24 @@ def get_property_table_unit(
|
|||||||
If a unit-entity is missing,
|
If a unit-entity is missing,
|
||||||
the value associated to the key is `null`.
|
the value associated to the key is `null`.
|
||||||
"""
|
"""
|
||||||
if prop.is_a("IfcPropertyTableValue"):
|
defining_unit = None
|
||||||
unit = prop.DefiningUnit
|
if unit := prop.DefiningUnit:
|
||||||
value = next(iter(prop.DefiningValues or ()), None)
|
defining_unit = unit
|
||||||
unit_defining = _auxiliary_method_compute_unit(ifc_file, unit=unit, value=value)
|
elif value := next(iter(prop.DefiningValues or ()), None):
|
||||||
|
if unit_type := get_measure_unit_type(value.is_a()):
|
||||||
|
defining_unit = get_project_unit(ifc_file, unit_type)
|
||||||
|
|
||||||
unit = prop.DefinedUnit
|
defined_unit = None
|
||||||
value = next(iter(prop.DefinedValues or ()), None)
|
if unit := prop.DefinedUnit:
|
||||||
unit_defined = _auxiliary_method_compute_unit(ifc_file, unit=unit, value=value)
|
defined_unit = unit
|
||||||
|
elif value := next(iter(prop.DefinedValues or ()), None):
|
||||||
|
if unit_type := get_measure_unit_type(value.is_a()):
|
||||||
|
defining_unit = get_project_unit(ifc_file, unit_type)
|
||||||
|
|
||||||
units = {
|
return {
|
||||||
"DefiningUnit": unit_defining,
|
"DefiningUnit": defining_unit,
|
||||||
"DefinedUnit": unit_defined,
|
"DefinedUnit": defined_unit,
|
||||||
}
|
}
|
||||||
|
|
||||||
# currently no other case
|
|
||||||
else:
|
|
||||||
units = {}
|
|
||||||
|
|
||||||
return units
|
|
||||||
|
|
||||||
|
|
||||||
def get_unit_measure_class(unit_type: str) -> MEASURE_CLASS:
|
def get_unit_measure_class(unit_type: str) -> MEASURE_CLASS:
|
||||||
@@ -876,27 +880,3 @@ def convert_file_length_units(ifc_file: ifcopenshell.file, target_units: str = "
|
|||||||
ifcopenshell.util.element.remove_deep2(file_patched, old_length)
|
ifcopenshell.util.element.remove_deep2(file_patched, old_length)
|
||||||
|
|
||||||
return file_patched
|
return file_patched
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
# AUXILIARY METHODS
|
|
||||||
# ----------------------------------------------------------------
|
|
||||||
|
|
||||||
def _auxiliary_method_compute_unit(
|
|
||||||
ifc_file: ifcopenshell.file,
|
|
||||||
/,
|
|
||||||
*,
|
|
||||||
unit: ifcopenshell.entity_instance | None = None,
|
|
||||||
value: ifcopenshell.entity_instance | None = None,
|
|
||||||
measure_class: str | None = None,
|
|
||||||
) -> ifcopenshell.entity_instance | None:
|
|
||||||
"""
|
|
||||||
Helper method to obtain unit-entity either directly
|
|
||||||
or indirectly via measure class.
|
|
||||||
"""
|
|
||||||
if isinstance(unit, ifcopenshell.entity_instance):
|
|
||||||
return unit
|
|
||||||
|
|
||||||
if isinstance(value, ifcopenshell.entity_instance):
|
|
||||||
measure_class = measure_class or value.is_a()
|
|
||||||
|
|
||||||
return ifc_file.unit_by_measure_class(measure_class)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user