From 2b583fd822b4a89fb19d320bb471c368a743c238 Mon Sep 17 00:00:00 2001 From: Jedd Morgan <45512892+JR-Morgan@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:34:16 +0100 Subject: [PATCH] Added unit scaling functions in units.py --- src/specklepy/objects/units.py | 37 ++++++++++++++++++++-- tests/unit/test_unit_scaling.py | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_unit_scaling.py diff --git a/src/specklepy/objects/units.py b/src/specklepy/objects/units.py index 182ea1d3..d795738c 100644 --- a/src/specklepy/objects/units.py +++ b/src/specklepy/objects/units.py @@ -35,6 +35,7 @@ class Units(Enum): Units.none: ["none", "null"], } + UNITS_ENCODINGS = { Units.none: 0, None: 0, @@ -49,6 +50,20 @@ class Units(Enum): } +UNIT_SCALE = { + Units.none: 1, + Units.mm: 0.001, + Units.cm: 0.01, + Units.m: 1.0, + Units.km: 1000.0, + Units.inches: 0.0254, + Units.feet: 0.3048, + Units.yards: 0.9144, + Units.miles: 1609.340, +} +"""Unit scaling factor to meters""" + + def get_units_from_string(unit: str) -> Units: if not isinstance(unit, str): raise SpeckleInvalidUnitException(unit) @@ -59,10 +74,10 @@ def get_units_from_string(unit: str) -> Units: raise SpeckleInvalidUnitException(unit) -def get_units_from_encoding(unit: int): +def get_units_from_encoding(unit: int) -> Units: for name, encoding in UNITS_ENCODINGS.items(): if unit == encoding: - return name + return name or Units.none raise SpeckleException( message=( @@ -87,3 +102,21 @@ def get_encoding_from_units(unit: Union[Units, str, None]): f"Please enter a valid unit to encode (eg {UNITS_ENCODINGS})." ) ) from e + + +def get_scale_factor_from_string(fromUnits: str, toUnits: str) -> float: + """Returns a scalar to convert distance values from one unit system to another""" + return get_scale_factor(get_units_from_string(fromUnits), get_units_from_string(toUnits)) + + +def get_scale_factor(fromUnits: Units, toUnits: Units) -> float: + """Returns a scalar to convert distance values from one unit system to another""" + return get_scale_factor_to_meters(fromUnits) / get_scale_factor_to_meters(toUnits) + + +def get_scale_factor_to_meters(fromUnits: Units) -> float: + """Returns a scalar to convert distance values from one unit system to meters""" + if fromUnits not in UNIT_SCALE: + raise ValueError(f"Invalid units provided: {fromUnits}") + + return UNIT_SCALE[fromUnits] \ No newline at end of file diff --git a/tests/unit/test_unit_scaling.py b/tests/unit/test_unit_scaling.py new file mode 100644 index 00000000..34627462 --- /dev/null +++ b/tests/unit/test_unit_scaling.py @@ -0,0 +1,56 @@ + + +import pytest + +from specklepy.objects.units import Units, get_scale_factor + + +@pytest.mark.parametrize( + "fromUnits, toUnits, inValue, expectedOutValue", + [ + #To self + (Units.km, Units.km, 1.5, 1.5), + (Units.km, Units.km, 0, 0), + (Units.m, Units.m, 1.5, 1.5), + (Units.m, Units.m, 0, 0), + (Units.cm, Units.cm, 1.5, 1.5), + (Units.cm, Units.cm, 0, 0), + (Units.mm, Units.mm, 1.5, 1.5), + (Units.mm, Units.mm, 0, 0), + (Units.miles, Units.miles, 1.5, 1.5), + (Units.miles, Units.miles, 0, 0), + (Units.yards, Units.yards, 1.5, 1.5), + (Units.yards, Units.yards, 0, 0), + (Units.feet, Units.feet, 1.5, 1.5), + (Units.feet, Units.feet, 0, 0), + + #To Meters + (Units.km, Units.m, 987654.321, 987654321), + (Units.m, Units.m, 987654.321, 987654.321), + (Units.mm, Units.m, 98765432.1, 98765.4321), + (Units.cm, Units.m, 9876543.21, 98765.4321), + + #To negative meters + (Units.km, Units.m, -987654.321, -987654321), + (Units.m, Units.m,- 987654.321, -987654.321), + (Units.mm, Units.m, -98765432.1, -98765.4321), + (Units.cm, Units.m, -9876543.21, -98765.4321), + + (Units.m, Units.km, 987654.321, 987.654321), + (Units.m, Units.cm, 987654.321, 98765432.1), + (Units.m, Units.mm, 987654.321, 987654321), + + #Imperial + (Units.miles, Units.m, 123.45, 198673.517), + (Units.miles, Units.inches, 123.45, 7821792), + (Units.yards, Units.m, 123.45, 112.88268), + (Units.yards, Units.inches, 123.45, 4444.2), + (Units.feet, Units.m, 123.45, 37.62756), + (Units.feet, Units.inches, 123.45, 1481.4), + (Units.inches, Units.m, 123.45, 3.13563), + ], +) +def test_get_scale_factor_between_units(fromUnits: Units, toUnits: Units, inValue: float, expectedOutValue: float): + Tolerance = 1e-10 + actual = inValue * get_scale_factor(fromUnits, toUnits) + assert(actual - expectedOutValue < Tolerance)