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 |