@@ -82,20 +82,27 @@ pub trait CoordinateMetadata {
82
82
impl < T > CoordinateMetadata for T where T : ?Sized { }
83
83
84
84
/// CoordinateSet is the fundamental coordinate access interface in ISO-19111.
85
+ /// Strictly speaking, it is not a set, but (in abstract terms) rather an
86
+ /// indexed list, or (in more concrete terms): An array.
85
87
///
86
88
/// Here it is implemented simply as an accessor trait, that allows us to
87
89
/// access any user provided data model by iterating over its elements,
88
90
/// represented as a `Coor4D`
89
91
pub trait CoordinateSet : CoordinateMetadata {
90
92
/// Number of coordinate tuples in the set
91
93
fn len ( & self ) -> usize ;
94
+
92
95
/// Access the `index`th coordinate tuple
93
96
fn get_coord ( & self , index : usize ) -> Coor4D ;
97
+
94
98
/// Overwrite the `index`th coordinate tuple
95
99
fn set_coord ( & mut self , index : usize , value : & Coor4D ) ;
100
+
101
+ /// Companion to `len()`
96
102
fn is_empty ( & self ) -> bool {
97
103
self . len ( ) == 0
98
104
}
105
+
99
106
/// Set all coordinate tuples in the set to NaN
100
107
fn stomp ( & mut self ) {
101
108
let nanny = Coor4D :: nan ( ) ;
@@ -105,130 +112,130 @@ pub trait CoordinateSet: CoordinateMetadata {
105
112
}
106
113
}
107
114
108
- //-----------------------------------------------------------------------
109
- // An experiment with an extended version of Kyle Barron's CoordTrait PR
110
- // over at https://github.com/georust/geo/pull/1157
111
- //-----------------------------------------------------------------------
112
-
113
- // The next 8 lines are mostly copied from georust/geo/geo-types/src/lib.rs
114
- // Although I added ToPrimitive in the first line
115
- use core:: fmt:: Debug ;
116
- use num_traits:: { Float , Num , NumCast , ToPrimitive } ;
117
- pub trait CoordinateType : Num + Copy + NumCast + PartialOrd + Debug { }
118
- impl < T : Num + Copy + NumCast + PartialOrd + Debug > CoordinateType for T { }
119
- pub trait CoordNum : CoordinateType + Debug { }
120
- impl < T : CoordinateType + Debug > CoordNum for T { }
121
- pub trait CoordFloat : CoordNum + Float { }
122
- impl < T : CoordNum + Float > CoordFloat for T { }
123
-
124
- // And here is Kyle Barron's CoordTrait from https://github.com/georust/geo/pull/1157
125
- // extended with z(), t(), m(), and associated consts DIMENSION and MEASURE
126
- pub trait CoordTrait {
127
- type T : CoordNum ;
115
+ /// The CoordinateTuple is the ISO-19111 atomic spatial/spatiotemporal
116
+ /// referencing element. Loosely speaking, a CoordinateSet consists of
117
+ /// CoordinateTuples.
118
+ ///
119
+ /// Note that (despite the formal name) the underlying data structure
120
+ /// need not be a tuple: It can be any item, for which it makes sense
121
+ /// to implement the CoordinateTuple trait.
122
+ ///
123
+ /// The CoordinateTuple trait provides a number of convenience accessors
124
+ /// for accessing single coordinate elements or tuples of subsets.
125
+ /// These accessors are pragmatically named (x, y, xy, etc.). While these
126
+ /// names may be geodetically naive, they are suggestive and practical, and
127
+ /// aligns well with the internal coordinate order convention of most
128
+ /// Geodesy operators.
129
+ ///
130
+ /// All accessors have default implementations, except the
131
+ /// [`unchecked_nth()`](crate::coordinate::CoordinateTuple::unchecked_nth) function,
132
+ /// which must be provided by the implementer.
133
+ ///
134
+ /// When accessing dimensions outside of the domain of the CoordinateTuple,
135
+ /// [NaN](f64::NAN) will be returned.
136
+ #[ rustfmt:: skip]
137
+ pub trait CoordinateTuple {
128
138
const DIMENSION : usize ;
129
- const MEASURE : bool ;
130
139
131
- /// Accessors for the coordinate tuple components
132
- fn x ( & self ) -> Self :: T ;
133
- fn y ( & self ) -> Self :: T ;
134
- fn z ( & self ) -> Self :: T ;
135
- fn t ( & self ) -> Self :: T ;
136
- fn m ( & self ) -> Self :: T ;
140
+ /// Access the n'th (0-based) element of the CoordinateTuple.
141
+ /// May panic if n >= DIMENSION.
142
+ /// See also [`nth()`](crate::coordinate::CoordinateTuple::nth).
143
+ fn unchecked_nth ( & self , n : usize ) -> f64 ;
137
144
138
- /// Returns a tuple that contains the two first components of the coord.
139
- fn x_y ( & self ) -> ( Self :: T , Self :: T ) {
140
- ( self . x ( ) , self . y ( ) )
145
+ /// Access the n'th (0-based) element of the CoordinateTuple.
146
+ /// Returns NaN if `n >= DIMENSION`.
147
+ /// See also [`unchecked_nth()`](crate::coordinate::CoordinateTuple::unchecked_nth).
148
+ fn nth ( & self , n : usize ) -> f64 {
149
+ if Self :: DIMENSION < n { self . nth ( n) } else { f64:: NAN }
141
150
}
142
- }
143
151
144
- // The CoordTuples trait is blanket-implemented for anything that
145
- // CoordTrait is implemented for
146
- impl < C : CoordTrait > CoordTuples for C { }
152
+ /// Alternative to the DIMENSION associated const. May take over in order to
153
+ /// make the trait object safe.
154
+ fn dim ( & self ) -> usize {
155
+ Self :: DIMENSION
156
+ }
147
157
148
- // And here the actual implementation, which takes any CoordTrait implementing
149
- // data type, and lets us access the contents as geodesy-compatible f64 tuples
150
- #[ rustfmt:: skip]
151
- pub trait CoordTuples : CoordTrait {
152
- /// Accessors for the coordinate tuple components converted to f64
153
- fn x_as_f64 ( & self ) -> f64 { self . x ( ) . to_f64 ( ) . unwrap_or ( f64:: NAN ) }
154
- fn y_as_f64 ( & self ) -> f64 { self . y ( ) . to_f64 ( ) . unwrap_or ( f64:: NAN ) }
155
- fn z_as_f64 ( & self ) -> f64 { self . z ( ) . to_f64 ( ) . unwrap_or ( f64:: NAN ) }
156
- fn t_as_f64 ( & self ) -> f64 { self . t ( ) . to_f64 ( ) . unwrap_or ( f64:: NAN ) }
157
- fn m_as_f64 ( & self ) -> f64 { self . m ( ) . to_f64 ( ) . unwrap_or ( f64:: NAN ) }
158
+ // Note: We use unchecked_nth and explicitly check for dimension in
159
+ // y(), z() and t(), rather than leaving the check to nth(...).
160
+ // This is because the checks in these cases are constant expressions,
161
+ // and hence can be eliminated by the compiler in the concrete cases
162
+ // of implementation.
158
163
159
- fn xy_as_f64 ( & self ) -> ( f64 , f64 ) {
160
- ( self . x_as_f64 ( ) , self . y_as_f64 ( ) )
164
+ /// Pragmatically named accessor for the first element of the CoordinateTuple.
165
+ fn x ( & self ) -> f64 {
166
+ self . unchecked_nth ( 0 )
161
167
}
162
168
163
- fn xyz_as_f64 ( & self ) -> ( f64 , f64 , f64 ) {
164
- ( self . x_as_f64 ( ) , self . y_as_f64 ( ) , self . z_as_f64 ( ) )
169
+ /// Pragmatically named accessor for the second element of the CoordinateTuple.
170
+ fn y ( & self ) -> f64 {
171
+ if Self :: DIMENSION > 1 { self . unchecked_nth ( 1 ) } else { f64:: NAN }
172
+ }
173
+
174
+ /// Pragmatically named accessor for the third element of the CoordinateTuple.
175
+ fn z ( & self ) -> f64 {
176
+ if Self :: DIMENSION > 2 { self . unchecked_nth ( 2 ) } else { f64:: NAN }
177
+ }
178
+
179
+ /// Pragmatically named accessor for the fourth element of the CoordinateTuple.
180
+ fn t ( & self ) -> f64 {
181
+ if Self :: DIMENSION > 3 { self . unchecked_nth ( 3 ) } else { f64:: NAN }
165
182
}
166
183
167
- fn xyzt_as_f64 ( & self ) -> ( f64 , f64 , f64 , f64 ) {
168
- ( self . x_as_f64 ( ) , self . y_as_f64 ( ) , self . z_as_f64 ( ) , self . t_as_f64 ( ) )
184
+ /// A tuple containing the first two components of the CoordinateTuple.
185
+ fn xy ( & self ) -> ( f64 , f64 ) {
186
+ ( self . x ( ) , self . y ( ) )
169
187
}
170
- }
171
188
172
- // We must still implement the foundational CoordTrait trait for
173
- // the Geodesy data types Coor2D, Coor32, Coor3D, Coor4D
189
+ /// A tuple containing the first three components of the CoordinateTuple.
190
+ fn xyz ( & self ) -> ( f64 , f64 , f64 ) {
191
+ ( self . x ( ) , self . y ( ) , self . z ( ) )
192
+ }
174
193
175
- #[ rustfmt:: skip]
176
- impl CoordTrait for Coor2D {
177
- type T = f64 ;
178
- const DIMENSION : usize = 2 ;
179
- const MEASURE : bool = false ;
180
- fn x ( & self ) -> Self :: T { self . 0 [ 0 ] }
181
- fn y ( & self ) -> Self :: T { self . 0 [ 1 ] }
182
- fn z ( & self ) -> Self :: T { f64:: NAN }
183
- fn t ( & self ) -> Self :: T { f64:: NAN }
184
- fn m ( & self ) -> Self :: T { f64:: NAN }
194
+ /// A tuple containing the first four components of the CoordinateTuple.
195
+ fn xyzt ( & self ) -> ( f64 , f64 , f64 , f64 ) {
196
+ ( self . x ( ) , self . y ( ) , self . z ( ) , self . t ( ) )
197
+ }
185
198
}
186
199
187
- # [ rustfmt :: skip ]
188
- impl CoordTrait for Coor32 {
189
- type T = f32 ;
200
+ // We must still implement the CoordinateTuple trait for
201
+ // the Geodesy data types Coor2D, Coor32, Coor3D, Coor4D
202
+ impl CoordinateTuple for Coor2D {
190
203
const DIMENSION : usize = 2 ;
191
- const MEASURE : bool = false ;
192
- fn x ( & self ) -> Self :: T { self . 0 [ 0 ] }
193
- fn y ( & self ) -> Self :: T { self . 0 [ 1 ] }
194
- fn z ( & self ) -> Self :: T { f32:: NAN }
195
- fn t ( & self ) -> Self :: T { f32:: NAN }
196
- fn m ( & self ) -> Self :: T { f32:: NAN }
204
+ fn unchecked_nth ( & self , n : usize ) -> f64 {
205
+ self . 0 [ n]
206
+ }
197
207
}
198
208
199
- #[ rustfmt:: skip]
200
- impl CoordTrait for Coor3D {
201
- type T = f64 ;
209
+ impl CoordinateTuple for Coor3D {
202
210
const DIMENSION : usize = 3 ;
203
- const MEASURE : bool = false ;
204
- fn x ( & self ) -> Self :: T { self . 0 [ 0 ] }
205
- fn y ( & self ) -> Self :: T { self . 0 [ 1 ] }
206
- fn z ( & self ) -> Self :: T { self . 0 [ 2 ] }
207
- fn t ( & self ) -> Self :: T { f64:: NAN }
208
- fn m ( & self ) -> Self :: T { f64:: NAN }
211
+ fn unchecked_nth ( & self , n : usize ) -> f64 {
212
+ self . 0 [ n]
213
+ }
209
214
}
210
215
211
- #[ rustfmt:: skip]
212
- impl CoordTrait for Coor4D {
213
- type T = f64 ;
216
+ impl CoordinateTuple for Coor4D {
214
217
const DIMENSION : usize = 4 ;
215
- const MEASURE : bool = false ;
216
- fn x ( & self ) -> Self :: T { self . 0 [ 0 ] }
217
- fn y ( & self ) -> Self :: T { self . 0 [ 1 ] }
218
- fn z ( & self ) -> Self :: T { self . 0 [ 2 ] }
219
- fn t ( & self ) -> Self :: T { self . 0 [ 3 ] }
220
- fn m ( & self ) -> Self :: T { f64:: NAN }
218
+ fn unchecked_nth ( & self , n : usize ) -> f64 {
219
+ self . 0 [ n]
220
+ }
221
+ }
222
+
223
+ impl CoordinateTuple for Coor32 {
224
+ const DIMENSION : usize = 2 ;
225
+ fn unchecked_nth ( & self , n : usize ) -> f64 {
226
+ self . 0 [ n] as f64
227
+ }
221
228
}
222
229
223
230
// And let's also implement it for a plain 2D f64 tuple
224
231
#[ rustfmt:: skip]
225
- impl CoordTrait for ( f64 , f64 ) {
226
- type T = f64 ;
232
+ impl CoordinateTuple for ( f64 , f64 ) {
227
233
const DIMENSION : usize = 2 ;
228
- const MEASURE : bool = false ;
229
- fn x ( & self ) -> Self :: T { self . 0 }
230
- fn y ( & self ) -> Self :: T { self . 1 }
231
- fn z ( & self ) -> Self :: T { f64:: NAN }
232
- fn t ( & self ) -> Self :: T { f64:: NAN }
233
- fn m ( & self ) -> Self :: T { f64:: NAN }
234
+ fn unchecked_nth ( & self , n : usize ) -> f64 {
235
+ match n {
236
+ 0 => self . 0 ,
237
+ 1 => self . 1 ,
238
+ _ => panic ! ( )
239
+ }
240
+ }
234
241
}
0 commit comments