1
1
from __future__ import annotations
2
2
3
- from dataclasses import dataclass
4
- from typing import ClassVar
5
- from typing import Final
3
+ from typing import TYPE_CHECKING , ClassVar
6
4
7
- from autoware_mtr .datatype import PolylineLabel
8
5
import numpy as np
9
- from numpy .typing import NDArray
6
+ # from dataclasses import field
7
+ from attr import define , field
8
+ from typing_extensions import Self
10
9
11
- __all__ = ( "Polyline" ,)
10
+ from awml_pred . datatype import MapType
12
11
12
+ from awml_pred .dataclass .utils import to_np_f32
13
13
14
- # TODO(ktro2828): Type definition
14
+ if TYPE_CHECKING :
15
+ from awml_pred .typing import NDArray , NDArrayF32
15
16
17
+ __all__ = ["Polyline" ]
16
18
17
- @dataclass
19
+
20
+ @define
18
21
class Polyline :
19
- """
20
- A dataclass of Polyline.
22
+ """A dataclass of Polyline.
21
23
22
24
Attributes
23
25
----------
24
- polyline_type (PolylineType ): `PolylineType` instance .
25
- waypoints (NDArray ): Waypoints of polyline.
26
+ polyline_type (MapType ): Type of polyline .
27
+ waypoints (NDArrayF32 ): Waypoints of polyline.
26
28
27
29
"""
28
30
29
- polyline_type : PolylineLabel
30
- waypoints : NDArray
31
+ polyline_type : MapType = field ()
32
+ waypoints : NDArrayF32 = field ( converter = to_np_f32 )
31
33
32
34
# NOTE: For the 1DArray indices must be a list.
33
35
XYZ_IDX : ClassVar [list [int ]] = [0 , 1 , 2 ]
34
36
XY_IDX : ClassVar [list [int ]] = [0 , 1 ]
35
37
FULL_DIM3D : ClassVar [int ] = 7
36
38
FULL_DIM2D : ClassVar [int ] = 5
37
39
38
- def __post_init__ (self ) -> None :
39
- if not isinstance (self .waypoints , np .ndarray ):
40
- self .waypoints = np .array (self .waypoints , dtype = np .float32 )
41
- assert isinstance (self .waypoints , np .ndarray )
42
- min_ndim : Final [int ] = 1
43
- point_dim : Final [int ] = 3
44
- assert self .waypoints .ndim > min_ndim and self .waypoints .shape [1 ] == point_dim
45
- assert isinstance (self .polyline_type , PolylineLabel )
40
+ @polyline_type .validator
41
+ def _check_type (self , attr , value ) -> None :
42
+ if not isinstance (value , MapType ):
43
+ raise TypeError (f"Unexpected type of { attr .name } : { type (value )} " )
44
+
45
+ @waypoints .validator
46
+ def _check_dim (self , attribute , value ) -> None :
47
+ if value .ndim < 1 or value .shape [1 ] != 3 :
48
+ raise ValueError (f"Unexpected { attribute .name } dimensions." )
49
+
50
+ @classmethod
51
+ def from_dict (cls , data : dict ) -> Self :
52
+ """Construct an instance from dict data.
53
+
54
+ Args:
55
+ ----
56
+ data (dict): Dict data of `Polyline`.
57
+
58
+ Returns:
59
+ -------
60
+ Polyline: Constructed instance.
61
+
62
+ """
63
+ return cls (** data )
46
64
47
65
@property
48
66
def xyz (self ) -> NDArray :
49
- """
50
- Return 3D positions.
67
+ """Return 3D positions.
51
68
52
69
Returns
53
70
-------
@@ -62,8 +79,7 @@ def xyz(self, xyz: NDArray) -> None:
62
79
63
80
@property
64
81
def xy (self ) -> NDArray :
65
- """
66
- Return 2D positions.
82
+ """Return 2D positions.
67
83
68
84
Returns
69
85
-------
@@ -78,8 +94,7 @@ def xy(self, xy: NDArray) -> None:
78
94
79
95
@property
80
96
def dxyz (self ) -> NDArray :
81
- """
82
- Return 3D normalized directions. The first element always becomes (0, 0, 0).
97
+ """Return 3D normalized directions. The first element always becomes (0, 0, 0).
83
98
84
99
Returns
85
100
-------
@@ -89,14 +104,12 @@ def dxyz(self) -> NDArray:
89
104
if self .is_empty ():
90
105
return np .empty ((0 , 3 ), dtype = np .float32 )
91
106
diff = np .diff (self .xyz , axis = 0 , prepend = self .xyz [0 ].reshape (- 1 , 3 ))
92
- norm = np .linalg .norm (diff , axis = - 1 , keepdims = True )
93
- zero : Final [float ] = 0.0
94
- return np .divide (diff , norm , where = (diff != zero ) & (norm != zero ))
107
+ norm = np .clip (np .linalg .norm (diff , axis = - 1 , keepdims = True ), a_min = 1e-6 , a_max = 1e9 )
108
+ return np .divide (diff , norm )
95
109
96
110
@property
97
111
def dxy (self ) -> NDArray :
98
- """
99
- Return 2D normalized directions. The first element always becomes (0, 0).
112
+ """Return 2D normalized directions. The first element always becomes (0, 0).
100
113
101
114
Returns
102
115
-------
@@ -106,40 +119,14 @@ def dxy(self) -> NDArray:
106
119
if self .is_empty ():
107
120
return np .empty ((0 , 2 ), dtype = np .float32 )
108
121
diff = np .diff (self .xy , axis = 0 , prepend = self .xy [0 ].reshape (- 1 , 2 ))
109
- norm = np .linalg .norm (diff , axis = - 1 , keepdims = True )
110
- zero : Final [float ] = 0.0
111
- return np .divide (diff , norm , where = (diff != zero ) & (norm != zero ))
112
-
113
- @property
114
- def type_id (self ) -> int :
115
- """
116
- Return the type ID in `int`.
117
-
118
- Returns
119
- -------
120
- int: Type ID.
121
-
122
- """
123
- return self .polyline_type .value
124
-
125
- @property
126
- def type_str (self ) -> str :
127
- """
128
- Return the type in `str`.
129
-
130
- Returns
131
- -------
132
- str: Type in `str`.
133
-
134
- """
135
- return self .polyline_type .as_str ()
122
+ norm = np .clip (np .linalg .norm (diff , axis = - 1 , keepdims = True ), a_min = 1e-6 , a_max = 1e9 )
123
+ return np .divide (diff , norm )
136
124
137
125
def __len__ (self ) -> int :
138
126
return len (self .waypoints )
139
127
140
128
def is_empty (self ) -> bool :
141
- """
142
- Indicate whether waypoints is empty array.
129
+ """Indicate whether waypoints is empty array.
143
130
144
131
Returns
145
132
-------
@@ -148,21 +135,19 @@ def is_empty(self) -> bool:
148
135
"""
149
136
return len (self .waypoints ) == 0
150
137
151
- def as_array (self , * , full : bool = False , as_3d : bool = True ) -> NDArray :
152
- """
153
- Return the polyline as `NDArray`.
138
+ def as_array (self , * , full : bool = False , as_3d : bool = True ) -> NDArrayF32 :
139
+ """Return the polyline as `NDArray`.
154
140
155
141
Args:
156
142
----
157
- full (bool, optional): Indicates whether to return
158
- `(x, y, z, dx, dy, dz, type_id)`. If `False`, returns `(x, y, z)`.
159
- Defaults to False.
143
+ full (bool, optional): Indicates whether to return `(x, y, z, dx, dy, dz, type_id)`.
144
+ If `False`, returns `(x, y, z)`. Defaults to False.
160
145
as_3d (bool, optional): If `True` returns array containing 3D coordinates.
161
146
Otherwise, 2D coordinates. Defaults to True.
162
147
163
148
Returns:
164
149
-------
165
- NDArray : Polyline array.
150
+ NDArrayF32 : Polyline array.
166
151
167
152
"""
168
153
if full :
@@ -174,7 +159,7 @@ def as_array(self, *, full: bool = False, as_3d: bool = True) -> NDArray:
174
159
)
175
160
176
161
shape = self .waypoints .shape [:- 1 ]
177
- type_id = np .full ((* shape , 1 ), self .type_id )
162
+ type_id = np .full ((* shape , 1 ), self .polyline_type . value )
178
163
return (
179
164
np .concatenate ([self .xyz , self .dxyz , type_id ], axis = 1 , dtype = np .float32 )
180
165
if as_3d
0 commit comments