5
5
import lombok .extern .slf4j .Slf4j ;
6
6
7
7
import java .time .LocalDateTime ;
8
- import java .util .*;
8
+ import java .util .ArrayList ;
9
+ import java .util .Arrays ;
10
+ import java .util .HashMap ;
11
+ import java .util .List ;
9
12
10
13
import static ch .naviqore .raptor .router .QueryState .INFINITY ;
11
14
@@ -30,6 +33,8 @@ class Query {
30
33
private final FootpathRelaxer footpathRelaxer ;
31
34
private final RouteScanner routeScanner ;
32
35
36
+ private final int numStops ;
37
+
33
38
private final int raptorRange ;
34
39
35
40
/**
@@ -65,6 +70,7 @@ class Query {
65
70
66
71
targetStops = new int [targetStopIndices .length * 2 ];
67
72
cutoffTime = determineCutoffTime ();
73
+ numStops = raptorData .getStopContext ().stops ().length ;
68
74
queryState = new QueryState (raptorData .getStopContext ().stops ().length , timeType );
69
75
70
76
// set up footpath relaxer and route scanner and inject stop labels and times
@@ -74,6 +80,21 @@ class Query {
74
80
raptorConfig .getDaysToScan ());
75
81
}
76
82
83
+ /**
84
+ * Check if there are any marked stops in the marked stops mask.
85
+ *
86
+ * @param markedStopsMask the marked stops mask to check.
87
+ * @return true if there are any marked stops, false otherwise.
88
+ */
89
+ private static boolean hasMarkedStops (boolean [] markedStopsMask ) {
90
+ for (boolean b : markedStopsMask ) {
91
+ if (b ) {
92
+ return true ;
93
+ }
94
+ }
95
+ return false ;
96
+ }
97
+
77
98
/**
78
99
* Main control flow of the routing algorithm. Spawns from source stops, coordinates route scanning, footpath
79
100
* relaxation, and time/label updates in the correct order.
@@ -93,40 +114,47 @@ class Query {
93
114
List <QueryState .Label []> run () {
94
115
95
116
// initially relax all source stops and add the newly improved stops by relaxation to the marked stops
96
- Set < Integer > markedStops = initialize ();
97
- markedStops . addAll ( footpathRelaxer .relaxInitial (sourceStopIndices ) );
98
- markedStops = removeSuboptimalLabelsForRound (0 , markedStops );
117
+ initialize ();
118
+ footpathRelaxer .relaxInitial ();
119
+ removeSuboptimalLabelsForRound (0 );
99
120
100
121
// if range is 0 or smaller there is no range, and we don't need to rerun rounds with different start offsets
101
122
if (raptorRange <= 0 ) {
102
- doRounds (markedStops );
123
+ doRounds ();
103
124
} else {
104
- doRangeRaptor (markedStops );
125
+ doRangeRaptor ();
105
126
}
106
127
return queryState .getBestLabelsPerRound ();
107
128
}
108
129
109
- void doRangeRaptor (Set < Integer > markedStops ) {
130
+ void doRangeRaptor () {
110
131
// prepare range offsets
111
- List <Integer > rangeOffsets = getRangeOffsets (markedStops , routeScanner );
132
+ // get initial marked stops to reset after each range offset
133
+ List <Integer > initialMarkedStops = new ArrayList <>();
112
134
HashMap <Integer , Integer > stopIdxSourceTimes = new HashMap <>();
113
- for (int stopIdx : markedStops ) {
135
+ for (int stopIdx = 0 ; stopIdx < numStops ; stopIdx ++) {
136
+ if (!queryState .isMarkedNextRound (stopIdx )) {
137
+ continue ;
138
+ }
139
+ initialMarkedStops .add (stopIdx );
114
140
stopIdxSourceTimes .put (stopIdx , queryState .getLabel (0 , stopIdx ).targetTime ());
115
141
}
116
-
142
+ List < Integer > rangeOffsets = getRangeOffsets ( initialMarkedStops , routeScanner );
117
143
// scan all range offsets in reverse order (earliest arrival / latest departure first)
118
144
for (int offsetIdx = rangeOffsets .size () - 1 ; offsetIdx >= 0 ; offsetIdx --) {
145
+ queryState .resetRounds ();
119
146
int rangeOffset = rangeOffsets .get (offsetIdx );
120
147
int timeFactor = timeType == TimeType .DEPARTURE ? 1 : -1 ;
121
148
log .debug ("Running rounds with range offset {}" , rangeOffset );
122
149
123
150
// set source times to the source times of the previous round
124
- for (int stopIdx : markedStops ) {
151
+ for (int stopIdx : initialMarkedStops ) {
125
152
QueryState .Label label = queryState .getLabel (0 , stopIdx );
126
153
int targetTime = stopIdxSourceTimes .get (stopIdx ) + timeFactor * rangeOffset ;
127
154
queryState .setLabel (0 , stopIdx , copyLabelWithNewTargetTime (label , targetTime ));
155
+ queryState .mark (stopIdx );
128
156
}
129
- doRounds (markedStops );
157
+ doRounds ();
130
158
}
131
159
}
132
160
@@ -146,26 +174,22 @@ QueryState.Label copyLabelWithNewTargetTime(QueryState.Label label, int targetTi
146
174
147
175
/**
148
176
* Method to perform the rounds of the routing algorithm (see {@link #run()}).
149
- *
150
- * @param markedStops the initially marked stops.
151
177
*/
152
- private void doRounds (Set < Integer > markedStops ) {
178
+ private void doRounds () {
153
179
154
- // continue with further rounds as long as there are new marked stops
155
- int round = 1 ;
156
- while (!markedStops .isEmpty () && (round - 1 ) <= config .getMaximumTransferNumber ()) {
180
+ // check if marked stops has any true values
181
+ while (queryState .hasMarkedStops () && (queryState .getRound ()) <= config .getMaximumTransferNumber ()) {
157
182
// add label layer for new round
158
183
queryState .addNewRound ();
159
184
160
185
// scan all routs and mark stops that have improved
161
- Set < Integer > markedStopsNext = routeScanner .scan (round , markedStops );
186
+ routeScanner .scan (queryState . getRound () );
162
187
163
188
// relax footpaths for all newly marked stops
164
- markedStopsNext . addAll ( footpathRelaxer .relax (round , markedStopsNext ));
189
+ footpathRelaxer .relax (queryState . getRound ( ));
165
190
166
191
// prepare next round
167
- markedStops = removeSuboptimalLabelsForRound (round , markedStopsNext );
168
- round ++;
192
+ removeSuboptimalLabelsForRound (queryState .getRound ());
169
193
}
170
194
}
171
195
@@ -180,13 +204,13 @@ private void doRounds(Set<Integer> markedStops) {
180
204
* 10:10, 10:20, and Route B has departures at 10:05, 10:15, 10:25, the range offsets are be 0, 10, 20 and not 0, 5,
181
205
* 10, 15, 20, 25 (note real values are in seconds and not minutes --> *60).
182
206
*
183
- * @param markedStops the marked stops to get the range offsets for.
184
- * @param routeScanner the route scanner to get the trip offsets for the stops.
207
+ * @param initialMarkedStops the initial marked stops to get the range offsets for.
208
+ * @param routeScanner the route scanner to get the trip offsets for the stops.
185
209
* @return the range offsets (in seconds) applicable for all marked stops.
186
210
*/
187
- private List <Integer > getRangeOffsets (Set <Integer > markedStops , RouteScanner routeScanner ) {
211
+ private List <Integer > getRangeOffsets (List <Integer > initialMarkedStops , RouteScanner routeScanner ) {
188
212
ArrayList <Integer > rangeOffsets = new ArrayList <>();
189
- for (int stopIdx : markedStops ) {
213
+ for (int stopIdx : initialMarkedStops ) {
190
214
List <Integer > stopRangeOffsets = routeScanner .getTripOffsetsForStop (stopIdx , raptorRange );
191
215
for (int i = 0 ; i < stopRangeOffsets .size (); i ++) {
192
216
// if the rangeOffsets list is not long enough, add the offset
@@ -210,10 +234,8 @@ private List<Integer> getRangeOffsets(Set<Integer> markedStops, RouteScanner rou
210
234
211
235
/**
212
236
* Set up the best times per stop and best labels per round for a new query.
213
- *
214
- * @return the initially marked stops.
215
237
*/
216
- Set < Integer > initialize () {
238
+ void initialize () {
217
239
log .debug ("Initializing global best times per stop and best labels per round" );
218
240
219
241
// fill target stops
@@ -224,7 +246,6 @@ Set<Integer> initialize() {
224
246
}
225
247
226
248
// set initial labels, best time and mark source stops
227
- Set <Integer > markedStops = new HashSet <>();
228
249
for (int i = 0 ; i < sourceStopIndices .length ; i ++) {
229
250
int currentStopIdx = sourceStopIndices [i ];
230
251
int targetTime = sourceTimes [i ];
@@ -233,42 +254,35 @@ Set<Integer> initialize() {
233
254
QueryState .NO_INDEX , QueryState .NO_INDEX , currentStopIdx , null );
234
255
queryState .setLabel (0 , currentStopIdx , label );
235
256
queryState .setBestTime (currentStopIdx , targetTime );
236
-
237
- markedStops .add (currentStopIdx );
257
+ queryState .mark (currentStopIdx );
238
258
}
239
-
240
- return markedStops ;
241
259
}
242
260
243
261
/**
244
262
* Nullify labels that are suboptimal for the current round. This method checks if the label time is worse than the
245
263
* optimal time mark and removes the mark for the next round and nullifies the label in this case.
246
264
*
247
- * @param round the round to remove suboptimal labels for.
248
- * @param markedStops the marked stops to check for suboptimal labels.
265
+ * @param round the round to remove suboptimal labels for.
249
266
*/
250
- Set < Integer > removeSuboptimalLabelsForRound (int round , Set < Integer > markedStops ) {
267
+ void removeSuboptimalLabelsForRound (int round ) {
251
268
int bestTime = getBestTimeForAllTargetStops ();
252
269
253
270
if (bestTime == INFINITY || bestTime == -INFINITY ) {
254
- return markedStops ;
271
+ return ;
255
272
}
256
273
257
- Set <Integer > markedStopsClean = new HashSet <>();
258
- for (int stopIdx : markedStops ) {
274
+ for (int stopIdx = 0 ; stopIdx < numStops ; stopIdx ++) {
275
+ if (!queryState .isMarkedNextRound (stopIdx )) {
276
+ continue ;
277
+ }
259
278
QueryState .Label label = queryState .getLabel (round , stopIdx );
260
279
if (label != null ) {
261
- if (timeType == TimeType .DEPARTURE && label .targetTime () > bestTime ) {
262
- queryState .setLabel (round , stopIdx , null );
263
- } else if (timeType == TimeType .ARRIVAL && label .targetTime () < bestTime ) {
280
+ if ((timeType == TimeType .DEPARTURE && label .targetTime () > bestTime ) || (timeType == TimeType .ARRIVAL && label .targetTime () < bestTime )) {
264
281
queryState .setLabel (round , stopIdx , null );
265
- } else {
266
- markedStopsClean .add (stopIdx );
282
+ queryState .unmark (stopIdx );
267
283
}
268
284
}
269
285
}
270
-
271
- return markedStopsClean ;
272
286
}
273
287
274
288
/**
0 commit comments