File : routeProviders/OpenRouteServiceRouteProvider.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, ONE, TWO, LAT, LNG, ELEVATION, LAT_LNG, HTTP_STATUS_OK } from '../main/Constants.js';
31
32
/* ------------------------------------------------------------------------------------------------------------------------- */
33
/**
34
This class implements the BaseRouteProvider for OpenRouteService. 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 OpenRouteServiceRouteProvider 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 5; }
62
63
    /**
64
    Enum for icons
65
    @type {Array.<String>}
66
    */
67
68
    static get #ICON_LIST ( ) {
69
        return [
70
            'kTurnLeft',
71
            'kTurnRight',
72
            'kTurnSharpLeft',
73
            'kTurnSharpRight',
74
            'kTurnSlightLeft',
75
            'kTurnSlightRight',
76
            'kContinueStraight',
77
            'kRoundaboutRight',
78
            'kRoundaboutExit',
79
            'kUturnLeft',
80
            'kArriveDefault',
81
            'kDepartDefault',
82
            'kStayLeft',
83
            'kStayRight'
84
        ];
85
    }
86
87
    /**
88
    Parse the response from the provider and add the received itinerary to the route itinerary
89
    @param {JsonObject} response the itinerary received from the provider
90
    @param {function} onOk a function to call when the response is parsed correctly
91
    @param {function} onError a function to call when an error occurs
92
    */
93
94
    #parseResponse ( response, onOk, onError ) {
95
96
        if ( ! response.routes || ZERO === response.routes.length ) {
97
            onError ( new Error ( 'Route not found' ) );
98
            return;
99
        }
100
        response.routes [ ZERO ].geometry = new polylineEncoder ( ).decode (
101
            response.routes [ ZERO ].geometry,
102
            [ OpenRouteServiceRouteProvider.#ROUND_VALUE, OpenRouteServiceRouteProvider.#ROUND_VALUE, TWO ]
103
        );
104
        this.#route.itinerary.itineraryPoints.removeAll ( );
105
        this.#route.itinerary.maneuvers.removeAll ( );
106
        this.#route.itinerary.hasProfile = true;
107
        this.#route.itinerary.ascent = ZERO;
108
        this.#route.itinerary.descent = ZERO;
109
110
        let wayPointIndex = ZERO;
111
        let itineraryPoint = new ItineraryPoint ( );
112
        itineraryPoint.lat = response.routes [ ZERO ].geometry [ wayPointIndex ] [ LAT ];
113
        itineraryPoint.lng = response.routes [ ZERO ].geometry [ wayPointIndex ] [ LNG ];
114
        itineraryPoint.elev = response.routes [ ZERO ].geometry [ wayPointIndex ] [ ELEVATION ];
115
        this.#route.itinerary.itineraryPoints.add ( itineraryPoint );
116
        wayPointIndex ++;
117
118
        response.routes [ ZERO ].segments.forEach (
119
            segment => {
120
                segment.steps.forEach (
121
                    step => {
122
                        let maneuver = new Maneuver ( );
123
                        maneuver.iconName = OpenRouteServiceRouteProvider.#ICON_LIST [ step.type ] || 'kUndefined';
124
                        maneuver.instruction = step.instruction;
125
                        maneuver.duration = step.duration;
126
                        maneuver.distance = step.distance;
127
                        maneuver.itineraryPointObjId = this.#route.itinerary.itineraryPoints.last.objId;
128
                        this.#route.itinerary.maneuvers.add ( maneuver );
129
                        while ( wayPointIndex <= step.way_points [ ONE ] ) {
130
                            if (
131
                                itineraryPoint.lat !== response.routes [ ZERO ].geometry [ wayPointIndex ] [ LAT ]
132
                                ||
133
                                itineraryPoint.lng !== response.routes [ ZERO ].geometry [ wayPointIndex ] [ LNG ]
134
                            ) {
135
                                itineraryPoint = new ItineraryPoint ( );
136
                                itineraryPoint.lat = response.routes [ ZERO ].geometry [ wayPointIndex ] [ LAT ];
137
                                itineraryPoint.lng = response.routes [ ZERO ].geometry [ wayPointIndex ] [ LNG ];
138
                                itineraryPoint.elev = response.routes [ ZERO ].geometry [ wayPointIndex ] [ ELEVATION ];
139
                                this.#route.itinerary.itineraryPoints.add ( itineraryPoint );
140
                            }
141
                            wayPointIndex ++;
142
                        }
143
                    }
144
                );
145
            }
146
        );
147
        const wayPointsIterator = this.#route.wayPoints.iterator;
148
        response.routes [ ZERO ].way_points.forEach (
149
            wayPoint => {
150
                if ( ! wayPointsIterator.done ) {
151
                    wayPointsIterator.value.latLng = response.routes [ ZERO ].geometry [ wayPoint ];
152
                }
153
            }
154
        );
155
156
        onOk ( this.#route );
157
    }
158
159
    /**
160
    Gives the url to call
161
    @return {String} a string with the url, wayPoints, transitMode, user language and Api key
162
    */
163
164
    #getUrl ( ) {
165
        let requestString = 'https://api.openrouteservice.org/v2/directions/';
166
        switch ( this.#route.itinerary.transitMode ) {
167
        case 'car' :
168
            requestString += 'driving-car';
169
            break;
170
        case 'bike' :
171
            requestString += 'cycling-regular';
172
            break;
173
        case 'pedestrian' :
174
            requestString += 'foot-walking';
175
            break;
176
        default :
177
            return;
178
        }
179
        return requestString;
180
    }
181
182
    /**
183
    Gives the request headers
184
    @return {Array.<Object>} an array with the needed request headers
185
    */
186
187
    #getRequestHeaders ( ) {
188
189
        const orsHeaders = new Headers ( );
190
        orsHeaders.append ( 'Accept', 'application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8' );
191
        orsHeaders.append ( 'Content-Type', 'application/json' );
192
        orsHeaders.append ( 'Authorization', this.#providerKey );
193
194
        return orsHeaders;
195
    }
196
197
    /**
198
    Gives the options and wayPoints for the request body
199
    @return {String} a string with the wayPoint coordinates, elevation param and language in JSON format
200
    */
201
202
    #getBody ( ) {
203
        let wayPointsString = null;
204
        this.#route.wayPoints.forEach (
205
            wayPoint => {
206
                wayPointsString = wayPointsString ? wayPointsString + ',' : '{"coordinates":[';
207
                wayPointsString +=
208
                    '[' + wayPoint.lng.toFixed ( LAT_LNG.fixed ) +
209
                    ',' + wayPoint.lat.toFixed ( LAT_LNG.fixed ) + ']';
210
            }
211
        );
212
        wayPointsString += '],"elevation":"true","language":"' + this.userLanguage + '"}';
213
214
        return wayPointsString;
215
216
    }
217
218
    /**
219
    Overload of the base class #getRoute ( ) method
220
    @param {function} onOk the Promise Success handler
221
    @param {function} onError the Promise Error handler
222
    */
223
224
    #getRoute ( onOk, onError ) {
225
        fetch ( this.#getUrl ( ), { method : 'POST', headers : this.#getRequestHeaders ( ), body : this.#getBody ( ) } )
226
            .then (
227
                response => {
228
                    if ( HTTP_STATUS_OK === response.status && response.ok ) {
229
                        response.json ( )
230
                            .then ( result => this.#parseResponse ( result, onOk, onError ) );
231
                    }
232
                    else {
233
                        onError ( new Error ( 'Invalid status ' + response.status ) );
234
                    }
235
                }
236
            )
237
            .catch (
238
239
                // calling onError without parameters because fetch don't accecpt to add something as parameter :-(...
240
                ( ) => { onError ( ); }
241
            );
242
    }
243
244
    /**
245
    The constructor
246
    */
247
248
    constructor ( ) {
249
        super ( );
250
        this.#providerKey = '';
251
    }
252
253
    /**
254
    Call the provider, using the waypoints defined in the route and, on success,
255
    complete the route with the data from the provider
256
    @param {Route} route The route to witch the data will be added
257
    @return {Promise} A Promise. On success, the Route is completed with the data given by the provider.
258
    */
259
260
    getPromiseRoute ( route ) {
261
        this.#route = route;
262
        return new Promise ( ( onOk, onError ) => this.#getRoute ( onOk, onError ) );
263
    }
264
265
    /**
266
    The icon used in the ProviderToolbarUI.
267
    Overload of the base class icon property
268
    @type {String}
269
    */
270
271
    get icon ( ) {
272
        return '' +
273
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QAzwBfAF8OMuGdAAAACXBIWX' +
274
            'MAAA7DAAAOwwHHb6hkAAAAB3RJTUUH4QoBEgIhgj0YgQAAAmdJREFUSMftlk9LYlEYxn+WinqdaWga0owaa9RWFS2iTZt27YJu5K' +
275
            '4bFNSqFkFEcaHvUNAmUjdCGdQHqE8QGVGRY1BpOZEFGepo+WcW43WySWYZAz6rc5/3HH7nfR84XJV/ZCTPO6iKd1IFXAFXwP8/WP' +
276
            '3a0FutmEURQ2sr5PMkgkEi6+ukr64A6HS5SvZnUilSoRBht5v09TVVgoDF6eRDeztqg4FMPE48EODH5ibP0ejbHWvq6vg2O4umpo' +
277
            'bvskxAltHW1mKbn0drMpUADyQJvyRxubKC0W6naWwMgObxcT739nK5vMzhxARXHg+1PT20TE2VH7VZFKnW6Yj4fDzd3vIcjRLx+V' +
278
            'Dr9ZgGBv4alwqIn5wAoLNYADC2tQEg2O3kczke/X4OJInAwkJ5sHIoeXZW9JS1UnupvErFx44OAH5eXPzef34OQIMo4lhcRHA4/p' +
279
            '2x2mgEIJtMFj1lrdQUvcz68fiYcOE7tLpKy/Q0+sZGDE1N2ObmeNjfJ+x2k43F3u5YgVQLQtGrLgAziUQJ2D86yvnSErlMhiqNhs' +
280
            'zDAwDPd3cEZJmwx8NTwfvU1cXXycnyo44HAsV8FAk2GwCJ09PSfPN5Ynt7XHu9GO12zENDf4q5HPe7u5zMzBDx+QAwWK3lwTdbW2' +
281
            'TTacyiiNZkQltfj3lwkEwqxc329ptZ3e/skAyHqevrQ2syYZNlOl0uarq7IZcrZq40Vbz46z8QXXMzDaKIodBpMhgksrFBKhQqyf' +
282
            'ZAkopnvvT3YxkeJnZ4SHhtDYvTieBwoBYEsskkj0dHRLxesvF4eXDlra6AK+AKuAIup1+E4uxBnFG6zQAAAABJRU5ErkJggg==';
283
    }
284
285
    /**
286
    The provider name.
287
    Overload of the base class name property
288
    @type {String}
289
    */
290
291
    get name ( ) { return 'OpenRouteService'; }
292
293
    /**
294
    The title to display in the ProviderToolbarUI button.
295
    Overload of the base class title property
296
    @type {String}
297
    */
298
299
    get title ( ) { return 'OpenRouteService'; }
300
301
    /**
302
    The possible transit modes for the provider.
303
    Overload of the base class transitModes property
304
    Must be a subarray of [ 'bike', 'pedestrian', 'car', 'train', 'line', 'circle' ]
305
    @type {Array.<String>}
306
    */
307
308
    get transitModes ( ) { return [ 'bike', 'pedestrian', 'car' ]; }
309
310
    /**
311
    A boolean indicating when a provider key is needed for the provider.
312
    Overload of the base class providerKeyNeeded property
313
    @type {Boolean}
314
    */
315
316
    get providerKeyNeeded ( ) { return true; }
317
318
    /**
319
    The provider key.
320
    Overload of the base class providerKey property
321
    */
322
323
    set providerKey ( providerKey ) { this.#providerKey = providerKey; }
324
}
325
326
window.TaN.addProvider ( OpenRouteServiceRouteProvider );
327
328
/* --- End of file --------------------------------------------------------------------------------------------------------- */
329