1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | import theSphericalTrigonometry from '../core/lib/SphericalTrigonometry.js'; |
26 | import ItineraryPoint from '../data/ItineraryPoint.js'; |
27 | import Maneuver from '../data/Maneuver.js'; |
28 | import publicTransportData from '../routeProviders/PublicTransportData.js'; |
29 | import PublicTransportHolesRemover from '../routeProviders/PublicTransportHolesRemover.js'; |
30 | |
31 | import { ZERO, ONE, TWO, THREE } from '../main/Constants.js'; |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | |
89 | |
90 | |
91 | class PublicTransportRouteBuilder { |
92 | |
93 | #selectedRelationId; |
94 | #nodes3Ways; |
95 | #route; |
96 | #publicTransportData; |
97 | |
98 | |
99 | |
100 | |
101 | #merge3WaysNodes ( ) { |
102 | |
103 | this.#nodes3Ways.forEach ( |
104 | node => { |
105 | |
106 | |
107 | let shortestWaydistance = Number.MAX_VALUE; |
108 | let shortestWay = null; |
109 | const linkedWaysId = node.startingWaysIds.concat ( node.endingWaysIds ); |
110 | linkedWaysId.forEach ( |
111 | wayId => { |
112 | const way = this.#publicTransportData.waysMap.get ( wayId ); |
113 | if ( way.distance < shortestWaydistance ) { |
114 | shortestWaydistance = way.distance; |
115 | shortestWay = way; |
116 | } |
117 | } |
118 | ); |
119 | |
120 | |
121 | this.#publicTransportData.removeFrom ( linkedWaysId, shortestWay.id ); |
122 | |
123 | |
124 | const clonedWay = this.#publicTransportData.waysMap.get ( |
125 | this.#publicTransportData.cloneWay ( shortestWay.id ) |
126 | ); |
127 | |
128 | |
129 | let tmpNodeId = null; |
130 | if ( this.#publicTransportData.firstOf ( shortestWay.nodesIds ) === node.id ) { |
131 | clonedWay.nodesIds.pop ( ); |
132 | clonedWay.nodesIds.push ( this.#publicTransportData.lastOf ( shortestWay.nodesIds ) ); |
133 | tmpNodeId = this.#publicTransportData.firstOf ( clonedWay.nodesIds ); |
134 | } |
135 | else { |
136 | clonedWay.nodesIds.shift ( ); |
137 | clonedWay.nodesIds.unshift ( this.#publicTransportData.firstOf ( shortestWay.nodesIds ) ); |
138 | tmpNodeId = this.#publicTransportData.lastOf ( clonedWay.nodesIds ); |
139 | } |
140 | |
141 | |
142 | const lastWay = this.#publicTransportData.waysMap.get ( linkedWaysId [ ONE ] ); |
143 | lastWay.nodesIds [ lastWay.nodesIds.indexOf ( node.id ) ] = tmpNodeId; |
144 | |
145 | |
146 | this.#publicTransportData.mergeWays ( |
147 | this.#publicTransportData.mergeWays ( |
148 | this.#publicTransportData.mergeWays ( |
149 | shortestWay.id, |
150 | clonedWay.id |
151 | ), |
152 | linkedWaysId [ ZERO ] |
153 | ), |
154 | lastWay.id ); |
155 | } |
156 | ); |
157 | } |
158 | |
159 | |
160 | |
161 | |
162 | #createRoute ( route ) { |
163 | |
164 | |
165 | let startStop = null; |
166 | let endStop = null; |
167 | let startStopDistance = Number.MAX_VALUE; |
168 | let endStopDistance = Number.MAX_VALUE; |
169 | |
170 | this.#publicTransportData.stopsMap.forEach ( |
171 | stopPoint => { |
172 | let distance = theSphericalTrigonometry.pointsDistance ( |
173 | [ stopPoint.lat, stopPoint.lon ], |
174 | this.#route.wayPoints.first.latLng |
175 | ); |
176 | if ( distance < startStopDistance ) { |
177 | startStopDistance = distance; |
178 | startStop = stopPoint; |
179 | } |
180 | distance = theSphericalTrigonometry.pointsDistance ( |
181 | [ stopPoint.lat, stopPoint.lon ], |
182 | this.#route.wayPoints.last.latLng |
183 | ); |
184 | if ( distance < endStopDistance ) { |
185 | endStopDistance = distance; |
186 | endStop = stopPoint; |
187 | } |
188 | } |
189 | ); |
190 | |
191 | |
192 | route.itinerary.itineraryPoints.removeAll ( ); |
193 | route.itinerary.maneuvers.removeAll ( ); |
194 | route.itinerary.hasProfile = false; |
195 | route.itinerary.ascent = ZERO; |
196 | route.itinerary.descent = ZERO; |
197 | |
198 | |
199 | |
200 | |
201 | |
202 | const NO_POINT_ADDED = 0; |
203 | const FIRST_POINT_REACHED = 1; |
204 | const OTHERS_POINTS_REACHED = 2; |
205 | const LAST_POINT_REACHED = 3; |
206 | const ALL_POINTS_ADDED = 4; |
207 | |
208 | let addPoint = NO_POINT_ADDED; |
209 | let reversePoints = false; |
210 | Array.from ( this.#publicTransportData.waysMap.values ( ) )[ ZERO ].nodesIds.forEach ( |
211 | nodeId => { |
212 | if ( NO_POINT_ADDED === addPoint && ( nodeId === startStop.id || nodeId === endStop.id ) ) { |
213 | |
214 | |
215 | addPoint = FIRST_POINT_REACHED; |
216 | reversePoints = ( nodeId === endStop.id ); |
217 | } |
218 | else if ( OTHERS_POINTS_REACHED === addPoint && ( nodeId === startStop.id || nodeId === endStop.id ) ) { |
219 | |
220 | |
221 | addPoint = LAST_POINT_REACHED; |
222 | } |
223 | if ( NO_POINT_ADDED < addPoint && ALL_POINTS_ADDED > addPoint ) { |
224 | |
225 | |
226 | const itineraryPoint = new ItineraryPoint ( ); |
227 | const node = this.#publicTransportData.nodesMap.get ( nodeId ); |
228 | itineraryPoint.latLng = [ node.lat, node.lon ]; |
229 | route.itinerary.itineraryPoints.add ( itineraryPoint ); |
230 | |
231 | |
232 | const stopNode = this.#publicTransportData.stopsMap.get ( nodeId ); |
233 | if ( stopNode ) { |
234 | |
235 | const maneuver = new Maneuver ( ); |
236 | let stopName = null; |
237 | if ( stopNode.tags && stopNode.tags.name ) { |
238 | stopName = stopNode.tags.name; |
239 | maneuver.instruction = stopName + ' : '; |
240 | } |
241 | if ( stopNode.id === startStop.id ) { |
242 | if ( stopName ) { |
243 | route.wayPoints.first.name = stopName; |
244 | } |
245 | maneuver.iconName = 'kTrainStart'; |
246 | maneuver.instruction += 'Monter dans le train'; |
247 | } |
248 | else if ( stopNode.id === endStop.id ) { |
249 | if ( stopName ) { |
250 | route.wayPoints.last.name = stopName; |
251 | } |
252 | maneuver.iconName = 'kTrainEnd'; |
253 | maneuver.instruction += 'Descendre du train'; |
254 | } |
255 | else { |
256 | maneuver.iconName = 'kTrainContinue'; |
257 | maneuver.instruction += 'Rester dans le train'; |
258 | } |
259 | maneuver.distance = ZERO; |
260 | maneuver.duration = ZERO; |
261 | maneuver.itineraryPointObjId = itineraryPoint.objId; |
262 | |
263 | route.itinerary.maneuvers.add ( maneuver ); |
264 | } |
265 | } |
266 | if ( FIRST_POINT_REACHED === addPoint ) { |
267 | |
268 | |
269 | addPoint = OTHERS_POINTS_REACHED; |
270 | } |
271 | if ( LAST_POINT_REACHED === addPoint ) { |
272 | |
273 | |
274 | addPoint = ALL_POINTS_ADDED; |
275 | } |
276 | } |
277 | ); |
278 | |
279 | |
280 | if ( reversePoints ) { |
281 | route.itinerary.itineraryPoints.reverse ( ); |
282 | route.itinerary.maneuvers.reverse ( ); |
283 | } |
284 | |
285 | |
286 | route.distance = ZERO; |
287 | |
288 | const maneuversIterator = route.itinerary.maneuvers.iterator; |
289 | maneuversIterator.done; |
290 | let previousManeuver = maneuversIterator.value; |
291 | maneuversIterator.done; |
292 | |
293 | const itineraryPointsIterator = route.itinerary.itineraryPoints.iterator; |
294 | itineraryPointsIterator.done; |
295 | let previousPoint = itineraryPointsIterator.value; |
296 | |
297 | while ( ! itineraryPointsIterator.done ) { |
298 | itineraryPointsIterator.value.distance = ZERO; |
299 | previousPoint.distance = theSphericalTrigonometry.pointsDistance ( |
300 | previousPoint.latLng, |
301 | itineraryPointsIterator.value.latLng |
302 | ); |
303 | route.distance += previousPoint.distance; |
304 | previousManeuver.distance += previousPoint.distance; |
305 | if ( maneuversIterator.value.itineraryPointObjId === itineraryPointsIterator.value.objId ) { |
306 | |
307 | previousManeuver = maneuversIterator.value; |
308 | previousManeuver.distance = ZERO; |
309 | |
310 | maneuversIterator.done; |
311 | } |
312 | previousPoint = itineraryPointsIterator.value; |
313 | } |
314 | |
315 | } |
316 | |
317 | |
318 | |
319 | |
320 | |
321 | constructor ( route, selectedRelationId ) { |
322 | Object.freeze ( this ); |
323 | this.#route = route; |
324 | this.#selectedRelationId = selectedRelationId; |
325 | this.#publicTransportData = new publicTransportData ( selectedRelationId ); |
326 | this.#nodes3Ways = []; |
327 | } |
328 | |
329 | buildRoute ( response, onOk, onError ) { |
330 | |
331 | |
332 | this.#nodes3Ways = []; |
333 | this.#publicTransportData.nodes3WaysCounter = ZERO; |
334 | |
335 | |
336 | this.#publicTransportData.createMaps ( response.elements ); |
337 | |
338 | |
339 | |
340 | |
341 | let nodeWithMoreThan3WaysFound = false; |
342 | this.#publicTransportData.nodesMap.forEach ( |
343 | node => { |
344 | const waysIds = node.startingWaysIds.concat ( node.endingWaysIds ); |
345 | switch ( waysIds.length ) { |
346 | case ZERO : |
347 | |
348 | |
349 | break; |
350 | case ONE : |
351 | |
352 | |
353 | break; |
354 | case TWO : |
355 | |
356 | |
357 | this.#publicTransportData.mergeWays ( waysIds [ ZERO ], waysIds [ ONE ] ); |
358 | break; |
359 | case THREE : |
360 | node.isNode3Ways = true; |
361 | this.#nodes3Ways.push ( node ); |
362 | this.#publicTransportData.nodes3WaysCounter ++; |
363 | break; |
364 | default : |
365 | nodeWithMoreThan3WaysFound = true; |
366 | window.TaN.showInfo ( |
367 | 'A node with more than 3 ways is found : ' + |
368 | node.id + |
369 | ' - the relation ' + |
370 | this.#selectedRelationId + |
371 | ' - ways ' |
372 | + node.startingWaysIds.concat ( node.endingWaysIds ) |
373 | ); |
374 | break; |
375 | } |
376 | } |
377 | ); |
378 | |
379 | if ( nodeWithMoreThan3WaysFound ) { |
380 | |
381 | onError ( new Error ( 'A node with more than 3 ways was found in the relation.See the console for more infos' ) ); |
382 | return; |
383 | } |
384 | |
385 | |
386 | if ( this.#publicTransportData.waysMap.size > ( ( this.#publicTransportData.nodes3WaysCounter * TWO ) + ONE ) ) { |
387 | new PublicTransportHolesRemover ( this.#publicTransportData ). removeHoles ( ); |
388 | window.TaN.showInfo ( |
389 | 'Holes found in the OSM relation number ' + this.#selectedRelationId + '. Try to correct OSM data.' |
390 | ); |
391 | } |
392 | |
393 | |
394 | if ( ZERO < this.#publicTransportData.nodes3WaysCounter ) { |
395 | this.#merge3WaysNodes ( ); |
396 | } |
397 | |
398 | |
399 | this.#createRoute ( this.#route ); |
400 | |
401 | onOk ( this.#route ); |
402 | |
403 | } |
404 | |
405 | } |
406 | |
407 | export default PublicTransportRouteBuilder; |
408 | |
409 | |
410 | |