File : core/lib/Router.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 theTravelNotesData from '../../data/TravelNotesData.js';
26
import theGeometry from './Geometry.js';
27
import theErrorsUI from '../../uis/errorsUI/ErrorsUI.js';
28
import theEventDispatcher from './EventDispatcher.js';
29
import theSphericalTrigonometry from './SphericalTrigonometry.js';
30
import Zoomer from '../../core/Zoomer.js';
31
import theProfileDialogsManager from '../../core/ProfileDialogsManager.js';
32
import theRouteEditor from '../../core/RouteEditor.js';
33
34
import { DISTANCE, ZERO } from '../../main/Constants.js';
35
36
/* ------------------------------------------------------------------------------------------------------------------------- */
37
/**
38
Start the routing and adapt the linked data after routing
39
*/
40
/* ------------------------------------------------------------------------------------------------------------------------- */
41
42
class Router {
43
44
    /**
45
    A guard to avoid that the router is called when already busy.
46
    @type {Boolean}
47
    */
48
49
    #routingRequestStarted;
50
51
    /**
52
    A flag indicating when a zoom to the route must be performed at the end of the routing
53
    @type {Boolean}
54
    */
55
56
    #zoomToRouteAfterRouting;
57
58
    /**
59
    This method compute the route, itineraryPoints and maneuvers distances
60
    @param {Route} route The route for witch the distances are computed
61
    */
62
63
    #computeRouteDistances ( route ) {
64
65
        // Computing the distance between itineraryPoints
66
        const itineraryPointsIterator = route.itinerary.itineraryPoints.iterator;
67
        const maneuverIterator = route.itinerary.maneuvers.iterator;
68
69
        itineraryPointsIterator.done;
70
        maneuverIterator.done;
71
72
        maneuverIterator.value.distance = DISTANCE.defaultValue;
73
        maneuverIterator.done;
74
        route.distance = DISTANCE.defaultValue;
75
        route.duration = DISTANCE.defaultValue;
76
77
        while ( ! itineraryPointsIterator.done ) {
78
            itineraryPointsIterator.previous.distance = theSphericalTrigonometry.pointsDistance (
79
                itineraryPointsIterator.previous.latLng,
80
                itineraryPointsIterator.value.latLng
81
            );
82
            route.distance += itineraryPointsIterator.previous.distance;
83
            maneuverIterator.previous.distance += itineraryPointsIterator.previous.distance;
84
            if ( maneuverIterator.value.itineraryPointObjId === itineraryPointsIterator.value.objId ) {
85
                route.duration += maneuverIterator.previous.duration;
86
                maneuverIterator.value.distance = DISTANCE.defaultValue;
87
                if (
88
                    maneuverIterator.next
89
                    &&
90
                    maneuverIterator.value.itineraryPointObjId === maneuverIterator.next.itineraryPointObjId
91
                ) {
92
93
                    // 2 maneuvers on the same itineraryPoint. We skip the first maneuver
94
                    maneuverIterator.done;
95
                    maneuverIterator.value.distance = DISTANCE.defaultValue;
96
                }
97
                maneuverIterator.done;
98
99
            }
100
        }
101
    }
102
103
    /**
104
    Error handler for the startRouting method
105
    @param {?Error} err the error to handle
106
    */
107
108
    #onRoutingError ( err ) {
109
        this.#routingRequestStarted = false;
110
        if ( err instanceof Error ) {
111
            console.error ( err );
112
            theErrorsUI.showError ( err.message );
113
        }
114
        else {
115
            theErrorsUI.showError ( 'A network error occurs when calling the provider' );
116
        }
117
    }
118
119
    /**
120
    Success handler for the startRouting method
121
    */
122
123
    #onRoutingOk ( ) {
124
125
        this.#routingRequestStarted = false;
126
        this.#computeRouteDistances ( theTravelNotesData.travel.editedRoute );
127
128
        // Placing the waypoints on the itinerary
129
        if ( 'circle' !== theTravelNotesData.travel.editedRoute.itinerary.transitMode ) {
130
            const wayPointsIterator = theTravelNotesData.travel.editedRoute.wayPoints.iterator;
131
            while ( ! wayPointsIterator.done ) {
132
                if ( wayPointsIterator.first ) {
133
                    wayPointsIterator.value.latLng =
134
                        theTravelNotesData.travel.editedRoute.itinerary.itineraryPoints.first.latLng;
135
                }
136
                else if ( wayPointsIterator.last ) {
137
                    wayPointsIterator.value.latLng =
138
                        theTravelNotesData.travel.editedRoute.itinerary.itineraryPoints.last.latLng;
139
                }
140
                else {
141
                    wayPointsIterator.value.latLng = theGeometry.getClosestLatLngDistance (
142
                        theTravelNotesData.travel.editedRoute,
143
                        wayPointsIterator.value.latLng
144
                    ).latLng;
145
                }
146
            }
147
        }
148
149
        // the position of the notes linked to the route is recomputed
150
        const notesIterator = theTravelNotesData.travel.editedRoute.notes.iterator;
151
        while ( ! notesIterator.done ) {
152
            const latLngDistance = theGeometry.getClosestLatLngDistance (
153
                theTravelNotesData.travel.editedRoute,
154
                notesIterator.value.latLng
155
            );
156
            notesIterator.value.latLng = latLngDistance.latLng;
157
            notesIterator.value.distance = latLngDistance.distance;
158
        }
159
160
        theRouteEditor.chainRoutes ( );
161
162
        // and the notes sorted
163
        theTravelNotesData.travel.editedRoute.notes.sort (
164
            ( first, second ) => first.distance - second.distance
165
        );
166
167
        if ( this.#zoomToRouteAfterRouting ) {
168
            new Zoomer ( ).zoomToRoute ( theTravelNotesData.travel.editedRoute.objId );
169
        }
170
171
        theProfileDialogsManager.createProfile ( theTravelNotesData.travel.editedRoute );
172
173
        theEventDispatcher.dispatch (
174
            'routeupdated',
175
            {
176
                removedRouteObjId : theTravelNotesData.travel.editedRoute.objId,
177
                addedRouteObjId : theTravelNotesData.travel.editedRoute.objId
178
            }
179
        );
180
181
        theEventDispatcher.dispatch ( 'updateroadbook' );
182
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
183
    }
184
185
    /**
186
    The constructor
187
    */
188
189
    constructor ( ) {
190
        Object.freeze ( this );
191
    }
192
193
    /**
194
    This method start the routing for the edited route.
195
    */
196
197
    startRouting ( ) {
198
        if (
199
            ( ! this.#routingRequestStarted )
200
            &&
201
            theTravelNotesData.travel.editedRoute.haveValidWayPoints ( )
202
        ) {
203
            this.#zoomToRouteAfterRouting = ZERO === theTravelNotesData.travel.editedRoute.itinerary.itineraryPoints.length;
204
            this.#routingRequestStarted = true;
205
            const routeProvider = theTravelNotesData.providers.get ( theTravelNotesData.routing.provider.toLowerCase ( ) );
206
            theTravelNotesData.travel.editedRoute.itinerary.provider = routeProvider.name;
207
            theTravelNotesData.travel.editedRoute.itinerary.transitMode = theTravelNotesData.routing.transitMode;
208
            routeProvider.getPromiseRoute ( theTravelNotesData.travel.editedRoute )
209
                .then ( ( ) => this.#onRoutingOk ( ) )
210
                .catch ( ( ) => this.#onRoutingError ( ) );
211
        }
212
    }
213
}
214
215
/* ------------------------------------------------------------------------------------------------------------------------- */
216
/**
217
The one and only one instance of Router class
218
@type {Router}
219
*/
220
/* ------------------------------------------------------------------------------------------------------------------------- */
221
222
const theRouter = new Router ( );
223
224
export default theRouter;
225
226
/* --- End of file --------------------------------------------------------------------------------------------------------- */
227