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 theConfig from '../../data/Config.js'; |
26 | import theSphericalTrigonometry from '../../core/lib/SphericalTrigonometry.js'; |
27 | import theTranslator from '../../core/uiLib/Translator.js'; |
28 | import RoundaboutData from './RoundaboutData.js'; |
29 | import { DISTANCE, ZERO, ONE, TWO, NOT_FOUND, ICON_POSITION } from '../../main/Constants.js'; |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | class StreetFinder { |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | #computeData; |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | #noteData; |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | #overpassAPIDataLoader; |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | #rcnRefOsmNode; |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | #iconOsmNode; |
80 | |
81 | |
82 | |
83 | |
84 | |
85 | |
86 | #iconOsmNodeId; |
87 | |
88 | |
89 | |
90 | |
91 | |
92 | |
93 | #incomingOsmNodeId; |
94 | |
95 | |
96 | |
97 | |
98 | |
99 | |
100 | #outgoingOsmNodeId; |
101 | |
102 | |
103 | |
104 | |
105 | |
106 | |
107 | #incomingStreetName; |
108 | |
109 | |
110 | |
111 | |
112 | |
113 | |
114 | #outgoingStreetName; |
115 | |
116 | |
117 | |
118 | |
119 | |
120 | |
121 | #roundaboutData; |
122 | |
123 | |
124 | |
125 | |
126 | |
127 | |
128 | |
129 | #getWayName ( way ) { |
130 | return ( way.tags.name ? way.tags.name : '' ) + |
131 | ( way.tags.name && way.tags.ref ? ' ' : '' ) + |
132 | ( way.tags.ref ? '[' + way.tags.ref + ']' : '' ); |
133 | } |
134 | |
135 | |
136 | |
137 | |
138 | |
139 | |
140 | |
141 | |
142 | #latLngCompare ( itineraryPoint ) { |
143 | |
144 | const COMPARE_PRECISION = 0.000005; |
145 | |
146 | let isWayPoint = false; |
147 | this.#computeData.route.wayPoints.forEach ( |
148 | wayPoint => { |
149 | if ( |
150 | ( Math.abs ( itineraryPoint.lat - wayPoint.lat ) < COMPARE_PRECISION ) |
151 | && |
152 | ( Math.abs ( itineraryPoint.lng - wayPoint.lng ) < COMPARE_PRECISION ) |
153 | ) { |
154 | isWayPoint = true; |
155 | } |
156 | } |
157 | ); |
158 | return ( |
159 | ! isWayPoint |
160 | && |
161 | ( |
162 | this.#noteData.latLng [ ZERO ] !== itineraryPoint.lat |
163 | || |
164 | this.#noteData.latLng [ ONE ] !== itineraryPoint.lng |
165 | ) |
166 | ); |
167 | } |
168 | |
169 | |
170 | |
171 | |
172 | |
173 | |
174 | #findOsmNodes ( ) { |
175 | |
176 | |
177 | const incomingItineraryPoint = this.#computeData.route.itinerary.itineraryPoints.previous ( |
178 | this.#computeData.nearestItineraryPointObjId, |
179 | itineraryPoint => this.#latLngCompare ( itineraryPoint ) |
180 | ); |
181 | const outgoingItineraryPoint = this.#computeData.route.itinerary.itineraryPoints.next ( |
182 | this.#computeData.nearestItineraryPointObjId, |
183 | itineraryPoint => this.#latLngCompare ( itineraryPoint ) |
184 | ); |
185 | |
186 | |
187 | let iconNodeDistance = Number.MAX_VALUE; |
188 | let rcnRefDistance = Number.MAX_VALUE; |
189 | let incomingNodeDistance = Number.MAX_VALUE; |
190 | let outgoingNodeDistance = Number.MAX_VALUE; |
191 | |
192 | let nodeDistance = DISTANCE.defaultValue; |
193 | |
194 | |
195 | this.#overpassAPIDataLoader.nodes.forEach ( |
196 | node => { |
197 | nodeDistance = theSphericalTrigonometry.pointsDistance ( |
198 | [ node.lat, node.lon ], |
199 | this.#noteData.latLng |
200 | ); |
201 | |
202 | |
203 | if ( |
204 | 'bike' === this.#computeData.route.itinerary.transitMode |
205 | && |
206 | node?.tags?.rcn_ref |
207 | && |
208 | node.tags [ 'network:type' ] |
209 | && |
210 | 'node_network' === node.tags [ 'network:type' ] |
211 | && |
212 | nodeDistance < theConfig.note.svgIcon.rcnRefDistance |
213 | && |
214 | nodeDistance < rcnRefDistance |
215 | ) { |
216 | this.#rcnRefOsmNode = node; |
217 | rcnRefDistance = nodeDistance; |
218 | } |
219 | |
220 | |
221 | if ( nodeDistance < iconNodeDistance ) { |
222 | this.#iconOsmNodeId = node.id; |
223 | iconNodeDistance = nodeDistance; |
224 | } |
225 | |
226 | |
227 | if ( incomingItineraryPoint ) { |
228 | nodeDistance = |
229 | theSphericalTrigonometry.pointsDistance ( [ node.lat, node.lon ], incomingItineraryPoint.latLng ); |
230 | if ( nodeDistance < incomingNodeDistance ) { |
231 | this.#incomingOsmNodeId = node.id; |
232 | incomingNodeDistance = nodeDistance; |
233 | } |
234 | } |
235 | |
236 | |
237 | if ( outgoingItineraryPoint ) { |
238 | nodeDistance = |
239 | theSphericalTrigonometry.pointsDistance ( [ node.lat, node.lon ], outgoingItineraryPoint.latLng ); |
240 | if ( nodeDistance < outgoingNodeDistance ) { |
241 | this.#outgoingOsmNodeId = node.id; |
242 | outgoingNodeDistance = nodeDistance; |
243 | } |
244 | } |
245 | } |
246 | ); |
247 | |
248 | this.#iconOsmNode = this.#overpassAPIDataLoader.nodes.get ( this.#iconOsmNodeId ); |
249 | } |
250 | |
251 | |
252 | |
253 | |
254 | |
255 | #findMiniRoundabout ( ) { |
256 | this.#roundaboutData.isMini = 'mini_roundabout' === this?.#iconOsmNode?.tags?.highway; |
257 | } |
258 | |
259 | |
260 | |
261 | |
262 | |
263 | #addRcnRefNumber ( ) { |
264 | if ( this.#rcnRefOsmNode ) { |
265 | this.#computeData.rcnRef = this.#rcnRefOsmNode.tags.rcn_ref; |
266 | this.#noteData.tooltipContent += |
267 | theTranslator.getText ( 'StreetFinder - rcnRef', { rcnRef : this.#computeData.rcnRef } ); |
268 | } |
269 | } |
270 | |
271 | |
272 | |
273 | |
274 | |
275 | #findStreets ( ) { |
276 | this.#overpassAPIDataLoader.ways.forEach ( |
277 | way => { |
278 | if ( ! way.nodes.includes ( this.#iconOsmNodeId ) ) { |
279 | return; |
280 | } |
281 | |
282 | const wayName = this.#getWayName ( way ); |
283 | const haveName = '' !== wayName; |
284 | |
285 | const isIncomingStreet = way.nodes.includes ( this.#incomingOsmNodeId ); |
286 | const isOutgoingStreet = way.nodes.includes ( this.#outgoingOsmNodeId ); |
287 | |
288 | |
289 | let streetOcurrences = way.nodes.filter ( nodeId => nodeId === this.#iconOsmNodeId ).length * TWO; |
290 | |
291 | |
292 | if ( way.nodes [ ZERO ] === this.#iconOsmNodeId ) { |
293 | streetOcurrences --; |
294 | } |
295 | |
296 | |
297 | if ( way.nodes [ way.nodes.length - ONE ] === this.#iconOsmNodeId ) { |
298 | streetOcurrences --; |
299 | } |
300 | |
301 | |
302 | if ( isIncomingStreet ) { |
303 | this.#incomingStreetName = haveName ? wayName : '???'; |
304 | streetOcurrences --; |
305 | if ( 'roundabout' === way?.tags?.junction ) { |
306 | this.#roundaboutData.isExit = true; |
307 | } |
308 | } |
309 | if ( ZERO === streetOcurrences ) { |
310 | return; |
311 | } |
312 | |
313 | |
314 | if ( isOutgoingStreet ) { |
315 | this.#outgoingStreetName = haveName ? wayName : '???'; |
316 | streetOcurrences --; |
317 | if ( 'roundabout' === way?.tags?.junction ) { |
318 | this.#roundaboutData.isEntry = true; |
319 | } |
320 | } |
321 | if ( ZERO === streetOcurrences || ! haveName ) { |
322 | return; |
323 | } |
324 | |
325 | |
326 | while ( ZERO !== streetOcurrences ) { |
327 | this.#noteData.address = |
328 | '' === this.#noteData.address ? wayName : this.#noteData.address + ' ⪥ ' + wayName; |
329 | streetOcurrences --; |
330 | } |
331 | } |
332 | ); |
333 | } |
334 | |
335 | |
336 | |
337 | |
338 | |
339 | #addCity ( ) { |
340 | if ( '' !== this.#overpassAPIDataLoader.city ) { |
341 | this.#noteData.address += |
342 | ' <span class="TravelNotes-NoteHtml-Address-City">' + this.#overpassAPIDataLoader.city + '</span>'; |
343 | } |
344 | if ( this.#overpassAPIDataLoader.place && this.#overpassAPIDataLoader.place !== this.#overpassAPIDataLoader.city ) { |
345 | this.#noteData.address += ' (' + this.#overpassAPIDataLoader.place + ')'; |
346 | } |
347 | } |
348 | |
349 | |
350 | |
351 | |
352 | |
353 | #addAddress ( ) { |
354 | |
355 | if ( ICON_POSITION.atStart === this.#computeData.positionOnRoute ) { |
356 | |
357 | |
358 | this.#noteData.address = '🟢 ' + this.#outgoingStreetName; |
359 | this.#addCity ( ); |
360 | } |
361 | else if ( ICON_POSITION.atEnd === this.#computeData.positionOnRoute ) { |
362 | |
363 | this.#addCity ( ); |
364 | |
365 | |
366 | this.#noteData.address = this.#incomingStreetName; |
367 | this.#addCity ( ); |
368 | this.#noteData.address += ' 🔴 '; |
369 | } |
370 | else { |
371 | |
372 | |
373 | this.#noteData.address = |
374 | this.#incomingStreetName + |
375 | ( '' === this.#noteData.address ? '' : ' ⪥ ' + this.#noteData.address ) + |
376 | ' ' + this.#computeData.directionArrow + ' ' + |
377 | this.#outgoingStreetName; |
378 | this.#addCity ( ); |
379 | } |
380 | } |
381 | |
382 | |
383 | |
384 | |
385 | |
386 | #addRoundaboutInfo ( ) { |
387 | if ( this.#roundaboutData.isEntry && ! this.#roundaboutData.isExit ) { |
388 | this.#noteData.tooltipContent += theTranslator.getText ( 'StreetFinder - entry roundabout' ); |
389 | } |
390 | else if ( ! this.#roundaboutData.isEntry && this.#roundaboutData.isExit ) { |
391 | this.#noteData.tooltipContent += theTranslator.getText ( 'StreetFinder - exit roundabout' ); |
392 | } |
393 | else if ( this.#roundaboutData.isEntry && this.#roundaboutData.isExit ) { |
394 | this.#noteData.tooltipContent += |
395 | theTranslator.getText ( 'StreetFinder - continue roundabout' ); |
396 | } |
397 | if ( this.#roundaboutData.isMini ) { |
398 | this.#noteData.tooltipContent += |
399 | theTranslator.getText ( 'StreetFinder - at the small roundabout on the ground' ); |
400 | } |
401 | } |
402 | |
403 | |
404 | |
405 | |
406 | |
407 | constructor ( ) { |
408 | Object.freeze ( this ); |
409 | } |
410 | |
411 | |
412 | |
413 | |
414 | |
415 | |
416 | |
417 | |
418 | findData ( computeData, noteData, overpassAPIDataLoader ) { |
419 | |
420 | this.#computeData = computeData; |
421 | this.#noteData = noteData; |
422 | this.#overpassAPIDataLoader = overpassAPIDataLoader; |
423 | |
424 | this.#iconOsmNodeId = NOT_FOUND; |
425 | this.#incomingOsmNodeId = NOT_FOUND; |
426 | this.#outgoingOsmNodeId = NOT_FOUND; |
427 | this.#incomingStreetName = ''; |
428 | this.#outgoingStreetName = ''; |
429 | this.#roundaboutData = new RoundaboutData ( ); |
430 | |
431 | this.#findOsmNodes ( ); |
432 | this.#findMiniRoundabout ( ); |
433 | this.#addRcnRefNumber ( ); |
434 | this.#findStreets ( ); |
435 | this.#addAddress ( ); |
436 | this.#addRoundaboutInfo ( ); |
437 | } |
438 | } |
439 | |
440 | export default StreetFinder; |
441 | |
442 | |
443 | |