Skip to content

Commit b5fb684

Browse files
authored
Merge branch 'main' into gergo/abstract_transport_no_pydantic
2 parents 52d53db + 65048cd commit b5fb684

File tree

8 files changed

+180
-166
lines changed

8 files changed

+180
-166
lines changed

src/specklepy/objects/GIS/CRS.py

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,12 @@
55
class CRS(Base, speckle_type="Objects.GIS.CRS"):
66
"""A Coordinate Reference System stored in wkt format"""
77

8-
def __init__(
9-
self,
10-
name: Optional[str] = None,
11-
authority_id: Optional[str] = None,
12-
wkt: Optional[str] = None,
13-
units: Optional[str] = None,
14-
units_native: Optional[str] = None,
15-
offset_x: Optional[float] = None,
16-
offset_y: Optional[float] = None,
17-
rotation: Optional[float] = None,
18-
**kwargs
19-
) -> None:
20-
super().__init__(**kwargs)
8+
name: Optional[str] = None
9+
authority_id: Optional[str] = None
10+
wkt: Optional[str] = None
11+
units_native: Optional[str] = None
12+
offset_x: Optional[float] = None
13+
offset_y: Optional[float] = None
14+
rotation: Optional[float] = None
2115

22-
self.name = name
23-
self.authority_id = authority_id
24-
self.wkt = wkt
25-
self.units = units or "m"
26-
self.units_native = units_native
27-
self.offset_x = offset_x
28-
self.offset_y = offset_y
29-
self.rotation = rotation
3016

src/specklepy/objects/GIS/geometry.py

Lines changed: 29 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,99 +6,48 @@
66

77
class GisPolygonGeometry(Base, speckle_type="Objects.GIS.PolygonGeometry", detachable={"displayValue"}):
88
"""GIS Polygon Geometry"""
9-
10-
def __init__(
11-
self,
12-
boundary: Optional[Union[Polyline, Arc, Line, Circle, Polycurve]] = None,
13-
voids: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]] ] = None,
14-
displayValue: Optional[List[Mesh]] = None,
15-
units: Optional[str] = None,
16-
**kwargs
17-
) -> None:
18-
super().__init__(**kwargs)
199

20-
self.boundary = boundary
21-
self.voids = voids
22-
self.displayValue = displayValue
23-
self.units = units or "m"
10+
boundary: Optional[Union[Polyline, Arc, Line, Circle, Polycurve]] = None
11+
voids: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]] ] = None
12+
displayValue: Optional[List[Mesh]] = None
2413

2514
class GisPolygonElement(Base, speckle_type="Objects.GIS.PolygonElement"):
2615
"""GIS Polygon element"""
27-
28-
def __init__(
29-
self,
30-
geometry: Optional[List[GisPolygonGeometry]] = None,
31-
attributes: Optional[Base] = None,
32-
units: Optional[str] = None,
33-
**kwargs
34-
) -> None:
35-
super().__init__(**kwargs)
3616

37-
self.geometry = geometry
38-
self.attributes = attributes
39-
self.units = units or "m"
17+
geometry: Optional[List[GisPolygonGeometry]] = None
18+
attributes: Optional[Base] = None
4019

4120
class GisLineElement(Base, speckle_type="Objects.GIS.LineElement"):
4221
"""GIS Polyline element"""
43-
44-
def __init__(
45-
self,
46-
geometry: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]]] = None,
47-
attributes: Optional[Base] = None,
48-
units: Optional[str] = None,
49-
**kwargs
50-
) -> None:
51-
super().__init__(**kwargs)
52-
53-
self.geometry = geometry
54-
self.attributes = attributes
55-
self.units = units or "m"
22+
23+
geometry: Optional[List[Union[Polyline, Arc, Line, Circle, Polycurve]]] = None,
24+
attributes: Optional[Base] = None,
5625

5726
class GisPointElement(Base, speckle_type="Objects.GIS.PointElement"):
5827
"""GIS Point element"""
59-
60-
def __init__(
61-
self,
62-
geometry: Optional[List[Point]] = None,
63-
attributes: Optional[Base] = None,
64-
units: Optional[str] = None,
65-
**kwargs
66-
) -> None:
67-
super().__init__(**kwargs)
68-
69-
self.geometry = geometry
70-
self.attributes = attributes
71-
self.units = units or "m"
28+
29+
geometry: Optional[List[Point]] = None,
30+
attributes: Optional[Base] = None,
7231

7332
class GisRasterElement(Base, speckle_type="Objects.GIS.RasterElement", detachable={"displayValue"}):
7433
"""GIS Raster element"""
7534

76-
def __init__(
77-
self,
78-
band_count: Optional[int] = None,
79-
band_names: Optional[List[str]] = None,
80-
x_origin: Optional[float] = None,
81-
y_origin: Optional[float] = None,
82-
x_size: Optional[int] = None,
83-
y_size: Optional[int] = None,
84-
x_resolution: Optional[float] = None,
85-
y_resolution: Optional[float] = None,
86-
noDataValue: Optional[List[float]] = None,
87-
displayValue: Optional[List[Mesh]] = None,
88-
units: Optional[str] = None,
89-
**kwargs
90-
) -> None:
91-
super().__init__(**kwargs)
92-
93-
self.band_count = band_count
94-
self.band_names = band_names
95-
self.x_origin = x_origin
96-
self.y_origin = y_origin
97-
self.x_size = x_size
98-
self.y_size = y_size
99-
self.x_resolution = x_resolution
100-
self.y_resolution = y_resolution
101-
self.noDataValue = noDataValue
102-
self.displayValue = displayValue
103-
self.units = units or "m"
35+
band_count: Optional[int] = None
36+
band_names: Optional[List[str]] = None
37+
x_origin: Optional[float] = None
38+
y_origin: Optional[float] = None
39+
x_size: Optional[int] = None
40+
y_size: Optional[int] = None
41+
x_resolution: Optional[float] = None
42+
y_resolution: Optional[float] = None
43+
noDataValue: Optional[List[float]] = None
44+
displayValue: Optional[List[Mesh]] = None
45+
46+
class GisTopography(GisRasterElement, speckle_type="Objects.GIS.GisTopography", detachable={"displayValue"}):
47+
"""GIS Raster element with 3d Topography representation"""
48+
49+
class GisNonGeometryElement(Base, speckle_type="Objects.GIS.NonGeometryElement"):
50+
"""GIS Table feature"""
51+
52+
attributes: Optional[Base] = None
10453

src/specklepy/objects/GIS/layers.py

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,22 @@ def __init__(
2828
self.geomType = geomType
2929
self.renderer = renderer or {}
3030

31-
class VectorLayer(Collection, detachable={"elements"}, speckle_type="Objects.GIS.VectorLayer", serialize_ignore={"features"}):
31+
class VectorLayer(
32+
Collection,
33+
detachable={"elements"},
34+
speckle_type="Objects.GIS.VectorLayer",
35+
serialize_ignore={"features"}):
36+
3237
"""GIS Vector Layer"""
3338

34-
def __init__(
35-
self,
36-
name: Optional[str]=None,
37-
crs: Optional[CRS]=None,
38-
units: Optional[str] = None,
39-
elements: Optional[List[Base]] = None,
40-
attributes: Optional[Base] = None,
41-
geomType: Optional[str] = None,
42-
renderer: Optional[Dict[str, Any]] = None,
43-
**kwargs
44-
) -> None:
45-
super().__init__(**kwargs)
46-
self.name = name or ""
47-
self.crs = crs
48-
self.units = units
49-
self.elements = elements or []
50-
self.attributes = attributes
51-
self.geomType = geomType or "None"
52-
self.renderer = renderer or {}
53-
self.collectionType = "VectorLayer"
39+
name: Optional[str]=None
40+
crs: Optional[CRS]=None
41+
units: Optional[str] = None
42+
elements: Optional[List[Base]] = None
43+
attributes: Optional[Base] = None
44+
geomType: Optional[str] = "None"
45+
renderer: Optional[Dict[str, Any]] = None
46+
collectionType = "VectorLayer"
5447

5548
@property
5649
@deprecated(version="2.14", reason="Use elements")
@@ -61,29 +54,23 @@ def features(self) -> Optional[List[Base]]:
6154
def features(self, value: Optional[List[Base]]) -> None:
6255
self.elements = value
6356

64-
class RasterLayer(Collection, detachable={"elements"}, speckle_type="Objects.GIS.RasterLayer", serialize_ignore={"features"}):
57+
class RasterLayer(
58+
Collection,
59+
detachable={"elements"},
60+
speckle_type="Objects.GIS.RasterLayer",
61+
serialize_ignore={"features"}):
62+
6563
"""GIS Raster Layer"""
6664

67-
def __init__(
68-
self,
69-
name: Optional[str] = None,
70-
crs: Optional[CRS]=None,
71-
units: Optional[str] = None,
72-
rasterCrs: Optional[CRS]=None,
73-
elements: Optional[List[Base]] = None,
74-
geomType: Optional[str] = None,
75-
renderer: Optional[Dict[str, Any]] = None,
76-
**kwargs
77-
) -> None:
78-
super().__init__(**kwargs)
79-
self.name = name or ""
80-
self.crs = crs
81-
self.units = units
82-
self.rasterCrs = rasterCrs
83-
self.elements = elements or []
84-
self.geomType = geomType or "None"
85-
self.renderer = renderer or {}
86-
self.collectionType = "RasterLayer"
65+
name: Optional[str] = None
66+
crs: Optional[CRS]=None
67+
units: Optional[str] = None
68+
rasterCrs: Optional[CRS]=None
69+
elements: Optional[List[Base]] = None
70+
geomType: Optional[str] = "None"
71+
renderer: Optional[Dict[str, Any]] = None
72+
collectionType = "RasterLayer"
73+
8774

8875
@property
8976
@deprecated(version="2.14", reason="Use elements")

src/specklepy/objects/base.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from stringcase import pascalcase
2020

21-
from specklepy.logging.exceptions import SpeckleException
21+
from specklepy.logging.exceptions import SpeckleException, SpeckleInvalidUnitException
2222
from specklepy.objects.units import Units, get_units_from_string
2323
from specklepy.transports.memory import MemoryTransport
2424

@@ -322,7 +322,7 @@ class Base(_RegisteringBase):
322322
id: Union[str, None] = None
323323
totalChildrenCount: Union[int, None] = None
324324
applicationId: Union[str, None] = None
325-
_units: Union[Units, None] = None
325+
_units: Union[None, str] = None
326326

327327
def __init__(self, **kwargs) -> None:
328328
super().__init__()
@@ -463,22 +463,19 @@ def add_detachable_attrs(self, names: Set[str]) -> None:
463463

464464
@property
465465
def units(self) -> Union[str, None]:
466-
if self._units:
467-
return self._units.value
468-
return None
466+
return self._units
469467

470468
@units.setter
471469
def units(self, value: Union[str, Units, None]):
472-
if value is None:
473-
units = value
470+
"""While this property accepts any string value, geometry expects units to be specific strings (see Units enum)"""
471+
if isinstance(value, str) or value is None:
472+
self._units = value
474473
elif isinstance(value, Units):
475-
units: Units = value
474+
self._units = value.value
476475
else:
477-
units = get_units_from_string(value)
478-
self._units = units
479-
# except SpeckleInvalidUnitException as ex:
480-
# warn(f"Units are reset to None. Reason {ex.message}")
481-
# self._units = None
476+
raise SpeckleInvalidUnitException(
477+
f"Unknown type {type(value)} received for units"
478+
)
482479

483480
def get_member_names(self) -> List[str]:
484481
"""Get all of the property names on this object, dynamic or not"""

src/specklepy/objects/units.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Units(Enum):
3535
Units.none: ["none", "null"],
3636
}
3737

38+
3839
UNITS_ENCODINGS = {
3940
Units.none: 0,
4041
None: 0,
@@ -49,6 +50,20 @@ class Units(Enum):
4950
}
5051

5152

53+
UNIT_SCALE = {
54+
Units.none: 1,
55+
Units.mm: 0.001,
56+
Units.cm: 0.01,
57+
Units.m: 1.0,
58+
Units.km: 1000.0,
59+
Units.inches: 0.0254,
60+
Units.feet: 0.3048,
61+
Units.yards: 0.9144,
62+
Units.miles: 1609.340,
63+
}
64+
"""Unit scaling factor to meters"""
65+
66+
5267
def get_units_from_string(unit: str) -> Units:
5368
if not isinstance(unit, str):
5469
raise SpeckleInvalidUnitException(unit)
@@ -59,10 +74,10 @@ def get_units_from_string(unit: str) -> Units:
5974
raise SpeckleInvalidUnitException(unit)
6075

6176

62-
def get_units_from_encoding(unit: int):
77+
def get_units_from_encoding(unit: int) -> Units:
6378
for name, encoding in UNITS_ENCODINGS.items():
6479
if unit == encoding:
65-
return name
80+
return name or Units.none
6681

6782
raise SpeckleException(
6883
message=(
@@ -72,13 +87,36 @@ def get_units_from_encoding(unit: int):
7287
)
7388

7489

75-
def get_encoding_from_units(unit: Union[Units, None]):
90+
def get_encoding_from_units(unit: Union[Units, str, None]):
91+
maybe_sanitized_unit = unit
92+
if isinstance(unit, str):
93+
for unit_enum, aliases in UNITS_STRINGS.items():
94+
if unit in aliases:
95+
maybe_sanitized_unit = unit_enum
7696
try:
77-
return UNITS_ENCODINGS[unit]
97+
return UNITS_ENCODINGS[maybe_sanitized_unit]
7898
except KeyError as e:
7999
raise SpeckleException(
80100
message=(
81-
f"No encoding exists for unit {unit}."
101+
f"No encoding exists for unit {maybe_sanitized_unit}."
82102
f"Please enter a valid unit to encode (eg {UNITS_ENCODINGS})."
83103
)
84104
) from e
105+
106+
107+
def get_scale_factor_from_string(fromUnits: str, toUnits: str) -> float:
108+
"""Returns a scalar to convert distance values from one unit system to another"""
109+
return get_scale_factor(get_units_from_string(fromUnits), get_units_from_string(toUnits))
110+
111+
112+
def get_scale_factor(fromUnits: Units, toUnits: Units) -> float:
113+
"""Returns a scalar to convert distance values from one unit system to another"""
114+
return get_scale_factor_to_meters(fromUnits) / get_scale_factor_to_meters(toUnits)
115+
116+
117+
def get_scale_factor_to_meters(fromUnits: Units) -> float:
118+
"""Returns a scalar to convert distance values from one unit system to meters"""
119+
if fromUnits not in UNIT_SCALE:
120+
raise ValueError(f"Invalid units provided: {fromUnits}")
121+
122+
return UNIT_SCALE[fromUnits]

tests/intergration/test_serialization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def test_serialize(self, base):
1717
deserialized = operations.deserialize(serialized)
1818

1919
assert base.get_id() == deserialized.get_id()
20-
assert base.units == "mm"
20+
assert base.units == "millimetres"
2121
assert isinstance(base.test_bases[0], Base)
2222
assert base["@revit_thing"].speckle_type == "SpecialRevitFamily"
2323
assert base["@detach"].name == deserialized["@detach"].name

0 commit comments

Comments
 (0)