mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
Merge pull request #4125 from Krande/fix/convert-units
Proposal for updating IfcPatch ConvertLengthUnit.py for compatibility with boost>=1.82
This commit is contained in:
@@ -16,8 +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/>.
|
||||
|
||||
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,
|
||||
@@ -214,12 +218,10 @@ si_conversions = {
|
||||
"fahrenheit": 1.8,
|
||||
}
|
||||
|
||||
|
||||
si_offsets = {
|
||||
"fahrenheit": -459.67,
|
||||
}
|
||||
|
||||
|
||||
imperial_types = {
|
||||
"thou": "LENGTHUNIT",
|
||||
"inch": "LENGTHUNIT",
|
||||
@@ -260,7 +262,6 @@ imperial_types = {
|
||||
"fahrenheit": "THERMODYNAMICTEMPERATUREUNIT",
|
||||
}
|
||||
|
||||
|
||||
prefix_symbols = {
|
||||
"EXA": "E",
|
||||
"PETA": "P",
|
||||
@@ -558,13 +559,13 @@ def calculate_unit_scale(ifc_file):
|
||||
|
||||
|
||||
def format_length(
|
||||
value,
|
||||
precision,
|
||||
decimal_places=2,
|
||||
suppress_zero_inches=True,
|
||||
unit_system="imperial",
|
||||
input_unit="foot",
|
||||
output_unit="foot",
|
||||
value,
|
||||
precision,
|
||||
decimal_places=2,
|
||||
suppress_zero_inches=True,
|
||||
unit_system="imperial",
|
||||
input_unit="foot",
|
||||
output_unit="foot",
|
||||
):
|
||||
"""Formats a length for readability and imperial formatting
|
||||
|
||||
@@ -624,3 +625,79 @@ def format_length(
|
||||
elif unit_system == "metric":
|
||||
rounded_val = round(value / precision) * precision
|
||||
return f"{rounded_val:.{decimal_places}f}"
|
||||
|
||||
|
||||
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() == 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() == 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() == 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
|
||||
|
||||
# Copy all elements from the original file to the patched file
|
||||
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)
|
||||
|
||||
# Traverse all elements and their nested attributes in the file and convert them
|
||||
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])
|
||||
|
||||
return file_patched
|
||||
|
||||
Reference in New Issue
Block a user