1
1
use std:: collections:: HashMap ;
2
2
3
- use geo:: { Euclidean , Length , LineString } ;
4
- use geojson:: Feature ;
5
-
6
3
use crate :: map_model:: Direction ;
7
4
use crate :: route:: Router ;
8
5
use crate :: { MapModel , Neighbourhood , RoadID } ;
6
+ use geo:: { Euclidean , Length , LineString } ;
7
+ use geojson:: Feature ;
9
8
10
9
pub struct Shortcuts {
11
10
pub paths : Vec < Path > ,
@@ -27,25 +26,57 @@ impl Shortcuts {
27
26
for start_i in & neighbourhood. border_intersections {
28
27
let start_intersection = map. get_i ( * start_i) ;
29
28
for start_r in & start_intersection. roads {
30
- // It's not a "shortcut" unless it cuts through the interior.
31
- if !neighbourhood. interior_roads . contains ( start_r) {
29
+ // It's not a "shortcut" unless it starts outside the interior and cuts through the interior.
30
+ if !neighbourhood. main_roads . contains ( start_r) {
32
31
continue ;
33
32
}
34
33
for end_i in & neighbourhood. border_intersections {
35
34
if start_i == end_i {
36
35
continue ;
37
36
}
38
37
let end_intersection = map. get_i ( * end_i) ;
39
- for end_r in & end_intersection. roads {
40
- // It's not a "shortcut" unless it cuts through the interior.
41
- if !neighbourhood. interior_roads . contains ( end_r) {
38
+ ' next_road : for end_r in & end_intersection. roads {
39
+ // It's not a "shortcut" unless it ends outside the interior after cutting through .
40
+ if !neighbourhood. main_roads . contains ( end_r) {
42
41
continue ;
43
42
}
43
+
44
44
let Some ( route) = router. route_from_roads ( * start_r, * end_r) else {
45
45
continue ;
46
46
} ;
47
+
47
48
let mut shortcut_length = 0.0 ;
48
- for ( r, _direction) in & route. steps {
49
+
50
+ let interior_steps = {
51
+ let mut steps = route. steps . iter ( ) ;
52
+ let first_step = steps. next ( ) . expect ( "route can't be empty" ) ;
53
+ let first_road = map. get_r ( first_step. 0 ) ;
54
+ shortcut_length += Euclidean . length ( & first_road. linestring ) ;
55
+ let mut steps_reversed = steps. rev ( ) ;
56
+ let Some ( final_step) = steps_reversed. next ( ) else {
57
+ continue ;
58
+ } ;
59
+ let final_road = map. get_r ( final_step. 0 ) ;
60
+ shortcut_length += Euclidean . length ( & final_road. linestring ) ;
61
+
62
+ // re-reverse back to the original ordering, but without first and final steps
63
+ steps_reversed. rev ( )
64
+ } ;
65
+
66
+ for ( r, _direction) in interior_steps {
67
+ // For the purpose of counting unique shortcuts, only the first and
68
+ // final steps should be on main roads.
69
+ //
70
+ // If we've left the interior, only the portion inside the interior counts
71
+ // as distinct plausible shortcut.
72
+ //
73
+ // If a route leaves and re-enters the interior through another border
74
+ // intersection, then that route will be counted as two distinct
75
+ // shortcuts.
76
+ if neighbourhood. main_roads . contains ( & r) {
77
+ break ' next_road;
78
+ }
79
+
49
80
let road = map. get_r ( * r) ;
50
81
* count_per_road. entry ( road. id ) . or_insert ( 0 ) += 1 ;
51
82
shortcut_length += Euclidean . length ( & road. linestring ) ;
0 commit comments