New utility functions for automatic local to global elevation conversions and easier unit conversions

This commit is contained in:
Dion Moult
2023-02-02 23:01:12 +11:00
parent f77d2f449d
commit 7c275a99a2
2 changed files with 87 additions and 4 deletions
@@ -18,6 +18,8 @@
import math
import numpy as np
import ifcopenshell
import ifcopenshell.util.unit
def dms2dd(degrees, minutes, seconds, ms=0):
@@ -52,6 +54,45 @@ def xyz2enh(x, y, z, eastings, northings, orthogonal_height, x_axis_abscissa, x_
return (eastings, northings, height)
def auto_z2e(ifc_file, z):
"""Convert a Z coordinate to an elevation using model georeferencing data
The necessary georeferencing map conversion is automatically detected from
the IFC map conversion parameters present in the IFC model. If no map
conversion is present, then the Z coordinate is returned unchanged.
:param ifc_file: The IFC file
:type ifc_file: ifcopenshell.file.file
:param z: The Z local engineering coordinate provided in project length units.
:type z: float
:return: The elevation in project length units.
:rtype: float
"""
try:
conversion = ifc_file.by_type("IfcMapConversion")
except:
return z
if not conversion or not conversion[0].OrthogonalHeight:
return z
conversion = conversion[0]
h = conversion.OrthogonalHeight
map_unit = conversion.TargetCRS.MapUnit
if map_unit:
project_unit = ifcopenshell.util.unit.get_project_unit(ifc_file, "LENGTHUNIT")
h = ifcopenshell.util.unit.convert(
h,
getattr(map_unit, "Prefix", None),
map_unit.Name,
getattr(project_unit, "Prefix", None),
project_unit.Name,
)
return z2e(z, h)
def z2e(z, h):
return z + h
def enh2xyz(e, n, h, eastings, northings, orthogonal_height, x_axis_abscissa, x_axis_ordinate, scale=None):
if scale is None:
scale = 1.0
@@ -325,6 +325,25 @@ def get_unit_assignment(ifc_file):
return unit_assignments[0]
def get_project_unit(ifc_file, unit_type):
"""Get the default project unit of a particular unit type
:param ifc_file: The IFC file.
:type ifc_file: ifcopenshell.file.file
:param unit_type: The type of unit, taken from the list of IFC unit types,
such as "LENGTHUNIT".
:type unit_type: str
:return: The IFC unit entity, or nothing if there is no default project unit
defined.
:rtype: ifcopenshell.entity_instance,None
"""
unit_assignment = get_unit_assignment(ifc_file)
if unit_assignment:
for unit in unit_assignment.Units or []:
if getattr(unit, "UnitType", None) == unit_type:
return unit
def get_property_unit(prop, ifc_file):
unit = getattr(prop, "Unit", None)
if unit:
@@ -431,19 +450,42 @@ def get_unit_symbol(unit):
return "?"
def convert_unit(value, from_unit, to_unit):
"""Convert from one unit to another unit
:param value: The numeric value you want to convert
:type value: float
:param from_unit: The IfcNamedUnit to confirm from.
:type from_unit: ifcopenshell.entity_instance.entity_instance
:param to_unit: The IfcNamedUnit to confirm from.
:type to_unit: ifcopenshell.entity_instance.entity_instance
:return: The converted value.
:rtype: float
"""
return convert(
value, getattr(from_unit, "Prefix", None), from_unit.Name, getattr(to_unit, "Prefix", None), to_unit.Name
)
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
"""Converts between length, area, and volume units
In this case, you manually specify the names and (optionally) prefixes to
convert to and from. In case you want to automatically convert to units
already available as IFC entities, consider using convert_unit() instead.
:param value: The numeric value you want to convert
:type value: float
:param from_prefix: A prefix from IfcSIPrefix. Can be None.
:type from_prefix: string
:type from_prefix: str,optional
:param from_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type from_unit: string
:type from_unit: str
:param to_prefix: A prefix from IfcSIPrefix. Can be None.
:type to_prefix: string
:type to_prefix: str,optional
:param to_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type to_unit: string
:type to_unit: str
:return: The converted value.
:rtype: float
"""
if from_unit in si_conversions:
value *= si_conversions[from_unit]