File : core/RouteEditor.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 theTranslator from './uiLib/Translator.js';
26
import theApiKeysManager from './ApiKeysManager.js';
27
import theTravelNotesData from '../data/TravelNotesData.js';
28
import theErrorsUI from '../uis/errorsUI/ErrorsUI.js';
29
import theDataSearchEngine from '../data/DataSearchEngine.js';
30
import Route from '../data/Route.js';
31
import GpxFactory from './lib/GpxFactory.js';
32
import RoutePropertiesDialog from '../dialogs/routePropertiesDialog/RoutePropertiesDialog.js';
33
import PrintRouteMapDialog from '../dialogs/printRouteMapDialog/PrintRouteMapDialog.js';
34
import theEventDispatcher from './lib/EventDispatcher.js';
35
import theProfileDialogsManager from './ProfileDialogsManager.js';
36
import RoutePrinter from '../printRoute/RoutePrinter.js';
37
38
import { ROUTE_EDITION_STATUS, DISTANCE, INVALID_OBJ_ID } from '../main/Constants.js';
39
import TempWayPointMarkerMouseOutEL from './mapEditor/TempWayPointMarkerEL/TempWayPointMarkerMouseOutEL.js';
40
41
/* ------------------------------------------------------------------------------------------------------------------------- */
42
/**
43
This class contains methods fot Routes creation or modifications
44
See theRouteEditor for the one and only one instance of this class
45
*/
46
/* ------------------------------------------------------------------------------------------------------------------------- */
47
48
class RouteEditor {
49
50
    /**
51
    The constructor
52
    */
53
54
    constructor ( ) {
55
        Object.freeze ( this );
56
    }
57
58
    /**
59
    This method add a route to the Travel and, if no other route is beind edited,
60
    start the edition of this new route
61
    */
62
63
    addRoute ( ) {
64
        const route = new Route ( );
65
        theTravelNotesData.travel.routes.add ( route );
66
        this.chainRoutes ( );
67
        if ( ROUTE_EDITION_STATUS.editedChanged === theTravelNotesData.travel.editedRoute.editionStatus ) {
68
            theEventDispatcher.dispatch ( 'updatetravelproperties' );
69
            theEventDispatcher.dispatch ( 'updateroadbook' );
70
        }
71
        else {
72
            this.editRoute ( route.objId );
73
        }
74
    }
75
76
    /**
77
    This method start the edition of a route
78
    @param {Number} routeObjId The objId of the route to edit.
79
    */
80
81
    editRoute ( routeObjId ) {
82
83
        // We verify that the provider  for this route is available
84
        const initialRoute = theDataSearchEngine.getRoute ( routeObjId );
85
        const providerName = initialRoute.itinerary.provider;
86
        const provider = theTravelNotesData.providers.get ( providerName.toLowerCase ( ) );
87
        if (
88
            providerName
89
            &&
90
            ( '' !== providerName )
91
            &&
92
            (
93
                ( ! provider )
94
                ||
95
                ( provider.providerKeyNeeded && ! theApiKeysManager.hasKey ( providerName ) )
96
            )
97
        ) {
98
            theErrorsUI.showError (
99
                theTranslator.getText (
100
                    'RouteEditor - Not possible to edit a route created with this provider',
101
                    { provider : providerName }
102
                )
103
            );
104
            return;
105
        }
106
107
        if ( INVALID_OBJ_ID !== theTravelNotesData.editedRouteObjId ) {
108
109
            // the current edited route is not changed (we have verified in the RouteContextMenu ). Cleaning the editors
110
            this.cancelEdition ( );
111
        }
112
113
        // Provider and transit mode are changed in the itinerary editor
114
        if ( providerName && '' !== providerName ) {
115
            theEventDispatcher.dispatch ( 'setprovider', { provider : providerName } );
116
        }
117
        const transitMode = initialRoute.itinerary.transitMode;
118
        if ( transitMode && '' !== transitMode ) {
119
            theEventDispatcher.dispatch ( 'settransitmode', { transitMode : transitMode } );
120
        }
121
122
        // The edited route is pushed in the editors
123
        theTravelNotesData.travel.editedRoute = new Route ( );
124
        initialRoute.editionStatus = ROUTE_EDITION_STATUS.editedNoChange;
125
126
        // Route is cloned, so we can have a cancel button in the editor
127
        theTravelNotesData.travel.editedRoute.jsonObject = initialRoute.jsonObject;
128
        theTravelNotesData.editedRouteObjId = initialRoute.objId;
129
        theTravelNotesData.travel.editedRoute.hidden = false;
130
        initialRoute.hidden = false;
131
        theProfileDialogsManager.updateProfile (
132
            theTravelNotesData.editedRouteObjId,
133
            theTravelNotesData.travel.editedRoute
134
        );
135
        this.chainRoutes ( );
136
        theEventDispatcher.dispatch (
137
            'routeupdated',
138
            {
139
                removedRouteObjId : initialRoute.objId,
140
                addedRouteObjId : theTravelNotesData.travel.editedRoute.objId
141
            }
142
        );
143
144
        theEventDispatcher.dispatch ( 'updateroadbook' );
145
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
146
    }
147
148
    /**
149
    This method removes a route from the travel
150
    @param {Number} routeObjId The objId of the Route to remove.
151
    */
152
153
    removeRoute ( routeObjId ) {
154
        let routeToDeleteObjId = routeObjId;
155
        if (
156
            (
157
                routeToDeleteObjId === theTravelNotesData.editedRouteObjId
158
                ||
159
                routeToDeleteObjId === theTravelNotesData.travel.editedRoute.objId
160
            )
161
        ) {
162
            routeToDeleteObjId = theTravelNotesData.editedRouteObjId;
163
            this.cancelEdition ( );
164
        }
165
166
        theEventDispatcher.dispatch (
167
            'routeupdated',
168
            {
169
                removedRouteObjId : routeToDeleteObjId,
170
                addedRouteObjId : INVALID_OBJ_ID
171
            }
172
        );
173
174
        theTravelNotesData.travel.routes.remove ( routeToDeleteObjId );
175
        theProfileDialogsManager.deleteProfile ( routeToDeleteObjId );
176
        this.chainRoutes ( );
177
178
        theEventDispatcher.dispatch ( 'updateroadbook' );
179
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
180
    }
181
182
    /**
183
    This method save the route to a gpx file
184
    @param {Number} routeObjId The objId of the Route to save.
185
    */
186
187
    saveGpx ( routeObjId ) {
188
        new GpxFactory ( ).routeToGpx ( routeObjId );
189
    }
190
191
    /**
192
    This method recompute the distances for all the chained routes and their notes
193
    */
194
195
    chainRoutes ( ) {
196
        const routesIterator = theTravelNotesData.travel.routes.iterator;
197
        let chainedDistance = DISTANCE.defaultValue;
198
        while ( ! routesIterator.done ) {
199
            if ( routesIterator.value.chain ) {
200
                routesIterator.value.chainedDistance = chainedDistance;
201
                chainedDistance += routesIterator.value.distance;
202
            }
203
            else {
204
                routesIterator.value.chainedDistance = DISTANCE.defaultValue;
205
            }
206
            const notesIterator = routesIterator.value.notes.iterator;
207
            while ( ! notesIterator.done ) {
208
                notesIterator.value.chainedDistance = routesIterator.value.chainedDistance;
209
            }
210
            if ( routesIterator.value.objId === theTravelNotesData.editedRouteObjId ) {
211
                theTravelNotesData.travel.editedRoute.chainedDistance =
212
                    theTravelNotesData.travel.editedRoute.chain
213
                        ?
214
                        routesIterator.value.chainedDistance
215
                        :
216
                        DISTANCE.defaultValue;
217
                const editedRouteNotesIterator = theTravelNotesData.travel.editedRoute.notes.iterator;
218
                while ( ! editedRouteNotesIterator.done ) {
219
                    editedRouteNotesIterator.value.chainedDistance = theTravelNotesData.travel.editedRoute.chainedDistance;
220
                }
221
            }
222
        }
223
    }
224
225
    /**
226
    This method save the edited route
227
    */
228
229
    saveEdition ( ) {
230
231
        // the edited route is cloned
232
        const clonedRoute = new Route ( );
233
        clonedRoute.jsonObject = theTravelNotesData.travel.editedRoute.jsonObject;
234
235
        // and the initial route replaced with the clone
236
        theTravelNotesData.travel.routes.replace ( theTravelNotesData.editedRouteObjId, clonedRoute );
237
        theTravelNotesData.editedRouteObjId = clonedRoute.objId;
238
239
        // cleaning editor
240
        this.cancelEdition ( );
241
    }
242
243
    /**
244
    This method cancel the route edition
245
    */
246
247
    cancelEdition ( ) {
248
249
        // Removing temp way point if any mainly for touch devices)
250
        TempWayPointMarkerMouseOutEL.handleEvent ( );
251
252
        // !!! order is important!!!
253
        const editedRoute = theDataSearchEngine.getRoute ( theTravelNotesData.editedRouteObjId );
254
        editedRoute.editionStatus = ROUTE_EDITION_STATUS.notEdited;
255
256
        theProfileDialogsManager.updateProfile (
257
            theTravelNotesData.travel.editedRoute.objId,
258
            editedRoute
259
        );
260
261
        theEventDispatcher.dispatch (
262
            'routeupdated',
263
            {
264
                removedRouteObjId : theTravelNotesData.travel.editedRoute.objId,
265
                addedRouteObjId : theTravelNotesData.editedRouteObjId
266
            }
267
        );
268
269
        theTravelNotesData.editedRouteObjId = INVALID_OBJ_ID;
270
        theTravelNotesData.travel.editedRoute = new Route ( );
271
        this.chainRoutes ( );
272
273
        theEventDispatcher.dispatch ( 'updateroadbook' );
274
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
275
    }
276
277
    /**
278
    This method show the RoutePropertiesDialog
279
    @param {Number} routeObjId The objId of the Route for witch the properties must be edited
280
    */
281
282
    routeProperties ( routeObjId ) {
283
        const route = theDataSearchEngine.getRoute ( routeObjId );
284
        new RoutePropertiesDialog ( route )
285
            .show ( )
286
            .then (
287
                ( ) => {
288
                    this.chainRoutes ( );
289
                    if ( route.haveValidWayPoints ( ) ) {
290
                        theEventDispatcher.dispatch (
291
                            'routepropertiesupdated',
292
                            {
293
                                routeObjId : route.objId
294
                            }
295
                        );
296
                    }
297
                    theEventDispatcher.dispatch ( 'updateroadbook' );
298
                    theEventDispatcher.dispatch ( 'updatetravelproperties' );
299
                    theEventDispatcher.dispatch ( 'updateprofilename', { routeObjId : routeObjId } );
300
                }
301
            )
302
            .catch (
303
                err => {
304
                    if ( err instanceof Error ) {
305
                        console.error ( err );
306
                    }
307
                }
308
            );
309
    }
310
311
    /**
312
    This method show the PrintRouteMapDialog and then print the maps
313
    @param {Number} routeObjId The objId of the Route for witch the maps must be printed
314
    */
315
316
    printRouteMap ( routeObjId ) {
317
        new PrintRouteMapDialog ( )
318
            .show ( )
319
            .then ( printRouteMapOptions => new RoutePrinter ( ).print ( printRouteMapOptions, routeObjId ) )
320
            .catch (
321
                err => {
322
                    if ( err instanceof Error ) {
323
                        console.error ( err );
324
                    }
325
                }
326
            );
327
    }
328
329
    /**
330
    This method show a route on the map
331
    @param {Number} routeObjId The objId of the Route to show
332
    */
333
334
    showRoute ( routeObjId ) {
335
        theDataSearchEngine.getRoute ( routeObjId ).hidden = false;
336
        theEventDispatcher.dispatch (
337
            'routeupdated',
338
            {
339
                removedRouteObjId : INVALID_OBJ_ID,
340
                addedRouteObjId : routeObjId
341
            }
342
        );
343
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
344
    }
345
346
    /**
347
    This method hide a route on the map
348
    @param {Number} routeObjId The objId of the Route to show
349
    */
350
351
    hideRoute ( routeObjId ) {
352
        theDataSearchEngine.getRoute ( routeObjId ).hidden = true;
353
        theEventDispatcher.dispatch (
354
            'routeupdated',
355
            {
356
                removedRouteObjId : routeObjId,
357
                addedRouteObjId : INVALID_OBJ_ID
358
            }
359
        );
360
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
361
    }
362
363
    /**
364
    This method shows all the routes on the map
365
    */
366
367
    showRoutes ( ) {
368
        const routesIterator = theTravelNotesData.travel.routes.iterator;
369
        while ( ! routesIterator.done ) {
370
            if ( routesIterator.value.hidden ) {
371
                routesIterator.value.hidden = false;
372
                theEventDispatcher.dispatch (
373
                    'routeupdated',
374
                    {
375
                        removedRouteObjId : INVALID_OBJ_ID,
376
                        addedRouteObjId : routesIterator.value.objId
377
                    }
378
                );
379
            }
380
        }
381
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
382
    }
383
384
    /**
385
    This method hide all the routes on the map
386
    */
387
388
    hideRoutes ( ) {
389
        const routesIterator = theTravelNotesData.travel.routes.iterator;
390
        while ( ! routesIterator.done ) {
391
            if (
392
                ! routesIterator.value.hidden
393
                &&
394
                routesIterator.value.objId !== theTravelNotesData.editedRouteObjId
395
            ) {
396
                routesIterator.value.hidden = true;
397
                theEventDispatcher.dispatch (
398
                    'routeupdated',
399
                    {
400
                        removedRouteObjId : routesIterator.value.objId,
401
                        addedRouteObjId : INVALID_OBJ_ID
402
                    }
403
                );
404
            }
405
        }
406
        theEventDispatcher.dispatch ( 'updatetravelproperties' );
407
    }
408
}
409
410
/* ------------------------------------------------------------------------------------------------------------------------- */
411
/**
412
The one and only one instance of RouteEditor class
413
@type {RouteEditor}
414
*/
415
/* ------------------------------------------------------------------------------------------------------------------------- */
416
417
const theRouteEditor = new RouteEditor ( );
418
419
export default theRouteEditor;
420
421
/* --- End of file --------------------------------------------------------------------------------------------------------- */
422