mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
dms2dd and dd2dms - fix doc-strings, refactor, add tests (#6763)
This commit is contained in:
@@ -23,6 +23,7 @@ import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
from typing import NamedTuple, Optional, Union
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
|
||||
MatrixType = ifcopenshell.util.placement.MatrixType
|
||||
|
||||
@@ -39,39 +40,54 @@ class HelmertTransformation(NamedTuple):
|
||||
factor_z: float
|
||||
|
||||
|
||||
def dms2dd(degrees: int, minutes: int, seconds: int, ms: int = 0) -> float:
|
||||
"""Convert degrees, minutes, and (milli)seconds to decimal degrees
|
||||
def dms2dd(degrees: int, minutes: int, seconds: int, us: int = 0) -> float:
|
||||
"""Convert degrees, minutes, and (micro)seconds to decimal degrees
|
||||
All components must be either positive or negative.
|
||||
|
||||
:param degrees: The degrees component
|
||||
:param minutes: The minutes component
|
||||
:param seconds: The seconds component
|
||||
:param ms: The milliseconds component
|
||||
:param us: The microseconds component
|
||||
:return: The angle in decimal degrees.
|
||||
"""
|
||||
dd = float(degrees) + float(minutes) / 60.0 + float(seconds) / (3600.0) + float(ms / 3600000000.0)
|
||||
return dd
|
||||
all_positive_or_zero = degrees >= 0 and minutes >= 0 and seconds >= 0 and us >= 0
|
||||
all_negative_or_zero = degrees <= 0 and minutes <= 0 and seconds <= 0 and us <= 0
|
||||
assert all_positive_or_zero or all_negative_or_zero
|
||||
return degrees + minutes / 60.0 + seconds / 3600.0 + us / 3600000000.0
|
||||
|
||||
|
||||
def dd2dms(dd: float, use_ms: bool = False) -> Union[tuple[float, float, float, float], tuple[float, float, float]]:
|
||||
"""Convert decimal degrees to degrees, minutes, and (milli)seconds format
|
||||
def dd2dms(dd: float, use_us: bool = False) -> Union[tuple[int, int, int, int], tuple[int, int, float]]:
|
||||
"""Convert decimal degrees to degrees, minutes, and (micro)seconds format
|
||||
|
||||
:param dd: The decimal degrees
|
||||
:param use_ms: True if to include milliseconds and false otherwise. Defaults to false.
|
||||
:return: The angle in a tuple of either 3 or 4 values, being degrees,
|
||||
minutes, seconds, and optionally milliseconds.
|
||||
:param use_us: True if to include microseconds and false otherwise. Defaults to false.
|
||||
:return: The angle in a tuple of either 3 or 4 values,
|
||||
4 values: integer number of degrees, integer number of minutes, integer number of seconds and integer number of microseconds
|
||||
3 values: integer number of degrees, integer number of minutes, and a float number for seconds
|
||||
:note: the tuple follows the format of IfcCompoundPlaneAngleMeasure. Namely all of its components are either positive or negative.
|
||||
"""
|
||||
dd = float(dd)
|
||||
sign = 1 if dd >= 0 else -1
|
||||
dd = abs(dd)
|
||||
if use_ms:
|
||||
seconds, ms = divmod(dd * 60 * 60 * 1000000, 1000000)
|
||||
minutes, seconds = divmod(dd * 60 * 60, 60)
|
||||
degrees, minutes = divmod(minutes, 60)
|
||||
if dd < 0:
|
||||
degrees = -degrees
|
||||
if use_ms:
|
||||
return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign, int(ms) * sign)
|
||||
return (int(degrees) * sign, int(minutes) * sign, int(seconds) * sign)
|
||||
dd_decimal = Decimal(str(dd))
|
||||
|
||||
degrees = int(dd_decimal)
|
||||
degrees_decimal = Decimal(degrees)
|
||||
|
||||
fractional_part = dd_decimal - degrees_decimal
|
||||
|
||||
minutes_decimal = fractional_part * Decimal(60)
|
||||
minutes = int(minutes_decimal)
|
||||
minutes_decimal_int = Decimal(minutes)
|
||||
|
||||
seconds_decimal = (minutes_decimal - minutes_decimal_int) * Decimal(60)
|
||||
|
||||
if use_us:
|
||||
seconds = int(seconds_decimal)
|
||||
seconds_decimal_int = Decimal(seconds)
|
||||
microseconds_decimal = (seconds_decimal - seconds_decimal_int) * Decimal(1000000)
|
||||
microseconds = int(microseconds_decimal.quantize(Decimal(1), rounding=ROUND_HALF_UP))
|
||||
return (degrees, minutes, seconds, microseconds)
|
||||
else:
|
||||
seconds_float = float(seconds_decimal)
|
||||
return (degrees, minutes, seconds_float)
|
||||
|
||||
|
||||
def xyz2enh(
|
||||
|
||||
@@ -468,3 +468,29 @@ class TestAngle2YAxis(test.bootstrap.IFC4):
|
||||
assert np.allclose(subject.angle2yaxis(45), (-a, a))
|
||||
assert np.allclose(subject.angle2yaxis(-135), (a, -a))
|
||||
assert np.allclose(subject.angle2yaxis(135), (-a, -a))
|
||||
|
||||
|
||||
class TestDMS2DDandDD2DMS(test.bootstrap.IFC4):
|
||||
def test_dms2dd_and_dd2dms(self):
|
||||
test_cases_3tuple = [
|
||||
(35.41, (35, 24, 36.0)),
|
||||
(-116.89, (-116, -53, -24.0)),
|
||||
]
|
||||
test_cases_4tuple = [
|
||||
(40.431389, (40, 25, 53, 400)),
|
||||
(-4.248056, (-4, -14, -53, -1600)),
|
||||
(-35.401389, (-35, -24, -5, -400)),
|
||||
(148.981667, (148, 58, 54, 1200)),
|
||||
]
|
||||
|
||||
for dd, dms in test_cases_3tuple:
|
||||
d, m, s = subject.dd2dms(dd)
|
||||
assert (d, m, s) == dms
|
||||
dd_converted = subject.dms2dd(dms[0], dms[1], dms[2])
|
||||
assert dd_converted == dd
|
||||
|
||||
for dd, dms in test_cases_4tuple:
|
||||
d, m, s, us = subject.dd2dms(dd, use_us=True)
|
||||
assert (d, m, s, us) == dms
|
||||
dd_converted = subject.dms2dd(dms[0], dms[1], dms[2], dms[3])
|
||||
assert dd_converted == dd
|
||||
|
||||
Reference in New Issue
Block a user