@@ -9,94 +9,89 @@ use geo::prelude::*;
9
9
use geo:: { coord, Line , LineString , Point , Rect } ;
10
10
use thiserror:: Error ;
11
11
12
- /// A curve is the fundamental building block for an LRM
13
- /// It provides basic primitives to locate/project points on it
14
- /// A curve can be part of a larger curve (e.g. for optimisation purpurses and have better bounding boxes)
15
- /// The curve can be implemented
12
+ /// A `Curve` is the fundamental building block for an LRM.
13
+ /// It provides basic primitives to locate/project points on it.
14
+ /// A `Curve` can be part of a larger `Curve` (e.g. for optimisation purposes and to have better bounding boxes).
15
+ /// The `Curve` can be implemented.
16
16
pub trait Curve {
17
- /// Project the point to the closest position on the curve
18
- /// Will fail if the curve is invalid (e.g. no points on it)
19
- /// or if the point is to far away
20
- /// If the curve is a piece of a larger curve (start_offset > 0)
21
- /// then the distance_along_curve if from the whole curve, not just the current piece
17
+ /// Builds a new `Curve` from a [LineString].
18
+ /// `max_extent` is the maximum distance that is considered to be “on the curve”.
19
+ /// `max_extent` plays a role in the bounding box.
20
+ fn new ( geom : LineString , max_extent : f64 ) -> Self ;
21
+
22
+ /// Projects the [Point] to the closest position on the `Curve`.
23
+ /// Will fail if the `Curve` is invalid (e.g. no `Point` on it)
24
+ /// or if the `Point` is too far away.
25
+ /// If the `Curve` is a piece of a larger `Curve` (`start_offset > 0`)
26
+ /// then the `distance_along_curve` if from the whole `Curve`, not just the current piece.
22
27
fn project ( & self , point : Point ) -> Result < CurveProjection , CurveError > ;
23
28
24
- /// Returns the geographical position of a point on the curve
25
- /// Will return an error if the CurveProjection is not on this Curve
26
- fn resolve ( & self , projection : & CurveProjection ) -> Result < Point , CurveError > ;
29
+ /// Returns the geographical position of a [Point] on the `Curve`.
30
+ /// Will return an error if the ` CurveProjection` is not on this ` Curve`.
31
+ fn resolve ( & self , projection : CurveProjection ) -> Result < Point , CurveError > ;
27
32
28
- /// Bounding box of the curve with a buffer of `max_extent`
33
+ /// Bounding box of the `Curve` with a buffer of `max_extent`.
29
34
fn bbox ( & self ) -> Rect ;
30
35
31
- /// The length of the curve
36
+ /// The length of the `Curve`.
32
37
fn length ( & self ) -> f64 ;
33
38
34
- /// Computes the normal at a given offset on the curve
35
- /// Will return an error if the curve is invalid or the offset is outside of the curve
36
- /// Points to the positive side (left)
39
+ /// Computes the normal at a given offset on the `Curve`.
40
+ /// Will return an error if the `Curve` is invalid or the offset is outside of the `Curve`.
41
+ /// Points to the positive side (left).
37
42
fn get_normal ( & self , offset : f64 ) -> Result < ( f64 , f64 ) , CurveError > ;
38
43
39
- /// Returns the point where the curve and the segment intersect
40
- /// If the segment intersects the curve multiple times, an intersection is chosen randomly
41
- /// When the segment is colinear with the curve it is ignored
44
+ /// Returns the [Point] where the `Curve` and the segment intersect.
45
+ /// If the segment intersects the `Curve` multiple times, an intersection is chosen randomly.
46
+ /// When the segment is colinear with the `Curve` it is ignored.
42
47
fn intersect_segment ( & self , segment : Line ) -> Option < Point > ;
43
48
44
- /// Is the geometry valid. Depending on the representation
45
- /// It must have at least two coordinates
46
- /// If there are exactly two coordinates, they must be different
49
+ /// Is the geometry valid. Depending on the representation.
50
+ /// It must have at least two coordinates.
51
+ /// If there are exactly two coordinates, they must be different.
47
52
fn is_valid ( & self ) -> bool ;
53
+
54
+ /// How far from the `Curve` could be considered to be still on the `Curve`.
55
+ fn max_extent ( & self ) -> f64 ;
48
56
}
49
57
50
- /// Errors when manipulating the curves
51
- #[ derive( Error , Debug ) ]
58
+ /// Errors when manipulating the [Curve] objects.
59
+ #[ derive( Error , Debug , PartialEq ) ]
52
60
pub enum CurveError {
53
- /// Depeding on the curve implementation the condition of validity might differ
61
+ /// The condition of validity might differ depending on the [Curve] implementation.
54
62
#[ error( "the curve geometry is not valid" ) ]
55
63
InvalidGeometry ,
56
- /// At least a coordinate is non a finite number (NaN, inifinite)
64
+ /// At least one coordinate is non a finite number (` NaN`, `infinite`).
57
65
#[ error( "the coordinates are not finite" ) ]
58
66
NotFiniteCoordinates ,
59
- /// A considered point is not on the curve
67
+ /// The considered [Point] is not on the [Curve].
60
68
#[ error( "the point is not on the curve" ) ]
61
69
NotOnTheCurve ,
62
70
}
63
71
64
72
/// Implementation based on [LineString]:
65
- /// the curve is a string of continous [Line]
66
- /// The coordinates are reprensented by f64.
67
- /// That means a precison of about 1_000_000th of a mm for a curve that spans around the Earth
73
+ /// the [Curve] is a string of continous [Line].
74
+ /// The coordinates are reprensented by ` f64` .
75
+ /// That means a precison of about 1_000_000th of a mm for a `Curve` that spans around the Earth.
68
76
pub struct LineStringCurve {
69
- /// When curve might be a piece of a longer curve
70
- /// then the start_offset allows to know how fare along the longer curve we are
77
+ /// When a [Curve] might be a piece of a longer `Curve`
78
+ /// then the ` start_offset` allows to know how fare along the longer `Curve` we are.
71
79
pub start_offset : f64 ,
72
80
73
- /// The max distance that is considered of being part of the curve
74
- /// It is used to compute the bounding box
81
+ /// The max distance that is considered of being part of the [Curve].
82
+ /// It is used to compute the bounding box.
75
83
pub max_extent : f64 ,
76
84
77
- /// The coordinates are considered as being planar
78
- /// All distance and length computations are in units of those coordinates
85
+ /// The coordinates are considered to be planar.
86
+ /// All distance and length calculations are expressed in the same units as coordinates.
79
87
pub geom : LineString ,
80
88
81
89
length : f64 ,
82
90
}
83
91
84
92
impl LineStringCurve {
85
- /// Build a new curve from a LineString
86
- /// max_extent is the maximum distance that is considered to be “on the curve”
87
- /// max_extent plays a role in the bounding box
88
- pub fn new ( geom : LineString , max_extent : f64 ) -> Self {
89
- let length = geom. euclidean_length ( ) ;
90
- Self {
91
- start_offset : 0. ,
92
- max_extent,
93
- geom,
94
- length,
95
- }
96
- }
97
-
98
- /// Splits the LineString into smaller curves of at most `max_len` length
99
- /// If the initial geometry is invalid, it returns an empty vector
93
+ /// Splits the [LineString] into smaller [Curve] objects of at most `max_len` length.
94
+ /// If the initial geometry is invalid, it returns an empty vector.
100
95
pub fn new_fragmented ( geom : LineString , max_len : f64 , max_extent : f64 ) -> Vec < Self > {
101
96
let n = ( geom. euclidean_length ( ) / max_len) . ceil ( ) as usize ;
102
97
geom. line_segmentize ( n)
@@ -112,6 +107,16 @@ impl LineStringCurve {
112
107
}
113
108
114
109
impl Curve for LineStringCurve {
110
+ fn new ( geom : LineString , max_extent : f64 ) -> Self {
111
+ let length = geom. euclidean_length ( ) ;
112
+ Self {
113
+ start_offset : 0. ,
114
+ max_extent,
115
+ geom,
116
+ length,
117
+ }
118
+ }
119
+
115
120
fn project ( & self , point : Point ) -> Result < CurveProjection , CurveError > {
116
121
if !self . is_valid ( ) {
117
122
return Err ( CurveError :: InvalidGeometry ) ;
@@ -128,7 +133,7 @@ impl Curve for LineStringCurve {
128
133
Orientation :: Clockwise => 1. ,
129
134
_ => -1. ,
130
135
} ;
131
- let offset = ( point. euclidean_distance ( & self . geom ) * sign) as isize ;
136
+ let offset = point. euclidean_distance ( & self . geom ) * sign;
132
137
133
138
Ok ( CurveProjection {
134
139
distance_along_curve,
@@ -139,7 +144,7 @@ impl Curve for LineStringCurve {
139
144
}
140
145
}
141
146
142
- fn resolve ( & self , projection : & CurveProjection ) -> Result < Point , CurveError > {
147
+ fn resolve ( & self , projection : CurveProjection ) -> Result < Point , CurveError > {
143
148
let fraction = ( projection. distance_along_curve - self . start_offset ) / self . length ( ) ;
144
149
if !( 0. ..=1. ) . contains ( & fraction) || fraction. is_nan ( ) {
145
150
Err ( CurveError :: NotOnTheCurve )
@@ -182,17 +187,17 @@ impl Curve for LineStringCurve {
182
187
}
183
188
184
189
fn get_normal ( & self , offset : f64 ) -> Result < ( f64 , f64 ) , CurveError > {
185
- // We find the point where the normal is computed
186
- let point = self . resolve ( & CurveProjection {
190
+ // We find the Point where the normal is computed
191
+ let point = self . resolve ( CurveProjection {
187
192
distance_along_curve : offset,
188
- offset : 0 ,
193
+ offset : 0. ,
189
194
} ) ?;
190
195
191
- // We find the line where the point is located
196
+ // We find the line where the Point is located
192
197
// This line will be used to construct the normal:
193
198
// - we translate it so that it starts at `0,0`
194
- // - we rotated it by 90°
195
- // - we scale it in order to be a unit vector
199
+ // - we rotate it by 90°
200
+ // - we scale it in order to become a unit vector
196
201
let line = self
197
202
. geom
198
203
. lines_iter ( )
@@ -211,18 +216,23 @@ impl Curve for LineStringCurve {
211
216
fn is_valid ( & self ) -> bool {
212
217
self . geom . coords_count ( ) >= 2 && ( self . geom . coords_count ( ) > 2 || !self . geom . is_closed ( ) )
213
218
}
219
+
220
+ fn max_extent ( & self ) -> f64 {
221
+ self . max_extent
222
+ }
214
223
}
215
224
216
- /// Represents a point in space projected on the curve
225
+ /// Represents a [Point] in space projected on the [Curve]
226
+ #[ derive( Clone , Copy ) ]
217
227
pub struct CurveProjection {
218
- /// How far from the curve start is located the point
219
- /// If the curve is part of a larger curve, start_offset is strictly positive
220
- /// and the start_offset will be considered
228
+ /// How far from the [Curve] start is located the [Point]
229
+ /// If the `Curve` is part of a larger `Curve`, ` start_offset` is strictly positive
230
+ /// and the ` start_offset` will be considered
221
231
pub distance_along_curve : f64 ,
222
- /// How far is the point from the curve (euclidian distance)
223
- /// It is positive if the point is located on the left of the curve
224
- /// and negative if the point is on the right
225
- pub offset : isize ,
232
+ /// How far is the [Point] from the [Curve] (euclidian distance)
233
+ /// It is positive if the `Point` is located on the left of the `Curve`
234
+ /// and negative if the `Point` is on the right
235
+ pub offset : f64 ,
226
236
}
227
237
228
238
#[ cfg( test) ]
@@ -244,11 +254,11 @@ mod tests {
244
254
245
255
let projected = c. project ( point ! { x: 1. , y: 1. } ) . unwrap ( ) ;
246
256
assert_eq ! ( 1. , projected. distance_along_curve) ;
247
- assert_eq ! ( 1 , projected. offset) ;
257
+ assert_eq ! ( 1. , projected. offset) ;
248
258
249
259
let projected = c. project ( point ! { x: 1. , y: -1. } ) . unwrap ( ) ;
250
260
assert_eq ! ( 1. , projected. distance_along_curve) ;
251
- assert_eq ! ( -1 , projected. offset) ;
261
+ assert_eq ! ( -1. , projected. offset) ;
252
262
253
263
c. start_offset = 1. ;
254
264
let projected = c. project ( point ! { x: 1. , y: -1. } ) . unwrap ( ) ;
@@ -261,18 +271,18 @@ mod tests {
261
271
262
272
let mut projection = CurveProjection {
263
273
distance_along_curve : 1. ,
264
- offset : 0 ,
274
+ offset : 0. ,
265
275
} ;
266
- let p = c. resolve ( & projection) . unwrap ( ) ;
276
+ let p = c. resolve ( projection) . unwrap ( ) ;
267
277
assert_eq ! ( p. x( ) , 1. ) ;
268
278
assert_eq ! ( p. y( ) , 0. ) ;
269
279
270
280
c. start_offset = 1. ;
271
- let p = c. resolve ( & projection) . unwrap ( ) ;
281
+ let p = c. resolve ( projection) . unwrap ( ) ;
272
282
assert_eq ! ( p. x( ) , 0. ) ;
273
283
274
284
projection. distance_along_curve = 4. ;
275
- assert ! ( c. resolve( & projection) . is_err( ) ) ;
285
+ assert ! ( c. resolve( projection) . is_err( ) ) ;
276
286
}
277
287
278
288
#[ test]
@@ -296,7 +306,7 @@ mod tests {
296
306
let segment = Line :: new ( coord ! { x: 10. , y: 10. } , coord ! { x: 20. , y: 10. } ) ;
297
307
assert ! ( c. intersect_segment( segment) . is_none( ) ) ;
298
308
299
- // Colinear
309
+ // Collinear
300
310
let segment = Line :: new ( coord ! { x: 0. , y: 0. , } , coord ! { x: 1. , y: 0. } ) ;
301
311
assert ! ( c. intersect_segment( segment) . is_none( ) ) ;
302
312
0 commit comments