@sapui5/sap.ui.vbm 1.129.0 → 1.131.0

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.
@@ -0,0 +1,872 @@
1
+
2
+
3
+ sap.ui.define([
4
+ "../lib/sapvbi",
5
+ "../VBIRenderer",
6
+ "./VectorUtils",
7
+ "./thirdparty/MaplibreStyles",
8
+ "./PayloadGenerator",
9
+ "./thirdparty/maplibregl",
10
+ "./VBITransformer"
11
+ ], function (vb, VBIRenderer, VectorUtils, MaplibreStyles, PayloadGenerator) {
12
+ 'use strict';
13
+
14
+ VBI.MapRenderer = {};
15
+ var map_container = "";
16
+ let isDragging = false;
17
+ let startY;
18
+ let startYpx;
19
+ let mapInitialZoom;
20
+ const scrollline_Length = 94 - 18;
21
+ const DRAG_THRESHOLD = 5;
22
+ let isDrawing = false;
23
+ let isCtrlPressed = false;
24
+ let lassoPoints = [];
25
+ var bounds = [];
26
+ let existingLines = [];
27
+ let predefinedMarkers = [];
28
+ let line = {};
29
+ let allMarkers = [];
30
+ let lineDrag = false;
31
+ let validDrop = false;
32
+ let start = null;
33
+ let current = null;
34
+ let isSelecting = false;
35
+
36
+
37
+ VBI.MapRenderer.setAdapter = (adapter) => {
38
+ this._adapter = adapter;
39
+ }
40
+
41
+ this._payloadGenerator = new PayloadGenerator(this._adapter);
42
+ // Process the GeoJSON spots and its properties
43
+ VBI.MapRenderer._processGeoSpot = (source, data) => {
44
+
45
+ }
46
+
47
+ // Process the GeoJSON routes/links and its properties
48
+ VBI.MapRenderer._processGeoRoutes = (source, data) => {
49
+
50
+ }
51
+
52
+ VBI.MapRenderer.renderMap = () => {
53
+
54
+ let geoJSON = VBI.VBITransformer.getTransformedJSON();
55
+
56
+ map_container = VBIRenderer.getId();
57
+
58
+ var styleSheet = document.createElement("style");
59
+
60
+ styleSheet.textContent = MaplibreStyles.loadStyles();
61
+ document.head.appendChild(styleSheet);
62
+
63
+ const map = VectorUtils.createMap(geoJSON, map_container);
64
+
65
+ class SAPMapNavControl {
66
+ onAdd(map) {
67
+ this.map = map;
68
+ // Create main container
69
+ this._container = document.createElement('div');
70
+ this._container.id = '__xmlview1--vbi-vbi-nav';
71
+ this._container.className = 'vbi-nav my-custom-control';
72
+ this._container.setAttribute('role', 'Navigation');
73
+ this._container.setAttribute('tabindex', '-1');
74
+ this._container.style.opacity = '0.5';
75
+
76
+
77
+ this._container.addEventListener('mouseover', function () {
78
+ this.style.opacity = '1';
79
+ this.style.boxShadow = '0 0 25px rgba(255, 0, 0, 0.8)';
80
+ });
81
+
82
+ this._container.addEventListener('mouseout', function () {
83
+ this.style.opacity = '0.5';
84
+ this.style.boxShadow = 'none';
85
+ });
86
+
87
+ // Create scroll area container
88
+ var scrollArea = document.createElement('div');
89
+ scrollArea.id = '__xmlview1--vbi-vbi-scrollarea';
90
+ scrollArea.className = 'vbi-scrollarea';
91
+ scrollArea.setAttribute('role', 'Slider');
92
+ scrollArea.setAttribute('tabindex', '0');
93
+
94
+ // Create scroll line upper ending div
95
+ var scrollLineUpperEnding = document.createElement('div');
96
+ scrollLineUpperEnding.id = '__xmlview1--vbi-vbi-scrolllineupperending';
97
+ scrollLineUpperEnding.className = 'vbi-scrolllineupperending';
98
+ scrollLineUpperEnding.setAttribute('role', 'Img');
99
+ scrollLineUpperEnding.setAttribute('tabindex', '-1');
100
+ scrollLineUpperEnding.style.cursor = 'pointer';
101
+ scrollLineUpperEnding.style.position = 'absolute';
102
+ scrollLineUpperEnding.top = '20px';
103
+
104
+
105
+ // Create scroll line div
106
+ var scrollLine = document.createElement('div');
107
+ scrollLine.id = '__xmlview1--vbi-vbi-scrollline';
108
+ scrollLine.className = 'vbi-scrollline';
109
+ scrollLine.setAttribute('role', 'Img');
110
+ scrollLine.setAttribute('tabindex', '-1');
111
+
112
+
113
+ // Create scroll line lower ending div
114
+ var scrollLineLowerEnding = document.createElement('div');
115
+ scrollLineLowerEnding.id = '__xmlview1--vbi-vbi-scrolllinelowerending';
116
+ scrollLineLowerEnding.className = 'vbi-scrolllinelowerending';
117
+ scrollLineLowerEnding.setAttribute('role', 'Img');
118
+ scrollLineLowerEnding.setAttribute('tabindex', '-1');
119
+ scrollLineLowerEnding.style.cursor = 'pointer';
120
+ scrollLineLowerEnding.style.position = 'absolute';
121
+ scrollLineLowerEnding.style.top = '100px';
122
+
123
+ // Create scroll point div
124
+ var scrollPoint = document.createElement('div');
125
+ scrollPoint.id = '__xmlview1--vbi-vbi-scrollpoint';
126
+ scrollPoint.className = 'vbi-scrollpoint';
127
+ scrollPoint.setAttribute('role', 'Button');
128
+ scrollPoint.setAttribute('aria-label', 'NAVCTL_TITLE_ZOOM');
129
+ scrollPoint.setAttribute('tabindex', '0');
130
+ scrollPoint.setAttribute('title', 'NAVCTL_TITLE_ZOOM');
131
+ scrollPoint.style.top = '4.43911px';
132
+ startYpx = 4.43911;
133
+
134
+ // Append scroll line elements to scroll area
135
+ scrollArea.appendChild(scrollLineUpperEnding);
136
+ scrollArea.appendChild(scrollLine);
137
+ scrollArea.appendChild(scrollLineLowerEnding);
138
+ scrollArea.appendChild(scrollPoint);
139
+ // Append scroll area to main container
140
+ this._container.appendChild(scrollArea);
141
+
142
+ // Create cursor div
143
+ var cursor = document.createElement('div');
144
+ cursor.id = '__xmlview1--vbi-vbi-cursor';
145
+ cursor.className = 'vbi-cursor';
146
+ cursor.setAttribute('role', 'Presentation');
147
+ cursor.setAttribute('tabindex', '-1');
148
+ // cursor.style.backgroundPosition = '-5px 305px';
149
+
150
+ // Append cursor to main container
151
+ this._container.appendChild(cursor);
152
+
153
+ // Create cursor grip container
154
+ var cursorGrip = document.createElement('div');
155
+ cursorGrip.id = '__xmlview1--vbi-vbi-cursor-grip';
156
+ cursorGrip.className = 'vbi-cursor-grip';
157
+ cursorGrip.setAttribute('role', 'Img');
158
+ cursorGrip.setAttribute('tabindex', '-1');
159
+
160
+ // Create cursor middle container
161
+ var cursorMiddle = document.createElement('div');
162
+ cursorMiddle.id = '__xmlview1--vbi-vbi-cursor-middle';
163
+ cursorMiddle.className = 'vbi-cursor-middle';
164
+ cursorMiddle.setAttribute('role', 'Img');
165
+ cursorMiddle.setAttribute('tabindex', '0');
166
+
167
+ // Create cursor left div
168
+ var cursorLeft = document.createElement('div');
169
+ cursorLeft.id = '__xmlview1--vbi-vbi-cursor-left';
170
+ cursorLeft.className = 'vbi-cursor-left';
171
+ cursorLeft.setAttribute('role', 'Button');
172
+ cursorLeft.setAttribute('aria-label', 'NAVCTL_TITLE_MOVE_LEFT');
173
+ cursorLeft.setAttribute('tabindex', '2');
174
+ cursorLeft.setAttribute('title', 'NAVCTL_TITLE_MOVE_LEFT');
175
+ // cursorLeft.style.backgroundPosition = '-134px 228px';
176
+
177
+ // Create cursor right div
178
+ var cursorRight = document.createElement('div');
179
+ cursorRight.id = '__xmlview1--vbi-vbi-cursor-right';
180
+ cursorRight.className = 'vbi-cursor-right';
181
+ cursorRight.setAttribute('role', 'Button');
182
+ cursorRight.setAttribute('aria-label', 'NAVCTL_TITLE_MOVE_RIGHT');
183
+ cursorRight.setAttribute('tabindex', '4');
184
+ cursorRight.setAttribute('title', 'NAVCTL_TITLE_MOVE_RIGHT');
185
+
186
+ // Create cursor top div
187
+ var cursorTop = document.createElement('div');
188
+ cursorTop.id = '__xmlview1--vbi-vbi-cursor-top';
189
+ cursorTop.className = 'vbi-cursor-top';
190
+ cursorTop.setAttribute('role', 'Button');
191
+ cursorTop.setAttribute('aria-label', 'NAVCTL_TITLE_MOVE_UP');
192
+ cursorTop.setAttribute('tabindex', '1');
193
+ cursorTop.setAttribute('title', 'NAVCTL_TITLE_MOVE_UP');
194
+ // cursorTop.style.backgroundPosition = '-82px 228px';
195
+
196
+ // Create cursor down div
197
+ var cursorDown = document.createElement('div');
198
+ cursorDown.id = '__xmlview1--vbi-vbi-cursor-down';
199
+ cursorDown.className = 'vbi-cursor-down';
200
+ cursorDown.setAttribute('role', 'Button');
201
+ cursorDown.setAttribute('aria-label', 'NAVCTL_TITLE_MOVE_DOWN');
202
+ cursorDown.setAttribute('tabindex', '5');
203
+ cursorDown.setAttribute('title', 'NAVCTL_TITLE_MOVE_DOWN');
204
+
205
+ // Create cursor reset div
206
+ var cursorReset = document.createElement('div');
207
+ cursorReset.id = '__xmlview1--vbi-vbi-cursor-reset';
208
+ cursorReset.className = 'vbi-cursor-reset';
209
+ cursorReset.setAttribute('role', 'Button');
210
+ cursorReset.setAttribute('aria-label', 'NAVCTL_TITLE_MOVE');
211
+ cursorReset.setAttribute('tabindex', '3');
212
+ cursorReset.setAttribute('title', 'NAVCTL_TITLE_MOVE');
213
+
214
+
215
+
216
+ // Append cursor buttons to cursor middle container
217
+ cursorMiddle.appendChild(cursorLeft);
218
+ cursorMiddle.appendChild(cursorRight);
219
+ cursorMiddle.appendChild(cursorTop);
220
+ cursorMiddle.appendChild(cursorDown);
221
+ cursorMiddle.appendChild(cursorReset);
222
+
223
+ cursorTop.addEventListener('click', function () {
224
+ map.panBy([0, -100]); // Move map up
225
+ });
226
+
227
+ cursorDown.addEventListener('click', function () {
228
+ map.panBy([0, 100]); // Move map down
229
+ });
230
+
231
+ cursorLeft.addEventListener('click', function () {
232
+ map.panBy([-100, 0]); // Move map left
233
+ });
234
+
235
+ cursorRight.addEventListener('click', function () {
236
+ map.panBy([100, 0]); // Move map right
237
+ });
238
+ cursorReset.addEventListener('click', function () {
239
+ map.setCenter([0, 0]); // Reset map
240
+ map.setZoom(0);
241
+ });
242
+
243
+
244
+
245
+ // Function to handle mouse movement during dragging
246
+ function onMouseMove(event) {
247
+ if (!isDragging) return;
248
+
249
+ const currentY = event.clientY;
250
+ const deltaY = currentY - startY; // Calculate the difference in Y movement
251
+
252
+ // Get the current position of the scroll point
253
+ let newTop = parseInt(scrollPoint.style.top || "0px") + deltaY;
254
+
255
+ // Get the boundaries of the scroll line
256
+ const upperBoundary = 0;
257
+ const lowerBoundary = scrollLine.offsetHeight - scrollPoint.offsetHeight; // Full scroll range
258
+
259
+ // Enforce the boundaries
260
+ newTop = Math.max(upperBoundary, Math.min(newTop, lowerBoundary));
261
+
262
+ // Update the scroll point position to match mouse drag
263
+ scrollPoint.style.top = `${newTop}px`;
264
+
265
+ // Calculate zoom level based on the position of the scroll point
266
+ const zoomRange = map.getMaxZoom() - map.getMinZoom();
267
+ const scrollPositionRatio = (newTop - upperBoundary) / (lowerBoundary - upperBoundary);
268
+ const newZoom = map.getMinZoom() + scrollPositionRatio * zoomRange;
269
+
270
+ // Apply the zoom to the map
271
+ map.setZoom(newZoom);
272
+
273
+ // Reset `startY` to current Y position after each drag movement
274
+ startY = currentY;
275
+ }
276
+
277
+ // Function to update scroll line position based on the map's zoom level
278
+ function updateScrollLinePosition() {
279
+ const zoomLevel = map.getZoom();
280
+
281
+ // Define boundaries
282
+ const upperBoundary = 0;
283
+ const lowerBoundary = scrollLine.offsetHeight - scrollPoint.offsetHeight;
284
+
285
+ // Calculate the maximum scrollable height
286
+ const maxScrollHeight = lowerBoundary - upperBoundary;
287
+
288
+ const minZoom = map.getMinZoom();
289
+ const maxZoom = map.getMaxZoom();
290
+
291
+ // Calculate the new scroll position based on the zoom level
292
+ const scrollPosition = ((zoomLevel - minZoom) / (maxZoom - minZoom)) * maxScrollHeight;
293
+
294
+ // Update the scroll point's position
295
+ scrollPoint.style.top = `${upperBoundary + scrollPosition}px`;
296
+ }
297
+
298
+ // Attach event listeners to update the scroll line position on zoom and move
299
+ map.on('zoom', updateScrollLinePosition);
300
+ map.on('move', updateScrollLinePosition);
301
+
302
+ // Initialize the scroll line position when the map loads
303
+ updateScrollLinePosition();
304
+
305
+ // Event listeners for drag functionality
306
+ scrollPoint.addEventListener('mousedown', (event) => {
307
+ isDragging = true;
308
+ startY = event.clientY;
309
+ document.addEventListener('mousemove', onMouseMove);
310
+ document.addEventListener('mouseup', onMouseUp);
311
+ });
312
+
313
+ // Function to stop dragging
314
+ function onMouseUp() {
315
+ isDragging = false;
316
+ document.removeEventListener('mousemove', onMouseMove);
317
+ document.removeEventListener('mouseup', onMouseUp);
318
+ }
319
+
320
+ // Attach the onMouseUp event listener globally
321
+ document.addEventListener('mouseup', onMouseUp);
322
+
323
+ // Append cursor middle to cursor grip
324
+ cursorGrip.appendChild(cursorMiddle);
325
+
326
+ // Append cursor grip to main container
327
+ this._container.appendChild(cursorGrip);
328
+
329
+ // Append main container to the map if available
330
+ if (map && map.getContainer()) {
331
+ map.getContainer().appendChild(this._container);
332
+ }
333
+
334
+ // Return the main container
335
+ return this._container;
336
+ }
337
+ onRemove() {
338
+ this._container.parentNode.removeChild(this._container);
339
+ this.map = undefined;
340
+ }
341
+ }
342
+ map.on('load', () => {
343
+ // Custom attribution/copyright control
344
+ map.addControl(new maplibregl.AttributionControl({
345
+ customAttribution: '<span>' + geoJSON[0].copyright + '</span>',
346
+ compact: false
347
+ })
348
+ );
349
+ // Scale control in mi,km or nm
350
+ map.addControl(new maplibregl.ScaleControl({
351
+ maxWidth: 80,
352
+ unit: geoJSON[0].scaleType
353
+ }));
354
+
355
+ // Parsing GeoJSON for each type of object
356
+ map.addSource('geojson-source', {
357
+ 'type': 'geojson',
358
+ 'data': geoJSON[1]
359
+ });
360
+
361
+
362
+ map.addControl(new SAPMapNavControl(), 'top-left');
363
+ // First layer for the border (wider line)
364
+ map.addLayer({
365
+ 'id': 'geojson-source-route-border',
366
+ 'type': 'line',
367
+ 'source': 'geojson-source',
368
+ 'layout': {
369
+ 'line-join': 'round',
370
+ 'line-cap': 'butt'
371
+ },
372
+ 'paint': {
373
+ 'line-color': ['get', 'BorderColor'],
374
+ 'line-width': ['get', 'BorderWidth'] // Slightly wider for border effect
375
+ },
376
+ 'filter': ['==', '$type', 'LineString']
377
+ });
378
+ map.addLayer({
379
+ 'id': 'geojson-source-route',
380
+ 'type': 'line',
381
+ 'source': 'geojson-source',
382
+ 'layout': {
383
+ 'line-join': 'round',
384
+ 'line-cap': 'butt'
385
+ },
386
+ 'paint': {
387
+ 'line-color': ['get', 'Color'],
388
+ 'line-width': ['get', 'LineWidth']
389
+ },
390
+ 'filter': ['==', '$type', 'LineString']
391
+ });
392
+
393
+ // Create a popup, but don't add it to the map yet.
394
+ const popup = new maplibregl.Popup({
395
+ closeButton: false,
396
+ closeOnClick: false
397
+ });
398
+ // add markers to map only for Points
399
+ bounds = [];
400
+ geoJSON[1].features.forEach((marker) => {
401
+ let markerCoordinates = marker.geometry.coordinates;
402
+ if (marker.geometry.type === 'Point') {
403
+ // create a DOM element for the marker (parent div)
404
+ const el = VectorUtils.createSpotElement(marker);
405
+
406
+ // Create child element for the SAP icon (icon overlay)
407
+ const child_el = VectorUtils.createIconElement(marker.properties.Icon);
408
+
409
+ // Append the icon inside the marker
410
+ el.appendChild(child_el);
411
+ // add marker to map
412
+ let spot = new maplibregl.Marker({
413
+ element: el,
414
+ draggable: true
415
+ }).setLngLat(marker.geometry.coordinates)
416
+ .on('dragend', onDragEnd)
417
+ .addTo(map);
418
+ let originalpos = spot.getLngLat();
419
+ allMarkers.push(spot);
420
+
421
+ function onDragEnd() {
422
+ const lngLat = spot.getLngLat();
423
+ if (lngLat !== 0) {
424
+ spot.setLngLat(originalpos);
425
+ }
426
+ }
427
+ // Function to return a promise that resolves when the map is clicked
428
+ function getClickCoordinates() {
429
+ return new Promise((resolve) => {
430
+ map.once('click', function (e) {
431
+ resolve(e.lngLat); // Resolve with the clicked coordinates
432
+ });
433
+ });
434
+ };
435
+
436
+ el.addEventListener('mouseenter', () => {
437
+ //Check if a line is dragged here
438
+ if (lineDrag) {
439
+ validDrop = true;
440
+ el.style.cursor = 'copy';
441
+ } else {
442
+ // Change the cursor style as a UI indicator
443
+ el.style.cursor = 'pointer';
444
+ popup.setLngLat(marker.geometry.coordinates).setHTML(marker.properties.ToolTip).addTo(map);
445
+ }
446
+
447
+ });
448
+ async function triggerPayloadSpot(e, event) {
449
+ let clickCoordinates = null;
450
+ if (event === 'DETAIL_REQUEST') {
451
+ // First, wait for the map click and get the coordinates
452
+ clickCoordinates = await getClickCoordinates();
453
+ } else {
454
+ clickCoordinates = { lng: marker.geometry.coordinates[0], lat: marker.geometry.coordinates[1] };
455
+ }
456
+ marker.properties.x = e.layerX;
457
+ marker.properties.y = e.layerY;
458
+ PayloadGenerator.objectClick('Spot', event, marker, clickCoordinates);
459
+ };
460
+
461
+ el.addEventListener('click', (e) => {
462
+ //Trigger payload
463
+ triggerPayloadSpot(e, 'DETAIL_REQUEST');
464
+ });
465
+
466
+ el.addEventListener('contextmenu', (e) => {
467
+ //Trigger payload
468
+ triggerPayloadSpot(e, 'CONTEXT_MENU_REQUEST');
469
+ var dropdown = marker.properties.menu;
470
+ var i, y;
471
+ var openDropdown = [];
472
+ for (i = 0; i < dropdown.MenuItem.length; i++) {
473
+ openDropdown[i] = dropdown.MenuItem[i].text + "<br>";
474
+ }
475
+ // document.getElementById("openDropdown[0]").innerHTML = "dsa";
476
+ // }
477
+ // const e2 = document.createElement('div');
478
+ // e2.className = 'contextmenu';
479
+ // Initialize the menu
480
+ const menuElement = document.getElementById('menu');
481
+ var dropdownContent = VectorUtils.createDropdownMenu(marker.properties.menu);
482
+ // menuElement.appendChild(dropdownContent);
483
+ popup.setLngLat(marker.geometry.coordinates).setHTML(dropdownContent.innerHTML).addTo(map);
484
+ // Prevent popup from closing when hovering inside
485
+ popup._container.addEventListener('mouseenter', () => {
486
+ // // This keeps the popup open
487
+ popup.setLngLat(marker.geometry.coordinates).addTo(map);
488
+ // console.log(`Clicked on: ${item.text}`);
489
+ });
490
+ // Attach event listeners for interactivity after the popup opens
491
+ popup.on('open', () => {
492
+ const popupElement = popup.getElement();
493
+ const menuContainer = popupElement.querySelector('.dropdown-content');
494
+
495
+ // Toggle dropdown visibility
496
+ popupElement.addEventListener('click', (event) => {
497
+ event.stopPropagation();
498
+ menuContainer.style.display = menuContainer.style.display === 'block' ? 'none' : 'block';
499
+ });
500
+
501
+ // Hide menu when clicking outside
502
+ // document.addEventListener('click', () => {
503
+ // menuContainer.style.display = 'none';
504
+ // });
505
+
506
+ // Handle submenu toggling
507
+ popupElement.querySelectorAll('li').forEach(li => {
508
+ const submenu = li.querySelector('ul');
509
+
510
+ li.addEventListener('click', (event) => {
511
+ event.stopPropagation();
512
+
513
+ if (submenu) {
514
+ submenu.style.display = submenu.style.display === 'block' ? 'none' : 'block'; // Keep submenu open when hovered
515
+ }
516
+
517
+ if (submenu) {
518
+ submenu.addEventListener('click', () => {
519
+ submenu.style.display = submenu.style.display === 'block' ? 'none' : 'block'; // Keep submenu open when hovered
520
+ });
521
+
522
+ submenu.addEventListener('mouseleave', () => {
523
+ submenu.style.display = submenu.style.display === 'block' ? 'none' : 'block'; // Hide submenu when mouse leaves
524
+ });
525
+
526
+ }
527
+ });
528
+ });
529
+
530
+ });
531
+ });
532
+
533
+
534
+ el.addEventListener('mouseleave', () => {
535
+ map.getCanvas().style.cursor = '';
536
+ popup.remove();
537
+ validDrop = false;
538
+ });
539
+
540
+ // Check if the coordinates already exist in predefinedMarkers
541
+ let exists = predefinedMarkers.some(
542
+ (coords) => coords[0] === markerCoordinates[0] && coords[1] === markerCoordinates[1]
543
+ );
544
+
545
+ // If it doesn't exist, push the new coordinates
546
+ if (!exists) {
547
+ predefinedMarkers.push(markerCoordinates);
548
+ }
549
+ } else if (marker.geometry.type == "LineString") {
550
+ line.id = "line_" + marker.id;
551
+ line.coordinates = marker.geometry.coordinates;
552
+ existingLines.push(line);
553
+ }
554
+ if (marker.geometry.type == "LineString") {
555
+ markerCoordinates.forEach((line) => {
556
+ let exists = bounds.some(
557
+ (coords) => coords[0] === line[0] && coords[1] === line[1]
558
+ );
559
+ //If it doesn't exist, push the new coordinates
560
+ if (!exists) {
561
+ bounds.push(line);
562
+ }
563
+ });
564
+ } else {
565
+ // Check if the coordinates already exist in predefinedMarkers
566
+ let exists = bounds.some(
567
+ (coords) => coords[0] === markerCoordinates[0] && coords[1] === markerCoordinates[1]
568
+ );
569
+
570
+ // If it doesn't exist, push the new coordinates
571
+ if (!exists) {
572
+ bounds.push(markerCoordinates);
573
+ }
574
+ }
575
+
576
+ });
577
+
578
+ //Focus the map into the features
579
+ var zoombounds = bounds.reduce((zoombounds, coord) => {
580
+ return zoombounds.extend(coord);
581
+ }, new maplibregl.LngLatBounds(bounds[0], bounds[0]));
582
+
583
+ map.fitBounds(zoombounds, {
584
+ padding: 150
585
+ });
586
+ });
587
+
588
+
589
+ // Change mouse cursor when hovering over the line
590
+ map.on('mouseenter', 'geojson-source-route', function () {
591
+ map.getCanvas().style.cursor = 'pointer';
592
+ });
593
+
594
+ // Revert mouse cursor back when not hovering
595
+ map.on('mouseleave', 'geojson-source-route', function () {
596
+ map.getCanvas().style.cursor = '';
597
+ });
598
+ map.on('click', 'geojson-source-route', function (e) {
599
+ //Trigger payload
600
+ triggerPayloadRoute(e, 'DETAIL_REQUEST');
601
+ });
602
+
603
+ map.on('contextmenu', 'geojson-source-route', function (e) {
604
+ //Trigger payload
605
+ triggerPayloadRoute(e, 'CONTEXT_MENU_REQUEST');
606
+ });
607
+
608
+ function triggerPayloadRoute(e, event) {
609
+ const coordinates = e.lngLat;
610
+ // Get the GeoJSON properties of the clicked line feature
611
+ var route = e.features[0];
612
+ route.properties.x = e.point.x;
613
+ route.properties.y = e.point.y;
614
+
615
+ PayloadGenerator.objectClick('Link', event, route, coordinates);
616
+ }
617
+ function onUp(e) {
618
+ const coords = e.lngLat;
619
+
620
+ if (validDrop) {
621
+ // Perform action for valid drop
622
+ console.log('Dropped on valid target');
623
+ } else {
624
+ // Perform action for invalid drop
625
+ console.log('Invalid drop');
626
+ }
627
+
628
+ // Reset cursor and remove event listeners
629
+ map.getCanvas().style.cursor = '';
630
+ map.off('mousemove', onMove);
631
+ map.off('touchmove', onMove);
632
+ lineDrag = false;
633
+ validDrop = false;
634
+ }
635
+ map.on('mousedown', 'geojson-source-route', (e) => {
636
+ // Check if the left mouse button is clicked (button === 0)
637
+ if (e.originalEvent.button === 0) {
638
+ // Prevent the default map drag behavior.
639
+ e.preventDefault();
640
+ lineDrag = true;
641
+
642
+ // Add move and up event listeners
643
+ map.on('mousemove', onMove);
644
+ map.once('mouseup', onUp);
645
+ }
646
+ });
647
+
648
+ function onMove(e) {
649
+ const features = map.queryRenderedFeatures(e.point, {
650
+ layers: ['geojson-source-route']
651
+ });
652
+
653
+ if (features.length || validDrop) {
654
+ map.getCanvas().style.cursor = 'copy';
655
+
656
+ } else {
657
+ // If not over a valid feature, show the "not-allowed" cursor
658
+ map.getCanvas().style.cursor = 'not-allowed';
659
+ }
660
+ };
661
+
662
+ // https://docs.mapbox.com/mapbox-gl-js/api/map/#map#flyto
663
+ map.on('contextmenu', (e) => {
664
+ // The event object (e) contains information like the
665
+ // coordinates of the point on the map that was clicked.
666
+ // FireAction
667
+ console.log('A contextmenu event has occurred at ' + e.lngLat);
668
+ //to be removed and replaced dynamically from file
669
+ // map.flyTo({
670
+ // center: [e.lngLat.lat, e.lngLat.lng],
671
+ // zoom: 5,
672
+ // speed: 5
673
+ // });
674
+ })
675
+ //VBI.VBITransformer.clearTransformedJSON();
676
+
677
+ document.addEventListener('keydown', function (event) {
678
+ switch (event.code) {
679
+ case 'Plus': // Zoom in when '+' is pressed
680
+ map.zoomIn();
681
+ break;
682
+ case 'Minus': // Zoom out when '-' is pressed
683
+ map.zoomOut();
684
+ break;
685
+ case 'ArrowUp': // Pan up when the up arrow key is pressed
686
+ map.panBy([0, -100]);
687
+ break;
688
+ case 'ArrowDown': // Pan down when the down arrow key is pressed
689
+ map.panBy([0, 100]);
690
+ break;
691
+ case 'ArrowLeft': // Pan left when the left arrow key is pressed
692
+ map.panBy([-100, 0]);
693
+ break;
694
+ case 'ArrowRight': // Pan right when the right arrow key is pressed
695
+ map.panBy([100, 0]);
696
+ break;
697
+ case 'KeyR': // Reset map to initial view when 'r' is pressed
698
+ map.setCenter([0, 0]);
699
+ map.setZoom(2);
700
+ break;
701
+ default:
702
+ break;
703
+ }
704
+ });
705
+
706
+ const box = document.createElement('div');
707
+ box.className = 'zoom-box';
708
+ //box.className = 'selection-box'
709
+ document.body.appendChild(box);
710
+ const mapContainer = document.getElementById('map');
711
+ const boxes = document.getElementById('selection-box');
712
+ window.addEventListener('keydown', (e) => {
713
+ if (e.code === 'ControlLeft') {
714
+ isCtrlPressed = true;
715
+ map.getCanvas().style.cursor = 'crosshair';
716
+ lassoPoints = [];
717
+ }
718
+ if (e.code === 'ShiftLeft') {
719
+ map.getCanvas().style.cursor = 'crosshair';
720
+ }
721
+ });
722
+
723
+ window.addEventListener('keyup', (e) => {
724
+ if (e.code === 'ControlLeft') {
725
+ isCtrlPressed = false;
726
+ if (lassoPoints.length > 2) {
727
+ completeLasso();
728
+ }
729
+ map.getCanvas().style.cursor = '';
730
+ }
731
+ if (e.code === 'ShiftLeft') {
732
+ map.getCanvas().style.cursor = '';
733
+ }
734
+ });
735
+ map.on('mousedown', (e) => {
736
+ if (isCtrlPressed) {
737
+ const clickedPoint = [e.lngLat.lng, e.lngLat.lat];
738
+ lassoPoints.push(clickedPoint);
739
+ isDrawing = true;
740
+ }
741
+
742
+ else if (e.originalEvent.shiftKey) {
743
+ isDrawing = true;
744
+ start = e.point;
745
+ box.style.display = 'block';
746
+ box.style.left = `${e.point.x}px`;
747
+ box.style.top = `${e.point.y}px`;
748
+ }
749
+ });
750
+ map.on('mousemove', (e) => {
751
+ if (isCtrlPressed) {
752
+ const movingPoint = [e.lngLat.lng, e.lngLat.lat];
753
+ lassoPoints.push(movingPoint);
754
+ drawLassoLine();
755
+ }
756
+
757
+ else if (e.originalEvent.shiftKey) {
758
+ current = e.point;
759
+ const minX = Math.min(start.x, current.x);
760
+ const minY = Math.min(start.y, current.y);
761
+ const width = Math.abs(start.x - current.x);
762
+ const height = Math.abs(start.y - current.y);
763
+ box.style.left = `${minX}px`;
764
+ box.style.top = `${minY}px`;
765
+ box.style.width = `${width}px`;
766
+ box.style.height = `${height}px`;
767
+
768
+ }
769
+ });
770
+
771
+ map.on('mouseup', (e) => {
772
+ if (isCtrlPressed) {
773
+ // Complete Lasso Selection
774
+ isDrawing = false;
775
+ if (lassoPoints.length > 2) {
776
+ lassoPoints.push(lassoPoints[0]);
777
+ completeLasso();
778
+ }
779
+ } else if (e.originalEvent.shiftKey) {
780
+ isDrawing = false;
781
+ box.style.display = 'none';
782
+ box.style.width = '0px';
783
+ box.style.height = '0px';
784
+ const bounds = [
785
+ map.unproject([Math.min(start.x, current.x), Math.min(start.y, current.y)]),
786
+ map.unproject([Math.max(start.x, current.x), Math.max(start.y, current.y)])
787
+ ];
788
+ console.log("Rectangular Zoom Bounds: ", bounds);
789
+ map.fitBounds(bounds, {
790
+ padding: 20
791
+ });
792
+ }
793
+ }
794
+ );
795
+ function drawLassoLine() {
796
+ if (map.getSource('lasso-line')) {
797
+ map.getSource('lasso-line').setData({
798
+ 'type': 'LineString',
799
+ 'coordinates': lassoPoints
800
+ });
801
+ } else {
802
+ map.addSource('lasso-line', {
803
+ 'type': 'geojson',
804
+ 'data': {
805
+ 'type': 'LineString',
806
+ 'coordinates': lassoPoints
807
+ }
808
+ });
809
+ map.addLayer({
810
+ 'id': 'lasso-line-layer',
811
+ 'type': 'line',
812
+ 'source': 'lasso-line',
813
+ 'paint': {
814
+ 'line-color': '#000000',
815
+ 'line-width': 2,
816
+ 'line-dasharray': [2, 2]
817
+ }
818
+ });
819
+ }
820
+ }
821
+
822
+ function completeLasso() {
823
+ if (map.getSource('lasso-polygon')) {
824
+ map.getSource('lasso-polygon').setData({
825
+ 'type': 'Polygon',
826
+ 'coordinates': [lassoPoints]
827
+ });
828
+ } else {
829
+ map.addSource('lasso-polygon', {
830
+ 'type': 'geojson',
831
+ 'data': {
832
+ 'type': 'Polygon',
833
+ 'coordinates': [lassoPoints]
834
+ }
835
+ });
836
+ map.addLayer({
837
+ 'id': 'lasso-polygon-layer',
838
+ 'type': 'fill',
839
+ 'source': 'lasso-polygon',
840
+ 'paint': {
841
+ 'fill-color': '#088',
842
+ 'fill-opacity': 0.5
843
+ }
844
+ });
845
+ }
846
+
847
+ // Remove lasso line and polygon after the selection is completed
848
+ setTimeout(() => {
849
+ map.removeLayer('lasso-line-layer');
850
+ map.removeSource('lasso-line');
851
+ map.removeLayer('lasso-polygon-layer');
852
+ map.removeSource('lasso-polygon');
853
+ lassoPoints = [];
854
+ }, 1000);
855
+ map.getCanvas().style.cursor = ''; // Reset cursor after lasso is completed
856
+ }
857
+ map.on('click', () => {
858
+ if (isDrawing) {
859
+ clearSelectionBox();
860
+ isDrawing = false;
861
+ }
862
+ });
863
+ // Function to clear selection box
864
+ function clearSelectionBox() {
865
+ box.style.display = 'none';
866
+ box.style.width = '0px';
867
+ box.style.height = '0px';
868
+ }
869
+ }
870
+
871
+ });
872
+