@fleetbase/fleetops-engine 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addon/components/context-panel.hbs +5 -0
- package/addon/components/context-panel.js +6 -0
- package/addon/components/driver-form-panel.hbs +22 -22
- package/addon/components/driver-form-panel.js +28 -3
- package/addon/components/driver-panel.hbs +8 -8
- package/addon/components/driver-panel.js +23 -6
- package/addon/components/fleet-vehicle-listing.hbs +1 -1
- package/addon/components/live-map.hbs +20 -9
- package/addon/components/live-map.js +1045 -354
- package/addon/components/settings-window.hbs +1 -1
- package/addon/components/vehicle-form-panel.hbs +24 -24
- package/addon/components/vehicle-form-panel.js +26 -3
- package/addon/components/vehicle-panel.hbs +8 -9
- package/addon/components/vehicle-panel.js +21 -4
- package/addon/controllers/operations/orders/index/new.js +3 -5
- package/addon/controllers/operations/orders/index/view.js +5 -7
- package/addon/controllers/operations/orders/index.js +15 -2
- package/addon/engine.js +6 -0
- package/addon/routes/operations/orders/index/view.js +4 -2
- package/addon/routes/operations/orders/index.js +5 -1
- package/addon/services/context-panel.js +106 -0
- package/addon/services/leaflet-contextmenu-manager.js +311 -0
- package/addon/services/leaflet-map-manager.js +61 -0
- package/addon/services/service-areas.js +242 -32
- package/addon/templates/application.hbs +2 -1
- package/addon/utils/find-leaflet-layer.js +13 -0
- package/addon/utils/fly-to-leaflet-layer.js +25 -0
- package/addon/utils/get-leaflet-layer-by-id.js +12 -0
- package/addon/utils/layer-can-bind-contextmenu.js +3 -0
- package/app/components/context-panel.js +1 -0
- package/app/services/context-panel.js +1 -0
- package/app/services/leaflet-contextmenu-manager.js +1 -0
- package/app/services/leaflet-map-manager.js +1 -0
- package/app/utils/find-leaflet-layer.js +1 -0
- package/app/utils/fly-to-leaflet-layer.js +1 -0
- package/app/utils/get-leaflet-layer-by-id.js +1 -0
- package/app/utils/layer-can-bind-contextmenu.js +1 -0
- package/composer.json +11 -1
- package/extension.json +1 -1
- package/package.json +2 -2
- package/server/src/Http/Controllers/Internal/v1/LiveController.php +7 -4
- package/server/src/Http/Filter/VehicleFilter.php +7 -0
- package/server/src/Http/Resources/v1/Vehicle.php +51 -39
- package/addon/services/map-manager.js +0 -3
- package/app/services/map-manager.js +0 -1
|
@@ -1,67 +1,227 @@
|
|
|
1
1
|
import Component from '@glimmer/component';
|
|
2
2
|
import { tracked } from '@glimmer/tracking';
|
|
3
3
|
import { inject as service } from '@ember/service';
|
|
4
|
-
import { action,
|
|
4
|
+
import { action, set } from '@ember/object';
|
|
5
5
|
import { isArray } from '@ember/array';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { dasherize, camelize } from '@ember/string';
|
|
7
|
+
import { singularize } from 'ember-inflector';
|
|
8
8
|
import { alias } from '@ember/object/computed';
|
|
9
|
-
import { guidFor } from '@ember/object/internals';
|
|
10
9
|
import { later } from '@ember/runloop';
|
|
11
10
|
import { allSettled } from 'rsvp';
|
|
11
|
+
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
|
|
12
12
|
|
|
13
13
|
const DEFAULT_LATITUDE = 1.369;
|
|
14
14
|
const DEFAULT_LONGITUDE = 103.8864;
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Component which displays live activity.
|
|
18
|
+
*
|
|
19
|
+
* @class
|
|
20
|
+
*/
|
|
16
21
|
export default class LiveMapComponent extends Component {
|
|
22
|
+
/**
|
|
23
|
+
* Inject the `store` service.
|
|
24
|
+
*
|
|
25
|
+
* @memberof LiveMapComponent
|
|
26
|
+
*/
|
|
17
27
|
@service store;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Inject the `fetch` service.
|
|
31
|
+
*
|
|
32
|
+
* @memberof LiveMapComponent
|
|
33
|
+
*/
|
|
18
34
|
@service fetch;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Inject the `socket` service.
|
|
38
|
+
*
|
|
39
|
+
* @memberof LiveMapComponent
|
|
40
|
+
*/
|
|
19
41
|
@service socket;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Inject the `currentUser` service.
|
|
45
|
+
*
|
|
46
|
+
* @memberof LiveMapComponent
|
|
47
|
+
*/
|
|
20
48
|
@service currentUser;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Inject the `notifications` service.
|
|
52
|
+
*
|
|
53
|
+
* @memberof LiveMapComponent
|
|
54
|
+
*/
|
|
21
55
|
@service notifications;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Inject the `serviceAreas` service.
|
|
59
|
+
*
|
|
60
|
+
* @memberof LiveMapComponent
|
|
61
|
+
*/
|
|
22
62
|
@service serviceAreas;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Inject the `appCache` service.
|
|
66
|
+
*
|
|
67
|
+
* @memberof LiveMapComponent
|
|
68
|
+
*/
|
|
23
69
|
@service appCache;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Inject the `universe` service.
|
|
73
|
+
*
|
|
74
|
+
* @memberof LiveMapComponent
|
|
75
|
+
*/
|
|
24
76
|
@service universe;
|
|
25
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Inject the `crud` service.
|
|
80
|
+
*
|
|
81
|
+
* @memberof LiveMapComponent
|
|
82
|
+
*/
|
|
83
|
+
@service crud;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Inject the `contextPanel` service.
|
|
87
|
+
*
|
|
88
|
+
* @memberof LiveMapComponent
|
|
89
|
+
*/
|
|
90
|
+
@service contextPanel;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Inject the `leafletMapManager` service.
|
|
94
|
+
*
|
|
95
|
+
* @memberof LiveMapComponent
|
|
96
|
+
*/
|
|
97
|
+
@service leafletMapManager;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Inject the `leafletContextmenuManager` service.
|
|
101
|
+
*
|
|
102
|
+
* @memberof LiveMapComponent
|
|
103
|
+
*/
|
|
104
|
+
@service leafletContextmenuManager;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* An array of routes.
|
|
108
|
+
* @type {Array}
|
|
109
|
+
*/
|
|
26
110
|
@tracked routes = [];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* An array of drivers.
|
|
114
|
+
* @type {Array}
|
|
115
|
+
*/
|
|
27
116
|
@tracked drivers = [];
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* An array of vehicles.
|
|
120
|
+
* @type {Array}
|
|
121
|
+
*/
|
|
122
|
+
@tracked vehicles = [];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* An array of places.
|
|
126
|
+
* @type {Array}
|
|
127
|
+
*/
|
|
28
128
|
@tracked places = [];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* An array of channels.
|
|
132
|
+
* @type {Array}
|
|
133
|
+
*/
|
|
29
134
|
@tracked channels = [];
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Indicates if data is loading.
|
|
138
|
+
* @type {boolean}
|
|
139
|
+
*/
|
|
30
140
|
@tracked isLoading = true;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Indicates if the component is ready.
|
|
144
|
+
* @type {boolean}
|
|
145
|
+
*/
|
|
31
146
|
@tracked isReady = false;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@tracked
|
|
38
|
-
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Controls for visibility.
|
|
150
|
+
* @type {Object}
|
|
151
|
+
*/
|
|
152
|
+
@tracked visibilityControls = {};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* An array of active service areas.
|
|
156
|
+
* @type {Array}
|
|
157
|
+
*/
|
|
39
158
|
@tracked activeServiceAreas = [];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* An array of editable map layers.
|
|
162
|
+
* @type {Array}
|
|
163
|
+
*/
|
|
40
164
|
@tracked editableLayers = [];
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The Leaflet map instance.
|
|
168
|
+
* @type {Object}
|
|
169
|
+
*/
|
|
41
170
|
@tracked leafletMap;
|
|
42
|
-
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The map's zoom level.
|
|
174
|
+
* @type {number}
|
|
175
|
+
*/
|
|
176
|
+
@tracked zoom = 12;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The feature group for drawing on the map.
|
|
180
|
+
* @type {Object}
|
|
181
|
+
*/
|
|
43
182
|
@tracked drawFeatureGroup;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The draw control for the map.
|
|
186
|
+
* @type {Object}
|
|
187
|
+
*/
|
|
44
188
|
@tracked drawControl;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The URL for the map's tile source.
|
|
192
|
+
* @type {string}
|
|
193
|
+
*/
|
|
194
|
+
@tracked tileSourceUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The latitude for the map view.
|
|
198
|
+
* @type {number}
|
|
199
|
+
*/
|
|
45
200
|
@tracked latitude = DEFAULT_LATITUDE;
|
|
46
|
-
@tracked longitude = DEFAULT_LONGITUDE;
|
|
47
|
-
@tracked skipSetCoordinates = false;
|
|
48
|
-
@tracked mapId = guidFor(this);
|
|
49
|
-
@alias('currentUser.latitude') userLatitude;
|
|
50
|
-
@alias('currentUser.longitude') userLongitude;
|
|
51
201
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
202
|
+
/**
|
|
203
|
+
* The longitude for the map view.
|
|
204
|
+
* @type {number}
|
|
205
|
+
*/
|
|
206
|
+
@tracked longitude = DEFAULT_LONGITUDE;
|
|
55
207
|
|
|
56
|
-
|
|
57
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Indicates if coordinate setting should be skipped.
|
|
210
|
+
* @type {boolean}
|
|
211
|
+
*/
|
|
212
|
+
@tracked skipSetCoordinates = false;
|
|
58
213
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
214
|
+
/**
|
|
215
|
+
* The user's latitude from the currentUser.
|
|
216
|
+
* @type {number}
|
|
217
|
+
*/
|
|
218
|
+
@alias('currentUser.latitude') userLatitude;
|
|
62
219
|
|
|
63
|
-
|
|
64
|
-
|
|
220
|
+
/**
|
|
221
|
+
* The user's longitude from the currentUser.
|
|
222
|
+
* @type {number}
|
|
223
|
+
*/
|
|
224
|
+
@alias('currentUser.longitude') userLongitude;
|
|
65
225
|
|
|
66
226
|
/**
|
|
67
227
|
* Creates an instance of LiveMapComponent.
|
|
@@ -69,26 +229,85 @@ export default class LiveMapComponent extends Component {
|
|
|
69
229
|
*/
|
|
70
230
|
constructor() {
|
|
71
231
|
super(...arguments);
|
|
232
|
+
this.skipSetCoordinates = getWithDefault(this.args, 'skipSetCoordinates', false);
|
|
233
|
+
this.zoom = getWithDefault(this.args, 'zoom', 12);
|
|
234
|
+
this.tileSourceUrl = getWithDefault(this.args, 'tileSourceUrl', 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png');
|
|
72
235
|
|
|
73
|
-
|
|
236
|
+
if (this.args.darkMode === true) {
|
|
237
|
+
this.tileSourceUrl = 'https://{s}.tile.jawg.io/jawg-matrix/{z}/{x}/{y}{r}.png?access-token=';
|
|
238
|
+
}
|
|
74
239
|
}
|
|
75
240
|
|
|
76
241
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* ----------------------------------------------------------------------------
|
|
242
|
+
* Initializes the LiveMapComponent by triggering events, setting initial coordinates,
|
|
243
|
+
* and loading required live data.
|
|
80
244
|
*
|
|
81
|
-
*
|
|
245
|
+
* @memberof LiveMapComponent
|
|
246
|
+
* @action
|
|
247
|
+
* @function
|
|
82
248
|
*/
|
|
83
|
-
@action
|
|
249
|
+
@action setupComponent() {
|
|
84
250
|
// trigger that initial coordinates have been set
|
|
85
|
-
this.universe.trigger('livemap.loaded', this);
|
|
251
|
+
this.universe.trigger('fleetops.livemap.loaded', this);
|
|
252
|
+
|
|
253
|
+
// set initial coordinates
|
|
254
|
+
this.setInitialCoordinates();
|
|
255
|
+
|
|
256
|
+
// load required live data
|
|
257
|
+
this.fetchLiveData('routes');
|
|
258
|
+
this.fetchLiveData('vehicles', {
|
|
259
|
+
onLoaded: (vehicles) => {
|
|
260
|
+
this.watchMovingObjects('vehicles', vehicles);
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
this.fetchLiveData('drivers', {
|
|
264
|
+
onLoaded: (drivers) => {
|
|
265
|
+
this.watchMovingObjects('drivers', drivers);
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
this.fetchLiveData('places');
|
|
269
|
+
this.listen();
|
|
270
|
+
this.ready();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Marks the LiveMapComponent as ready by setting the "isReady" property and triggering
|
|
275
|
+
* the "onReady" action and a "fleetops.livemap.ready" event.
|
|
276
|
+
*
|
|
277
|
+
* @memberof LiveMapComponent
|
|
278
|
+
* @function
|
|
279
|
+
*/
|
|
280
|
+
ready() {
|
|
281
|
+
this.isReady = true;
|
|
282
|
+
this.triggerAction('onReady');
|
|
283
|
+
this.universe.trigger('fleetops.livemap.ready', this);
|
|
284
|
+
}
|
|
86
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Sets the initial coordinates for the LiveMapComponent.
|
|
288
|
+
*
|
|
289
|
+
* This function checks if initial coordinates are available in the appCache, and if not,
|
|
290
|
+
* it fetches the coordinates using the "getInitialCoordinates" function. It sets the
|
|
291
|
+
* latitude and longitude properties and triggers an event to notify that coordinates
|
|
292
|
+
* have been set.
|
|
293
|
+
*
|
|
294
|
+
* @memberof LiveMapComponent
|
|
295
|
+
* @async
|
|
296
|
+
* @function
|
|
297
|
+
* @returns {Promise<[number, number] | null>} An array containing the latitude and longitude
|
|
298
|
+
* if available, or null if the function is skipped.
|
|
299
|
+
*/
|
|
300
|
+
async setInitialCoordinates() {
|
|
87
301
|
if (this.skipSetCoordinates === false) {
|
|
88
302
|
if (this.appCache.has(['map_latitude', 'map_longitude'])) {
|
|
89
303
|
this.latitude = this.appCache.get('map_latitude');
|
|
90
304
|
this.longitude = this.appCache.get('map_longitude');
|
|
91
305
|
this.isReady = true;
|
|
306
|
+
|
|
307
|
+
// trigger that initial coordinates is set to livemap component
|
|
308
|
+
this.universe.trigger('fleetops.livemap.has_coordinates', { latitude: this.latitude, longitude: this.longitude });
|
|
309
|
+
|
|
310
|
+
return [this.latitude, this.longitude];
|
|
92
311
|
}
|
|
93
312
|
|
|
94
313
|
const { latitude, longitude } = await this.getInitialCoordinates();
|
|
@@ -100,39 +319,49 @@ export default class LiveMapComponent extends Component {
|
|
|
100
319
|
|
|
101
320
|
this.latitude = latitude;
|
|
102
321
|
this.longitude = longitude;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// trigger that initial coordinates have been set
|
|
106
|
-
this.universe.trigger('livemap.has_coordinates', { latitude: this.latitude, longitude: this.longitude });
|
|
107
|
-
|
|
108
|
-
this.routes = await this.fetchActiveRoutes();
|
|
109
|
-
this.drivers = await this.fetchActiveDrivers();
|
|
110
|
-
this.places = await this.fetchActivePlaces();
|
|
111
|
-
this.serviceAreaRecords = await this.fetchServiceAreas();
|
|
112
|
-
this.isReady = true;
|
|
322
|
+
this.isReady = true;
|
|
113
323
|
|
|
114
|
-
|
|
115
|
-
|
|
324
|
+
// trigger that initial coordinates is set to livemap component
|
|
325
|
+
this.universe.trigger('fleetops.livemap.has_coordinates', { latitude: this.latitude, longitude: this.longitude });
|
|
116
326
|
|
|
117
|
-
|
|
118
|
-
this.args.onReady(this);
|
|
327
|
+
return [this.latitude, this.longitude];
|
|
119
328
|
}
|
|
120
329
|
|
|
121
|
-
|
|
122
|
-
this.universe.trigger('livemap.ready', this);
|
|
330
|
+
return null;
|
|
123
331
|
}
|
|
124
332
|
|
|
125
|
-
|
|
333
|
+
/**
|
|
334
|
+
* Sets up the LiveMap component and the Leaflet map instance.
|
|
335
|
+
*
|
|
336
|
+
* This function initializes the LiveMap component, associates it with the Leaflet map instance,
|
|
337
|
+
* triggers the "fleetops.livemap.leaflet_ready" event, and performs additional setup tasks like
|
|
338
|
+
* configuring context menus, hiding draw controls, and associating the map with the "serviceAreas"
|
|
339
|
+
* service. It also triggers the "onLoad" action with the provided event and target.
|
|
340
|
+
*
|
|
341
|
+
* @action
|
|
342
|
+
* @function
|
|
343
|
+
* @param {Event} event - The event object.
|
|
344
|
+
*/
|
|
345
|
+
@action setupMap(event) {
|
|
126
346
|
const { target } = event;
|
|
127
347
|
|
|
128
348
|
// set liveMapComponent component to instance
|
|
129
|
-
set(
|
|
349
|
+
set(target, 'liveMap', this);
|
|
130
350
|
|
|
131
351
|
// set map instance
|
|
132
352
|
this.leafletMap = target;
|
|
133
353
|
|
|
354
|
+
// trigger liveMap ready through universe
|
|
355
|
+
this.universe.trigger('fleetops.livemap.leaflet_ready', event, target);
|
|
356
|
+
|
|
357
|
+
// make fleetops map globally available on the window
|
|
358
|
+
window.FleetOpsLeafletMap = target;
|
|
359
|
+
|
|
360
|
+
// store this component to universe
|
|
361
|
+
this.universe.set('FleetOpsLiveMap', this);
|
|
362
|
+
|
|
134
363
|
// setup context menu
|
|
135
|
-
this.
|
|
364
|
+
this.createMapContextMenu(target);
|
|
136
365
|
|
|
137
366
|
// hide draw controls by default
|
|
138
367
|
this.hideDrawControls();
|
|
@@ -140,98 +369,269 @@ export default class LiveMapComponent extends Component {
|
|
|
140
369
|
// set instance to service areas service
|
|
141
370
|
this.serviceAreas.setMapInstance(target);
|
|
142
371
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
372
|
+
// trigger map loaded event
|
|
373
|
+
this.triggerAction('onLoad', ...arguments);
|
|
146
374
|
}
|
|
147
375
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
376
|
+
/**
|
|
377
|
+
* Invokes an action by name on the current component and its arguments (if defined).
|
|
378
|
+
*
|
|
379
|
+
* This function checks if an action with the specified name exists on the current component.
|
|
380
|
+
* If found, it invokes the action with the provided parameters. It also checks the component's
|
|
381
|
+
* arguments for the action and invokes it if defined.
|
|
382
|
+
*
|
|
383
|
+
* @action
|
|
384
|
+
* @function
|
|
385
|
+
* @param {string} actionName - The name of the action to trigger.
|
|
386
|
+
* @param {...any} params - Optional parameters to pass to the action.
|
|
387
|
+
*/
|
|
388
|
+
@action triggerAction(actionName, ...params) {
|
|
389
|
+
if (typeof this[actionName] === 'function') {
|
|
390
|
+
this[actionName](...params);
|
|
151
391
|
}
|
|
152
392
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
map?.contextmenu?.removeAllItems();
|
|
393
|
+
if (typeof this.args[actionName] === 'function') {
|
|
394
|
+
this.args[actionName](...params);
|
|
156
395
|
}
|
|
396
|
+
}
|
|
157
397
|
|
|
158
|
-
|
|
159
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Fetches live data from the specified path and updates the component state accordingly.
|
|
400
|
+
*
|
|
401
|
+
* @memberof LiveMapComponent
|
|
402
|
+
* @function
|
|
403
|
+
* @param {string} path - The path to fetch live data from.
|
|
404
|
+
* @param {Object} [options={}] - Optional configuration options.
|
|
405
|
+
* @param {Object} [options.params={}] - Additional parameters to include in the request.
|
|
406
|
+
* @param {Function} [options.onLoaded] - A callback function to execute when the data is loaded.
|
|
407
|
+
* @returns {Promise} A promise that resolves with the fetched data.
|
|
408
|
+
*/
|
|
409
|
+
fetchLiveData(path, options = {}) {
|
|
410
|
+
this.isLoading = true;
|
|
160
411
|
|
|
161
|
-
|
|
412
|
+
const internalName = camelize(path);
|
|
413
|
+
const callbackFnName = `on${internalName}Loaded`;
|
|
414
|
+
const params = getWithDefault(options, 'params', {});
|
|
162
415
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
416
|
+
return this.fetch
|
|
417
|
+
.get(`fleet-ops/live/${path}`, params, { normalizeToEmberData: true, normalizeModelType: singularize(internalName) })
|
|
418
|
+
.then((data) => {
|
|
419
|
+
this.triggerAction(callbackFnName);
|
|
420
|
+
this.createVisibilityControl(internalName);
|
|
421
|
+
this[internalName] = data;
|
|
166
422
|
|
|
167
|
-
|
|
168
|
-
|
|
423
|
+
if (typeof options.onLoaded === 'function') {
|
|
424
|
+
options.onLoaded(data);
|
|
425
|
+
}
|
|
169
426
|
|
|
170
|
-
|
|
171
|
-
|
|
427
|
+
return data;
|
|
428
|
+
})
|
|
429
|
+
.finally(() => {
|
|
430
|
+
this.isLoading = false;
|
|
431
|
+
});
|
|
432
|
+
}
|
|
172
433
|
|
|
173
|
-
|
|
174
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Creates or updates a visibility control for a specific element by name.
|
|
436
|
+
*
|
|
437
|
+
* @function
|
|
438
|
+
* @param {string} name - The name or identifier for the visibility control.
|
|
439
|
+
* @param {boolean} [visible=true] - A boolean value indicating whether the element is initially visible (default is true).
|
|
440
|
+
*/
|
|
441
|
+
createVisibilityControl(name, visible = true) {
|
|
442
|
+
this.visibilityControls = {
|
|
443
|
+
...this.visibilityControls,
|
|
444
|
+
[name]: visible,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
175
447
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Hide all visibility controls associated with the current instance.
|
|
450
|
+
*/
|
|
451
|
+
hideAll() {
|
|
452
|
+
const controls = Object.keys(this.visibilityControls);
|
|
179
453
|
|
|
180
|
-
|
|
181
|
-
|
|
454
|
+
for (let i = 0; i < controls.length; i++) {
|
|
455
|
+
const control = controls.objectAt(i);
|
|
456
|
+
this.hide(control);
|
|
182
457
|
}
|
|
183
458
|
}
|
|
184
459
|
|
|
185
|
-
|
|
186
|
-
|
|
460
|
+
/**
|
|
461
|
+
* Show all visibility controls associated with the current instance.
|
|
462
|
+
*/
|
|
463
|
+
showAll() {
|
|
464
|
+
const controls = Object.keys(this.visibilityControls);
|
|
187
465
|
|
|
188
|
-
|
|
189
|
-
|
|
466
|
+
for (let i = 0; i < controls.length; i++) {
|
|
467
|
+
const control = controls.objectAt(i);
|
|
468
|
+
this.show(control);
|
|
190
469
|
}
|
|
191
470
|
}
|
|
192
471
|
|
|
193
|
-
|
|
194
|
-
|
|
472
|
+
/**
|
|
473
|
+
* Hides a specific element by name using a visibility control.
|
|
474
|
+
*
|
|
475
|
+
* @function
|
|
476
|
+
* @param {string} name - The name or identifier of the element to hide.
|
|
477
|
+
*/
|
|
478
|
+
hide(name) {
|
|
479
|
+
if (isArray(name)) {
|
|
480
|
+
return name.forEach(this.hide);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
this.createVisibilityControl(name, false);
|
|
484
|
+
}
|
|
195
485
|
|
|
196
|
-
|
|
197
|
-
|
|
486
|
+
/**
|
|
487
|
+
* Shows a specific element by name using a visibility control.
|
|
488
|
+
*
|
|
489
|
+
* @function
|
|
490
|
+
* @param {string} name - The name or identifier of the element to show.
|
|
491
|
+
*/
|
|
492
|
+
show(name) {
|
|
493
|
+
if (isArray(name)) {
|
|
494
|
+
return name.forEach(this.show);
|
|
198
495
|
}
|
|
496
|
+
|
|
497
|
+
this.createVisibilityControl(name, true);
|
|
199
498
|
}
|
|
200
499
|
|
|
201
|
-
|
|
202
|
-
|
|
500
|
+
/**
|
|
501
|
+
* Check if a specific element or feature is currently visible based on its name.
|
|
502
|
+
*
|
|
503
|
+
* @param {string} name - The name of the element or feature to check visibility for.
|
|
504
|
+
* @returns {boolean} Returns `true` if the element or feature is currently visible, `false` otherwise.
|
|
505
|
+
*/
|
|
506
|
+
isVisible(name) {
|
|
507
|
+
return this.visibilityControls[name] === true;
|
|
203
508
|
}
|
|
204
509
|
|
|
205
|
-
|
|
206
|
-
|
|
510
|
+
/**
|
|
511
|
+
* Toggles the context menu item for enabling/disabling draw controls.
|
|
512
|
+
*
|
|
513
|
+
* @param {Object} [options] - Optional settings for the context menu item.
|
|
514
|
+
* @param {string} [options.onText='Hide draw controls...'] - Text to display when enabling draw controls.
|
|
515
|
+
* @param {string} [options.offText='Enable draw controls...'] - Text to display when disabling draw controls.
|
|
516
|
+
* @param {string} [options.callback=function] - Callback function to trigger after toggle.
|
|
517
|
+
*/
|
|
518
|
+
toggleDrawControlContextMenuItem(options = {}) {
|
|
519
|
+
const toggle = !this.isVisible('drawControls');
|
|
520
|
+
|
|
521
|
+
this.leafletContextmenuManager.toggleContextMenuItem('map', 'draw controls', {
|
|
522
|
+
onText: 'Hide draw controls...',
|
|
523
|
+
offText: 'Enable draw controls...',
|
|
524
|
+
toggle,
|
|
525
|
+
callback: (isToggled) => {
|
|
526
|
+
if (isToggled) {
|
|
527
|
+
this.showDrawControls();
|
|
528
|
+
} else {
|
|
529
|
+
this.hideDrawControls();
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
...options,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Removes a specific service area from the context menu.
|
|
538
|
+
*
|
|
539
|
+
* @param {Object} serviceArea - The service area to be removed from the context menu.
|
|
540
|
+
*/
|
|
541
|
+
removeServiceAreaFromContextMenu(serviceArea) {
|
|
542
|
+
this.leafletContextmenuManager.removeItemFromContextMenu('map', `Focus Service Area: ${serviceArea.name}`);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Get a Leaflet layer from the map based on its ID.
|
|
547
|
+
*
|
|
548
|
+
* @param {string} id - The ID of the Leaflet layer to retrieve.
|
|
549
|
+
* @returns {Object|null} The found Leaflet layer or `null` if not found.
|
|
550
|
+
*/
|
|
551
|
+
getLeafletLayerById(id) {
|
|
552
|
+
return this.leafletMapManager.getLeafletLayerById(this.leafletMap, id);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Find a specific Leaflet layer on the map using a callback function.
|
|
557
|
+
*
|
|
558
|
+
* @param {Function} callback - A callback function that defines the condition for finding the layer.
|
|
559
|
+
* @returns {Object|null} The found Leaflet layer or `null` if not found.
|
|
560
|
+
*/
|
|
561
|
+
findLeafletLayer(callback) {
|
|
562
|
+
return this.leafletMapManager.findLeafletLayer(this.leafletMap, callback);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Find an editable layer in the collection by its record ID.
|
|
567
|
+
*
|
|
568
|
+
* @param {Object} record - The record with the ID used for lookup.
|
|
569
|
+
* @returns {Layer|null} The found editable layer, or null if not found.
|
|
570
|
+
* @memberof LiveMapComponent
|
|
571
|
+
*/
|
|
572
|
+
getLeafletLayerByRecordId(record) {
|
|
573
|
+
const id = getWithDefault(record, 'id', record);
|
|
574
|
+
let targetLayer = null;
|
|
575
|
+
|
|
576
|
+
this.leafletMap.eachLayer((layer) => {
|
|
577
|
+
// Check if the layer has an ID property
|
|
578
|
+
if (layer.record_id === id) {
|
|
579
|
+
targetLayer = layer;
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
return targetLayer;
|
|
207
584
|
}
|
|
208
585
|
|
|
209
586
|
/**
|
|
210
|
-
*
|
|
211
|
-
* TRACKED LAYER UTILITIES
|
|
212
|
-
* ----------------------------------------------------------------------------
|
|
587
|
+
* Push an editable layer to the collection of editable layers.
|
|
213
588
|
*
|
|
214
|
-
*
|
|
589
|
+
* @param {Layer} layer - The layer to be added to the collection.
|
|
590
|
+
* @memberof LiveMapComponent
|
|
215
591
|
*/
|
|
216
|
-
|
|
592
|
+
pushEditableLayer(layer) {
|
|
217
593
|
if (!this.editableLayers.includes(layer)) {
|
|
218
594
|
this.editableLayers.pushObject(layer);
|
|
219
595
|
}
|
|
220
596
|
}
|
|
221
597
|
|
|
222
|
-
|
|
223
|
-
|
|
598
|
+
/**
|
|
599
|
+
* Remove an editable layer from the collection by its record ID.
|
|
600
|
+
*
|
|
601
|
+
* @param {Object} record - The record with the ID used for removal.
|
|
602
|
+
* @memberof LiveMapComponent
|
|
603
|
+
*/
|
|
604
|
+
removeEditableLayerByRecordId(record) {
|
|
605
|
+
const id = getWithDefault(record, 'id', record);
|
|
606
|
+
const index = this.editableLayers.findIndex((layer) => layer.record_id === id);
|
|
224
607
|
const layer = this.editableLayers.objectAt(index);
|
|
225
608
|
|
|
226
|
-
this.drawFeatureGroup
|
|
227
|
-
|
|
609
|
+
if (this.drawFeatureGroup) {
|
|
610
|
+
this.drawFeatureGroup.addLayer(layer);
|
|
611
|
+
this.editableLayers.removeAt(index);
|
|
612
|
+
}
|
|
228
613
|
}
|
|
229
614
|
|
|
230
|
-
|
|
231
|
-
|
|
615
|
+
/**
|
|
616
|
+
* Find an editable layer in the collection by its record ID.
|
|
617
|
+
*
|
|
618
|
+
* @param {Object} record - The record with the ID used for lookup.
|
|
619
|
+
* @returns {Layer|null} The found editable layer, or null if not found.
|
|
620
|
+
* @memberof LiveMapComponent
|
|
621
|
+
*/
|
|
622
|
+
findEditableLayerByRecordId(record) {
|
|
623
|
+
const id = getWithDefault(record, 'id', record);
|
|
624
|
+
return this.editableLayers.find((layer) => layer.record_id === id);
|
|
232
625
|
}
|
|
233
626
|
|
|
234
|
-
|
|
627
|
+
/**
|
|
628
|
+
* Peek a record for a given layer by its record ID and type.
|
|
629
|
+
*
|
|
630
|
+
* @param {Layer} layer - The layer associated with a record.
|
|
631
|
+
* @returns {Object|null} The peeked record, or null if not found.
|
|
632
|
+
* @memberof LiveMapComponent
|
|
633
|
+
*/
|
|
634
|
+
peekRecordForLayer(layer) {
|
|
235
635
|
if (layer.record_id && layer.record_type) {
|
|
236
636
|
return this.store.peekRecord(dasherize(layer.record_type), layer.record_id);
|
|
237
637
|
}
|
|
@@ -240,27 +640,22 @@ export default class LiveMapComponent extends Component {
|
|
|
240
640
|
}
|
|
241
641
|
|
|
242
642
|
/**
|
|
243
|
-
*
|
|
244
|
-
* LAYER EVENTS
|
|
245
|
-
* ----------------------------------------------------------------------------
|
|
643
|
+
* Handle the 'drawstop' event.
|
|
246
644
|
*
|
|
247
|
-
*
|
|
645
|
+
* @param {Event} event - The 'drawstop' event object.
|
|
646
|
+
* @param {Layer} layer - The layer associated with the event.
|
|
647
|
+
* @memberof LiveMapComponent
|
|
248
648
|
*/
|
|
249
|
-
|
|
250
|
-
@action onAction(actionName, ...params) {
|
|
251
|
-
if (typeof this[actionName] === 'function') {
|
|
252
|
-
this[actionName](...params);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (typeof this.args[actionName] === 'function') {
|
|
256
|
-
this.args[actionName](...params);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
649
|
@action onDrawDrawstop(event, layer) {
|
|
261
650
|
this.serviceAreas.createGenericLayer(event, layer);
|
|
262
651
|
}
|
|
263
652
|
|
|
653
|
+
/**
|
|
654
|
+
* Handle the 'deleted' event for drawn elements.
|
|
655
|
+
*
|
|
656
|
+
* @param {Event} event - The 'deleted' event object.
|
|
657
|
+
* @memberof LiveMapComponent
|
|
658
|
+
*/
|
|
264
659
|
@action onDrawDeleted(event) {
|
|
265
660
|
/** @var {L.LayerGroup} layers */
|
|
266
661
|
const { layers } = event;
|
|
@@ -278,6 +673,12 @@ export default class LiveMapComponent extends Component {
|
|
|
278
673
|
});
|
|
279
674
|
}
|
|
280
675
|
|
|
676
|
+
/**
|
|
677
|
+
* Handle the 'edited' event for drawn elements.
|
|
678
|
+
*
|
|
679
|
+
* @param {Event} event - The 'edited' event object.
|
|
680
|
+
* @memberof LiveMapComponent
|
|
681
|
+
*/
|
|
281
682
|
@action onDrawEdited(event) {
|
|
282
683
|
/** @var {L.LayerGroup} layers */
|
|
283
684
|
const { layers } = event;
|
|
@@ -301,89 +702,219 @@ export default class LiveMapComponent extends Component {
|
|
|
301
702
|
allSettled(requests);
|
|
302
703
|
}
|
|
303
704
|
|
|
705
|
+
/**
|
|
706
|
+
* Handle the addition of a service area layer.
|
|
707
|
+
*
|
|
708
|
+
* @param {ServiceAreaModel} serviceArea - The service area object.
|
|
709
|
+
* @param {Event} event - The event object associated with the addition.
|
|
710
|
+
* @memberof LiveMapComponent
|
|
711
|
+
*/
|
|
304
712
|
@action onServiceAreaLayerAdded(serviceArea, event) {
|
|
305
713
|
const { target } = event;
|
|
306
714
|
|
|
307
715
|
set(target, 'record_id', serviceArea.id);
|
|
308
716
|
set(target, 'record_type', 'service-area');
|
|
309
717
|
|
|
310
|
-
//
|
|
311
|
-
|
|
718
|
+
// set the layer instance to the serviceArea model
|
|
719
|
+
set(serviceArea, '_layer', target);
|
|
720
|
+
|
|
721
|
+
if (this.drawFeatureGroup) {
|
|
722
|
+
// add to draw feature group
|
|
723
|
+
this.drawFeatureGroup.addLayer(target);
|
|
724
|
+
}
|
|
312
725
|
|
|
313
726
|
// this.flyToBoundsOnly(target);
|
|
314
|
-
this.
|
|
727
|
+
this.createServiceAreaContextMenu(serviceArea, target);
|
|
315
728
|
this.pushEditableLayer(target);
|
|
316
729
|
}
|
|
317
730
|
|
|
731
|
+
/**
|
|
732
|
+
* Handle the addition of a zone layer.
|
|
733
|
+
*
|
|
734
|
+
* @param {ZoneModel} zone - The zone object.
|
|
735
|
+
* @param {Event} event - The event object associated with the addition.
|
|
736
|
+
* @memberof LiveMapComponent
|
|
737
|
+
*/
|
|
318
738
|
@action onZoneLayerAdd(zone, event) {
|
|
319
739
|
const { target } = event;
|
|
320
740
|
|
|
321
741
|
set(target, 'record_id', zone.id);
|
|
322
742
|
set(target, 'record_type', 'zone');
|
|
323
743
|
|
|
324
|
-
//
|
|
325
|
-
|
|
744
|
+
// set the layer instance to the zone model
|
|
745
|
+
set(zone, '_layer', target);
|
|
326
746
|
|
|
327
|
-
this.
|
|
747
|
+
if (this.drawFeatureGroup) {
|
|
748
|
+
// add to draw feature group
|
|
749
|
+
this.drawFeatureGroup.addLayer(target);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
this.createZoneContextMenu(zone, target);
|
|
328
753
|
this.pushEditableLayer(target);
|
|
329
754
|
}
|
|
330
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Handle the creation of the draw feature group.
|
|
758
|
+
*
|
|
759
|
+
* @param {DrawFeatureGroup} drawFeatureGroup - The draw feature group instance.
|
|
760
|
+
* @memberof LiveMapComponent
|
|
761
|
+
*/
|
|
331
762
|
@action onDrawFeatureGroupCreated(drawFeatureGroup) {
|
|
332
763
|
this.drawFeatureGroup = drawFeatureGroup;
|
|
333
764
|
}
|
|
334
765
|
|
|
335
|
-
|
|
336
|
-
|
|
766
|
+
/**
|
|
767
|
+
* Handle the addition of a driver marker.
|
|
768
|
+
*
|
|
769
|
+
* @param {DriverModel} driver - The driver object.
|
|
770
|
+
* @param {Event} event - The event object associated with the addition.
|
|
771
|
+
* @memberof LiveMapComponent
|
|
772
|
+
*/
|
|
773
|
+
@action onDriverAdded(driver, event) {
|
|
774
|
+
const { target } = event;
|
|
775
|
+
|
|
776
|
+
set(target, 'record_id', driver.id);
|
|
777
|
+
set(target, 'record_type', 'driver');
|
|
778
|
+
|
|
779
|
+
// set the marker instance to the driver model
|
|
780
|
+
set(driver, '_marker', target);
|
|
781
|
+
|
|
782
|
+
this.createDriverContextMenu(driver, target);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Handle the click event of a driver marker.
|
|
787
|
+
*
|
|
788
|
+
* @param {DriverModel} driver - The driver object.
|
|
789
|
+
* @param {Event} event - The event object associated with the addition.
|
|
790
|
+
* @memberof LiveMapComponent
|
|
791
|
+
*/
|
|
792
|
+
@action onDriverClicked(driver) {
|
|
793
|
+
this.contextPanel.focus(driver);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Handle the addition of a vehicle marker.
|
|
798
|
+
*
|
|
799
|
+
* @param {VehicleModel} vehicle - The vehicle object.
|
|
800
|
+
* @param {Event} event - The event object associated with the addition.
|
|
801
|
+
* @memberof LiveMapComponent
|
|
802
|
+
*/
|
|
803
|
+
@action onVehicleAdded(vehicle, event) {
|
|
804
|
+
const { target } = event;
|
|
805
|
+
|
|
806
|
+
set(target, 'record_id', vehicle.id);
|
|
807
|
+
set(target, 'record_type', 'vehicle');
|
|
808
|
+
|
|
809
|
+
// set the marker instance to the vehicle model
|
|
810
|
+
set(vehicle, '_marker', target);
|
|
811
|
+
|
|
812
|
+
this.createVehicleContextMenu(vehicle, target);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Handle the click event of a vehicle marker.
|
|
817
|
+
*
|
|
818
|
+
* @param {VehicleModel} vehicle - The vehicle object.
|
|
819
|
+
* @param {Event} event - The event object associated with the addition.
|
|
820
|
+
* @memberof LiveMapComponent
|
|
821
|
+
*/
|
|
822
|
+
@action onVehicleClicked(vehicle) {
|
|
823
|
+
this.contextPanel.focus(vehicle);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Handle the creation of the draw control.
|
|
828
|
+
*
|
|
829
|
+
* @param {DrawControl} drawControl - The draw control instance.
|
|
830
|
+
* @memberof LiveMapComponent
|
|
831
|
+
*/
|
|
832
|
+
@action onDrawControlCreated(drawControl) {
|
|
833
|
+
this.drawControl = drawControl;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Hide the draw controls on the map.
|
|
838
|
+
*
|
|
839
|
+
* @param {Object} [options={}] - Additional options.
|
|
840
|
+
* @param {string|boolean} [options.text] - Text to set for the menu item or `true` to set the default text.
|
|
841
|
+
* @param {function} [options.callback] - A callback function to execute.
|
|
842
|
+
* @memberof LiveMapComponent
|
|
843
|
+
*/
|
|
844
|
+
@action hideDrawControls(options = {}) {
|
|
845
|
+
this.hide('drawControls');
|
|
337
846
|
|
|
338
|
-
|
|
339
|
-
|
|
847
|
+
const text = getWithDefault(options, 'text');
|
|
848
|
+
const callback = getWithDefault(options, 'callback');
|
|
340
849
|
|
|
341
|
-
|
|
850
|
+
if (typeof callback === 'function') {
|
|
851
|
+
callback();
|
|
852
|
+
}
|
|
342
853
|
|
|
343
|
-
|
|
344
|
-
|
|
854
|
+
if (typeof text === 'string') {
|
|
855
|
+
this.leafletContextmenuManager.changeMenuItemText('map', 'draw controls', text);
|
|
856
|
+
}
|
|
345
857
|
|
|
346
|
-
|
|
347
|
-
|
|
858
|
+
if (text === true) {
|
|
859
|
+
this.leafletContextmenuManager.changeMenuItemText('map', 'draw controls', 'Enable draw controls...');
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (this.drawControl) {
|
|
863
|
+
this.leafletMap.removeControl(this.drawControl);
|
|
864
|
+
}
|
|
348
865
|
}
|
|
349
866
|
|
|
350
867
|
/**
|
|
351
|
-
*
|
|
352
|
-
* LEAFLET UTILITIES
|
|
353
|
-
* ----------------------------------------------------------------------------
|
|
354
|
-
*
|
|
355
|
-
* Functions are used to help or utilize on Leaflet Layers/ Controls.
|
|
868
|
+
* Show the draw controls on the map.
|
|
356
869
|
*
|
|
870
|
+
* @param {Object} [options={}] - Additional options.
|
|
871
|
+
* @param {string|boolean} [options.text] - Text to set for the menu item or `true` to set the default text.
|
|
872
|
+
* @param {function} [options.callback] - A callback function to execute.
|
|
873
|
+
* @memberof LiveMapComponent
|
|
357
874
|
*/
|
|
358
|
-
@action
|
|
359
|
-
|
|
360
|
-
|
|
875
|
+
@action showDrawControls(options = {}) {
|
|
876
|
+
this.show('drawControls');
|
|
877
|
+
|
|
878
|
+
const text = getWithDefault(options, 'text');
|
|
879
|
+
const callback = getWithDefault(options, 'callback');
|
|
880
|
+
|
|
881
|
+
if (typeof callback === 'function') {
|
|
882
|
+
callback();
|
|
361
883
|
}
|
|
362
884
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
885
|
+
if (typeof text === 'string') {
|
|
886
|
+
this.leafletContextmenuManager.changeMenuItemText('map', 'draw controls', text);
|
|
887
|
+
}
|
|
367
888
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
889
|
+
if (text === true) {
|
|
890
|
+
this.leafletContextmenuManager.changeMenuItemText('map', 'draw controls', 'Hide draw controls...');
|
|
891
|
+
}
|
|
372
892
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
this.toggleDrawControlContextMenuItem();
|
|
893
|
+
if (this.drawControl) {
|
|
894
|
+
this.leafletMap.addControl(this.drawControl);
|
|
895
|
+
}
|
|
377
896
|
}
|
|
378
897
|
|
|
379
|
-
|
|
380
|
-
|
|
898
|
+
/**
|
|
899
|
+
* Focus on a layer associated with a record.
|
|
900
|
+
*
|
|
901
|
+
* @param {Object} record - The record to focus on.
|
|
902
|
+
* @memberof LiveMapComponent
|
|
903
|
+
*/
|
|
904
|
+
@action focusLayerBoundsByRecord(record) {
|
|
905
|
+
const layer = this.getLeafletLayerByRecordId(record);
|
|
381
906
|
|
|
382
907
|
if (layer) {
|
|
383
908
|
this.flyToBoundsOnly(layer);
|
|
384
909
|
}
|
|
385
910
|
}
|
|
386
911
|
|
|
912
|
+
/**
|
|
913
|
+
* Fly to a service area layer on the map.
|
|
914
|
+
*
|
|
915
|
+
* @param {ServiceAreaModel} serviceArea - The service area object to fly to.
|
|
916
|
+
* @memberof LiveMapComponent
|
|
917
|
+
*/
|
|
387
918
|
@action flyToServiceArea(serviceArea) {
|
|
388
919
|
const layer = this.findEditableLayerByRecordId(serviceArea);
|
|
389
920
|
|
|
@@ -392,11 +923,12 @@ export default class LiveMapComponent extends Component {
|
|
|
392
923
|
}
|
|
393
924
|
}
|
|
394
925
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
926
|
+
/**
|
|
927
|
+
* Focus on a service area by activating it and then flying to it on the map.
|
|
928
|
+
*
|
|
929
|
+
* @param {ServiceArea} serviceArea - The service area to focus on.
|
|
930
|
+
* @memberof LiveMapComponent
|
|
931
|
+
*/
|
|
400
932
|
@action focusServiceArea(serviceArea) {
|
|
401
933
|
this.activateServiceArea(serviceArea);
|
|
402
934
|
|
|
@@ -409,7 +941,13 @@ export default class LiveMapComponent extends Component {
|
|
|
409
941
|
);
|
|
410
942
|
}
|
|
411
943
|
|
|
412
|
-
|
|
944
|
+
/**
|
|
945
|
+
* Blur all service areas except for those specified in the 'except' array.
|
|
946
|
+
*
|
|
947
|
+
* @param {Array} except - An array of records to exclude from blurring.
|
|
948
|
+
* @memberof LiveMapComponent
|
|
949
|
+
*/
|
|
950
|
+
blurAllServiceAreas(except = []) {
|
|
413
951
|
if (!isArray(except)) {
|
|
414
952
|
except = [];
|
|
415
953
|
}
|
|
@@ -441,7 +979,13 @@ export default class LiveMapComponent extends Component {
|
|
|
441
979
|
}
|
|
442
980
|
}
|
|
443
981
|
|
|
444
|
-
|
|
982
|
+
/**
|
|
983
|
+
* Focus on all service areas except for those specified in the 'except' array by activating them.
|
|
984
|
+
*
|
|
985
|
+
* @param {Array} except - An array of records to exclude from activation.
|
|
986
|
+
* @memberof LiveMapComponent
|
|
987
|
+
*/
|
|
988
|
+
focusAllServiceAreas(except = []) {
|
|
445
989
|
if (!isArray(except)) {
|
|
446
990
|
except = [];
|
|
447
991
|
}
|
|
@@ -463,93 +1007,143 @@ export default class LiveMapComponent extends Component {
|
|
|
463
1007
|
}
|
|
464
1008
|
}
|
|
465
1009
|
|
|
466
|
-
|
|
1010
|
+
/**
|
|
1011
|
+
* Blur a specific service area by removing it from the active service areas.
|
|
1012
|
+
*
|
|
1013
|
+
* @param {ServiceAreaModel} serviceArea - The service area to blur.
|
|
1014
|
+
* @memberof LiveMapComponent
|
|
1015
|
+
*/
|
|
1016
|
+
blurServiceArea(serviceArea) {
|
|
467
1017
|
if (this.activeServiceAreas.includes(serviceArea)) {
|
|
468
1018
|
this.activeServiceAreas.removeObject(serviceArea);
|
|
469
1019
|
}
|
|
470
1020
|
}
|
|
471
1021
|
|
|
472
|
-
|
|
1022
|
+
/**
|
|
1023
|
+
* Activate a service area by adding it to the active service areas.
|
|
1024
|
+
*
|
|
1025
|
+
* @param {ServiceAreaModel} serviceArea - The service area to activate.
|
|
1026
|
+
* @memberof LiveMapComponent
|
|
1027
|
+
*/
|
|
1028
|
+
activateServiceArea(serviceArea) {
|
|
473
1029
|
if (!this.activeServiceAreas.includes(serviceArea)) {
|
|
474
1030
|
this.activeServiceAreas.pushObject(serviceArea);
|
|
475
1031
|
}
|
|
476
1032
|
}
|
|
477
1033
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
@action toggleDrivers() {
|
|
487
|
-
this.isDriversVisible = !this.isDriversVisible;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
@action hidePlaces() {
|
|
491
|
-
this.isPlacesVisible = false;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
@action showPlaces() {
|
|
495
|
-
this.isPlacesVisible = true;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
@action togglePlaces() {
|
|
499
|
-
this.isPlacesVisible = !this.isPlacesVisible;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
@action hideRoutes() {
|
|
503
|
-
this.isRoutesVisible = false;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
@action showRoutes() {
|
|
507
|
-
this.isRoutesVisible = true;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
@action toggleRoutes() {
|
|
511
|
-
this.isRoutesVisible = !this.isRoutesVisible;
|
|
512
|
-
}
|
|
513
|
-
|
|
1034
|
+
/**
|
|
1035
|
+
* Show coordinates information by displaying them as an info notification.
|
|
1036
|
+
*
|
|
1037
|
+
* @param {Event} event - The event containing latitude and longitude information.
|
|
1038
|
+
* @memberof LiveMapComponent
|
|
1039
|
+
*/
|
|
514
1040
|
@action showCoordinates(event) {
|
|
515
1041
|
this.notifications.info(event.latlng);
|
|
516
1042
|
}
|
|
517
1043
|
|
|
1044
|
+
/**
|
|
1045
|
+
* Center the map on a specific location provided in the event.
|
|
1046
|
+
*
|
|
1047
|
+
* @param {Event} event - The event containing the target location (latlng).
|
|
1048
|
+
* @memberof LiveMapComponent
|
|
1049
|
+
*/
|
|
518
1050
|
@action centerMap(event) {
|
|
519
|
-
this.leafletMap
|
|
1051
|
+
this.leafletMap.panTo(event.latlng);
|
|
520
1052
|
}
|
|
521
1053
|
|
|
1054
|
+
/**
|
|
1055
|
+
* Zoom in on the map.
|
|
1056
|
+
*
|
|
1057
|
+
* @memberof LiveMapComponent
|
|
1058
|
+
*/
|
|
522
1059
|
@action zoomIn() {
|
|
523
|
-
this.leafletMap
|
|
1060
|
+
this.leafletMap.zoomIn();
|
|
524
1061
|
}
|
|
525
1062
|
|
|
1063
|
+
/**
|
|
1064
|
+
* Zoom out on the map.
|
|
1065
|
+
*
|
|
1066
|
+
* @memberof LiveMapComponent
|
|
1067
|
+
*/
|
|
526
1068
|
@action zoomOut() {
|
|
527
|
-
this.leafletMap
|
|
1069
|
+
this.leafletMap.zoomOut();
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Set the maximum bounds of the map based on the provided layer's bounds.
|
|
1074
|
+
*
|
|
1075
|
+
* @param {Layer} layer - The layer used to determine the map's maximum bounds.
|
|
1076
|
+
* @memberof LiveMapComponent
|
|
1077
|
+
*/
|
|
1078
|
+
setMaxBoundsFromLayer(layer) {
|
|
1079
|
+
if (layer && typeof layer.getBounds === 'function') {
|
|
1080
|
+
const bounds = layer.getBounds();
|
|
1081
|
+
|
|
1082
|
+
this.leafletMap.flyToBounds(bounds);
|
|
1083
|
+
this.leafletMap.setMaxBounds(bounds);
|
|
1084
|
+
}
|
|
528
1085
|
}
|
|
529
1086
|
|
|
530
|
-
|
|
531
|
-
|
|
1087
|
+
/**
|
|
1088
|
+
* Fly to and focus on a specific layer's bounds on the map.
|
|
1089
|
+
*
|
|
1090
|
+
* @param {Layer} layer - The layer to focus on.
|
|
1091
|
+
* @memberof LiveMapComponent
|
|
1092
|
+
*/
|
|
1093
|
+
flyToBoundsOnly(layer) {
|
|
1094
|
+
if (layer && typeof layer.getBounds === 'function') {
|
|
1095
|
+
const bounds = layer.getBounds();
|
|
532
1096
|
|
|
533
|
-
|
|
534
|
-
|
|
1097
|
+
this.leafletMap.flyToBounds(bounds);
|
|
1098
|
+
}
|
|
535
1099
|
}
|
|
536
1100
|
|
|
537
|
-
|
|
538
|
-
|
|
1101
|
+
/**
|
|
1102
|
+
* Focus on a specific layer and optionally zoom in/out on it.
|
|
1103
|
+
*
|
|
1104
|
+
* @param {Layer} layer - The layer to focus on.
|
|
1105
|
+
* @param {number} zoom - The zoom level for the focus operation.
|
|
1106
|
+
* @param {Object} options - Additional options for the focus operation.
|
|
1107
|
+
* @memberof LiveMapComponent
|
|
1108
|
+
*/
|
|
1109
|
+
@action focusLayer(layer, zoom, options = {}) {
|
|
1110
|
+
this.leafletMapManager.flyToLayer(this.leafletMap, layer, zoom, options);
|
|
539
1111
|
|
|
540
|
-
|
|
1112
|
+
if (typeof options.onAfterFocus === 'function') {
|
|
1113
|
+
options.onAfterFocus(layer);
|
|
1114
|
+
}
|
|
541
1115
|
}
|
|
542
1116
|
|
|
543
1117
|
/**
|
|
544
|
-
*
|
|
545
|
-
* CONTEXT MENU INITIALIZERS
|
|
546
|
-
* ----------------------------------------------------------------------------
|
|
1118
|
+
* Focuses the Leaflet map on a specific layer associated with a record.
|
|
547
1119
|
*
|
|
548
|
-
*
|
|
1120
|
+
* @param {Object} record - The record associated with the target layer.
|
|
1121
|
+
* @param {number} zoom - The desired zoom level for the map.
|
|
1122
|
+
* @param {Object} [options={}] - Additional options for the map focus.
|
|
1123
|
+
* @returns {void}
|
|
549
1124
|
*
|
|
1125
|
+
* @example
|
|
1126
|
+
* focusLayerByRecord(recordData, 12, { animate: true });
|
|
550
1127
|
*/
|
|
1128
|
+
@action focusLayerByRecord(record, zoom, options = {}) {
|
|
1129
|
+
const layer = this.getLeafletLayerByRecordId(record);
|
|
1130
|
+
|
|
1131
|
+
if (layer) {
|
|
1132
|
+
this.focusLayer(layer, zoom, options);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
if (typeof options.onAfterFocusWithRecord === 'function') {
|
|
1136
|
+
options.onAfterFocusWithRecord(record, layer);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
551
1139
|
|
|
552
|
-
|
|
1140
|
+
/**
|
|
1141
|
+
* Create a context menu for the map with various options.
|
|
1142
|
+
*
|
|
1143
|
+
* @param {L.Map} map - The map to which the context menu is attached.
|
|
1144
|
+
* @memberof LiveMapComponent
|
|
1145
|
+
*/
|
|
1146
|
+
@action createMapContextMenu(map) {
|
|
553
1147
|
const contextmenuItems = [
|
|
554
1148
|
{
|
|
555
1149
|
text: 'Show coordinates...',
|
|
@@ -572,8 +1166,8 @@ export default class LiveMapComponent extends Component {
|
|
|
572
1166
|
index: 3,
|
|
573
1167
|
},
|
|
574
1168
|
{
|
|
575
|
-
text: this.
|
|
576
|
-
callback:
|
|
1169
|
+
text: this.isVisible('drawControls') ? `Hide draw controls...` : `Enable draw controls...`,
|
|
1170
|
+
callback: this.toggleDrawControlContextMenuItem.bind(this),
|
|
577
1171
|
index: 4,
|
|
578
1172
|
},
|
|
579
1173
|
{
|
|
@@ -586,67 +1180,159 @@ export default class LiveMapComponent extends Component {
|
|
|
586
1180
|
},
|
|
587
1181
|
];
|
|
588
1182
|
|
|
589
|
-
//
|
|
590
|
-
const serviceAreas = this.serviceAreas.getFromCache()
|
|
1183
|
+
// Add Service Area Context Menu Items
|
|
1184
|
+
const serviceAreas = this.serviceAreas.getFromCache();
|
|
591
1185
|
|
|
592
|
-
if (serviceAreas
|
|
1186
|
+
if (isArray(serviceAreas)) {
|
|
593
1187
|
contextmenuItems.pushObject({
|
|
594
1188
|
separator: true,
|
|
595
1189
|
});
|
|
596
|
-
}
|
|
597
1190
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
1191
|
+
// Add for each Service Area
|
|
1192
|
+
for (let i = 0; i < serviceAreas.length; i++) {
|
|
1193
|
+
const serviceArea = serviceAreas.objectAt(i);
|
|
1194
|
+
const nextIndex = contextmenuItems.length + 2;
|
|
601
1195
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
1196
|
+
contextmenuItems.pushObject({
|
|
1197
|
+
text: `Focus Service Area: ${serviceArea.name}`,
|
|
1198
|
+
callback: () => this.focusServiceArea(serviceArea),
|
|
1199
|
+
index: nextIndex,
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
607
1202
|
}
|
|
608
1203
|
|
|
609
|
-
|
|
1204
|
+
// create contextmenu registry
|
|
1205
|
+
const contextmenuRegistry = this.leafletContextmenuManager.createContextMenu('map', map, contextmenuItems);
|
|
1206
|
+
|
|
1207
|
+
// trigger that contextmenu registry was created
|
|
1208
|
+
this.universe.createRegistryEvent('contextmenu:map', 'created', contextmenuRegistry, this.leafletContextmenuManager);
|
|
610
1209
|
|
|
611
|
-
return
|
|
1210
|
+
return contextmenuRegistry;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Rebuild the context menu for the map.
|
|
1215
|
+
* This function calls the `createMapContextMenu` method.
|
|
1216
|
+
* @memberof LiveMapComponent
|
|
1217
|
+
*/
|
|
1218
|
+
rebuildMapContextMenu() {
|
|
1219
|
+
this.createMapContextMenu(this.leafletMap);
|
|
612
1220
|
}
|
|
613
1221
|
|
|
614
|
-
|
|
1222
|
+
/**
|
|
1223
|
+
* Create a context menu for a driver marker on the map.
|
|
1224
|
+
*
|
|
1225
|
+
* @param {DriverModel} driver - The driver associated with the marker.
|
|
1226
|
+
* @param {L.Layer} layer - The layer representing the driver marker.
|
|
1227
|
+
* @memberof LiveMapComponent
|
|
1228
|
+
*/
|
|
1229
|
+
@action createDriverContextMenu(driver, layer) {
|
|
615
1230
|
const contextmenuItems = [
|
|
616
1231
|
{
|
|
617
1232
|
separator: true,
|
|
618
1233
|
},
|
|
619
1234
|
{
|
|
620
1235
|
text: `View Driver: ${driver.name}`,
|
|
621
|
-
|
|
1236
|
+
callback: () => this.contextPanel.focus(driver),
|
|
622
1237
|
},
|
|
623
1238
|
{
|
|
624
1239
|
text: `Edit Driver: ${driver.name}`,
|
|
625
|
-
|
|
1240
|
+
callback: () => this.contextPanel.focus(driver, 'editing'),
|
|
626
1241
|
},
|
|
627
1242
|
{
|
|
628
1243
|
text: `Delete Driver: ${driver.name}`,
|
|
629
|
-
|
|
1244
|
+
callback: () => this.crud.delete(driver),
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
text: `View Vehicle for: ${driver.name}`,
|
|
1248
|
+
callback: () => this.contextPanel.focus(driver.vehicle),
|
|
1249
|
+
},
|
|
1250
|
+
];
|
|
1251
|
+
|
|
1252
|
+
// append items from universe registry
|
|
1253
|
+
const registeredContextMenuItems = this.universe.getMenuItemsFromRegistry('contextmenu:driver');
|
|
1254
|
+
if (isArray(registeredContextMenuItems)) {
|
|
1255
|
+
contextmenuItems = [
|
|
1256
|
+
...contextmenuItems,
|
|
1257
|
+
...registeredContextMenuItems.map((menuItem) => {
|
|
1258
|
+
return {
|
|
1259
|
+
text: menuItem.title,
|
|
1260
|
+
callback: () => {
|
|
1261
|
+
return menuItem.onClick(driver, layer, menuItem);
|
|
1262
|
+
},
|
|
1263
|
+
};
|
|
1264
|
+
}),
|
|
1265
|
+
];
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
// create contextmenu registry
|
|
1269
|
+
const contextmenuRegistry = this.leafletContextmenuManager.createContextMenu(`driver:${driver.public_id}`, layer, contextmenuItems, { driver });
|
|
1270
|
+
|
|
1271
|
+
// trigger that contextmenu registry was created
|
|
1272
|
+
this.universe.createRegistryEvent('contextmenu:driver', 'created', contextmenuRegistry, this.leafletContextmenuManager);
|
|
1273
|
+
|
|
1274
|
+
return contextmenuRegistry;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
/**
|
|
1278
|
+
* Create a context menu for a vehicle marker on the map.
|
|
1279
|
+
*
|
|
1280
|
+
* @param {Vehicle} vehicle - The vehicle associated with the marker.
|
|
1281
|
+
* @param {Layer} layer - The layer representing the vehicle marker.
|
|
1282
|
+
* @memberof LiveMapComponent
|
|
1283
|
+
*/
|
|
1284
|
+
@action createVehicleContextMenu(vehicle, layer) {
|
|
1285
|
+
let contextmenuItems = [
|
|
1286
|
+
{
|
|
1287
|
+
separator: true,
|
|
630
1288
|
},
|
|
631
1289
|
{
|
|
632
|
-
text: `
|
|
633
|
-
|
|
1290
|
+
text: `View Vehicle: ${vehicle.displayName}`,
|
|
1291
|
+
callback: () => this.contextPanel.focus(vehicle),
|
|
634
1292
|
},
|
|
635
1293
|
{
|
|
636
|
-
text: `
|
|
637
|
-
|
|
1294
|
+
text: `Edit Vehicle: ${vehicle.displayName}`,
|
|
1295
|
+
callback: () => this.contextPanel.focus(vehicle, 'editing'),
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
text: `Delete Vehicle: ${vehicle.displayName}`,
|
|
1299
|
+
callback: () => this.crud.delete(vehicle),
|
|
638
1300
|
},
|
|
639
1301
|
];
|
|
640
1302
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1303
|
+
// append items from universe registry
|
|
1304
|
+
const registeredContextMenuItems = this.universe.getMenuItemsFromRegistry('contextmenu:vehicle');
|
|
1305
|
+
if (isArray(registeredContextMenuItems)) {
|
|
1306
|
+
contextmenuItems = [
|
|
1307
|
+
...contextmenuItems,
|
|
1308
|
+
...registeredContextMenuItems.map((menuItem) => {
|
|
1309
|
+
return {
|
|
1310
|
+
text: menuItem.title,
|
|
1311
|
+
callback: () => {
|
|
1312
|
+
return menuItem.onClick(vehicle, layer, menuItem);
|
|
1313
|
+
},
|
|
1314
|
+
};
|
|
1315
|
+
}),
|
|
1316
|
+
];
|
|
646
1317
|
}
|
|
1318
|
+
|
|
1319
|
+
// create contextmenu registry
|
|
1320
|
+
const contextmenuRegistry = this.leafletContextmenuManager.createContextMenu(`vehicle:${vehicle.public_id}`, layer, contextmenuItems, { vehicle });
|
|
1321
|
+
|
|
1322
|
+
// trigger that contextmenu registry was created
|
|
1323
|
+
this.universe.createRegistryEvent('contextmenu:vehicle', 'created', contextmenuRegistry, this.leafletContextmenuManager);
|
|
1324
|
+
|
|
1325
|
+
return contextmenuRegistry;
|
|
647
1326
|
}
|
|
648
1327
|
|
|
649
|
-
|
|
1328
|
+
/**
|
|
1329
|
+
* Create a context menu for a zone layer on the map.
|
|
1330
|
+
*
|
|
1331
|
+
* @param {ZoneModel} zone - The zone associated with the layer.
|
|
1332
|
+
* @param {Layer} layer - The layer representing the zone.
|
|
1333
|
+
* @memberof LiveMapComponent
|
|
1334
|
+
*/
|
|
1335
|
+
@action createZoneContextMenu(zone, layer) {
|
|
650
1336
|
const contextmenuItems = [
|
|
651
1337
|
{
|
|
652
1338
|
separator: true,
|
|
@@ -670,15 +1356,23 @@ export default class LiveMapComponent extends Component {
|
|
|
670
1356
|
},
|
|
671
1357
|
];
|
|
672
1358
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
1359
|
+
// create contextmenu registry
|
|
1360
|
+
const contextmenuRegistry = this.leafletContextmenuManager.createContextMenu(`zone:${zone.public_id}`, layer, contextmenuItems, { zone });
|
|
1361
|
+
|
|
1362
|
+
// trigger that contextmenu registry was created
|
|
1363
|
+
this.universe.createRegistryEvent('contextmenu:zone', 'created', contextmenuRegistry, this.leafletContextmenuManager);
|
|
1364
|
+
|
|
1365
|
+
return contextmenuRegistry;
|
|
679
1366
|
}
|
|
680
1367
|
|
|
681
|
-
|
|
1368
|
+
/**
|
|
1369
|
+
* Create a context menu for a service area layer on the map.
|
|
1370
|
+
*
|
|
1371
|
+
* @param {ServiceAreaModel} serviceArea - The service area associated with the layer.
|
|
1372
|
+
* @param {Layer} layer - The layer representing the service area.
|
|
1373
|
+
* @memberof LiveMapComponent
|
|
1374
|
+
*/
|
|
1375
|
+
@action createServiceAreaContextMenu(serviceArea, layer) {
|
|
682
1376
|
const contextmenuItems = [
|
|
683
1377
|
{
|
|
684
1378
|
separator: true,
|
|
@@ -704,30 +1398,32 @@ export default class LiveMapComponent extends Component {
|
|
|
704
1398
|
callback: () =>
|
|
705
1399
|
this.serviceAreas.deleteServiceArea(serviceArea, {
|
|
706
1400
|
onFinish: () => {
|
|
707
|
-
this.
|
|
1401
|
+
this.rebuildMapContextMenu();
|
|
708
1402
|
this.removeEditableLayerByRecordId(serviceArea);
|
|
709
1403
|
},
|
|
710
1404
|
}),
|
|
711
1405
|
},
|
|
712
1406
|
];
|
|
713
1407
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
1408
|
+
// create contextmenu registry
|
|
1409
|
+
const contextmenuRegistry = this.leafletContextmenuManager.createContextMenu(`service-area:${serviceArea.public_id}`, layer, contextmenuItems, { serviceArea });
|
|
1410
|
+
|
|
1411
|
+
// trigger that contextmenu registry was created
|
|
1412
|
+
this.universe.createRegistryEvent('contextmenu:service-area', 'created', contextmenuRegistry, this.leafletContextmenuManager);
|
|
1413
|
+
|
|
1414
|
+
return contextmenuRegistry;
|
|
720
1415
|
}
|
|
721
1416
|
|
|
722
1417
|
/**
|
|
723
|
-
*
|
|
724
|
-
* Async/Socket Functions
|
|
725
|
-
* ----------------------------------------------------------------------------
|
|
1418
|
+
* Listens for events on the company channel and logs incoming data.
|
|
726
1419
|
*
|
|
727
|
-
*
|
|
1420
|
+
* This function sets up a WebSocket connection, subscribes to the company-specific channel,
|
|
1421
|
+
* and listens for events. When events are received, it logs them to the console.
|
|
728
1422
|
*
|
|
1423
|
+
* @async
|
|
1424
|
+
* @function
|
|
729
1425
|
*/
|
|
730
|
-
|
|
1426
|
+
async listen() {
|
|
731
1427
|
// setup socket
|
|
732
1428
|
const socket = this.socket.instance();
|
|
733
1429
|
|
|
@@ -751,26 +1447,49 @@ export default class LiveMapComponent extends Component {
|
|
|
751
1447
|
})();
|
|
752
1448
|
}
|
|
753
1449
|
|
|
754
|
-
|
|
755
|
-
|
|
1450
|
+
/**
|
|
1451
|
+
* Watches and manages moving objects (e.g., drivers or vehicles) on the LiveMapComponent.
|
|
1452
|
+
*
|
|
1453
|
+
* This function can be used to watch different types of moving objects by specifying
|
|
1454
|
+
* the 'type' parameter.
|
|
1455
|
+
*
|
|
1456
|
+
* @action
|
|
1457
|
+
* @function
|
|
1458
|
+
* @param {string} objectType - The type of moving object to watch (e.g., 'drivers', 'vehicles').
|
|
1459
|
+
* @param {Array} movingObjects - An array of moving objects to watch.
|
|
1460
|
+
*/
|
|
1461
|
+
watchMovingObjects(objectType, objects = []) {
|
|
1462
|
+
// Setup socket
|
|
756
1463
|
const socket = this.socket.instance();
|
|
757
1464
|
|
|
758
|
-
//
|
|
759
|
-
for (let i = 0; i <
|
|
760
|
-
const
|
|
761
|
-
this.
|
|
1465
|
+
// Listen for moving objects
|
|
1466
|
+
for (let i = 0; i < objects.length; i++) {
|
|
1467
|
+
const movingObject = objects.objectAt(i);
|
|
1468
|
+
this.listenForMovingObject(objectType, movingObject, socket);
|
|
762
1469
|
}
|
|
763
1470
|
}
|
|
764
1471
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1472
|
+
/**
|
|
1473
|
+
* Listens for events related to a specific type of moving object (e.g., driver or vehicle) and manages the associated marker.
|
|
1474
|
+
*
|
|
1475
|
+
* This function subscribes to the channel corresponding to the provided 'objectType' and the specific 'movingObject'
|
|
1476
|
+
* to listen for location-related events. It processes and updates the associated marker when events are received.
|
|
1477
|
+
*
|
|
1478
|
+
* @async
|
|
1479
|
+
* @function
|
|
1480
|
+
* @param {string} objectType - The type of moving object being watched (e.g., 'drivers', 'vehicles').
|
|
1481
|
+
* @param {Object} movingObject - The specific moving object to track.
|
|
1482
|
+
* @param {Socket} socket - The WebSocket instance used for communication.
|
|
1483
|
+
*/
|
|
1484
|
+
async listenForMovingObject(objectType, movingObject, socket) {
|
|
1485
|
+
// Listen on the specific channel
|
|
1486
|
+
const channelId = `${objectType}.${movingObject.id}`;
|
|
768
1487
|
const channel = socket.subscribe(channelId);
|
|
769
1488
|
|
|
770
|
-
//
|
|
1489
|
+
// Track the channel
|
|
771
1490
|
this.channels.pushObject(channel);
|
|
772
1491
|
|
|
773
|
-
//
|
|
1492
|
+
// Listen to the channel for events
|
|
774
1493
|
await channel.listener('subscribe').once();
|
|
775
1494
|
|
|
776
1495
|
// Initialize an empty buffer to store incoming events
|
|
@@ -787,11 +1506,19 @@ export default class LiveMapComponent extends Component {
|
|
|
787
1506
|
// Process sorted events
|
|
788
1507
|
for (const output of eventBuffer) {
|
|
789
1508
|
const { event, data } = output;
|
|
1509
|
+
|
|
1510
|
+
// log incoming event
|
|
790
1511
|
console.log(`${event} - #${data.additionalData.index} (${output.created_at}) [ ${data.location.coordinates.join(' ')} ]`);
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1512
|
+
|
|
1513
|
+
// get movingObject marker
|
|
1514
|
+
const objectMarker = movingObject._layer;
|
|
1515
|
+
|
|
1516
|
+
if (objectMarker) {
|
|
1517
|
+
// Update the object's heading degree
|
|
1518
|
+
objectMarker.setRotationAngle(data.heading);
|
|
1519
|
+
// Move the object's marker to new coordinates
|
|
1520
|
+
objectMarker.slideTo(data.location.coordinates, { duration: 2000 });
|
|
1521
|
+
}
|
|
795
1522
|
}
|
|
796
1523
|
|
|
797
1524
|
// Clear the buffer
|
|
@@ -801,12 +1528,12 @@ export default class LiveMapComponent extends Component {
|
|
|
801
1528
|
// Start a timer to process the buffer at intervals
|
|
802
1529
|
setInterval(processBuffer, bufferTime);
|
|
803
1530
|
|
|
804
|
-
//
|
|
1531
|
+
// Get incoming data and console out
|
|
805
1532
|
(async () => {
|
|
806
1533
|
for await (let output of channel) {
|
|
807
1534
|
const { event } = output;
|
|
808
1535
|
|
|
809
|
-
if (event ===
|
|
1536
|
+
if (event === `${objectType}.location_changed` || event === `${objectType}.simulated_location_changed`) {
|
|
810
1537
|
// Add the incoming event to the buffer
|
|
811
1538
|
eventBuffer.push(output);
|
|
812
1539
|
}
|
|
@@ -814,15 +1541,27 @@ export default class LiveMapComponent extends Component {
|
|
|
814
1541
|
})();
|
|
815
1542
|
}
|
|
816
1543
|
|
|
1544
|
+
/**
|
|
1545
|
+
* Close all socket channels associated subscribed to.
|
|
1546
|
+
* @memberof LiveMapComponent
|
|
1547
|
+
*/
|
|
817
1548
|
@action closeChannels() {
|
|
818
|
-
|
|
819
|
-
|
|
1549
|
+
if (isArray(this.channels)) {
|
|
1550
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1551
|
+
const channel = this.channels.objectAt(i);
|
|
820
1552
|
|
|
821
|
-
|
|
1553
|
+
channel.close();
|
|
1554
|
+
}
|
|
822
1555
|
}
|
|
823
1556
|
}
|
|
824
1557
|
|
|
825
|
-
|
|
1558
|
+
/**
|
|
1559
|
+
* Retrieve the initial coordinates for the map view.
|
|
1560
|
+
*
|
|
1561
|
+
* @returns {Promise} A promise that resolves to an object containing latitude and longitude.
|
|
1562
|
+
* @memberof LiveMapComponent
|
|
1563
|
+
*/
|
|
1564
|
+
getInitialCoordinates() {
|
|
826
1565
|
const initialCoordinates = {
|
|
827
1566
|
latitude: DEFAULT_LATITUDE,
|
|
828
1567
|
longitude: DEFAULT_LONGITUDE,
|
|
@@ -880,73 +1619,13 @@ export default class LiveMapComponent extends Component {
|
|
|
880
1619
|
});
|
|
881
1620
|
}
|
|
882
1621
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
this.isLoading = false;
|
|
891
|
-
|
|
892
|
-
if (typeof this.args.onRoutesLoaded === 'function') {
|
|
893
|
-
this.args.onRoutesLoaded(routes);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
resolve(routes);
|
|
897
|
-
})
|
|
898
|
-
.catch(() => {
|
|
899
|
-
resolve([]);
|
|
900
|
-
});
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
@action fetchActiveDrivers() {
|
|
905
|
-
this.isLoading = true;
|
|
906
|
-
|
|
907
|
-
return new Promise((resolve) => {
|
|
908
|
-
this.fetch
|
|
909
|
-
.get('fleet-ops/live/drivers', {}, { normalizeToEmberData: true, normalizeModelType: 'driver' })
|
|
910
|
-
.then((drivers) => {
|
|
911
|
-
this.isLoading = false;
|
|
912
|
-
|
|
913
|
-
if (typeof this.args.onDriversLoaded === 'function') {
|
|
914
|
-
this.args.onDriversLoaded(drivers);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
resolve(drivers);
|
|
918
|
-
})
|
|
919
|
-
.catch(() => {
|
|
920
|
-
resolve([]);
|
|
921
|
-
});
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
@action fetchActivePlaces() {
|
|
926
|
-
this.isLoading = true;
|
|
927
|
-
|
|
928
|
-
// get the center of map
|
|
929
|
-
const center = this.leafletMap.getCenter();
|
|
930
|
-
|
|
931
|
-
return new Promise((resolve) => {
|
|
932
|
-
this.fetch
|
|
933
|
-
.get('fleet-ops/live/places', { within: center }, { normalizeToEmberData: true, normalizeModelType: 'place' })
|
|
934
|
-
.then((places) => {
|
|
935
|
-
this.isLoading = false;
|
|
936
|
-
|
|
937
|
-
if (typeof this.args.onPlacesLoaded === 'function') {
|
|
938
|
-
this.args.onPlacesLoaded(places);
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
resolve(places);
|
|
942
|
-
})
|
|
943
|
-
.catch(() => {
|
|
944
|
-
resolve([]);
|
|
945
|
-
});
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
@action fetchServiceAreas() {
|
|
1622
|
+
/**
|
|
1623
|
+
* Fetch service areas and cache them if not already cached.
|
|
1624
|
+
*
|
|
1625
|
+
* @returns {Promise} A promise that resolves to an array of service area records.
|
|
1626
|
+
* @memberof LiveMapComponent
|
|
1627
|
+
*/
|
|
1628
|
+
fetchServiceAreas() {
|
|
950
1629
|
this.isLoading = true;
|
|
951
1630
|
|
|
952
1631
|
return new Promise((resolve) => {
|
|
@@ -968,14 +1647,26 @@ export default class LiveMapComponent extends Component {
|
|
|
968
1647
|
});
|
|
969
1648
|
}
|
|
970
1649
|
|
|
971
|
-
|
|
1650
|
+
/**
|
|
1651
|
+
* Get the local latitude for the map view.
|
|
1652
|
+
*
|
|
1653
|
+
* @returns {number} The local latitude.
|
|
1654
|
+
* @memberof LiveMapComponent
|
|
1655
|
+
*/
|
|
1656
|
+
getLocalLatitude() {
|
|
972
1657
|
const whois = this.currentUser.getOption('whois');
|
|
973
1658
|
const latitude = this.appCache.get('map_latitude');
|
|
974
1659
|
|
|
975
1660
|
return latitude || whois?.latitude || DEFAULT_LATITUDE;
|
|
976
1661
|
}
|
|
977
1662
|
|
|
978
|
-
|
|
1663
|
+
/**
|
|
1664
|
+
* Get the local longitude for the map view.
|
|
1665
|
+
*
|
|
1666
|
+
* @returns {number} The local longitude.
|
|
1667
|
+
* @memberof LiveMapComponent
|
|
1668
|
+
*/
|
|
1669
|
+
getLocalLongitude() {
|
|
979
1670
|
const whois = this.currentUser.getOption('whois');
|
|
980
1671
|
const longitude = this.appCache.get('map_longitude');
|
|
981
1672
|
|