File : routeProviders/MapzenValhallaRouteProvider.js

1
/*
2
Copyright - 2017 2023 - wwwouaiebe - Contact: https://www.ouaie.be/
3
4
This  program is free software;
5
you can redistribute it and/or modify it under the terms of the
6
GNU General Public License as published by the Free Software Foundation;
7
either version 3 of the License, or any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
*/
18
/*
19
Changes:
20
    - v4.0.0:
21
        - created from v3.6.0
22
Doc reviewed 202208
23
 */
24
25
import PolylineEncoder from '../core/lib/PolylineEncoder.js';
26
import ItineraryPoint from '../data/ItineraryPoint.js';
27
import Maneuver from '../data/Maneuver.js';
28
import BaseRouteProvider from '../routeProviders/BaseRouteProvider.js';
29
30
import { ZERO, HTTP_STATUS_OK, DISTANCE } from '../main/Constants.js';
31
32
/* ------------------------------------------------------------------------------------------------------------------------- */
33
/**
34
This class implements the BaseRouteProvider for MapzenValhalla. It's not possible to instanciate
35
this class because the class is not exported from the module. Only one instance is created and added to the list
36
of Providers of TravelNotes
37
*/
38
/* ------------------------------------------------------------------------------------------------------------------------- */
39
40
class MapzenValhallaRouteProvider extends BaseRouteProvider {
41
42
    /**
43
    The provider key. Will be set by TravelNotes
44
    @type {String}
45
    */
46
47
    #providerKey;
48
49
    /**
50
    A reference to the edited route
51
    @type {Route}
52
    */
53
54
    #route;
55
56
    /**
57
    The round value used by PolylineEncoder
58
    @type {Number}
59
    */
60
    // eslint-disable-next-line no-magic-numbers
61
    static get #ROUND_VALUE ( ) { return 6; }
62
63
    /**
64
    Enum for icons
65
    @type {Array.<String>}
66
    */
67
68
    static get #ICON_LIST ( ) {
69
        return [
70
            'kUndefined', // kNone = 0;
71
            'kDepartDefault', // kStart = 1;
72
            'kDepartRight', // kStartRight = 2;
73
            'kDepartLeft', // kStartLeft = 3;
74
            'kArriveDefault', // kDestination = 4;
75
            'kArriveRight', // kDestinationRight = 5;
76
            'kArriveLeft', // kDestinationLeft = 6;
77
            'kNewNameStraight', // kBecomes = 7;
78
            'kContinueStraight', // kContinue = 8;
79
            'kTurnSlightRight', // kSlightRight = 9;
80
            'kTurnRight', // kRight = 10;
81
            'kTurnSharpRight', // kSharpRight = 11;
82
            'kUturnRight', // kUturnRight = 12;
83
            'kUturnLeft', // kUturnLeft = 13;
84
            'kTurnSharpLeft', // kSharpLeft = 14;
85
            'kTurnLeft', // kLeft = 15;
86
            'kTurnSlightLeft', // kSlightLeft = 16;
87
            'kUndefined', // kRampStraight = 17;
88
            'kOnRampRight', // kRampRight = 18;
89
            'kOnRampLeft', // kRampLeft = 19;
90
            'kOffRampRight', // kExitRight = 20;
91
            'kOffRampLeft', // kExitLeft = 21;
92
            'kStayStraight', // kStayStraight = 22;
93
            'kStayRight', // kStayRight = 23;
94
            'kStayLeft', // kStayLeft = 24;
95
            'kMergeDefault', // kMerge = 25;
96
            'kRoundaboutRight', // kRoundaboutEnter = 26;
97
            'kRoundaboutExit', // kRoundaboutExit = 27;
98
            'kFerryEnter', // kFerryEnter = 28;
99
            'kFerryExit', // kFerryExit = 29;
100
            'kUndefined', // kTransit = 30;
101
            'kUndefined', // kTransitTransfer = 31;
102
            'kUndefined', // kTransitRemainOn = 32;
103
            'kUndefined', // kTransitConnectionStart = 33;
104
            'kUndefined', // kTransitConnectionTransfer = 34;
105
            'kUndefined', // kTransitConnectionDestination = 35;
106
            'kUndefined' // kPostTransitConnectionDestination = 36;
107
        ];
108
    }
109
110
    /**
111
    Parse the response from the provider and add the received itinerary to the route itinerary
112
    @param {JsonObject} response the itinerary received from the provider
113
    @param {function} onOk a function to call when the response is parsed correctly
114
    @param {function} onError a function to call when an error occurs
115
    */
116
117
    #parseResponse ( response, onOk, onError ) {
118
        if ( ZERO === response.trip.legs.length ) {
119
            onError ( 'Route not found' );
120
            return;
121
        }
122
123
        this.#route.itinerary.itineraryPoints.removeAll ( );
124
        this.#route.itinerary.maneuvers.removeAll ( );
125
        this.#route.itinerary.hasProfile = false;
126
        this.#route.itinerary.ascent = ZERO;
127
        this.#route.itinerary.descent = ZERO;
128
129
        response.trip.legs.forEach (
130
            leg => {
131
                leg.shape = new PolylineEncoder ( ).decode (
132
                    leg.shape,
133
                    [ MapzenValhallaRouteProvider.#ROUND_VALUE, MapzenValhallaRouteProvider.#ROUND_VALUE ]
134
                );
135
                const itineraryPoints = [];
136
                for ( let shapePointCounter = ZERO; shapePointCounter < leg.shape.length; shapePointCounter ++ ) {
137
                    let itineraryPoint = new ItineraryPoint ( );
138
                    itineraryPoint.latLng = leg.shape [ shapePointCounter ];
139
                    itineraryPoints.push ( itineraryPoint );
140
                    this.#route.itinerary.itineraryPoints.add ( itineraryPoint );
141
                }
142
                leg.maneuvers.forEach (
143
                    mapzenManeuver => {
144
                        const travelNotesManeuver = new Maneuver ( );
145
                        travelNotesManeuver.iconName = MapzenValhallaRouteProvider.#ICON_LIST [ mapzenManeuver.type || ZERO ];
146
                        travelNotesManeuver.instruction = mapzenManeuver.instruction || '';
147
                        travelNotesManeuver.distance = ( mapzenManeuver.length || ZERO ) * DISTANCE.metersInKm;
148
                        travelNotesManeuver.duration = mapzenManeuver.time || ZERO;
149
                        travelNotesManeuver.itineraryPointObjId = itineraryPoints [ mapzenManeuver.begin_shape_index ].objId;
150
                        this.#route.itinerary.maneuvers.add ( travelNotesManeuver );
151
                    }
152
                );
153
            }
154
        );
155
156
        const wayPointsIterator = this.#route.wayPoints.iterator;
157
        response.trip.locations.forEach (
158
            curLocation => {
159
                if ( ! wayPointsIterator.done ) {
160
                    wayPointsIterator.value.latLng = [ curLocation.lat, curLocation.lon ];
161
                }
162
            }
163
        );
164
165
        onOk ( this.#route );
166
    }
167
168
    /**
169
    Gives the url to call
170
    @return {String} a string with the url, wayPoints, transitMode, user language and Api key
171
    */
172
173
    #getUrl ( ) {
174
        const request = {
175
            locations : [],
176
            costing : '',
177
            // eslint-disable-next-line camelcase
178
            directions_options : { language : this.userLanguage },
179
            // eslint-disable-next-line camelcase
180
            costing_options : {}
181
        };
182
183
        const wayPointsIterator = this.#route.wayPoints.iterator;
184
        while ( ! wayPointsIterator.done ) {
185
            request.locations.push (
186
                {
187
                    lat : wayPointsIterator.value.lat,
188
                    lon : wayPointsIterator.value.lng,
189
                    type : wayPointsIterator.first || wayPointsIterator.last ? 'break' : 'through'
190
                }
191
            );
192
        }
193
194
        switch ( this.#route.itinerary.transitMode ) {
195
        case 'bike' :
196
            request.costing = 'bicycle';
197
            // eslint-disable-next-line camelcase
198
            request.costing_options = {
199
                bicycle : {
200
                    // eslint-disable-next-line camelcase, no-magic-numbers
201
                    maneuver_penalty : 30,
202
                    // eslint-disable-next-line camelcase
203
                    bicycle_type : 'Cross',
204
                    // eslint-disable-next-line camelcase
205
                    cycling_speed : '20.0',
206
                    // eslint-disable-next-line camelcase
207
                    use_roads : '0.25',
208
                    // eslint-disable-next-line camelcase
209
                    use_hills : '0.25'
210
                }
211
            };
212
213
            break;
214
        case 'pedestrian' :
215
            request.costing = 'pedestrian';
216
            // eslint-disable-next-line camelcase
217
            request.costing_options = { pedestrian : { walking_speed : '4.0' } };
218
            break;
219
        case 'car' :
220
            request.costing = 'auto';
221
            // eslint-disable-next-line camelcase
222
            request.costing_options = { auto : { country_crossing_cost : '60' } };
223
            break;
224
        default :
225
            break;
226
        }
227
228
        return 'https://api.stadiamaps.com/route?json=' + JSON.stringify ( request ) + '&api_key=' + this.#providerKey;
229
230
    }
231
232
    /**
233
    Overload of the base class #getRoute ( ) method
234
    @param {function} onOk the Promise Success handler
235
    @param {function} onError the Promise Error handler
236
    */
237
238
    #getRoute ( onOk, onError ) {
239
        fetch ( this.#getUrl ( ) )
240
            .then (
241
                response => {
242
                    if ( HTTP_STATUS_OK === response.status && response.ok ) {
243
                        response.json ( )
244
                            .then ( result => this.#parseResponse ( result, onOk, onError ) );
245
                    }
246
                    else {
247
                        onError ( new Error ( 'Invalid status ' + response.status ) );
248
                    }
249
                }
250
            )
251
            .catch (
252
253
                // calling onError without parameters because fetch don't accecpt to add something as parameter :-(...
254
                ( ) => { onError ( ); }
255
            );
256
    }
257
258
    /**
259
    The constructor
260
    */
261
262
    constructor ( ) {
263
        super ( );
264
        this.#providerKey = '';
265
    }
266
267
    /**
268
    Call the provider, using the waypoints defined in the route and, on success,
269
    complete the route with the data from the provider
270
    @param {Route} route The route to witch the data will be added
271
    @return {Promise} A Promise. On success, the Route is completed with the data given by the provider.
272
    */
273
274
    getPromiseRoute ( route ) {
275
        this.#route = route;
276
        return new Promise ( ( onOk, onError ) => this.#getRoute ( onOk, onError ) );
277
    }
278
279
    /**
280
    The icon used in the ProviderToolbarUI.
281
    Overload of the base class icon property
282
    @type {String}
283
    */
284
285
    get icon ( ) {
286
        return '' +
287
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wAAAAAzJ3zzAAAGTElEQV' +
288
            'RIx+VXe1BU1xn/zjn7ugvL4sIuQnll5U0ELAQxig7WiQYz6NRHa6O206qdSXXSxs60dTK200zNY9q0dcRpMs1jkrRNWmaijCVoaU' +
289
            'uHgJESg1JY2DfIQ4EFlr13797de87pH5VILamsnTZ/9Pfn7373/s73fb/7nXMQ55zDpwAMnxLuW/hyZ7frfy7MOY+91vi2TZKkwH' +
290
            '9F+NpHfZNupzeoqlRczN+cuCXKkmJ2O73auxYUHvbfCHR/0BO6l7BmKZJSOk8ISXINeW3uId9c72WHuG33Jla+5kGlsDhP7+gfAg' +
291
            'CA3g/7eHFpYcTt8kq9PX38z+91keKKPMw5CNU1VUBVGiIaYlpKAy3l6tM//oVXVWnyfFDU7NxTHyqrKE3x+4al/r5BnSAYxM72bu' +
292
            'IeGLFRykLffPrQtN87smJ1RUk0L99uDkzPhJ7/wWmhpLwg2na+c+Kdiy+XLDtj20prZlb2ZyaudF01vXr21xZjgkGu27oBr6ksU8' +
293
            '3mJPJ6Y5MNIQBCsMlqTYnk2rOh/7qDn/ttS7jvr06Wk5/BajZUTXucw+lxlVqr1eCMzHTLqoJcedtjm5XMrAw+6HBBS/MfNKVlRS' +
294
            'JCd2IH+oe0gamZSK49W7v38R3Sl76sCZxrakm123NTrDZLclzmQhhHGWM6grGsKEowyZxkqa6ptBz5xleSXENe8z8Z8MN+9eDX9l' +
295
            'kEo6C2XvijlmiITafXIUqZHmEkx5UxwTjMKE1NNCWkqioNLzJd5NK5DkIIBuDA6/fU3Vy/sQb/5NQZufeKI+27Pzo6yyijVltKqq' +
296
            'qqgBGeAQDjsoUxxtFwWB7T6XUrGWN0gfd6/CFCsNVelCUdOLR31uv2G7539JQZY6RNSUsOF5cUCn7fCMMYRWKxWAhhROPKWKPVIE' +
297
            'opzcnNCkXkiHi15xq9/tHfyKXz7+sOPbkvkJmVLv6y8S3LmO+WCeN/NLxhzyPhQYcTGwyGYFlFaRKlVEQI4WULcw6xwuJ84nH5cH' +
298
            'NTa9Q9cCNzwUzb99TNY4KVZ77zMwEAzgLAFgCoZIzH8grs0oljz2UjBJaMHFtw45aa5LpHaqcYYzLGWFhSWI2p1OX0zPZdGyBtFz' +
299
            'qILCm2j422yME166vmn/9+owUA9r/VfPbd/Q1PrACAqe2766S+awMCQoAAAMaHJ81vv9IMALCCUhbaurN2tqJyNS4oyjMIgqD/JF' +
300
            'cvuU2uKs6enQ+G9LIYMQKA5TZtBgC8dn1lqPmdS6Z4ZvW/TC7OIebz+mc9Lh/vbL+C3AM3bAgBPHXyyPT537Vij2PEAgAKAFwCgO' +
301
            'pVJdmoYdc2zU+feXlhMXC71CzXnj1fXFKYhjE23LPHCIHW5xnWr7Akhw8fPShE5MhoKCQmm0yJsscxknU7TM85PLbrwLaZNVXlkZ' +
302
            'd+/rp+7abyucrqcpaWnhZklCYJghBufrdVW/pgsWHZA0QOy0FCcHTYN2IOBGYTKz5blvhBV0/SorKHfvjit0eNCYLm6SefS3t0x+' +
303
            'fY0aeOJK/bsNYCALbrvf0mhBGNKlEW7+4kGASDcWZmLmjW66KiKAVbmtqNACB//fjjYas1NfTS6Tes48OTCQghKF1dHP34gxoSZo' +
304
            'xbDQZDLud8Kq6RyTjXazVagyzJQUKI3H25R123eY3U+MazMeAAp06czh0fnkxY6GdKqiXtzvAhyvjohB8TrDDGjXEJc8Z0mGCFUm' +
305
            'oUBENqVk7m3I5dj/KO9i5GNCS8OHbjlhr+++aLEy6nJ0gpVTQagjgHPcE4yjnXxVVqxrgBYzwuimGhrfUvzOXwWwtKHlAf3ljNch' +
306
            '7IVji/838XlRSIiqIIXR3d6gsnz4Qfqi1PlOUIRRgHlIiSEZfw1GRg/MSxZ40JJsG4+0B9pGFX/dTY6Hhye1snFBTdTMwvzZ5cOA' +
307
            'hMTU7TP13s0KyrrYqefOG4Oj52M/bKmd+kH957nALAKADYly28dftmdvCrX+DvXWjTNb3ZEklN60rcUr9J88UDn+fpGStNgtEguQ' +
308
            'dGYOvOWlbz8EM5efn26YH+QfjVa03EMziiLyqzo2PHj5jcLq/0706Mnwifx39rbjY4czc/PjYxs7/hCd579Xrw7meSJM27nJ55fg' +
309
            '8Avw8wxqKH931rThTFaX6fgPt9sev9K07+HwD9392d/g5xBCylN3zlQgAAAABJRU5ErkJggg==';
310
    }
311
312
    /**
313
    The provider name.
314
    Overload of the base class name property
315
    @type {String}
316
    */
317
318
    get name ( ) { return 'MapzenValhalla'; }
319
320
    /**
321
    The title to display in the ProviderToolbarUI button.
322
    Overload of the base class title property
323
    @type {String}
324
    */
325
326
    get title ( ) { return 'Mapzen Valhalla with Stadia Maps'; }
327
328
    /**
329
    The possible transit modes for the provider.
330
    Overload of the base class transitModes property
331
    Must be a subarray of [ 'bike', 'pedestrian', 'car', 'train', 'line', 'circle' ]
332
    @type {Array.<String>}
333
    */
334
335
    get transitModes ( ) { return [ 'bike', 'pedestrian', 'car' ]; }
336
337
    /**
338
    A boolean indicating when a provider key is needed for the provider.
339
    Overload of the base class providerKeyNeeded property
340
    @type {Boolean}
341
    */
342
343
    get providerKeyNeeded ( ) { return true; }
344
345
    /**
346
    The provider key.
347
    Overload of the base class providerKey property
348
    */
349
350
    set providerKey ( providerKey ) { this.#providerKey = providerKey; }
351
}
352
353
window.TaN.addProvider ( MapzenValhallaRouteProvider );
354
355
/* --- End of file --------------------------------------------------------------------------------------------------------- */
356