File : routeProviders/OsrmRouteProvider.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
import theOsrmTextInstructions from '../routeProviders/OsrmTextInstructions.js';
30
import { ICON_LIST } from '../routeProviders/IconList.js';
31
import { ZERO, ONE, LAT_LNG, HTTP_STATUS_OK } from '../main/Constants.js';
32
33
/* ------------------------------------------------------------------------------------------------------------------------- */
34
/**
35
This class implements the BaseRouteProvider for Osrm. It's not possible to instanciate
36
this class because the class is not exported from the module. Only one instance is created and added to the list
37
of Providers of TravelNotes
38
*/
39
/* ------------------------------------------------------------------------------------------------------------------------- */
40
41
class OsrmRouteProvider extends BaseRouteProvider {
42
43
    /**
44
    A reference to the edited route
45
    @type {Route}
46
    */
47
48
    #route;
49
50
    /**
51
    The round value used by PolylineEncoder
52
    @type {Number}
53
    */
54
    // eslint-disable-next-line no-magic-numbers
55
    static get #ROUND_VALUE ( ) { return 6; }
56
57
    /**
58
    Parse the response from the provider and add the received itinerary to the route itinerary
59
    @param {JsonObject} response the itinerary received from the provider
60
    @param {function} onOk a function to call when the response is parsed correctly
61
    @param {function} onError a function to call when an error occurs
62
    */
63
64
    #parseResponse ( response, onOk, onError ) {
65
66
        if ( 'Ok' !== response.code ) {
67
            onError ( new Error ( 'Response code not ok' ) );
68
            return;
69
        }
70
71
        if ( ZERO === response.routes.length ) {
72
            onError ( new Error ( 'Route not found' ) );
73
            return;
74
        }
75
76
        this.#route.itinerary.itineraryPoints.removeAll ( );
77
        this.#route.itinerary.maneuvers.removeAll ( );
78
        this.#route.itinerary.hasProfile = false;
79
        this.#route.itinerary.ascent = ZERO;
80
        this.#route.itinerary.descent = ZERO;
81
82
        const polylineEncoder = new PolylineEncoder ( );
83
        response.routes [ ZERO ].geometry = polylineEncoder.decode (
84
            response.routes [ ZERO ].geometry,
85
            [ OsrmRouteProvider.#ROUND_VALUE, OsrmRouteProvider.#ROUND_VALUE ]
86
        );
87
88
        response.routes [ ZERO ].legs.forEach (
89
            leg => {
90
                let lastPointWithDistance = ZERO;
91
                leg.steps.forEach (
92
                    step => {
93
                        step.geometry = polylineEncoder.decode (
94
                            step.geometry,
95
                            [ OsrmRouteProvider.#ROUND_VALUE, OsrmRouteProvider.#ROUND_VALUE ]
96
                        );
97
                        const maneuver = new Maneuver ( );
98
                        maneuver.iconName =
99
                            ICON_LIST [ step.maneuver.type ]
100
                                ?
101
                                ICON_LIST [ step.maneuver.type ] [ step.maneuver.modifier ]
102
                                ||
103
                                ICON_LIST [ step.maneuver.type ] [ 'default' ]
104
                                :
105
                                ICON_LIST [ 'default' ] [ 'default' ];
106
107
                        maneuver.instruction = theOsrmTextInstructions.compile ( this.userLanguage, step );
108
                        maneuver.duration = step.duration;
109
                        let distance = ZERO;
110
                        for (
111
                            let geometryCounter = ZERO;
112
                            ( ONE === step.geometry.length )
113
                                ?
114
                                ( ONE > geometryCounter )
115
                                :
116
                                ( geometryCounter < step.geometry.length );
117
                            geometryCounter ++ ) {
118
                            const itineraryPoint = new ItineraryPoint ( );
119
                            itineraryPoint.latLng = [
120
                                step.geometry [ geometryCounter ] [ ZERO ],
121
                                step.geometry [ geometryCounter ] [ ONE ]
122
                            ];
123
124
                            itineraryPoint.distance =
125
                                leg.annotation.distance [ lastPointWithDistance ]
126
                                    ?
127
                                    leg.annotation.distance [ lastPointWithDistance ]
128
                                    :
129
                                    ZERO;
130
                            this.#route.itinerary.itineraryPoints.add ( itineraryPoint );
131
                            if ( geometryCounter !== step.geometry.length - ONE ) {
132
                                distance += itineraryPoint.distance;
133
                                lastPointWithDistance ++;
134
                            }
135
                            if ( ZERO === geometryCounter ) {
136
                                maneuver.itineraryPointObjId = itineraryPoint.objId;
137
                            }
138
                        }
139
                        maneuver.distance = distance;
140
                        this.#route.itinerary.maneuvers.add ( maneuver );
141
                    }
142
                );
143
            }
144
        );
145
146
        const wayPointsIterator = this.#route.wayPoints.iterator;
147
        response.waypoints.forEach (
148
            wayPoint => {
149
                if ( ! wayPointsIterator.done ) {
150
                    wayPointsIterator.value.latLng = [ wayPoint.location [ ONE ], wayPoint.location [ ZERO ] ];
151
                }
152
            }
153
        );
154
155
        onOk ( this.#route );
156
    }
157
158
    /**
159
    Gives the url to call
160
    @return {String} a string with the url, wayPoints, transitMode, user language and Api key
161
    */
162
163
    #getUrl ( ) {
164
165
        let wayPointsString = null;
166
        this.#route.wayPoints.forEach (
167
            wayPoint => {
168
                wayPointsString = wayPointsString ? wayPointsString + ';' : '';
169
                wayPointsString +=
170
                    wayPoint.lng.toFixed ( LAT_LNG.fixed ) + ',' +
171
                    wayPoint.lat.toFixed ( LAT_LNG.fixed );
172
            }
173
        );
174
175
        // openstreetmap.de server
176
        let transitModeString = '';
177
        switch ( this.#route.itinerary.transitMode ) {
178
        case 'car' :
179
            transitModeString = 'routed-car';
180
            break;
181
        case 'bike' :
182
            transitModeString = 'routed-bike';
183
            break;
184
        case 'pedestrian' :
185
            transitModeString = 'routed-foot';
186
            break;
187
        default :
188
            return;
189
        }
190
        return 'https://routing.openstreetmap.de/' +
191
            transitModeString +
192
            '/route/v1/driving/' +
193
            wayPointsString +
194
            '?geometries=polyline6&overview=full&steps=true&annotations=distance';
195
    }
196
197
    /**
198
    Implementation of the base class #getRoute ( )
199
    @param {function} onOk the Promise Success handler
200
    @param {function} onError the Promise Error handler
201
    */
202
203
    #getRoute ( onOk, onError ) {
204
        fetch ( this.#getUrl ( ) )
205
            .then (
206
                response => {
207
                    if ( HTTP_STATUS_OK === response.status && response.ok ) {
208
                        response.json ( )
209
                            .then ( result => this.#parseResponse ( result, onOk, onError ) );
210
                    }
211
                    else {
212
                        onError ( new Error ( 'Invalid status ' + response.status ) );
213
                    }
214
                }
215
            );
216
    }
217
218
    /**
219
    The constructor
220
    */
221
222
    constructor ( ) {
223
        super ( );
224
    }
225
226
    /**
227
    Call the provider, using the waypoints defined in the route and, on success,
228
    complete the route with the data from the provider
229
    @param {Route} route The route to witch the data will be added
230
    @return {Promise} A Promise. On success, the Route is completed with the data given by the provider.
231
    */
232
233
    getPromiseRoute ( route ) {
234
        this.#route = route;
235
        return new Promise ( ( onOk, onError ) => this.#getRoute ( onOk, onError ) );
236
    }
237
238
    /**
239
    The icon used in the ProviderToolbarUI.
240
    Overload of the base class icon property
241
    @type {String}
242
    */
243
244
    get icon ( ) {
245
        return '' +
246
            'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWX' +
247
            'MAAA7DAAAOwwHHb6hkAAAAB3RJTUUH4QkaDwEMOImNWgAABTZJREFUSMftl21sFEUYx3+zu3ft9a7Q0tIXoKUCIkIVeYsECmgbBK' +
248
            'IIEVC0QEs0UIEghA+CEkkIaCyJBhFBBC00qHxoQClvhgLyEonyckhbFKS90iK0FAqFHr273R0/XNlCymEREr44X3by7DPzm5n9z8' +
249
            'x/hZRS8giKwiMqjwys3U/y/pIqdh0tp8RTS229F0yIaxtBz+R2jOjfhSFPJbW6L9Gab/zFdjfv5x/k2g0f4TYVISUCEJKmp8QfMG' +
250
            'jjsLNoShozxvR7MHD1VS+D53+Pp/oqmqJYoEBAxzQlQoKmCMJUJTgAwDQkye0j2f1JJvHRzvsH/3m+jqfn5IOUICWGYZIY5WTScz' +
251
            '1J751EYjsXArh45QZ73BV8W1RC7VUvNkVBEBzUoZXZPJEc03rwhboGkqettWYopeSb2SPIHPbkPZfvu6JSZizfidIExpQU5+eQ0M' +
252
            '7VOnDS9LVcvNKAkBDtDOf35ZOIj3K2SjSXrnoZNDOPuvqbCCA+yklxfs6/g3O3HOXd/ANoikCakvPrphEfFWG9P1R6ni+3n6Ck4h' +
253
            'JI6JUcS/YLqaQ/09nKuVx/kx6TVoGUmIZkYVYac18beG+wfeLnGIaBqZtseGckk59rXt7xH/5IwaHTRNjVO1Tt8wcY1b8rWxaPs3' +
254
            'J/OHiarKVbsKkqqoCLW+eFPkB2uD0EfAEAOsa67oC+tHgzW387iyNMQ0qJ1xfA6wsgpSTcprHX7eHFBZus/DFp3XksMRoAX0Bn16' +
255
            '9nQ4N3HT+H0FQApqb3suLbj5Sx7UgZihA0Bgxmje5HeV4Ong05zB7bn8aAjhCCfSfOUfjLGatd5vBUkGDXVPYeKQ8NdldcCu7FgM' +
256
            'HIPilWfMU2N2E2FVNK3pvwLLlvDqNzXBuS2rdh6dShfJCZhikldpvKmkK31e75vin4AjoAxWU1ocHV17zBimnS4bYtcKysGgC/T2' +
257
            'feK/1bKHTu+AF4G4OfyH2m2oonxrgwTGmpPSRYms11VRFWPaA3vZCSMFvL4z3MpnFLorrZ3IkixG19y9DgmMjwWy34+0qDFe/RqR' +
258
            '0AtjAbeUXFLcAbfjpJRHhwQI835QJU1zVYE4hqEx4anJocAwKEprK3uNKKZ6X3wjAlqiKYvXoP63c3wzfuKWHGil2oioJhSt7IaB' +
259
            'bl/hPnsNuCYu2ZEhd6Hxcc/ovxuYUoqqB7QhSnVmRZid1z1lFZcx0BGLqB36+DBIddw6YKhITEaCen8qZbbQbmfE1ZVR2GYbBuwc' +
260
            'uMHdrj7jMeN7CbVf+j8jI7jnmaBfbpZDrEuDClRFMVnA47LocdTVUwTUlctJPDK7Ot/KKj5ZSW1yIBw5R3QO/qQOaN7QcSNJvKq8' +
261
            'sKaWhSq8th5+xXb7F40mA6xUbScNPPDa+fjjGRLMwczOm86bR1hgFw06eT/dFWwuzBZZ45bkDrLglX5kp8fh0hITk2kpOfTcFhb5' +
262
            '1ZafTrDJqZx7mL1xAED4+qzXMQrfFcB5ZMQPcbAFTWXichazU/3ya2UOVQcRXdMldRUX0teFT6DQo/ntgCek8jsPO4h1GLCrDbNZ' +
263
            'ASv1+nX9d4sjJSyeidREJ00AhcqGtgn7uCjUUlHDt9AYdNQyDx+XQKlkxgxIAu9299jpbVMGT+JgzDsG4j2TQIacqgFhRBuKagCm' +
264
            'HlaIpgZ+7r9O2e8N/NXsAwmbVmD2u2ubFpKpoiWpo9JNIIGr63R/dhWU4Gmqo8uMsE8OsG64tK2X3cQ7Gnhsv1jShS0L5tOL2SY8' +
265
            'jok8Lk4anYm263h2Jv//+FeRjlHxKxS4in4X1YAAAAAElFTkSuQmCC';
266
    }
267
268
    /**
269
    The provider name.
270
    Overload of the base class name property
271
    @type {String}
272
    */
273
274
    get name ( ) { return 'OSRM'; }
275
276
    /**
277
    The title to display in the ProviderToolbarUI button.
278
    Overload of the base class title property
279
    @type {String}
280
    */
281
282
    get title ( ) { return 'OSRM'; }
283
284
    /**
285
    The possible transit modes for the provider.
286
    Overload of the base class transitModes property
287
    Must be a subarray of [ 'bike', 'pedestrian', 'car', 'train', 'line', 'circle' ]
288
    @type {Array.<String>}
289
    */
290
291
    get transitModes ( ) { return [ 'bike', 'pedestrian', 'car' ]; }
292
293
    /**
294
    A boolean indicating when a provider key is needed for the provider.
295
    Overload of the base class providerKeyNeeded property
296
    @type {Boolean}
297
    */
298
299
    get providerKeyNeeded ( ) { return false; }
300
301
    /**
302
    The user language. Overload of the base class userLanguage property.
303
    It's needed to overload the setter AND the getter otherwise the getter returns undefined
304
    @type {String}
305
    */
306
307
    get userLanguage ( ) { return super.userLanguage; }
308
    set userLanguage ( userLanguage ) {
309
        super.userLanguage = theOsrmTextInstructions.loadLanguage ( userLanguage );
310
    }
311
}
312
313
window.TaN.addProvider ( OsrmRouteProvider );
314
315
/* --- End of file --------------------------------------------------------------------------------------------------------- */
316