add test and update ci

This commit is contained in:
krande
2023-12-17 18:09:28 +01:00
parent eccc21277d
commit 9335c8a0c8
4 changed files with 523 additions and 32 deletions
@@ -16,9 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
from math import pi
from fractions import Fraction
from math import pi
from typing import Tuple, Iterable, Any
import ifcopenshell
import ifcopenshell.api
prefixes = {
"EXA": 1e18,
@@ -624,67 +627,75 @@ def format_length(
return f"{rounded_val:.{decimal_places}f}"
def get_base_type_name(
content_type: ifcopenshell.ifcopenshell_wrapper.named_type | ifcopenshell.ifcopenshell_wrapper.type_declaration) -> ifcopenshell.ifcopenshell_wrapper.type_declaration | None:
def is_attr_type(
content_type: ifcopenshell.ifcopenshell_wrapper.named_type | ifcopenshell.ifcopenshell_wrapper.type_declaration,
ifc_unit_type_name: str) -> ifcopenshell.ifcopenshell_wrapper.type_declaration | None:
cur_decl = content_type
while hasattr(cur_decl, "declared_type") is True:
cur_decl = cur_decl.declared_type()
if hasattr(cur_decl, "name") is False:
continue
if cur_decl.name() == "IfcLengthMeasure":
if cur_decl.name() == ifc_unit_type_name:
return cur_decl
if isinstance(cur_decl, ifcopenshell.ifcopenshell_wrapper.aggregation_type):
res = cur_decl.type_of_element()
cur_decl = res.declared_type()
if hasattr(cur_decl, "name") and cur_decl.name() == "IfcLengthMeasure":
if hasattr(cur_decl, "name") and cur_decl.name() == ifc_unit_type_name:
return cur_decl
while hasattr(cur_decl, "declared_type") is True:
cur_decl = cur_decl.declared_type()
if hasattr(cur_decl, "name") is False:
continue
if cur_decl.name() == "IfcLengthMeasure":
if cur_decl.name() == ifc_unit_type_name:
return cur_decl
return None
def iter_element_and_attributes_per_type(ifc_file: ifcopenshell.file, attr_type_name: str) -> Iterable[
Tuple[ifcopenshell.entity_instance, ifcopenshell.ifcopenshell_wrapper.attribute, Any, str]]:
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
for element in ifc_file:
entity = schema.declaration_by_name(element.is_a())
attrs = entity.all_attributes()
for i, (attr, val, is_derived) in enumerate(zip(attrs, list(element), entity.derived())):
if is_derived:
continue
# Get all methods and attributes of the element
attr_type = attr.type_of_attribute()
base_type = is_attr_type(attr_type, attr_type_name)
if base_type is None:
continue
if val is None:
continue
yield element, attr, val
def convert_file_length_units(ifc_file: ifcopenshell.file, target_units: str) -> ifcopenshell.file:
"""Converts all units in an IFC file to the specified target units. Returns a new file."""
prefix = "MILLI" if target_units == "MILLIMETERS" else None
file_patched = ifcopenshell.api.run("project.create_file", version=ifc_file.schema)
# Copy all elements from the original file to the patched file
for el in ifc_file:
file_patched.add(el)
file_patched = ifcopenshell.file.from_string(ifc_file.wrapped_data.to_string())
unit_assignment = ifcopenshell.util.unit.get_unit_assignment(file_patched)
old_length = [u for u in unit_assignment.Units if getattr(u, "UnitType", None) == "LENGTHUNIT"][0]
new_length = ifcopenshell.api.run("unit.add_si_unit", file_patched, unit_type="LENGTHUNIT", prefix=prefix)
schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(ifc_file.schema)
# Traverse all elements and their nested attributes in the file and convert them
for element in file_patched:
entity = schema.declaration_by_name(element.is_a())
attrs = entity.all_attributes()
for i, (attr, val, is_derived) in enumerate(zip(attrs, list(element), entity.derived())):
if is_derived:
continue
# Get all methods and attributes of the element
attr_type = attr.type_of_attribute()
base_type = get_base_type_name(attr_type)
if base_type is None:
continue
if val is None:
continue
if isinstance(val, tuple):
new_el = [ifcopenshell.util.unit.convert_unit(v, old_length, new_length) for v in val]
setattr(element, attr.name(), tuple(new_el))
else:
new_el = ifcopenshell.util.unit.convert_unit(val, old_length, new_length)
# set the new value
setattr(element, attr.name(), new_el)
for element, attr, val in iter_element_and_attributes_per_type(file_patched, "IfcLengthMeasure"):
if isinstance(val, tuple):
new_value = [ifcopenshell.util.unit.convert_unit(v, old_length, new_length) for v in val]
setattr(element, attr.name(), tuple(new_value))
else:
new_value = ifcopenshell.util.unit.convert_unit(val, old_length, new_length)
setattr(element, attr.name(), new_value)
file_patched.remove(old_length)
unit_assignment.Units = tuple([new_length, *unit_assignment.Units])