@@ -107,6 +107,37 @@ pub fn angle_of_line(line: Line) -> f64 {
107
107
( line. dy ( ) ) . atan2 ( line. dx ( ) ) . to_degrees ( )
108
108
}
109
109
110
+ /// North is 0°
111
+ /// East is 90°
112
+ /// South is 180°
113
+ /// West is 270°
114
+ pub fn euclidean_bearing ( origin : Coord , destination : Coord ) -> f64 {
115
+ ( angle_of_line ( Line :: new ( origin, destination) ) + 450.0 ) % 360.0
116
+ }
117
+
118
+ /// The bearing of the first segment of `linestring` starting from `endpoint`.
119
+ ///
120
+ /// precondition: `endpoint` must be either the first or last point in `linestring`
121
+ /// precondition: `linestring` must have at least 2 coordinates
122
+ pub fn bearing_from_endpoint ( endpoint : Point , linestring : & LineString ) -> f64 {
123
+ assert ! (
124
+ linestring. 0 . len( ) >= 2 ,
125
+ "zero length roads should be filtered out"
126
+ ) ;
127
+ let next_coord = if endpoint. 0 == linestring. 0 [ 0 ] {
128
+ linestring. 0 [ 1 ]
129
+ } else if endpoint. 0 == linestring. 0 [ linestring. 0 . len ( ) - 1 ] {
130
+ linestring. 0 [ linestring. 0 . len ( ) - 2 ]
131
+ } else {
132
+ // I'm assuming this won't happen, but maybe it's possible,
133
+ // e.g. to different rounding schemes.
134
+ debug_assert ! ( false , "road does not terminate at intersection" ) ;
135
+ linestring. 0 [ 1 ]
136
+ } ;
137
+
138
+ euclidean_bearing ( endpoint. 0 , next_coord)
139
+ }
140
+
110
141
pub fn angle_of_pt_on_line ( linestring : & LineString , pt : Coord ) -> f64 {
111
142
let line = linestring
112
143
. lines ( )
@@ -267,3 +298,103 @@ pub fn invert_polygon(wgs84_polygon: Polygon) -> Polygon {
267
298
vec ! [ wgs84_polygon. into_inner( ) . 0 ] ,
268
299
)
269
300
}
301
+
302
+ /// The "diagonal line" is an equal angular distance from a and b.
303
+ /// The diagonal bearing is the bearing of this "diagonal line".
304
+ ///
305
+ /// That is, given the bearing of a and b, returns the bearing of line c.
306
+ ///
307
+ /// ```ignore
308
+ /// a b
309
+ /// \ /
310
+ /// ∂° \/ ∂°
311
+ /// ------------ c
312
+ /// ```
313
+ pub fn diagonal_bearing ( bearing_a : f64 , bearing_b : f64 ) -> f64 {
314
+ let angle_between = bearing_b - bearing_a;
315
+ ( angle_between / 2.0 + 90.0 + bearing_a) % 360.0
316
+ }
317
+
318
+ #[ cfg( test) ]
319
+ mod tests {
320
+ use super :: * ;
321
+ use approx:: assert_relative_eq;
322
+ use geo:: wkt;
323
+
324
+ #[ test]
325
+ fn test_line_bearing ( ) {
326
+ let p1 = Point :: new ( 0.0 , 0.0 ) ;
327
+
328
+ // Due South in our projection
329
+ assert_relative_eq ! ( 90.0 , angle_of_line( Line :: new( p1, Point :: new( 0.0 , 1.0 ) ) ) ) ;
330
+ // East
331
+ assert_relative_eq ! ( 0.0 , angle_of_line( Line :: new( p1, Point :: new( 1.0 , 0.0 ) ) ) ) ;
332
+ // North
333
+ assert_relative_eq ! ( -90.0 , angle_of_line( Line :: new( p1, Point :: new( 0.0 , -1.0 ) ) ) ) ;
334
+ // West
335
+ assert_relative_eq ! ( 180.0 , angle_of_line( Line :: new( p1, Point :: new( -1.0 , 0.0 ) ) ) ) ;
336
+ }
337
+
338
+ #[ test]
339
+ fn test_bearing_from_endpoint ( ) {
340
+ let p1 = Point :: new ( 0.0 , 0.0 ) ;
341
+
342
+ // p1 is start point
343
+
344
+ // North
345
+ assert_relative_eq ! (
346
+ 0. ,
347
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. 0. , 0. -1. ) ) )
348
+ ) ;
349
+ // East
350
+ assert_relative_eq ! (
351
+ 90. ,
352
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. 0. , 1. 0. ) ) )
353
+ ) ;
354
+ // South
355
+ assert_relative_eq ! (
356
+ 180. ,
357
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. 0. , 0. 1. ) ) )
358
+ ) ;
359
+ // West
360
+ assert_relative_eq ! (
361
+ 270. ,
362
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. 0. , -1. 0. ) ) )
363
+ ) ;
364
+ // Northwest
365
+ assert_relative_eq ! (
366
+ 315. ,
367
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. 0. , -1. -1. ) ) )
368
+ ) ;
369
+
370
+ // Flipped - p1 is now the end point, not the start point
371
+
372
+ // North
373
+ assert_relative_eq ! (
374
+ 0. ,
375
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. -1. , 0. 0. ) ) )
376
+ ) ;
377
+ // East
378
+ assert_relative_eq ! (
379
+ 90. ,
380
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 1. 0. , 0. 0. ) ) )
381
+ ) ;
382
+ // South
383
+ assert_relative_eq ! (
384
+ 180. ,
385
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( 0. 1. , 0. 0. ) ) )
386
+ ) ;
387
+ // West
388
+ assert_relative_eq ! (
389
+ 270. ,
390
+ bearing_from_endpoint( p1, & wkt!( LINESTRING ( -1. 0. , 0. 0. ) ) )
391
+ ) ;
392
+ }
393
+
394
+ #[ test]
395
+ fn test_diagonal_angle ( ) {
396
+ assert_eq ! ( 135.0 , diagonal_bearing( 0. , 90. ) ) ;
397
+ assert_eq ! ( 270.0 , diagonal_bearing( 135. , 225. ) ) ;
398
+ assert_eq ! ( 270.0 , diagonal_bearing( 300. , 60. ) ) ;
399
+ }
400
+ }
0 commit comments