@regionerne/gis-komponent 0.0.21 → 0.0.28

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.
Files changed (37) hide show
  1. package/fesm2022/regionerne-gis-komponent.mjs +2267 -472
  2. package/fesm2022/regionerne-gis-komponent.mjs.map +1 -1
  3. package/lib/components/active-objects/activeObjects.component.d.ts +25 -3
  4. package/lib/components/confirm-dialog/confirm-dialog.component.d.ts +18 -0
  5. package/lib/components/gis-komponent/gis-komponent.component.d.ts +17 -3
  6. package/lib/components/layer-selector/layer-selector.component.d.ts +25 -13
  7. package/lib/components/legends-list/legends-list.component.d.ts +22 -20
  8. package/lib/components/map-search/map-search.component.d.ts +20 -5
  9. package/lib/components/selected-feature-info/selected-feature-info.component.d.ts +15 -0
  10. package/lib/components/toolbox/toolbox.component.d.ts +50 -39
  11. package/lib/models/GisKomponentSettings.d.ts +20 -0
  12. package/lib/models/ILayer.d.ts +9 -0
  13. package/lib/models/ILayerGroup.d.ts +2 -8
  14. package/lib/models/IProfile.d.ts +9 -0
  15. package/lib/models/icons.constants.d.ts +5 -0
  16. package/lib/models/profile.d.ts +2 -0
  17. package/lib/services/centerPoint.service.d.ts +17 -0
  18. package/lib/services/confirm-dialog.service.d.ts +11 -0
  19. package/lib/services/conflictAnalysisSearch.service.d.ts +19 -0
  20. package/lib/services/currentItems.service.d.ts +3 -0
  21. package/lib/services/drawLayerSource.service.d.ts +4 -0
  22. package/lib/services/featureHelper.service.d.ts +0 -1
  23. package/lib/services/geometry.service.d.ts +27 -0
  24. package/lib/services/highlight.service.d.ts +12 -9
  25. package/lib/services/hoverInfoSearch.service.d.ts +24 -0
  26. package/lib/services/infoSearch.service.d.ts +36 -0
  27. package/lib/services/layerError.service.d.ts +1 -0
  28. package/lib/services/layerHelper.service.d.ts +6 -3
  29. package/lib/services/layoutService.d.ts +12 -0
  30. package/lib/services/overlap.service.d.ts +21 -0
  31. package/lib/services/printDrawLayerSource.service.d.ts +1 -2
  32. package/lib/services/printHelper.service.d.ts +9 -0
  33. package/lib/services/searchProvider.service.d.ts +29 -0
  34. package/lib/services/showInfo.service.d.ts +15 -0
  35. package/lib/services/showInfoHover.service.d.ts +11 -0
  36. package/lib/services/wfsSearch.service.d.ts +39 -0
  37. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import * as i1$1 from '@angular/common';
2
- import { CommonModule } from '@angular/common';
2
+ import { CommonModule, AsyncPipe, JsonPipe } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { InjectionToken, inject, Injectable, Component, Input, ViewChild } from '@angular/core';
5
- import * as i2 from '@angular/material/icon';
4
+ import { InjectionToken, inject, Injectable, Component, Input, ViewChild, Inject, EventEmitter, Output } from '@angular/core';
5
+ import * as i3 from '@angular/material/icon';
6
6
  import { MatIconModule } from '@angular/material/icon';
7
7
  import Map$1 from 'ol/Map';
8
8
  import View from 'ol/View';
@@ -12,7 +12,7 @@ import WMTSCapabilities from 'ol/format/WMTSCapabilities';
12
12
  import ImageLayer from 'ol/layer/Image';
13
13
  import { HttpClient, HttpErrorResponse, HttpHeaders, provideHttpClient, withInterceptors } from '@angular/common/http';
14
14
  import OLLayerGroup from 'ol/layer/Group';
15
- import * as i5 from '@angular/material/select';
15
+ import * as i6$1 from '@angular/material/select';
16
16
  import { MatSelectModule } from '@angular/material/select';
17
17
  import * as i3$1 from '@angular/material/list';
18
18
  import { MatListModule } from '@angular/material/list';
@@ -22,49 +22,66 @@ import { register } from 'ol/proj/proj4';
22
22
  import proj4 from 'proj4';
23
23
  import { Control, Rotate } from 'ol/control';
24
24
  import { Vector, TileWMS } from 'ol/source';
25
- import { BehaviorSubject, Subject, of, tap, map, ReplaySubject, filter, switchMap, forkJoin, catchError, combineLatest, throwError } from 'rxjs';
25
+ import { Subject, BehaviorSubject, tap, switchMap, EMPTY, of, map, ReplaySubject, filter, interval, combineLatest, Observable, takeUntil, take, mergeWith, debounceTime, distinctUntilChanged, forkJoin, catchError, throwError } from 'rxjs';
26
26
  import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS';
27
- import * as i4 from '@angular/cdk/drag-drop';
27
+ import VectorLayer from 'ol/layer/Vector';
28
+ import TileSource from 'ol/source/Tile';
29
+ import ImageSource from 'ol/source/Image';
30
+ import VectorSource from 'ol/source/Vector';
31
+ import { unByKey } from 'ol/Observable';
32
+ import { MAT_SNACK_BAR_DATA, MatSnackBarRef, MatSnackBar } from '@angular/material/snack-bar';
33
+ import * as i5 from '@angular/cdk/drag-drop';
28
34
  import { moveItemInArray, transferArrayItem, DragDropModule } from '@angular/cdk/drag-drop';
29
35
  import * as i1 from '@angular/material/form-field';
30
36
  import { MatFormFieldModule } from '@angular/material/form-field';
31
- import * as i2$1 from '@angular/forms';
37
+ import * as i1$2 from '@angular/forms';
32
38
  import { FormsModule } from '@angular/forms';
33
- import * as i3 from '@angular/material/expansion';
39
+ import * as i6 from '@angular/material/expansion';
34
40
  import { MatExpansionModule } from '@angular/material/expansion';
35
- import * as i6 from '@angular/material/input';
41
+ import * as i7 from '@angular/material/input';
36
42
  import { MatInputModule } from '@angular/material/input';
37
43
  import ol_control_Control from 'ol/control/Control';
38
- import VectorLayer from 'ol/layer/Vector';
39
- import TileSource from 'ol/source/Tile';
40
- import ImageSource from 'ol/source/Image';
41
- import VectorSource from 'ol/source/Vector';
42
- import { unByKey } from 'ol/Observable';
43
- import { MAT_SNACK_BAR_DATA, MatSnackBarRef, MatSnackBar } from '@angular/material/snack-bar';
44
+ import * as i8 from '@angular/material/tooltip';
45
+ import { MatTooltipModule } from '@angular/material/tooltip';
44
46
  import 'ol/ol.css';
45
- import { LineString, Point } from 'ol/geom';
47
+ import { Point as Point$1, LineString } from 'ol/geom';
46
48
  import { getArea, getLength } from 'ol/sphere';
47
- import Draw from 'ol/interaction/Draw';
49
+ import Draw, { createBox, createRegularPolygon } from 'ol/interaction/Draw';
48
50
  import { Snap, Select as Select$1, Modify } from 'ol/interaction';
49
- import { Style, Circle, Stroke, Fill, Text } from 'ol/style';
50
- import * as i4$1 from '@angular/material/core';
51
+ import { Style, Circle, Stroke, Fill, Icon, Text, RegularShape } from 'ol/style';
52
+ import * as i4 from '@angular/material/core';
51
53
  import { MatOptionModule } from '@angular/material/core';
52
54
  import * as SLDReader from '@nieuwlandgeo/sldreader';
53
55
  import WKT from 'ol/format/WKT';
54
- import { extend, createEmpty, buffer as buffer$1 } from 'ol/extent';
55
56
  import Feature$1 from 'ol/Feature';
56
57
  import { never, always } from 'ol/events/condition';
57
- import { buffer, featureCollection, difference, booleanIntersects, union } from '@turf/turf';
58
+ import { buffer, lineIntersect, lineSplit, featureCollection, difference, booleanIntersects, union, point, booleanPointInPolygon, area, feature, booleanWithin, intersect } from '@turf/turf';
58
59
  import GeoJSON from 'ol/format/GeoJSON';
59
- import * as i7 from '@angular/material/tooltip';
60
- import { MatTooltipModule } from '@angular/material/tooltip';
61
- import * as i8 from '@angular/material/slide-toggle';
60
+ import * as i9 from '@angular/material/slide-toggle';
62
61
  import { MatSlideToggleModule } from '@angular/material/slide-toggle';
63
62
  import { Feature } from 'ol';
63
+ import { extend, createEmpty, buffer as buffer$1 } from 'ol/extent';
64
+ import { easeOut } from 'ol/easing';
64
65
  import Select from 'ol/interaction/Select';
65
66
  import html2canvas from 'html2canvas-pro';
67
+ import CircleStyle from 'ol/style/Circle';
68
+ import { transform } from 'ol/proj';
69
+ import * as i2 from '@angular/material/button';
70
+ import { MatButtonModule } from '@angular/material/button';
71
+ import * as i1$3 from '@angular/material/dialog';
72
+ import { MAT_DIALOG_DATA, MatDialogModule, MatDialogTitle, MatDialogContent, MatDialogActions } from '@angular/material/dialog';
73
+ import WFS from 'ol/format/WFS';
74
+ import Point from 'ol/geom/Point';
75
+ import { intersects, contains, like } from 'ol/format/filter';
76
+ import { BufferOp } from 'jsts/org/locationtech/jts/operation/buffer';
77
+ import { GeoJSONReader, GeoJSONWriter } from 'jsts/org/locationtech/jts/io';
78
+ import GeometryFactory from 'jsts/org/locationtech/jts/geom/GeometryFactory';
79
+ import PrecisionModel from 'jsts/org/locationtech/jts/geom/PrecisionModel';
66
80
  import * as i5$1 from '@angular/material/autocomplete';
67
81
  import { MatAutocompleteModule } from '@angular/material/autocomplete';
82
+ import { map as map$1 } from 'rxjs/operators';
83
+ import { createStringXY } from 'ol/coordinate';
84
+ import GeoJSON$1 from 'ol/format/GeoJSON.js';
68
85
 
69
86
  const GISKOMPONENT_CONFIG = new InjectionToken('GisKomponentConfig');
70
87
 
@@ -116,83 +133,6 @@ class CopyrightControl extends Control {
116
133
  }
117
134
  }
118
135
 
119
- class LayerHelperService {
120
- activeLayersChanged = new BehaviorSubject(true);
121
- _layerDbIdKey = 'layerDbId';
122
- _layerGroupDbIdKey = 'layerGroupDbId';
123
- _layerIdsToDisplayInMapKeyName = 'layerIdsToDisplayInMap';
124
- setDbId(layer, id) {
125
- layer.set(this._layerDbIdKey, id);
126
- }
127
- getDbId(layer) {
128
- const dbId = layer.get(this._layerDbIdKey) ?? -1;
129
- return +dbId;
130
- }
131
- setLayerGroupDbId(layer, id) {
132
- layer.set(this._layerGroupDbIdKey, id);
133
- }
134
- getLayerGroupDbId(layer) {
135
- const dbId = layer.get(this._layerGroupDbIdKey) ?? -1;
136
- return +dbId;
137
- }
138
- applyCachedLayersToDisplayInMap(map, profileId) {
139
- this.activeLayersChanged.next(true);
140
- const layerIdsCachedToDisplay = localStorage.getItem(`${this._layerIdsToDisplayInMapKeyName}_${profileId}`)?.split(',');
141
- if (layerIdsCachedToDisplay) {
142
- //Bottom layers are rendered first, top layers are rendered last (on top)
143
- layerIdsCachedToDisplay.reverse();
144
- map.getLayers().getArray().forEach(layergroup => {
145
- if (layergroup instanceof OLLayerGroup) {
146
- layergroup.getLayers().getArray().forEach(l => {
147
- if (!layerIdsCachedToDisplay.some(id => l.get(this._layerDbIdKey) == id)) {
148
- const current = l.getVisible();
149
- if (current) {
150
- l.setVisible(!current);
151
- }
152
- }
153
- else {
154
- //Set z index to match the order in cache
155
- l.setZIndex(layerIdsCachedToDisplay.indexOf(l.get(this._layerDbIdKey).toString()));
156
- //console.log('layer id '+l.get(this._layerDbIdKey)+' - setZIndex:'+layerIdsCachedToDisplay.indexOf(l.get(this._layerDbIdKey).toString()));
157
- }
158
- });
159
- }
160
- });
161
- }
162
- }
163
- updateLayerOpacityInMap(map, layer) {
164
- map.getLayers().getArray().forEach(layergroup => {
165
- if (layergroup instanceof OLLayerGroup) {
166
- layergroup.getLayers().getArray().forEach(l => {
167
- if (l.get(this._layerDbIdKey) === layer.id && layer.opacity) {
168
- l.setOpacity(layer.opacity);
169
- }
170
- });
171
- }
172
- });
173
- }
174
- toggleLayerInMap(map, layer) {
175
- map.getLayers().getArray().forEach(layergroup => {
176
- if (layergroup instanceof OLLayerGroup) {
177
- layergroup.getLayers().getArray().forEach(l => {
178
- if (l.get(this._layerDbIdKey) === layer.id) {
179
- const current = l.getVisible();
180
- l.setVisible(!current);
181
- }
182
- });
183
- }
184
- });
185
- }
186
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
187
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerHelperService, providedIn: 'root' });
188
- }
189
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerHelperService, decorators: [{
190
- type: Injectable,
191
- args: [{
192
- providedIn: 'root'
193
- }]
194
- }] });
195
-
196
136
  class LibNotificationComponent {
197
137
  data = inject(MAT_SNACK_BAR_DATA);
198
138
  _snackRef = inject((MatSnackBarRef));
@@ -257,6 +197,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
257
197
 
258
198
  class LayerErrorService {
259
199
  layerStatusChanged = new Subject();
200
+ layerErrored = new Subject();
260
201
  layerStatuses = new Map();
261
202
  listeners = new Map();
262
203
  _errorService = inject(LibErrorService);
@@ -275,6 +216,7 @@ class LayerErrorService {
275
216
  console.error(`[Layer ${layerId}] load error:`, event);
276
217
  this._errorService.show(new Error(`Indlæsningsfejl for lag med id ${layerId}.`));
277
218
  this.layerStatusChanged.next(layerId);
219
+ this.layerErrored.next(layerId);
278
220
  };
279
221
  const clearError = () => {
280
222
  this.layerStatuses.set(layerId, { layerId, hasError: false });
@@ -324,10 +266,193 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
324
266
  args: [{ providedIn: 'root' }]
325
267
  }] });
326
268
 
327
- class LayerSelectorComponent {
328
- set contentIcon(content) {
329
- this._layerSelectorIcon = content;
269
+ class LayerHelperService {
270
+ activeLayersChanged = new BehaviorSubject(true);
271
+ _layerDbIdKey = 'layerDbId';
272
+ _layerGroupDbIdKey = 'layerGroupDbId';
273
+ _mapFilteredLayerGroupsKeyName = 'mapFilteredLayerGroups';
274
+ _layerErrorService = inject(LayerErrorService);
275
+ layerActivatedByBackgroundGroup = new Subject();
276
+ setDbId(layer, id) {
277
+ layer.set(this._layerDbIdKey, id);
278
+ }
279
+ getDbId(layer) {
280
+ const dbId = layer.get(this._layerDbIdKey) ?? -1;
281
+ return +dbId;
282
+ }
283
+ setLayerGroupDbId(layer, id) {
284
+ layer.set(this._layerGroupDbIdKey, id);
285
+ }
286
+ getLayerGroupDbId(layer) {
287
+ const dbId = layer.get(this._layerGroupDbIdKey) ?? -1;
288
+ return +dbId;
289
+ }
290
+ applyCachedLayersToDisplayInMap(map, profileId) {
291
+ this.activeLayersChanged.next(true);
292
+ const cacheValue = localStorage.getItem(`${this._mapFilteredLayerGroupsKeyName}_${profileId}`);
293
+ if (cacheValue) {
294
+ const layersCachedToDisplay = JSON.parse(cacheValue).cachedLayerGroups
295
+ .flatMap((lg) => lg.layers)
296
+ .map((l) => ({ id: l.id, visible: l.visible }));
297
+ //Bottom layers are rendered first, top layers are rendered last (on top)
298
+ layersCachedToDisplay.reverse();
299
+ map.getLayers().getArray().forEach(layergroup => {
300
+ if (layergroup instanceof OLLayerGroup) {
301
+ layergroup.getLayers().getArray().forEach(l => {
302
+ const cachedLayer = layersCachedToDisplay
303
+ .find((layer) => l.get(this._layerDbIdKey) == layer.id);
304
+ const current = l.getVisible();
305
+ if (cachedLayer != undefined && current != cachedLayer.visible) {
306
+ l.setVisible(cachedLayer.visible);
307
+ }
308
+ //Set z index to match the order in cache
309
+ l.setZIndex(layersCachedToDisplay.indexOf(cachedLayer));
310
+ });
311
+ }
312
+ });
313
+ }
314
+ }
315
+ startFailoverForBackgroundGroup(aMap, groupId) {
316
+ const currentIndex$ = new BehaviorSubject(0);
317
+ const layergroup = aMap.getLayers().getArray()
318
+ .find(layergroup => layergroup instanceof OLLayerGroup && layergroup.get('id') === groupId);
319
+ const items = layergroup.getLayers().getArray().sort((a, b) => (b.getZIndex() ?? 0) - (a.getZIndex() ?? 0));
320
+ // Handles activating the current item
321
+ const activation$ = currentIndex$.pipe(tap(index => {
322
+ // deactivate all first
323
+ items.forEach(layer => layer.setVisible(false));
324
+ // if out of range, stop completely
325
+ if (index >= items.length) {
326
+ console.warn('No more items left. Stopping failover.');
327
+ return;
328
+ }
329
+ // activate current
330
+ const layer = items[index];
331
+ layer.setVisible(true);
332
+ this.layerActivatedByBackgroundGroup.next(layer.get(this._layerDbIdKey));
333
+ }),
334
+ // Listen for failures only while we have a valid index
335
+ switchMap(index => {
336
+ if (index >= items.length)
337
+ return EMPTY;
338
+ const currentItem = items[index];
339
+ return this._layerErrorService.layerErrored.pipe(tap(failedId => {
340
+ // If the current one failed, move to next
341
+ if (failedId === currentItem.get(this._layerDbIdKey)) {
342
+ currentIndex$.next(index + 1);
343
+ }
344
+ }));
345
+ }));
346
+ activation$.subscribe();
347
+ }
348
+ updateLayerOpacityInMap(map, layer) {
349
+ map.getLayers().getArray().forEach(layergroup => {
350
+ if (layergroup instanceof OLLayerGroup) {
351
+ layergroup.getLayers().getArray().forEach(l => {
352
+ if (l.get(this._layerDbIdKey) === layer.id && layer.opacity) {
353
+ l.setOpacity(layer.opacity);
354
+ }
355
+ });
356
+ }
357
+ });
358
+ }
359
+ toggleLayerInMap(map, layerId) {
360
+ map.getLayers().getArray().forEach(layergroup => {
361
+ if (layergroup instanceof OLLayerGroup) {
362
+ layergroup.getLayers().getArray().forEach(l => {
363
+ if (l.get(this._layerDbIdKey) === layerId) {
364
+ const current = l.getVisible();
365
+ l.setVisible(!current);
366
+ }
367
+ });
368
+ }
369
+ });
370
+ }
371
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
372
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerHelperService, providedIn: 'root' });
373
+ }
374
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerHelperService, decorators: [{
375
+ type: Injectable,
376
+ args: [{
377
+ providedIn: 'root'
378
+ }]
379
+ }] });
380
+
381
+ class LayoutService {
382
+ loadInitialPositionFromProfile(widgetName, initialPosition) {
383
+ let dragPosition = { x: 2, y: -25 };
384
+ switch (initialPosition) {
385
+ case 'øverst/venstre':
386
+ dragPosition = { x: 46, y: -119 };
387
+ this._savePosition(widgetName, dragPosition);
388
+ break;
389
+ case 'midte/venstre':
390
+ dragPosition = { x: 1, y: 247 };
391
+ this._savePosition(widgetName, dragPosition);
392
+ break;
393
+ case 'nederst/venstre':
394
+ dragPosition = { x: 0, y: 493 };
395
+ this._savePosition(widgetName, dragPosition);
396
+ break;
397
+ case 'øverst/midte':
398
+ dragPosition = { x: 849, y: -154 };
399
+ this._savePosition(widgetName, dragPosition);
400
+ break;
401
+ case 'nederst/midte':
402
+ dragPosition = { x: 895, y: 481 };
403
+ this._savePosition(widgetName, dragPosition);
404
+ break;
405
+ case 'øverst/højre':
406
+ dragPosition = { x: 1599, y: -121 };
407
+ this._savePosition(widgetName, dragPosition);
408
+ break;
409
+ case 'midte/højre':
410
+ dragPosition = { x: 1600, y: 164 };
411
+ this._savePosition(widgetName, dragPosition);
412
+ break;
413
+ case 'nederst/højre':
414
+ dragPosition = { x: 1600, y: 500 };
415
+ this._savePosition(widgetName, dragPosition);
416
+ break;
417
+ }
418
+ return dragPosition;
419
+ }
420
+ bringElementIntoViewIfNeeded(elementClass, offsetValue, collapsed, map) {
421
+ const element = document.querySelector(elementClass);
422
+ const mapControls = map.getControls();
423
+ console.log('map controls', mapControls);
424
+ if (!element)
425
+ return;
426
+ const left = parseInt(element.style.left || '0', 10);
427
+ console.log('bringToolboxIntoViewIfNeeded left ', left, element, element.style);
428
+ if (collapsed) {
429
+ // Widget minimized, remove the style for left
430
+ element.style.left = '0em';
431
+ }
432
+ else {
433
+ // If widget expands outside screen on the right (left is > <offsetValue> em), then bring it back
434
+ element.style.left = `${left > offsetValue ? offsetValue : left}em`;
435
+ }
436
+ }
437
+ _savePosition(widgetStorageKey, position) {
438
+ try {
439
+ localStorage.setItem(widgetStorageKey, JSON.stringify(position));
440
+ }
441
+ catch (error) {
442
+ console.error('Error saving position to localStorage:', error);
443
+ }
330
444
  }
445
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
446
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayoutService, providedIn: 'root' });
447
+ }
448
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayoutService, decorators: [{
449
+ type: Injectable,
450
+ args: [{
451
+ providedIn: 'root'
452
+ }]
453
+ }] });
454
+
455
+ class LayerSelectorComponent {
331
456
  set contentBody(content) {
332
457
  this._layerSelectorBody = content;
333
458
  }
@@ -337,20 +462,26 @@ class LayerSelectorComponent {
337
462
  searchText = '';
338
463
  allLayerGroups = [];
339
464
  filteredLayerGroups = [];
340
- showLegend = false; // TO DO - this will be added later, as a change (probably as a Profile setting)
341
- showSelector = false;
342
- legendUrl = 'https://docs.geoserver.org/main/en/user/_images/samplelegend.png'; // TO DO - remove this (mockup for now, will be done as a change later)
343
- _namesToGoLast = ['Baggrundskort'];
344
- _layerSelectorIcon;
345
- _layerSelectorIconControl;
465
+ filteredBackgroundLayerGroups = [];
466
+ showLegend = false;
467
+ collapsed = true;
468
+ dragPosition = { x: 0, y: 0 };
469
+ legendUrl = 'https://docs.geoserver.org/main/en/user/_images/samplelegend.png';
346
470
  _layerSelectorBody;
347
471
  _layerSelectorBodyControl;
472
+ layerSelectorDragPosition = { x: 0, y: 0 };
348
473
  _mapFilteredLayerGroupsKeyName = 'mapFilteredLayerGroups';
349
- _layerIdsToDisplayInMapKeyName = 'layerIdsToDisplayInMap';
474
+ POSITION_STORAGE_KEY = 'layerSelectorPosition';
350
475
  _layerHelper = inject(LayerHelperService);
351
476
  _layerErrorService = inject(LayerErrorService);
477
+ _layoutService = inject(LayoutService);
478
+ LAYER_SELECTOR_POSITION_KEY = 'layerSelectorPosition';
352
479
  ngOnInit() {
353
- this._layerErrorService.layerStatusChanged
480
+ this._loadLayerSelectorPosition();
481
+ this._loadPosition();
482
+ this._layerHelper.layerActivatedByBackgroundGroup
483
+ .subscribe(layerId => this.toggleLayer(layerId, undefined, true));
484
+ this._layerErrorService.layerErrored
354
485
  .subscribe(layerId => {
355
486
  const layerHasErrors = this._layerErrorService.getLayerStatus(layerId)?.hasError;
356
487
  if (layerHasErrors != undefined)
@@ -359,11 +490,12 @@ class LayerSelectorComponent {
359
490
  }
360
491
  ngOnChanges(changes) {
361
492
  if (changes['profile'] && this.profile) {
493
+ this._loadPosition();
362
494
  this.allLayerGroups = this.profile.layerGroups
363
495
  .map((lg, idx) => ({ ...lg,
364
496
  noOfVisibleLayers: lg.layers.filter(l => l.visible).length,
365
497
  visible: lg.layers.some(l => l.visible),
366
- expanded: lg.layers.some(l => l.visible),
498
+ expanded: lg.folded === false,
367
499
  layers: lg.layers.filter((l) => l.activeInSelector),
368
500
  sortOrder: idx }))
369
501
  .filter(g => g.layers.length > 0);
@@ -372,31 +504,81 @@ class LayerSelectorComponent {
372
504
  const cachedProfileInfo = JSON.parse(cacheValue);
373
505
  if (cachedProfileInfo.profile.versionId < this.profile.versionId) {
374
506
  //Profile version changed in backend, use that
375
- this.filteredLayerGroups = this.setfilteredGroups();
507
+ this.filterChanged();
376
508
  this._cacheProfileInfo();
377
509
  }
378
510
  else {
379
- this.filteredLayerGroups = cachedProfileInfo.cachedLayerGroups;
511
+ this.filteredLayerGroups = cachedProfileInfo.cachedLayerGroups.filter(gr => !gr.background);
512
+ ;
513
+ this.filteredBackgroundLayerGroups = cachedProfileInfo.cachedLayerGroups.filter(gr => gr.background);
380
514
  this._setVisibleLayersFromCache(cachedProfileInfo.cachedLayerGroups);
381
515
  }
382
516
  }
383
517
  else {
384
- this.filteredLayerGroups = this.setfilteredGroups();
518
+ this.filterChanged();
385
519
  this._cacheProfileInfo();
386
520
  }
387
- this._initializeMapIconControl();
521
+ // Apply Failover logic to all background groups
522
+ this.filteredBackgroundLayerGroups
523
+ .forEach(group => {
524
+ group.layers.forEach(layer => layer.visible = false); //Turn all layers off in selector to begin with
525
+ group.visible = true; // Expand the group (there will be at least one active layer)
526
+ group.expanded = true;
527
+ this._layerHelper.startFailoverForBackgroundGroup(this.map, group.id);
528
+ });
529
+ this._initializeMapControl();
388
530
  }
389
531
  }
390
- toggleLayer(event, layer) {
391
- event.stopPropagation(); // Prevent the panel from expanding/collapsing
392
- this._layerHelper.toggleLayerInMap(this.map, layer);
532
+ _loadLayerSelectorPosition() {
533
+ try {
534
+ const saved = localStorage.getItem(this.LAYER_SELECTOR_POSITION_KEY);
535
+ if (saved) {
536
+ this.layerSelectorDragPosition = JSON.parse(saved);
537
+ }
538
+ else if (this.profile) {
539
+ this.layerSelectorDragPosition = this._layoutService.loadInitialPositionFromProfile(this.LAYER_SELECTOR_POSITION_KEY, this.profile.layerSelectorInitialPosition);
540
+ }
541
+ }
542
+ catch (error) {
543
+ console.error('Error loading layer selector position from localStorage:', error);
544
+ this.layerSelectorDragPosition = { x: 0, y: 0 };
545
+ }
546
+ }
547
+ onLayerSelectorDragEnded(event) {
548
+ const position = event.source.getFreeDragPosition();
549
+ this.layerSelectorDragPosition = position;
550
+ try {
551
+ localStorage.setItem(this.LAYER_SELECTOR_POSITION_KEY, JSON.stringify(position));
552
+ }
553
+ catch (error) {
554
+ console.error('Error saving layer selector position to localStorage:', error);
555
+ }
556
+ }
557
+ toggleLayer(layerId, event, skipMapToggle = false) {
558
+ event?.stopPropagation(); // Prevent the panel from expanding/collapsing
559
+ if (!skipMapToggle)
560
+ this._layerHelper.toggleLayerInMap(this.map, layerId);
393
561
  // Toggle layer in all groups in the selector UI
394
- this.filteredLayerGroups.forEach(group => {
562
+ this.filteredLayerGroups.concat(this.filteredBackgroundLayerGroups).forEach(group => {
563
+ let singleLaterIdToKeepOn = 0;
395
564
  group.layers.forEach(l => {
396
- if (layer.id === l.id) {
565
+ if (layerId === l.id) {
397
566
  l.visible = !l.visible;
567
+ if (l.visible && !group.allowMultiSelect) {
568
+ // Group doesn't allow multi select and we just turned this layer on
569
+ // so turn all others off
570
+ singleLaterIdToKeepOn = l.id;
571
+ }
398
572
  }
399
573
  });
574
+ if (singleLaterIdToKeepOn > 0) {
575
+ group.layers.forEach(l => {
576
+ if (singleLaterIdToKeepOn !== l.id) {
577
+ l.visible = false;
578
+ this._layerHelper.toggleLayerInMap(this.map, l.id);
579
+ }
580
+ });
581
+ }
400
582
  group.noOfVisibleLayers = group.layers.filter(l => l.visible).length;
401
583
  group.visible = group.layers.some(l => l.visible);
402
584
  group.expanded = group.layers.some(l => l.visible);
@@ -404,7 +586,7 @@ class LayerSelectorComponent {
404
586
  this._cacheProfileInfo();
405
587
  }
406
588
  setLayerErrorStatus(layerId, errorStatus) {
407
- this.filteredLayerGroups.forEach(group => {
589
+ this.filteredLayerGroups.concat(this.filteredBackgroundLayerGroups).forEach(group => {
408
590
  group.layers.forEach(l => {
409
591
  if (layerId === l.id) {
410
592
  l.hasErrors = errorStatus;
@@ -415,34 +597,39 @@ class LayerSelectorComponent {
415
597
  toggleGroup(event, layerGroup) {
416
598
  event.stopPropagation(); // Prevent the panel from expanding/collapsing
417
599
  const visible = !layerGroup.visible;
418
- layerGroup.layers.forEach(layer => {
419
- if (layer.visible != visible) {
420
- //Toggle layer in map
421
- this._layerHelper.toggleLayerInMap(this.map, layer);
422
- // Toggle layer in all groups in the selector UI
423
- this.filteredLayerGroups.forEach(group => {
424
- group.layers.forEach(l => {
425
- if (layer.id === l.id) {
426
- l.visible = !l.visible;
427
- }
600
+ if (visible && layerGroup.background) {
601
+ //Toggle background group on, apply failover logic
602
+ layerGroup.layers.forEach(layer => layer.visible = false); // Turn all off in selector to begin with
603
+ this._layerHelper.startFailoverForBackgroundGroup(this.map, layerGroup.id);
604
+ }
605
+ else {
606
+ layerGroup.layers.forEach(layer => {
607
+ if (layer.visible != visible) {
608
+ //Toggle layer in map
609
+ this._layerHelper.toggleLayerInMap(this.map, layer.id);
610
+ // Toggle layer in all groups in the selector UI
611
+ this.filteredLayerGroups.concat(this.filteredBackgroundLayerGroups).forEach(group => {
612
+ group.layers.forEach(l => {
613
+ if (layer.id === l.id) {
614
+ l.visible = !l.visible;
615
+ }
616
+ });
617
+ group.noOfVisibleLayers = group.layers.filter(l => l.visible).length;
618
+ group.visible = group.layers.some(l => l.visible);
619
+ group.expanded = group.layers.some(l => l.visible);
428
620
  });
429
- group.noOfVisibleLayers = group.layers.filter(l => l.visible).length;
430
- group.visible = group.layers.some(l => l.visible);
431
- group.expanded = group.layers.some(l => l.visible);
432
- });
433
- }
434
- });
621
+ }
622
+ });
623
+ }
435
624
  layerGroup.visible = visible;
436
625
  layerGroup.expanded = layerGroup.layers.some(l => l.visible);
437
626
  this._cacheProfileInfo();
438
627
  }
439
628
  toggleLayerSelector() {
440
- this.showSelector = !this.showSelector;
441
- if (this.showSelector) {
442
- this._addMapBodyControl();
443
- }
444
- else {
445
- this._removeMapBodyControl();
629
+ this.collapsed = !this.collapsed;
630
+ if (this.collapsed) {
631
+ this.searchText = '';
632
+ this.filterChanged();
446
633
  }
447
634
  }
448
635
  updateOpacity(layer) {
@@ -451,12 +638,16 @@ class LayerSelectorComponent {
451
638
  stopDrag(event) {
452
639
  event.stopPropagation(); // prevents drag container from activating
453
640
  }
641
+ filterChanged() {
642
+ this.filteredLayerGroups = this.setfilteredGroups().filter(gr => !gr.background);
643
+ this.filteredBackgroundLayerGroups = this.setfilteredGroups().filter(gr => gr.background);
644
+ }
454
645
  setfilteredGroups() {
455
646
  const text = this.searchText.toLowerCase();
456
647
  return this.allLayerGroups
457
648
  .map((g, idx) => {
458
649
  if (!text) {
459
- return { ...g, expanded: false, sortOrder: idx,
650
+ return { ...g, expanded: g.folded === false, sortOrder: idx,
460
651
  layers: g.layers.map(layer => ({ ...layer, opacity: 1 }))
461
652
  }; // collapsed by default
462
653
  }
@@ -470,17 +661,15 @@ class LayerSelectorComponent {
470
661
  })
471
662
  .filter(g => g.layers.length > 0)
472
663
  .sort((a, b) => {
473
- const aIsLast = this._namesToGoLast.includes(a.name) || a.layers.some(layer => layer.background);
474
- const bIsLast = this._namesToGoLast.includes(b.name) || b.layers.some(layer => layer.background);
475
- if (aIsLast && !bIsLast)
664
+ if (a.background && !b.background)
476
665
  return 1;
477
- if (!aIsLast && bIsLast)
666
+ if (!a.background && b.background)
478
667
  return -1;
479
668
  return a.sortOrder - b.sortOrder;
480
669
  });
481
670
  }
482
- dropGroup(event) {
483
- moveItemInArray(this.filteredLayerGroups, event.previousIndex, event.currentIndex);
671
+ dropGroup(event, groupArray) {
672
+ moveItemInArray(groupArray, event.previousIndex, event.currentIndex);
484
673
  this.updateGroupSortOrders();
485
674
  this._cacheProfileInfo();
486
675
  }
@@ -496,41 +685,57 @@ class LayerSelectorComponent {
496
685
  }
497
686
  clearSearchText() {
498
687
  this.searchText = '';
499
- this.filteredLayerGroups = this.setfilteredGroups();
688
+ this.filterChanged();
500
689
  this._cacheProfileInfo();
501
690
  }
502
691
  updateGroupSortOrders() {
503
692
  this.filteredLayerGroups.forEach((g, idx) => (g.sortOrder = idx));
693
+ this.filteredBackgroundLayerGroups.forEach((g, idx) => (g.sortOrder = idx));
504
694
  }
505
695
  updateLayerSortOrders(group) {
506
696
  group.layers.forEach((layer, idx) => (layer.sortOrder = idx));
507
697
  }
698
+ onDragEnded(event) {
699
+ const position = event.source.getFreeDragPosition();
700
+ this.dragPosition = position;
701
+ this._savePosition(position);
702
+ }
703
+ _savePosition(position) {
704
+ try {
705
+ localStorage.setItem(this.POSITION_STORAGE_KEY, JSON.stringify(position));
706
+ }
707
+ catch (error) {
708
+ console.error('Error saving position to localStorage:', error);
709
+ }
710
+ }
711
+ _loadPosition() {
712
+ try {
713
+ const savedPosition = localStorage.getItem(this.POSITION_STORAGE_KEY);
714
+ if (savedPosition) {
715
+ this.dragPosition = JSON.parse(savedPosition);
716
+ }
717
+ else if (this.profile) {
718
+ this.dragPosition = this._layoutService.loadInitialPositionFromProfile(this.POSITION_STORAGE_KEY, this.profile.layerSelectorInitialPosition);
719
+ }
720
+ }
721
+ catch (error) {
722
+ console.error('Error loading position from localStorage:', error);
723
+ this.dragPosition = { x: 0, y: 0 };
724
+ }
725
+ }
508
726
  _cacheProfileInfo() {
509
- const cacheItem = { profile: this.profile, cachedLayerGroups: this.filteredLayerGroups };
727
+ const cacheItem = { profile: this.profile, cachedLayerGroups: this.filteredLayerGroups.concat(this.filteredBackgroundLayerGroups) };
510
728
  localStorage.setItem(`${this._mapFilteredLayerGroupsKeyName}_${this.profile.id}`, JSON.stringify(cacheItem));
511
- localStorage.setItem(`${this._layerIdsToDisplayInMapKeyName}_${this.profile.id}`, this.filteredLayerGroups
512
- .flatMap(lg => lg.layers)
513
- .filter(lg => lg.visible)
514
- .map(lg => lg.id).join(','));
515
729
  // Reflect new order in map
516
730
  this._layerHelper.applyCachedLayersToDisplayInMap(this.map, this.profile.id);
517
731
  }
518
- _initializeMapIconControl() {
519
- this.map.removeControl(this._layerSelectorIconControl);
520
- if (this._layerSelectorIcon?.nativeElement) {
521
- const element = this._layerSelectorIcon.nativeElement;
522
- this._layerSelectorIconControl = new ol_control_Control({ element: element });
523
- this.map.addControl(this._layerSelectorIconControl);
524
- }
525
- }
526
- _addMapBodyControl() {
527
- this.map.removeControl(this._layerSelectorBodyControl);
528
- const element = this._layerSelectorBody.nativeElement;
529
- this._layerSelectorBodyControl = new ol_control_Control({ element: element });
530
- this.map.addControl(this._layerSelectorBodyControl);
531
- }
532
- _removeMapBodyControl() {
732
+ _initializeMapControl() {
533
733
  this.map.removeControl(this._layerSelectorBodyControl);
734
+ if (this._layerSelectorBody?.nativeElement) {
735
+ const element = this._layerSelectorBody.nativeElement;
736
+ this._layerSelectorBodyControl = new ol_control_Control({ element: element });
737
+ this.map.addControl(this._layerSelectorBodyControl);
738
+ }
534
739
  }
535
740
  _setVisibleLayersFromCache(cachedFilteredLayergroups) {
536
741
  this.allLayerGroups.forEach(group => group.layers.forEach(layer => {
@@ -539,16 +744,13 @@ class LayerSelectorComponent {
539
744
  }));
540
745
  }
541
746
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
542
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: LayerSelectorComponent, isStandalone: true, selector: "lib-layer-selector", inputs: { map: "map", profile: "profile", currentZoomLevel: "currentZoomLevel" }, viewQueries: [{ propertyName: "contentIcon", first: true, predicate: ["layerSelectorIcon"], descendants: true }, { propertyName: "contentBody", first: true, predicate: ["layerSelectorBody"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div #layerSelectorIcon id=\"layerSelector\" class=\"ol-unselectable ol-control layer-selector-icon\">\n <mat-icon (click)=\"toggleLayerSelector()\">layers</mat-icon>\n</div>\n<div #layerSelectorBody [class.display-none]=\"!showSelector\" class=\"layer-selector-body-wrapper\" cdkDrag cdkDragBoundary=\".map-container\">\n <div class=\"drag-handle-selector\" cdkDragHandle>\n <mat-icon>drag_indicator</mat-icon>\n </div>\n <div class=\"ol-unselectable ol-control layer-selector-body\">\n <div class=\"search-section\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Filtrer</mat-label>\n <input \n matInput \n type=\"text\" \n [(ngModel)]=\"searchText\" \n (ngModelChange)=\"filteredLayerGroups=setfilteredGroups()\"\n placeholder=\"Skriv for at filtrere...\"\n />\n </mat-form-field>\n <mat-icon (click)=\"clearSearchText()\">undo</mat-icon>\n </div>\n \n <div\n cdkDropList\n [cdkDropListData]=\"filteredLayerGroups\"\n (cdkDropListDropped)=\"dropGroup($event)\"\n class=\"item-list\">\n @for (group of filteredLayerGroups; track group.id; let gIndex = $index) {\n <div class=\"group\" cdkDrag cdkDragPreviewDisabled>\n <mat-expansion-panel [(expanded)]=\"group.expanded\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n @if (group.expanded) {\n <mat-icon>arrow_upward</mat-icon>\n }\n @if (!group.expanded) {\n <mat-icon>arrow_downward</mat-icon> \n }\n {{ group.name }} \n <mat-icon class=\"lightbulb\">lightbulb</mat-icon>\n ({{ group.noOfVisibleLayers }}/{{ group.layers.length }})\n @if (group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-on\">power</mat-icon>(t\u00E6nd)\n }\n @if (!group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-off\">power_off</mat-icon>(sluk)\n }\n </mat-panel-title>\n </mat-expansion-panel-header>\n <!-- This is only shown during drag -->\n <!-- <div *cdkDragPreview class=\"drag-preview\">\n {{ group.name }}\n </div> -->\n\n <!-- Placeholder to avoid jump -->\n <!-- <div *cdkDragPlaceholder class=\"drag-placeholder\"></div> -->\n\n <div\n cdkDropList\n [cdkDropListData]=\"group.layers\"\n (cdkDropListDropped)=\"dropLayer($event, group)\"\n class=\"item-list\">\n @for (layer of group.layers; track layer.id; let iIndex = $index) {\n <mat-expansion-panel expanded=\"false\" [disabled]=\"!layer.description\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" cdkDrag cdkDragPreviewDisabled>\n <div class=\"item-left\">\n <mat-icon class=\"drag-indicator\">drag_indicator</mat-icon>\n <span>{{ layer.name }}</span>\n </div>\n <div class=\"item-center\">\n @if (layer.maxZoom < currentZoomLevel || layer.minZoom > currentZoomLevel) {\n <mat-icon class=\"zoom-off\">browser_not_supported</mat-icon>\n <span class=\"icon-label\">(zoom)</span>\n }\n @if (layer.hasErrors) {\n <mat-icon class=\"zoom-off\">wifi_off</mat-icon>\n <span class=\"icon-label\">(fejle)</span>\n }\n </div>\n <div class=\"item-right\">\n @if (layer.visible) {\n <mat-icon (click)=\"toggleLayer($event, layer)\" class=\"power-on\">power</mat-icon>\n <span class=\"icon-label\">(t\u00E6nd)</span>\n }\n @if (!layer.visible) {\n <mat-icon (click)=\"toggleLayer($event, layer)\" class=\"power-off\">power_off</mat-icon>\n <span class=\"icon-label\">(sluk)</span>\n }\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"layer.opacity\"\n (click)=\"stopDrag($event)\"\n (input)=\"updateOpacity(layer)\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"item\">{{ layer.description }}</div>\n @if (showLegend) {\n <img [src]=\"legendUrl\" class=\"legend-thumbnail\"/>\n }\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n </div>\n }\n </div>\n </div>\n</div>", styles: ["::ng-deep .layer-selector-icon{position:absolute;left:auto;right:1em;bottom:4.5em;z-index:1000}::ng-deep .layer-selector-icon mat-icon{display:flex;justify-content:center;align-items:center;cursor:pointer;transition:all .3s ease;outline:none;border:0;height:40px;border-radius:5px;width:40px;background:color-mix(in srgb,#000 60%,transparent);box-shadow:#0000004d 0 1px 4px -1px;font-size:2em;color:#fff}::ng-deep .layer-selector-icon mat-icon:hover{box-shadow:0 4px 12px #0003;color:#fff}.legend-thumbnail{max-width:200px;max-height:200px;width:auto;height:auto;border:2px solid #dee2e6;border-radius:6px;padding:6px}.layer-selector-body-wrapper{position:absolute;left:auto;right:4em;bottom:.2em;z-index:1000;cursor:grab;max-width:calc(100vw - 8em)}.layer-selector-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}.layer-selector-body-wrapper .ol-control{border-radius:0}@media (max-width: 767px){.layer-selector-body-wrapper{right:.5em;bottom:4em;max-width:calc(100vw - 7em);left:.5em;width:calc(100vw - 7em)}}@media (min-width: 768px) and (max-width: 1024px){.layer-selector-body-wrapper{right:1em;bottom:4em;max-width:calc(100vw - 2em)}}.drag-handle-selector{display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,#000 60%,transparent);border-radius:4px 4px 0 0;padding:4px;cursor:grab;box-shadow:0 -2px 8px #0003}.drag-handle-selector mat-icon{color:#fff;font-size:20px;width:20px;height:20px}::ng-deep .layer-selector-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:color-mix(in srgb,#000 60%,transparent);box-shadow:0 4px 20px #00000026;width:480px;max-width:100%;max-height:660px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 767px){::ng-deep .layer-selector-body{width:100%;max-height:60vh}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body{width:100%;max-width:480px;max-height:50vh}}::ng-deep .layer-selector-body .search-section{display:flex;align-items:center;gap:6px;padding:8px 12px 6px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section{padding:6px 8px 4px;gap:4px}}::ng-deep .layer-selector-body .search-section mat-form-field{flex:1}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-text-field-wrapper{padding-bottom:0}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-wrapper{padding-bottom:0;margin-bottom:0}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-infix{padding-bottom:6px;min-height:auto}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline{background:#fff}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline-thick{color:#1976d2}::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:13px;padding:3px 0}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{color:#fff;font-weight:500;font-size:13px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-subscript-wrapper{height:0;margin-top:0}::ng-deep .layer-selector-body .search-section mat-icon{color:#fff;cursor:pointer;padding:6px;border-radius:4px;transition:all .2s ease;font-size:24px;width:24px;height:24px;display:flex;justify-content:center;align-items:center}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-icon{font-size:20px;width:20px;height:20px;padding:4px}}::ng-deep .layer-selector-body .search-section mat-icon:hover{color:#f9fafb}::ng-deep .layer-selector-body .item-list{flex:1;overflow-y:auto;padding:6px;max-height:660px;width:100%}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list{padding:4px;max-height:calc(60vh - 80px)}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body .item-list{max-height:calc(70vh - 80px)}}::ng-deep .layer-selector-body .item-list .group{margin-bottom:6px;overflow:hidden;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel{box-shadow:none!important;border-radius:0!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 11px;height:40px;background:color-mix(in srgb,#000 55%,transparent)}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 20px;height:36px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header:hover{background:color-mix(in srgb,#000 60%,transparent)!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{align-items:center;gap:6px;font-weight:600;color:#fff;font-size:13px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{gap:4px;font-size:11px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{color:#fff;font-size:16px;width:16px;height:16px;transition:transform .2s ease}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.lightbulb{color:#dfca0e}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-on{color:#4caf50}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-off{color:#f44336}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content{background:color-mix(in srgb,#000 40%,transparent)}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:4px 0}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:2px 0}}::ng-deep .layer-selector-body .item-list .group .item-list{padding:0;max-height:none}::ng-deep .layer-selector-body .item-list .group .item-list .item{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:6px;padding:8px 12px;background:transparent;transition:all .2s ease;color:#fff;cursor:grab}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item{gap:4px;padding:6px 8px}}::ng-deep .layer-selector-body .item-list .group .item-list .item:last-child{border-bottom:none}::ng-deep .layer-selector-body .item-list .group .item-list .item:hover{background-color:transparent;transition:all .2s ease}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left{display:flex;align-items:center;gap:3px;flex:1;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{flex:1;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{font-size:11px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center{display:flex;align-items:center;gap:2px;flex-shrink:0;justify-content:center;position:relative;left:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:11px;opacity:.8;margin-right:4px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:9px;margin-right:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right{display:flex;align-items:center;flex:1;justify-content:flex-end;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right .icon-label{font-size:11px;opacity:.8;margin-right:4px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right .icon-label{font-size:9px;margin-right:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:80px;height:4px;margin:0;flex-shrink:0}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:60px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon{flex-shrink:0}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{color:#fff;font-size:16px;width:16px;height:16px;cursor:grab}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{color:#4caf50;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on:hover{background:#4caf5033}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{color:#f44336;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off:hover{background:#f4433633}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{color:#f44336;font-size:16px;width:16px;height:16px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{font-size:14px;width:14px;height:14px}}::ng-deep .mat-expansion-indicator svg{fill:#fff!important}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 15px #00000026;background:#fff;padding:10px 12px}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.item-list.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.power-on{color:#4caf50}.power-off{color:#f44336}.display-none{display:none}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar{width:8px;border-radius:10px;border:5px solid transparent}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb{background:#0000004d;border:5px solid transparent;border-radius:10px;background-clip:padding-box}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb:hover{background:#0006;background-clip:padding-box;border:3px solid transparent}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] });
747
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: LayerSelectorComponent, isStandalone: true, selector: "lib-layer-selector", inputs: { map: "map", profile: "profile", currentZoomLevel: "currentZoomLevel" }, viewQueries: [{ propertyName: "contentBody", first: true, predicate: ["layerSelectorBody"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n #layerSelectorBody\n class=\"layer-selector-body-wrapper\"\n cdkDrag\n cdkDragBoundary=\".map-container\"\n [cdkDragFreeDragPosition]=\"layerSelectorDragPosition\"\n (cdkDragEnded)=\"onLayerSelectorDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n\n <div class=\"drag-handle-selector\" cdkDragHandle>\n <div class=\"drag-handle-content\">\n <div class=\"drag-handle-icons\">\n <mat-icon class=\"icon-left\">layers</mat-icon>\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleLayerSelector()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n </div>\n\n <div class=\"ol-unselectable ol-control layer-selector-body\" *ngIf=\"!collapsed\">\n <div class=\"search-section\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Filtrer</mat-label>\n <input \n matInput \n type=\"text\" \n [(ngModel)]=\"searchText\" \n (ngModelChange)=\"filteredLayerGroups=setfilteredGroups()\"\n placeholder=\"Skriv for at filtrere...\"\n />\n </mat-form-field>\n <mat-icon (click)=\"clearSearchText()\">undo</mat-icon>\n </div>\n <div *ngIf=\"filteredLayerGroups && filteredLayerGroups.length > 0\" class=\"group-header\">Mine Temaer</div>\n <div\n cdkDropList\n [cdkDropListData]=\"filteredLayerGroups\"\n (cdkDropListDropped)=\"dropGroup($event, filteredLayerGroups)\"\n class=\"item-list\">\n @for (group of filteredLayerGroups; track group.id; let gIndex = $index) {\n <div class=\"group\" cdkDrag cdkDragPreviewDisabled>\n <mat-expansion-panel [(expanded)]=\"group.expanded\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n @if (group.expanded) {\n <mat-icon>arrow_upward</mat-icon>\n }\n @if (!group.expanded) {\n <mat-icon>arrow_downward</mat-icon> \n }\n {{ group.name }} \n <mat-icon class=\"lightbulb\">lightbulb</mat-icon>\n ({{ group.noOfVisibleLayers }}/{{ group.layers.length }})\n @if (group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon> \n }\n @if (!group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div\n cdkDropList\n [cdkDropListData]=\"group.layers\"\n (cdkDropListDropped)=\"dropLayer($event, group)\"\n class=\"item-list\">\n @for (layer of group.layers; track layer.id; let iIndex = $index) {\n <mat-expansion-panel expanded=\"false\" [disabled]=\"!layer.description\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" cdkDrag cdkDragPreviewDisabled>\n <div class=\"item-left\">\n <mat-icon class=\"drag-indicator\">drag_indicator</mat-icon>\n <span>{{ layer.name }}</span>\n </div>\n <div class=\"item-center\">\n @if (layer.maxZoom < currentZoomLevel || layer.minZoom > currentZoomLevel) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Zoom\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">browser_not_supported\n </mat-icon>\n }\n @if (layer.hasErrors) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Fejl\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">priority_high\n </mat-icon>\n\n }\n </div>\n <div class=\"item-right\">\n @if (layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n @if (!layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"layer.opacity\"\n (click)=\"stopDrag($event)\"\n (input)=\"updateOpacity(layer)\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"layer-description\">{{ layer.description }}</div>\n @if (showLegend) {\n <img [src]=\"legendUrl\" class=\"legend-thumbnail\"/>\n }\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n </div>\n }\n </div>\n <div *ngIf=\"filteredBackgroundLayerGroups && filteredBackgroundLayerGroups.length > 0\" class=\"group-header\">Baggrundskort</div>\n <div\n cdkDropList\n [cdkDropListData]=\"filteredBackgroundLayerGroups\"\n (cdkDropListDropped)=\"dropGroup($event, filteredBackgroundLayerGroups)\"\n class=\"item-list\">\n @for (group of filteredBackgroundLayerGroups; track group.id; let gIndex = $index) {\n <div class=\"group\" cdkDrag cdkDragPreviewDisabled>\n <mat-expansion-panel [(expanded)]=\"group.expanded\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n @if (group.expanded) {\n <mat-icon>arrow_upward</mat-icon>\n }\n @if (!group.expanded) {\n <mat-icon>arrow_downward</mat-icon> \n }\n {{ group.name }} \n <mat-icon class=\"lightbulb\">lightbulb</mat-icon>\n ({{ group.noOfVisibleLayers }}/{{ group.layers.length }})\n @if (group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon> \n }\n @if (!group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div\n cdkDropList\n [cdkDropListData]=\"group.layers\"\n (cdkDropListDropped)=\"dropLayer($event, group)\"\n class=\"item-list\">\n @for (layer of group.layers; track layer.id; let iIndex = $index) {\n <mat-expansion-panel expanded=\"false\" [disabled]=\"!layer.description\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" cdkDrag cdkDragPreviewDisabled>\n <div class=\"item-left\">\n <mat-icon class=\"drag-indicator\">drag_indicator</mat-icon>\n <span>{{ layer.name }}</span>\n </div>\n <div class=\"item-center\">\n @if (layer.maxZoom < currentZoomLevel || layer.minZoom > currentZoomLevel) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Zoom\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">browser_not_supported\n </mat-icon>\n }\n @if (layer.hasErrors) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Fejl\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">priority_high\n </mat-icon>\n\n }\n </div>\n <div class=\"item-right\">\n @if (layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n @if (!layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"layer.opacity\"\n (click)=\"stopDrag($event)\"\n (input)=\"updateOpacity(layer)\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"layer-description\">{{ layer.description }}</div>\n @if (showLegend) {\n <img [src]=\"legendUrl\" class=\"legend-thumbnail\"/>\n }\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n </div>\n }\n </div>\n </div>\n</div>", styles: [".legend-thumbnail{max-width:200px;max-height:200px;width:auto;height:auto;border:2px solid #dee2e6;border-radius:6px;padding:6px}.layer-selector-body-wrapper{position:absolute;left:1em;top:10em;z-index:1000;cursor:grab;max-width:calc(100vw - 8em);display:flex;flex-direction:column}.layer-selector-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons{justify-content:space-between}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons .icon-left{order:1;margin-right:auto}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons .drag-indicator-right{order:1}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons .toggle-icon{order:3}.layer-selector-body-wrapper.collapsed .layer-selector-body{display:none}.layer-selector-body-wrapper.collapsed .drag-handle-icons{justify-content:flex-end}.layer-selector-body-wrapper.collapsed .drag-handle-icons .icon-left{order:1;margin-right:0}.layer-selector-body-wrapper.collapsed .drag-handle-icons .drag-indicator-right{order:1}.layer-selector-body-wrapper.collapsed .drag-handle-icons .toggle-icon{order:3}.layer-selector-body-wrapper .ol-control{border-radius:0}.layer-selector-body-wrapper .group-header{color:#fff;padding:0 14px}@media (max-width: 767px){.layer-selector-body-wrapper{right:.5em;bottom:4em;max-width:calc(100vw - 7em);left:.5em;width:calc(100vw - 7em)}}@media (min-width: 768px) and (max-width: 1024px){.layer-selector-body-wrapper{right:3.5em;bottom:.5em;max-width:calc(100vw - 2em)}}.drag-handle-selector{display:flex;justify-content:flex-end;border-radius:5px 5px 0 0;padding:5px;cursor:grab;background:#292a2d}.drag-handle-content{display:flex;align-items:center;width:100%}.drag-handle-icons{display:flex;align-items:center;gap:16px;width:100%}.drag-handle-icons mat-icon{color:#fff;font-size:18px;width:18px;height:18px;cursor:pointer;transition:all .2s ease;display:flex;align-items:center;justify-content:center;flex-shrink:0}.drag-handle-icons mat-icon:first-child{cursor:grab}.drag-indicator-right{cursor:grab}.toggle-icon:hover{color:#fff;background:#ffffff26}::ng-deep .layer-selector-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:#292a2d;box-shadow:0 4px 20px #00000026;width:480px;max-width:100%;max-height:317px;min-height:120px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 767px){::ng-deep .layer-selector-body{width:100%;max-height:70vh;min-height:140px}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body{width:100%;max-width:445px;max-height:76vh;min-height:110px}}::ng-deep .layer-selector-body .search-section{display:flex;align-items:center;gap:6px;padding:8px 12px 6px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section{padding:6px 8px 4px;gap:4px}}::ng-deep .layer-selector-body .search-section mat-form-field{flex:1}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-text-field-wrapper{padding-bottom:0;background:#000}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-wrapper{padding-bottom:0;margin-bottom:0}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-infix{padding-bottom:6px;min-height:auto}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline{background:#fff}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline-thick{color:#1976d2}::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:13px;padding:3px 0}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{color:#fff;font-weight:500;font-size:13px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-subscript-wrapper{height:0;margin-top:0}::ng-deep .layer-selector-body .search-section mat-icon{color:#fff;cursor:pointer;padding:6px;border-radius:4px;transition:all .2s ease;font-size:24px;width:24px;height:24px;display:flex;justify-content:center;align-items:center}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-icon{font-size:20px;width:20px;height:20px;padding:4px}}::ng-deep .layer-selector-body .search-section mat-icon:hover{color:#f9fafb}::ng-deep .layer-selector-body .item-list{flex:1;overflow-y:auto;max-height:660px;width:100%}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list{padding:4px;max-height:calc(60vh - 80px)}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body .item-list{max-height:calc(77vh - 78px)}}::ng-deep .layer-selector-body .item-list .group{overflow:hidden;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel{box-shadow:none!important;border-radius:0!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 11px;height:40px;background:#000!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header:hover{background:#333!important}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 20px;height:36px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header:hover{background:color-mix(in srgb,#000 60%,transparent)!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{align-items:center;gap:6px;font-weight:600;color:#bdc1c3cc;font-size:17px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{gap:4px;font-size:11px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{color:#bdc1c3cc;font-size:16px;width:16px;height:16px;transition:transform .2s ease}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.lightbulb{color:#dfca0e}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-on{color:#4caf50}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-off{color:#f44336}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content{background:#4d4f55}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:4px 0;background:#000}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:2px 0}}::ng-deep .layer-selector-body .item-list .group .item-list{padding:0;max-height:none}::ng-deep .layer-selector-body .item-list .group .item-list .layer-description{font-size:13px;padding:0 31px;color:#fffc}::ng-deep .layer-selector-body .item-list .group .item-list .item{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:6px;background:transparent;transition:all .2s ease;color:#fff;cursor:grab;font-size:14px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item{gap:4px;padding:6px 8px}}::ng-deep .layer-selector-body .item-list .group .item-list .item:last-child{border-bottom:none}::ng-deep .layer-selector-body .item-list .group .item-list .item:hover{background-color:transparent;transition:all .2s ease}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left{display:flex;align-items:center;gap:3px;flex:1;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{font-size:13px;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;white-space:normal;flex:1;min-width:0;color:#fff}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{font-size:11px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center{display:flex;align-items:center;gap:2px;flex-shrink:0;justify-content:center;position:relative;left:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:11px;opacity:.8;margin-right:4px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:9px;margin-right:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right{display:flex;align-items:center;flex:1;justify-content:flex-end;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:80px;height:4px;margin:0;flex-shrink:0}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:60px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon{flex-shrink:0}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{color:#fff;font-size:16px;width:16px;height:16px;cursor:grab}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{color:#4caf50;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on:hover{background:#4caf5033}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{color:#f44336;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off:hover{background:#f4433633}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{color:#f44336;font-size:16px;width:16px;height:16px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{font-size:14px;width:14px;height:14px}}::ng-deep .mat-expansion-indicator svg{fill:#fff!important}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 15px #00000026;background:#fff;padding:10px 12px}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.item-list.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.power-on{color:#4caf50}.power-off{color:#f44336}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar{width:12px}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i6.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i6.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
543
748
  }
544
749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LayerSelectorComponent, decorators: [{
545
750
  type: Component,
546
751
  args: [{ selector: 'lib-layer-selector', imports: [MatFormFieldModule, CommonModule, MatIconModule, FormsModule, DragDropModule,
547
- MatExpansionModule, MatInputModule], template: "<div #layerSelectorIcon id=\"layerSelector\" class=\"ol-unselectable ol-control layer-selector-icon\">\n <mat-icon (click)=\"toggleLayerSelector()\">layers</mat-icon>\n</div>\n<div #layerSelectorBody [class.display-none]=\"!showSelector\" class=\"layer-selector-body-wrapper\" cdkDrag cdkDragBoundary=\".map-container\">\n <div class=\"drag-handle-selector\" cdkDragHandle>\n <mat-icon>drag_indicator</mat-icon>\n </div>\n <div class=\"ol-unselectable ol-control layer-selector-body\">\n <div class=\"search-section\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Filtrer</mat-label>\n <input \n matInput \n type=\"text\" \n [(ngModel)]=\"searchText\" \n (ngModelChange)=\"filteredLayerGroups=setfilteredGroups()\"\n placeholder=\"Skriv for at filtrere...\"\n />\n </mat-form-field>\n <mat-icon (click)=\"clearSearchText()\">undo</mat-icon>\n </div>\n \n <div\n cdkDropList\n [cdkDropListData]=\"filteredLayerGroups\"\n (cdkDropListDropped)=\"dropGroup($event)\"\n class=\"item-list\">\n @for (group of filteredLayerGroups; track group.id; let gIndex = $index) {\n <div class=\"group\" cdkDrag cdkDragPreviewDisabled>\n <mat-expansion-panel [(expanded)]=\"group.expanded\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n @if (group.expanded) {\n <mat-icon>arrow_upward</mat-icon>\n }\n @if (!group.expanded) {\n <mat-icon>arrow_downward</mat-icon> \n }\n {{ group.name }} \n <mat-icon class=\"lightbulb\">lightbulb</mat-icon>\n ({{ group.noOfVisibleLayers }}/{{ group.layers.length }})\n @if (group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-on\">power</mat-icon>(t\u00E6nd)\n }\n @if (!group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-off\">power_off</mat-icon>(sluk)\n }\n </mat-panel-title>\n </mat-expansion-panel-header>\n <!-- This is only shown during drag -->\n <!-- <div *cdkDragPreview class=\"drag-preview\">\n {{ group.name }}\n </div> -->\n\n <!-- Placeholder to avoid jump -->\n <!-- <div *cdkDragPlaceholder class=\"drag-placeholder\"></div> -->\n\n <div\n cdkDropList\n [cdkDropListData]=\"group.layers\"\n (cdkDropListDropped)=\"dropLayer($event, group)\"\n class=\"item-list\">\n @for (layer of group.layers; track layer.id; let iIndex = $index) {\n <mat-expansion-panel expanded=\"false\" [disabled]=\"!layer.description\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" cdkDrag cdkDragPreviewDisabled>\n <div class=\"item-left\">\n <mat-icon class=\"drag-indicator\">drag_indicator</mat-icon>\n <span>{{ layer.name }}</span>\n </div>\n <div class=\"item-center\">\n @if (layer.maxZoom < currentZoomLevel || layer.minZoom > currentZoomLevel) {\n <mat-icon class=\"zoom-off\">browser_not_supported</mat-icon>\n <span class=\"icon-label\">(zoom)</span>\n }\n @if (layer.hasErrors) {\n <mat-icon class=\"zoom-off\">wifi_off</mat-icon>\n <span class=\"icon-label\">(fejle)</span>\n }\n </div>\n <div class=\"item-right\">\n @if (layer.visible) {\n <mat-icon (click)=\"toggleLayer($event, layer)\" class=\"power-on\">power</mat-icon>\n <span class=\"icon-label\">(t\u00E6nd)</span>\n }\n @if (!layer.visible) {\n <mat-icon (click)=\"toggleLayer($event, layer)\" class=\"power-off\">power_off</mat-icon>\n <span class=\"icon-label\">(sluk)</span>\n }\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"layer.opacity\"\n (click)=\"stopDrag($event)\"\n (input)=\"updateOpacity(layer)\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"item\">{{ layer.description }}</div>\n @if (showLegend) {\n <img [src]=\"legendUrl\" class=\"legend-thumbnail\"/>\n }\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n </div>\n }\n </div>\n </div>\n</div>", styles: ["::ng-deep .layer-selector-icon{position:absolute;left:auto;right:1em;bottom:4.5em;z-index:1000}::ng-deep .layer-selector-icon mat-icon{display:flex;justify-content:center;align-items:center;cursor:pointer;transition:all .3s ease;outline:none;border:0;height:40px;border-radius:5px;width:40px;background:color-mix(in srgb,#000 60%,transparent);box-shadow:#0000004d 0 1px 4px -1px;font-size:2em;color:#fff}::ng-deep .layer-selector-icon mat-icon:hover{box-shadow:0 4px 12px #0003;color:#fff}.legend-thumbnail{max-width:200px;max-height:200px;width:auto;height:auto;border:2px solid #dee2e6;border-radius:6px;padding:6px}.layer-selector-body-wrapper{position:absolute;left:auto;right:4em;bottom:.2em;z-index:1000;cursor:grab;max-width:calc(100vw - 8em)}.layer-selector-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}.layer-selector-body-wrapper .ol-control{border-radius:0}@media (max-width: 767px){.layer-selector-body-wrapper{right:.5em;bottom:4em;max-width:calc(100vw - 7em);left:.5em;width:calc(100vw - 7em)}}@media (min-width: 768px) and (max-width: 1024px){.layer-selector-body-wrapper{right:1em;bottom:4em;max-width:calc(100vw - 2em)}}.drag-handle-selector{display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,#000 60%,transparent);border-radius:4px 4px 0 0;padding:4px;cursor:grab;box-shadow:0 -2px 8px #0003}.drag-handle-selector mat-icon{color:#fff;font-size:20px;width:20px;height:20px}::ng-deep .layer-selector-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:color-mix(in srgb,#000 60%,transparent);box-shadow:0 4px 20px #00000026;width:480px;max-width:100%;max-height:660px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 767px){::ng-deep .layer-selector-body{width:100%;max-height:60vh}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body{width:100%;max-width:480px;max-height:50vh}}::ng-deep .layer-selector-body .search-section{display:flex;align-items:center;gap:6px;padding:8px 12px 6px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section{padding:6px 8px 4px;gap:4px}}::ng-deep .layer-selector-body .search-section mat-form-field{flex:1}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-text-field-wrapper{padding-bottom:0}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-wrapper{padding-bottom:0;margin-bottom:0}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-infix{padding-bottom:6px;min-height:auto}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline{background:#fff}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline-thick{color:#1976d2}::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:13px;padding:3px 0}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{color:#fff;font-weight:500;font-size:13px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-subscript-wrapper{height:0;margin-top:0}::ng-deep .layer-selector-body .search-section mat-icon{color:#fff;cursor:pointer;padding:6px;border-radius:4px;transition:all .2s ease;font-size:24px;width:24px;height:24px;display:flex;justify-content:center;align-items:center}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-icon{font-size:20px;width:20px;height:20px;padding:4px}}::ng-deep .layer-selector-body .search-section mat-icon:hover{color:#f9fafb}::ng-deep .layer-selector-body .item-list{flex:1;overflow-y:auto;padding:6px;max-height:660px;width:100%}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list{padding:4px;max-height:calc(60vh - 80px)}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body .item-list{max-height:calc(70vh - 80px)}}::ng-deep .layer-selector-body .item-list .group{margin-bottom:6px;overflow:hidden;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel{box-shadow:none!important;border-radius:0!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 11px;height:40px;background:color-mix(in srgb,#000 55%,transparent)}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 20px;height:36px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header:hover{background:color-mix(in srgb,#000 60%,transparent)!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{align-items:center;gap:6px;font-weight:600;color:#fff;font-size:13px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{gap:4px;font-size:11px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{color:#fff;font-size:16px;width:16px;height:16px;transition:transform .2s ease}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.lightbulb{color:#dfca0e}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-on{color:#4caf50}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-off{color:#f44336}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content{background:color-mix(in srgb,#000 40%,transparent)}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:4px 0}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:2px 0}}::ng-deep .layer-selector-body .item-list .group .item-list{padding:0;max-height:none}::ng-deep .layer-selector-body .item-list .group .item-list .item{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:6px;padding:8px 12px;background:transparent;transition:all .2s ease;color:#fff;cursor:grab}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item{gap:4px;padding:6px 8px}}::ng-deep .layer-selector-body .item-list .group .item-list .item:last-child{border-bottom:none}::ng-deep .layer-selector-body .item-list .group .item-list .item:hover{background-color:transparent;transition:all .2s ease}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left{display:flex;align-items:center;gap:3px;flex:1;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{flex:1;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{font-size:11px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center{display:flex;align-items:center;gap:2px;flex-shrink:0;justify-content:center;position:relative;left:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:11px;opacity:.8;margin-right:4px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:9px;margin-right:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right{display:flex;align-items:center;flex:1;justify-content:flex-end;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right .icon-label{font-size:11px;opacity:.8;margin-right:4px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right .icon-label{font-size:9px;margin-right:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:80px;height:4px;margin:0;flex-shrink:0}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:60px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon{flex-shrink:0}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{color:#fff;font-size:16px;width:16px;height:16px;cursor:grab}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{color:#4caf50;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on:hover{background:#4caf5033}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{color:#f44336;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off:hover{background:#f4433633}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{color:#f44336;font-size:16px;width:16px;height:16px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{font-size:14px;width:14px;height:14px}}::ng-deep .mat-expansion-indicator svg{fill:#fff!important}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 15px #00000026;background:#fff;padding:10px 12px}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.item-list.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.power-on{color:#4caf50}.power-off{color:#f44336}.display-none{display:none}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar{width:8px;border-radius:10px;border:5px solid transparent}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb{background:#0000004d;border:5px solid transparent;border-radius:10px;background-clip:padding-box}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb:hover{background:#0006;background-clip:padding-box;border:3px solid transparent}\n"] }]
548
- }], propDecorators: { contentIcon: [{
549
- type: ViewChild,
550
- args: ['layerSelectorIcon', { static: false }]
551
- }], contentBody: [{
752
+ MatExpansionModule, MatInputModule, MatTooltipModule], template: "<div\n #layerSelectorBody\n class=\"layer-selector-body-wrapper\"\n cdkDrag\n cdkDragBoundary=\".map-container\"\n [cdkDragFreeDragPosition]=\"layerSelectorDragPosition\"\n (cdkDragEnded)=\"onLayerSelectorDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n\n <div class=\"drag-handle-selector\" cdkDragHandle>\n <div class=\"drag-handle-content\">\n <div class=\"drag-handle-icons\">\n <mat-icon class=\"icon-left\">layers</mat-icon>\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleLayerSelector()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n </div>\n\n <div class=\"ol-unselectable ol-control layer-selector-body\" *ngIf=\"!collapsed\">\n <div class=\"search-section\">\n <mat-form-field appearance=\"outline\" class=\"w-full\">\n <mat-label>Filtrer</mat-label>\n <input \n matInput \n type=\"text\" \n [(ngModel)]=\"searchText\" \n (ngModelChange)=\"filteredLayerGroups=setfilteredGroups()\"\n placeholder=\"Skriv for at filtrere...\"\n />\n </mat-form-field>\n <mat-icon (click)=\"clearSearchText()\">undo</mat-icon>\n </div>\n <div *ngIf=\"filteredLayerGroups && filteredLayerGroups.length > 0\" class=\"group-header\">Mine Temaer</div>\n <div\n cdkDropList\n [cdkDropListData]=\"filteredLayerGroups\"\n (cdkDropListDropped)=\"dropGroup($event, filteredLayerGroups)\"\n class=\"item-list\">\n @for (group of filteredLayerGroups; track group.id; let gIndex = $index) {\n <div class=\"group\" cdkDrag cdkDragPreviewDisabled>\n <mat-expansion-panel [(expanded)]=\"group.expanded\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n @if (group.expanded) {\n <mat-icon>arrow_upward</mat-icon>\n }\n @if (!group.expanded) {\n <mat-icon>arrow_downward</mat-icon> \n }\n {{ group.name }} \n <mat-icon class=\"lightbulb\">lightbulb</mat-icon>\n ({{ group.noOfVisibleLayers }}/{{ group.layers.length }})\n @if (group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon> \n }\n @if (!group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div\n cdkDropList\n [cdkDropListData]=\"group.layers\"\n (cdkDropListDropped)=\"dropLayer($event, group)\"\n class=\"item-list\">\n @for (layer of group.layers; track layer.id; let iIndex = $index) {\n <mat-expansion-panel expanded=\"false\" [disabled]=\"!layer.description\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" cdkDrag cdkDragPreviewDisabled>\n <div class=\"item-left\">\n <mat-icon class=\"drag-indicator\">drag_indicator</mat-icon>\n <span>{{ layer.name }}</span>\n </div>\n <div class=\"item-center\">\n @if (layer.maxZoom < currentZoomLevel || layer.minZoom > currentZoomLevel) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Zoom\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">browser_not_supported\n </mat-icon>\n }\n @if (layer.hasErrors) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Fejl\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">priority_high\n </mat-icon>\n\n }\n </div>\n <div class=\"item-right\">\n @if (layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n @if (!layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"layer.opacity\"\n (click)=\"stopDrag($event)\"\n (input)=\"updateOpacity(layer)\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"layer-description\">{{ layer.description }}</div>\n @if (showLegend) {\n <img [src]=\"legendUrl\" class=\"legend-thumbnail\"/>\n }\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n </div>\n }\n </div>\n <div *ngIf=\"filteredBackgroundLayerGroups && filteredBackgroundLayerGroups.length > 0\" class=\"group-header\">Baggrundskort</div>\n <div\n cdkDropList\n [cdkDropListData]=\"filteredBackgroundLayerGroups\"\n (cdkDropListDropped)=\"dropGroup($event, filteredBackgroundLayerGroups)\"\n class=\"item-list\">\n @for (group of filteredBackgroundLayerGroups; track group.id; let gIndex = $index) {\n <div class=\"group\" cdkDrag cdkDragPreviewDisabled>\n <mat-expansion-panel [(expanded)]=\"group.expanded\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n @if (group.expanded) {\n <mat-icon>arrow_upward</mat-icon>\n }\n @if (!group.expanded) {\n <mat-icon>arrow_downward</mat-icon> \n }\n {{ group.name }} \n <mat-icon class=\"lightbulb\">lightbulb</mat-icon>\n ({{ group.noOfVisibleLayers }}/{{ group.layers.length }})\n @if (group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon> \n }\n @if (!group.visible) {\n <mat-icon (click)=\"toggleGroup($event, group)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n </mat-panel-title>\n </mat-expansion-panel-header>\n\n <div\n cdkDropList\n [cdkDropListData]=\"group.layers\"\n (cdkDropListDropped)=\"dropLayer($event, group)\"\n class=\"item-list\">\n @for (layer of group.layers; track layer.id; let iIndex = $index) {\n <mat-expansion-panel expanded=\"false\" [disabled]=\"!layer.description\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" cdkDrag cdkDragPreviewDisabled>\n <div class=\"item-left\">\n <mat-icon class=\"drag-indicator\">drag_indicator</mat-icon>\n <span>{{ layer.name }}</span>\n </div>\n <div class=\"item-center\">\n @if (layer.maxZoom < currentZoomLevel || layer.minZoom > currentZoomLevel) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Zoom\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">browser_not_supported\n </mat-icon>\n }\n @if (layer.hasErrors) {\n <mat-icon class=\"zoom-off\" \n matTooltip=\"Fejl\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">priority_high\n </mat-icon>\n\n }\n </div>\n <div class=\"item-right\">\n @if (layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-on\" \n matTooltip=\"T\u00E6nd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n @if (!layer.visible) {\n <mat-icon (click)=\"toggleLayer(layer.id, $event)\" class=\"power-off\"\n matTooltip=\"Sluk\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"above\">power_settings_new\n </mat-icon>\n }\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"layer.opacity\"\n (click)=\"stopDrag($event)\"\n (input)=\"updateOpacity(layer)\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"layer-description\">{{ layer.description }}</div>\n @if (showLegend) {\n <img [src]=\"legendUrl\" class=\"legend-thumbnail\"/>\n }\n </mat-expansion-panel>\n }\n </div>\n </mat-expansion-panel>\n </div>\n }\n </div>\n </div>\n</div>", styles: [".legend-thumbnail{max-width:200px;max-height:200px;width:auto;height:auto;border:2px solid #dee2e6;border-radius:6px;padding:6px}.layer-selector-body-wrapper{position:absolute;left:1em;top:10em;z-index:1000;cursor:grab;max-width:calc(100vw - 8em);display:flex;flex-direction:column}.layer-selector-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons{justify-content:space-between}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons .icon-left{order:1;margin-right:auto}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons .drag-indicator-right{order:1}.layer-selector-body-wrapper:not(.collapsed) .drag-handle-icons .toggle-icon{order:3}.layer-selector-body-wrapper.collapsed .layer-selector-body{display:none}.layer-selector-body-wrapper.collapsed .drag-handle-icons{justify-content:flex-end}.layer-selector-body-wrapper.collapsed .drag-handle-icons .icon-left{order:1;margin-right:0}.layer-selector-body-wrapper.collapsed .drag-handle-icons .drag-indicator-right{order:1}.layer-selector-body-wrapper.collapsed .drag-handle-icons .toggle-icon{order:3}.layer-selector-body-wrapper .ol-control{border-radius:0}.layer-selector-body-wrapper .group-header{color:#fff;padding:0 14px}@media (max-width: 767px){.layer-selector-body-wrapper{right:.5em;bottom:4em;max-width:calc(100vw - 7em);left:.5em;width:calc(100vw - 7em)}}@media (min-width: 768px) and (max-width: 1024px){.layer-selector-body-wrapper{right:3.5em;bottom:.5em;max-width:calc(100vw - 2em)}}.drag-handle-selector{display:flex;justify-content:flex-end;border-radius:5px 5px 0 0;padding:5px;cursor:grab;background:#292a2d}.drag-handle-content{display:flex;align-items:center;width:100%}.drag-handle-icons{display:flex;align-items:center;gap:16px;width:100%}.drag-handle-icons mat-icon{color:#fff;font-size:18px;width:18px;height:18px;cursor:pointer;transition:all .2s ease;display:flex;align-items:center;justify-content:center;flex-shrink:0}.drag-handle-icons mat-icon:first-child{cursor:grab}.drag-indicator-right{cursor:grab}.toggle-icon:hover{color:#fff;background:#ffffff26}::ng-deep .layer-selector-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:#292a2d;box-shadow:0 4px 20px #00000026;width:480px;max-width:100%;max-height:317px;min-height:120px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 767px){::ng-deep .layer-selector-body{width:100%;max-height:70vh;min-height:140px}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body{width:100%;max-width:445px;max-height:76vh;min-height:110px}}::ng-deep .layer-selector-body .search-section{display:flex;align-items:center;gap:6px;padding:8px 12px 6px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section{padding:6px 8px 4px;gap:4px}}::ng-deep .layer-selector-body .search-section mat-form-field{flex:1}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-text-field-wrapper{padding-bottom:0;background:#000}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-wrapper{padding-bottom:0;margin-bottom:0}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-infix{padding-bottom:6px;min-height:auto}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline{background:#fff}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-outline-thick{color:#1976d2}::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:13px;padding:3px 0}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field input{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{color:#fff;font-weight:500;font-size:13px}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-form-field .mat-form-field-label{font-size:12px}}::ng-deep .layer-selector-body .search-section mat-form-field .mat-mdc-form-field-subscript-wrapper{height:0;margin-top:0}::ng-deep .layer-selector-body .search-section mat-icon{color:#fff;cursor:pointer;padding:6px;border-radius:4px;transition:all .2s ease;font-size:24px;width:24px;height:24px;display:flex;justify-content:center;align-items:center}@media (max-width: 767px){::ng-deep .layer-selector-body .search-section mat-icon{font-size:20px;width:20px;height:20px;padding:4px}}::ng-deep .layer-selector-body .search-section mat-icon:hover{color:#f9fafb}::ng-deep .layer-selector-body .item-list{flex:1;overflow-y:auto;max-height:660px;width:100%}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list{padding:4px;max-height:calc(60vh - 80px)}}@media (min-width: 768px) and (max-width: 1024px){::ng-deep .layer-selector-body .item-list{max-height:calc(77vh - 78px)}}::ng-deep .layer-selector-body .item-list .group{overflow:hidden;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel{box-shadow:none!important;border-radius:0!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 11px;height:40px;background:#000!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header:hover{background:#333!important}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header{padding:0 20px;height:36px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header:hover{background:color-mix(in srgb,#000 60%,transparent)!important}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{align-items:center;gap:6px;font-weight:600;color:#bdc1c3cc;font-size:17px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title{gap:4px;font-size:11px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{color:#bdc1c3cc;font-size:16px;width:16px;height:16px;transition:transform .2s ease}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.lightbulb{color:#dfca0e}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-on{color:#4caf50}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel mat-expansion-panel-header mat-panel-title mat-icon.power-off{color:#f44336}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content{background:#4d4f55}::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:4px 0;background:#000}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group mat-expansion-panel .mat-expansion-panel-content .mat-expansion-panel-body{padding:2px 0}}::ng-deep .layer-selector-body .item-list .group .item-list{padding:0;max-height:none}::ng-deep .layer-selector-body .item-list .group .item-list .layer-description{font-size:13px;padding:0 31px;color:#fffc}::ng-deep .layer-selector-body .item-list .group .item-list .item{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:6px;background:transparent;transition:all .2s ease;color:#fff;cursor:grab;font-size:14px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item{gap:4px;padding:6px 8px}}::ng-deep .layer-selector-body .item-list .group .item-list .item:last-child{border-bottom:none}::ng-deep .layer-selector-body .item-list .group .item-list .item:hover{background-color:transparent;transition:all .2s ease}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left{display:flex;align-items:center;gap:3px;flex:1;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{font-size:13px;word-wrap:break-word;word-break:break-word;overflow-wrap:break-word;white-space:normal;flex:1;min-width:0;color:#fff}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-left span{font-size:11px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center{display:flex;align-items:center;gap:2px;flex-shrink:0;justify-content:center;position:relative;left:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:11px;opacity:.8;margin-right:4px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-center .icon-label{font-size:9px;margin-right:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right{display:flex;align-items:center;flex:1;justify-content:flex-end;min-width:0}::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:80px;height:4px;margin:0;flex-shrink:0}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item .item-right input[type=range]{width:60px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon{flex-shrink:0}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{color:#fff;font-size:16px;width:16px;height:16px;cursor:grab}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.drag-indicator{font-size:14px;width:14px;height:14px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{color:#4caf50;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-on:hover{background:#4caf5033}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{color:#f44336;font-size:18px;width:18px;height:18px;cursor:pointer;padding:3px;border-radius:3px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off{font-size:16px;width:16px;height:16px;padding:2px}}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.power-off:hover{background:#f4433633}::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{color:#f44336;font-size:16px;width:16px;height:16px}@media (max-width: 767px){::ng-deep .layer-selector-body .item-list .group .item-list .item mat-icon.zoom-off{font-size:14px;width:14px;height:14px}}::ng-deep .mat-expansion-indicator svg{fill:#fff!important}.cdk-drag-preview{box-sizing:border-box;border-radius:4px;box-shadow:0 5px 15px #00000026;background:#fff;padding:10px 12px}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.item-list.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.power-on{color:#4caf50}.power-off{color:#f44336}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar{width:12px}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .layer-selector-body .item-list::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}\n"] }]
753
+ }], propDecorators: { contentBody: [{
552
754
  type: ViewChild,
553
755
  args: ['layerSelectorBody', { static: false }]
554
756
  }], map: [{
@@ -618,6 +820,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
618
820
  }] });
619
821
 
620
822
  class CurrentItemsService {
823
+ constructor() {
824
+ const dt = new Date();
825
+ console.log("🚀 ~ CurrentItemsService ~ constructor ~ dt:", dt);
826
+ }
621
827
  _map;
622
828
  get map() { return this._map; }
623
829
  set map(value) {
@@ -625,25 +831,26 @@ class CurrentItemsService {
625
831
  this._mapSubject.next(value);
626
832
  }
627
833
  profile;
628
- _mapSubject = new ReplaySubject();
834
+ _mapSubject = new ReplaySubject(1);
629
835
  map$ = this._mapSubject.asObservable();
836
+ gisKomponentSettings;
630
837
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CurrentItemsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
631
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CurrentItemsService, providedIn: 'root' });
838
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CurrentItemsService });
632
839
  }
633
840
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CurrentItemsService, decorators: [{
634
- type: Injectable,
635
- args: [{
636
- providedIn: 'root'
637
- }]
638
- }] });
841
+ type: Injectable
842
+ }], ctorParameters: () => [] });
639
843
 
640
844
  class DrawLayerSourceService {
641
845
  _current = inject(CurrentItemsService);
642
846
  _features = new Subject;
643
847
  features$ = this._features.asObservable();
848
+ _selectedForHighlight = new Subject();
849
+ selectedForHighlight$ = this._selectedForHighlight.asObservable();
644
850
  source = new VectorSource();
645
851
  layer = new VectorLayer({
646
- source: this.source
852
+ source: this.source,
853
+ zIndex: 999
647
854
  });
648
855
  _disabled = false;
649
856
  constructor() {
@@ -673,6 +880,16 @@ class DrawLayerSourceService {
673
880
  return newFeature;
674
881
  });
675
882
  }
883
+ get allCleanedFeatures() {
884
+ return this.allFeatures.map(f => {
885
+ f.unset('_centerpoint');
886
+ f.unset('_parentPolyId');
887
+ f.unset('LOCKED');
888
+ f.unset('STYLENAME');
889
+ f.unset('showlabelfrom');
890
+ return f;
891
+ });
892
+ }
676
893
  remove(id) {
677
894
  const feature = this.source.getFeatureById(id);
678
895
  if (feature) {
@@ -690,25 +907,26 @@ class DrawLayerSourceService {
690
907
  this._features.next(features);
691
908
  this._disabled = false;
692
909
  }
910
+ setFeaturesSelectedForHighlight(features) {
911
+ if (!Array.isArray(features)) {
912
+ this.setFeaturesSelectedForHighlight([features]);
913
+ return;
914
+ }
915
+ this._selectedForHighlight.next(features);
916
+ }
693
917
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DrawLayerSourceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
694
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DrawLayerSourceService, providedIn: 'root' });
918
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DrawLayerSourceService });
695
919
  }
696
920
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: DrawLayerSourceService, decorators: [{
697
- type: Injectable,
698
- args: [{
699
- providedIn: 'root'
700
- }]
921
+ type: Injectable
701
922
  }], ctorParameters: () => [] });
702
923
 
703
924
  class FeatureHelperService {
704
925
  FEATURE_TYPE_ID = 'TypeId';
705
926
  FEATURE_LOCKED = 'LOCKED';
706
- _idCounter = 0;
927
+ _idCounter = 1;
707
928
  isLocked = (feature) => feature.get(this.FEATURE_LOCKED) === 'true';
708
929
  lock(feature) { feature.set(this.FEATURE_LOCKED, 'true'); }
709
- constructor() {
710
- console.log('FeatureHelperService, constructor');
711
- }
712
930
  setTypeId(feature, typeId) {
713
931
  feature.set(this.FEATURE_TYPE_ID, typeId);
714
932
  }
@@ -724,7 +942,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
724
942
  args: [{
725
943
  providedIn: 'root'
726
944
  }]
727
- }], ctorParameters: () => [] });
945
+ }] });
728
946
 
729
947
  class UndoRedoService {
730
948
  _drawlayerSourceService;
@@ -786,13 +1004,10 @@ class UndoRedoService {
786
1004
  this._drawlayerSourceService.setFeatures(features);
787
1005
  }
788
1006
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: UndoRedoService, deps: [{ token: DrawLayerSourceService }], target: i0.ɵɵFactoryTarget.Injectable });
789
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: UndoRedoService, providedIn: 'root' });
1007
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: UndoRedoService });
790
1008
  }
791
1009
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: UndoRedoService, decorators: [{
792
- type: Injectable,
793
- args: [{
794
- providedIn: 'root'
795
- }]
1010
+ type: Injectable
796
1011
  }], ctorParameters: () => [{ type: DrawLayerSourceService }] });
797
1012
 
798
1013
  class GeometrySplitService {
@@ -812,8 +1027,25 @@ class GeometrySplitService {
812
1027
  const bufferedLine = buffer(lineGeoJSON, bufferMeters, { units: 'meters' });
813
1028
  // Move back to 25832
814
1029
  bufferedLine.geometry.coordinates = bufferedLine.geometry.coordinates.map(a => a.map(c => proj4('EPSG:4326', 'EPSG:25832', c)));
815
- const overlappingFeatures = vectorSource.getFeatures().filter(f => f.getGeometry()?.intersectsExtent(lineFeature.getGeometry()?.getExtent()));
816
- overlappingFeatures.forEach((feature) => {
1030
+ const overlappingFeatures = vectorSource.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(lineFeature.getGeometry()?.getExtent()));
1031
+ overlappingFeatures.filter(f => f.getGeometry()?.getType() === 'LineString').forEach(feature => {
1032
+ const linestringFeatureObject = this.geoJsonFormat.writeFeatureObject(feature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1033
+ const lineFeatureObject = this.geoJsonFormat.writeFeatureObject(lineFeature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1034
+ if (lineIntersect(lineFeatureObject, linestringFeatureObject)) {
1035
+ const lines = lineSplit(linestringFeatureObject, lineFeatureObject);
1036
+ const newFeatures = lines.features.map(lineSplitFeature => {
1037
+ const newFeature = feature.clone();
1038
+ this._featureHelper.setId(newFeature);
1039
+ const newGeometry = this.geoJsonFormat.readGeometry(lineSplitFeature.geometry, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1040
+ newFeature.setGeometry(newGeometry);
1041
+ return newFeature;
1042
+ });
1043
+ vectorSource.addFeatures(newFeatures);
1044
+ vectorSource.removeFeature(feature);
1045
+ }
1046
+ });
1047
+ // Handle polygons
1048
+ overlappingFeatures.filter(f => f.getGeometry()?.getType() === 'Polygon').forEach((feature) => {
817
1049
  const overlappingFeatureGeoJson = this.geoJsonFormat.writeFeatureObject(feature);
818
1050
  const features = featureCollection([overlappingFeatureGeoJson, bufferedLine]);
819
1051
  const clipped = difference(features);
@@ -858,61 +1090,103 @@ class ZoomService {
858
1090
  });
859
1091
  }
860
1092
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ZoomService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
861
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ZoomService, providedIn: 'root' });
1093
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ZoomService });
862
1094
  }
863
1095
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ZoomService, decorators: [{
864
- type: Injectable,
865
- args: [{
866
- providedIn: 'root'
867
- }]
1096
+ type: Injectable
868
1097
  }] });
869
1098
 
870
- class highlightService {
871
- constructor() {
872
- console.log('highlightservice, constructor');
873
- }
1099
+ class HighlightService {
1100
+ _wktFormat = new WKT();
874
1101
  _zoomService = inject(ZoomService);
875
- _style = new Style({
876
- stroke: new Stroke({
877
- color: '#0ff',
878
- width: 3
879
- }),
880
- fill: new Fill({
881
- color: 'rgba(0, 0, 255, 0.3)'
882
- }),
883
- image: new Circle({
884
- fill: new Fill({ color: [175, 202, 10, 1] }),
885
- stroke: new Stroke({ color: [40, 112, 133, 1], width: 2 }),
886
- radius: 5
887
- })
888
- });
1102
+ _current = inject(CurrentItemsService);
1103
+ fadeStart = '_fadeStart';
1104
+ fadeEnd = '_fadeEnd';
889
1105
  _highlightSource = new Vector({ wrapX: false });
890
- highlightLayer = new VectorLayer({
1106
+ _highlightLayer = new VectorLayer({
891
1107
  source: this._highlightSource,
892
- style: this._style
1108
+ zIndex: 999
893
1109
  });
1110
+ constructor() {
1111
+ this._current.map$.subscribe({
1112
+ next: map => {
1113
+ map.addLayer(this._highlightLayer);
1114
+ }
1115
+ });
1116
+ // To make the highlight fade out, I reset the style every 20 ms
1117
+ interval(20).subscribe({
1118
+ next: () => {
1119
+ this._highlightSource.getFeatures().forEach(f => {
1120
+ const start = f.get(this.fadeStart);
1121
+ const end = f.get(this.fadeEnd);
1122
+ const now = performance.now();
1123
+ const t = Math.min(1, (now - start) / (end - start));
1124
+ // Adding and removing forces a redraw of the features
1125
+ if (t < 1) {
1126
+ const progress = 1 - easeOut(t);
1127
+ const newStyle = this._getFadedStyle(progress);
1128
+ const newFeature = f.clone();
1129
+ newFeature.setStyle(newStyle);
1130
+ this._highlightSource.addFeature(newFeature);
1131
+ }
1132
+ queueMicrotask(() => this._highlightSource.removeFeature(f));
1133
+ });
1134
+ }
1135
+ });
1136
+ }
894
1137
  clear() {
895
1138
  this._highlightSource.clear();
896
1139
  }
897
1140
  highlight(feature, zoomToFeatures = true) {
898
1141
  if (!Array.isArray(feature)) {
899
- this.highlight([feature]);
1142
+ this.highlight([this._ensureFeature(feature)]);
900
1143
  return;
901
1144
  }
902
- // If the feature has a style, it overwrites the style of the layer, so I create a new feature without properties or style.
903
- this._highlightSource.addFeatures(feature.map(f => new Feature({ geometry: f.getGeometry() })));
1145
+ const newFeatures = feature.map(f => {
1146
+ const newFeature = new Feature({ geometry: f.getGeometry(), style: this._getFadedStyle(1) });
1147
+ const now = performance.now();
1148
+ newFeature.set(this.fadeStart, now);
1149
+ const fadeMs = this._current.gisKomponentSettings?.highlightFadeTimerMs ?? 2000;
1150
+ newFeature.set(this.fadeEnd, now + fadeMs); // set the style to fade (and be removed) to two seconds
1151
+ return newFeature;
1152
+ });
1153
+ this._highlightSource.addFeatures(newFeatures);
904
1154
  if (zoomToFeatures) {
905
1155
  this._zoomService.zoomToFeatures(feature);
906
1156
  }
907
1157
  }
908
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: highlightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
909
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: highlightService, providedIn: 'root' });
1158
+ _ensureFeature(feature) {
1159
+ if (typeof feature === 'string') {
1160
+ const newFeature = this._wktFormat.readFeature(feature, { dataProjection: 'EPSG:25832', featureProjection: 'EPSG:25832' });
1161
+ return newFeature;
1162
+ }
1163
+ else {
1164
+ return feature;
1165
+ }
1166
+ }
1167
+ _getFadedStyle(fade) {
1168
+ const newStyle = new Style({
1169
+ stroke: new Stroke({
1170
+ color: [0, 255, 255, fade],
1171
+ width: 3
1172
+ }),
1173
+ fill: new Fill({
1174
+ //color: 'rgba(0, 0, 255, 0.3)'
1175
+ color: [0, 0, 255, fade]
1176
+ }),
1177
+ image: new Circle({
1178
+ fill: new Fill({ color: [175, 202, 10, fade] }),
1179
+ stroke: new Stroke({ color: [40, 112, 133, fade], width: 2 }),
1180
+ radius: 5
1181
+ })
1182
+ });
1183
+ return newStyle;
1184
+ }
1185
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HighlightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1186
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HighlightService });
910
1187
  }
911
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: highlightService, decorators: [{
912
- type: Injectable,
913
- args: [{
914
- providedIn: 'root'
915
- }]
1188
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HighlightService, decorators: [{
1189
+ type: Injectable
916
1190
  }], ctorParameters: () => [] });
917
1191
 
918
1192
  class InteractionHelperService {
@@ -932,13 +1206,10 @@ class InteractionHelperService {
932
1206
  });
933
1207
  }
934
1208
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InteractionHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
935
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InteractionHelperService, providedIn: 'root' });
1209
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InteractionHelperService });
936
1210
  }
937
1211
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InteractionHelperService, decorators: [{
938
- type: Injectable,
939
- args: [{
940
- providedIn: 'root'
941
- }]
1212
+ type: Injectable
942
1213
  }] });
943
1214
 
944
1215
  class MergeFeaturesService {
@@ -950,6 +1221,9 @@ class MergeFeaturesService {
950
1221
  formatter = new GeoJSON();
951
1222
  _formatterOptions = { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' };
952
1223
  _selectFilter(f, typeId) {
1224
+ if (this._featureHelper.isLocked(f)) {
1225
+ return false;
1226
+ }
953
1227
  if (this._selectedMergeFeature) {
954
1228
  const previusFeatureObject = this.formatter.writeFeatureObject(this._selectedMergeFeature, this._formatterOptions);
955
1229
  const bufferedPreviouslySelected = buffer(previusFeatureObject, 5, { units: 'centimeters' });
@@ -994,13 +1268,10 @@ class MergeFeaturesService {
994
1268
  });
995
1269
  }
996
1270
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MergeFeaturesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
997
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MergeFeaturesService, providedIn: 'root' });
1271
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MergeFeaturesService });
998
1272
  }
999
1273
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MergeFeaturesService, decorators: [{
1000
- type: Injectable,
1001
- args: [{
1002
- providedIn: 'root'
1003
- }]
1274
+ type: Injectable
1004
1275
  }] });
1005
1276
 
1006
1277
  class IconsConstants {
@@ -1019,30 +1290,20 @@ class IconsConstants {
1019
1290
  static lineStringIconBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAA1BJREFUSEutVmtIk2EYXb/EWBDZ7UdQP6L+WZkQEfVDCNR/gWAQqCAKQhJJUSK0KV7nZdbWdLk5pyjeFbcpzsuWSzfRTQfS5ty8X0rNOW0OiTh970cbbqYu5+Dd923wnfOe5znveb5TjL+fysrKWIfDwXK5XFd3d3dPu///n2tQUNBOcHDwHJPJ5CclJQk8z1ZVVcmLioqgVqthNBphs9mOtcizBEMoFJJlpgkqKiriBALBsQA/G+eQ2rKOiE+b+GqZ9sKQSqUQiUSlDC6XO6vRaPwm0E3MIqtzFTdKnQjhbCFeYkKUeAIZ8jUvjJGREXA4HDuDzWb/MplMhxKQ3RV0f0eEyIGQfAceS80QDfRCbWyhF7knhL6lZbFYYJCvg2peplrBk2o7mDkbiJJMI7tL6wF1g7uvd/jLqB1c8sL6J4FsZAFPa+04l7eNh/xFvO0YhdLQfiCwm4CQE4V7N7uP4CbXietcB9LaDWgdVhwJulcF2cR5zjZIj9wk+wgYbPgN2mtsRbWuD2WaAYgGVegZa6MbTlx1IgQyvQzPOhcQLt3E/fJvKJB8RPOQDBc5To9lA1KgMMiQLLPg3vtFhL/WIvHVS/SMNtB9I+YgKgIiICUpUXYjJrsS0WkFyOZloc/QhNK+AYQLtgIn8LXp3t/EKMSNASmQUyWKU8zRPXgknkdJnRD9Y020SRIkRsTX/wiMoMMgR2znMm619CMscwXJ6eno1TegXttFu+lB+ebJEVxhLyIyhY3bpbO0i5Ib1kGCMKASEQWR7esIbVbhMmsJ9xOLIVXPHH6S/Tlo1V+UiKmx4kLhFsLqdhHaogJRkJqeB9/Q9FsBiQ0SH8Qd14p3kCFbQ/f4PBKU21QPjiCg4vq3xWKhpfkqeKcYpg8OCT4SgMR67hjQT874R0CNylWdTuchEKhUdN4zM9cQLd7wnErfSDeYZ5CitONu7QZVIiqD3uR4lYiMz9zcXCeDmp0fampqaIIzOVsI5f2kJ9aYybthvgSTUzbIhifxnNOM2BeFyMkvgtls9iiUSCTg8/lqei7zeDx7Y2PjviYdNfynpqwwGMah1eqg1+thtVppDAJOjWJXW1vbWc+bhVgslpPxSTofyCIY1EvEEDXwLxHwP1I4uvOad9ZNAAAAAElFTkSuQmCC';
1020
1291
  static printBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAxBJREFUeNqslctrJFUUxn+3Xv2o6q6uHjs9rWjC6CIiAQMtAV0JYhYuRFy4ExcJwb/CletBMgxITIib+QcmuBhEXEQN6mQhOEPbY3AIjGmTVPUz/arKdZFJd6e7egxtvk3VPbfu99W557vnCp5iY2Pjo0ql8lmz2Zxut9txJkAkEjmJxWKPLcu6tby8fBtAAGxubm4dHR29l8/ncRwHy7Im4ader+N5HoVCAaCwsrIyK9bW1j7udrtfLy4ucpXY3t6m2+1+ofi+//nCwgJXjfn5eRqNxieKruu5eDx+5QKmaeK6rq0BqmEYvQmv1qDkVScivZ5OkrLMCzFtcHBYrvLl3W+RwbOJpJQACCEukgmVpfffJpNKhgv89fchMoBXXnuDROoaJ7UKhd9+6s036zVqZZduuw1INN3AtG3MZBohwJcB+wdH4wUGfhHkKVKe9kLV40Nq1Spy+i1kdg4Ule7xIyp/fke72SR9/XkEAjmcVRj/owe/Xhi3ThrUqmVO8yvIzCxRDVQFGs4NRO51Wj/epO55JJz0CJd2meI1Kh4yl4fMLPNZhRcSClJCpS3ZeZIlePld6n9skXCckbXKZQQ6rRZyavYsGx/u7QV8s+dTakg0RULmVU4DnyAYdYd27ohn4XxbJfDwuF+Xonf23tv1EColTHUYeiSK+Ofh+A8OH6BqGqqmjgoMezn0VNoO4sl9ROn3kA53gFK8h2U7g7n0t0hR/rsM0bhJIuVQ2/0K+eKbPZtyXETsfU8sqmPa6dC12mUyAEimn0OPRKmX7tN5/MPZQTMMLMfBtO1+vYZqesGmM7kMmlDxx/SKmGkRMy2kBIHsV/+cTBVM5zLjBTKpJJ9++A4H7mTNLnfNxjbjIwIyCAKhqmcOSMSipF7qd8QgCFBVtfccFxsHpdVqua7r9gLDC87Hg/GwWNj1mUwmm4qu63eKxeKVXzi7u7tEIpGfBcDq6qqXzWZTc3NzDF4+k6DT6bCzs0O5XG7NzMzkejZYX1/f2t/fX5RSav9HQAjhT01N/WIYxgdLS0ulfwcAKTQlaNRcuW0AAAAASUVORK5CYII=';
1021
1292
  static drawBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAilJREFUSImt1UlsTXEUx/FPJyFNTEHQEE2R1FzRRpSILSsrFlaEjSWJhQUrC7GzEjVUY1gQImJadEHEEBIRFWNoqVjowlAxhFqc/0uf29e+tvpLXu5993/u93/O/wyXoWs0ZqT7eSgfBqNfTcUD/MBZ/MHmkYLPwWtcwRP0oBt1OIPa/4EvxAc0Y3aC38Fb/BIRLcKU4cDr0YUjKEUJ1os8vEibPcMB/EbNUOAr8CnBSzJrDSKqDenag1MpkobBwFfhM5oKwPO1JcG70IZvuFwMvhpfcbQInPB2tzi2HlFZa7BXlHRBeDdOiDPP13zsQWOB956jXeTje/rNyhqtTJ6fzsDX4n6eh9MLbLBMlOzzZHcwa1AvEnrev925Du+xXJzt7QLwnLbhC1pTBFW5hXn4iGsYlYmoHQtSROewfYANYGJyojXHmpAg91CZZ1iHV1iS4M24mnEgq3JcEONkbO5hjTjXTXmGC/A0bVKGk2L2DAQvTXZtmJRdPIw3KZrFeCwapkzMmZZ0359KREm/VLgAVOIhbiUPapNHLTiub7lm4U1iEM4cwM5OMVuq86I6VgRemuAdee8V1EaR5Nw03K9vL2RVJqLrVGTAzcYjjE//d4mkH9L/mKgQuekU34kBVYW7IsE7RP0uxTsxLrKfxDG4lOBzi8FzahQVcFFvL1SLnFwXzQPjcGOwng9Gk3FTJHGrqLCOkYLnVIF9+CminDWS8HxN01sIg9Zflop8Ez+7Xi0AAAAASUVORK5CYII=';
1293
+ static featureSearchIconBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAA59JREFUSEullm9MU1cYxi+fCNolRhQ+jkSjCRoTk0Y+NDMEMLEmbiMRNXMRTYDSCWE4smCMtPUPuoZYsjbFri0FUYONDqIwIm6jShw0ZULR9rYNLeg2zUTFoigJMc/ue/BWKi0U1+TJbU/vfX7nPe97z3uSuHefxsbGPeFwWDU9Pf2poGXieFJSEgdA/LngNTk5+XVKSsoDiURiKC4uNkZubmpq6qirq4PD4YDb7UYwGPwo0bPkYTKZSD4GMJvN+41G40cZ3nI/QPnVp8j56QW8gVCUR3NzMywWSz2n0+nGent7Ewb03x/D8V+eYF39FFK1kyi08ZBb7+Nox3iUh8vlglarneA0Gs0Mz/MLAmh2P9z4FzmWMFLPhLGt2QfL7V/hcF9lou8E/HBpVSoVOLVaHde8oecx8s9PQHLqOeS2EE529UVMRXPxutnwCBfv/BPlFRNw3fUX9l6cwMrTL/GZ4W9UXxtA9932uMYigOAU4dwo5gHW66awVhfG4fa7+NnZuajp3ChoEqu0L0E5EiHzAJwa80y7B+w4UaeBUqmEorQUikMV76Usg0KhYP+dPHsChY1eVlVRAKKIA7EANMsiRQlCYw+ZgqPRGhXG/hz24OvKKtjvXEOadipSsiyCRAD7lJW40vU7yo4ch+J7dZQqa2rRfvMW8ovL0D1wmeWNioMmnTAg0QgIUP/bbUiNk0sD7D/03aIR0BIRgJaUCoWqMeEICOAPhuD2+DHo4aM07A3AOXiP5UAEHLC5Udj6bAmA8uqYEZRU1UDMwVcVh3HD1YrWvi62fcjOvVgaIFYV5eXlIXt7NnJ25WJjbi4yVQ6sPjOJkstPQRth4ksUJwICpF1Pw4ZjG5D1RRa27v4SnV0d8Pv9S0yyAIgXAQG4cQ7L3cshPSiFLF+GBlMDvF7vbARzN7t4L9qebypi5kCMgAAkgmWqMpEtz4bT6YwA3gYCARbSh4CaTid7cQ4UlcSsormA9PZ0rDm7hi2VWqOGx+OZBQit8kl/f38EYOzpYfu9RDOOHdbn7K3UN5hQVPotisqroiQCMswZ2LJvC6Q7pTCZTcx8aGgItbW1U5zQO39saWlhgE9OTWKT/hXrWIP8KBsbGRkBz/vA++aLALICGbI+z4K8QI4Lly7AJ9wXCoVgs9lgMBgcrC/r9foJu90uGC3c2RY7DFD1UHKFQwSEVvymra1tReRkYbVaO4SEz9C6/R+Rh3CI+ENo+Olk/h8veXXcPn9a8AAAAABJRU5ErkJggg==';
1294
+ static zoomIconBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNqsk+ERwiAMhdtOwAiOwAg4gYzACI7gJp4ToBPoBu0GukG7gYZeehcjgZxt7t4fCB8JvLSNHA5kQQY0gQbQo/kjAugJemeU1o9aUKokCiCuO+YXI2YOBWw9CPvFNpfEESHSu44kV2yfvpmrdOLYm/6E1bZB4iwV0LGFixJ4YxV/AelvvZTASdromo2jY+4/KM/RvCFnaGoZoxgAah1T+7W+ADXohCU3am9N/vIsx+NlfAyDBLUMSiuuzXYRWgP0wsUztBXAHn9yxzyaDH1FM+emar/GciFT5WmtjznUbTEcDiubYR8BBgD2fnjD/AKVHwAAAABJRU5ErkJggg==';
1295
+ static cutIconBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gYDDB8MtOM2YAAAAdBJREFUSMfl1T2MTFEUB/DfjOcjviq7DRsRRCKiY+kVmleKwkchKgmrZWhEVqFhswqVRLIFiShOfBSi0C2xUSgVxGKzIxIJdmVHRnMneXmZtxijcpp33jnn/v/3no97+cdS62VRnuftsi0ian0hKIC/xzRWYGcVSb1H8DFsx14cwFTVyeo9ZGgatzGHDG9xpiq4F4IPeIcF/EAb+/pJsAuBw9iGdRjpSw0KADswiocpRcv6eYKT6bsBQ31t0zzPB9BIKRmMiGae57uxCQOpFpciYvK3CfI8X5XUlQmos3gYe3C1EN5Ohb+GRkR8zRYB3pgKebEiZLKgz+IxnuBKSuMzTGQV4Edws2SeT1MrFbaT/1c4iudo4TXu4SAmaiXg9WmQivIZH3ELZ5OtERGjaXK/YDO+4Xvyz2EGQ/VFwGdxB1sjYktENJL9DR4V4lbjFJZgKcaT/gnLswrwkYgYq7iHXhZbM9kbiaSNtcn9IiLms8L90pFjEXGjBH45qTO4EBFPu5CsSaZmRAx2/OUin+sC/gD70++JIvivhqxMMFxeXLp+D0XE3T8d+6zbDkrATRzH/b9+Mrs8GKdxHa2IaPVCkBXA62m3UzifumDBfy8/ARhwrGEODAGpAAAAAElFTkSuQmCC';
1296
+ static copyIconBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gYDDCItkmJugAAAAHBJREFUSMftlEEOgDAIBIvp93jtPtCevBhT10m10bjXEmAWSilvV7iBmbk6cZICdeIUOIqppJCk2CfDnQ8lOEuyvWMC2uXlGTg+/wTzCVzV3nfvJXSLYwKXBhO4Wu4+18gi93QPX9MpFj0/A+zvZ9UAaFN03sNpI7gAAAAASUVORK5CYII=';
1297
+ static copyWithBufferBase64 = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gYDDCQhzY6FLQAAAIJJREFUSMftVW0KwCAIreH1PK0HrF9BuOVHErIxIQqrl74nVsrbrXIHIjYiqisfIjYL8Dh/A9IuPQVgCVJ8ZPaN9TzPw5OlOQDpDOwASXsafduRujPggnGwVVX9GbgzuE63CogCaBSFNdA4B2vTMovHLF8Db5kea2ppFOVqEPo4PmMdpg1975VbWTEAAAAASUVORK5CYII=';
1022
1298
  }
1023
1299
 
1024
1300
  class PrintDrawLayerSourceService {
1025
1301
  _current = inject(CurrentItemsService);
1026
- // private _features = new Subject<Feature[]>;
1027
- // features$ = this._features.asObservable();
1028
1302
  source = new VectorSource();
1029
1303
  layer = new VectorLayer({
1030
1304
  source: this.source,
1031
- style: new Style({
1032
- stroke: new Stroke({
1033
- color: 'blue',
1034
- width: 2
1035
- }),
1036
- text: new Text({
1037
- text: 'MySpecialText',
1038
- font: '12px Calibri,sans-serif',
1039
- fill: new Fill({ color: '#000' }),
1040
- stroke: new Stroke({ color: '#fff', width: 3 }),
1041
- placement: 'line'
1042
- })
1043
- })
1305
+ zIndex: 999
1044
1306
  });
1045
- // private _disabled = false;
1046
1307
  constructor() {
1047
1308
  this.layer.set('PRINTDRAWLAYER', 'true');
1048
1309
  this._initListener();
@@ -1050,25 +1311,594 @@ class PrintDrawLayerSourceService {
1050
1311
  _initListener() {
1051
1312
  this._current.map$.subscribe({
1052
1313
  next: map => {
1053
- console.log("🚀 ~ PrintDrawLayerSourceService ~ _initListener ~ map:", map);
1054
1314
  map.addLayer(this.layer);
1055
1315
  }
1056
1316
  });
1057
- // this.source.on(['addfeature', 'changefeature', 'removefeature'], evt => {
1058
- // if (this._disabled) { return; }
1059
- // const features = this.source.getFeatures();
1060
- // this._features.next(features);
1061
- // });
1062
1317
  }
1063
1318
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintDrawLayerSourceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1064
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintDrawLayerSourceService, providedIn: 'root' });
1319
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintDrawLayerSourceService });
1065
1320
  }
1066
1321
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintDrawLayerSourceService, decorators: [{
1322
+ type: Injectable
1323
+ }], ctorParameters: () => [] });
1324
+
1325
+ class CenterPointService {
1326
+ _centerPointProperty = '_centerpoint';
1327
+ _settingsHelper = inject(KomponentSettingsHelperService);
1328
+ _current = inject(CurrentItemsService);
1329
+ _drawLayerService = inject(DrawLayerSourceService);
1330
+ _interactionHelper = inject(InteractionHelperService);
1331
+ _geoJson = new GeoJSON();
1332
+ _snackbar = inject(MatSnackBar);
1333
+ _featureHelper = inject(FeatureHelperService);
1334
+ handleFeatureDeleted(featureId) {
1335
+ const centerFeature = this._drawLayerService.source.getFeatures().find(f => f.get('_parentPolyId') == featureId);
1336
+ if (centerFeature) {
1337
+ this._drawLayerService.remove(centerFeature.getId());
1338
+ }
1339
+ }
1340
+ isCenterpoint(feature) {
1341
+ return feature.get(this._centerPointProperty) === true;
1342
+ }
1343
+ setCenterPoint(style, withinPolygon, callBack) {
1344
+ const style$ = this._settingsHelper.getStyle(style, this._current.profile.styleRepositoryWorkspace, this._current.profile.styleRepositoryGeoserver, 'Point');
1345
+ combineLatest([style$, this._current.map$]).subscribe({
1346
+ next: ([loadedStyle, map]) => {
1347
+ let parentPolyId;
1348
+ const centerDraw = new Draw({
1349
+ type: 'Point',
1350
+ source: this._drawLayerService.source,
1351
+ condition: evt => {
1352
+ if (!withinPolygon) {
1353
+ return true;
1354
+ }
1355
+ // The center needs to be inside a feature, so since turf is great for figuring out if something is in something, move the click and features to EPSG:4326
1356
+ const lonlat = transform(evt.coordinate, 'EPSG:25832', 'EPSG:4326');
1357
+ const pt = point(lonlat);
1358
+ const polygons = this._drawLayerService.source.getFeatures().filter(f => f.getGeometry() && f.getGeometry().getType() === 'Polygon'); // No point in checking linestrings and points
1359
+ const features = this._geoJson.writeFeaturesObject(polygons, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1360
+ const parentPoly = features.features.find(f => booleanPointInPolygon(pt, f));
1361
+ if (!parentPoly) {
1362
+ this._snackbar.open('Centerpunktet skal være inde i en flade', '', { duration: 3000 });
1363
+ return false;
1364
+ }
1365
+ // Save the parent polygon's id on the center. If the parent is deleted, the point will be deleted too
1366
+ parentPolyId = parentPoly.id;
1367
+ return !!parentPoly;
1368
+ },
1369
+ style: loadedStyle
1370
+ });
1371
+ this._interactionHelper.setAsTemp(centerDraw);
1372
+ map.addInteraction(centerDraw);
1373
+ centerDraw.on('drawend', evt => {
1374
+ const existing = this._drawLayerService.source.getFeatures().filter(f => f.get(this._centerPointProperty) === true);
1375
+ // There can only be ONE centerpoint, so remove previous if exists
1376
+ if (existing) {
1377
+ this._drawLayerService.source.removeFeatures(existing);
1378
+ }
1379
+ // Mark feature as centerpoint - this is to find it again an remove
1380
+ evt.feature.set(this._centerPointProperty, true);
1381
+ evt.feature.set('_parentPolyId', parentPolyId);
1382
+ this._featureHelper.setId(evt.feature);
1383
+ callBack();
1384
+ });
1385
+ }
1386
+ });
1387
+ }
1388
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CenterPointService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1389
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CenterPointService });
1390
+ }
1391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: CenterPointService, decorators: [{
1392
+ type: Injectable
1393
+ }] });
1394
+
1395
+ class ConfirmDialogComponent {
1396
+ dialogRef;
1397
+ data;
1398
+ constructor(dialogRef, data) {
1399
+ this.dialogRef = dialogRef;
1400
+ this.data = data;
1401
+ }
1402
+ choose(choice) {
1403
+ this.dialogRef.close(choice);
1404
+ }
1405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConfirmDialogComponent, deps: [{ token: i1$3.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
1406
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ConfirmDialogComponent, isStandalone: true, selector: "app-confirm-dialog", ngImport: i0, template: "\n@if(data.title) {\n <h2 mat-dialog-title>{{ data.title }}</h2>\n}\n<mat-dialog-content>\n <p>{{ data.message }}</p>\n</mat-dialog-content>\n\n<mat-dialog-actions align=\"end\">\n <!-- Sekund\u00E6r handling -->\n <button mat-button (click)=\"choose('secondary')\">\n {{ data.secondaryText }}\n </button>\n\n <!-- Prim\u00E6r handling -->\n <button mat-raised-button color=\"primary\" (click)=\"choose('primary')\">\n {{ data.primaryText }}\n </button>\n</mat-dialog-actions>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$3.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$3.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$3.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }] });
1407
+ }
1408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConfirmDialogComponent, decorators: [{
1409
+ type: Component,
1410
+ args: [{ selector: 'app-confirm-dialog', imports: [CommonModule, MatDialogModule, MatButtonModule, MatDialogTitle, MatDialogContent, MatDialogActions], template: "\n@if(data.title) {\n <h2 mat-dialog-title>{{ data.title }}</h2>\n}\n<mat-dialog-content>\n <p>{{ data.message }}</p>\n</mat-dialog-content>\n\n<mat-dialog-actions align=\"end\">\n <!-- Sekund\u00E6r handling -->\n <button mat-button (click)=\"choose('secondary')\">\n {{ data.secondaryText }}\n </button>\n\n <!-- Prim\u00E6r handling -->\n <button mat-raised-button color=\"primary\" (click)=\"choose('primary')\">\n {{ data.primaryText }}\n </button>\n</mat-dialog-actions>\n" }]
1411
+ }], ctorParameters: () => [{ type: i1$3.MatDialogRef }, { type: undefined, decorators: [{
1412
+ type: Inject,
1413
+ args: [MAT_DIALOG_DATA]
1414
+ }] }] });
1415
+
1416
+ class ConfirmDialogService {
1417
+ dialog;
1418
+ constructor(dialog) {
1419
+ this.dialog = dialog;
1420
+ }
1421
+ open(data) {
1422
+ const dialogRef = this.dialog.open(ConfirmDialogComponent, {
1423
+ width: '400px',
1424
+ disableClose: !!data.disableClose,
1425
+ data
1426
+ });
1427
+ return dialogRef.afterClosed();
1428
+ }
1429
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConfirmDialogService, deps: [{ token: i1$3.MatDialog }], target: i0.ɵɵFactoryTarget.Injectable });
1430
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConfirmDialogService, providedIn: 'root' });
1431
+ }
1432
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConfirmDialogService, decorators: [{
1433
+ type: Injectable,
1434
+ args: [{ providedIn: 'root' }]
1435
+ }], ctorParameters: () => [{ type: i1$3.MatDialog }] });
1436
+
1437
+ class OverlapService {
1438
+ _drawLayerService = inject(DrawLayerSourceService);
1439
+ _featureHelper = inject(FeatureHelperService);
1440
+ _snackbar = inject(MatSnackBar);
1441
+ _confirmService = inject(ConfirmDialogService);
1442
+ _geoJsonFormat = new GeoJSON();
1443
+ EPSILON = 0.1;
1444
+ handleOverlaps(features) {
1445
+ let newClippedFeature = null;
1446
+ let unlockedFeaturesAsGeoJson = [];
1447
+ let lockedClipped = false;
1448
+ let anyOverlappingLockedFeatures = false;
1449
+ features.forEach(feature => {
1450
+ // Handle the locked.
1451
+ const overlappingLockedFeatures = this._drawLayerService.source.getFeatures().filter(f => this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(feature.getGeometry().getExtent()));
1452
+ const clipped = this._handleLockedFeaturesOverlapping(overlappingLockedFeatures, feature);
1453
+ if (!lockedClipped && clipped) {
1454
+ lockedClipped = clipped;
1455
+ }
1456
+ //Handle the unlocked.
1457
+ const overlappingUnlockedFeatures = this._drawLayerService.source.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(feature.getGeometry().getExtent()));
1458
+ if (overlappingUnlockedFeatures.length > 0) {
1459
+ anyOverlappingLockedFeatures = true;
1460
+ let newFeatureGeoJson = this._geoJsonFormat.writeFeatureObject(feature);
1461
+ const newFeatureArea = area(newFeatureGeoJson);
1462
+ unlockedFeaturesAsGeoJson = overlappingUnlockedFeatures.map(f => this._geoJsonFormat.writeFeatureObject(f));
1463
+ // If null the whole new feature is inside a the existing
1464
+ newClippedFeature = difference(featureCollection([newFeatureGeoJson, ...unlockedFeaturesAsGeoJson]));
1465
+ // if the new feature is completely inside one of the existing features, newClippedFeature will be null
1466
+ const areaNewClippedFeature = newClippedFeature ? area(newClippedFeature) : 1;
1467
+ if (Math.abs(newFeatureArea - areaNewClippedFeature) < this.EPSILON) {
1468
+ anyOverlappingLockedFeatures = false;
1469
+ }
1470
+ }
1471
+ });
1472
+ if (lockedClipped) {
1473
+ this._snackbar.open("Der var overlap med låste flader, så den indtegnede flade er blevet beskåret", '', { duration: 3000 });
1474
+ }
1475
+ if (anyOverlappingLockedFeatures) {
1476
+ return this._confirmService.open({
1477
+ primaryText: 'Nye',
1478
+ secondaryText: 'Eksisterende',
1479
+ disableClose: true,
1480
+ message: 'Den nye flade overlapper med eksisterende flade. Ønsker du at beskære de nye eller de eksisterende?',
1481
+ title: 'Beskæring'
1482
+ }).pipe(map(msg => {
1483
+ return this._handleUnlockedFeaturesOverlapping(features, msg);
1484
+ }));
1485
+ }
1486
+ else {
1487
+ return of(features);
1488
+ }
1489
+ }
1490
+ _handleUnlockedFeaturesOverlapping(features, msg) {
1491
+ features.forEach(feature => {
1492
+ const overlappingUnlockedFeatures = this._drawLayerService.source.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(feature.getGeometry().getExtent()));
1493
+ if (overlappingUnlockedFeatures.length > 0) {
1494
+ let newFeatureGeoJson = this._geoJsonFormat.writeFeatureObject(feature);
1495
+ const unlockedFeaturesAsGeoJson = overlappingUnlockedFeatures.map(f => this._geoJsonFormat.writeFeatureObject(f));
1496
+ const newClippedFeature = difference(featureCollection([newFeatureGeoJson, ...unlockedFeaturesAsGeoJson]));
1497
+ if (msg === 'primary') {
1498
+ if (newClippedFeature) {
1499
+ const newGeometry = this._geoJsonFormat.readGeometry(newClippedFeature.geometry);
1500
+ feature.setGeometry(newGeometry);
1501
+ }
1502
+ else {
1503
+ feature.setGeometry(undefined);
1504
+ }
1505
+ }
1506
+ else {
1507
+ unlockedFeaturesAsGeoJson.forEach(f => {
1508
+ const featureArea = area(f.geometry);
1509
+ const fc = featureCollection([f, newFeatureGeoJson]);
1510
+ const featureDiff = difference(fc);
1511
+ if (featureDiff !== null) {
1512
+ const featureDiffArea = area(featureDiff.geometry);
1513
+ if (Math.abs(featureArea - featureDiffArea) > this.EPSILON) {
1514
+ const existingFeature = this._drawLayerService.source.getFeatureById(f.id);
1515
+ if (existingFeature) {
1516
+ const newGeometry = this._geoJsonFormat.readGeometry(featureDiff?.geometry);
1517
+ existingFeature.setGeometry(newGeometry);
1518
+ }
1519
+ }
1520
+ }
1521
+ });
1522
+ }
1523
+ }
1524
+ });
1525
+ features = features.filter(feature => feature.getGeometry() !== undefined);
1526
+ return features;
1527
+ }
1528
+ handleOverlap(feature) {
1529
+ // Handle the locked.
1530
+ const overlappingLockedFeatures = this._drawLayerService.source.getFeatures().filter(f => this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(feature.getGeometry().getExtent()));
1531
+ const clipped = this._handleLockedFeaturesOverlapping(overlappingLockedFeatures, feature);
1532
+ if (clipped) {
1533
+ this._snackbar.open("Der var overlap med låste flader, så den indtegnede flade er blevet beskåret", '', { duration: 3000 });
1534
+ }
1535
+ // Handle the unlocked.
1536
+ const overlappingUnlockedFeatures = this._drawLayerService.source.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(feature.getGeometry().getExtent()));
1537
+ if (overlappingUnlockedFeatures.length === 0) {
1538
+ return of(feature);
1539
+ }
1540
+ else {
1541
+ let newFeatureGeoJson = this._geoJsonFormat.writeFeatureObject(feature);
1542
+ const newFeatureArea = area(newFeatureGeoJson);
1543
+ const unlockedFeaturesAsGeoJson = overlappingUnlockedFeatures.map(f => this._geoJsonFormat.writeFeatureObject(f));
1544
+ const newClippedFeature = difference(featureCollection([newFeatureGeoJson, ...unlockedFeaturesAsGeoJson]));
1545
+ // if the new feature is completely inside one of the existing features, newClippedFeature will be null
1546
+ const areaNewClippedFeature = newClippedFeature ? area(newClippedFeature) : 1;
1547
+ if (Math.abs(newFeatureArea - areaNewClippedFeature) < this.EPSILON) {
1548
+ return of(feature);
1549
+ }
1550
+ else {
1551
+ return this._confirmService.open({
1552
+ primaryText: 'Nye',
1553
+ secondaryText: 'Eksisterende',
1554
+ disableClose: true,
1555
+ message: 'Den nye flade overlapper med eksisterende flade. Ønsker du at beskære de nye eller de eksisterende?',
1556
+ title: 'Beskæring'
1557
+ }).pipe(map(msg => {
1558
+ if (msg === 'primary') {
1559
+ const newGeometry = this._geoJsonFormat.readGeometry(newClippedFeature.geometry);
1560
+ feature.setGeometry(newGeometry);
1561
+ return feature;
1562
+ }
1563
+ else {
1564
+ unlockedFeaturesAsGeoJson.forEach(f => {
1565
+ const featureArea = area(f.geometry);
1566
+ const fc = featureCollection([f, newFeatureGeoJson]);
1567
+ const featureDiff = difference(fc);
1568
+ const featureDiffArea = area(featureDiff.geometry);
1569
+ if (Math.abs(featureArea - featureDiffArea) > this.EPSILON) {
1570
+ const existingFeature = this._drawLayerService.source.getFeatureById(f.id);
1571
+ if (existingFeature) {
1572
+ const newGeometry = this._geoJsonFormat.readGeometry(featureDiff?.geometry);
1573
+ existingFeature.setGeometry(newGeometry);
1574
+ }
1575
+ }
1576
+ });
1577
+ return feature;
1578
+ }
1579
+ }));
1580
+ }
1581
+ }
1582
+ }
1583
+ _handleLockedFeaturesOverlapping(overlappingFeatures, orgFeature) {
1584
+ let newFeatureGeoJson = this._geoJsonFormat.writeFeatureObject(orgFeature);
1585
+ const orgFeatureArea = area(newFeatureGeoJson);
1586
+ if (overlappingFeatures.length > 0) {
1587
+ const overlappingLockedFeaturesAsGeoJson = overlappingFeatures.map(f => this._geoJsonFormat.writeFeatureObject(f));
1588
+ const features = featureCollection([newFeatureGeoJson, ...overlappingLockedFeaturesAsGeoJson]);
1589
+ const clipped = difference(features);
1590
+ if (clipped) {
1591
+ const clippedArea = area(clipped.geometry);
1592
+ // The overlap is done by checking extent. To make sure there's an actual change, I check if the areas are diffent
1593
+ // but sometimes, the calculations of the same area can be a bit of. Like 0.01. To make sure that doesn't register as a change
1594
+ // I check that there's actually a change.
1595
+ const actuallyClipped = Math.abs(orgFeatureArea - clippedArea) > this.EPSILON;
1596
+ if (actuallyClipped) {
1597
+ const geom = this._geoJsonFormat.readGeometry(clipped.geometry);
1598
+ orgFeature.setGeometry(geom);
1599
+ return true;
1600
+ }
1601
+ }
1602
+ }
1603
+ return false;
1604
+ }
1605
+ splitByFeature(cutterFeature, remove = false) {
1606
+ let result = [];
1607
+ const cutterGeo = this._olToGeoJSON(cutterFeature.getGeometry());
1608
+ const cutter = feature(cutterGeo);
1609
+ const overlappingFeatures = this._drawLayerService.source.getFeatures().filter(f => f.getGeometry()?.intersectsExtent(cutterFeature.getGeometry().getExtent()));
1610
+ overlappingFeatures.forEach(feature$1 => {
1611
+ const geom = feature$1.getGeometry();
1612
+ if (!geom)
1613
+ return;
1614
+ const geo = this._olToGeoJSON(geom);
1615
+ const aTurfFeature = feature(geo);
1616
+ if (!booleanIntersects(aTurfFeature, cutter)) {
1617
+ return;
1618
+ }
1619
+ // Feature is fully inside the cutter, so remove the whole feature
1620
+ if (remove && booleanWithin(aTurfFeature, cutter)) {
1621
+ this._drawLayerService.source.removeFeature(feature$1);
1622
+ return;
1623
+ }
1624
+ // Outside part(s)
1625
+ const fc = featureCollection([aTurfFeature, cutter]);
1626
+ const diff = difference(fc);
1627
+ // Inside part(s)
1628
+ const inter = intersect(fc);
1629
+ // Add outside part(s)
1630
+ if (diff)
1631
+ result = result.concat(this._addTurfToResult(diff));
1632
+ // Add inside part(s)
1633
+ if (!remove && inter)
1634
+ result = result.concat(this._addTurfToResult(inter));
1635
+ // Remove the original overlapping feature (it was split into diff + inter)
1636
+ if (result.length > 0)
1637
+ this._drawLayerService.source.removeFeature(feature$1);
1638
+ });
1639
+ return of(result);
1640
+ }
1641
+ _addTurfToResult(aTurfFeature) {
1642
+ const result = [];
1643
+ if (aTurfFeature.geometry.type === 'MultiPolygon') {
1644
+ aTurfFeature.geometry.coordinates.forEach((coords) => {
1645
+ result.push(new Feature(this._geoJSONToOl({
1646
+ type: 'Polygon',
1647
+ coordinates: coords
1648
+ })));
1649
+ });
1650
+ }
1651
+ else {
1652
+ result.push(new Feature(this._geoJSONToOl(aTurfFeature.geometry)));
1653
+ }
1654
+ return result;
1655
+ }
1656
+ _olToGeoJSON(geometry) {
1657
+ const geoJson = new GeoJSON();
1658
+ return geoJson.writeGeometryObject(geometry.clone().transform('EPSG:25832', 'EPSG:4326'));
1659
+ }
1660
+ _geoJSONToOl(geo) {
1661
+ const geoJson = new GeoJSON();
1662
+ return geoJson.readGeometry(geo).transform('EPSG:4326', 'EPSG:25832');
1663
+ }
1664
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: OverlapService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1665
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: OverlapService });
1666
+ }
1667
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: OverlapService, decorators: [{
1668
+ type: Injectable
1669
+ }] });
1670
+
1671
+ class SearchProviderBase {
1672
+ _http = inject(HttpClient);
1673
+ wfsFormat = new WFS();
1674
+ _serializer = new XMLSerializer();
1675
+ search(searchValue, maxCount) {
1676
+ const wftSearches = [];
1677
+ const searchLayers = this.getSearchableLayers();
1678
+ searchLayers.forEach(layer => {
1679
+ const filter = this.getFilter(searchValue, layer); // like(layer.searchField!, `*${searchValue}*`, '*', '?', '/', false);
1680
+ var searchFeature = this.wfsFormat.writeGetFeature({
1681
+ featureTypes: [layer.layers.includes(':') ? layer.layers.split(':')[1] : ''],
1682
+ outputFormat: 'application/json',
1683
+ featureNS: '',
1684
+ featurePrefix: '',
1685
+ srsName: 'EPSG:25832',
1686
+ maxFeatures: maxCount,
1687
+ filter
1688
+ });
1689
+ wftSearches.push(this._http.post(layer.baseUrl.replace('wms', 'wfs'), this._serializer.serializeToString(searchFeature), { headers: { 'Content-Type': 'text/xml' }, responseType: 'json' }).pipe(map(r => {
1690
+ const result = this.map(layer, r);
1691
+ return result;
1692
+ })));
1693
+ });
1694
+ return combineLatest(wftSearches);
1695
+ }
1696
+ }
1697
+ class InfoSearchProvider extends SearchProviderBase {
1698
+ _current = inject(CurrentItemsService);
1699
+ getSearchableLayers() {
1700
+ return this._current.profile.layerGroups.flatMap(lg => lg.layers).filter(f => f.enableShowInfo);
1701
+ }
1702
+ getFilter(searchValue, layer) {
1703
+ const point = new Point(searchValue);
1704
+ return intersects(layer.geometryField, point);
1705
+ }
1706
+ map(layer, f) {
1707
+ const fieldsToExclude = new Set(layer.fieldsToExcludeFromInfoFromInfo.split(',').map(f => f.toLowerCase().trim()));
1708
+ return {
1709
+ layerName: layer.name,
1710
+ values: f.features.map(f => this._mapFeature(f, fieldsToExclude))
1711
+ };
1712
+ }
1713
+ _mapFeature(f, excludeSet) {
1714
+ const result = {};
1715
+ const props = f.properties || {};
1716
+ for (const [key, value] of Object.entries(props)) {
1717
+ if (excludeSet.has(key.toLowerCase()))
1718
+ continue;
1719
+ const kind = this._getValueKind(value);
1720
+ result[key] = { kind, data: value, name: key };
1721
+ }
1722
+ return result;
1723
+ }
1724
+ _getValueKind(value) {
1725
+ const imgTypes = ['.png', '.jpg', '.gif', '.jpeg'];
1726
+ if (typeof value === 'string') {
1727
+ if (value.startsWith('http://') || value.startsWith('https://')) {
1728
+ if (imgTypes.some(ext => value.endsWith(ext))) {
1729
+ return 'img';
1730
+ }
1731
+ return 'url';
1732
+ }
1733
+ }
1734
+ return 'data';
1735
+ }
1736
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InfoSearchProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
1737
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InfoSearchProvider });
1738
+ }
1739
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: InfoSearchProvider, decorators: [{
1740
+ type: Injectable
1741
+ }] });
1742
+
1743
+ class ShowInfoService {
1744
+ _current = inject(CurrentItemsService);
1745
+ _listenKey = null;
1746
+ _active = false;
1747
+ _infoSearchProvider = inject(InfoSearchProvider);
1748
+ _features = new Subject();
1749
+ features$ = this._features.asObservable();
1750
+ stopShowInfo() {
1751
+ this._features.next(null);
1752
+ }
1753
+ startShowInfo() {
1754
+ if (this._active) {
1755
+ return;
1756
+ }
1757
+ this._active = true;
1758
+ this._current.map$.subscribe({
1759
+ next: map => {
1760
+ this._listenKey = this._listenKey ?? map.on('singleclick', evt => {
1761
+ this.disable();
1762
+ this._infoSearchProvider.search(evt.coordinate, 5).subscribe({
1763
+ next: r => {
1764
+ if (r.flatMap(l => l.values).length > 0) {
1765
+ this._features.next(r);
1766
+ }
1767
+ else {
1768
+ this._features.next(null);
1769
+ }
1770
+ this.disable();
1771
+ },
1772
+ error: err => {
1773
+ this.disable();
1774
+ }
1775
+ });
1776
+ });
1777
+ }
1778
+ });
1779
+ }
1780
+ disable() {
1781
+ if (!this._active) {
1782
+ return;
1783
+ }
1784
+ if (this._listenKey) {
1785
+ unByKey(this._listenKey);
1786
+ this._listenKey = null;
1787
+ }
1788
+ this._active = false;
1789
+ }
1790
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ShowInfoService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1791
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ShowInfoService });
1792
+ }
1793
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ShowInfoService, decorators: [{
1794
+ type: Injectable
1795
+ }] });
1796
+
1797
+ class GeometryService {
1798
+ config = inject(GISKOMPONENT_CONFIG);
1799
+ _baseUrl = this.config.apiBaseUrl;
1800
+ _http = inject(HttpClient);
1801
+ validate(wkt) {
1802
+ const url = `${this._baseUrl}/api/geometry/validate`;
1803
+ return this._http.post(url, { wkt: wkt });
1804
+ }
1805
+ dmpValidate(wkt) {
1806
+ const url = `${this._baseUrl}/api/geometry/dmpvalidate`;
1807
+ return this._http.post(url, { wkt: wkt });
1808
+ }
1809
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GeometryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1810
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GeometryService, providedIn: 'root' });
1811
+ }
1812
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GeometryService, decorators: [{
1067
1813
  type: Injectable,
1068
1814
  args: [{
1069
1815
  providedIn: 'root'
1070
1816
  }]
1071
- }], ctorParameters: () => [] });
1817
+ }] });
1818
+
1819
+ class PrintHelperService {
1820
+ setVisibiltyOnDOMElements(baseElement, visible) {
1821
+ const elements = ['activeobjects', '.ol-zoom', 'map-toolbox', '.ol-mouse-position', '.layer-selector-body-wrapper'];
1822
+ this._setVisibiltyOnDOMElements(baseElement, elements, visible);
1823
+ }
1824
+ getImgData(map, printFormat, printDimensions) {
1825
+ const htmlElement = map.getViewport();
1826
+ this.setVisibiltyOnDOMElements(htmlElement, false);
1827
+ return new Promise((resolve, reject) => {
1828
+ html2canvas(htmlElement, {
1829
+ useCORS: true,
1830
+ }).then(originalCanvas => {
1831
+ if (printDimensions) {
1832
+ const srcW = originalCanvas.width;
1833
+ const srcH = originalCanvas.height;
1834
+ const scale = Math.min(printDimensions[0] / srcW, printDimensions[1] / srcH);
1835
+ const outW = Math.round(srcW * scale);
1836
+ const outH = Math.round(srcH * scale);
1837
+ // 3) Lav nyt canvas til nedskalering
1838
+ const scaledCanvas = document.createElement('canvas');
1839
+ scaledCanvas.width = outW;
1840
+ scaledCanvas.height = outH;
1841
+ const ctx = scaledCanvas.getContext('2d');
1842
+ ctx.imageSmoothingEnabled = true;
1843
+ ctx.imageSmoothingQuality = 'high';
1844
+ ctx.drawImage(originalCanvas, 0, 0, outW, outH);
1845
+ const dataUrl = scaledCanvas.toDataURL(printFormat);
1846
+ resolve(dataUrl);
1847
+ }
1848
+ else {
1849
+ const imgData = originalCanvas.toDataURL(printFormat);
1850
+ resolve(imgData);
1851
+ }
1852
+ });
1853
+ });
1854
+ }
1855
+ _setVisibiltyOnDOMElements(baseElement, elements, visible) {
1856
+ let visibilityValue = 'hidden';
1857
+ if (visible) {
1858
+ visibilityValue = 'visible';
1859
+ }
1860
+ elements.forEach(e => {
1861
+ const activeobjects = baseElement.querySelector(e);
1862
+ if (activeobjects) {
1863
+ activeobjects.style.visibility = visibilityValue;
1864
+ }
1865
+ });
1866
+ }
1867
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintHelperService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1868
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintHelperService, providedIn: 'root' });
1869
+ }
1870
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PrintHelperService, decorators: [{
1871
+ type: Injectable,
1872
+ args: [{
1873
+ providedIn: 'root'
1874
+ }]
1875
+ }] });
1876
+
1877
+ class SearchProviderService {
1878
+ _cancelSearch = new Subject();
1879
+ maxSearchResults = 8;
1880
+ searchProviders = [];
1881
+ search(searchString, geometry) {
1882
+ this._cancelSearch.next('');
1883
+ let combinedSearch = new Observable();
1884
+ this.searchProviders.forEach(searchProvider => {
1885
+ const search = searchProvider
1886
+ .search(searchString, geometry, this.maxSearchResults)
1887
+ .pipe(takeUntil(this._cancelSearch.asObservable()))
1888
+ .pipe(take(this.maxSearchResults));
1889
+ combinedSearch = combinedSearch ? combinedSearch.pipe(mergeWith(search)) : search;
1890
+ });
1891
+ return combinedSearch;
1892
+ }
1893
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SearchProviderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1894
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SearchProviderService, providedIn: 'root' });
1895
+ }
1896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SearchProviderService, decorators: [{
1897
+ type: Injectable,
1898
+ args: [{
1899
+ providedIn: 'root'
1900
+ }]
1901
+ }] });
1072
1902
 
1073
1903
  class ToolboxComponent {
1074
1904
  // Inputs
@@ -1088,13 +1918,23 @@ class ToolboxComponent {
1088
1918
  _featureHelper = inject(FeatureHelperService);
1089
1919
  _undoRedo = inject(UndoRedoService);
1090
1920
  _geomSplitService = inject(GeometrySplitService);
1091
- _highlight = inject(highlightService);
1921
+ _highlight = inject(HighlightService);
1092
1922
  _interactionHelper = inject(InteractionHelperService);
1093
1923
  _mergeService = inject(MergeFeaturesService);
1924
+ _centerPointService = inject(CenterPointService);
1925
+ _overlap = inject(OverlapService);
1926
+ _zoomService = inject(ZoomService);
1927
+ _showInfo = inject(ShowInfoService);
1928
+ _geometryService = inject(GeometryService);
1929
+ _printHelper = inject(PrintHelperService);
1930
+ _layoutService = inject(LayoutService);
1931
+ _searchService = inject(SearchProviderService);
1094
1932
  _POSITION_STORAGE_KEY = 'toolbox_position';
1095
- _current = inject(CurrentItemsService);
1933
+ // private _current = inject(CurrentItemsService);
1096
1934
  _originalMapWidth = 0;
1097
1935
  _originalMapHeight = 0;
1936
+ pointClickKey;
1937
+ drawInteraction;
1098
1938
  mapWidth = 0;
1099
1939
  mapHeight = 0;
1100
1940
  format = "image/png";
@@ -1106,7 +1946,10 @@ class ToolboxComponent {
1106
1946
  [800, 600]
1107
1947
  ];
1108
1948
  printDrawLabel = "";
1109
- printDrawTool = "point";
1949
+ bufferInMeters = 0;
1950
+ printDrawTool = "Point";
1951
+ filteredResults = [];
1952
+ filteredResultsMetadata = [];
1110
1953
  selectedGeometrySetting = undefined;
1111
1954
  undoIconBase64 = IconsConstants.undoIconBase64;
1112
1955
  redoIconBase64 = IconsConstants.redoIconBase64;
@@ -1119,6 +1962,11 @@ class ToolboxComponent {
1119
1962
  removePointsIconBase64 = IconsConstants.removePointsIconBase64;
1120
1963
  wktIconBase64 = IconsConstants.wktIconBase64;
1121
1964
  pointIconBase64 = IconsConstants.pointIconBase64;
1965
+ featureSearchIconBase64 = IconsConstants.featureSearchIconBase64;
1966
+ objectSearchCopyIconBase64 = IconsConstants.copyIconBase64;
1967
+ objectSearchCopyWithBufferIconBase64 = IconsConstants.copyWithBufferBase64;
1968
+ objectSearchCutIconBase64 = IconsConstants.cutIconBase64;
1969
+ objectSearchZoomIconBase64 = IconsConstants.zoomIconBase64;
1122
1970
  polygonIconBase64 = IconsConstants.polygonIconBase64;
1123
1971
  lineStringIconBase64 = IconsConstants.lineStringIconBase64;
1124
1972
  printBase64 = IconsConstants.printBase64;
@@ -1126,6 +1974,8 @@ class ToolboxComponent {
1126
1974
  dragPosition = { x: 0, y: 0 };
1127
1975
  snap = false;
1128
1976
  showInputWKT = false;
1977
+ showGeometryTypes = false;
1978
+ filteredGeometryTypeSettings = [];
1129
1979
  WKTString;
1130
1980
  activeMode = null;
1131
1981
  activateShowInputWKT() {
@@ -1136,8 +1986,186 @@ class ToolboxComponent {
1136
1986
  this._clearAllInteractions();
1137
1987
  this.showInputWKT = false;
1138
1988
  }
1989
+ toggleFeatureSearch() {
1990
+ if (this.activeMode != 'search-feature') {
1991
+ this._clearAllInteractions();
1992
+ this.activeMode = 'search-feature';
1993
+ this.showGeometryTypes = true;
1994
+ this._updateFilteredGeometryTypeSettings('Polygon');
1995
+ this._enablePointSearch();
1996
+ }
1997
+ else {
1998
+ this.activeMode = null;
1999
+ this.showGeometryTypes = false;
2000
+ this._disablePointSearch();
2001
+ this.filteredResults = [];
2002
+ }
2003
+ }
2004
+ togglePointSearch() {
2005
+ if (this.activeMode != 'search-point') {
2006
+ this._clearAllInteractions();
2007
+ this._disablePolygonSearch();
2008
+ this.activeMode = 'search-point';
2009
+ this._enablePointSearch();
2010
+ }
2011
+ else {
2012
+ this.activeMode = null;
2013
+ this._disablePointSearch();
2014
+ }
2015
+ }
2016
+ togglePolygonSearch() {
2017
+ this._disablePointSearch();
2018
+ if (this.activeMode != 'search-polygon') {
2019
+ this.activeMode = 'search-polygon';
2020
+ this.drawInteraction = new Draw({
2021
+ source: new VectorSource(),
2022
+ type: 'Polygon'
2023
+ });
2024
+ this.map.addInteraction(this.drawInteraction);
2025
+ this.drawInteraction.on('drawend', (evt) => {
2026
+ this._searchService.search(undefined, evt.feature.getGeometry())
2027
+ .subscribe({
2028
+ next: result => {
2029
+ this.filteredResults = result.filter(result => result.total > 0);
2030
+ }
2031
+ });
2032
+ });
2033
+ }
2034
+ else {
2035
+ this._disablePolygonSearch();
2036
+ this.activeMode = null;
2037
+ }
2038
+ }
2039
+ _updateFilteredGeometryTypeSettings(geomType) {
2040
+ this.filteredGeometryTypeSettings = this.settings.geometryTypeSettings.filter(setting => setting.availableGeometryTypes?.includes(geomType));
2041
+ if (this.filteredGeometryTypeSettings.length === 1) {
2042
+ this.selectedGeometrySetting = this.filteredGeometryTypeSettings[0];
2043
+ this.settingsChanged();
2044
+ }
2045
+ }
2046
+ _disablePolygonSearch() {
2047
+ if (this.drawInteraction) {
2048
+ this.map.removeInteraction(this.drawInteraction);
2049
+ this.drawInteraction = undefined;
2050
+ }
2051
+ this.filteredResults = [];
2052
+ }
2053
+ _enablePointSearch() {
2054
+ this.pointClickKey = this.map.on('singleclick', (evt) => {
2055
+ const point = new Point$1(evt.coordinate);
2056
+ this._searchService.search(undefined, point)
2057
+ .subscribe({
2058
+ next: result => {
2059
+ this.filteredResults = result.filter(result => result.total > 0);
2060
+ }
2061
+ });
2062
+ });
2063
+ }
2064
+ _disablePointSearch() {
2065
+ if (this.pointClickKey) {
2066
+ unByKey(this.pointClickKey);
2067
+ this.pointClickKey = undefined;
2068
+ }
2069
+ this.filteredResults = [];
2070
+ }
2071
+ splitBySearchedObject(item, event) {
2072
+ event.stopPropagation();
2073
+ if (!item.wkt) {
2074
+ return;
2075
+ }
2076
+ if (!this.selectedGeometrySetting) {
2077
+ return;
2078
+ }
2079
+ const wktFormat = new WKT();
2080
+ let feature = wktFormat.readFeature(item.wkt, { featureProjection: 'EPSG:25832', dataProjection: 'EPSG:25832' });
2081
+ const geomType = feature.getGeometry()?.getType();
2082
+ if (!geomType) {
2083
+ return;
2084
+ }
2085
+ combineLatest([this._settingsHelper.getStyle(this.selectedGeometrySetting?.style, this.profile.styleRepositoryWorkspace, this.profile.styleRepositoryGeoserver, geomType),
2086
+ this._overlap.splitByFeature(feature)])
2087
+ .subscribe({
2088
+ next: ([style, features]) => {
2089
+ this._undoRedo.enable();
2090
+ features.forEach(feature => {
2091
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
2092
+ this._featureHelper.setId(feature);
2093
+ feature.setStyle(style);
2094
+ this._drawLayerService.source.addFeature(feature);
2095
+ });
2096
+ }
2097
+ });
2098
+ }
2099
+ zoomSearchedObject(item, event) {
2100
+ event.stopPropagation();
2101
+ this.highlight(item.wkt, event);
2102
+ }
2103
+ toggleItemInfo(item, event) {
2104
+ event.stopPropagation();
2105
+ item.showMetadata = !item.showMetadata;
2106
+ }
2107
+ cutBySearchedObject(item, event) {
2108
+ event.stopPropagation();
2109
+ if (!item.wkt) {
2110
+ return;
2111
+ }
2112
+ if (!this.selectedGeometrySetting) {
2113
+ return;
2114
+ }
2115
+ const wktFormat = new WKT();
2116
+ let feature = wktFormat.readFeature(item.wkt, { featureProjection: 'EPSG:25832', dataProjection: 'EPSG:25832' });
2117
+ const geomType = feature.getGeometry()?.getType();
2118
+ if (!geomType) {
2119
+ return;
2120
+ }
2121
+ combineLatest([this._settingsHelper.getStyle(this.selectedGeometrySetting?.style, this.profile.styleRepositoryWorkspace, this.profile.styleRepositoryGeoserver, geomType),
2122
+ this._overlap.splitByFeature(feature, true)])
2123
+ .subscribe({
2124
+ next: ([style, features]) => {
2125
+ this._undoRedo.enable();
2126
+ features.forEach(feature => {
2127
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
2128
+ this._featureHelper.setId(feature);
2129
+ feature.setStyle(style);
2130
+ this._drawLayerService.source.addFeature(feature);
2131
+ });
2132
+ }
2133
+ });
2134
+ }
2135
+ _bufferGeometry(geometry, bufferMeters) {
2136
+ const format = new GeoJSON();
2137
+ const precisionModel = new PrecisionModel(); // floating precision
2138
+ const geometryFactory = new GeometryFactory(precisionModel);
2139
+ const reader = new GeoJSONReader(geometryFactory);
2140
+ const writer = new GeoJSONWriter();
2141
+ // Convert OL geometry → GeoJSON → JSTS
2142
+ const geoJson = format.writeGeometryObject(geometry);
2143
+ const jstsGeom = reader.read(geoJson);
2144
+ // Create buffer
2145
+ const bufferedJstsGeom = BufferOp.bufferOp(jstsGeom, bufferMeters);
2146
+ // Convert back to OL geometry
2147
+ const bufferedGeoJson = writer.write(bufferedJstsGeom);
2148
+ return format.readGeometry(bufferedGeoJson);
2149
+ }
1139
2150
  settingsChanged(evt) {
1140
2151
  this._clearAllInteractions();
2152
+ setTimeout(() => {
2153
+ switch (this.activeMode) {
2154
+ case 'draw-point': {
2155
+ this._startDraw('Point');
2156
+ break;
2157
+ }
2158
+ case 'draw-polygon': {
2159
+ this._startDraw('Polygon');
2160
+ break;
2161
+ }
2162
+ case 'draw-linestring': {
2163
+ this._startDraw('LineString');
2164
+ break;
2165
+ }
2166
+ default: break;
2167
+ }
2168
+ }, 300);
1141
2169
  }
1142
2170
  undo() {
1143
2171
  this._undoRedo.undo();
@@ -1145,6 +2173,10 @@ class ToolboxComponent {
1145
2173
  redo() {
1146
2174
  this._undoRedo.redo();
1147
2175
  }
2176
+ startShowInfo() {
2177
+ this._clearAllInteractions();
2178
+ this._showInfo.startShowInfo();
2179
+ }
1148
2180
  onSnapChange() {
1149
2181
  if (this.snap) {
1150
2182
  this._addSnap();
@@ -1168,11 +2200,42 @@ class ToolboxComponent {
1168
2200
  }
1169
2201
  this._settingsHelper.getStyle(this.selectedGeometrySetting.style, this.profile.styleRepositoryWorkspace, this.profile.styleRepositoryGeoserver, featureType).subscribe({
1170
2202
  next: featureStyle => {
1171
- feature.setStyle(featureStyle);
1172
- this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
1173
- this._highlight.highlight(feature);
1174
- const extent = buffer$1(feature.getGeometry().getExtent(), 10);
1175
- this.map.getView().fit(extent);
2203
+ if (featureType === 'Polygon') {
2204
+ this.handleFeatureValidation(feature).subscribe({
2205
+ next: (result) => {
2206
+ if (result) {
2207
+ result.forEach(feature => {
2208
+ feature.setStyle(featureStyle);
2209
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
2210
+ this._featureHelper.setId(feature);
2211
+ });
2212
+ this._overlap.handleOverlaps(result).pipe(filter(f => !!f)).subscribe({
2213
+ next: f => {
2214
+ if (f) {
2215
+ f.forEach(feature => {
2216
+ this._drawLayerService.source.addFeature(feature);
2217
+ });
2218
+ this._zoomService.zoomToFeatures(f);
2219
+ if (f.length > 1) {
2220
+ this._snackbar.open(`Den indtegnede flade var ikke gyldig, men rettet til ${f.length} nye flader`, '', { duration: 3000 });
2221
+ }
2222
+ }
2223
+ }
2224
+ });
2225
+ }
2226
+ else {
2227
+ this._snackbar.open(`Validering af de tegnede flader resulterede i ingen flader`, '', { duration: 3000 });
2228
+ }
2229
+ }
2230
+ });
2231
+ }
2232
+ else {
2233
+ feature.setStyle(featureStyle);
2234
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
2235
+ this._featureHelper.setId(feature);
2236
+ this._zoomService.zoomToFeatures([feature]);
2237
+ this._drawLayerService.source.addFeature(feature);
2238
+ }
1176
2239
  }
1177
2240
  });
1178
2241
  }
@@ -1184,6 +2247,14 @@ class ToolboxComponent {
1184
2247
  type: 'Polygon',
1185
2248
  source: this._drawLayerService.source
1186
2249
  });
2250
+ setCenterPoint() {
2251
+ this._clearAllInteractions();
2252
+ this.activeMode = 'center-point';
2253
+ this._centerPointService.setCenterPoint(this.settings.centerPoint.style, this.settings.centerPoint.withinPolygon, () => {
2254
+ this.activeMode = null;
2255
+ this._clearAllInteractions();
2256
+ });
2257
+ }
1187
2258
  _actionSource = new VectorSource();
1188
2259
  clipHole() {
1189
2260
  this._clearAllInteractions();
@@ -1194,7 +2265,7 @@ class ToolboxComponent {
1194
2265
  });
1195
2266
  this._interactionHelper.setAsTemp(clipHoleDraw);
1196
2267
  clipHoleDraw.on('drawend', evt => {
1197
- const overlappingFeatures = this._drawLayerService.source.getFeatures().filter(f => f.getGeometry()?.intersectsExtent(evt.feature.getGeometry().getExtent()));
2268
+ const overlappingFeatures = this._drawLayerService.source.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(evt.feature.getGeometry().getExtent()));
1198
2269
  const geoJsonFormat = new GeoJSON();
1199
2270
  const holeGeoJson = geoJsonFormat.writeFeatureObject(evt.feature);
1200
2271
  overlappingFeatures.forEach(f => {
@@ -1243,14 +2314,52 @@ class ToolboxComponent {
1243
2314
  this.map.addInteraction(splitInteraction);
1244
2315
  this._addSnap();
1245
2316
  }
2317
+ startDrawPointUrl(url) {
2318
+ this._clearAllInteractions();
2319
+ const style = new Style({
2320
+ image: new Icon({
2321
+ src: url,
2322
+ scale: 1,
2323
+ anchor: [0.5, 1],
2324
+ anchorXUnits: 'fraction',
2325
+ anchorYUnits: 'fraction'
2326
+ })
2327
+ });
2328
+ const drawInteraction = new Draw({
2329
+ type: 'Point',
2330
+ source: new VectorSource(),
2331
+ style
2332
+ });
2333
+ this.activeMode = 'draw-point';
2334
+ this._updateFilteredGeometryTypeSettings('Point');
2335
+ this.showGeometryTypes = true;
2336
+ drawInteraction.on('drawend', evt => {
2337
+ const feature = evt.feature.clone();
2338
+ this._featureHelper.setId(feature);
2339
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting.typeId);
2340
+ feature.setStyle(style);
2341
+ this._drawLayerService.source.addFeature(feature);
2342
+ });
2343
+ this._interactionHelper.setAsTemp(drawInteraction);
2344
+ this.map.addInteraction(drawInteraction);
2345
+ }
1246
2346
  startDrawPoint() {
1247
2347
  this._startDraw('Point');
2348
+ this.showGeometryTypes = false;
2349
+ this._updateFilteredGeometryTypeSettings('Point');
2350
+ this.showGeometryTypes = true;
1248
2351
  }
1249
2352
  startDrawLineString() {
1250
2353
  this._startDraw('LineString');
2354
+ this.showGeometryTypes = false;
2355
+ this._updateFilteredGeometryTypeSettings('LineString');
2356
+ this.showGeometryTypes = true;
1251
2357
  }
1252
2358
  startDrawPolygon() {
1253
2359
  this._startDraw('Polygon');
2360
+ this.showGeometryTypes = false;
2361
+ this._updateFilteredGeometryTypeSettings('Polygon');
2362
+ this.showGeometryTypes = true;
1254
2363
  }
1255
2364
  _deleteSelect;
1256
2365
  get deleteSelect() {
@@ -1264,13 +2373,14 @@ class ToolboxComponent {
1264
2373
  this._interactionHelper.setAsTemp(this.deleteSelect);
1265
2374
  this.deleteSelect.on('select', evt => {
1266
2375
  this._drawLayerService.source.removeFeatures(evt.selected);
2376
+ evt.selected.forEach(f => this._centerPointService.handleFeatureDeleted(f.getId()));
1267
2377
  });
1268
2378
  return this._deleteSelect;
1269
2379
  }
1270
- _deleting = false;
2380
+ // private _deleting: boolean = false;
1271
2381
  startDelete() {
1272
2382
  this._clearAllInteractions();
1273
- this._deleting = true;
2383
+ // this._deleting = true;
1274
2384
  this.activeMode = 'delete';
1275
2385
  this.map.addInteraction(this.deleteSelect);
1276
2386
  this._addSnap();
@@ -1280,38 +2390,143 @@ class ToolboxComponent {
1280
2390
  this.activeMode = 'edit-remove';
1281
2391
  const modifyRemove = new Modify({
1282
2392
  source: this._drawLayerService.source,
2393
+ condition: evt => {
2394
+ // first, get all features from the draw-layer
2395
+ const feature = this.map.forEachFeatureAtPixel(evt.pixel, f => f, { layerFilter: l => l == this._drawLayerService.layer });
2396
+ if (!feature) {
2397
+ return false;
2398
+ }
2399
+ return !this._featureHelper.isLocked(feature);
2400
+ },
1283
2401
  // Make sure delete is allowed and other interactions are not.
1284
2402
  deleteCondition: always,
1285
2403
  insertVertexCondition: never,
1286
- snapToPointer: this.snap
2404
+ snapToPointer: this.snap,
1287
2405
  });
1288
2406
  this._interactionHelper.setAsTemp(modifyRemove);
1289
2407
  this.map.addInteraction(modifyRemove);
1290
2408
  this._addSnap();
1291
2409
  }
2410
+ highlight(wkt, event) {
2411
+ event.stopPropagation();
2412
+ this._highlight.highlight(wkt);
2413
+ }
2414
+ addToActiveObjectsList(item, event, withBuffer = false) {
2415
+ event.stopPropagation();
2416
+ if (!item.wkt) {
2417
+ return;
2418
+ }
2419
+ if (!this.selectedGeometrySetting) {
2420
+ return;
2421
+ }
2422
+ const wktFormat = new WKT();
2423
+ let feature = wktFormat.readFeature(item.wkt, { featureProjection: 'EPSG:25832', dataProjection: 'EPSG:25832' });
2424
+ if (withBuffer) {
2425
+ const bufferedGeometry = this._bufferGeometry(feature.getGeometry(), this.bufferInMeters);
2426
+ feature = new Feature$1({ geometry: bufferedGeometry });
2427
+ }
2428
+ const geomType = feature.getGeometry()?.getType();
2429
+ if (!geomType) {
2430
+ return;
2431
+ }
2432
+ combineLatest([this.handleFeatureValidation(feature), this._settingsHelper.getStyle(this.selectedGeometrySetting?.style, this.profile.styleRepositoryWorkspace, this.profile.styleRepositoryGeoserver, geomType)])
2433
+ .subscribe({
2434
+ next: ([validationResult, featureStyle]) => {
2435
+ if (validationResult) {
2436
+ validationResult.forEach(feature => {
2437
+ feature.setStyle(featureStyle);
2438
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
2439
+ this._featureHelper.setId(feature);
2440
+ });
2441
+ this._overlap.handleOverlaps(validationResult).pipe(filter(f => !!f)).subscribe({
2442
+ next: f => {
2443
+ if (f) {
2444
+ f.forEach(feature => {
2445
+ this._undoRedo.enable();
2446
+ this._drawLayerService.source.addFeature(feature);
2447
+ });
2448
+ this._zoomService.zoomToFeatures(f);
2449
+ if (f.length > 1) {
2450
+ this._snackbar.open(`Den indtegnede flade var ikke gyldig, men rettet til ${f.length} nye flader`, '', { duration: 3000 });
2451
+ }
2452
+ }
2453
+ }
2454
+ });
2455
+ }
2456
+ else {
2457
+ this._snackbar.open(`Validering af de tegnede flader resulterede i ingen flader`, '', { duration: 3000 });
2458
+ }
2459
+ }
2460
+ });
2461
+ }
1292
2462
  startEdit() {
1293
2463
  this._clearAllInteractions();
1294
2464
  this.activeMode = 'edit';
1295
2465
  const modify = new Modify({
1296
2466
  source: this._drawLayerService.source,
2467
+ condition: evt => {
2468
+ // first, get all features from the draw-layer
2469
+ const feature = this.map.forEachFeatureAtPixel(evt.pixel, f => f, { layerFilter: l => l == this._drawLayerService.layer });
2470
+ if (!feature) {
2471
+ return false;
2472
+ }
2473
+ return !this._featureHelper.isLocked(feature);
2474
+ },
1297
2475
  // No delete
1298
2476
  deleteCondition: () => false,
1299
2477
  // Allow moving existing and add new points
1300
- condition: always,
2478
+ // condition: always,
1301
2479
  insertVertexCondition: always,
1302
2480
  });
1303
2481
  /* Modify fires changes to features a LOT, so to not flood the UndoRedo service, I disable until ending the modify
1304
2482
  and then force an update, to add the updated feature to the undo-service. */
1305
2483
  modify.on('modifystart', () => this._undoRedo.disable());
1306
- modify.on('modifyend', (evt) => { this._undoRedo.enable(); evt.features.forEach(f => f.set('updated', new Date().toISOString())); });
2484
+ modify.on('modifyend', (evt) => {
2485
+ this._undoRedo.enable();
2486
+ evt.features.forEach(f => {
2487
+ f.set('updated', new Date().toISOString());
2488
+ const geom = f.getGeometry();
2489
+ if (geom && geom.getType() === 'Polygon') {
2490
+ //START VALIDATING
2491
+ this.handleFeatureValidation(f).subscribe({
2492
+ next: (result) => {
2493
+ if (result && result.length > 0) {
2494
+ const featureStyle = f.getStyle();
2495
+ const featureTypeId = this._featureHelper.typeId(f);
2496
+ this._drawLayerService.source.removeFeature(f);
2497
+ result.forEach(feature => {
2498
+ feature.setStyle(featureStyle);
2499
+ this._featureHelper.setTypeId(feature, featureTypeId || '');
2500
+ this._featureHelper.setId(feature);
2501
+ this._drawLayerService.source.addFeature(feature);
2502
+ });
2503
+ this._overlap.handleOverlaps(result).pipe(filter(f => !!f)).subscribe({
2504
+ next: f => {
2505
+ if (f) {
2506
+ f.forEach(feature => {
2507
+ this._drawLayerService.source.addFeature(feature);
2508
+ });
2509
+ if (f.length > 1) {
2510
+ this._snackbar.open(`Den indtegnede flade var ikke gyldig, men rettet til ${f.length} nye flader`, '', { duration: 3000 });
2511
+ }
2512
+ }
2513
+ }
2514
+ });
2515
+ }
2516
+ else {
2517
+ this._snackbar.open(`Validering af de tegnede flader resulterede i ingen flader`, '', { duration: 3000 });
2518
+ }
2519
+ }
2520
+ });
2521
+ //END VALIDATING
2522
+ }
2523
+ });
2524
+ });
1307
2525
  this._interactionHelper.setAsTemp(modify);
1308
2526
  this.map.addInteraction(modify);
1309
2527
  }
1310
2528
  _startDraw(geomType) {
1311
2529
  this._clearAllInteractions();
1312
- if (!this.selectedGeometrySetting) {
1313
- return;
1314
- }
1315
2530
  // Set the active mode after clearing
1316
2531
  if (geomType === 'Polygon')
1317
2532
  this.activeMode = 'draw-polygon';
@@ -1319,21 +2534,58 @@ class ToolboxComponent {
1319
2534
  this.activeMode = 'draw-linestring';
1320
2535
  else if (geomType === 'Point')
1321
2536
  this.activeMode = 'draw-point';
2537
+ if (!this.selectedGeometrySetting) {
2538
+ return;
2539
+ }
1322
2540
  this._settingsHelper.getStyle(this.selectedGeometrySetting.style, this.profile.styleRepositoryWorkspace, this.profile.styleRepositoryGeoserver, geomType)
1323
2541
  .subscribe({
1324
2542
  next: style => {
1325
2543
  const drawInteraction = new Draw({
1326
2544
  type: geomType,
1327
- source: this._drawLayerService.source,
2545
+ source: new VectorSource(),
1328
2546
  style: style
1329
2547
  });
2548
+ this._undoRedo.disable();
1330
2549
  drawInteraction.on('drawend', evt => {
1331
- this._featureHelper.setTypeId(evt.feature, this.selectedGeometrySetting?.typeId || '');
1332
- this._featureHelper.setId(evt.feature);
1333
- evt.feature.setStyle(style);
2550
+ if (geomType === 'Polygon') {
2551
+ // Validate feature
2552
+ this.handleFeatureValidation(evt.feature).subscribe({
2553
+ next: (result) => {
2554
+ if (result) {
2555
+ result.forEach(feature => {
2556
+ this._featureHelper.setTypeId(feature, this.selectedGeometrySetting?.typeId || '');
2557
+ this._featureHelper.setId(feature);
2558
+ feature.setStyle(style);
2559
+ });
2560
+ this._overlap.handleOverlaps(result).pipe(filter(f => !!f)).subscribe({
2561
+ next: f => {
2562
+ if (f) {
2563
+ f.forEach(feature => {
2564
+ this._undoRedo.enable();
2565
+ this._drawLayerService.source.addFeature(feature);
2566
+ });
2567
+ if (f.length > 1) {
2568
+ this._snackbar.open(`Den indtegnede flade var ikke gyldig, men rettet til ${f.length} nye flader`, '', { duration: 3000 });
2569
+ }
2570
+ }
2571
+ }
2572
+ });
2573
+ }
2574
+ else {
2575
+ this._snackbar.open(`Validering af de tegnede flader resulterede i ingen flader`, '', { duration: 3000 });
2576
+ }
2577
+ }
2578
+ });
2579
+ }
2580
+ else {
2581
+ this._featureHelper.setTypeId(evt.feature, this.selectedGeometrySetting?.typeId || '');
2582
+ this._featureHelper.setId(evt.feature);
2583
+ evt.feature.setStyle(style);
2584
+ this._undoRedo.enable();
2585
+ this._drawLayerService.source.addFeature(evt.feature);
2586
+ }
1334
2587
  });
1335
2588
  this._interactionHelper.setAsTemp(drawInteraction);
1336
- // this._setDrawLayer()
1337
2589
  this.map.addInteraction(drawInteraction);
1338
2590
  this._addSnap();
1339
2591
  this.map.getTargetElement().style.cursor = 'crosshair'; // Placeholder for now
@@ -1344,6 +2596,7 @@ class ToolboxComponent {
1344
2596
  _areaSource = new VectorSource();
1345
2597
  _areaLayer = new VectorLayer({
1346
2598
  source: this._areaSource,
2599
+ zIndex: 999,
1347
2600
  style: (feature) => {
1348
2601
  const geom = feature.getGeometry();
1349
2602
  const area = getArea(geom);
@@ -1369,6 +2622,7 @@ class ToolboxComponent {
1369
2622
  _distanceLabelSource = new VectorSource();
1370
2623
  _distanceLabelLayer = new VectorLayer({
1371
2624
  source: this._distanceLabelSource,
2625
+ zIndex: 999,
1372
2626
  style: (feature) => {
1373
2627
  return new Style({
1374
2628
  text: new Text({
@@ -1383,6 +2637,7 @@ class ToolboxComponent {
1383
2637
  _distanceSource = new VectorSource();
1384
2638
  _distanceLayer = new VectorLayer({
1385
2639
  source: this._distanceSource,
2640
+ zIndex: 999,
1386
2641
  style: (feature) => {
1387
2642
  const geom = feature.getGeometry();
1388
2643
  const length = getLength(geom);
@@ -1433,7 +2688,7 @@ class ToolboxComponent {
1433
2688
  const mid = [(c1[0] + c2[0]) / 2, (c1[1] + c2[1]) / 2];
1434
2689
  const formatted = `${length.toFixed(2)} m`;
1435
2690
  const labelFeature = new Feature$1({
1436
- geometry: new Point(mid),
2691
+ geometry: new Point$1(mid),
1437
2692
  label: formatted
1438
2693
  });
1439
2694
  this._distanceLabelSource.addFeature(labelFeature);
@@ -1443,7 +2698,7 @@ class ToolboxComponent {
1443
2698
  const endPoint = coords[coords.length - 1];
1444
2699
  const formattedTotal = `${totalLength.toFixed(2)} m`;
1445
2700
  const totalFeature = new Feature$1({
1446
- geometry: new Point(endPoint),
2701
+ geometry: new Point$1(endPoint),
1447
2702
  label: `Total: ${formattedTotal}`
1448
2703
  });
1449
2704
  this._distanceLabelSource.addFeature(totalFeature);
@@ -1455,8 +2710,8 @@ class ToolboxComponent {
1455
2710
  this._highlight.clear();
1456
2711
  this.stopMeasureArea();
1457
2712
  this.stopMeasureLength();
1458
- this._deleting = false;
1459
2713
  this._interactionHelper.clearTempFromMap();
2714
+ this._showInfo.disable();
1460
2715
  this._undoRedo.enable();
1461
2716
  }
1462
2717
  ngOnInit() {
@@ -1470,6 +2725,11 @@ class ToolboxComponent {
1470
2725
  });
1471
2726
  this._loadPosition();
1472
2727
  }
2728
+ ngOnChanges(changes) {
2729
+ if (changes['profile'] && this.profile) {
2730
+ this._loadPosition();
2731
+ }
2732
+ }
1473
2733
  onDragEnded(event) {
1474
2734
  this.dragPosition = {
1475
2735
  x: event.source.getFreeDragPosition().x,
@@ -1481,12 +2741,15 @@ class ToolboxComponent {
1481
2741
  try {
1482
2742
  const savedPosition = localStorage.getItem(this._POSITION_STORAGE_KEY);
1483
2743
  if (savedPosition) {
1484
- const position = JSON.parse(savedPosition);
1485
- this.dragPosition = position;
2744
+ this.dragPosition = JSON.parse(savedPosition);
2745
+ }
2746
+ else if (this.profile) {
2747
+ this.dragPosition = this._layoutService.loadInitialPositionFromProfile(this._POSITION_STORAGE_KEY, this.profile.toolboxInitialPosition);
1486
2748
  }
1487
2749
  }
1488
2750
  catch (error) {
1489
- console.warn('Could not load toolbox position from localStorage:', error);
2751
+ console.error('Error loading position from localStorage:', error);
2752
+ this.dragPosition = { x: 0, y: 0 };
1490
2753
  }
1491
2754
  }
1492
2755
  _savePosition() {
@@ -1494,7 +2757,7 @@ class ToolboxComponent {
1494
2757
  localStorage.setItem(this._POSITION_STORAGE_KEY, JSON.stringify(this.dragPosition));
1495
2758
  }
1496
2759
  catch (error) {
1497
- console.warn('Could not save toolbox position to localStorage:', error);
2760
+ console.error('Error saving position to localStorage:', error);
1498
2761
  }
1499
2762
  }
1500
2763
  startMergeFeatures() {
@@ -1540,6 +2803,9 @@ class ToolboxComponent {
1540
2803
  this.stopMeasureArea();
1541
2804
  this.stopMeasureLength();
1542
2805
  }
2806
+ if (this.profile.toolboxInitialPosition.includes('højre')) {
2807
+ this._layoutService.bringElementIntoViewIfNeeded('.toolbox-wrapper', -57, this.collapsed, this.map);
2808
+ }
1543
2809
  }
1544
2810
  startMeasureArea() {
1545
2811
  if (this._active === 'Area') {
@@ -1614,7 +2880,7 @@ class ToolboxComponent {
1614
2880
  }
1615
2881
  doPrint() {
1616
2882
  let htmlElement = this.map.getViewport();
1617
- this._setVisibiltyOnDOMElements(htmlElement, false);
2883
+ this._printHelper.setVisibiltyOnDOMElements(htmlElement, false);
1618
2884
  html2canvas(htmlElement, {
1619
2885
  useCORS: true,
1620
2886
  }).then(canvas => {
@@ -1630,66 +2896,124 @@ class ToolboxComponent {
1630
2896
  }
1631
2897
  link.download = download;
1632
2898
  link.click();
1633
- this._setVisibiltyOnDOMElements(htmlElement, true);
2899
+ this._printHelper.setVisibiltyOnDOMElements(htmlElement, true);
1634
2900
  });
1635
2901
  }
1636
- _setVisibiltyOnDOMElements(baseElement, visible) {
1637
- let visibilityValue = 'hidden';
1638
- if (visible) {
1639
- visibilityValue = 'visible';
1640
- }
1641
- const activeobjects = baseElement.querySelector('activeobjects');
1642
- if (activeobjects) {
1643
- activeobjects.style.visibility = visibilityValue;
1644
- }
1645
- const zoom = baseElement.querySelector('.ol-zoom');
1646
- if (zoom) {
1647
- zoom.style.visibility = visibilityValue;
1648
- }
1649
- const mapToolbox = baseElement.querySelector('map-toolbox');
1650
- if (mapToolbox) {
1651
- mapToolbox.style.visibility = visibilityValue;
1652
- }
1653
- const mousePosition = baseElement.querySelector('.ol-mouse-position');
1654
- if (mousePosition) {
1655
- mousePosition.style.visibility = visibilityValue;
1656
- }
1657
- const layerSelector = baseElement.querySelector('.layer-selector-body-wrapper');
1658
- if (layerSelector) {
1659
- layerSelector.style.visibility = visibilityValue;
2902
+ // private _setVisibiltyOnDOMElements(baseElement: HTMLElement, visible: boolean): void {
2903
+ // let visibilityValue = 'hidden';
2904
+ // if (visible) { visibilityValue = 'visible'; }
2905
+ // const activeobjects = baseElement.querySelector('activeobjects') as HTMLElement;
2906
+ // if (activeobjects) { activeobjects!.style.visibility = visibilityValue; }
2907
+ // const zoom = baseElement.querySelector('.ol-zoom') as HTMLElement;
2908
+ // if (zoom) { zoom!.style.visibility = visibilityValue; }
2909
+ // const mapToolbox = baseElement.querySelector('map-toolbox') as HTMLElement;
2910
+ // if (mapToolbox) { mapToolbox!.style.visibility = visibilityValue; }
2911
+ // const mousePosition = baseElement.querySelector('.ol-mouse-position') as HTMLElement;
2912
+ // if (mousePosition) { mousePosition!.style.visibility = visibilityValue; }
2913
+ // const layerSelector = baseElement.querySelector('.layer-selector-body-wrapper') as HTMLElement;
2914
+ // if (layerSelector) { layerSelector!.style.visibility = visibilityValue; }
2915
+ // }
2916
+ startSelectFeatureHighlight() {
2917
+ if (this.activeMode === 'select-highlight') {
2918
+ return;
1660
2919
  }
2920
+ this._clearAllInteractions();
2921
+ this.activeMode = 'select-highlight';
2922
+ const highlightSelect = new Select$1({
2923
+ layers: [this._drawLayerService.layer],
2924
+ style: undefined,
2925
+ });
2926
+ this._interactionHelper.setAsTemp(highlightSelect);
2927
+ highlightSelect.on('select', evt => {
2928
+ this._drawLayerService.setFeaturesSelectedForHighlight(evt.selected);
2929
+ this.activeMode = null;
2930
+ this._clearAllInteractions();
2931
+ });
2932
+ this.map.addInteraction(highlightSelect);
1661
2933
  }
1662
2934
  startDrawMode() {
1663
2935
  if (this._active === 'PrintDraw') {
1664
2936
  this._active = 'No';
1665
2937
  this._removePrintDrawToolInteraction();
1666
2938
  this.activeMode = null;
2939
+ this.printDrawTool = 'Point';
1667
2940
  return;
1668
2941
  }
1669
2942
  this._active = 'PrintDraw';
1670
2943
  this._clearAllInteractions();
1671
2944
  this._addPrintDrawToolInteraction();
1672
2945
  this.activeMode = null;
2946
+ this.printDrawTool = 'Point';
1673
2947
  }
1674
2948
  handlePrintDrawToolChanged() {
1675
2949
  if (this.active === 'PrintDraw') {
1676
- this.printDrawTool;
1677
- console.log("🚀 ~ ToolboxComponent ~ handlePrintDrawToolChanged ~ this.printDrawTool:", this.printDrawTool);
2950
+ this._removePrintDrawToolInteraction();
2951
+ this._addPrintDrawToolInteraction();
1678
2952
  }
1679
2953
  }
2954
+ handleClearPrintDrawFeatures() {
2955
+ this._printDrawLayerService.source.clear();
2956
+ }
1680
2957
  printDraw;
1681
2958
  _addPrintDrawToolInteraction() {
1682
2959
  if (this.active === 'PrintDraw') {
1683
- const features = this._printDrawLayerService.source.getFeatures();
1684
- console.log("🚀 ~ ToolboxComponent ~ _addPrintDrawToolInteraction ~ features:", features);
1685
- console.log("🚀 ~ ToolboxComponent ~ _addPrintDrawToolInteraction ~ this._current.map:", this._current.map?.getLayers());
1686
- this.printDraw = new Draw({
1687
- source: this._printDrawLayerService.source,
1688
- type: 'LineString',
1689
- style: this._printDrawStyle
1690
- });
2960
+ switch (this.printDrawTool) {
2961
+ case 'Arrow':
2962
+ this.printDraw = new Draw({
2963
+ source: this._printDrawLayerService.source,
2964
+ type: 'LineString',
2965
+ });
2966
+ break;
2967
+ case 'Square':
2968
+ this.printDraw = new Draw({
2969
+ source: this._printDrawLayerService.source,
2970
+ type: 'Circle',
2971
+ geometryFunction: createRegularPolygon(4)
2972
+ });
2973
+ break;
2974
+ case 'Rectangle':
2975
+ this.printDraw = new Draw({
2976
+ source: this._printDrawLayerService.source,
2977
+ type: 'Circle',
2978
+ geometryFunction: createBox()
2979
+ });
2980
+ break;
2981
+ default:
2982
+ this.printDraw = new Draw({
2983
+ source: this._printDrawLayerService.source,
2984
+ type: this.printDrawTool,
2985
+ });
2986
+ break;
2987
+ }
1691
2988
  this.printDraw.on('drawend', evt => {
1692
- evt.feature.setStyle(this._printDrawStyle);
2989
+ const feature = evt.feature;
2990
+ feature.set("printDrawLabel", this.printDrawLabel);
2991
+ feature.set("printDrawTool", this.printDrawTool);
2992
+ if (this.printDrawTool === 'Arrow') {
2993
+ feature.setStyle((feature) => {
2994
+ const styles = this._getDrawToolStyle(feature);
2995
+ const geom = feature.getGeometry();
2996
+ const rot = this._lastSegmentRotation(geom);
2997
+ const arrowHead = new RegularShape({
2998
+ points: 3,
2999
+ radius: 10,
3000
+ angle: Math.PI / 2,
3001
+ rotation: -rot,
3002
+ rotateWithView: true,
3003
+ stroke: new Stroke({ color: '#1f6feb', width: 3 })
3004
+ });
3005
+ const endCoord = geom.getCoordinateAt(1);
3006
+ const arrowEndStyle = new Style({
3007
+ image: arrowHead,
3008
+ geometry: () => new Point$1(endCoord)
3009
+ });
3010
+ styles.push(arrowEndStyle);
3011
+ return styles;
3012
+ });
3013
+ }
3014
+ else {
3015
+ feature.setStyle((feature) => this._getDrawToolStyle(feature));
3016
+ }
1693
3017
  });
1694
3018
  }
1695
3019
  this.map.addInteraction(this.printDraw);
@@ -1698,55 +3022,57 @@ class ToolboxComponent {
1698
3022
  _removePrintDrawToolInteraction() {
1699
3023
  this.map.removeInteraction(this.printDraw);
1700
3024
  }
1701
- _printDrawStyle = new Style({
1702
- stroke: new Stroke({
1703
- color: 'blue',
1704
- width: 2
1705
- }),
1706
- text: new Text({
1707
- text: 'MySpecialText',
1708
- font: '12px Calibri,sans-serif',
1709
- fill: new Fill({ color: '#000' }),
1710
- stroke: new Stroke({ color: '#fff', width: 3 }),
1711
- placement: 'line'
1712
- })
1713
- });
1714
- styles = {
1715
- Point: {
1716
- 'circle-radius': 5,
1717
- 'circle-fill-color': 'red',
1718
- 'stroke-color': 'yellow',
1719
- 'stroke-width': 2,
1720
- 'fill-color': 'blue',
1721
- },
1722
- LineString: {
1723
- 'circle-radius': 5,
1724
- 'circle-fill-color': 'red',
1725
- 'stroke-color': 'yellow',
1726
- 'stroke-width': 2,
1727
- 'fill-color': 'blue',
1728
- },
1729
- Polygon: {
1730
- 'circle-radius': 5,
1731
- 'circle-fill-color': 'red',
1732
- 'stroke-color': 'yellow',
1733
- 'stroke-width': 2,
1734
- 'fill-color': 'blue',
1735
- },
1736
- Circle: {
1737
- 'circle-radius': 5,
1738
- 'circle-fill-color': 'red',
1739
- 'stroke-color': 'blue',
1740
- 'stroke-width': 2,
1741
- 'fill-color': 'yellow',
1742
- },
1743
- };
3025
+ _lastSegmentRotation(lineString) {
3026
+ const coords = lineString.getCoordinates();
3027
+ if (!coords || coords.length < 2)
3028
+ return 0;
3029
+ const [x1, y1] = coords[coords.length - 2];
3030
+ const [x2, y2] = coords[coords.length - 1];
3031
+ return Math.atan2(y2 - y1, x2 - x1);
3032
+ }
3033
+ _getDrawToolStyle(feature) {
3034
+ const textStyle = new Style({
3035
+ text: new Text({
3036
+ text: feature.get("printDrawLabel"),
3037
+ font: '12px Calibri,sans-serif',
3038
+ fill: new Fill({ color: '#000' }),
3039
+ stroke: new Stroke({ color: '#fff', width: 3 }),
3040
+ placement: 'point'
3041
+ })
3042
+ });
3043
+ const lineStyle = new Style({
3044
+ stroke: new Stroke({
3045
+ color: '#1f6feb',
3046
+ width: 3
3047
+ })
3048
+ });
3049
+ const circleStyle = new Style({
3050
+ image: new CircleStyle({
3051
+ radius: 5,
3052
+ fill: new Fill({ color: '#7da8e9ff' }), // teal fill
3053
+ stroke: new Stroke({ color: '#1f6feb', width: 2 }),
3054
+ })
3055
+ });
3056
+ let styles = [textStyle, lineStyle];
3057
+ if (feature.get("printDrawTool") === 'Point') {
3058
+ styles.push(circleStyle);
3059
+ }
3060
+ return styles;
3061
+ }
3062
+ handleFeatureValidation(feature) {
3063
+ const wktFormat = new WKT();
3064
+ const wkt = wktFormat.writeFeature(feature);
3065
+ return this._geometryService.validate(wkt)
3066
+ .pipe(map(result => {
3067
+ return result.correctedGeometries?.map(wkt => wktFormat.readFeature(wkt));
3068
+ }));
3069
+ }
1744
3070
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ToolboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1745
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ToolboxComponent, isStandalone: true, selector: "map-toolbox", inputs: { map: "map", showMeasureDistance: "showMeasureDistance", showMeasureArea: "showMeasureArea", collapsed: "collapsed", settings: "settings", profile: "profile", WKTInputEnabled: "WKTInputEnabled", deleteEnabled: "deleteEnabled" }, ngImport: i0, template: "<div class=\"toolbox-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\"\n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\">\n <div class=\"drag-handle-toolbox\" cdkDragHandle>\n <mat-icon>build_circle</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleCollapsed($event)\">{{ collapsed ? 'flip_to_front' : 'remove' }}</mat-icon> </div>\n <div class=\"toolbox-container\" [class.collapsed]=\"collapsed\">\n @if(!collapsed) {\n <div class=\"toolbox-content\">\n @if (settings.geometryTypeSettings && settings.geometryTypeSettings.length) {\n <mat-select class=\"geometry-selector\" (selectionChange)=\"settingsChanged($event)\" [(ngModel)]=\"selectedGeometrySetting\" [compareWith]=\"compareGeometrySetting\">\n @for (setting of settings.geometryTypeSettings; track setting) {\n <mat-option [value]=\"setting\">{{setting.typeName}}</mat-option>\n }\n </mat-select> \n }\n <div class=\"all-tools-container\">\n <div class=\"main-tools\">\n @if(!settings?.undoDisabled) {\n <img \n [src]=\"undoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"undo()\" \n matTooltip=\"Fortryd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Undo\">\n\n <img \n [src]=\"redoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"redo()\" \n matTooltip=\"Gentag\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Redo\">\n }\n <mat-slide-toggle [(ngModel)]=\"snap\" (change)=\"onSnapChange()\">Snap</mat-slide-toggle>\n @if(settings.editEnabled) {\n <img \n [src]=\"editIconBase64\" \n [class.active]=\"activeMode === 'edit'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEdit()\" \n matTooltip=\"Rediger\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Rediger\">\n\n <img \n [src]=\"removePointsIconBase64\" \n [class.active]=\"activeMode === 'edit-remove'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEditRemovePoints()\" \n matTooltip=\"Fjern punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Fjern punkter\">\n }\n @if(settings.cutHoleEnabled) {\n <img \n [src]=\"trimIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'clip-hole'\" \n (click)=\"clipHole()\" \n matTooltip=\"Klip hul\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Klip hul\">\n }\n @if(settings.splitEnabled) {\n <img \n [src]=\"splitIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'split'\"\n (click)=\"split()\" \n matTooltip=\"Sk\u00E6r over\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Sk\u00E6r over\">\n }\n @if(settings.changeTypeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n [class.active]=\"activeMode === 'change-type'\" \n (click)=\"startChangeType()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Skift type\" \n matTooltipPosition=\"below\">\n find_replace\n </mat-icon>\n }\n @if(settings.mergeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n (click)=\"startMergeFeatures()\" \n matTooltipPosition=\"below\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Saml flader\" \n [class.active]=\"activeMode === 'merge-features'\">\n merge\n </mat-icon>\n }\n </div>\n @if(selectedGeometrySetting && selectedGeometrySetting.availableGeometryTypes?.length) {\n <div class=\"geometry-tools\">\n @for(geomType of selectedGeometrySetting.availableGeometryTypes;track geomType) {\n @if(geomType === 'Polygon') {\n <img \n [src]=\"polygonIconBase64\" \n [class.active]=\"activeMode === 'draw-polygon'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPolygon()\" \n matTooltip=\"Polygon\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Polygon\">\n }\n @if(geomType === 'LineString') {\n <img \n [src]=\"lineStringIconBase64\" \n [class.active]=\"activeMode === 'draw-linestring'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawLineString()\" \n matTooltip=\"LineString\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"LineString\">\n }\n @if(geomType === 'Point') {\n <img \n [src]=\"pointIconBase64\" \n [class.active]=\"activeMode === 'draw-point'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPoint()\" \n matTooltip=\"Punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkter\">\n }\n }\n <img \n [src]=\"wktIconBase64\" \n class=\"compact-icon secondary custom-image-icon\"\n (click)=\"activateShowInputWKT()\" \n matTooltip=\"WKT\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"WKT\">\n </div>\n }\n <div class=\"tool-separator\" *ngIf=\"deleteEnabled || showMeasureDistance || showMeasureArea || profile.showPrint\"></div>\n <div class=\"measurement-print-tools\">\n <div class=\"measurement-tools\">\n @if (deleteEnabled) {\n <img \n [src]=\"deleteIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'delete'\"\n (click)=\"startDelete()\" \n matTooltip=\"Slet\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Slet\">\n }\n @if (showMeasureDistance) {\n <img \n [src]=\"measureDistanceIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-distance'\"\n (click)=\"startMeasureLength()\" \n matTooltip=\"M\u00E5le afstand\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le afstand\">\n }\n @if(showMeasureArea) {\n <img \n [src]=\"measureAreaIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-area'\"\n (click)=\"startMeasureArea()\" \n matTooltip=\"M\u00E5le areal\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le areal\">\n }\n </div>\n @if(profile.showPrint) {\n <div class=\"tool-separator print-separator\"></div>\n <div class=\"print-tools\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Print'\"\n (click)=\"startPrintMode()\" \n matTooltip=\"Udskriv\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Udskriv\">\n <!-- <img \n [src]=\"drawBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Draw'\"\n (click)=\"startDrawMode()\" \n matTooltip=\"Tegn\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Tegn\"> -->\n </div>\n }\n </div>\n </div>\n @if(showInputWKT) {\n <div class=\"wkt-section\">\n <input class=\"compact-input\" [(ngModel)]=\"WKTString\">\n <div class=\"wkt-actions\">\n <button class=\"compact-button primary\" (click)=\"ReadWKT()\">Indl\u00E6s WKT</button>\n <button class=\"compact-button\" (click)=\"cancelWKT()\">Annuller</button>\n </div>\n </div>\n }\n @if(profile.showPrint) {\n @if(active === \"Print\") {\n <div class=\"print-config\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon\"\n (click)=\"doPrint()\" \n alt=\"Udskriv\"> \n <span class=\"icon-separator\">i</span>\n <select id=\"formatSelector\" [(ngModel)]=\"format\">\n <option value=\"image/png\" selected>.PNG</option>\n <option value=\"image/jpeg\">.JPG</option>\n </select>\n <select id=\"dimensionSelector\" [(ngModel)]=\"dimId\" (change)=\"handleDimensionSelected()\">\n <option value=\"99\" selected></option>\n <option value=\"0\">1920 X 1080</option>\n <option value=\"1\">1680 X 1050</option>\n <option value=\"2\">1280 X 800</option>\n <option value=\"3\">800 X 600</option>\n </select>\n <input type=\"text\" [(ngModel)]=\"mapWidth\"/> \n <span class=\"icon-separator\">x</span>\n <input type=\"text\" [(ngModel)]=\"mapHeight\"/>\n <button (click)=\"setNewMapDimensions()\">V\u00E6lg</button>\n </div>\n }\n @if(active === \"PrintDraw\") {\n <div class=\"print-draw-config\">\n <span class=\"icon-separator\">Label</span>\n <input type=\"text\" [(ngModel)]=\"printDrawLabel\"/>\n <select id=\"drawToolSelector\" [(ngModel)]=\"printDrawTool\" (change)=\"handlePrintDrawToolChanged()\">\n <option value=\"arrow\" selected>Pil</option>\n <option value=\"point\">Punkt</option>\n <option value=\"line\">Linje</option>\n <option value=\"polygon\">Polygon</option>\n <option value=\"cirkel\">Cirkel</option>\n <option value=\"square\">Kvadrat</option>\n <option value=\"rectangle\">Firkant</option>\n </select>\n <button>Ryd</button>\n </div>\n }\n }\n </div>\n }\n </div>\n</div>\n", styles: [".toolbox-wrapper{position:absolute;left:1em;top:31.5em;z-index:1000;cursor:grab;max-width:95vw}.toolbox-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}.drag-handle-toolbox{display:flex;align-items:center;justify-content:space-between;background:color-mix(in srgb,#000 60%,transparent);padding:2px 10px;cursor:grab;gap:8px}.drag-handle-toolbox mat-icon{color:#fff;font-size:18px;width:18px;height:18px}.drag-handle-toolbox mat-icon:first-child{flex:1;text-align:center}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.toggle-icon:hover{color:#fff;background:#ffffff26}.toggle-icon:active{background:#ffffff40}:host{position:relative;display:flex;justify-content:center}:host.expanded{width:auto;min-width:320px;padding:12px}.toolbox-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;background:color-mix(in srgb,#000 60%,transparent);box-shadow:#0000004d 0 1px 4px -1px;padding:10px;width:auto;min-width:32px;transition:all .3s ease;cursor:default}.toolbox-container.collapsed{display:none}.toolbox-content{display:flex;flex-direction:column;width:100%;gap:3px;animation:slideDown .3s cubic-bezier(.4,0,.2,1)}.all-tools-container{display:flex;flex-direction:row;align-items:center;flex-wrap:nowrap;width:100%;justify-content:flex-start;overflow-x:hidden}.all-tools-container::-webkit-scrollbar{height:4px}.all-tools-container::-webkit-scrollbar-thumb{background:#ccc;border-radius:2px}.main-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.main-tools ::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:12px;font-weight:500;color:#fff}.geometry-selector{width:100%}.geometry-selector ::ng-deep .mat-mdc-select{font-size:14px;line-height:1.4;border-radius:6px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:32px;min-height:32px;padding:0 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);transition:all .2s ease}.geometry-selector ::ng-deep .mat-mdc-select-trigger:hover{background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:14px;font-weight:500;color:#fff}.geometry-selector ::ng-deep .mat-mdc-select-arrow-wrapper{height:16px;transform:scale(.85)}.geometry-selector ::ng-deep .mat-mdc-form-field-infix{min-height:32px;padding:6px 0}.geometry-selector ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .mat-mdc-select-panel{min-width:fit-content!important;max-width:320px!important;background:color-mix(in srgb,#000 100%,transparent)!important;border:none!important;border-radius:8px!important;box-shadow:0 8px 24px #0000001f,0 2px 6px #00000014!important;margin-top:6px!important;padding:4px 0!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:14px!important;min-height:36px!important;padding:8px 14px!important;transition:all .15s ease!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option:hover:not(.mat-mdc-option-disabled){background:#444849!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option.mat-active{background:color-mix(in srgb,#000 60%,transparent)!important;color:#fff!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px!important;font-weight:500!important;color:#fff!important}::ng-deep .cdk-overlay-pane{z-index:1001}::ng-deep .cdk-overlay-backdrop{z-index:1000}.geometry-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap;background:color-mix(in srgb,#000 20%,transparent);border-radius:8px;padding:4px 8px;border:1px solid rgba(255,255,255,.1)}.measurement-tools,.print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-tools .compact-icon,.print-tools .compact-icon{transition:all .3s cubic-bezier(.4,0,.2,1)}.measurement-tools .compact-icon.custom-image-icon,.print-tools .compact-icon.custom-image-icon{padding:4px;background:none;border:1px solid rgba(255,255,255,.2)}.measurement-tools .compact-icon.custom-image-icon img,.print-tools .compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:filter .2s ease}.measurement-tools .compact-icon.custom-image-icon.active,.print-tools .compact-icon.custom-image-icon.active{background:linear-gradient(147.38deg,#0ea5e9,#075985);border-color:transparent;box-shadow:0 4px 12px #0ea5e966}.measurement-tools .compact-icon.custom-image-icon.active img,.print-tools .compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1)}.measurement-tools .compact-icon.custom-image-icon.active:hover,.print-tools .compact-icon.custom-image-icon.active:hover{box-shadow:0 6px 20px #0ea5e980}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active),.print-tools .compact-icon.custom-image-icon:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);border-color:#0ea5e9;box-shadow:0 2px 8px #0ea5e94d}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active) img,.print-tools .compact-icon.custom-image-icon:hover:not(.active) img{filter:brightness(0) invert(.8)}.print-tools .compact-icon{background:linear-gradient(147.38deg,#4f46e5,#3730a3);color:#fff;border:1px solid rgba(255,255,255,.2)}.print-tools .compact-icon.print-icon{background:linear-gradient(147.38deg,#10b981,#047857)}.print-tools .compact-icon.draw-icon{background:linear-gradient(147.38deg,#f59e0b,#d97706)}.print-tools .compact-icon.active{background:linear-gradient(147.38deg,#ef4444,#dc2626);box-shadow:0 4px 12px #ef444466;border-color:transparent;transform:scale(1.05)}.print-tools .compact-icon.active:hover{box-shadow:0 6px 20px #ef444480;transform:scale(1.08)}.print-tools .compact-icon:hover:not(.active){box-shadow:0 4px 12px #0000004d;opacity:.9}.tool-separator{width:1px;height:24px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.3),transparent);margin:0 4px}.tool-separator.print-separator{height:28px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.5),transparent)}.compact-icon{cursor:pointer;color:#fff;transition:all .3s cubic-bezier(.4,0,.2,1);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:6px;background:color-mix(in srgb,#000 40%,transparent);border:1px solid rgba(255,255,255,.2);box-shadow:0 1px 2px #0000001a;flex-shrink:0}.compact-icon.active{color:#fff;background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent;box-shadow:0 8px 25px #667eea4d;transform:scale(1.05)}.compact-icon.active:hover{box-shadow:0 12px 35px #667eea66;transform:scale(1.08)}.compact-icon:hover:not(.active){color:#fff;background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.compact-icon.custom-image-icon{padding:0;background:none;border:none}.compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:transform .2s ease}.compact-icon.custom-image-icon.active{background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent}.compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1);transform:scale(1.1)}.compact-icon.custom-image-icon:hover:not(.active) img{transform:scale(1.05)}.wkt-section{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid rgba(255,255,255,.1);animation:fadeIn .2s ease}.wkt-actions{display:flex;gap:6px;justify-content:space-between}.compact-button{padding:6px 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);color:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);text-transform:uppercase;letter-spacing:.4px;min-height:32px;display:flex;align-items:center;justify-content:center;flex:1;box-shadow:0 1px 2px #0000000d}.compact-button.primary.active{background:linear-gradient(135deg,#667eea,#764ba2);box-shadow:0 8px 25px #667eea4d;color:#fff}.compact-button.primary.active:hover{box-shadow:0 12px 35px #667eea66}.compact-button.secondary{background:color-mix(in srgb,#000 20%,transparent);color:#fff}.compact-button.secondary:hover{background:color-mix(in srgb,#5a6268 60%,transparent)}.compact-button:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);color:#fff}.compact-input{padding:8px 10px;border:none;border-radius:6px;font-size:13px;transition:all .2s ease;background:color-mix(in srgb,#000 60%,transparent);color:#fff}.compact-input:focus{outline:none;border-color:#1976d2;background:color-mix(in srgb,#000 70%,transparent);box-shadow:0 0 0 3px #1976d233}.print-config,.print-draw-config{display:flex;align-items:center;gap:8px;padding:5px;background:color-mix(in srgb,#000 40%,transparent);border-radius:8px;border:1px solid rgba(255,255,255,.1);animation:slideDown .3s ease}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{background:color-mix(in srgb,#000 60%,transparent);border:1px solid rgba(255,255,255,.2);color:#fff;border-radius:6px;font-size:12px;height:32px;min-height:32px;padding:0 10px}.print-config select:focus,.print-config input:focus,.print-draw-config select:focus,.print-draw-config input:focus{outline:none;border-color:#0ea5e9;box-shadow:0 0 0 2px #0ea5e933}.print-config select,.print-draw-config select{cursor:pointer}.print-config option,.print-draw-config option{background:color-mix(in srgb,#000 98%,transparent)!important;border:none!important}.print-config input,.print-draw-config input{width:60px;text-align:center}.print-config button,.print-draw-config button{background:linear-gradient(147.38deg,#10b981,#047857);color:#fff;border:none;padding:6px 12px;border-radius:4px;font-size:12px;cursor:pointer;transition:all .2s ease}.print-config button:hover,.print-draw-config button:hover{box-shadow:0 4px 12px #10b9814d}.print-config .icon-separator,.print-draw-config .icon-separator{color:#70706f}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes pulse{0%{box-shadow:0 0 #667eeab3}70%{box-shadow:0 0 0 6px #667eea00}to{box-shadow:0 0 #667eea00}}.compact-icon.active{animation:pulse 2s infinite}@media (max-width: 768px){.toolbox-wrapper{left:.5em;top:8em;max-width:calc(100vw - 2.5em)}.drag-handle-toolbox{padding:3px 6px;gap:6px}.drag-handle-toolbox mat-icon{font-size:16px;width:16px;height:16px}.toggle-icon{font-size:16px!important;width:16px!important;height:16px!important}.toolbox-container{padding:8px;min-width:28px;max-width:calc(100vw - 3em)}:host{padding:8px;min-width:32px}:host.expanded{min-width:280px;padding:10px}.all-tools-container{flex-wrap:wrap;gap:6px;max-height:50vh;overflow-y:auto;padding:0;-ms-overflow-style:none;scrollbar-width:none}.all-tools-container::-webkit-scrollbar{display:none}.main-tools,.geometry-tools,.measurement-print-tools,.print-tools{display:flex;flex-wrap:wrap;justify-content:center;gap:4px}.compact-icon{width:28px;height:28px;flex-shrink:0}.measurement-print-tools{padding:3px 6px}.geometry-selector{width:100%;margin-bottom:8px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:28px;min-height:28px;padding:0 8px;font-size:12px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:12px}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 2em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:13px!important;min-height:32px!important;padding:6px 12px!important}.print-config,.print-draw-config{flex-direction:column;align-items:stretch;gap:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:11px;padding:4px 8px;height:28px;min-height:28px}.print-config input,.print-draw-config input{width:50px}.print-config button,.print-draw-config button{padding:4px 8px;font-size:11px;height:28px}.wkt-section .compact-input{font-size:12px;padding:6px 8px}.wkt-section .compact-button{font-size:11px;padding:5px 8px;min-height:28px}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:11px!important}}@media (max-width: 480px){.toolbox-wrapper{left:.25em;top:6em;transform-origin:left top;max-width:calc(100vw - .5em)}.drag-handle-toolbox{padding:2px 4px;gap:4px}.drag-handle-toolbox mat-icon{font-size:14px;width:14px;height:14px}.toggle-icon{font-size:14px!important;width:14px!important;height:14px!important}.toolbox-container{padding:6px;min-width:24px}.toolbox-content{gap:8px}.all-tools-container{flex-direction:row;align-items:center;max-height:60vh}.main-tools,.geometry-tools{gap:3px;flex-wrap:wrap;justify-content:center}.measurement-print-tools{gap:3px;flex-wrap:wrap;padding:2px 4px;width:100%;justify-content:center}.measurement-tools,.print-tools{gap:3px}.compact-icon{width:26px;height:26px}.tool-separator{display:none}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:26px;min-height:26px;padding:0 6px;font-size:11px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:11px}.compact-button{font-size:10px;letter-spacing:.2px;padding:4px 6px;min-height:26px}.compact-input{font-size:11px;padding:4px 6px}.print-config,.print-draw-config{gap:4px;padding:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:10px;height:26px;min-height:26px;padding:2px 6px}.print-config input,.print-draw-config input{width:45px}.print-config button,.print-draw-config button{flex:1;min-width:60px;font-size:10px;padding:3px 6px;height:26px}.print-config .icon-separator,.print-draw-config .icon-separator{font-size:10px}.wkt-section{gap:6px;padding-top:8px}::ng-deep .mat-mdc-slide-toggle{transform:scale(.9)}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:10px!important}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 1em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:12px!important;min-height:28px!important;padding:4px 10px!important}}::ng-deep .mat-mdc-simple-snack-bar{background-color:#fff!important}::ng-deep .mat-mdc-tooltip{--mdc-plain-tooltip-container-color: #050505 !important;--mdc-plain-tooltip-supporting-text-color: white !important;border-radius:6px}::ng-deep .mat-mdc-tooltip .mdc-tooltip__surface{background-color:#050505!important;color:#fff!important;border-radius:6px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i8.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }] });
3071
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ToolboxComponent, isStandalone: true, selector: "map-toolbox", inputs: { map: "map", showMeasureDistance: "showMeasureDistance", showMeasureArea: "showMeasureArea", collapsed: "collapsed", settings: "settings", profile: "profile", WKTInputEnabled: "WKTInputEnabled", deleteEnabled: "deleteEnabled" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"toolbox-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\"\n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\">\n <div class=\"drag-handle-toolbox\" cdkDragHandle>\n <mat-icon class=\"left-icon\">handyman</mat-icon>\n \n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleCollapsed($event)\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n \n <div class=\"toolbox-container\" [class.collapsed]=\"collapsed\">\n @if(!collapsed) {\n <div class=\"toolbox-content\">\n @if (showGeometryTypes && filteredGeometryTypeSettings.length > 0) {\n <mat-select class=\"geometry-selector\" (selectionChange)=\"settingsChanged($event)\" [(ngModel)]=\"selectedGeometrySetting\" [compareWith]=\"compareGeometrySetting\">\n @for (setting of filteredGeometryTypeSettings; track setting) {\n <mat-option [value]=\"setting\">{{setting.typeName}}</mat-option>\n }\n </mat-select> \n }\n <div class=\"all-tools-container\">\n <div class=\"main-tools\">\n @if(!settings?.undoDisabled) {\n <img \n [src]=\"undoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"undo()\" \n matTooltip=\"Fortryd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Undo\">\n\n <img \n [src]=\"redoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"redo()\" \n matTooltip=\"Gendan\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Redo\">\n }\n <mat-icon \n class=\"compact-icon\" \n (click)=\"startShowInfo()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Vis info\" \n matTooltipPosition=\"below\">\n info\n </mat-icon>\n <mat-icon \n class=\"compact-icon\" \n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"S\u00F8g punkt\" \n matTooltipPosition=\"below\"\n (click)=\"togglePointSearch()\">adjust\n </mat-icon>\n <mat-icon \n class=\"compact-icon\" \n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"S\u00F8g polygon\" \n matTooltipPosition=\"below\"\n (click)=\"togglePolygonSearch()\">crop_din\n </mat-icon>\n <mat-icon\n class=\"compact-icon\" \n (click)=\"startSelectFeatureHighlight()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Fremh\u00E6v\" \n matTooltipPosition=\"below\"\n >star</mat-icon> // TODO: set correct icon for this\n\n <mat-slide-toggle [(ngModel)]=\"snap\" (change)=\"onSnapChange()\">Snap</mat-slide-toggle>\n @if(settings.editEnabled) {\n <img \n [src]=\"editIconBase64\" \n [class.active]=\"activeMode === 'edit'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEdit()\" \n matTooltip=\"Rediger\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Rediger\">\n\n <img \n [src]=\"removePointsIconBase64\" \n [class.active]=\"activeMode === 'edit-remove'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEditRemovePoints()\" \n matTooltip=\"Fjern punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Fjern punkter\">\n }\n @if(settings.cutHoleEnabled) {\n <img \n [src]=\"trimIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'clip-hole'\" \n (click)=\"clipHole()\" \n matTooltip=\"Klip hul\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Klip hul\">\n }\n @if(settings.splitEnabled) {\n <img \n [src]=\"splitIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'split'\"\n (click)=\"split()\" \n matTooltip=\"Sk\u00E6r over\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Sk\u00E6r over\">\n }\n @if(settings.changeTypeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n [class.active]=\"activeMode === 'change-type'\" \n (click)=\"startChangeType()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Skift type\" \n matTooltipPosition=\"below\">\n find_replace\n </mat-icon>\n }\n @if(settings.mergeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n (click)=\"startMergeFeatures()\" \n matTooltipPosition=\"below\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Saml flader\" \n [class.active]=\"activeMode === 'merge-features'\">\n merge\n </mat-icon>\n }\n @if(settings.centerPoint) {\n <mat-icon\n (click)=\"setCenterPoint()\"\n matTooltipPosition=\"below\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"S\u00E6t centerpunkt\" \n [class.active]=\"activeMode === 'center-point'\"\n >adjust</mat-icon>\n }\n </div>\n <div class=\"geometry-tools\">\n <img \n [src]=\"featureSearchIconBase64\" \n [class.active]=\"activeMode === 'search-feature'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"toggleFeatureSearch()\" \n matTooltip=\"Udpegede objekter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkt\">\n\n <img \n [src]=\"polygonIconBase64\" \n [class.active]=\"activeMode === 'draw-polygon'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPolygon()\" \n matTooltip=\"Polygon\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Polygon\">\n <img \n [src]=\"lineStringIconBase64\" \n [class.active]=\"activeMode === 'draw-linestring'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawLineString()\" \n matTooltip=\"LineString\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"LineString\">\n \n @if(selectedGeometrySetting && selectedGeometrySetting.icons && selectedGeometrySetting.icons.length > 0) {\n @for(iconUrl of selectedGeometrySetting.icons;track iconUrl) {\n <img \n [src]=\"iconUrl\" \n [class.active]=\"activeMode === 'draw-point'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPointUrl(iconUrl)\" \n matTooltip=\"Punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkter\">}\n } @else {\n <img \n [src]=\"pointIconBase64\" \n [class.active]=\"activeMode === 'draw-point'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPoint()\" \n matTooltip=\"Punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkter\">\n }\n <img \n [src]=\"wktIconBase64\" \n class=\"compact-icon secondary custom-image-icon\"\n (click)=\"activateShowInputWKT()\" \n matTooltip=\"WKT\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"WKT\">\n </div>\n <div class=\"tool-separator\" *ngIf=\"deleteEnabled || showMeasureDistance || showMeasureArea || profile.showPrint\"></div>\n <div class=\"measurement-print-tools\">\n <div class=\"measurement-tools\">\n @if (deleteEnabled) {\n <img \n [src]=\"deleteIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'delete'\"\n (click)=\"startDelete()\" \n matTooltip=\"Slet\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Slet\">\n }\n @if (showMeasureDistance) {\n <img \n [src]=\"measureDistanceIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-distance'\"\n (click)=\"startMeasureLength()\" \n matTooltip=\"M\u00E5le afstand\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le afstand\">\n }\n @if(showMeasureArea) {\n <img \n [src]=\"measureAreaIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-area'\"\n (click)=\"startMeasureArea()\" \n matTooltip=\"M\u00E5le areal\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le areal\">\n }\n </div>\n @if(profile.showPrint) {\n <div class=\"tool-separator print-separator\"></div>\n <div class=\"print-tools\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Print'\"\n (click)=\"startPrintMode()\" \n matTooltip=\"Udskriv\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Udskriv\">\n <img \n [src]=\"drawBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Draw'\"\n (click)=\"startDrawMode()\" \n matTooltip=\"Tegn\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Tegn\">\n </div>\n }\n </div>\n </div>\n @if(showInputWKT) {\n <div class=\"wkt-section\">\n <input class=\"compact-input\" [(ngModel)]=\"WKTString\">\n <div class=\"wkt-actions\">\n <button class=\"compact-button primary\" (click)=\"ReadWKT()\">Indl\u00E6s WKT</button>\n <button class=\"compact-button\" (click)=\"cancelWKT()\">Annuller</button>\n </div>\n </div>\n }\n @if(profile.showPrint) {\n @if(active === \"Print\") {\n <div class=\"print-config\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon\"\n (click)=\"doPrint()\" \n alt=\"Udskriv\"> \n <span class=\"icon-separator\">i</span>\n <select id=\"formatSelector\" [(ngModel)]=\"format\">\n <option value=\"image/png\" selected>.PNG</option>\n <option value=\"image/jpeg\">.JPG</option>\n </select>\n <select id=\"dimensionSelector\" [(ngModel)]=\"dimId\" (change)=\"handleDimensionSelected()\">\n <option value=\"99\" selected></option>\n <option value=\"0\">1920 X 1080</option>\n <option value=\"1\">1680 X 1050</option>\n <option value=\"2\">1280 X 800</option>\n <option value=\"3\">800 X 600</option>\n </select>\n <input type=\"text\" [(ngModel)]=\"mapWidth\"/> \n <span class=\"icon-separator\">x</span>\n <input type=\"text\" [(ngModel)]=\"mapHeight\"/>\n <button (click)=\"setNewMapDimensions()\">V\u00E6lg</button>\n </div>\n }\n @if(active === \"PrintDraw\") {\n <div class=\"print-draw-config\">\n <span class=\"icon-separator\">Label</span>\n <input type=\"text\" [(ngModel)]=\"printDrawLabel\"/>\n <select id=\"drawToolSelector\" [(ngModel)]=\"printDrawTool\" (change)=\"handlePrintDrawToolChanged()\">\n <option value=\"Arrow\" selected>Pil</option>\n <option value=\"Point\">Punkt</option>\n <option value=\"LineString\">Linje</option>\n <option value=\"Polygon\">Polygon</option>\n <option value=\"Circle\">Cirkel</option>\n <option value=\"Square\">Kvadrat</option>\n <option value=\"Rectangle\">Firkant</option>\n </select>\n <button class=\"compact-button\" (click)=\"handleClearPrintDrawFeatures()\">Ryd</button>\n </div>\n }\n }\n </div>\n }\n </div>\n @if (activeMode === 'search-feature' && filteredResults.length > 0) {\n <div class=\"geometry-search-panel\">\n <div class=\"buffer-item\">\n <mat-label>Buffer</mat-label>\n <input matInput type=\"number\" name=\"buffer\" [(ngModel)]=\"bufferInMeters\">\n <mat-label>m</mat-label>\n </div>\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\" class=\"search-result-option\">\n <span class=\"result-title\">\n {{ result.title }} ({{result.items.length}} af {{ result.total }})\n </span>\n <div (click)=\"highlight(item.wkt, $event)\" *ngFor=\"let item of result.items\" class=\"search-result-item\">\n <div class=\"item-left\">\n <span class=\"item-header\"> {{item.header}} </span> \n </div>\n <div class=\"item-right\">\n <img \n [src]=\"objectSearchCopyIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"addToActiveObjectsList(item, $event)\" \n matTooltip=\"Kopier\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Kopier\">\n <img \n [src]=\"objectSearchCutIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"cutBySearchedObject(item, $event)\" \n matTooltip=\"Fjern\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Fjern\">\n <img \n [src]=\"objectSearchCutIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"splitBySearchedObject(item, $event)\" \n matTooltip=\"Opsk\u00E6r\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Opsk\u00E6r\">\n <img \n [src]=\"objectSearchCopyWithBufferIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"addToActiveObjectsList(item, $event, true)\" \n matTooltip=\"Kopier med buffer\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Kopier med buffer\">\n <img \n [src]=\"objectSearchZoomIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"zoomSearchedObject(item, $event)\" \n matTooltip=\"Zoom\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Zoom\">\n <button\n (click)=\"toggleItemInfo(item, $event)\">\n <mat-icon class=\"button-icon\">info_outline</mat-icon>\n </button>\n </div> \n @if (item.showMetadata) {\n <div class=\"metadata-panel\">\n @for(feature of item.metadata; track feature) {\n <div class=\"feature-item\">\n <div *ngFor=\"let kv of (feature | keyvalue)\">\n {{ kv.value.name }}:\n <ng-container [ngSwitch]=\"kv.value.kind\">\n <img *ngSwitchCase=\"'img'\" [src]=\"kv.value.data\" alt=\"{{ kv.key }}\" style=\"max-width:240px;\" />\n <a *ngSwitchCase=\"'url'\" [href]=\"kv.value.data\" target=\"_blank\" rel=\"noopener noreferrer\">\n {{ kv.key }}\n <mat-icon class=\"link-icon\">open_in_new</mat-icon>\n </a>\n <span *ngSwitchDefault>{{ kv.value.data }}</span>\n </ng-container>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </mat-option>\n </div>\n }\n @if ((activeMode === \"search-point\" || activeMode === \"search-polygon\") && filteredResults.length > 0) {\n <div class=\"geometry-search-panel\">\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\" class=\"search-result-option\">\n <span class=\"result-title\">\n {{ result.title }} ({{result.items.length}} af {{ result.total }})\n </span>\n <div (click)=\"highlight(item.wkt, $event)\" *ngFor=\"let item of result.items\" class=\"search-result-item\">\n <div class=\"item-left\">\n <span class=\"item-header\"> {{item.header}} </span> \n </div>\n </div>\n </mat-option>\n </div>\n }\n</div>\n", styles: [".toolbox-wrapper{position:absolute;left:1em;top:10em;z-index:10;cursor:grab;max-width:95vw}.toolbox-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}::ng-deep .metadata-panel{margin-top:5px;margin-bottom:5px;border:white;border-width:2px}::ng-deep .geometry-search-panel{border-radius:5px!important;max-height:320px!important;background:color-mix(in srgb,#000 85%,transparent)!important;box-shadow:0 8px 24px #00000026!important;margin-top:10px;overflow-y:auto!important;padding:8px!important;max-width:430px}::ng-deep .geometry-search-panel .buffer-item{align-items:end}::ng-deep .geometry-search-panel .buffer-item input{width:45px;margin:5px}::ng-deep .geometry-search-panel .search-result-item{display:flex;flex-wrap:wrap;margin:5px}::ng-deep .geometry-search-panel .search-result-item .item-left{display:flex;align-items:center;gap:3px;flex:1;min-width:0}::ng-deep .geometry-search-panel .search-result-item .item-right{display:flex;align-items:center;flex:1;justify-content:flex-end;min-width:0;margin-left:10px}::ng-deep .geometry-search-panel .search-result-item .item-right .custom-image-icon{background:#fff!important;margin-left:5px}::ng-deep .geometry-search-panel .search-result-item .item-right button{width:38px;margin-left:5px}::ng-deep .geometry-search-panel .search-result-item .metadata-panel{flex-basis:98%}::ng-deep .geometry-search-panel::-webkit-scrollbar{width:12px}::ng-deep .geometry-search-panel::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .geometry-search-panel::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .geometry-search-panel::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .geometry-search-panel::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}::ng-deep .geometry-search-panel .mat-mdc-option{min-height:30px!important;border-radius:5px!important;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:auto;margin-bottom:6px!important;padding:8px 0!important}::ng-deep .geometry-search-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px;font-weight:400;color:#fff;line-height:1.5}::ng-deep .geometry-search-panel mat-label{color:#fff}.drag-handle-toolbox{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;gap:8px;border-radius:5px 5px 0 0;color:#fff}.drag-handle-toolbox mat-icon{color:#fff;font-size:18px;width:18px;height:18px}.drag-handle-toolbox mat-icon:first-child{flex:1;text-align:left}.right-icons{display:flex;align-items:center;gap:6px}.left-icon{flex-shrink:0}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.toggle-icon:hover{color:#fff;background:#ffffff26}.toggle-icon:active{background:#ffffff40}:host{position:relative;display:flex;justify-content:center}:host.expanded{width:auto;min-width:320px;padding:12px}.toolbox-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;background:#292a2d;box-shadow:#0000004d 0 1px 4px -1px;padding:10px;width:auto;min-width:32px;transition:all .3s ease;cursor:default;border-radius:0 0 5px 5px}.toolbox-container.collapsed{display:none}.toolbox-content{display:flex;flex-direction:column;width:100%;gap:3px;animation:slideDown .3s cubic-bezier(.4,0,.2,1)}.all-tools-container{display:flex;flex-direction:row;align-items:center;flex-wrap:nowrap;width:100%;justify-content:flex-start;overflow-x:hidden}.all-tools-container::-webkit-scrollbar{height:4px}.all-tools-container::-webkit-scrollbar-thumb{background:#ccc;border-radius:2px}.main-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.main-tools ::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:12px;font-weight:500;color:#fff}.geometry-selector{width:100%}.geometry-selector ::ng-deep .mat-mdc-select{font-size:14px;line-height:1.4;border-radius:6px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:32px;min-height:32px;padding:0 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);transition:all .2s ease}.geometry-selector ::ng-deep .mat-mdc-select-trigger:hover{background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:14px;font-weight:500;color:#fff}.geometry-selector ::ng-deep .mat-mdc-select-arrow-wrapper{height:16px;transform:scale(.85)}.geometry-selector ::ng-deep .mat-mdc-form-field-infix{min-height:32px;padding:6px 0}.geometry-selector ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .mat-mdc-select-panel{min-width:fit-content!important;max-width:320px!important;background:color-mix(in srgb,#000 100%,transparent)!important;border:none!important;border-radius:8px!important;box-shadow:0 8px 24px #0000001f,0 2px 6px #00000014!important;margin-top:6px!important;padding:4px 0!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:14px!important;min-height:36px!important;padding:8px 14px!important;transition:all .15s ease!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option:hover:not(.mat-mdc-option-disabled){background:#444849!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option.mat-active{background:color-mix(in srgb,#000 60%,transparent)!important;color:#fff!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px!important;font-weight:500!important;color:#fff!important}::ng-deep .cdk-overlay-pane{z-index:1001}::ng-deep .cdk-overlay-backdrop{z-index:1000}.geometry-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap;background:color-mix(in srgb,#000 20%,transparent);border-radius:8px;padding:4px 8px;border:1px solid rgba(255,255,255,.1)}.measurement-tools,.print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-tools .compact-icon,.print-tools .compact-icon{transition:all .3s cubic-bezier(.4,0,.2,1)}.measurement-tools .compact-icon.custom-image-icon,.print-tools .compact-icon.custom-image-icon{padding:4px;background:none;border:1px solid rgba(255,255,255,.2)}.measurement-tools .compact-icon.custom-image-icon img,.print-tools .compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:filter .2s ease}.measurement-tools .compact-icon.custom-image-icon.active,.print-tools .compact-icon.custom-image-icon.active{background:linear-gradient(147.38deg,#0ea5e9,#075985);border-color:transparent;box-shadow:0 4px 12px #0ea5e966}.measurement-tools .compact-icon.custom-image-icon.active img,.print-tools .compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1)}.measurement-tools .compact-icon.custom-image-icon.active:hover,.print-tools .compact-icon.custom-image-icon.active:hover{box-shadow:0 6px 20px #0ea5e980}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active),.print-tools .compact-icon.custom-image-icon:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);border-color:#0ea5e9;box-shadow:0 2px 8px #0ea5e94d}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active) img,.print-tools .compact-icon.custom-image-icon:hover:not(.active) img{filter:brightness(0) invert(.8)}.print-tools .compact-icon{background:linear-gradient(147.38deg,#4f46e5,#3730a3);color:#fff;border:1px solid rgba(255,255,255,.2)}.print-tools .compact-icon.print-icon{background:linear-gradient(147.38deg,#10b981,#047857)}.print-tools .compact-icon.draw-icon{background:linear-gradient(147.38deg,#f59e0b,#d97706)}.print-tools .compact-icon.active{background:linear-gradient(147.38deg,#ef4444,#dc2626);box-shadow:0 4px 12px #ef444466;border-color:transparent;transform:scale(1.05)}.print-tools .compact-icon.active:hover{box-shadow:0 6px 20px #ef444480;transform:scale(1.08)}.print-tools .compact-icon:hover:not(.active){box-shadow:0 4px 12px #0000004d;opacity:.9}.tool-separator{width:1px;height:24px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.3),transparent);margin:0 4px}.tool-separator.print-separator{height:28px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.5),transparent)}.compact-icon{cursor:pointer;color:#fff;transition:all .3s cubic-bezier(.4,0,.2,1);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:6px;background:color-mix(in srgb,#000 40%,transparent);border:1px solid rgba(255,255,255,.2);box-shadow:0 1px 2px #0000001a;flex-shrink:0}.compact-icon.active{color:#fff;background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent;box-shadow:0 8px 25px #667eea4d;transform:scale(1.05)}.compact-icon.active:hover{box-shadow:0 12px 35px #667eea66;transform:scale(1.08)}.compact-icon:hover:not(.active){color:#fff;background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.compact-icon.custom-image-icon{padding:0;background:none;border:none}.compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:transform .2s ease}.compact-icon.custom-image-icon.active{background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent}.compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1);transform:scale(1.1)}.compact-icon.custom-image-icon:hover:not(.active) img{transform:scale(1.05)}.wkt-section{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid rgba(255,255,255,.1);animation:fadeIn .2s ease}.wkt-actions{display:flex;gap:6px;justify-content:space-between}.compact-button{padding:6px 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);color:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);text-transform:uppercase;letter-spacing:.4px;min-height:32px;display:flex;align-items:center;justify-content:center;flex:1;box-shadow:0 1px 2px #0000000d}.compact-button.primary.active{background:linear-gradient(135deg,#667eea,#764ba2);box-shadow:0 8px 25px #667eea4d;color:#fff}.compact-button.primary.active:hover{box-shadow:0 12px 35px #667eea66}.compact-button.secondary{background:color-mix(in srgb,#000 20%,transparent);color:#fff}.compact-button.secondary:hover{background:color-mix(in srgb,#5a6268 60%,transparent)}.compact-input{padding:8px 10px;border:none;border-radius:6px;font-size:13px;transition:all .2s ease;background:color-mix(in srgb,#000 60%,transparent);color:#fff}.compact-input:focus{outline:none;border-color:#1976d2;background:color-mix(in srgb,#000 70%,transparent);box-shadow:0 0 0 3px #1976d233}.print-config,.print-draw-config{display:flex;align-items:center;gap:8px;padding:5px;background:color-mix(in srgb,#000 40%,transparent);border-radius:8px;border:1px solid rgba(255,255,255,.1);animation:slideDown .3s ease}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{background:color-mix(in srgb,#000 60%,transparent);border:1px solid rgba(255,255,255,.2);color:#fff;border-radius:6px;font-size:12px;height:32px;min-height:32px;padding:0 10px}.print-config select:focus,.print-config input:focus,.print-draw-config select:focus,.print-draw-config input:focus{outline:none;border-color:#0ea5e9;box-shadow:0 0 0 2px #0ea5e933}.print-config select,.print-draw-config select{cursor:pointer}.print-config option,.print-draw-config option{background:color-mix(in srgb,#000 98%,transparent)!important;border:none!important}.print-config input,.print-draw-config input{width:60px;text-align:center}.print-config button,.print-draw-config button{background:linear-gradient(147.38deg,#10b981,#047857);color:#fff;border:none;padding:6px 12px;border-radius:4px;font-size:12px;cursor:pointer;transition:all .2s ease}.print-config button:hover,.print-draw-config button:hover{box-shadow:0 4px 12px #10b9814d}.print-config .icon-separator,.print-draw-config .icon-separator{color:#fff}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes pulse{0%{box-shadow:0 0 #667eeab3}70%{box-shadow:0 0 0 6px #667eea00}to{box-shadow:0 0 #667eea00}}.compact-icon.active{animation:pulse 2s infinite}@media (max-width: 768px){.toolbox-wrapper{left:.5em;top:8em;max-width:calc(100vw - 2.5em)}.drag-handle-toolbox{padding:3px 6px;gap:6px}.drag-handle-toolbox mat-icon{font-size:16px;width:16px;height:16px}.toggle-icon{font-size:16px!important;width:16px!important;height:16px!important}.toolbox-container{padding:8px;min-width:28px;max-width:calc(100vw - 3em)}:host{padding:8px;min-width:32px}:host.expanded{min-width:280px;padding:10px}.all-tools-container{flex-wrap:wrap;gap:6px;max-height:50vh;overflow-y:auto;padding:0;-ms-overflow-style:none;scrollbar-width:none}.all-tools-container::-webkit-scrollbar{display:none}.main-tools,.geometry-tools,.measurement-print-tools,.print-tools{display:flex;flex-wrap:wrap;justify-content:center;gap:4px}.compact-icon{width:28px;height:28px;flex-shrink:0}.measurement-print-tools{padding:3px 6px}.geometry-selector{width:100%;margin-bottom:8px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:28px;min-height:28px;padding:0 8px;font-size:12px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:12px}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 2em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:13px!important;min-height:32px!important;padding:6px 12px!important}.print-config,.print-draw-config{flex-direction:column;align-items:stretch;gap:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:11px;padding:4px 8px;height:28px;min-height:28px}.print-config input,.print-draw-config input{width:50px}.print-config button,.print-draw-config button{padding:4px 8px;font-size:11px;height:28px}.wkt-section .compact-input{font-size:12px;padding:6px 8px}.wkt-section .compact-button{font-size:11px;padding:5px 8px;min-height:28px}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:11px!important}}@media (max-width: 480px){.toolbox-wrapper{left:.25em;top:6em;transform-origin:left top;max-width:calc(100vw - .5em)}.drag-handle-toolbox{padding:2px 4px;gap:4px}.drag-handle-toolbox mat-icon{font-size:14px;width:14px;height:14px}.toggle-icon{font-size:14px!important;width:14px!important;height:14px!important}.toolbox-container{padding:6px;min-width:24px}.toolbox-content{gap:8px}.all-tools-container{flex-direction:row;align-items:center;max-height:60vh}.main-tools,.geometry-tools{gap:3px;flex-wrap:wrap;justify-content:center}.measurement-print-tools{gap:3px;flex-wrap:wrap;padding:2px 4px;width:100%;justify-content:center}.measurement-tools,.print-tools{gap:3px}.compact-icon{width:26px;height:26px}.tool-separator{display:none}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:26px;min-height:26px;padding:0 6px;font-size:11px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:11px}.compact-button{font-size:10px;letter-spacing:.2px;padding:4px 6px;min-height:26px}.compact-input{font-size:11px;padding:4px 6px}.print-config,.print-draw-config{gap:4px;padding:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:10px;height:26px;min-height:26px;padding:2px 6px}.print-config input,.print-draw-config input{width:45px}.print-config button,.print-draw-config button{flex:1;min-width:60px;font-size:10px;padding:3px 6px;height:26px}.print-config .icon-separator,.print-draw-config .icon-separator{font-size:10px}.wkt-section{gap:6px;padding-top:8px}::ng-deep .mat-mdc-slide-toggle{transform:scale(.9)}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:10px!important}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 1em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:12px!important;min-height:28px!important;padding:4px 10px!important}}::ng-deep .mat-mdc-simple-snack-bar{background-color:#fff!important}::ng-deep .mat-mdc-tooltip{--mdc-plain-tooltip-container-color: #050505 !important;--mdc-plain-tooltip-supporting-text-color: white !important;border-radius:6px}::ng-deep .mat-mdc-tooltip .mdc-tooltip__surface{background-color:#050505!important;color:#fff!important;border-radius:6px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "component", type: i6$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i9.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }] });
1746
3072
  }
1747
3073
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ToolboxComponent, decorators: [{
1748
3074
  type: Component,
1749
- args: [{ selector: 'map-toolbox', imports: [FormsModule, CommonModule, MatIconModule, MatOptionModule, MatSelectModule, DragDropModule, MatTooltipModule, MatSlideToggleModule], template: "<div class=\"toolbox-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\"\n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\">\n <div class=\"drag-handle-toolbox\" cdkDragHandle>\n <mat-icon>build_circle</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleCollapsed($event)\">{{ collapsed ? 'flip_to_front' : 'remove' }}</mat-icon> </div>\n <div class=\"toolbox-container\" [class.collapsed]=\"collapsed\">\n @if(!collapsed) {\n <div class=\"toolbox-content\">\n @if (settings.geometryTypeSettings && settings.geometryTypeSettings.length) {\n <mat-select class=\"geometry-selector\" (selectionChange)=\"settingsChanged($event)\" [(ngModel)]=\"selectedGeometrySetting\" [compareWith]=\"compareGeometrySetting\">\n @for (setting of settings.geometryTypeSettings; track setting) {\n <mat-option [value]=\"setting\">{{setting.typeName}}</mat-option>\n }\n </mat-select> \n }\n <div class=\"all-tools-container\">\n <div class=\"main-tools\">\n @if(!settings?.undoDisabled) {\n <img \n [src]=\"undoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"undo()\" \n matTooltip=\"Fortryd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Undo\">\n\n <img \n [src]=\"redoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"redo()\" \n matTooltip=\"Gentag\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Redo\">\n }\n <mat-slide-toggle [(ngModel)]=\"snap\" (change)=\"onSnapChange()\">Snap</mat-slide-toggle>\n @if(settings.editEnabled) {\n <img \n [src]=\"editIconBase64\" \n [class.active]=\"activeMode === 'edit'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEdit()\" \n matTooltip=\"Rediger\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Rediger\">\n\n <img \n [src]=\"removePointsIconBase64\" \n [class.active]=\"activeMode === 'edit-remove'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEditRemovePoints()\" \n matTooltip=\"Fjern punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Fjern punkter\">\n }\n @if(settings.cutHoleEnabled) {\n <img \n [src]=\"trimIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'clip-hole'\" \n (click)=\"clipHole()\" \n matTooltip=\"Klip hul\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Klip hul\">\n }\n @if(settings.splitEnabled) {\n <img \n [src]=\"splitIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'split'\"\n (click)=\"split()\" \n matTooltip=\"Sk\u00E6r over\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Sk\u00E6r over\">\n }\n @if(settings.changeTypeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n [class.active]=\"activeMode === 'change-type'\" \n (click)=\"startChangeType()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Skift type\" \n matTooltipPosition=\"below\">\n find_replace\n </mat-icon>\n }\n @if(settings.mergeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n (click)=\"startMergeFeatures()\" \n matTooltipPosition=\"below\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Saml flader\" \n [class.active]=\"activeMode === 'merge-features'\">\n merge\n </mat-icon>\n }\n </div>\n @if(selectedGeometrySetting && selectedGeometrySetting.availableGeometryTypes?.length) {\n <div class=\"geometry-tools\">\n @for(geomType of selectedGeometrySetting.availableGeometryTypes;track geomType) {\n @if(geomType === 'Polygon') {\n <img \n [src]=\"polygonIconBase64\" \n [class.active]=\"activeMode === 'draw-polygon'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPolygon()\" \n matTooltip=\"Polygon\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Polygon\">\n }\n @if(geomType === 'LineString') {\n <img \n [src]=\"lineStringIconBase64\" \n [class.active]=\"activeMode === 'draw-linestring'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawLineString()\" \n matTooltip=\"LineString\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"LineString\">\n }\n @if(geomType === 'Point') {\n <img \n [src]=\"pointIconBase64\" \n [class.active]=\"activeMode === 'draw-point'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPoint()\" \n matTooltip=\"Punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkter\">\n }\n }\n <img \n [src]=\"wktIconBase64\" \n class=\"compact-icon secondary custom-image-icon\"\n (click)=\"activateShowInputWKT()\" \n matTooltip=\"WKT\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"WKT\">\n </div>\n }\n <div class=\"tool-separator\" *ngIf=\"deleteEnabled || showMeasureDistance || showMeasureArea || profile.showPrint\"></div>\n <div class=\"measurement-print-tools\">\n <div class=\"measurement-tools\">\n @if (deleteEnabled) {\n <img \n [src]=\"deleteIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'delete'\"\n (click)=\"startDelete()\" \n matTooltip=\"Slet\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Slet\">\n }\n @if (showMeasureDistance) {\n <img \n [src]=\"measureDistanceIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-distance'\"\n (click)=\"startMeasureLength()\" \n matTooltip=\"M\u00E5le afstand\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le afstand\">\n }\n @if(showMeasureArea) {\n <img \n [src]=\"measureAreaIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-area'\"\n (click)=\"startMeasureArea()\" \n matTooltip=\"M\u00E5le areal\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le areal\">\n }\n </div>\n @if(profile.showPrint) {\n <div class=\"tool-separator print-separator\"></div>\n <div class=\"print-tools\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Print'\"\n (click)=\"startPrintMode()\" \n matTooltip=\"Udskriv\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Udskriv\">\n <!-- <img \n [src]=\"drawBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Draw'\"\n (click)=\"startDrawMode()\" \n matTooltip=\"Tegn\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Tegn\"> -->\n </div>\n }\n </div>\n </div>\n @if(showInputWKT) {\n <div class=\"wkt-section\">\n <input class=\"compact-input\" [(ngModel)]=\"WKTString\">\n <div class=\"wkt-actions\">\n <button class=\"compact-button primary\" (click)=\"ReadWKT()\">Indl\u00E6s WKT</button>\n <button class=\"compact-button\" (click)=\"cancelWKT()\">Annuller</button>\n </div>\n </div>\n }\n @if(profile.showPrint) {\n @if(active === \"Print\") {\n <div class=\"print-config\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon\"\n (click)=\"doPrint()\" \n alt=\"Udskriv\"> \n <span class=\"icon-separator\">i</span>\n <select id=\"formatSelector\" [(ngModel)]=\"format\">\n <option value=\"image/png\" selected>.PNG</option>\n <option value=\"image/jpeg\">.JPG</option>\n </select>\n <select id=\"dimensionSelector\" [(ngModel)]=\"dimId\" (change)=\"handleDimensionSelected()\">\n <option value=\"99\" selected></option>\n <option value=\"0\">1920 X 1080</option>\n <option value=\"1\">1680 X 1050</option>\n <option value=\"2\">1280 X 800</option>\n <option value=\"3\">800 X 600</option>\n </select>\n <input type=\"text\" [(ngModel)]=\"mapWidth\"/> \n <span class=\"icon-separator\">x</span>\n <input type=\"text\" [(ngModel)]=\"mapHeight\"/>\n <button (click)=\"setNewMapDimensions()\">V\u00E6lg</button>\n </div>\n }\n @if(active === \"PrintDraw\") {\n <div class=\"print-draw-config\">\n <span class=\"icon-separator\">Label</span>\n <input type=\"text\" [(ngModel)]=\"printDrawLabel\"/>\n <select id=\"drawToolSelector\" [(ngModel)]=\"printDrawTool\" (change)=\"handlePrintDrawToolChanged()\">\n <option value=\"arrow\" selected>Pil</option>\n <option value=\"point\">Punkt</option>\n <option value=\"line\">Linje</option>\n <option value=\"polygon\">Polygon</option>\n <option value=\"cirkel\">Cirkel</option>\n <option value=\"square\">Kvadrat</option>\n <option value=\"rectangle\">Firkant</option>\n </select>\n <button>Ryd</button>\n </div>\n }\n }\n </div>\n }\n </div>\n</div>\n", styles: [".toolbox-wrapper{position:absolute;left:1em;top:31.5em;z-index:1000;cursor:grab;max-width:95vw}.toolbox-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}.drag-handle-toolbox{display:flex;align-items:center;justify-content:space-between;background:color-mix(in srgb,#000 60%,transparent);padding:2px 10px;cursor:grab;gap:8px}.drag-handle-toolbox mat-icon{color:#fff;font-size:18px;width:18px;height:18px}.drag-handle-toolbox mat-icon:first-child{flex:1;text-align:center}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.toggle-icon:hover{color:#fff;background:#ffffff26}.toggle-icon:active{background:#ffffff40}:host{position:relative;display:flex;justify-content:center}:host.expanded{width:auto;min-width:320px;padding:12px}.toolbox-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;background:color-mix(in srgb,#000 60%,transparent);box-shadow:#0000004d 0 1px 4px -1px;padding:10px;width:auto;min-width:32px;transition:all .3s ease;cursor:default}.toolbox-container.collapsed{display:none}.toolbox-content{display:flex;flex-direction:column;width:100%;gap:3px;animation:slideDown .3s cubic-bezier(.4,0,.2,1)}.all-tools-container{display:flex;flex-direction:row;align-items:center;flex-wrap:nowrap;width:100%;justify-content:flex-start;overflow-x:hidden}.all-tools-container::-webkit-scrollbar{height:4px}.all-tools-container::-webkit-scrollbar-thumb{background:#ccc;border-radius:2px}.main-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.main-tools ::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:12px;font-weight:500;color:#fff}.geometry-selector{width:100%}.geometry-selector ::ng-deep .mat-mdc-select{font-size:14px;line-height:1.4;border-radius:6px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:32px;min-height:32px;padding:0 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);transition:all .2s ease}.geometry-selector ::ng-deep .mat-mdc-select-trigger:hover{background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:14px;font-weight:500;color:#fff}.geometry-selector ::ng-deep .mat-mdc-select-arrow-wrapper{height:16px;transform:scale(.85)}.geometry-selector ::ng-deep .mat-mdc-form-field-infix{min-height:32px;padding:6px 0}.geometry-selector ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .mat-mdc-select-panel{min-width:fit-content!important;max-width:320px!important;background:color-mix(in srgb,#000 100%,transparent)!important;border:none!important;border-radius:8px!important;box-shadow:0 8px 24px #0000001f,0 2px 6px #00000014!important;margin-top:6px!important;padding:4px 0!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:14px!important;min-height:36px!important;padding:8px 14px!important;transition:all .15s ease!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option:hover:not(.mat-mdc-option-disabled){background:#444849!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option.mat-active{background:color-mix(in srgb,#000 60%,transparent)!important;color:#fff!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px!important;font-weight:500!important;color:#fff!important}::ng-deep .cdk-overlay-pane{z-index:1001}::ng-deep .cdk-overlay-backdrop{z-index:1000}.geometry-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap;background:color-mix(in srgb,#000 20%,transparent);border-radius:8px;padding:4px 8px;border:1px solid rgba(255,255,255,.1)}.measurement-tools,.print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-tools .compact-icon,.print-tools .compact-icon{transition:all .3s cubic-bezier(.4,0,.2,1)}.measurement-tools .compact-icon.custom-image-icon,.print-tools .compact-icon.custom-image-icon{padding:4px;background:none;border:1px solid rgba(255,255,255,.2)}.measurement-tools .compact-icon.custom-image-icon img,.print-tools .compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:filter .2s ease}.measurement-tools .compact-icon.custom-image-icon.active,.print-tools .compact-icon.custom-image-icon.active{background:linear-gradient(147.38deg,#0ea5e9,#075985);border-color:transparent;box-shadow:0 4px 12px #0ea5e966}.measurement-tools .compact-icon.custom-image-icon.active img,.print-tools .compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1)}.measurement-tools .compact-icon.custom-image-icon.active:hover,.print-tools .compact-icon.custom-image-icon.active:hover{box-shadow:0 6px 20px #0ea5e980}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active),.print-tools .compact-icon.custom-image-icon:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);border-color:#0ea5e9;box-shadow:0 2px 8px #0ea5e94d}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active) img,.print-tools .compact-icon.custom-image-icon:hover:not(.active) img{filter:brightness(0) invert(.8)}.print-tools .compact-icon{background:linear-gradient(147.38deg,#4f46e5,#3730a3);color:#fff;border:1px solid rgba(255,255,255,.2)}.print-tools .compact-icon.print-icon{background:linear-gradient(147.38deg,#10b981,#047857)}.print-tools .compact-icon.draw-icon{background:linear-gradient(147.38deg,#f59e0b,#d97706)}.print-tools .compact-icon.active{background:linear-gradient(147.38deg,#ef4444,#dc2626);box-shadow:0 4px 12px #ef444466;border-color:transparent;transform:scale(1.05)}.print-tools .compact-icon.active:hover{box-shadow:0 6px 20px #ef444480;transform:scale(1.08)}.print-tools .compact-icon:hover:not(.active){box-shadow:0 4px 12px #0000004d;opacity:.9}.tool-separator{width:1px;height:24px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.3),transparent);margin:0 4px}.tool-separator.print-separator{height:28px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.5),transparent)}.compact-icon{cursor:pointer;color:#fff;transition:all .3s cubic-bezier(.4,0,.2,1);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:6px;background:color-mix(in srgb,#000 40%,transparent);border:1px solid rgba(255,255,255,.2);box-shadow:0 1px 2px #0000001a;flex-shrink:0}.compact-icon.active{color:#fff;background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent;box-shadow:0 8px 25px #667eea4d;transform:scale(1.05)}.compact-icon.active:hover{box-shadow:0 12px 35px #667eea66;transform:scale(1.08)}.compact-icon:hover:not(.active){color:#fff;background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.compact-icon.custom-image-icon{padding:0;background:none;border:none}.compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:transform .2s ease}.compact-icon.custom-image-icon.active{background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent}.compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1);transform:scale(1.1)}.compact-icon.custom-image-icon:hover:not(.active) img{transform:scale(1.05)}.wkt-section{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid rgba(255,255,255,.1);animation:fadeIn .2s ease}.wkt-actions{display:flex;gap:6px;justify-content:space-between}.compact-button{padding:6px 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);color:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);text-transform:uppercase;letter-spacing:.4px;min-height:32px;display:flex;align-items:center;justify-content:center;flex:1;box-shadow:0 1px 2px #0000000d}.compact-button.primary.active{background:linear-gradient(135deg,#667eea,#764ba2);box-shadow:0 8px 25px #667eea4d;color:#fff}.compact-button.primary.active:hover{box-shadow:0 12px 35px #667eea66}.compact-button.secondary{background:color-mix(in srgb,#000 20%,transparent);color:#fff}.compact-button.secondary:hover{background:color-mix(in srgb,#5a6268 60%,transparent)}.compact-button:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);color:#fff}.compact-input{padding:8px 10px;border:none;border-radius:6px;font-size:13px;transition:all .2s ease;background:color-mix(in srgb,#000 60%,transparent);color:#fff}.compact-input:focus{outline:none;border-color:#1976d2;background:color-mix(in srgb,#000 70%,transparent);box-shadow:0 0 0 3px #1976d233}.print-config,.print-draw-config{display:flex;align-items:center;gap:8px;padding:5px;background:color-mix(in srgb,#000 40%,transparent);border-radius:8px;border:1px solid rgba(255,255,255,.1);animation:slideDown .3s ease}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{background:color-mix(in srgb,#000 60%,transparent);border:1px solid rgba(255,255,255,.2);color:#fff;border-radius:6px;font-size:12px;height:32px;min-height:32px;padding:0 10px}.print-config select:focus,.print-config input:focus,.print-draw-config select:focus,.print-draw-config input:focus{outline:none;border-color:#0ea5e9;box-shadow:0 0 0 2px #0ea5e933}.print-config select,.print-draw-config select{cursor:pointer}.print-config option,.print-draw-config option{background:color-mix(in srgb,#000 98%,transparent)!important;border:none!important}.print-config input,.print-draw-config input{width:60px;text-align:center}.print-config button,.print-draw-config button{background:linear-gradient(147.38deg,#10b981,#047857);color:#fff;border:none;padding:6px 12px;border-radius:4px;font-size:12px;cursor:pointer;transition:all .2s ease}.print-config button:hover,.print-draw-config button:hover{box-shadow:0 4px 12px #10b9814d}.print-config .icon-separator,.print-draw-config .icon-separator{color:#70706f}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes pulse{0%{box-shadow:0 0 #667eeab3}70%{box-shadow:0 0 0 6px #667eea00}to{box-shadow:0 0 #667eea00}}.compact-icon.active{animation:pulse 2s infinite}@media (max-width: 768px){.toolbox-wrapper{left:.5em;top:8em;max-width:calc(100vw - 2.5em)}.drag-handle-toolbox{padding:3px 6px;gap:6px}.drag-handle-toolbox mat-icon{font-size:16px;width:16px;height:16px}.toggle-icon{font-size:16px!important;width:16px!important;height:16px!important}.toolbox-container{padding:8px;min-width:28px;max-width:calc(100vw - 3em)}:host{padding:8px;min-width:32px}:host.expanded{min-width:280px;padding:10px}.all-tools-container{flex-wrap:wrap;gap:6px;max-height:50vh;overflow-y:auto;padding:0;-ms-overflow-style:none;scrollbar-width:none}.all-tools-container::-webkit-scrollbar{display:none}.main-tools,.geometry-tools,.measurement-print-tools,.print-tools{display:flex;flex-wrap:wrap;justify-content:center;gap:4px}.compact-icon{width:28px;height:28px;flex-shrink:0}.measurement-print-tools{padding:3px 6px}.geometry-selector{width:100%;margin-bottom:8px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:28px;min-height:28px;padding:0 8px;font-size:12px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:12px}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 2em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:13px!important;min-height:32px!important;padding:6px 12px!important}.print-config,.print-draw-config{flex-direction:column;align-items:stretch;gap:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:11px;padding:4px 8px;height:28px;min-height:28px}.print-config input,.print-draw-config input{width:50px}.print-config button,.print-draw-config button{padding:4px 8px;font-size:11px;height:28px}.wkt-section .compact-input{font-size:12px;padding:6px 8px}.wkt-section .compact-button{font-size:11px;padding:5px 8px;min-height:28px}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:11px!important}}@media (max-width: 480px){.toolbox-wrapper{left:.25em;top:6em;transform-origin:left top;max-width:calc(100vw - .5em)}.drag-handle-toolbox{padding:2px 4px;gap:4px}.drag-handle-toolbox mat-icon{font-size:14px;width:14px;height:14px}.toggle-icon{font-size:14px!important;width:14px!important;height:14px!important}.toolbox-container{padding:6px;min-width:24px}.toolbox-content{gap:8px}.all-tools-container{flex-direction:row;align-items:center;max-height:60vh}.main-tools,.geometry-tools{gap:3px;flex-wrap:wrap;justify-content:center}.measurement-print-tools{gap:3px;flex-wrap:wrap;padding:2px 4px;width:100%;justify-content:center}.measurement-tools,.print-tools{gap:3px}.compact-icon{width:26px;height:26px}.tool-separator{display:none}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:26px;min-height:26px;padding:0 6px;font-size:11px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:11px}.compact-button{font-size:10px;letter-spacing:.2px;padding:4px 6px;min-height:26px}.compact-input{font-size:11px;padding:4px 6px}.print-config,.print-draw-config{gap:4px;padding:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:10px;height:26px;min-height:26px;padding:2px 6px}.print-config input,.print-draw-config input{width:45px}.print-config button,.print-draw-config button{flex:1;min-width:60px;font-size:10px;padding:3px 6px;height:26px}.print-config .icon-separator,.print-draw-config .icon-separator{font-size:10px}.wkt-section{gap:6px;padding-top:8px}::ng-deep .mat-mdc-slide-toggle{transform:scale(.9)}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:10px!important}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 1em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:12px!important;min-height:28px!important;padding:4px 10px!important}}::ng-deep .mat-mdc-simple-snack-bar{background-color:#fff!important}::ng-deep .mat-mdc-tooltip{--mdc-plain-tooltip-container-color: #050505 !important;--mdc-plain-tooltip-supporting-text-color: white !important;border-radius:6px}::ng-deep .mat-mdc-tooltip .mdc-tooltip__surface{background-color:#050505!important;color:#fff!important;border-radius:6px}\n"] }]
3075
+ args: [{ selector: 'map-toolbox', imports: [FormsModule, CommonModule, MatIconModule, MatOptionModule, MatSelectModule, DragDropModule, MatTooltipModule, MatSlideToggleModule], template: "<div class=\"toolbox-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\"\n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\">\n <div class=\"drag-handle-toolbox\" cdkDragHandle>\n <mat-icon class=\"left-icon\">handyman</mat-icon>\n \n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleCollapsed($event)\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n \n <div class=\"toolbox-container\" [class.collapsed]=\"collapsed\">\n @if(!collapsed) {\n <div class=\"toolbox-content\">\n @if (showGeometryTypes && filteredGeometryTypeSettings.length > 0) {\n <mat-select class=\"geometry-selector\" (selectionChange)=\"settingsChanged($event)\" [(ngModel)]=\"selectedGeometrySetting\" [compareWith]=\"compareGeometrySetting\">\n @for (setting of filteredGeometryTypeSettings; track setting) {\n <mat-option [value]=\"setting\">{{setting.typeName}}</mat-option>\n }\n </mat-select> \n }\n <div class=\"all-tools-container\">\n <div class=\"main-tools\">\n @if(!settings?.undoDisabled) {\n <img \n [src]=\"undoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"undo()\" \n matTooltip=\"Fortryd\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Undo\">\n\n <img \n [src]=\"redoIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"redo()\" \n matTooltip=\"Gendan\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Redo\">\n }\n <mat-icon \n class=\"compact-icon\" \n (click)=\"startShowInfo()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Vis info\" \n matTooltipPosition=\"below\">\n info\n </mat-icon>\n <mat-icon \n class=\"compact-icon\" \n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"S\u00F8g punkt\" \n matTooltipPosition=\"below\"\n (click)=\"togglePointSearch()\">adjust\n </mat-icon>\n <mat-icon \n class=\"compact-icon\" \n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"S\u00F8g polygon\" \n matTooltipPosition=\"below\"\n (click)=\"togglePolygonSearch()\">crop_din\n </mat-icon>\n <mat-icon\n class=\"compact-icon\" \n (click)=\"startSelectFeatureHighlight()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Fremh\u00E6v\" \n matTooltipPosition=\"below\"\n >star</mat-icon> // TODO: set correct icon for this\n\n <mat-slide-toggle [(ngModel)]=\"snap\" (change)=\"onSnapChange()\">Snap</mat-slide-toggle>\n @if(settings.editEnabled) {\n <img \n [src]=\"editIconBase64\" \n [class.active]=\"activeMode === 'edit'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEdit()\" \n matTooltip=\"Rediger\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Rediger\">\n\n <img \n [src]=\"removePointsIconBase64\" \n [class.active]=\"activeMode === 'edit-remove'\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"startEditRemovePoints()\" \n matTooltip=\"Fjern punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Fjern punkter\">\n }\n @if(settings.cutHoleEnabled) {\n <img \n [src]=\"trimIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'clip-hole'\" \n (click)=\"clipHole()\" \n matTooltip=\"Klip hul\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Klip hul\">\n }\n @if(settings.splitEnabled) {\n <img \n [src]=\"splitIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'split'\"\n (click)=\"split()\" \n matTooltip=\"Sk\u00E6r over\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Sk\u00E6r over\">\n }\n @if(settings.changeTypeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n [class.active]=\"activeMode === 'change-type'\" \n (click)=\"startChangeType()\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Skift type\" \n matTooltipPosition=\"below\">\n find_replace\n </mat-icon>\n }\n @if(settings.mergeEnabled) {\n <mat-icon \n class=\"compact-icon\" \n (click)=\"startMergeFeatures()\" \n matTooltipPosition=\"below\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"Saml flader\" \n [class.active]=\"activeMode === 'merge-features'\">\n merge\n </mat-icon>\n }\n @if(settings.centerPoint) {\n <mat-icon\n (click)=\"setCenterPoint()\"\n matTooltipPosition=\"below\"\n [matTooltipShowDelay]=\"200\"\n matTooltipClass=\"custom-tooltip\"\n [matTooltipHideDelay]=\"300\" \n matTooltip=\"S\u00E6t centerpunkt\" \n [class.active]=\"activeMode === 'center-point'\"\n >adjust</mat-icon>\n }\n </div>\n <div class=\"geometry-tools\">\n <img \n [src]=\"featureSearchIconBase64\" \n [class.active]=\"activeMode === 'search-feature'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"toggleFeatureSearch()\" \n matTooltip=\"Udpegede objekter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkt\">\n\n <img \n [src]=\"polygonIconBase64\" \n [class.active]=\"activeMode === 'draw-polygon'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPolygon()\" \n matTooltip=\"Polygon\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Polygon\">\n <img \n [src]=\"lineStringIconBase64\" \n [class.active]=\"activeMode === 'draw-linestring'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawLineString()\" \n matTooltip=\"LineString\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"LineString\">\n \n @if(selectedGeometrySetting && selectedGeometrySetting.icons && selectedGeometrySetting.icons.length > 0) {\n @for(iconUrl of selectedGeometrySetting.icons;track iconUrl) {\n <img \n [src]=\"iconUrl\" \n [class.active]=\"activeMode === 'draw-point'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPointUrl(iconUrl)\" \n matTooltip=\"Punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkter\">}\n } @else {\n <img \n [src]=\"pointIconBase64\" \n [class.active]=\"activeMode === 'draw-point'\"\n class=\"compact-icon primary custom-image-icon\"\n (click)=\"startDrawPoint()\" \n matTooltip=\"Punkter\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Punkter\">\n }\n <img \n [src]=\"wktIconBase64\" \n class=\"compact-icon secondary custom-image-icon\"\n (click)=\"activateShowInputWKT()\" \n matTooltip=\"WKT\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"WKT\">\n </div>\n <div class=\"tool-separator\" *ngIf=\"deleteEnabled || showMeasureDistance || showMeasureArea || profile.showPrint\"></div>\n <div class=\"measurement-print-tools\">\n <div class=\"measurement-tools\">\n @if (deleteEnabled) {\n <img \n [src]=\"deleteIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'delete'\"\n (click)=\"startDelete()\" \n matTooltip=\"Slet\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Slet\">\n }\n @if (showMeasureDistance) {\n <img \n [src]=\"measureDistanceIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-distance'\"\n (click)=\"startMeasureLength()\" \n matTooltip=\"M\u00E5le afstand\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le afstand\">\n }\n @if(showMeasureArea) {\n <img \n [src]=\"measureAreaIconBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"activeMode === 'measure-area'\"\n (click)=\"startMeasureArea()\" \n matTooltip=\"M\u00E5le areal\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"M\u00E5le areal\">\n }\n </div>\n @if(profile.showPrint) {\n <div class=\"tool-separator print-separator\"></div>\n <div class=\"print-tools\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Print'\"\n (click)=\"startPrintMode()\" \n matTooltip=\"Udskriv\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Udskriv\">\n <img \n [src]=\"drawBase64\" \n class=\"compact-icon custom-image-icon\"\n [class.active]=\"active === 'Draw'\"\n (click)=\"startDrawMode()\" \n matTooltip=\"Tegn\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Tegn\">\n </div>\n }\n </div>\n </div>\n @if(showInputWKT) {\n <div class=\"wkt-section\">\n <input class=\"compact-input\" [(ngModel)]=\"WKTString\">\n <div class=\"wkt-actions\">\n <button class=\"compact-button primary\" (click)=\"ReadWKT()\">Indl\u00E6s WKT</button>\n <button class=\"compact-button\" (click)=\"cancelWKT()\">Annuller</button>\n </div>\n </div>\n }\n @if(profile.showPrint) {\n @if(active === \"Print\") {\n <div class=\"print-config\">\n <img \n [src]=\"printBase64\" \n class=\"compact-icon\"\n (click)=\"doPrint()\" \n alt=\"Udskriv\"> \n <span class=\"icon-separator\">i</span>\n <select id=\"formatSelector\" [(ngModel)]=\"format\">\n <option value=\"image/png\" selected>.PNG</option>\n <option value=\"image/jpeg\">.JPG</option>\n </select>\n <select id=\"dimensionSelector\" [(ngModel)]=\"dimId\" (change)=\"handleDimensionSelected()\">\n <option value=\"99\" selected></option>\n <option value=\"0\">1920 X 1080</option>\n <option value=\"1\">1680 X 1050</option>\n <option value=\"2\">1280 X 800</option>\n <option value=\"3\">800 X 600</option>\n </select>\n <input type=\"text\" [(ngModel)]=\"mapWidth\"/> \n <span class=\"icon-separator\">x</span>\n <input type=\"text\" [(ngModel)]=\"mapHeight\"/>\n <button (click)=\"setNewMapDimensions()\">V\u00E6lg</button>\n </div>\n }\n @if(active === \"PrintDraw\") {\n <div class=\"print-draw-config\">\n <span class=\"icon-separator\">Label</span>\n <input type=\"text\" [(ngModel)]=\"printDrawLabel\"/>\n <select id=\"drawToolSelector\" [(ngModel)]=\"printDrawTool\" (change)=\"handlePrintDrawToolChanged()\">\n <option value=\"Arrow\" selected>Pil</option>\n <option value=\"Point\">Punkt</option>\n <option value=\"LineString\">Linje</option>\n <option value=\"Polygon\">Polygon</option>\n <option value=\"Circle\">Cirkel</option>\n <option value=\"Square\">Kvadrat</option>\n <option value=\"Rectangle\">Firkant</option>\n </select>\n <button class=\"compact-button\" (click)=\"handleClearPrintDrawFeatures()\">Ryd</button>\n </div>\n }\n }\n </div>\n }\n </div>\n @if (activeMode === 'search-feature' && filteredResults.length > 0) {\n <div class=\"geometry-search-panel\">\n <div class=\"buffer-item\">\n <mat-label>Buffer</mat-label>\n <input matInput type=\"number\" name=\"buffer\" [(ngModel)]=\"bufferInMeters\">\n <mat-label>m</mat-label>\n </div>\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\" class=\"search-result-option\">\n <span class=\"result-title\">\n {{ result.title }} ({{result.items.length}} af {{ result.total }})\n </span>\n <div (click)=\"highlight(item.wkt, $event)\" *ngFor=\"let item of result.items\" class=\"search-result-item\">\n <div class=\"item-left\">\n <span class=\"item-header\"> {{item.header}} </span> \n </div>\n <div class=\"item-right\">\n <img \n [src]=\"objectSearchCopyIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"addToActiveObjectsList(item, $event)\" \n matTooltip=\"Kopier\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Kopier\">\n <img \n [src]=\"objectSearchCutIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"cutBySearchedObject(item, $event)\" \n matTooltip=\"Fjern\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Fjern\">\n <img \n [src]=\"objectSearchCutIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"splitBySearchedObject(item, $event)\" \n matTooltip=\"Opsk\u00E6r\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Opsk\u00E6r\">\n <img \n [src]=\"objectSearchCopyWithBufferIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"addToActiveObjectsList(item, $event, true)\" \n matTooltip=\"Kopier med buffer\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Kopier med buffer\">\n <img \n [src]=\"objectSearchZoomIconBase64\" \n class=\"compact-icon custom-image-icon\"\n (click)=\"zoomSearchedObject(item, $event)\" \n matTooltip=\"Zoom\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipClass=\"custom-tooltip\"\n matTooltipPosition=\"below\"\n alt=\"Zoom\">\n <button\n (click)=\"toggleItemInfo(item, $event)\">\n <mat-icon class=\"button-icon\">info_outline</mat-icon>\n </button>\n </div> \n @if (item.showMetadata) {\n <div class=\"metadata-panel\">\n @for(feature of item.metadata; track feature) {\n <div class=\"feature-item\">\n <div *ngFor=\"let kv of (feature | keyvalue)\">\n {{ kv.value.name }}:\n <ng-container [ngSwitch]=\"kv.value.kind\">\n <img *ngSwitchCase=\"'img'\" [src]=\"kv.value.data\" alt=\"{{ kv.key }}\" style=\"max-width:240px;\" />\n <a *ngSwitchCase=\"'url'\" [href]=\"kv.value.data\" target=\"_blank\" rel=\"noopener noreferrer\">\n {{ kv.key }}\n <mat-icon class=\"link-icon\">open_in_new</mat-icon>\n </a>\n <span *ngSwitchDefault>{{ kv.value.data }}</span>\n </ng-container>\n </div>\n </div>\n }\n </div>\n }\n </div>\n </mat-option>\n </div>\n }\n @if ((activeMode === \"search-point\" || activeMode === \"search-polygon\") && filteredResults.length > 0) {\n <div class=\"geometry-search-panel\">\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\" class=\"search-result-option\">\n <span class=\"result-title\">\n {{ result.title }} ({{result.items.length}} af {{ result.total }})\n </span>\n <div (click)=\"highlight(item.wkt, $event)\" *ngFor=\"let item of result.items\" class=\"search-result-item\">\n <div class=\"item-left\">\n <span class=\"item-header\"> {{item.header}} </span> \n </div>\n </div>\n </mat-option>\n </div>\n }\n</div>\n", styles: [".toolbox-wrapper{position:absolute;left:1em;top:10em;z-index:10;cursor:grab;max-width:95vw}.toolbox-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}::ng-deep .metadata-panel{margin-top:5px;margin-bottom:5px;border:white;border-width:2px}::ng-deep .geometry-search-panel{border-radius:5px!important;max-height:320px!important;background:color-mix(in srgb,#000 85%,transparent)!important;box-shadow:0 8px 24px #00000026!important;margin-top:10px;overflow-y:auto!important;padding:8px!important;max-width:430px}::ng-deep .geometry-search-panel .buffer-item{align-items:end}::ng-deep .geometry-search-panel .buffer-item input{width:45px;margin:5px}::ng-deep .geometry-search-panel .search-result-item{display:flex;flex-wrap:wrap;margin:5px}::ng-deep .geometry-search-panel .search-result-item .item-left{display:flex;align-items:center;gap:3px;flex:1;min-width:0}::ng-deep .geometry-search-panel .search-result-item .item-right{display:flex;align-items:center;flex:1;justify-content:flex-end;min-width:0;margin-left:10px}::ng-deep .geometry-search-panel .search-result-item .item-right .custom-image-icon{background:#fff!important;margin-left:5px}::ng-deep .geometry-search-panel .search-result-item .item-right button{width:38px;margin-left:5px}::ng-deep .geometry-search-panel .search-result-item .metadata-panel{flex-basis:98%}::ng-deep .geometry-search-panel::-webkit-scrollbar{width:12px}::ng-deep .geometry-search-panel::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .geometry-search-panel::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .geometry-search-panel::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .geometry-search-panel::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .geometry-search-panel::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}::ng-deep .geometry-search-panel .mat-mdc-option{min-height:30px!important;border-radius:5px!important;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:auto;margin-bottom:6px!important;padding:8px 0!important}::ng-deep .geometry-search-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px;font-weight:400;color:#fff;line-height:1.5}::ng-deep .geometry-search-panel mat-label{color:#fff}.drag-handle-toolbox{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;gap:8px;border-radius:5px 5px 0 0;color:#fff}.drag-handle-toolbox mat-icon{color:#fff;font-size:18px;width:18px;height:18px}.drag-handle-toolbox mat-icon:first-child{flex:1;text-align:left}.right-icons{display:flex;align-items:center;gap:6px}.left-icon{flex-shrink:0}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.toggle-icon:hover{color:#fff;background:#ffffff26}.toggle-icon:active{background:#ffffff40}:host{position:relative;display:flex;justify-content:center}:host.expanded{width:auto;min-width:320px;padding:12px}.toolbox-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;background:#292a2d;box-shadow:#0000004d 0 1px 4px -1px;padding:10px;width:auto;min-width:32px;transition:all .3s ease;cursor:default;border-radius:0 0 5px 5px}.toolbox-container.collapsed{display:none}.toolbox-content{display:flex;flex-direction:column;width:100%;gap:3px;animation:slideDown .3s cubic-bezier(.4,0,.2,1)}.all-tools-container{display:flex;flex-direction:row;align-items:center;flex-wrap:nowrap;width:100%;justify-content:flex-start;overflow-x:hidden}.all-tools-container::-webkit-scrollbar{height:4px}.all-tools-container::-webkit-scrollbar-thumb{background:#ccc;border-radius:2px}.main-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.main-tools ::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:12px;font-weight:500;color:#fff}.geometry-selector{width:100%}.geometry-selector ::ng-deep .mat-mdc-select{font-size:14px;line-height:1.4;border-radius:6px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:32px;min-height:32px;padding:0 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);transition:all .2s ease}.geometry-selector ::ng-deep .mat-mdc-select-trigger:hover{background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:14px;font-weight:500;color:#fff}.geometry-selector ::ng-deep .mat-mdc-select-arrow-wrapper{height:16px;transform:scale(.85)}.geometry-selector ::ng-deep .mat-mdc-form-field-infix{min-height:32px;padding:6px 0}.geometry-selector ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .mat-mdc-select-panel{min-width:fit-content!important;max-width:320px!important;background:color-mix(in srgb,#000 100%,transparent)!important;border:none!important;border-radius:8px!important;box-shadow:0 8px 24px #0000001f,0 2px 6px #00000014!important;margin-top:6px!important;padding:4px 0!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:14px!important;min-height:36px!important;padding:8px 14px!important;transition:all .15s ease!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option:hover:not(.mat-mdc-option-disabled){background:#444849!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option.mat-active{background:color-mix(in srgb,#000 60%,transparent)!important;color:#fff!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px!important;font-weight:500!important;color:#fff!important}::ng-deep .cdk-overlay-pane{z-index:1001}::ng-deep .cdk-overlay-backdrop{z-index:1000}.geometry-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap;background:color-mix(in srgb,#000 20%,transparent);border-radius:8px;padding:4px 8px;border:1px solid rgba(255,255,255,.1)}.measurement-tools,.print-tools{display:flex;flex-direction:row;align-items:center;gap:6px;flex-wrap:nowrap}.measurement-tools .compact-icon,.print-tools .compact-icon{transition:all .3s cubic-bezier(.4,0,.2,1)}.measurement-tools .compact-icon.custom-image-icon,.print-tools .compact-icon.custom-image-icon{padding:4px;background:none;border:1px solid rgba(255,255,255,.2)}.measurement-tools .compact-icon.custom-image-icon img,.print-tools .compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:filter .2s ease}.measurement-tools .compact-icon.custom-image-icon.active,.print-tools .compact-icon.custom-image-icon.active{background:linear-gradient(147.38deg,#0ea5e9,#075985);border-color:transparent;box-shadow:0 4px 12px #0ea5e966}.measurement-tools .compact-icon.custom-image-icon.active img,.print-tools .compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1)}.measurement-tools .compact-icon.custom-image-icon.active:hover,.print-tools .compact-icon.custom-image-icon.active:hover{box-shadow:0 6px 20px #0ea5e980}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active),.print-tools .compact-icon.custom-image-icon:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);border-color:#0ea5e9;box-shadow:0 2px 8px #0ea5e94d}.measurement-tools .compact-icon.custom-image-icon:hover:not(.active) img,.print-tools .compact-icon.custom-image-icon:hover:not(.active) img{filter:brightness(0) invert(.8)}.print-tools .compact-icon{background:linear-gradient(147.38deg,#4f46e5,#3730a3);color:#fff;border:1px solid rgba(255,255,255,.2)}.print-tools .compact-icon.print-icon{background:linear-gradient(147.38deg,#10b981,#047857)}.print-tools .compact-icon.draw-icon{background:linear-gradient(147.38deg,#f59e0b,#d97706)}.print-tools .compact-icon.active{background:linear-gradient(147.38deg,#ef4444,#dc2626);box-shadow:0 4px 12px #ef444466;border-color:transparent;transform:scale(1.05)}.print-tools .compact-icon.active:hover{box-shadow:0 6px 20px #ef444480;transform:scale(1.08)}.print-tools .compact-icon:hover:not(.active){box-shadow:0 4px 12px #0000004d;opacity:.9}.tool-separator{width:1px;height:24px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.3),transparent);margin:0 4px}.tool-separator.print-separator{height:28px;background:linear-gradient(to bottom,transparent,rgba(255,255,255,.5),transparent)}.compact-icon{cursor:pointer;color:#fff;transition:all .3s cubic-bezier(.4,0,.2,1);width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:6px;background:color-mix(in srgb,#000 40%,transparent);border:1px solid rgba(255,255,255,.2);box-shadow:0 1px 2px #0000001a;flex-shrink:0}.compact-icon.active{color:#fff;background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent;box-shadow:0 8px 25px #667eea4d;transform:scale(1.05)}.compact-icon.active:hover{box-shadow:0 12px 35px #667eea66;transform:scale(1.08)}.compact-icon:hover:not(.active){color:#fff;background:color-mix(in srgb,#000 70%,transparent);border-color:#667eea;box-shadow:0 4px 12px #0003}.compact-icon.custom-image-icon{padding:0;background:none;border:none}.compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:transform .2s ease}.compact-icon.custom-image-icon.active{background:linear-gradient(135deg,#667eea,#764ba2);border-color:transparent}.compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1);transform:scale(1.1)}.compact-icon.custom-image-icon:hover:not(.active) img{transform:scale(1.05)}.wkt-section{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px solid rgba(255,255,255,.1);animation:fadeIn .2s ease}.wkt-actions{display:flex;gap:6px;justify-content:space-between}.compact-button{padding:6px 10px;border:none;border-radius:6px;background:color-mix(in srgb,#000 60%,transparent);color:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);text-transform:uppercase;letter-spacing:.4px;min-height:32px;display:flex;align-items:center;justify-content:center;flex:1;box-shadow:0 1px 2px #0000000d}.compact-button.primary.active{background:linear-gradient(135deg,#667eea,#764ba2);box-shadow:0 8px 25px #667eea4d;color:#fff}.compact-button.primary.active:hover{box-shadow:0 12px 35px #667eea66}.compact-button.secondary{background:color-mix(in srgb,#000 20%,transparent);color:#fff}.compact-button.secondary:hover{background:color-mix(in srgb,#5a6268 60%,transparent)}.compact-input{padding:8px 10px;border:none;border-radius:6px;font-size:13px;transition:all .2s ease;background:color-mix(in srgb,#000 60%,transparent);color:#fff}.compact-input:focus{outline:none;border-color:#1976d2;background:color-mix(in srgb,#000 70%,transparent);box-shadow:0 0 0 3px #1976d233}.print-config,.print-draw-config{display:flex;align-items:center;gap:8px;padding:5px;background:color-mix(in srgb,#000 40%,transparent);border-radius:8px;border:1px solid rgba(255,255,255,.1);animation:slideDown .3s ease}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{background:color-mix(in srgb,#000 60%,transparent);border:1px solid rgba(255,255,255,.2);color:#fff;border-radius:6px;font-size:12px;height:32px;min-height:32px;padding:0 10px}.print-config select:focus,.print-config input:focus,.print-draw-config select:focus,.print-draw-config input:focus{outline:none;border-color:#0ea5e9;box-shadow:0 0 0 2px #0ea5e933}.print-config select,.print-draw-config select{cursor:pointer}.print-config option,.print-draw-config option{background:color-mix(in srgb,#000 98%,transparent)!important;border:none!important}.print-config input,.print-draw-config input{width:60px;text-align:center}.print-config button,.print-draw-config button{background:linear-gradient(147.38deg,#10b981,#047857);color:#fff;border:none;padding:6px 12px;border-radius:4px;font-size:12px;cursor:pointer;transition:all .2s ease}.print-config button:hover,.print-draw-config button:hover{box-shadow:0 4px 12px #10b9814d}.print-config .icon-separator,.print-draw-config .icon-separator{color:#fff}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes pulse{0%{box-shadow:0 0 #667eeab3}70%{box-shadow:0 0 0 6px #667eea00}to{box-shadow:0 0 #667eea00}}.compact-icon.active{animation:pulse 2s infinite}@media (max-width: 768px){.toolbox-wrapper{left:.5em;top:8em;max-width:calc(100vw - 2.5em)}.drag-handle-toolbox{padding:3px 6px;gap:6px}.drag-handle-toolbox mat-icon{font-size:16px;width:16px;height:16px}.toggle-icon{font-size:16px!important;width:16px!important;height:16px!important}.toolbox-container{padding:8px;min-width:28px;max-width:calc(100vw - 3em)}:host{padding:8px;min-width:32px}:host.expanded{min-width:280px;padding:10px}.all-tools-container{flex-wrap:wrap;gap:6px;max-height:50vh;overflow-y:auto;padding:0;-ms-overflow-style:none;scrollbar-width:none}.all-tools-container::-webkit-scrollbar{display:none}.main-tools,.geometry-tools,.measurement-print-tools,.print-tools{display:flex;flex-wrap:wrap;justify-content:center;gap:4px}.compact-icon{width:28px;height:28px;flex-shrink:0}.measurement-print-tools{padding:3px 6px}.geometry-selector{width:100%;margin-bottom:8px}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:28px;min-height:28px;padding:0 8px;font-size:12px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:12px}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 2em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:13px!important;min-height:32px!important;padding:6px 12px!important}.print-config,.print-draw-config{flex-direction:column;align-items:stretch;gap:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:11px;padding:4px 8px;height:28px;min-height:28px}.print-config input,.print-draw-config input{width:50px}.print-config button,.print-draw-config button{padding:4px 8px;font-size:11px;height:28px}.wkt-section .compact-input{font-size:12px;padding:6px 8px}.wkt-section .compact-button{font-size:11px;padding:5px 8px;min-height:28px}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:11px!important}}@media (max-width: 480px){.toolbox-wrapper{left:.25em;top:6em;transform-origin:left top;max-width:calc(100vw - .5em)}.drag-handle-toolbox{padding:2px 4px;gap:4px}.drag-handle-toolbox mat-icon{font-size:14px;width:14px;height:14px}.toggle-icon{font-size:14px!important;width:14px!important;height:14px!important}.toolbox-container{padding:6px;min-width:24px}.toolbox-content{gap:8px}.all-tools-container{flex-direction:row;align-items:center;max-height:60vh}.main-tools,.geometry-tools{gap:3px;flex-wrap:wrap;justify-content:center}.measurement-print-tools{gap:3px;flex-wrap:wrap;padding:2px 4px;width:100%;justify-content:center}.measurement-tools,.print-tools{gap:3px}.compact-icon{width:26px;height:26px}.tool-separator{display:none}.geometry-selector ::ng-deep .mat-mdc-select-trigger{height:26px;min-height:26px;padding:0 6px;font-size:11px}.geometry-selector ::ng-deep .mat-mdc-select-value{font-size:11px}.compact-button{font-size:10px;letter-spacing:.2px;padding:4px 6px;min-height:26px}.compact-input{font-size:11px;padding:4px 6px}.print-config,.print-draw-config{gap:4px;padding:6px}.print-config select,.print-config input,.print-draw-config select,.print-draw-config input{font-size:10px;height:26px;min-height:26px;padding:2px 6px}.print-config input,.print-draw-config input{width:45px}.print-config button,.print-draw-config button{flex:1;min-width:60px;font-size:10px;padding:3px 6px;height:26px}.print-config .icon-separator,.print-draw-config .icon-separator{font-size:10px}.wkt-section{gap:6px;padding-top:8px}::ng-deep .mat-mdc-slide-toggle{transform:scale(.9)}::ng-deep .mat-mdc-slide-toggle .mdc-label{font-size:10px!important}::ng-deep .mat-mdc-select-panel{max-width:calc(100vw - 1em)!important}::ng-deep .mat-mdc-select-panel .mat-mdc-option{font-size:12px!important;min-height:28px!important;padding:4px 10px!important}}::ng-deep .mat-mdc-simple-snack-bar{background-color:#fff!important}::ng-deep .mat-mdc-tooltip{--mdc-plain-tooltip-container-color: #050505 !important;--mdc-plain-tooltip-supporting-text-color: white !important;border-radius:6px}::ng-deep .mat-mdc-tooltip .mdc-tooltip__surface{background-color:#050505!important;color:#fff!important;border-radius:6px}\n"] }]
1750
3076
  }], ctorParameters: () => [], propDecorators: { map: [{
1751
3077
  type: Input,
1752
3078
  args: [{ required: true }]
@@ -1768,46 +3094,134 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
1768
3094
  type: Input
1769
3095
  }] } });
1770
3096
 
3097
+ class ConflictAnalysisSearchProvider extends SearchProviderBase {
3098
+ _current = inject(CurrentItemsService);
3099
+ getSearchableLayers() {
3100
+ return this._current.profile ? this._current.profile.layerGroups.flatMap(lg => lg.layers).filter(l => l.useInConflictAnalysis) : [];
3101
+ }
3102
+ getFilter(searchValue, layer) {
3103
+ return contains(layer.geometryField, searchValue);
3104
+ }
3105
+ map(l, f) {
3106
+ return {
3107
+ conflict: f.features.length > 0,
3108
+ layerName: l.name
3109
+ };
3110
+ }
3111
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConflictAnalysisSearchProvider, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
3112
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConflictAnalysisSearchProvider });
3113
+ }
3114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ConflictAnalysisSearchProvider, decorators: [{
3115
+ type: Injectable
3116
+ }] });
3117
+
1771
3118
  class ActiveObjectsComponent {
1772
3119
  _drawLayerService = inject(DrawLayerSourceService);
3120
+ _centerPointService = inject(CenterPointService);
3121
+ _conflictService = inject(ConflictAnalysisSearchProvider);
3122
+ _undoRedo = inject(UndoRedoService);
3123
+ isZoomedToAll = false;
1773
3124
  features$ = this._drawLayerService.features$;
1774
- collapsed = false;
3125
+ collapsed = true;
1775
3126
  settings;
3127
+ sessionDone = new EventEmitter();
3128
+ profile;
1776
3129
  activeFeatures = [];
1777
3130
  _featureHelper = inject(FeatureHelperService);
1778
- _highlight = inject(highlightService);
3131
+ _highlight = inject(HighlightService);
1779
3132
  _zoomService = inject(ZoomService);
3133
+ _current = inject(CurrentItemsService);
3134
+ _layoutService = inject(LayoutService);
1780
3135
  opacity = 1;
1781
3136
  dragPosition = { x: 0, y: 0 };
1782
3137
  highlightedId = null;
1783
3138
  POSITION_STORAGE_KEY = 'activeObjects';
3139
+ _hasCenterpoint = false;
3140
+ _featuresForHighlight$ = this._drawLayerService.selectedForHighlight$;
3141
+ get centerPointOk() {
3142
+ return this._hasCenterpoint || !this._current.gisKomponentSettings.centerPoint || !this._current.gisKomponentSettings.centerPoint.required;
3143
+ }
3144
+ conflictAnalysisAvailable = false;
3145
+ get allFeaturesOk() {
3146
+ return !this._current.gisKomponentSettings.containsCheck ||
3147
+ !this._current.gisKomponentSettings.containsCheck.requiredForEndSession ||
3148
+ this.activeFeatures.flatMap(af => af.features).every(f => f.containedOk === 'yes');
3149
+ }
3150
+ get endSessionEnabled() {
3151
+ return !this._current.gisKomponentSettings.endSessionDisabled && this.allFeaturesOk;
3152
+ }
1784
3153
  constructor() {
1785
3154
  this.features$.subscribe({
1786
3155
  next: features => {
3156
+ this._hasCenterpoint = features.some(f => this._centerPointService.isCenterpoint(f));
1787
3157
  const featureTypes = [...new Set(features.map(f => this._featureHelper.typeId(f)).filter(typeId => !!typeId))];
1788
3158
  const featuresObj = featureTypes
1789
3159
  .map(ft => ({ featureType: ft,
1790
3160
  display: this.settings.geometryTypeSettings.find(gts => gts.typeId === ft).typeName,
1791
3161
  features: features.filter(f => this._featureHelper.typeId(f) === ft)
1792
- .map(f => ({ id: f.getId(), feature: f, locked: this._featureHelper.isLocked(f), area: this._getAreaString(f) })) }));
3162
+ .map(f => ({
3163
+ id: f.getId(),
3164
+ feature: f,
3165
+ containedOk: this.conflictAnalysisAvailable ? 'unknown' : 'yes',
3166
+ locked: this._featureHelper.isLocked(f),
3167
+ area: this._getAreaString(f)
3168
+ })) }));
1793
3169
  this.activeFeatures = [...featuresObj];
3170
+ this.runConflictAnalysis();
3171
+ }
3172
+ });
3173
+ this._featuresForHighlight$.subscribe({
3174
+ next: features => {
3175
+ console.log("🚀 ~ ActiveObjectsComponent ~ constructor ~ features:", features);
1794
3176
  }
1795
3177
  });
1796
3178
  }
1797
3179
  ngOnInit() {
1798
3180
  this._loadPosition();
3181
+ this.conflictAnalysisAvailable = !!this._current.gisKomponentSettings.containsCheck && this._current.gisKomponentSettings.containsCheck.enabled &&
3182
+ this._current.profile.layerGroups.flatMap(lg => lg.layers).some(l => l.useInConflictAnalysis);
3183
+ }
3184
+ endSession() {
3185
+ this.sessionDone.emit();
3186
+ }
3187
+ runConflictAnalysis() {
3188
+ this.activeFeatures.forEach(af => {
3189
+ const unlockedFeatures = af.features.filter(f => !f.locked);
3190
+ // unlockedFeatures.forEach(f => f.conflicts = 'unknown');
3191
+ const conflictSearches$ = unlockedFeatures.map(f => this._conflictService.search(f.feature.getGeometry(), 1));
3192
+ combineLatest(conflictSearches$).subscribe({
3193
+ next: result => {
3194
+ result.forEach((f, i) => {
3195
+ af.features[i].containedOk = f.some(fx => fx.conflict) ? 'yes' : 'no';
3196
+ af.features[i].containedMissingIn = f.filter(fx => !fx.conflict).map(fx => fx.layerName);
3197
+ });
3198
+ }
3199
+ });
3200
+ });
3201
+ }
3202
+ ngOnChanges(changes) {
3203
+ if (changes['profile'] && this.profile) {
3204
+ this._loadPosition();
3205
+ }
1799
3206
  }
1800
3207
  _getAreaString(feature) {
1801
- if (feature.getGeometry()?.getType() !== 'Polygon') { // We only have polygon, linestring and points and only polygons have area
3208
+ if (feature.getGeometry()?.getType() !== 'Polygon' && feature.getGeometry()?.getType() !== 'MultiPolygon') { // We only have polygon, linestring and points and only multi/polygons have area
1802
3209
  return '';
1803
3210
  }
1804
3211
  return this.settings.sizeInHa ? `(${(getArea(feature.getGeometry()) / 10000).toFixed(2)} ha)` : `(${getArea(feature.getGeometry()).toFixed(2)} m2)`;
1805
3212
  }
3213
+ reset() {
3214
+ this._undoRedo.reset();
3215
+ this.highlightedId = null;
3216
+ this._highlight.clear();
3217
+ this.zoomToAll();
3218
+ }
1806
3219
  zoomToAll() {
1807
- const formatter = new GeoJSON({ dataProjection: 'EPSG:25832', featureProjection: 'EPSG:25832' });
1808
- const allTheFeatures = formatter.writeFeaturesObject(this._drawLayerService.allFeatures);
1809
- console.log("🚀 ~ ActiveObjectsComponent ~ zoomToAll ~ allTheFeatures:", JSON.stringify(allTheFeatures));
1810
3220
  this._zoomService.zoomToFeatures(this._drawLayerService.allFeatures);
3221
+ this.isZoomedToAll = true;
3222
+ }
3223
+ onMapInteraction() {
3224
+ this.isZoomedToAll = false;
1811
3225
  }
1812
3226
  highlight(id) {
1813
3227
  if (this.highlightedId === id) {
@@ -1827,6 +3241,7 @@ class ActiveObjectsComponent {
1827
3241
  }
1828
3242
  delete(id) {
1829
3243
  this._drawLayerService.remove(id);
3244
+ this._centerPointService.handleFeatureDeleted(id);
1830
3245
  this._highlight.clear();
1831
3246
  }
1832
3247
  togglePanel() {
@@ -1851,6 +3266,9 @@ class ActiveObjectsComponent {
1851
3266
  if (savedPosition) {
1852
3267
  this.dragPosition = JSON.parse(savedPosition);
1853
3268
  }
3269
+ else if (this.profile) {
3270
+ this.dragPosition = this._layoutService.loadInitialPositionFromProfile(this.POSITION_STORAGE_KEY, this.profile.activeObjectsInitialPosition);
3271
+ }
1854
3272
  }
1855
3273
  catch (error) {
1856
3274
  console.error('Error loading position from localStorage:', error);
@@ -1858,33 +3276,167 @@ class ActiveObjectsComponent {
1858
3276
  }
1859
3277
  }
1860
3278
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ActiveObjectsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1861
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ActiveObjectsComponent, isStandalone: true, selector: "activeObjects", inputs: { settings: "settings" }, ngImport: i0, template: "<div class=\"active-objects-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle-active-objects\" cdkDragHandle>\n <mat-icon class=\"zoom-icon\" (click)=\"zoomToAll()\">share_reviewsr</mat-icon>\n @if(!collapsed) {\n <h4>Aktive flader</h4>\n }\n <mat-icon class=\"toggle-icon\" (click)=\"togglePanel()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n <div class=\"active-objects-container\">\n @if(!collapsed) {\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"opacity\"\n (input)=\"updateOpacity()\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n }\n\n @if(!collapsed) {\n <div class=\"active-objects-content\">\n <mat-icon class=\"zoom-icon\" (click)=\"zoomToAll()\">highlight</mat-icon>\n @for(featureTypeObj of activeFeatures; track featureTypeObj.featureType) {\n <mat-expansion-panel>\n <mat-expansion-panel-header \n [matTooltip]=\"featureTypeObj.display\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"right\">\n <span class=\"panel-title\">\n {{featureTypeObj.display}} ({{featureTypeObj.features.length}})\n </span>\n </mat-expansion-panel-header>\n <div class=\"item-list\">\n @for(item of featureTypeObj.features; track item.id) {\n <div class=\"item\">\n <div class=\"item-text\">\n <span class=\"item-id\">{{item.id}}</span>\n <span class=\"item-area\">{{item.area}}</span>\n </div>\n <mat-icon *ngIf=\"!item.locked\" (click)=\"delete(item.id)\">delete</mat-icon>\n <mat-icon (click)=\"highlight(item.id)\" [class.highlight-active]=\"highlightedId === item.id\">highlight</mat-icon>\n </div>\n }\n </div>\n </mat-expansion-panel>\n }\n </div>\n }\n </div> \n</div>", styles: ["@charset \"UTF-8\";.active-objects-wrapper{position:absolute;top:15em;left:1em;z-index:1000;cursor:grab;box-shadow:0 2px 10px #0000001a;background:color-mix(in srgb,#000 60%,transparent);width:fit-content;max-width:295px;transition:width .3s ease}.active-objects-wrapper.collapsed{width:70px;max-width:150px}.active-objects-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .panel-title{display:block;overflow:visible;text-overflow:clip;white-space:normal;max-width:220px;margin-right:10px;line-height:1.3;word-break:break-word}.item{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-radius:4px;background:color-mix(in srgb,#000 60%,transparent)!important;transition:all .2s ease;font-size:14px;color:#fff;min-height:40px;cursor:default}.item .item-text{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}.item .item-id{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:2px}.item .item-area{font-size:12px;color:#ccc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.item mat-icon{cursor:pointer;color:#f44336;font-size:18px;width:18px;height:18px;flex-shrink:0;margin-left:8px;transition:color .2s ease,background .2s ease}.item mat-icon:hover{color:#d32f2f}.highlight-active{color:#4caf50!important;text-shadow:0 0 6px #4caf50;transform:scale(1.1);transition:all .2s ease}.drag-handle-active-objects{display:flex;align-items:center;justify-content:space-between;background:color-mix(in srgb,#000 0%,transparent);border-radius:8px 8px 0 0;padding:4px 8px;cursor:grab}.drag-handle-active-objects h4{margin:0;padding:5px;background:transparent;font-weight:600;color:#fff;flex:1;font-size:16px;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.drag-handle-active-objects .zoom-icon{color:#fff;font-size:18px;width:18px;height:18px}.drag-handle-active-objects .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:22px;width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:6px;flex-shrink:0;margin:0}.drag-handle-active-objects .toggle-icon:hover{color:#ffffffb3;background:#1976d214}.active-objects-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;transition:all .3s ease;cursor:default;box-sizing:border-box}.active-objects-container .panel-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:5px;cursor:default}.active-objects-container .panel-header h4{margin:0;padding:5px;background:transparent;font-weight:600;color:#fff;flex:1;font-size:16px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.active-objects-container .panel-header .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:22px;width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:6px;flex-shrink:0;margin:0}.active-objects-container .panel-header .toggle-icon:hover{color:#ffffffb3;background:#1976d214}.active-objects-container .collapsed-title{color:#fff;font-weight:600;font-size:12px;padding:8px 4px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.active-objects-container .collapsed-title:hover{color:#1976d2}.active-objects-content{display:block;max-height:278px;overflow:auto}.active-objects-content::-webkit-scrollbar{width:8px;border-radius:10px;border:5px solid transparent}.active-objects-content::-webkit-scrollbar-thumb{background:#0000004d;border:5px solid transparent;border-radius:10px;background-clip:padding-box}.active-objects-content::-webkit-scrollbar-thumb:hover{background:#0006;background-clip:padding-box;border:3px solid transparent}.active-objects-content .zoom-icon{color:#4caf50!important;border-radius:50%;padding:6px;margin:4px;cursor:pointer;transition:all .3s ease;font-size:20px;width:20px;height:20px}.active-objects-content .zoom-icon:hover{background:#4caf5033!important}mat-expansion-panel{border-radius:0!important;box-shadow:none!important}mat-expansion-panel:last-child{border-bottom:none}mat-expansion-panel ::ng-deep .mat-expansion-panel-header{padding:0 16px;font-weight:500}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .mat-content{display:flex;justify-content:space-between;align-items:center;color:#fff}mat-expansion-panel ::ng-deep .mat-expansion-panel-body{padding:0}.item-list{display:flex;flex-direction:column;gap:4px;padding:8px;max-height:300px;overflow-y:auto}.item-list::-webkit-scrollbar{width:6px}.item-list::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.item-list::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:3px}.item-list::-webkit-scrollbar-thumb:hover{background:#a8a8a8}@media (max-width: 768px){.active-objects-wrapper{right:.5em;top:8em;max-width:280px;width:280px}.item{font-size:13px;padding:6px 10px}}@media (max-width: 480px){.active-objects-wrapper{right:.5em;left:.5em;max-width:calc(100vw - 1em);width:auto}.item-list{max-height:200px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
3279
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: ActiveObjectsComponent, isStandalone: true, selector: "activeObjects", inputs: { settings: "settings", profile: "profile" }, outputs: { sessionDone: "sessionDone" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"active-objects-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle-active-objects\" cdkDragHandle>\n <mat-icon class=\"zoom-icon\" (click)=\"zoomToAll()\">dynamic_form</mat-icon>\n \n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"togglePanel()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n \n <div class=\"active-objects-container\">\n @if(!collapsed) {\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"opacity\"\n (input)=\"updateOpacity()\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n }\n\n @if(!collapsed) {\n <div class=\"active-objects-content\">\n <div class=\"header-icons\">\n <mat-icon \n class=\"zoom-icon\" \n [class.zoom-active]=\"isZoomedToAll\"\n (click)=\"zoomToAll()\" \n matTooltip=\"Zoom til alle\" \n matTooltipPosition=\"above\">\n fit_screen\n </mat-icon> \n <mat-icon class=\"reset-icon\" (click)=\"reset()\" matTooltip=\"Nulstilles\" matTooltipPosition=\"above\">replay</mat-icon>\n </div>\n @for(featureTypeObj of activeFeatures; track featureTypeObj.featureType) {\n <mat-expansion-panel>\n <mat-expansion-panel-header \n [matTooltip]=\"featureTypeObj.display\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"right\">\n <span class=\"panel-title\">\n {{featureTypeObj.display}} ({{featureTypeObj.features.length}})\n </span>\n </mat-expansion-panel-header>\n <div class=\"item-list\">\n @for(item of featureTypeObj.features; track item.id) {\n <div class=\"item\">\n <div class=\"item-text\">\n <span class=\"item-id\">{{item.id}}</span>\n <span class=\"item-area\">{{item.area}}</span>\n @if(item.containedMissingIn && item.containedMissingIn.length > 0) {\n <span>Udenfor {{item.containedMissingIn.join(', ')}} </span> \n }\n </div>\n @if(!item.locked) {\n <mat-icon (click)=\"delete(item.id)\">delete</mat-icon>\n }\n <mat-icon (click)=\"highlight(item.id)\" [class.highlight-active]=\"highlightedId === item.id\">power_settings_new</mat-icon>\n </div>\n }\n </div>\n </mat-expansion-panel>\n }\n @if(!centerPointOk) {\n <span>\n Der er ikke noget centerpunkt og det skal der v\u00E6re\n </span>\n } @else {\n @if (endSessionEnabled) {\n <button (click)=\"endSession()\">Afslut</button>\n }\n }\n </div>\n }\n </div> \n</div>", styles: [".active-objects-wrapper{position:absolute;top:8em;left:1em;z-index:2;cursor:grab;box-shadow:0 2px 10px #0000001a;background:#292a2d;width:350px;max-width:350px;transition:width .3s ease,max-width .3s ease;border-radius:5px 5px 0 0}.active-objects-wrapper.collapsed{width:90px}.active-objects-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .panel-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:320px;margin-right:10px;line-height:1.3;font-weight:600;color:#bdc1c3cc;font-size:17px}.item{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-radius:4px;background:color-mix(in srgb,#000 60%,transparent)!important;transition:all .2s ease;font-size:14px;color:#fff;min-height:40px;cursor:default}.item .item-text{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}.item .item-id{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:2px;max-width:250px;font-size:13px}.item .item-area{font-size:12px;color:#ccc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:250px}.item mat-icon{cursor:pointer;color:#f44336;font-size:18px;width:18px;height:18px;flex-shrink:0;margin-left:8px;transition:color .2s ease,background .2s ease}.item mat-icon:hover{color:#d32f2f}.highlight-active{color:#4caf50!important;text-shadow:0 0 6px #4caf50;transform:scale(1.1);transition:all .2s ease}.drag-handle-active-objects{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;color:#fff;border-radius:5px 5px 0 0}.drag-handle-active-objects .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.drag-handle-active-objects .toggle-icon:hover{color:#fff;background:#ffffff26}.right-icons{display:flex;align-items:center;gap:6px}.right-icons .drag-indicator-right{font-size:18px!important;width:18px!important;height:18px!important}.active-objects-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;transition:all .3s ease;cursor:default;box-sizing:border-box}.active-objects-container .panel-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;cursor:default;background:color-mix(in srgb,#000 70%,transparent);border-bottom:1px solid rgba(255,255,255,.1)}.active-objects-container .collapsed-title{color:#fff;font-weight:600;font-size:12px;padding:8px 4px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.active-objects-container .collapsed-title:hover{color:#1976d2}.header-icons{display:flex!important;align-items:center!important;justify-content:flex-end!important;gap:8px!important;padding:0 10px!important}.header-icons .reset-icon{cursor:pointer;color:#fff;font-size:20px;width:20px;height:20px;transition:all .2s ease;padding:4px}.header-icons .reset-icon:hover{color:#ff9800}.active-objects-container input[type=range]{width:95%;margin:8px auto}.active-objects-content{display:block;width:100%;max-height:278px;overflow:auto;background:#000}.active-objects-content::-webkit-scrollbar{width:12px}.active-objects-content::-webkit-scrollbar-track{background:#757474;border-radius:8px}.active-objects-content::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}.active-objects-content::-webkit-scrollbar-thumb:hover{background:#0f1012}.active-objects-content::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}.active-objects-content::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}.active-objects-content::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}.active-objects-content::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}.active-objects-content::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}.active-objects-content .zoom-icon{color:#fff;font-size:18px;width:18px;height:18px;opacity:.6;transition:all .2s ease}.active-objects-content .zoom-icon:hover{color:#4caf50;opacity:1}.active-objects-content .zoom-icon.zoom-active{color:#4caf50!important;text-shadow:0 0 6px #4caf50;opacity:1}mat-expansion-panel{border-radius:0!important;box-shadow:none!important;width:100%}mat-expansion-panel:last-child{border-bottom:none}mat-expansion-panel ::ng-deep .mat-expansion-panel-header{padding:0 16px;font-weight:500}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .mat-content{display:flex;justify-content:space-between;align-items:center;color:#fff}mat-expansion-panel ::ng-deep .mat-expansion-panel-body{padding:0}.item-list{display:flex;flex-direction:column;padding:8px}@media (max-width: 768px){.active-objects-wrapper{right:.5em;top:8em;max-width:280px;width:280px}.item{font-size:13px;padding:6px 10px}}@media (max-width: 480px){.active-objects-wrapper{right:.5em;left:.5em;max-width:calc(100vw - 1em);width:auto}.item-list{max-height:200px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i6.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
1862
3280
  }
1863
3281
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ActiveObjectsComponent, decorators: [{
1864
3282
  type: Component,
1865
- args: [{ selector: 'activeObjects', imports: [CommonModule, FormsModule, MatExpansionModule, MatIconModule, DragDropModule, MatTooltipModule], template: "<div class=\"active-objects-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle-active-objects\" cdkDragHandle>\n <mat-icon class=\"zoom-icon\" (click)=\"zoomToAll()\">share_reviewsr</mat-icon>\n @if(!collapsed) {\n <h4>Aktive flader</h4>\n }\n <mat-icon class=\"toggle-icon\" (click)=\"togglePanel()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n <div class=\"active-objects-container\">\n @if(!collapsed) {\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"opacity\"\n (input)=\"updateOpacity()\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n }\n\n @if(!collapsed) {\n <div class=\"active-objects-content\">\n <mat-icon class=\"zoom-icon\" (click)=\"zoomToAll()\">highlight</mat-icon>\n @for(featureTypeObj of activeFeatures; track featureTypeObj.featureType) {\n <mat-expansion-panel>\n <mat-expansion-panel-header \n [matTooltip]=\"featureTypeObj.display\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"right\">\n <span class=\"panel-title\">\n {{featureTypeObj.display}} ({{featureTypeObj.features.length}})\n </span>\n </mat-expansion-panel-header>\n <div class=\"item-list\">\n @for(item of featureTypeObj.features; track item.id) {\n <div class=\"item\">\n <div class=\"item-text\">\n <span class=\"item-id\">{{item.id}}</span>\n <span class=\"item-area\">{{item.area}}</span>\n </div>\n <mat-icon *ngIf=\"!item.locked\" (click)=\"delete(item.id)\">delete</mat-icon>\n <mat-icon (click)=\"highlight(item.id)\" [class.highlight-active]=\"highlightedId === item.id\">highlight</mat-icon>\n </div>\n }\n </div>\n </mat-expansion-panel>\n }\n </div>\n }\n </div> \n</div>", styles: ["@charset \"UTF-8\";.active-objects-wrapper{position:absolute;top:15em;left:1em;z-index:1000;cursor:grab;box-shadow:0 2px 10px #0000001a;background:color-mix(in srgb,#000 60%,transparent);width:fit-content;max-width:295px;transition:width .3s ease}.active-objects-wrapper.collapsed{width:70px;max-width:150px}.active-objects-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .panel-title{display:block;overflow:visible;text-overflow:clip;white-space:normal;max-width:220px;margin-right:10px;line-height:1.3;word-break:break-word}.item{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-radius:4px;background:color-mix(in srgb,#000 60%,transparent)!important;transition:all .2s ease;font-size:14px;color:#fff;min-height:40px;cursor:default}.item .item-text{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}.item .item-id{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:2px}.item .item-area{font-size:12px;color:#ccc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.item mat-icon{cursor:pointer;color:#f44336;font-size:18px;width:18px;height:18px;flex-shrink:0;margin-left:8px;transition:color .2s ease,background .2s ease}.item mat-icon:hover{color:#d32f2f}.highlight-active{color:#4caf50!important;text-shadow:0 0 6px #4caf50;transform:scale(1.1);transition:all .2s ease}.drag-handle-active-objects{display:flex;align-items:center;justify-content:space-between;background:color-mix(in srgb,#000 0%,transparent);border-radius:8px 8px 0 0;padding:4px 8px;cursor:grab}.drag-handle-active-objects h4{margin:0;padding:5px;background:transparent;font-weight:600;color:#fff;flex:1;font-size:16px;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.drag-handle-active-objects .zoom-icon{color:#fff;font-size:18px;width:18px;height:18px}.drag-handle-active-objects .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:22px;width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:6px;flex-shrink:0;margin:0}.drag-handle-active-objects .toggle-icon:hover{color:#ffffffb3;background:#1976d214}.active-objects-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;transition:all .3s ease;cursor:default;box-sizing:border-box}.active-objects-container .panel-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:5px;cursor:default}.active-objects-container .panel-header h4{margin:0;padding:5px;background:transparent;font-weight:600;color:#fff;flex:1;font-size:16px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.active-objects-container .panel-header .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:22px;width:22px;height:22px;display:flex;align-items:center;justify-content:center;border-radius:6px;flex-shrink:0;margin:0}.active-objects-container .panel-header .toggle-icon:hover{color:#ffffffb3;background:#1976d214}.active-objects-container .collapsed-title{color:#fff;font-weight:600;font-size:12px;padding:8px 4px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.active-objects-container .collapsed-title:hover{color:#1976d2}.active-objects-content{display:block;max-height:278px;overflow:auto}.active-objects-content::-webkit-scrollbar{width:8px;border-radius:10px;border:5px solid transparent}.active-objects-content::-webkit-scrollbar-thumb{background:#0000004d;border:5px solid transparent;border-radius:10px;background-clip:padding-box}.active-objects-content::-webkit-scrollbar-thumb:hover{background:#0006;background-clip:padding-box;border:3px solid transparent}.active-objects-content .zoom-icon{color:#4caf50!important;border-radius:50%;padding:6px;margin:4px;cursor:pointer;transition:all .3s ease;font-size:20px;width:20px;height:20px}.active-objects-content .zoom-icon:hover{background:#4caf5033!important}mat-expansion-panel{border-radius:0!important;box-shadow:none!important}mat-expansion-panel:last-child{border-bottom:none}mat-expansion-panel ::ng-deep .mat-expansion-panel-header{padding:0 16px;font-weight:500}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .mat-content{display:flex;justify-content:space-between;align-items:center;color:#fff}mat-expansion-panel ::ng-deep .mat-expansion-panel-body{padding:0}.item-list{display:flex;flex-direction:column;gap:4px;padding:8px;max-height:300px;overflow-y:auto}.item-list::-webkit-scrollbar{width:6px}.item-list::-webkit-scrollbar-track{background:#f1f1f1;border-radius:3px}.item-list::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:3px}.item-list::-webkit-scrollbar-thumb:hover{background:#a8a8a8}@media (max-width: 768px){.active-objects-wrapper{right:.5em;top:8em;max-width:280px;width:280px}.item{font-size:13px;padding:6px 10px}}@media (max-width: 480px){.active-objects-wrapper{right:.5em;left:.5em;max-width:calc(100vw - 1em);width:auto}.item-list{max-height:200px}}\n"] }]
3283
+ args: [{ selector: 'activeObjects', imports: [CommonModule, FormsModule, MatExpansionModule, MatIconModule, DragDropModule, MatTooltipModule], template: "<div class=\"active-objects-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle-active-objects\" cdkDragHandle>\n <mat-icon class=\"zoom-icon\" (click)=\"zoomToAll()\">dynamic_form</mat-icon>\n \n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"togglePanel()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n \n <div class=\"active-objects-container\">\n @if(!collapsed) {\n <input \n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n [(ngModel)]=\"opacity\"\n (input)=\"updateOpacity()\"\n (mousedown)=\"stopDrag($event)\"\n (touchstart)=\"stopDrag($event)\"\n (pointerdown)=\"stopDrag($event)\"\n >\n }\n\n @if(!collapsed) {\n <div class=\"active-objects-content\">\n <div class=\"header-icons\">\n <mat-icon \n class=\"zoom-icon\" \n [class.zoom-active]=\"isZoomedToAll\"\n (click)=\"zoomToAll()\" \n matTooltip=\"Zoom til alle\" \n matTooltipPosition=\"above\">\n fit_screen\n </mat-icon> \n <mat-icon class=\"reset-icon\" (click)=\"reset()\" matTooltip=\"Nulstilles\" matTooltipPosition=\"above\">replay</mat-icon>\n </div>\n @for(featureTypeObj of activeFeatures; track featureTypeObj.featureType) {\n <mat-expansion-panel>\n <mat-expansion-panel-header \n [matTooltip]=\"featureTypeObj.display\" \n [matTooltipShowDelay]=\"200\"\n [matTooltipHideDelay]=\"300\" \n matTooltipPosition=\"right\">\n <span class=\"panel-title\">\n {{featureTypeObj.display}} ({{featureTypeObj.features.length}})\n </span>\n </mat-expansion-panel-header>\n <div class=\"item-list\">\n @for(item of featureTypeObj.features; track item.id) {\n <div class=\"item\">\n <div class=\"item-text\">\n <span class=\"item-id\">{{item.id}}</span>\n <span class=\"item-area\">{{item.area}}</span>\n @if(item.containedMissingIn && item.containedMissingIn.length > 0) {\n <span>Udenfor {{item.containedMissingIn.join(', ')}} </span> \n }\n </div>\n @if(!item.locked) {\n <mat-icon (click)=\"delete(item.id)\">delete</mat-icon>\n }\n <mat-icon (click)=\"highlight(item.id)\" [class.highlight-active]=\"highlightedId === item.id\">power_settings_new</mat-icon>\n </div>\n }\n </div>\n </mat-expansion-panel>\n }\n @if(!centerPointOk) {\n <span>\n Der er ikke noget centerpunkt og det skal der v\u00E6re\n </span>\n } @else {\n @if (endSessionEnabled) {\n <button (click)=\"endSession()\">Afslut</button>\n }\n }\n </div>\n }\n </div> \n</div>", styles: [".active-objects-wrapper{position:absolute;top:8em;left:1em;z-index:2;cursor:grab;box-shadow:0 2px 10px #0000001a;background:#292a2d;width:350px;max-width:350px;transition:width .3s ease,max-width .3s ease;border-radius:5px 5px 0 0}.active-objects-wrapper.collapsed{width:90px}.active-objects-wrapper.cdk-drag-dragging{opacity:.8;cursor:grab;z-index:1001}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .panel-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:320px;margin-right:10px;line-height:1.3;font-weight:600;color:#bdc1c3cc;font-size:17px}.item{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-radius:4px;background:color-mix(in srgb,#000 60%,transparent)!important;transition:all .2s ease;font-size:14px;color:#fff;min-height:40px;cursor:default}.item .item-text{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}.item .item-id{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:2px;max-width:250px;font-size:13px}.item .item-area{font-size:12px;color:#ccc;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:250px}.item mat-icon{cursor:pointer;color:#f44336;font-size:18px;width:18px;height:18px;flex-shrink:0;margin-left:8px;transition:color .2s ease,background .2s ease}.item mat-icon:hover{color:#d32f2f}.highlight-active{color:#4caf50!important;text-shadow:0 0 6px #4caf50;transform:scale(1.1);transition:all .2s ease}.drag-handle-active-objects{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;color:#fff;border-radius:5px 5px 0 0}.drag-handle-active-objects .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.drag-handle-active-objects .toggle-icon:hover{color:#fff;background:#ffffff26}.right-icons{display:flex;align-items:center;gap:6px}.right-icons .drag-indicator-right{font-size:18px!important;width:18px!important;height:18px!important}.active-objects-container{display:flex;flex-direction:column;align-items:center;width:100%;gap:10px;transition:all .3s ease;cursor:default;box-sizing:border-box}.active-objects-container .panel-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;cursor:default;background:color-mix(in srgb,#000 70%,transparent);border-bottom:1px solid rgba(255,255,255,.1)}.active-objects-container .collapsed-title{color:#fff;font-weight:600;font-size:12px;padding:8px 4px;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;white-space:nowrap;text-align:center}.active-objects-container .collapsed-title:hover{color:#1976d2}.header-icons{display:flex!important;align-items:center!important;justify-content:flex-end!important;gap:8px!important;padding:0 10px!important}.header-icons .reset-icon{cursor:pointer;color:#fff;font-size:20px;width:20px;height:20px;transition:all .2s ease;padding:4px}.header-icons .reset-icon:hover{color:#ff9800}.active-objects-container input[type=range]{width:95%;margin:8px auto}.active-objects-content{display:block;width:100%;max-height:278px;overflow:auto;background:#000}.active-objects-content::-webkit-scrollbar{width:12px}.active-objects-content::-webkit-scrollbar-track{background:#757474;border-radius:8px}.active-objects-content::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}.active-objects-content::-webkit-scrollbar-thumb:hover{background:#0f1012}.active-objects-content::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}.active-objects-content::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}.active-objects-content::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}.active-objects-content::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}.active-objects-content::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}.active-objects-content .zoom-icon{color:#fff;font-size:18px;width:18px;height:18px;opacity:.6;transition:all .2s ease}.active-objects-content .zoom-icon:hover{color:#4caf50;opacity:1}.active-objects-content .zoom-icon.zoom-active{color:#4caf50!important;text-shadow:0 0 6px #4caf50;opacity:1}mat-expansion-panel{border-radius:0!important;box-shadow:none!important;width:100%}mat-expansion-panel:last-child{border-bottom:none}mat-expansion-panel ::ng-deep .mat-expansion-panel-header{padding:0 16px;font-weight:500}mat-expansion-panel ::ng-deep .mat-expansion-panel-header .mat-content{display:flex;justify-content:space-between;align-items:center;color:#fff}mat-expansion-panel ::ng-deep .mat-expansion-panel-body{padding:0}.item-list{display:flex;flex-direction:column;padding:8px}@media (max-width: 768px){.active-objects-wrapper{right:.5em;top:8em;max-width:280px;width:280px}.item{font-size:13px;padding:6px 10px}}@media (max-width: 480px){.active-objects-wrapper{right:.5em;left:.5em;max-width:calc(100vw - 1em);width:auto}.item-list{max-height:200px}}\n"] }]
1866
3284
  }], ctorParameters: () => [], propDecorators: { settings: [{
1867
3285
  type: Input,
1868
3286
  args: [{ required: true }]
3287
+ }], sessionDone: [{
3288
+ type: Output
3289
+ }], profile: [{
3290
+ type: Input,
3291
+ args: [{ required: true }]
1869
3292
  }] } });
1870
3293
 
3294
+ class WFSSearchService {
3295
+ name = 'WFS Search';
3296
+ _http = inject(HttpClient);
3297
+ wfsFormat = new WFS();
3298
+ wktFormat = new WKT();
3299
+ _searcheableLayers = [];
3300
+ geoJsonFormat = new GeoJSON({ dataProjection: 'EPSG:25832', featureProjection: 'EPSG:25832' });
3301
+ _serializer = new XMLSerializer();
3302
+ search(searchValue, geometry, maxCount = 5) {
3303
+ const wftSearches = [];
3304
+ this._searcheableLayers.flatMap(layer => {
3305
+ const filter = searchValue ? like(layer.searchField, `*${searchValue}*`, '*', '?', '/', false) :
3306
+ geometry ? intersects(layer.geometryField, geometry, 'EPSG:25832') : undefined;
3307
+ var searchFeature = this.wfsFormat.writeGetFeature({
3308
+ featureTypes: [layer.featureType.includes(':') ? layer.featureType.split(':')[1] : ''],
3309
+ outputFormat: 'application/json',
3310
+ featureNS: '',
3311
+ featurePrefix: '',
3312
+ srsName: 'EPSG:25832',
3313
+ maxFeatures: maxCount,
3314
+ filter
3315
+ });
3316
+ const fieldsToExclude = new Set(layer.fieldsToExcludeFromInfoFromInfo.split(',').map(f => f.toLowerCase().trim()));
3317
+ wftSearches.push(this._http.post(layer.baseUrl.replace('wms', 'wfs'), this._serializer.serializeToString(searchFeature), { headers: { 'Content-Type': 'text/xml' }, responseType: 'json' }).pipe(map$1(r => {
3318
+ const result = {
3319
+ title: layer.name,
3320
+ total: r.numberMatched ?? 0,
3321
+ items: r.features.map(f => ({ header: f.properties[layer.headerField], wkt: this._formatWKT(f), metadata: this._mapFeatureMetadata(f, fieldsToExclude), showMetadata: false }))
3322
+ };
3323
+ return result;
3324
+ })));
3325
+ });
3326
+ return combineLatest(wftSearches);
3327
+ }
3328
+ onSelect(searchResult, targetSrs) {
3329
+ // TO DO : add logic for when an item is selected (right now, the map search component does a highlight on it)
3330
+ }
3331
+ onClear() {
3332
+ // TO DO : add logic for when the results list is cleared
3333
+ }
3334
+ setSearcheableLayers(layers) {
3335
+ this._searcheableLayers = layers;
3336
+ }
3337
+ _formatWKT(feature) {
3338
+ const olFeature = this.geoJsonFormat.readFeature(feature);
3339
+ return this.wktFormat.writeFeature(olFeature);
3340
+ }
3341
+ _mapFeatureMetadata(f, excludeSet) {
3342
+ const result = {};
3343
+ const props = f.properties || {};
3344
+ for (const [key, value] of Object.entries(props)) {
3345
+ if (excludeSet.has(key.toLowerCase()))
3346
+ continue;
3347
+ const kind = this._getValueKind(value);
3348
+ result[key] = { kind, data: value, name: key };
3349
+ }
3350
+ return [result];
3351
+ }
3352
+ _getValueKind(value) {
3353
+ const imgTypes = ['.png', '.jpg', '.gif', '.jpeg'];
3354
+ if (typeof value === 'string') {
3355
+ if (value.startsWith('http://') || value.startsWith('https://')) {
3356
+ if (imgTypes.some(ext => value.endsWith(ext))) {
3357
+ return 'img';
3358
+ }
3359
+ return 'url';
3360
+ }
3361
+ }
3362
+ return 'data';
3363
+ }
3364
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: WFSSearchService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3365
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: WFSSearchService, providedIn: 'root' });
3366
+ }
3367
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: WFSSearchService, decorators: [{
3368
+ type: Injectable,
3369
+ args: [{
3370
+ providedIn: 'root'
3371
+ }]
3372
+ }] });
3373
+
1871
3374
  class MapSearchComponent {
1872
3375
  searchAddress = '';
1873
- allResults = ['address1', 'address2', 'address3', 'address4'];
3376
+ profile;
3377
+ // allResults = ['address1', 'address2', 'address3', 'address4'];
1874
3378
  filteredResults = [];
1875
3379
  collapsed = true;
1876
3380
  dragPosition = { x: 0, y: 0 };
1877
3381
  POSITION_STORAGE_KEY = 'mapSearchPosition';
3382
+ _mapFilteredLayerGroupsKeyName = 'mapFilteredLayerGroups';
3383
+ _highlight = inject(HighlightService);
3384
+ _searchSubject = new Subject();
3385
+ _searchService = inject(SearchProviderService);
3386
+ _searchProviderService = inject(SearchProviderService);
3387
+ _wfsSearch = inject(WFSSearchService);
3388
+ _layerHelperService = inject(LayerHelperService);
3389
+ _layoutService = inject(LayoutService);
1878
3390
  ngOnInit() {
3391
+ this._layerHelperService.activeLayersChanged
3392
+ .subscribe(() => this._addWFSSearchLayers());
1879
3393
  this.filterResults();
1880
3394
  this._loadPosition();
3395
+ this._addWFSSearch();
3396
+ this._searchSubject.pipe(debounceTime(200), distinctUntilChanged(), switchMap(value => this._searchService.search(value, undefined))).subscribe({
3397
+ next: result => {
3398
+ this.filteredResults = result.filter(result => result.total > 0);
3399
+ }
3400
+ });
3401
+ }
3402
+ ngOnChanges(changes) {
3403
+ if (changes['profile'] && this.profile) {
3404
+ this._loadPosition();
3405
+ this._addWFSSearchLayers();
3406
+ }
3407
+ }
3408
+ _addWFSSearch() {
3409
+ this._searchProviderService.searchProviders = [];
3410
+ this._searchProviderService.searchProviders.push(this._wfsSearch);
3411
+ }
3412
+ _addWFSSearchLayers() {
3413
+ if (this.profile) {
3414
+ const cacheValue = localStorage.getItem(`${this._mapFilteredLayerGroupsKeyName}_${this.profile.id}`);
3415
+ if (cacheValue) {
3416
+ const cachedProfileInfo = JSON.parse(cacheValue);
3417
+ const visibleLayerIds = cachedProfileInfo.cachedLayerGroups
3418
+ .flatMap(group => group.layers)
3419
+ .filter(layer => layer.visible)
3420
+ .map(layer => layer.id);
3421
+ const searcheableLayers = cachedProfileInfo.profile
3422
+ .layerGroups.flatMap(group => group.layers)
3423
+ .filter(layer => layer.wfsSearchable && visibleLayerIds.includes(layer.id))
3424
+ .map(layer => ({ featureType: layer.layers, baseUrl: layer.baseUrl, name: layer.name,
3425
+ headerField: layer.headerField, searchField: layer.searchField, geometryField: layer.geometryField,
3426
+ fieldsToExcludeFromInfoFromInfo: layer.fieldsToExcludeFromInfoFromInfo }));
3427
+ this._wfsSearch.setSearcheableLayers(searcheableLayers);
3428
+ }
3429
+ }
1881
3430
  }
1882
3431
  filterResults() {
1883
3432
  if (!this.searchAddress) {
1884
3433
  this.filteredResults = [];
1885
3434
  return;
1886
3435
  }
1887
- this.filteredResults = this.allResults.filter(r => r.toLowerCase().includes(this.searchAddress.toLowerCase()));
3436
+ this._searchSubject.next(this.searchAddress);
3437
+ // this._wfsSearch.search(this.searchAddress).subscribe(r => {
3438
+ // this.filteredResults = [r];
3439
+ // });
1888
3440
  }
1889
3441
  toggleSearch() {
1890
3442
  this.collapsed = !this.collapsed;
@@ -1893,6 +3445,10 @@ class MapSearchComponent {
1893
3445
  this.filterResults();
1894
3446
  }
1895
3447
  }
3448
+ highlight(wkt, event) {
3449
+ event.stopPropagation();
3450
+ this._highlight.highlight(wkt);
3451
+ }
1896
3452
  onDragEnded(event) {
1897
3453
  const position = event.source.getFreeDragPosition();
1898
3454
  this.dragPosition = position;
@@ -1912,6 +3468,9 @@ class MapSearchComponent {
1912
3468
  if (savedPosition) {
1913
3469
  this.dragPosition = JSON.parse(savedPosition);
1914
3470
  }
3471
+ else if (this.profile) {
3472
+ this.dragPosition = this._layoutService.loadInitialPositionFromProfile(this.POSITION_STORAGE_KEY, this.profile.searchInitialPosition);
3473
+ }
1915
3474
  }
1916
3475
  catch (error) {
1917
3476
  console.error('Error loading position from localStorage:', error);
@@ -1919,7 +3478,7 @@ class MapSearchComponent {
1919
3478
  }
1920
3479
  }
1921
3480
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MapSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1922
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: MapSearchComponent, isStandalone: true, selector: "lib-map-search", ngImport: i0, template: "<div class=\"search-container\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle\" cdkDragHandle>\n <mat-icon>search</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleSearch()\">{{ collapsed ? 'flip_to_front' : 'remove' }}</mat-icon>\n </div>\n <div class=\"search-content\" *ngIf=\"!collapsed\">\n <mat-form-field appearance=\"outline\" class=\"search-field\">\n <mat-label>S\u00F8ge</mat-label>\n <input\n type=\"text\"\n matInput\n [(ngModel)]=\"searchAddress\"\n [matAutocomplete]=\"auto\"\n (input)=\"filterResults()\"\n />\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\">\n {{ result }}\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n </div>\n</div>", styles: [".search-container{position:absolute;top:8em;left:1em;z-index:1000;cursor:grab;max-width:400px;min-width:48px;width:auto}.search-container.collapsed{width:48px;min-width:68px;max-width:48px}.search-container.cdk-drag-dragging{opacity:.8;cursor:grab}.drag-handle{display:flex;align-items:center;justify-content:space-between;background:color-mix(in srgb,#000 60%,transparent);padding:4px;cursor:grab}.drag-handle mat-icon:not(.toggle-icon){color:#fff;font-size:20px;width:20px;height:20px}.search-content{display:flex;align-items:center;background:color-mix(in srgb,#000 60%,transparent);padding:5px;transition:all .3s ease;box-sizing:border-box;cursor:default}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:20px;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:6px;flex-shrink:0}.toggle-icon:hover{color:#2196f3;background:#2196f314}.search-field{width:100%;min-width:200px;cursor:pointer;flex:1}.search-field ::ng-deep .mat-mdc-form-field-focus-overlay{background-color:transparent}.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important;border-width:1.5px!important;transition:border-color .3s ease,border-width .3s ease}.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important}.search-field ::ng-deep .mat-mdc-form-field-label{color:#fff!important;font-weight:500;font-size:15px}.search-field ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-form-field-label{color:#2196f3!important}.search-field ::ng-deep .mat-mdc-input-element{font-size:15px;color:#fff;font-weight:400;line-height:1.5}.search-field ::ng-deep .mat-mdc-input-element::placeholder{color:#ccc;font-weight:400;opacity:.8}.search-field ::ng-deep .mdc-floating-label{padding:0 4px;left:40px!important}::ng-deep .mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input{margin-left:12px;color:#fff!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{color:#fff!important}::ng-deep .mat-mdc-option .mat-pseudo-checkbox-minimal{color:#fff!important}::ng-deep .mat-mdc-form-field-flex{height:40px!important}::ng-deep .mat-mdc-autocomplete-panel{border-radius:12px!important;max-height:320px!important;background:color-mix(in srgb,#000 85%,transparent)!important;box-shadow:0 8px 24px #00000026!important;margin-top:8px!important;overflow:hidden!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:30px!important;border-radius:8px!important;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mdc-list-item--disabled){background-color:#0000004d!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px;font-weight:400;color:inherit;line-height:1.5}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar{width:6px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-track{background:#f1f1f1;border-radius:0 12px 12px 0}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:3px;transition:background .3s ease}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb:hover{background:#a8a8a8}::ng-deep .mat-mdc-text-field-wrapper{background:color-mix(in srgb,#000 60%,transparent);border-radius:8px;box-shadow:#0000004d 0 1px 4px -1px;transition:all .3s cubic-bezier(.4,0,.2,1)}::ng-deep .mat-mdc-text-field-wrapper:hover{box-shadow:0 4px 16px #00000026}::ng-deep .mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label{color:#fff!important}::ng-deep .mdc-text-field{border-radius:8px!important}@media (max-width: 768px){.search-container{left:2em;top:1.5em;max-width:350px}.search-container.collapsed{width:44px;min-width:44px;max-width:44px}.search-field{min-width:180px}.toggle-icon{font-size:18px;width:18px;height:18px}}@media (max-width: 480px){.search-container{left:1.5em;right:1.5em;max-width:calc(100vw - 3em)}.search-container.collapsed{width:40px;min-width:40px;max-width:40px;right:auto}.search-field{min-width:150px}.search-field ::ng-deep .mat-mdc-input-element{font-size:14px}::ng-deep .mat-mdc-autocomplete-panel{max-height:240px!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:48px!important;padding:10px 16px!important}.toggle-icon{font-size:16px;width:16px;height:16px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i4$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] });
3481
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: MapSearchComponent, isStandalone: true, selector: "lib-map-search", inputs: { profile: "profile" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"search-container\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle\" cdkDragHandle>\n <mat-icon class=\"icon-left\">search</mat-icon>\n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleSearch()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n\n <div class=\"search-content\" *ngIf=\"!collapsed\">\n <mat-form-field appearance=\"outline\" class=\"search-field\">\n <mat-label>S\u00F8ge</mat-label>\n <input\n type=\"text\"\n matInput\n [(ngModel)]=\"searchAddress\"\n [matAutocomplete]=\"auto\"\n (input)=\"filterResults()\"\n />\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\" class=\"search-result-option\">\n <span class=\"result-title\">\n {{ result.title }} ({{result.items.length}} af {{ result.total }})\n </span>\n <div *ngFor=\"let item of result.items\" class=\"search-result-item\">\n <span class=\"item-header\">\n {{item.header}}\n </span>\n <mat-icon (click)=\"highlight(item.wkt, $event)\">power_settings_new </mat-icon>\n </div>\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n </div>\n</div>", styles: [".search-container{position:absolute;top:8em;left:1em;z-index:2;cursor:grab;max-width:400px;min-width:48px;width:auto}.search-container.cdk-drag-dragging{opacity:.8;cursor:grab}.compact-icon.custom-image-icon{padding:4px;background:none;border:1px solid rgba(255,255,255,.2)}.compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:filter .2s ease}.compact-icon.custom-image-icon.active{background:linear-gradient(147.38deg,#0ea5e9,#075985);border-color:transparent;box-shadow:0 4px 12px #0ea5e966}.compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1)}.compact-icon.custom-image-icon.active:hover{box-shadow:0 6px 20px #0ea5e980}.compact-icon.custom-image-icon:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);border-color:#0ea5e9;box-shadow:0 2px 8px #0ea5e94d}.compact-icon.custom-image-icon:hover:not(.active) img{filter:brightness(0) invert(.8)}.drag-handle{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;border-radius:5px 5px 0 0;color:#fff}.right-icons{display:flex;align-items:center;gap:6px}.right-icons .drag-indicator-right{font-size:18px!important;width:18px!important;height:18px!important}.search-content{display:flex;flex-direction:column;background:#292a2d;padding:6px;transition:all .3s ease;box-sizing:border-box;cursor:default;border-radius:0 0 5px 5px}.compact-icon{transition:all .3s cubic-bezier(.4,0,.2,1)}.compact-icon.custom-image-icon{background:none;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.toggle-icon:hover{color:#fff;background:#ffffff26}.search-field{width:100%;min-width:200px;cursor:pointer;flex:1}.search-field ::ng-deep .mat-mdc-form-field-focus-overlay{background-color:transparent}.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important;border-width:1.5px!important;transition:border-color .3s ease,border-width .3s ease}.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important}.search-field ::ng-deep .mat-mdc-form-field-label{color:#fff!important;font-weight:500;font-size:15px}.search-field ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-form-field-label{color:#2196f3!important}.search-field ::ng-deep .mat-mdc-input-element{font-size:15px;color:#fff;font-weight:400;line-height:1.5}.search-field ::ng-deep .mat-mdc-input-element::placeholder{color:#ccc;font-weight:400;opacity:.8}.search-field ::ng-deep .mdc-floating-label{padding:0 4px;left:40px!important}::ng-deep .mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input{margin-left:12px;color:#fff!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{color:#fff!important}::ng-deep .mat-mdc-option .mat-pseudo-checkbox-minimal{color:#fff!important}::ng-deep .mat-mdc-form-field-flex{height:40px!important}::ng-deep .mat-mdc-autocomplete-panel{border-radius:5px!important;max-height:320px!important;background:color-mix(in srgb,#000 85%,transparent)!important;box-shadow:0 8px 24px #00000026!important;margin-top:64px;overflow-y:auto!important;padding:8px!important}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar{width:12px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:30px!important;border-radius:5px!important;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:auto;margin-bottom:6px!important;padding:8px 0!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px;font-weight:400;color:inherit;line-height:1.5}.search-result-option{display:flex;flex-direction:column;gap:8px}.result-title{font-weight:600;font-size:13px;color:#bdc1c3cc!important;display:block;margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px}.search-result-item{display:flex;align-items:center;justify-content:space-between;padding:6px 4px;border-radius:5px;margin-left:4px;transition:all .6s ease}.search-result-item:hover{color:#f4f4f5;background:#ffffff1a}.search-result-item .item-header{font-size:14px;color:#fff!important;flex:1;font-weight:500}.search-result-item mat-icon{color:#fff!important;font-size:20px;width:20px;height:20px;cursor:pointer;transition:all .2s ease}.search-result-item mat-icon:hover{color:#f7f7f7!important}::ng-deep .mat-mdc-text-field-wrapper{background:color-mix(in srgb,#000 60%,transparent);border-radius:5px;box-shadow:#0000004d 0 1px 4px -1px;transition:all .3s cubic-bezier(.4,0,.2,1)}::ng-deep .mat-mdc-text-field-wrapper:hover{box-shadow:0 4px 16px #00000026}::ng-deep .mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label{color:#fff!important}::ng-deep .mdc-text-field{border-radius:5px!important}@media (max-width: 768px){.search-container{left:2em;top:1.5em;max-width:350px}.search-container.collapsed{width:44px;min-width:88px;max-width:44px}.search-field{min-width:180px}.toggle-icon{font-size:18px;width:18px;height:18px}}@media (max-width: 480px){.search-container{left:1.5em;right:1.5em;max-width:calc(100vw - 3em)}.search-container.collapsed{width:40px;min-width:40px;max-width:40px;right:auto}.search-field{min-width:150px}.search-field ::ng-deep .mat-mdc-input-element{font-size:14px}::ng-deep .mat-mdc-autocomplete-panel{max-height:240px!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:48px!important;padding:10px 16px!important}.toggle-icon{font-size:16px;width:16px;height:16px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatAutocompleteModule }, { kind: "component", type: i5$1.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "component", type: i4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i5$1.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "ngmodule", type: MatOptionModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }] });
1923
3482
  }
1924
3483
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MapSearchComponent, decorators: [{
1925
3484
  type: Component,
@@ -1932,8 +3491,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
1932
3491
  MatOptionModule,
1933
3492
  MatIconModule,
1934
3493
  DragDropModule
1935
- ], template: "<div class=\"search-container\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle\" cdkDragHandle>\n <mat-icon>search</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleSearch()\">{{ collapsed ? 'flip_to_front' : 'remove' }}</mat-icon>\n </div>\n <div class=\"search-content\" *ngIf=\"!collapsed\">\n <mat-form-field appearance=\"outline\" class=\"search-field\">\n <mat-label>S\u00F8ge</mat-label>\n <input\n type=\"text\"\n matInput\n [(ngModel)]=\"searchAddress\"\n [matAutocomplete]=\"auto\"\n (input)=\"filterResults()\"\n />\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\">\n {{ result }}\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n </div>\n</div>", styles: [".search-container{position:absolute;top:8em;left:1em;z-index:1000;cursor:grab;max-width:400px;min-width:48px;width:auto}.search-container.collapsed{width:48px;min-width:68px;max-width:48px}.search-container.cdk-drag-dragging{opacity:.8;cursor:grab}.drag-handle{display:flex;align-items:center;justify-content:space-between;background:color-mix(in srgb,#000 60%,transparent);padding:4px;cursor:grab}.drag-handle mat-icon:not(.toggle-icon){color:#fff;font-size:20px;width:20px;height:20px}.search-content{display:flex;align-items:center;background:color-mix(in srgb,#000 60%,transparent);padding:5px;transition:all .3s ease;box-sizing:border-box;cursor:default}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:20px;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:6px;flex-shrink:0}.toggle-icon:hover{color:#2196f3;background:#2196f314}.search-field{width:100%;min-width:200px;cursor:pointer;flex:1}.search-field ::ng-deep .mat-mdc-form-field-focus-overlay{background-color:transparent}.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important;border-width:1.5px!important;transition:border-color .3s ease,border-width .3s ease}.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important}.search-field ::ng-deep .mat-mdc-form-field-label{color:#fff!important;font-weight:500;font-size:15px}.search-field ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-form-field-label{color:#2196f3!important}.search-field ::ng-deep .mat-mdc-input-element{font-size:15px;color:#fff;font-weight:400;line-height:1.5}.search-field ::ng-deep .mat-mdc-input-element::placeholder{color:#ccc;font-weight:400;opacity:.8}.search-field ::ng-deep .mdc-floating-label{padding:0 4px;left:40px!important}::ng-deep .mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input{margin-left:12px;color:#fff!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{color:#fff!important}::ng-deep .mat-mdc-option .mat-pseudo-checkbox-minimal{color:#fff!important}::ng-deep .mat-mdc-form-field-flex{height:40px!important}::ng-deep .mat-mdc-autocomplete-panel{border-radius:12px!important;max-height:320px!important;background:color-mix(in srgb,#000 85%,transparent)!important;box-shadow:0 8px 24px #00000026!important;margin-top:8px!important;overflow:hidden!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:30px!important;border-radius:8px!important;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mdc-list-item--disabled){background-color:#0000004d!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px;font-weight:400;color:inherit;line-height:1.5}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar{width:6px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-track{background:#f1f1f1;border-radius:0 12px 12px 0}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:3px;transition:background .3s ease}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb:hover{background:#a8a8a8}::ng-deep .mat-mdc-text-field-wrapper{background:color-mix(in srgb,#000 60%,transparent);border-radius:8px;box-shadow:#0000004d 0 1px 4px -1px;transition:all .3s cubic-bezier(.4,0,.2,1)}::ng-deep .mat-mdc-text-field-wrapper:hover{box-shadow:0 4px 16px #00000026}::ng-deep .mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label{color:#fff!important}::ng-deep .mdc-text-field{border-radius:8px!important}@media (max-width: 768px){.search-container{left:2em;top:1.5em;max-width:350px}.search-container.collapsed{width:44px;min-width:44px;max-width:44px}.search-field{min-width:180px}.toggle-icon{font-size:18px;width:18px;height:18px}}@media (max-width: 480px){.search-container{left:1.5em;right:1.5em;max-width:calc(100vw - 3em)}.search-container.collapsed{width:40px;min-width:40px;max-width:40px;right:auto}.search-field{min-width:150px}.search-field ::ng-deep .mat-mdc-input-element{font-size:14px}::ng-deep .mat-mdc-autocomplete-panel{max-height:240px!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:48px!important;padding:10px 16px!important}.toggle-icon{font-size:16px;width:16px;height:16px}}\n"] }]
1936
- }] });
3494
+ ], template: "<div class=\"search-container\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n <div class=\"drag-handle\" cdkDragHandle>\n <mat-icon class=\"icon-left\">search</mat-icon>\n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleSearch()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n </div>\n\n <div class=\"search-content\" *ngIf=\"!collapsed\">\n <mat-form-field appearance=\"outline\" class=\"search-field\">\n <mat-label>S\u00F8ge</mat-label>\n <input\n type=\"text\"\n matInput\n [(ngModel)]=\"searchAddress\"\n [matAutocomplete]=\"auto\"\n (input)=\"filterResults()\"\n />\n <mat-autocomplete #auto=\"matAutocomplete\">\n <mat-option *ngFor=\"let result of filteredResults\" [value]=\"result\" class=\"search-result-option\">\n <span class=\"result-title\">\n {{ result.title }} ({{result.items.length}} af {{ result.total }})\n </span>\n <div *ngFor=\"let item of result.items\" class=\"search-result-item\">\n <span class=\"item-header\">\n {{item.header}}\n </span>\n <mat-icon (click)=\"highlight(item.wkt, $event)\">power_settings_new </mat-icon>\n </div>\n </mat-option>\n </mat-autocomplete>\n </mat-form-field>\n </div>\n</div>", styles: [".search-container{position:absolute;top:8em;left:1em;z-index:2;cursor:grab;max-width:400px;min-width:48px;width:auto}.search-container.cdk-drag-dragging{opacity:.8;cursor:grab}.compact-icon.custom-image-icon{padding:4px;background:none;border:1px solid rgba(255,255,255,.2)}.compact-icon.custom-image-icon img{width:100%;height:100%;object-fit:contain;transition:filter .2s ease}.compact-icon.custom-image-icon.active{background:linear-gradient(147.38deg,#0ea5e9,#075985);border-color:transparent;box-shadow:0 4px 12px #0ea5e966}.compact-icon.custom-image-icon.active img{filter:brightness(0) invert(1)}.compact-icon.custom-image-icon.active:hover{box-shadow:0 6px 20px #0ea5e980}.compact-icon.custom-image-icon:hover:not(.active){background:color-mix(in srgb,#000 70%,transparent);border-color:#0ea5e9;box-shadow:0 2px 8px #0ea5e94d}.compact-icon.custom-image-icon:hover:not(.active) img{filter:brightness(0) invert(.8)}.drag-handle{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;border-radius:5px 5px 0 0;color:#fff}.right-icons{display:flex;align-items:center;gap:6px}.right-icons .drag-indicator-right{font-size:18px!important;width:18px!important;height:18px!important}.search-content{display:flex;flex-direction:column;background:#292a2d;padding:6px;transition:all .3s ease;box-sizing:border-box;cursor:default;border-radius:0 0 5px 5px}.compact-icon{transition:all .3s cubic-bezier(.4,0,.2,1)}.compact-icon.custom-image-icon{background:none;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer}.toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.toggle-icon:hover{color:#fff;background:#ffffff26}.search-field{width:100%;min-width:200px;cursor:pointer;flex:1}.search-field ::ng-deep .mat-mdc-form-field-focus-overlay{background-color:transparent}.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important;border-width:1.5px!important;transition:border-color .3s ease,border-width .3s ease}.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__leading,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__notch,.search-field:not(.mat-focused):hover ::ng-deep .mdc-notched-outline .mdc-notched-outline__trailing{border-color:transparent!important}.search-field ::ng-deep .mat-mdc-form-field-label{color:#fff!important;font-weight:500;font-size:15px}.search-field ::ng-deep .mat-mdc-form-field.mat-focused .mat-mdc-form-field-label{color:#2196f3!important}.search-field ::ng-deep .mat-mdc-input-element{font-size:15px;color:#fff;font-weight:400;line-height:1.5}.search-field ::ng-deep .mat-mdc-input-element::placeholder{color:#ccc;font-weight:400;opacity:.8}.search-field ::ng-deep .mdc-floating-label{padding:0 4px;left:40px!important}::ng-deep .mat-mdc-form-field.mat-form-field-animations-enabled .mdc-text-field__input{margin-left:12px;color:#fff!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{color:#fff!important}::ng-deep .mat-mdc-option .mat-pseudo-checkbox-minimal{color:#fff!important}::ng-deep .mat-mdc-form-field-flex{height:40px!important}::ng-deep .mat-mdc-autocomplete-panel{border-radius:5px!important;max-height:320px!important;background:color-mix(in srgb,#000 85%,transparent)!important;box-shadow:0 8px 24px #00000026!important;margin-top:64px;overflow-y:auto!important;padding:8px!important}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar{width:12px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .mat-mdc-autocomplete-panel::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:30px!important;border-radius:5px!important;transition:all .2s cubic-bezier(.4,0,.2,1);position:relative;overflow:auto;margin-bottom:6px!important;padding:8px 0!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .mdc-list-item__primary-text{font-size:14px;font-weight:400;color:inherit;line-height:1.5}.search-result-option{display:flex;flex-direction:column;gap:8px}.result-title{font-weight:600;font-size:13px;color:#bdc1c3cc!important;display:block;margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px}.search-result-item{display:flex;align-items:center;justify-content:space-between;padding:6px 4px;border-radius:5px;margin-left:4px;transition:all .6s ease}.search-result-item:hover{color:#f4f4f5;background:#ffffff1a}.search-result-item .item-header{font-size:14px;color:#fff!important;flex:1;font-weight:500}.search-result-item mat-icon{color:#fff!important;font-size:20px;width:20px;height:20px;cursor:pointer;transition:all .2s ease}.search-result-item mat-icon:hover{color:#f7f7f7!important}::ng-deep .mat-mdc-text-field-wrapper{background:color-mix(in srgb,#000 60%,transparent);border-radius:5px;box-shadow:#0000004d 0 1px 4px -1px;transition:all .3s cubic-bezier(.4,0,.2,1)}::ng-deep .mat-mdc-text-field-wrapper:hover{box-shadow:0 4px 16px #00000026}::ng-deep .mat-mdc-form-field:not(.mat-form-field-disabled) .mat-mdc-floating-label.mdc-floating-label{color:#fff!important}::ng-deep .mdc-text-field{border-radius:5px!important}@media (max-width: 768px){.search-container{left:2em;top:1.5em;max-width:350px}.search-container.collapsed{width:44px;min-width:88px;max-width:44px}.search-field{min-width:180px}.toggle-icon{font-size:18px;width:18px;height:18px}}@media (max-width: 480px){.search-container{left:1.5em;right:1.5em;max-width:calc(100vw - 3em)}.search-container.collapsed{width:40px;min-width:40px;max-width:40px;right:auto}.search-field{min-width:150px}.search-field ::ng-deep .mat-mdc-input-element{font-size:14px}::ng-deep .mat-mdc-autocomplete-panel{max-height:240px!important}::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option{min-height:48px!important;padding:10px 16px!important}.toggle-icon{font-size:16px;width:16px;height:16px}}\n"] }]
3495
+ }], propDecorators: { profile: [{
3496
+ type: Input
3497
+ }] } });
1937
3498
 
1938
3499
  class LayerService {
1939
3500
  config = inject(GISKOMPONENT_CONFIG);
@@ -1954,69 +3515,81 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
1954
3515
 
1955
3516
  class LegendsListComponent {
1956
3517
  _http = inject(HttpClient);
3518
+ _layerService = inject(LayerService);
3519
+ _layerHelperService = inject(LayerHelperService);
3520
+ _layoutService = inject(LayoutService);
3521
+ config = inject(GISKOMPONENT_CONFIG);
3522
+ map;
3523
+ profile;
3524
+ filteredLayersDetailed = [];
3525
+ collapsed = true;
3526
+ dragPosition = { x: 0, y: 0 };
3527
+ _baseUrl = this.config.apiBaseUrl;
3528
+ _legendsListBodyControl;
3529
+ _mapFilteredLayerGroupsKeyName = 'mapFilteredLayerGroups';
3530
+ POSITION_STORAGE_KEY = 'legendsListPosition';
1957
3531
  ngOnInit() {
1958
- this._initializeMapIconControl();
3532
+ this._loadPosition();
1959
3533
  this._layerHelperService.activeLayersChanged.subscribe(() => this._setFilteredLegends().subscribe(result => this.filteredLayersDetailed = result));
1960
3534
  }
1961
3535
  ngOnChanges(changes) {
1962
- if (changes['profile'] && this.profile)
3536
+ if (changes['profile'] && this.profile) {
3537
+ this._loadPosition();
1963
3538
  this._setFilteredLegends().subscribe(result => this.filteredLayersDetailed = result);
3539
+ }
1964
3540
  }
1965
- set contentIcon(content) {
1966
- this._legendsListIcon = content;
1967
- }
1968
- set contentBody(content) {
1969
- this._legendsListBody = content;
3541
+ ngOnDestroy() {
3542
+ if (this._legendsListBodyControl) {
3543
+ this.map.removeControl(this._legendsListBodyControl);
3544
+ }
1970
3545
  }
1971
- map;
1972
- profile;
1973
- searchText = '';
1974
- filteredLayersDetailed = [];
1975
- showList = false;
1976
- config = inject(GISKOMPONENT_CONFIG);
1977
- _baseUrl = this.config.apiBaseUrl;
1978
- _legendsListIcon;
1979
- _legendsListIconControl;
1980
- _legendsListBody;
1981
- _legendsListBodyControl;
1982
- _layerIdsToDisplayInMapKeyName = 'layerIdsToDisplayInMap';
1983
- _layerService = inject(LayerService);
1984
- _layerHelperService = inject(LayerHelperService);
1985
3546
  toggleLegendsList() {
1986
- this.showList = !this.showList;
1987
- if (this.showList) {
1988
- this._addMapBodyControl();
1989
- }
1990
- else {
1991
- this._removeMapBodyControl();
1992
- }
3547
+ this.collapsed = !this.collapsed;
1993
3548
  }
1994
- _initializeMapIconControl() {
1995
- this.map.removeControl(this._legendsListIconControl);
1996
- const element = this._legendsListIcon?.nativeElement;
1997
- if (element) {
1998
- this._legendsListIconControl = new ol_control_Control({ element: element });
1999
- this.map.addControl(this._legendsListIconControl);
2000
- }
3549
+ onDragEnded(event) {
3550
+ const position = event.source.getFreeDragPosition();
3551
+ this.dragPosition = position;
3552
+ this._savePosition(position);
2001
3553
  }
2002
3554
  _getImageUrl(fileName) {
2003
3555
  return `${this._baseUrl}/api/image/${fileName}`;
2004
3556
  }
2005
- _addMapBodyControl() {
2006
- this.map.removeControl(this._legendsListBodyControl);
2007
- const element = this._legendsListBody.nativeElement;
2008
- this._legendsListBodyControl = new ol_control_Control({ element: element });
2009
- this.map.addControl(this._legendsListBodyControl);
3557
+ _savePosition(position) {
3558
+ try {
3559
+ localStorage.setItem(this.POSITION_STORAGE_KEY, JSON.stringify(position));
3560
+ }
3561
+ catch (error) {
3562
+ console.error('Error saving legends list position to localStorage:', error);
3563
+ }
2010
3564
  }
2011
- _removeMapBodyControl() {
2012
- this.map.removeControl(this._legendsListBodyControl);
3565
+ _loadPosition() {
3566
+ try {
3567
+ const savedPosition = localStorage.getItem(this.POSITION_STORAGE_KEY);
3568
+ if (savedPosition) {
3569
+ this.dragPosition = JSON.parse(savedPosition);
3570
+ }
3571
+ else if (this.profile) {
3572
+ this.dragPosition = this._layoutService.loadInitialPositionFromProfile(this.POSITION_STORAGE_KEY, this.profile.legendsListInitialPosition);
3573
+ }
3574
+ }
3575
+ catch (error) {
3576
+ console.error('Error loading legends list position from localStorage:', error);
3577
+ this.dragPosition = { x: 0, y: 0 };
3578
+ }
2013
3579
  }
2014
3580
  _setFilteredLegends() {
2015
3581
  if (!this.profile?.id) {
2016
3582
  return of([]);
2017
3583
  }
2018
- const layerIdsCachedToDisplay = localStorage.getItem(`${this._layerIdsToDisplayInMapKeyName}_${this.profile?.id}`)?.split(',').map(Number);
2019
- if (!layerIdsCachedToDisplay) {
3584
+ const cacheValue = localStorage.getItem(`${this._mapFilteredLayerGroupsKeyName}_${this.profile?.id}`);
3585
+ if (!cacheValue) {
3586
+ return of([]);
3587
+ }
3588
+ const layerIdsCachedToDisplay = JSON.parse(cacheValue).cachedLayerGroups
3589
+ .flatMap((lg) => lg.layers)
3590
+ .filter((l) => l.visible)
3591
+ .map((l) => l.id);
3592
+ if (!layerIdsCachedToDisplay || layerIdsCachedToDisplay.length === 0) {
2020
3593
  return of([]);
2021
3594
  }
2022
3595
  return this._layerService.getByIds(layerIdsCachedToDisplay)
@@ -2053,19 +3626,13 @@ class LegendsListComponent {
2053
3626
  })));
2054
3627
  }
2055
3628
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LegendsListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2056
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: LegendsListComponent, isStandalone: true, selector: "lib-legends-list", inputs: { map: "map", profile: "profile" }, viewQueries: [{ propertyName: "contentIcon", first: true, predicate: ["legendsListIcon"], descendants: true }, { propertyName: "contentBody", first: true, predicate: ["legendsListBody"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div #legendsListIcon class=\"ol-unselectable ol-control legends-list-icon\">\n <mat-icon (click)=\"toggleLegendsList()\">area_chart</mat-icon>\n</div>\n<div #legendsListBody [class.display-none]=\"!showList\" class=\"legends-list-body-wrapper\" cdkDrag cdkDragBoundary=\".map-container\">\n <div class=\"drag-handle-selector\" cdkDragHandle>\n <mat-icon>drag_indicator</mat-icon>\n </div>\n <div class=\"ol-unselectable ol-control legends-list-body\">\n <div class=\"item-list\">\n @for (layer of filteredLayersDetailed; track layer.id; let iIndex = $index) {\n @if (layer.imageUrl) {\n <mat-expansion-panel expanded=\"true\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" ><span>{{ layer.name }}</span></div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"legend\">\n <span><img [src]=\"layer.imageUrl\" class=\"legend-thumbnail\"/></span>\n </div>\n </mat-expansion-panel>\n }\n }\n </div>\n </div>\n</div>", styles: ["::ng-deep .legends-list-icon{position:absolute;left:auto;right:1em;bottom:1em;z-index:1000}::ng-deep .legends-list-icon mat-icon{display:flex;justify-content:center;align-items:center;cursor:pointer;transition:all .3s ease;outline:none;border:0;height:40px;border-radius:5px;width:40px;background:color-mix(in srgb,#000 60%,transparent);box-shadow:#0000004d 0 1px 4px -1px;font-size:2em;color:#fff}::ng-deep .legends-list-icon mat-icon:hover{box-shadow:0 4px 12px #0003;color:#fff}.legends-list-body-wrapper{position:absolute;left:auto;right:35em;bottom:.2em;z-index:1000;cursor:grab}@media (max-width: 1024px){.legends-list-body-wrapper{right:32em;bottom:5em}}@media (max-width: 768px){.legends-list-body-wrapper{position:fixed;right:3em;left:1em;bottom:5em;width:calc(100% - 7em);max-width:100%}.legends-list-body-wrapper.cdk-drag-dragging{z-index:1002}}@media (max-width: 480px){.legends-list-body-wrapper{bottom:4em}}.legends-list-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grabbing;z-index:1001}.legends-list-body-wrapper .ol-control{border-radius:0}.drag-handle-selector{display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,#000 60%,transparent);border-radius:4px 4px 0 0;padding:4px;cursor:grab;box-shadow:0 -2px 8px #0003}@media (max-width: 768px){.drag-handle-selector{padding:8px}}.drag-handle-selector mat-icon{color:#fff;font-size:20px;width:20px;height:20px}@media (max-width: 768px){.drag-handle-selector mat-icon{font-size:24px;width:24px;height:24px}}::ng-deep .legends-list-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:color-mix(in srgb,#000 60%,transparent);box-shadow:0 4px 20px #00000026;width:290px;max-height:660px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 1024px){::ng-deep .legends-list-body{width:280px}}@media (max-width: 768px){::ng-deep .legends-list-body{width:100%;max-height:60vh;max-height:calc(var(--vh, 1vh) * 60)}}@media (max-width: 480px){::ng-deep .legends-list-body{max-height:70vh;max-height:calc(var(--vh, 1vh) * 70)}}::ng-deep .legends-list-body .item-list{flex:1;overflow-y:auto;padding:6px;max-height:900px}@media (max-width: 768px){::ng-deep .legends-list-body .item-list{padding:8px;max-height:none}}::ng-deep .legends-list-body .item-list .legend{overflow:hidden;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .legends-list-body .item-list .legend span{color:#fff}::ng-deep .legends-list-body .item-list .item{display:flex;align-items:center;gap:6px;padding:8px 12px;background:transparent;transition:all .2s ease;color:#fff;cursor:grab}@media (max-width: 768px){::ng-deep .legends-list-body .item-list .item{padding:12px 16px;gap:8px}}::ng-deep .legends-list-body .item-list .item:last-child{border-bottom:none}@media (max-width: 768px){::ng-deep .legends-list-body .item-list mat-expansion-panel{margin-bottom:8px}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header{padding:0 8px;height:auto;min-height:48px}::ng-deep .legends-list-body .item-list .legend{padding:8px}::ng-deep .legends-list-body .item-list .legend-thumbnail{max-width:100%;height:auto;max-height:150px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar{width:6px}}.legend-thumbnail{max-width:200px;max-height:200px;width:auto;height:auto;border:2px solid #dee2e6;border-radius:6px;padding:6px}@media (max-width: 768px){.legend-thumbnail{max-width:100%;max-height:150px;padding:4px}}::ng-deep .legends-list-body .item-list::-webkit-scrollbar{width:8px;border-radius:10px;border:5px solid transparent}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb{background:#0000004d;border:5px solid transparent;border-radius:10px;background-clip:padding-box}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb:hover{background:#0006;background-clip:padding-box;border:3px solid transparent}.display-none{display:none}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i4.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatInputModule }] });
3629
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: LegendsListComponent, isStandalone: true, selector: "lib-legends-list", inputs: { map: "map", profile: "profile" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"legends-list-body-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n\n <div class=\"drag-handle-legends\" cdkDragHandle>\n <mat-icon class=\"legend-icon\" (click)=\"toggleLegendsList()\">area_chart</mat-icon>\n\n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleLegendsList()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n</div>\n\n<div class=\"legends-list-container\">\n @if(!collapsed) {\n <div class=\"ol-unselectable ol-control legends-list-body\">\n <div class=\"item-list\">\n @for (layer of filteredLayersDetailed; track layer.id; let i = $index) {\n @if (layer.imageUrl) {\n <mat-expansion-panel [expanded]=\"i === 0\"> \n <mat-expansion-panel-header>\n <span class=\"panel-title\">\n {{ layer.name }}\n </span>\n </mat-expansion-panel-header>\n <div class=\"legend\">\n <img [src]=\"layer.imageUrl\" class=\"legend-thumbnail\"/>\n </div>\n </mat-expansion-panel>\n }\n }\n </div>\n </div>\n}\n</div>\n</div>", styles: [".legends-list-body-wrapper{position:absolute;left:1em;top:10em;z-index:1000;cursor:grab;box-shadow:0 2px 10px #0000001a;background:#292a2d;width:250px;max-width:250px;transition:width .3s ease,max-width .3s ease;border-radius:5px 5px 0 0}.legends-list-body-wrapper.collapsed{width:90px}.legends-list-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grabbing;z-index:1001}@media (max-width: 1024px){.legends-list-body-wrapper{width:240px;max-width:240px}}@media (max-width: 768px){.legends-list-body-wrapper{position:fixed;right:1em;bottom:5em;width:calc(100% - 2em);max-width:280px}.legends-list-body-wrapper.collapsed{width:90px}}@media (max-width: 480px){.legends-list-body-wrapper{bottom:4em}}.drag-handle-legends{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;color:#fff;border-radius:5px 5px 0 0}.drag-handle-legends .legend-icon{color:#fff;font-size:18px;width:18px;height:18px;cursor:pointer;transition:all .2s ease}.drag-handle-legends .legend-icon:hover{color:#4caf50}.drag-handle-legends .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.drag-handle-legends .toggle-icon:hover{color:#fff;background:#ffffff26}.right-icons{display:flex;align-items:center;gap:6px}.right-icons .drag-indicator-right{font-size:18px!important;width:18px!important;height:18px!important;color:#fff}.legends-list-container{display:flex;flex-direction:column;align-items:center;width:100%;transition:all .3s ease;cursor:default;box-sizing:border-box}::ng-deep .legends-list-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:#000;width:100%;max-height:317px;min-height:80px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 1024px){::ng-deep .legends-list-body{max-height:450px;min-height:70px}}@media (max-width: 768px){::ng-deep .legends-list-body{max-height:60vh;max-height:calc(var(--vh, 1vh) * 60);min-height:100px}}@media (max-width: 480px){::ng-deep .legends-list-body{max-height:70vh;max-height:calc(var(--vh, 1vh) * 70);min-height:90px}}::ng-deep .legends-list-body .item-list{flex:1 1 auto;overflow-y:auto;padding:4px;min-height:0;background:#000;max-height:500px}@media (max-width: 768px){::ng-deep .legends-list-body .item-list{padding:6px;max-height:400px}}::ng-deep .legends-list-body .item-list mat-expansion-panel{border-radius:0!important;box-shadow:none!important;width:100%;margin-bottom:4px}::ng-deep .legends-list-body .item-list mat-expansion-panel:last-child{border-bottom:none}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header{padding:0 8px;height:40px!important;min-height:40px!important}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header .mat-content{display:flex;align-items:center}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header .panel-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;line-height:1.3;font-weight:600;color:#bdc1c3cc;font-size:17px}@media (max-width: 768px){::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header .panel-title{max-width:180px;font-size:15px}}::ng-deep .legends-list-body .item-list .mat-expansion-panel-body{padding:4px 8px 8px!important}::ng-deep .legends-list-body .item-list .legend{overflow:hidden;padding:4px;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .legends-list-body .item-list .legend span{color:#fff}.legend-thumbnail{max-width:100%;width:auto;height:auto;max-height:150px;border:1px solid #dee2e6;border-radius:4px;padding:4px;display:block}@media (max-width: 768px){.legend-thumbnail{max-height:120px;padding:3px}}::ng-deep .legends-list-body .item-list::-webkit-scrollbar{width:12px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}@media (max-width: 768px){.legends-list-body-wrapper{right:.5em;bottom:5em;max-width:280px}}@media (max-width: 480px){.legends-list-body-wrapper{right:.5em;left:.5em;max-width:calc(100vw - 1em);width:auto}.legends-list-body-wrapper.collapsed{width:90px;left:auto}::ng-deep .legends-list-body .item-list{max-height:300px}}\n"], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i5.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i6.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "ngmodule", type: MatInputModule }] });
2057
3630
  }
2058
3631
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: LegendsListComponent, decorators: [{
2059
3632
  type: Component,
2060
3633
  args: [{ selector: 'lib-legends-list', imports: [MatFormFieldModule, CommonModule, MatIconModule, FormsModule, DragDropModule,
2061
- MatExpansionModule, MatInputModule], template: "<div #legendsListIcon class=\"ol-unselectable ol-control legends-list-icon\">\n <mat-icon (click)=\"toggleLegendsList()\">area_chart</mat-icon>\n</div>\n<div #legendsListBody [class.display-none]=\"!showList\" class=\"legends-list-body-wrapper\" cdkDrag cdkDragBoundary=\".map-container\">\n <div class=\"drag-handle-selector\" cdkDragHandle>\n <mat-icon>drag_indicator</mat-icon>\n </div>\n <div class=\"ol-unselectable ol-control legends-list-body\">\n <div class=\"item-list\">\n @for (layer of filteredLayersDetailed; track layer.id; let iIndex = $index) {\n @if (layer.imageUrl) {\n <mat-expansion-panel expanded=\"true\"> \n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"item\" ><span>{{ layer.name }}</span></div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n <div class=\"legend\">\n <span><img [src]=\"layer.imageUrl\" class=\"legend-thumbnail\"/></span>\n </div>\n </mat-expansion-panel>\n }\n }\n </div>\n </div>\n</div>", styles: ["::ng-deep .legends-list-icon{position:absolute;left:auto;right:1em;bottom:1em;z-index:1000}::ng-deep .legends-list-icon mat-icon{display:flex;justify-content:center;align-items:center;cursor:pointer;transition:all .3s ease;outline:none;border:0;height:40px;border-radius:5px;width:40px;background:color-mix(in srgb,#000 60%,transparent);box-shadow:#0000004d 0 1px 4px -1px;font-size:2em;color:#fff}::ng-deep .legends-list-icon mat-icon:hover{box-shadow:0 4px 12px #0003;color:#fff}.legends-list-body-wrapper{position:absolute;left:auto;right:35em;bottom:.2em;z-index:1000;cursor:grab}@media (max-width: 1024px){.legends-list-body-wrapper{right:32em;bottom:5em}}@media (max-width: 768px){.legends-list-body-wrapper{position:fixed;right:3em;left:1em;bottom:5em;width:calc(100% - 7em);max-width:100%}.legends-list-body-wrapper.cdk-drag-dragging{z-index:1002}}@media (max-width: 480px){.legends-list-body-wrapper{bottom:4em}}.legends-list-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grabbing;z-index:1001}.legends-list-body-wrapper .ol-control{border-radius:0}.drag-handle-selector{display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,#000 60%,transparent);border-radius:4px 4px 0 0;padding:4px;cursor:grab;box-shadow:0 -2px 8px #0003}@media (max-width: 768px){.drag-handle-selector{padding:8px}}.drag-handle-selector mat-icon{color:#fff;font-size:20px;width:20px;height:20px}@media (max-width: 768px){.drag-handle-selector mat-icon{font-size:24px;width:24px;height:24px}}::ng-deep .legends-list-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:color-mix(in srgb,#000 60%,transparent);box-shadow:0 4px 20px #00000026;width:290px;max-height:660px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 1024px){::ng-deep .legends-list-body{width:280px}}@media (max-width: 768px){::ng-deep .legends-list-body{width:100%;max-height:60vh;max-height:calc(var(--vh, 1vh) * 60)}}@media (max-width: 480px){::ng-deep .legends-list-body{max-height:70vh;max-height:calc(var(--vh, 1vh) * 70)}}::ng-deep .legends-list-body .item-list{flex:1;overflow-y:auto;padding:6px;max-height:900px}@media (max-width: 768px){::ng-deep .legends-list-body .item-list{padding:8px;max-height:none}}::ng-deep .legends-list-body .item-list .legend{overflow:hidden;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .legends-list-body .item-list .legend span{color:#fff}::ng-deep .legends-list-body .item-list .item{display:flex;align-items:center;gap:6px;padding:8px 12px;background:transparent;transition:all .2s ease;color:#fff;cursor:grab}@media (max-width: 768px){::ng-deep .legends-list-body .item-list .item{padding:12px 16px;gap:8px}}::ng-deep .legends-list-body .item-list .item:last-child{border-bottom:none}@media (max-width: 768px){::ng-deep .legends-list-body .item-list mat-expansion-panel{margin-bottom:8px}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header{padding:0 8px;height:auto;min-height:48px}::ng-deep .legends-list-body .item-list .legend{padding:8px}::ng-deep .legends-list-body .item-list .legend-thumbnail{max-width:100%;height:auto;max-height:150px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar{width:6px}}.legend-thumbnail{max-width:200px;max-height:200px;width:auto;height:auto;border:2px solid #dee2e6;border-radius:6px;padding:6px}@media (max-width: 768px){.legend-thumbnail{max-width:100%;max-height:150px;padding:4px}}::ng-deep .legends-list-body .item-list::-webkit-scrollbar{width:8px;border-radius:10px;border:5px solid transparent}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb{background:#0000004d;border:5px solid transparent;border-radius:10px;background-clip:padding-box}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb:hover{background:#0006;background-clip:padding-box;border:3px solid transparent}.display-none{display:none}\n"] }]
2062
- }], propDecorators: { contentIcon: [{
2063
- type: ViewChild,
2064
- args: ['legendsListIcon', { static: false }]
2065
- }], contentBody: [{
2066
- type: ViewChild,
2067
- args: ['legendsListBody', { static: false }]
2068
- }], map: [{
3634
+ MatExpansionModule, MatInputModule], template: "<div class=\"legends-list-body-wrapper\" \n cdkDrag \n cdkDragBoundary=\".map-container\" \n [cdkDragFreeDragPosition]=\"dragPosition\"\n (cdkDragEnded)=\"onDragEnded($event)\"\n [class.collapsed]=\"collapsed\">\n\n <div class=\"drag-handle-legends\" cdkDragHandle>\n <mat-icon class=\"legend-icon\" (click)=\"toggleLegendsList()\">area_chart</mat-icon>\n\n <div class=\"right-icons\">\n <mat-icon class=\"drag-indicator-right\">open_with</mat-icon>\n <mat-icon class=\"toggle-icon\" (click)=\"toggleLegendsList()\">\n {{ collapsed ? 'flip_to_front' : 'remove' }}\n </mat-icon>\n </div>\n</div>\n\n<div class=\"legends-list-container\">\n @if(!collapsed) {\n <div class=\"ol-unselectable ol-control legends-list-body\">\n <div class=\"item-list\">\n @for (layer of filteredLayersDetailed; track layer.id; let i = $index) {\n @if (layer.imageUrl) {\n <mat-expansion-panel [expanded]=\"i === 0\"> \n <mat-expansion-panel-header>\n <span class=\"panel-title\">\n {{ layer.name }}\n </span>\n </mat-expansion-panel-header>\n <div class=\"legend\">\n <img [src]=\"layer.imageUrl\" class=\"legend-thumbnail\"/>\n </div>\n </mat-expansion-panel>\n }\n }\n </div>\n </div>\n}\n</div>\n</div>", styles: [".legends-list-body-wrapper{position:absolute;left:1em;top:10em;z-index:1000;cursor:grab;box-shadow:0 2px 10px #0000001a;background:#292a2d;width:250px;max-width:250px;transition:width .3s ease,max-width .3s ease;border-radius:5px 5px 0 0}.legends-list-body-wrapper.collapsed{width:90px}.legends-list-body-wrapper.cdk-drag-dragging{opacity:.8;cursor:grabbing;z-index:1001}@media (max-width: 1024px){.legends-list-body-wrapper{width:240px;max-width:240px}}@media (max-width: 768px){.legends-list-body-wrapper{position:fixed;right:1em;bottom:5em;width:calc(100% - 2em);max-width:280px}.legends-list-body-wrapper.collapsed{width:90px}}@media (max-width: 480px){.legends-list-body-wrapper{bottom:4em}}.drag-handle-legends{display:flex;align-items:center;justify-content:space-between;background:#292a2d;padding:2px 8px;cursor:grab;color:#fff;border-radius:5px 5px 0 0}.drag-handle-legends .legend-icon{color:#fff;font-size:18px;width:18px;height:18px;cursor:pointer;transition:all .2s ease}.drag-handle-legends .legend-icon:hover{color:#4caf50}.drag-handle-legends .toggle-icon{cursor:pointer;color:#fff;transition:all .2s ease;-webkit-user-select:none;user-select:none;font-size:18px!important;width:18px!important;height:18px!important;display:flex;align-items:center;justify-content:center;border-radius:4px;padding:2px;flex-shrink:0}.drag-handle-legends .toggle-icon:hover{color:#fff;background:#ffffff26}.right-icons{display:flex;align-items:center;gap:6px}.right-icons .drag-indicator-right{font-size:18px!important;width:18px!important;height:18px!important;color:#fff}.legends-list-container{display:flex;flex-direction:column;align-items:center;width:100%;transition:all .3s ease;cursor:default;box-sizing:border-box}::ng-deep .legends-list-body{position:relative;left:auto;right:auto;bottom:auto;z-index:auto;background:#000;width:100%;max-height:317px;min-height:80px;overflow:hidden;display:flex;flex-direction:column}@media (max-width: 1024px){::ng-deep .legends-list-body{max-height:450px;min-height:70px}}@media (max-width: 768px){::ng-deep .legends-list-body{max-height:60vh;max-height:calc(var(--vh, 1vh) * 60);min-height:100px}}@media (max-width: 480px){::ng-deep .legends-list-body{max-height:70vh;max-height:calc(var(--vh, 1vh) * 70);min-height:90px}}::ng-deep .legends-list-body .item-list{flex:1 1 auto;overflow-y:auto;padding:4px;min-height:0;background:#000;max-height:500px}@media (max-width: 768px){::ng-deep .legends-list-body .item-list{padding:6px;max-height:400px}}::ng-deep .legends-list-body .item-list mat-expansion-panel{border-radius:0!important;box-shadow:none!important;width:100%;margin-bottom:4px}::ng-deep .legends-list-body .item-list mat-expansion-panel:last-child{border-bottom:none}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header{padding:0 8px;height:40px!important;min-height:40px!important}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header .mat-content{display:flex;align-items:center}::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header .panel-title{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;line-height:1.3;font-weight:600;color:#bdc1c3cc;font-size:17px}@media (max-width: 768px){::ng-deep .legends-list-body .item-list mat-expansion-panel .mat-expansion-panel-header .panel-title{max-width:180px;font-size:15px}}::ng-deep .legends-list-body .item-list .mat-expansion-panel-body{padding:4px 8px 8px!important}::ng-deep .legends-list-body .item-list .legend{overflow:hidden;padding:4px;box-shadow:0 -2px 2px #4868b20a,0 2px 2px #6a6f7517,0 1px 2px #4868b214}::ng-deep .legends-list-body .item-list .legend span{color:#fff}.legend-thumbnail{max-width:100%;width:auto;height:auto;max-height:150px;border:1px solid #dee2e6;border-radius:4px;padding:4px;display:block}@media (max-width: 768px){.legend-thumbnail{max-height:120px;padding:3px}}::ng-deep .legends-list-body .item-list::-webkit-scrollbar{width:12px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .legends-list-body .item-list::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}@media (max-width: 768px){.legends-list-body-wrapper{right:.5em;bottom:5em;max-width:280px}}@media (max-width: 480px){.legends-list-body-wrapper{right:.5em;left:.5em;max-width:calc(100vw - 1em);width:auto}.legends-list-body-wrapper.collapsed{width:90px;left:auto}::ng-deep .legends-list-body .item-list{max-height:300px}}\n"] }]
3635
+ }], propDecorators: { map: [{
2069
3636
  type: Input
2070
3637
  }], profile: [{
2071
3638
  type: Input
@@ -2084,19 +3651,49 @@ class FeatureLoaderService {
2084
3651
  return;
2085
3652
  }
2086
3653
  const profile = this._current.profile;
3654
+ if (!settings || !settings.geometryTypeSettings)
3655
+ return;
2087
3656
  const allTheFeatures = settings.geometryTypeSettings.filter(gt => gt.features).map(gt => {
2088
3657
  const rawFeatures = this.fomatter.readFeatures(gt.features);
2089
3658
  rawFeatures.forEach(f => {
2090
3659
  this._featureHelper.setId(f);
2091
3660
  this._featureHelper.setTypeId(f, gt.typeId);
3661
+ if (gt.disableEditFeatures) {
3662
+ this._featureHelper.lock(f);
3663
+ }
2092
3664
  f.set('STYLENAME', gt.style);
3665
+ if (gt.showLabelFromProperty) {
3666
+ f.set('showlabelfrom', gt.showLabelFromProperty);
3667
+ }
2093
3668
  });
2094
3669
  return rawFeatures;
2095
3670
  }).flatMap(f => f);
2096
3671
  const getAllStyles$ = allTheFeatures.map(f => this._settingsHelper.getStyle(f.get('STYLENAME'), profile?.styleRepositoryWorkspace, profile?.styleRepositoryGeoserver, f.getGeometry().getType()));
3672
+ if (getAllStyles$.length === 0) {
3673
+ this._undoRedo.init();
3674
+ return;
3675
+ }
2097
3676
  combineLatest(getAllStyles$).subscribe({
2098
3677
  next: styles => {
2099
- styles.forEach((s, i) => allTheFeatures[i].setStyle(s));
3678
+ styles.forEach((s, i) => {
3679
+ const feature = allTheFeatures[i];
3680
+ if (feature.get('showlabelfrom')) {
3681
+ const styles = Array.isArray(s) ? s : [s];
3682
+ const textStyle = new Text({
3683
+ font: '14px Calibri,sans-serif',
3684
+ fill: new Fill({ color: '#000' }),
3685
+ stroke: new Stroke({ color: '#fff', width: 3 }),
3686
+ textAlign: 'center',
3687
+ text: feature.get(feature.get('showlabelfrom'))
3688
+ });
3689
+ const styleText = new Style({ text: textStyle });
3690
+ styles.push(styleText);
3691
+ feature.setStyle(styles);
3692
+ }
3693
+ else {
3694
+ feature.setStyle(s);
3695
+ }
3696
+ });
2100
3697
  this._drawHelperService.setFeatures(allTheFeatures);
2101
3698
  this._zoom.zoomToFeatures(allTheFeatures);
2102
3699
  this._undoRedo.init();
@@ -2104,22 +3701,157 @@ class FeatureLoaderService {
2104
3701
  });
2105
3702
  }
2106
3703
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FeatureLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2107
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FeatureLoaderService, providedIn: 'root' });
3704
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FeatureLoaderService });
2108
3705
  }
2109
3706
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: FeatureLoaderService, decorators: [{
2110
- type: Injectable,
2111
- args: [{
2112
- providedIn: 'root'
2113
- }]
3707
+ type: Injectable
3708
+ }] });
3709
+
3710
+ class SelectedFeatureInfoComponent {
3711
+ _infoService = inject(ShowInfoService);
3712
+ features = null;
3713
+ collapsedStates = {};
3714
+ ngOnInit() {
3715
+ this._infoService.features$.subscribe({
3716
+ next: f => {
3717
+ this.features = f ? [...f] : null;
3718
+ if (this.features) {
3719
+ this.features.forEach(item => {
3720
+ if (this.collapsedStates[item.layerName] === undefined) {
3721
+ this.collapsedStates[item.layerName] = true;
3722
+ }
3723
+ });
3724
+ }
3725
+ }
3726
+ });
3727
+ }
3728
+ toggleCollapse(layerName) {
3729
+ this.collapsedStates[layerName] = !this.collapsedStates[layerName];
3730
+ }
3731
+ isCollapsed(layerName) {
3732
+ return this.collapsedStates[layerName] || false;
3733
+ }
3734
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SelectedFeatureInfoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3735
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: SelectedFeatureInfoComponent, isStandalone: true, selector: "selected-feature-info", ngImport: i0, template: "\n@if(features) {\n <div class=\"selected-features-wrapper\"> \n\n @for(item of features; track item.layerName) { \n <div class=\"feature-section\">\n <h4 (click)=\"toggleCollapse(item.layerName)\" class=\"collapsible-header\">\n <mat-icon class=\"collapse-icon\">\n {{ isCollapsed(item.layerName) ? 'keyboard_arrow_up' : 'expand_more' }}\n </mat-icon>\n {{item.layerName}}\n </h4>\n \n <div class=\"feature-content\" [class.collapsed]=\"isCollapsed(item.layerName)\">\n @for(feature of item.values; track feature) {\n <div class=\"feature-item\">\n <div *ngFor=\"let kv of (feature | keyvalue)\">\n {{ kv.value.name }}:\n <ng-container [ngSwitch]=\"kv.value.kind\">\n <img *ngSwitchCase=\"'img'\" [src]=\"kv.value.data\" alt=\"{{ kv.key }}\" style=\"max-width:240px;\" />\n <a *ngSwitchCase=\"'url'\" [href]=\"kv.value.data\" target=\"_blank\" rel=\"noopener noreferrer\">\n {{ kv.key }}\n <mat-icon class=\"link-icon\">open_in_new</mat-icon>\n </a>\n <span *ngSwitchDefault>{{ kv.value.data }}</span>\n </ng-container>\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n}\n", styles: ["::ng-deep .selected-features-wrapper{background:#292a2d;border-radius:5px;box-shadow:0 4px 6px #00000012,0 10px 20px #0000001a;max-width:420px;max-height:450px;overflow-y:auto;position:relative;font-family:Avenir Next W01,Lato,-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.8)}::ng-deep .selected-features-wrapper::-webkit-scrollbar{width:12px}::ng-deep .selected-features-wrapper::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .selected-features-wrapper::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .selected-features-wrapper::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}::ng-deep .selected-features-wrapper .feature-section{padding:20px 24px;position:relative}::ng-deep .selected-features-wrapper .feature-section:after{content:\"\";position:absolute;bottom:0;left:24px;right:24px;height:1px;background:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.08) 20%,rgba(0,0,0,.08) 80%,transparent 100%)}::ng-deep .selected-features-wrapper .feature-section:last-child:after{display:none}::ng-deep .selected-features-wrapper .feature-section h4.collapsible-header{margin:0 0 16px;font-size:18px;font-weight:700;color:#bdc1c3cc;letter-spacing:-.3px;position:relative;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;display:flex;align-items:center}::ng-deep .selected-features-wrapper .feature-section h4.collapsible-header:hover{color:#d4d8dacc}::ng-deep .selected-features-wrapper .feature-section h4.collapsible-header .collapse-icon{position:absolute;right:0;top:50%;transform:translateY(-50%);font-size:24px;width:24px;height:24px;transition:transform .3s ease;color:#fff}::ng-deep .selected-features-wrapper .feature-section .feature-content{max-height:2000px;overflow:hidden;transition:max-height .4s cubic-bezier(.4,0,.2,1),opacity .3s ease,margin-top .3s ease;opacity:1}::ng-deep .selected-features-wrapper .feature-section .feature-content.collapsed{max-height:0;opacity:0;margin-top:0}::ng-deep .selected-features-wrapper .feature-section .feature-item{margin-top:14px;background:#000;border-radius:10px;padding:16px;box-shadow:0 2px 4px #0000000a,0 1px 2px #0000000f;border:1px solid rgba(0,0,0,.06);transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden;color:#fff}::ng-deep .selected-features-wrapper .feature-section .feature-item:hover{box-shadow:0 4px 12px #2196f326,0 2px 4px #00000014;border-color:#2196f333}::ng-deep .selected-features-wrapper .feature-section .feature-item:hover:before{opacity:1}::ng-deep .selected-features-wrapper .feature-section .feature-item:first-of-type{margin-top:0}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of]{display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;margin-bottom:12px;font-size:14px;line-height:1.6;padding:6px 0;color:#546e7a;font-weight:600;white-space:nowrap;display:flex;align-items:center}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of]:last-child{margin-bottom:0}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of]:after{content:\"\";display:inline-block;width:4px;height:4px;background:#2196f3;border-radius:50%;margin-left:6px;opacity:.5}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of] span{color:#1a1a1a;font-weight:500;word-break:break-word;background:linear-gradient(135deg,#f5f5f5,#fafafa);padding:4px 10px;border-radius:6px;border:1px solid rgba(0,0,0,.05)}::ng-deep .selected-features-wrapper img{margin-top:8px;border-radius:8px;border:2px solid #e3f2fd;max-width:100%;max-height:200px;display:block;box-shadow:0 4px 12px #0000001a;transition:transform .3s ease,box-shadow .3s ease}::ng-deep .selected-features-wrapper img:hover{box-shadow:0 8px 20px #00000026}::ng-deep .selected-features-wrapper a{color:#89bbeb!important;font-weight:600;transition:all .3s ease;text-decoration:underline;position:relative;padding:2px 4px;border-radius:3px}::ng-deep .selected-features-wrapper a:hover{color:#0d78ca!important;background-color:transparent;text-decoration:underline;transform:translateY(-1px)}::ng-deep .selected-features-wrapper a:active{transform:translateY(0)}::ng-deep .selected-features-wrapper a:after{content:\"\";position:absolute;width:100%;height:1px;bottom:-1px;left:0;background-color:#0d78ca;transform:scaleX(0);transition:transform .3s ease}::ng-deep .selected-features-wrapper a:hover:after{transform:scaleX(1)}@media (max-width: 768px){.selected-features-wrapper{max-width:100%;border-radius:12px 12px 0 0}.selected-features-wrapper .feature-section{padding:16px 20px}.selected-features-wrapper .feature-section:after{left:20px;right:20px}.selected-features-wrapper .feature-section h4.collapsible-header{font-size:16px}.selected-features-wrapper .feature-section .feature-item>div{grid-template-columns:1fr;font-size:13px;gap:4px}.selected-features-wrapper .feature-section .feature-item>div:after{display:none}.selected-features-wrapper .feature-section .feature-item>div span,.selected-features-wrapper .feature-section .feature-item>div a{margin-left:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
3736
+ }
3737
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: SelectedFeatureInfoComponent, decorators: [{
3738
+ type: Component,
3739
+ args: [{ selector: 'selected-feature-info', imports: [CommonModule, FormsModule, AsyncPipe, JsonPipe, MatButtonModule, MatIconModule], template: "\n@if(features) {\n <div class=\"selected-features-wrapper\"> \n\n @for(item of features; track item.layerName) { \n <div class=\"feature-section\">\n <h4 (click)=\"toggleCollapse(item.layerName)\" class=\"collapsible-header\">\n <mat-icon class=\"collapse-icon\">\n {{ isCollapsed(item.layerName) ? 'keyboard_arrow_up' : 'expand_more' }}\n </mat-icon>\n {{item.layerName}}\n </h4>\n \n <div class=\"feature-content\" [class.collapsed]=\"isCollapsed(item.layerName)\">\n @for(feature of item.values; track feature) {\n <div class=\"feature-item\">\n <div *ngFor=\"let kv of (feature | keyvalue)\">\n {{ kv.value.name }}:\n <ng-container [ngSwitch]=\"kv.value.kind\">\n <img *ngSwitchCase=\"'img'\" [src]=\"kv.value.data\" alt=\"{{ kv.key }}\" style=\"max-width:240px;\" />\n <a *ngSwitchCase=\"'url'\" [href]=\"kv.value.data\" target=\"_blank\" rel=\"noopener noreferrer\">\n {{ kv.key }}\n <mat-icon class=\"link-icon\">open_in_new</mat-icon>\n </a>\n <span *ngSwitchDefault>{{ kv.value.data }}</span>\n </ng-container>\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n}\n", styles: ["::ng-deep .selected-features-wrapper{background:#292a2d;border-radius:5px;box-shadow:0 4px 6px #00000012,0 10px 20px #0000001a;max-width:420px;max-height:450px;overflow-y:auto;position:relative;font-family:Avenir Next W01,Lato,-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,.8)}::ng-deep .selected-features-wrapper::-webkit-scrollbar{width:12px}::ng-deep .selected-features-wrapper::-webkit-scrollbar-track{background:#757474;border-radius:8px}::ng-deep .selected-features-wrapper::-webkit-scrollbar-thumb{background:#1a1c1f;border-radius:8px;border:2px solid #2a2c30}::ng-deep .selected-features-wrapper::-webkit-scrollbar-thumb:hover{background:#0f1012}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button{width:12px;height:16px;background:#2a2c30;border:1px solid #1a1c1f}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:decrement{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 4l-4 4h8z'/%3E%3C/svg%3E\") no-repeat center;border-radius:8px 8px 0 0}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:decrement:hover{background-color:#1a1c1f}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:increment{background:#2a2c30 url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8l4-4H2z'/%3E%3C/svg%3E\") no-repeat center;border-radius:0 0 8px 8px}::ng-deep .selected-features-wrapper::-webkit-scrollbar-button:vertical:increment:hover{background-color:#1a1c1f}::ng-deep .selected-features-wrapper .feature-section{padding:20px 24px;position:relative}::ng-deep .selected-features-wrapper .feature-section:after{content:\"\";position:absolute;bottom:0;left:24px;right:24px;height:1px;background:linear-gradient(90deg,transparent 0%,rgba(0,0,0,.08) 20%,rgba(0,0,0,.08) 80%,transparent 100%)}::ng-deep .selected-features-wrapper .feature-section:last-child:after{display:none}::ng-deep .selected-features-wrapper .feature-section h4.collapsible-header{margin:0 0 16px;font-size:18px;font-weight:700;color:#bdc1c3cc;letter-spacing:-.3px;position:relative;cursor:pointer;-webkit-user-select:none;user-select:none;transition:color .2s ease;display:flex;align-items:center}::ng-deep .selected-features-wrapper .feature-section h4.collapsible-header:hover{color:#d4d8dacc}::ng-deep .selected-features-wrapper .feature-section h4.collapsible-header .collapse-icon{position:absolute;right:0;top:50%;transform:translateY(-50%);font-size:24px;width:24px;height:24px;transition:transform .3s ease;color:#fff}::ng-deep .selected-features-wrapper .feature-section .feature-content{max-height:2000px;overflow:hidden;transition:max-height .4s cubic-bezier(.4,0,.2,1),opacity .3s ease,margin-top .3s ease;opacity:1}::ng-deep .selected-features-wrapper .feature-section .feature-content.collapsed{max-height:0;opacity:0;margin-top:0}::ng-deep .selected-features-wrapper .feature-section .feature-item{margin-top:14px;background:#000;border-radius:10px;padding:16px;box-shadow:0 2px 4px #0000000a,0 1px 2px #0000000f;border:1px solid rgba(0,0,0,.06);transition:all .3s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden;color:#fff}::ng-deep .selected-features-wrapper .feature-section .feature-item:hover{box-shadow:0 4px 12px #2196f326,0 2px 4px #00000014;border-color:#2196f333}::ng-deep .selected-features-wrapper .feature-section .feature-item:hover:before{opacity:1}::ng-deep .selected-features-wrapper .feature-section .feature-item:first-of-type{margin-top:0}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of]{display:grid;grid-template-columns:auto 1fr;gap:8px;align-items:start;margin-bottom:12px;font-size:14px;line-height:1.6;padding:6px 0;color:#546e7a;font-weight:600;white-space:nowrap;display:flex;align-items:center}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of]:last-child{margin-bottom:0}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of]:after{content:\"\";display:inline-block;width:4px;height:4px;background:#2196f3;border-radius:50%;margin-left:6px;opacity:.5}::ng-deep .selected-features-wrapper div[ng-reflect-ng-for-of] span{color:#1a1a1a;font-weight:500;word-break:break-word;background:linear-gradient(135deg,#f5f5f5,#fafafa);padding:4px 10px;border-radius:6px;border:1px solid rgba(0,0,0,.05)}::ng-deep .selected-features-wrapper img{margin-top:8px;border-radius:8px;border:2px solid #e3f2fd;max-width:100%;max-height:200px;display:block;box-shadow:0 4px 12px #0000001a;transition:transform .3s ease,box-shadow .3s ease}::ng-deep .selected-features-wrapper img:hover{box-shadow:0 8px 20px #00000026}::ng-deep .selected-features-wrapper a{color:#89bbeb!important;font-weight:600;transition:all .3s ease;text-decoration:underline;position:relative;padding:2px 4px;border-radius:3px}::ng-deep .selected-features-wrapper a:hover{color:#0d78ca!important;background-color:transparent;text-decoration:underline;transform:translateY(-1px)}::ng-deep .selected-features-wrapper a:active{transform:translateY(0)}::ng-deep .selected-features-wrapper a:after{content:\"\";position:absolute;width:100%;height:1px;bottom:-1px;left:0;background-color:#0d78ca;transform:scaleX(0);transition:transform .3s ease}::ng-deep .selected-features-wrapper a:hover:after{transform:scaleX(1)}@media (max-width: 768px){.selected-features-wrapper{max-width:100%;border-radius:12px 12px 0 0}.selected-features-wrapper .feature-section{padding:16px 20px}.selected-features-wrapper .feature-section:after{left:20px;right:20px}.selected-features-wrapper .feature-section h4.collapsible-header{font-size:16px}.selected-features-wrapper .feature-section .feature-item>div{grid-template-columns:1fr;font-size:13px;gap:4px}.selected-features-wrapper .feature-section .feature-item>div:after{display:none}.selected-features-wrapper .feature-section .feature-item>div span,.selected-features-wrapper .feature-section .feature-item>div a{margin-left:0}}\n"] }]
3740
+ }] });
3741
+
3742
+ 'ol/format/geojson';
3743
+ class HoverInfoSearchService extends SearchProviderBase {
3744
+ _current = inject(CurrentItemsService);
3745
+ geojsonFormat = new GeoJSON;
3746
+ getSearchableLayers() {
3747
+ return this._current.profile.layerGroups.flatMap(l => l.layers).filter(l => l.showInfoOnHover);
3748
+ }
3749
+ getFilter(searchValue, layer) {
3750
+ const point = new Point(searchValue);
3751
+ return intersects(layer.geometryField, point);
3752
+ }
3753
+ map(l, f) {
3754
+ return {
3755
+ values: f.features.map(f => ({
3756
+ center: this._getCenterPoint(f.geometry),
3757
+ showValue: f.properties ? f.properties[l.propertyToShowOnHover] : ''
3758
+ }))
3759
+ };
3760
+ }
3761
+ _getCenterPoint(geometry) {
3762
+ const geom = this.geojsonFormat.readGeometry(geometry);
3763
+ const geomType = geom.getType();
3764
+ switch (geomType) {
3765
+ case 'Point':
3766
+ return geom;
3767
+ case 'LineString':
3768
+ const ls = geom;
3769
+ return new Point(ls.getCoordinateAt(0.5));
3770
+ case 'Polygon':
3771
+ const poly = geom;
3772
+ return poly.getInteriorPoint();
3773
+ // case 'MultiLineString':
3774
+ // case 'MultiPolygon':
3775
+ // case 'GeometryCollection':
3776
+ // case 'Circle':
3777
+ // return new Point([]);
3778
+ }
3779
+ return new Point([]); //
3780
+ }
3781
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HoverInfoSearchService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
3782
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HoverInfoSearchService });
3783
+ }
3784
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HoverInfoSearchService, decorators: [{
3785
+ type: Injectable
3786
+ }] });
3787
+
3788
+ class ShowInfoHoverService {
3789
+ _current = inject(CurrentItemsService);
3790
+ _search = inject(HoverInfoSearchService);
3791
+ source = new VectorSource();
3792
+ _layer = new VectorLayer({
3793
+ source: this.source,
3794
+ zIndex: 500,
3795
+ });
3796
+ init() {
3797
+ this._current.map$.subscribe({
3798
+ next: map => {
3799
+ let timer;
3800
+ map.addLayer(this._layer);
3801
+ map.on('pointermove', evt => {
3802
+ if (evt.dragging) {
3803
+ return;
3804
+ }
3805
+ clearTimeout(timer); // for every move, restart the timer
3806
+ timer = setTimeout(() => {
3807
+ this.source.clear();
3808
+ this._search.search(evt.coordinate, 2).subscribe({
3809
+ next: features => {
3810
+ const newFeatures = features
3811
+ .flatMap(f => f.values)
3812
+ .map(f => {
3813
+ const styleForFeature = this._getStyle(f.showValue);
3814
+ const newFeature = new Feature({ geometry: f.center });
3815
+ newFeature.setStyle(styleForFeature);
3816
+ return newFeature;
3817
+ });
3818
+ this.source.addFeatures(newFeatures);
3819
+ }
3820
+ });
3821
+ }, 300);
3822
+ });
3823
+ }
3824
+ });
3825
+ }
3826
+ _getStyle(text) {
3827
+ const style = new Style({
3828
+ text: new Text({
3829
+ text: text,
3830
+ font: '12px Calibri,sans-serif',
3831
+ fill: new Fill({ color: '#000' }),
3832
+ stroke: new Stroke({ color: '#fff', width: 3 }),
3833
+ overflow: true,
3834
+ placement: 'point'
3835
+ })
3836
+ });
3837
+ return style;
3838
+ }
3839
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ShowInfoHoverService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3840
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ShowInfoHoverService });
3841
+ }
3842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: ShowInfoHoverService, decorators: [{
3843
+ type: Injectable
2114
3844
  }] });
2115
3845
 
2116
3846
  class GisKomponentComponent {
2117
3847
  _profileService = inject(ProfileService);
2118
- // private readonly _undoRedo = inject(UndoRedoService);
2119
3848
  _http = inject(HttpClient);
2120
3849
  _layerHelper = inject(LayerHelperService);
2121
3850
  _layerErrorService = inject(LayerErrorService);
2122
3851
  _featureLoader = inject(FeatureLoaderService);
3852
+ _drawLayerService = inject(DrawLayerSourceService);
3853
+ _showHoverInfoService = inject(ShowInfoHoverService);
3854
+ _printHelper = inject(PrintHelperService);
2123
3855
  showToolbox = true;
2124
3856
  showActiveObjects = true;
2125
3857
  showLegendList = true;
@@ -2127,7 +3859,10 @@ class GisKomponentComponent {
2127
3859
  identifier;
2128
3860
  settings;
2129
3861
  toolbarRef;
2130
- activeObjectsRef;
3862
+ // @ViewChild('activeObjectsRef', {static: true }) activeObjectsRef!: ElementRef;
3863
+ selectedFeatureInfoRef;
3864
+ sessionDone = new EventEmitter();
3865
+ featuresChanged = new EventEmitter();
2131
3866
  map;
2132
3867
  toolbarCollapsed = false;
2133
3868
  layerPanelCollapsed = false;
@@ -2139,10 +3874,13 @@ class GisKomponentComponent {
2139
3874
  profiles = [];
2140
3875
  selectedProfile;
2141
3876
  profileShowToolbox = false;
2142
- _highlightService = inject(highlightService);
3877
+ _highlightService = inject(HighlightService);
2143
3878
  _currentItems = inject(CurrentItemsService);
2144
3879
  layers = [];
2145
- mousePositionCtrl = new ol_control_mouse({ projection: 'EPSG:25832' });
3880
+ mousePositionCtrl = new ol_control_mouse({
3881
+ projection: 'EPSG:25832',
3882
+ coordinateFormat: createStringXY(2)
3883
+ });
2146
3884
  onMoveEndCheckZoomFunction = () => { };
2147
3885
  currentZoomLevel = 0;
2148
3886
  scaleCtrl = new ol_control_scale({
@@ -2152,6 +3890,9 @@ class GisKomponentComponent {
2152
3890
  text: true,
2153
3891
  minWidth: 120
2154
3892
  });
3893
+ constructor() {
3894
+ this._currentItems.gisKomponentSettings = this.settings;
3895
+ }
2155
3896
  profileSelected(profileIdentifier) {
2156
3897
  this._profileService.getByIdentifier(profileIdentifier).subscribe({
2157
3898
  next: profile => {
@@ -2173,11 +3914,11 @@ class GisKomponentComponent {
2173
3914
  layers: lg.layers
2174
3915
  .filter(layer => layer.activeInSelector)
2175
3916
  .sort((a, b) => a.sortOrder - b.sortOrder)
2176
- .map((l, index) => this._mapLayer(l, capabilityObject, 'EPSG:25832', lg.id, index)) // for now, we default to EPSG:25832.
3917
+ .map((l, index) => this._mapLayer(l, capabilityObject, 'EPSG:25832', lg.id, index)), // for now, we default to EPSG:25832.
3918
+ properties: { id: lg.id }
2177
3919
  }))
2178
3920
  ];
2179
3921
  this.map.setLayers(layers);
2180
- this.map.addLayer(this._highlightService.highlightLayer);
2181
3922
  if (this.settings) {
2182
3923
  this._featureLoader.loadFeaturesSettings(this.settings);
2183
3924
  }
@@ -2192,11 +3933,18 @@ class GisKomponentComponent {
2192
3933
  this.profileShowToolbox = true;
2193
3934
  this.map.addControl(toolbarControl);
2194
3935
  }
3936
+ this._currentItems.gisKomponentSettings = this.settings;
2195
3937
  this.activeObjectsReady = true;
2196
- const activeObjectsControl = new Control({
2197
- element: this.activeObjectsRef.nativeElement
3938
+ // if (this.activeObjectsRef) {
3939
+ // const activeObjectsControl = new Control({
3940
+ // element: this.activeObjectsRef.nativeElement
3941
+ // });
3942
+ // this.map.addControl(activeObjectsControl);
3943
+ // }
3944
+ const selectedFeatureInfoRefControl = new Control({
3945
+ element: this.selectedFeatureInfoRef.nativeElement
2198
3946
  });
2199
- this.map.addControl(activeObjectsControl);
3947
+ this.map.addControl(selectedFeatureInfoRefControl);
2200
3948
  this._handleShowMouseCoordinates(profile.showCoordinates);
2201
3949
  this._handleShowScale(profile.showScale);
2202
3950
  this._setLogo(profile.logoUrl);
@@ -2205,11 +3953,33 @@ class GisKomponentComponent {
2205
3953
  this.onMoveEndCheckZoomFunction = () => {
2206
3954
  this._checkZoom(this.map);
2207
3955
  };
3956
+ this._drawLayerService.features$.subscribe({
3957
+ next: features => {
3958
+ const geojson = new GeoJSON$1();
3959
+ this.featuresChanged.emit(geojson.writeFeaturesObject(features));
3960
+ }
3961
+ });
2208
3962
  this.map.on('moveend', this.onMoveEndCheckZoomFunction);
2209
- this._currentItems.map = this.map;
3963
+ this._showHoverInfoService.init();
2210
3964
  }
2211
3965
  });
2212
3966
  }
3967
+ sessionDoneFromActiveObject() {
3968
+ const formatter = new GeoJSON$1();
3969
+ const sessionDone = {
3970
+ doneFrom: 'activeObjects',
3971
+ features: formatter.writeFeaturesObject(this._drawLayerService.allCleanedFeatures)
3972
+ };
3973
+ if (this._currentItems.gisKomponentSettings.printMapSettings && this._currentItems.gisKomponentSettings.printMapSettings.doPrintWhenSessionIsClosed) {
3974
+ this._printHelper.getImgData(this.map, this._currentItems.gisKomponentSettings.printMapSettings.printFormat, this._currentItems.gisKomponentSettings.printMapSettings.printDimensions).then(dataUrl => {
3975
+ sessionDone.imgData = dataUrl;
3976
+ this.sessionDone.emit(sessionDone);
3977
+ });
3978
+ }
3979
+ else {
3980
+ this.sessionDone.emit(sessionDone);
3981
+ }
3982
+ }
2213
3983
  _checkZoom(map) {
2214
3984
  this.currentZoomLevel = map.getView().getZoom() ?? this.currentZoomLevel;
2215
3985
  }
@@ -2326,7 +4096,26 @@ class GisKomponentComponent {
2326
4096
  this.map = new Map$1({
2327
4097
  target: 'map',
2328
4098
  });
4099
+ // this._currentItems.gisKomponentSettings = this.settings!;
2329
4100
  this.map.addControl(new Rotate({ autoHide: false }));
4101
+ this._addRotateControl();
4102
+ }
4103
+ // Added custom north-arrow with a compass one
4104
+ _addRotateControl() {
4105
+ const iconElement = document.createElement('span');
4106
+ iconElement.innerHTML = `
4107
+ <svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
4108
+ <path d="M12 3 L16 10 L12 8 L8 10 Z" fill="currentColor"/>
4109
+ <path d="M12 21 L8 14 L12 16 L16 14 Z" fill="currentColor"/>
4110
+ <circle cx="12" cy="12" r="1.5" fill="currentColor"/>
4111
+ </svg>
4112
+ `;
4113
+ const rotateControl = new Rotate({
4114
+ label: iconElement,
4115
+ tipLabel: 'Reset to north',
4116
+ autoHide: false
4117
+ });
4118
+ this.map.addControl(rotateControl);
2330
4119
  }
2331
4120
  toggleShowConflicts() {
2332
4121
  this.showConflicts = !this.showConflicts;
@@ -2344,12 +4133,14 @@ class GisKomponentComponent {
2344
4133
  this.activeObjectsCollapsed = !this.activeObjectsCollapsed;
2345
4134
  }
2346
4135
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GisKomponentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2347
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: GisKomponentComponent, isStandalone: true, selector: "gis-komponent", inputs: { showToolbox: "showToolbox", showActiveObjects: "showActiveObjects", showLegendList: "showLegendList", showMapSearch: "showMapSearch", identifier: "identifier", settings: "settings" }, providers: [LayerHelperService], viewQueries: [{ propertyName: "toolbarRef", first: true, predicate: ["toolbarRef"], descendants: true, static: true }, { propertyName: "activeObjectsRef", first: true, predicate: ["activeObjectsRef"], descendants: true, static: true }], ngImport: i0, template: "@if (selectedProfile && selectedProfile.showLayerSelector) {\n <lib-layer-selector [map]=\"map\" [profile]=\"selectedProfile\" [currentZoomLevel]=\"currentZoomLevel\"></lib-layer-selector>\n}\n@if (selectedProfile && selectedProfile.showLegends) {\n <lib-legends-list [map]=\"map\" [profile]=\"selectedProfile\"></lib-legends-list>\n}\n\n<!-- <span>Zoom: {{ currentZoomLevel}}</span> -->\n<div #activeObjectsRef>\n @if(showActiveObjects && activeObjectsReady && settings) {\n <activeObjects [settings]=\"settings\"></activeObjects>\n }\n</div>\n<div #toolbarRef class=\"map-toolbar-container\">\n @if(showToolbox && profileShowToolbox && settings) {\n <map-toolbox [map]=\"map\" \n [profile]=\"selectedProfile\"\n [settings]=\"settings\"\n [collapsed]=\"selectedProfile.toolbarCollapsed\" [WKTInputEnabled]=\"settings?.WKTInputEnabled\" [deleteEnabled]=\"settings?.deleteEnabled\" [showMeasureArea]=\"selectedProfile.showAreaMeasurement\" [showMeasureDistance]=\"selectedProfile.showDistanceMeasurement\"></map-toolbox>\n }\n</div>\n\n<div class=\"map-container\">\n <lib-map-search *ngIf=\"showMapSearch\"></lib-map-search>\n\n <!-- Objektvisning -->\n <!-- @if (showObjects) {\n <div class=\"object-panel\" [class.collapsed]=\"activeObjectsCollapsed\">\n <div class=\"header\" (click)=\"toggleActiveObjectsCollapsed()\">\n <span>Aktive objekter ({{activeObjects.length}})</span>\n <mat-icon>{{ activeObjectsCollapsed ? 'expand_more' : 'expand_less' }}</mat-icon>\n </div>\n @if (!activeObjectsCollapsed){\n <ul>\n <li *ngFor=\"let obj of activeObjects\">{{ obj.name }} ({{obj.size}})</li>\n </ul>\n }\n </div>\n } -->\n\n <!-- Konflikter -->\n @if (showConflicts) {\n\n <div class=\"conflict-panel\" [class.collapsed]=\"conflictsCollapsed\">\n <div class=\"header\">\n <span>Konflikter (3)</span>\n <mat-icon>{{ activeObjectsCollapsed ? 'expand_more' : 'expand_less' }}</mat-icon> -->\n </div>\n <mat-list>\n <mat-list-item>\n <span matListItemTitle>Lag #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #2 (svarer ikke)</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #3</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #2</span>\n </mat-list-item>\n </mat-list>\n </div>\n \n } \n\n <!-- Kort -->\n <div id=\"map\" class=\"map\"></div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;overflow:hidden}::ng-deep .lib-error-snackbar{background-color:#d32f2f;color:#fff;font-weight:500}::ng-deep .map-container{position:relative;height:81vh;width:100%;overflow:hidden}::ng-deep #map{width:100%;height:100%;position:absolute;inset:0;overflow:hidden}::ng-deep .ol-viewport{overflow:hidden!important}::ng-deep ::ng-deep .ol-logo{position:absolute;left:auto;right:3em;top:6.25em}::ng-deep ::ng-deep .ol-copyright{background-color:transparent;position:absolute;bottom:10px;width:100%;text-align:center}::ng-deep ::ng-deep .toolbar{position:absolute;top:10px;left:10px;background:#fff;padding:4px;border-radius:4px;display:flex;flex-direction:column;transition:width .3s;z-index:1000;overflow:hidden}::ng-deep ::ng-deep .toolbar.collapsed{width:40px;overflow:hidden}::ng-deep .object-panel{position:absolute;bottom:10px;left:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .object-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep .conflict-panel{position:absolute;bottom:10px;right:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .conflict-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep ::ng-deep .ol-zoom.ol-unselectable.ol-control{display:flex;flex-direction:column;position:absolute;top:2rem;left:1rem}::ng-deep ::ng-deep button.ol-zoom-in{font-size:2em;width:40px;height:40px;background-color:color-mix(in srgb,#000 60%,transparent);border:none;box-shadow:none;cursor:pointer;color:#fff}::ng-deep ::ng-deep button.ol-zoom-in:hover{color:#e9e3e3;outline:none}::ng-deep button.ol-zoom-out{font-size:2em;width:40px;height:40px;background-color:color-mix(in srgb,#000 60%,transparent);border:none;box-shadow:none;cursor:pointer;color:#fff}::ng-deep button.ol-zoom-out:hover{color:#e9e3e3;outline:none}::ng-deep button.ol-rotate-reset{font-size:2em;width:40px;height:40px;background-color:color-mix(in srgb,#000 60%,transparent);border:none;box-shadow:none;cursor:pointer;color:#fff}::ng-deep button.ol-rotate-reset:hover{color:#e9e3e3;outline:none}::ng-deep .ol-scale-bar.ol-unselectable{position:absolute;bottom:1rem;left:1rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3$1.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i3$1.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i3$1.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i3$1.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: LayerSelectorComponent, selector: "lib-layer-selector", inputs: ["map", "profile", "currentZoomLevel"] }, { kind: "component", type: MapSearchComponent, selector: "lib-map-search" }, { kind: "component", type: ToolboxComponent, selector: "map-toolbox", inputs: ["map", "showMeasureDistance", "showMeasureArea", "collapsed", "settings", "profile", "WKTInputEnabled", "deleteEnabled"] }, { kind: "component", type: ActiveObjectsComponent, selector: "activeObjects", inputs: ["settings"] }, { kind: "component", type: LegendsListComponent, selector: "lib-legends-list", inputs: ["map", "profile"] }] });
4136
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: GisKomponentComponent, isStandalone: true, selector: "gis-komponent", inputs: { showToolbox: "showToolbox", showActiveObjects: "showActiveObjects", showLegendList: "showLegendList", showMapSearch: "showMapSearch", identifier: "identifier", settings: "settings" }, outputs: { sessionDone: "sessionDone", featuresChanged: "featuresChanged" }, providers: [CurrentItemsService, FeatureLoaderService, DrawLayerSourceService, ZoomService, UndoRedoService, ShowInfoHoverService, HoverInfoSearchService, CenterPointService, PrintDrawLayerSourceService,
4137
+ HighlightService, ShowInfoService, InfoSearchProvider, InteractionHelperService, ConflictAnalysisSearchProvider, MergeFeaturesService, OverlapService], viewQueries: [{ propertyName: "toolbarRef", first: true, predicate: ["toolbarRef"], descendants: true, static: true }, { propertyName: "selectedFeatureInfoRef", first: true, predicate: ["selectedFeatureInfoRef"], descendants: true, static: true }], ngImport: i0, template: "@if (selectedProfile && selectedProfile.showLayerSelector) {\n <lib-layer-selector [map]=\"map\" [profile]=\"selectedProfile\" [currentZoomLevel]=\"currentZoomLevel\"></lib-layer-selector>\n}\n@if (selectedProfile && selectedProfile.showLegends) {\n <lib-legends-list [map]=\"map\" [profile]=\"selectedProfile\"></lib-legends-list>\n}\n\n<div #selectedFeatureInfoRef>\n <selected-feature-info></selected-feature-info>\n</div>\n\n@if(showActiveObjects && activeObjectsReady && settings && selectedProfile) {\n <activeObjects [settings]=\"settings\" [profile]=\"selectedProfile\" (sessionDone)=\"sessionDoneFromActiveObject()\"></activeObjects>\n}\n<div #toolbarRef class=\"map-toolbar-container\">\n @if(showToolbox && profileShowToolbox && settings) {\n <map-toolbox [map]=\"map\" \n [profile]=\"selectedProfile\"\n [settings]=\"settings\"\n [collapsed]=\"selectedProfile.toolbarCollapsed\" [WKTInputEnabled]=\"settings?.WKTInputEnabled\" [deleteEnabled]=\"settings?.deleteEnabled\" [showMeasureArea]=\"selectedProfile.showAreaMeasurement\" [showMeasureDistance]=\"selectedProfile.showDistanceMeasurement\"></map-toolbox>\n }\n</div>\n\n<div class=\"map-container\">\n <lib-map-search *ngIf=\"showMapSearch\" [profile]=\"selectedProfile\"></lib-map-search>\n\n\n <!-- Konflikter -->\n @if (showConflicts) {\n\n <div class=\"conflict-panel\" [class.collapsed]=\"conflictsCollapsed\">\n <div class=\"header\">\n <span>Konflikter (3)</span>\n <mat-icon>{{ activeObjectsCollapsed ? 'expand_more' : 'expand_less' }}</mat-icon> -->\n </div>\n <mat-list>\n <mat-list-item>\n <span matListItemTitle>Lag #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #2 (svarer ikke)</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #3</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #2</span>\n </mat-list-item>\n </mat-list>\n </div>\n \n } \n\n <!-- Kort -->\n <div id=\"map\" class=\"map\"></div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;overflow:hidden}::ng-deep .lib-error-snackbar{background-color:#d32f2f;color:#fff;font-weight:500}::ng-deep .map-container{position:relative;height:81vh;width:100%;overflow:hidden}::ng-deep #map{width:100%;height:100%;position:absolute;inset:0;overflow:hidden}::ng-deep .ol-viewport{overflow:hidden!important}::ng-deep ::ng-deep .ol-logo{position:absolute;left:auto;right:3em;top:6.25em}::ng-deep ::ng-deep .ol-copyright{background-color:transparent;position:absolute;bottom:10px;width:100%;text-align:center}::ng-deep ::ng-deep .toolbar{position:absolute;top:10px;left:10px;background:#fff;padding:4px;border-radius:4px;display:flex;flex-direction:column;transition:width .3s;z-index:1000;overflow:hidden}::ng-deep ::ng-deep .toolbar.collapsed{width:40px;overflow:hidden}::ng-deep .object-panel{position:absolute;bottom:10px;left:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .object-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep .conflict-panel{position:absolute;bottom:10px;right:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .conflict-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep ::ng-deep .ol-zoom.ol-unselectable.ol-control{display:flex;flex-direction:column;position:absolute;left:1rem}::ng-deep ::ng-deep button.ol-zoom-in{font-size:2em;width:40px;height:40px;background-color:#292a2d;border:none;box-shadow:none;cursor:pointer;border-radius:5px 5px 0 0!important;color:#fff}::ng-deep ::ng-deep button.ol-zoom-in:hover{color:#e9e3e3;outline:none}::ng-deep ::ng-deep .ol-scale-text{display:flex}::ng-deep button.ol-zoom-out{font-size:2em;width:40px;height:40px;background-color:#292a2d;border:none;box-shadow:none;cursor:pointer;color:#fff;border-radius:0 0 5px 5px!important}::ng-deep button.ol-zoom-out:hover{color:#e9e3e3;outline:none}::ng-deep button.ol-rotate-reset{font-size:2em;width:40px;height:40px;background-color:#292a2d;border:none;box-shadow:none;cursor:pointer;color:#fff;border-radius:5px}::ng-deep button.ol-rotate-reset:hover{color:#e9e3e3;outline:none}::ng-deep .ol-scale-bar.ol-unselectable{position:absolute;bottom:3rem;left:1rem}::ng-deep .ol-mouse-position{top:44em;left:12px;position:absolute;background:color-mix(in srgb,#000 21%,transparent);color:#fffcfc;width:189px;height:30px;padding:2px;border-radius:5px;font-family:Avenir Next W01,Lato,-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;text-align:center;display:flex;align-items:center;justify-content:flex-start}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatListModule }, { kind: "component", type: i3$1.MatList, selector: "mat-list", exportAs: ["matList"] }, { kind: "component", type: i3$1.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i3$1.MatListItemLine, selector: "[matListItemLine]" }, { kind: "directive", type: i3$1.MatListItemTitle, selector: "[matListItemTitle]" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: LayerSelectorComponent, selector: "lib-layer-selector", inputs: ["map", "profile", "currentZoomLevel"] }, { kind: "component", type: MapSearchComponent, selector: "lib-map-search", inputs: ["profile"] }, { kind: "component", type: ToolboxComponent, selector: "map-toolbox", inputs: ["map", "showMeasureDistance", "showMeasureArea", "collapsed", "settings", "profile", "WKTInputEnabled", "deleteEnabled"] }, { kind: "component", type: ActiveObjectsComponent, selector: "activeObjects", inputs: ["settings", "profile"], outputs: ["sessionDone"] }, { kind: "component", type: LegendsListComponent, selector: "lib-legends-list", inputs: ["map", "profile"] }, { kind: "component", type: SelectedFeatureInfoComponent, selector: "selected-feature-info" }] });
2348
4138
  }
2349
4139
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GisKomponentComponent, decorators: [{
2350
4140
  type: Component,
2351
- args: [{ selector: 'gis-komponent', imports: [CommonModule, MatIconModule, MatListModule, MatSelectModule, LayerSelectorComponent, MapSearchComponent, ToolboxComponent, ActiveObjectsComponent, LegendsListComponent], providers: [LayerHelperService], template: "@if (selectedProfile && selectedProfile.showLayerSelector) {\n <lib-layer-selector [map]=\"map\" [profile]=\"selectedProfile\" [currentZoomLevel]=\"currentZoomLevel\"></lib-layer-selector>\n}\n@if (selectedProfile && selectedProfile.showLegends) {\n <lib-legends-list [map]=\"map\" [profile]=\"selectedProfile\"></lib-legends-list>\n}\n\n<!-- <span>Zoom: {{ currentZoomLevel}}</span> -->\n<div #activeObjectsRef>\n @if(showActiveObjects && activeObjectsReady && settings) {\n <activeObjects [settings]=\"settings\"></activeObjects>\n }\n</div>\n<div #toolbarRef class=\"map-toolbar-container\">\n @if(showToolbox && profileShowToolbox && settings) {\n <map-toolbox [map]=\"map\" \n [profile]=\"selectedProfile\"\n [settings]=\"settings\"\n [collapsed]=\"selectedProfile.toolbarCollapsed\" [WKTInputEnabled]=\"settings?.WKTInputEnabled\" [deleteEnabled]=\"settings?.deleteEnabled\" [showMeasureArea]=\"selectedProfile.showAreaMeasurement\" [showMeasureDistance]=\"selectedProfile.showDistanceMeasurement\"></map-toolbox>\n }\n</div>\n\n<div class=\"map-container\">\n <lib-map-search *ngIf=\"showMapSearch\"></lib-map-search>\n\n <!-- Objektvisning -->\n <!-- @if (showObjects) {\n <div class=\"object-panel\" [class.collapsed]=\"activeObjectsCollapsed\">\n <div class=\"header\" (click)=\"toggleActiveObjectsCollapsed()\">\n <span>Aktive objekter ({{activeObjects.length}})</span>\n <mat-icon>{{ activeObjectsCollapsed ? 'expand_more' : 'expand_less' }}</mat-icon>\n </div>\n @if (!activeObjectsCollapsed){\n <ul>\n <li *ngFor=\"let obj of activeObjects\">{{ obj.name }} ({{obj.size}})</li>\n </ul>\n }\n </div>\n } -->\n\n <!-- Konflikter -->\n @if (showConflicts) {\n\n <div class=\"conflict-panel\" [class.collapsed]=\"conflictsCollapsed\">\n <div class=\"header\">\n <span>Konflikter (3)</span>\n <mat-icon>{{ activeObjectsCollapsed ? 'expand_more' : 'expand_less' }}</mat-icon> -->\n </div>\n <mat-list>\n <mat-list-item>\n <span matListItemTitle>Lag #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #2 (svarer ikke)</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #3</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #2</span>\n </mat-list-item>\n </mat-list>\n </div>\n \n } \n\n <!-- Kort -->\n <div id=\"map\" class=\"map\"></div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;overflow:hidden}::ng-deep .lib-error-snackbar{background-color:#d32f2f;color:#fff;font-weight:500}::ng-deep .map-container{position:relative;height:81vh;width:100%;overflow:hidden}::ng-deep #map{width:100%;height:100%;position:absolute;inset:0;overflow:hidden}::ng-deep .ol-viewport{overflow:hidden!important}::ng-deep ::ng-deep .ol-logo{position:absolute;left:auto;right:3em;top:6.25em}::ng-deep ::ng-deep .ol-copyright{background-color:transparent;position:absolute;bottom:10px;width:100%;text-align:center}::ng-deep ::ng-deep .toolbar{position:absolute;top:10px;left:10px;background:#fff;padding:4px;border-radius:4px;display:flex;flex-direction:column;transition:width .3s;z-index:1000;overflow:hidden}::ng-deep ::ng-deep .toolbar.collapsed{width:40px;overflow:hidden}::ng-deep .object-panel{position:absolute;bottom:10px;left:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .object-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep .conflict-panel{position:absolute;bottom:10px;right:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .conflict-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep ::ng-deep .ol-zoom.ol-unselectable.ol-control{display:flex;flex-direction:column;position:absolute;top:2rem;left:1rem}::ng-deep ::ng-deep button.ol-zoom-in{font-size:2em;width:40px;height:40px;background-color:color-mix(in srgb,#000 60%,transparent);border:none;box-shadow:none;cursor:pointer;color:#fff}::ng-deep ::ng-deep button.ol-zoom-in:hover{color:#e9e3e3;outline:none}::ng-deep button.ol-zoom-out{font-size:2em;width:40px;height:40px;background-color:color-mix(in srgb,#000 60%,transparent);border:none;box-shadow:none;cursor:pointer;color:#fff}::ng-deep button.ol-zoom-out:hover{color:#e9e3e3;outline:none}::ng-deep button.ol-rotate-reset{font-size:2em;width:40px;height:40px;background-color:color-mix(in srgb,#000 60%,transparent);border:none;box-shadow:none;cursor:pointer;color:#fff}::ng-deep button.ol-rotate-reset:hover{color:#e9e3e3;outline:none}::ng-deep .ol-scale-bar.ol-unselectable{position:absolute;bottom:1rem;left:1rem}\n"] }]
2352
- }], propDecorators: { showToolbox: [{
4141
+ args: [{ selector: 'gis-komponent', imports: [CommonModule, MatIconModule, MatListModule, MatSelectModule, LayerSelectorComponent, MapSearchComponent, ToolboxComponent, ActiveObjectsComponent, LegendsListComponent, SelectedFeatureInfoComponent], providers: [CurrentItemsService, FeatureLoaderService, DrawLayerSourceService, ZoomService, UndoRedoService, ShowInfoHoverService, HoverInfoSearchService, CenterPointService, PrintDrawLayerSourceService,
4142
+ HighlightService, ShowInfoService, InfoSearchProvider, InteractionHelperService, ConflictAnalysisSearchProvider, MergeFeaturesService, OverlapService], template: "@if (selectedProfile && selectedProfile.showLayerSelector) {\n <lib-layer-selector [map]=\"map\" [profile]=\"selectedProfile\" [currentZoomLevel]=\"currentZoomLevel\"></lib-layer-selector>\n}\n@if (selectedProfile && selectedProfile.showLegends) {\n <lib-legends-list [map]=\"map\" [profile]=\"selectedProfile\"></lib-legends-list>\n}\n\n<div #selectedFeatureInfoRef>\n <selected-feature-info></selected-feature-info>\n</div>\n\n@if(showActiveObjects && activeObjectsReady && settings && selectedProfile) {\n <activeObjects [settings]=\"settings\" [profile]=\"selectedProfile\" (sessionDone)=\"sessionDoneFromActiveObject()\"></activeObjects>\n}\n<div #toolbarRef class=\"map-toolbar-container\">\n @if(showToolbox && profileShowToolbox && settings) {\n <map-toolbox [map]=\"map\" \n [profile]=\"selectedProfile\"\n [settings]=\"settings\"\n [collapsed]=\"selectedProfile.toolbarCollapsed\" [WKTInputEnabled]=\"settings?.WKTInputEnabled\" [deleteEnabled]=\"settings?.deleteEnabled\" [showMeasureArea]=\"selectedProfile.showAreaMeasurement\" [showMeasureDistance]=\"selectedProfile.showDistanceMeasurement\"></map-toolbox>\n }\n</div>\n\n<div class=\"map-container\">\n <lib-map-search *ngIf=\"showMapSearch\" [profile]=\"selectedProfile\"></lib-map-search>\n\n\n <!-- Konflikter -->\n @if (showConflicts) {\n\n <div class=\"conflict-panel\" [class.collapsed]=\"conflictsCollapsed\">\n <div class=\"header\">\n <span>Konflikter (3)</span>\n <mat-icon>{{ activeObjectsCollapsed ? 'expand_more' : 'expand_less' }}</mat-icon> -->\n </div>\n <mat-list>\n <mat-list-item>\n <span matListItemTitle>Lag #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #2 (svarer ikke)</span>\n </mat-list-item>\n <mat-list-item>\n <span matListItemTitle>Lag #3</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #1</span>\n <span matListItemLine><mat-icon>highlight</mat-icon> Objekt #2</span>\n </mat-list-item>\n </mat-list>\n </div>\n \n } \n\n <!-- Kort -->\n <div id=\"map\" class=\"map\"></div>\n</div>\n", styles: [":host{display:block;width:100%;height:100%;overflow:hidden}::ng-deep .lib-error-snackbar{background-color:#d32f2f;color:#fff;font-weight:500}::ng-deep .map-container{position:relative;height:81vh;width:100%;overflow:hidden}::ng-deep #map{width:100%;height:100%;position:absolute;inset:0;overflow:hidden}::ng-deep .ol-viewport{overflow:hidden!important}::ng-deep ::ng-deep .ol-logo{position:absolute;left:auto;right:3em;top:6.25em}::ng-deep ::ng-deep .ol-copyright{background-color:transparent;position:absolute;bottom:10px;width:100%;text-align:center}::ng-deep ::ng-deep .toolbar{position:absolute;top:10px;left:10px;background:#fff;padding:4px;border-radius:4px;display:flex;flex-direction:column;transition:width .3s;z-index:1000;overflow:hidden}::ng-deep ::ng-deep .toolbar.collapsed{width:40px;overflow:hidden}::ng-deep .object-panel{position:absolute;bottom:10px;left:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .object-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep .conflict-panel{position:absolute;bottom:10px;right:10px;background:#fff;padding:8px;border-radius:4px;z-index:1000;max-height:calc(85vh - 20px);overflow-y:auto}::ng-deep .conflict-panel .header{display:flex;justify-content:space-between;padding:8px;cursor:pointer;background:#f0f0f0}::ng-deep ::ng-deep .ol-zoom.ol-unselectable.ol-control{display:flex;flex-direction:column;position:absolute;left:1rem}::ng-deep ::ng-deep button.ol-zoom-in{font-size:2em;width:40px;height:40px;background-color:#292a2d;border:none;box-shadow:none;cursor:pointer;border-radius:5px 5px 0 0!important;color:#fff}::ng-deep ::ng-deep button.ol-zoom-in:hover{color:#e9e3e3;outline:none}::ng-deep ::ng-deep .ol-scale-text{display:flex}::ng-deep button.ol-zoom-out{font-size:2em;width:40px;height:40px;background-color:#292a2d;border:none;box-shadow:none;cursor:pointer;color:#fff;border-radius:0 0 5px 5px!important}::ng-deep button.ol-zoom-out:hover{color:#e9e3e3;outline:none}::ng-deep button.ol-rotate-reset{font-size:2em;width:40px;height:40px;background-color:#292a2d;border:none;box-shadow:none;cursor:pointer;color:#fff;border-radius:5px}::ng-deep button.ol-rotate-reset:hover{color:#e9e3e3;outline:none}::ng-deep .ol-scale-bar.ol-unselectable{position:absolute;bottom:3rem;left:1rem}::ng-deep .ol-mouse-position{top:44em;left:12px;position:absolute;background:color-mix(in srgb,#000 21%,transparent);color:#fffcfc;width:189px;height:30px;padding:2px;border-radius:5px;font-family:Avenir Next W01,Lato,-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;text-align:center;display:flex;align-items:center;justify-content:flex-start}\n"] }]
4143
+ }], ctorParameters: () => [], propDecorators: { showToolbox: [{
2353
4144
  type: Input
2354
4145
  }], showActiveObjects: [{
2355
4146
  type: Input
@@ -2365,9 +4156,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
2365
4156
  }], toolbarRef: [{
2366
4157
  type: ViewChild,
2367
4158
  args: ['toolbarRef', { static: true }]
2368
- }], activeObjectsRef: [{
4159
+ }], selectedFeatureInfoRef: [{
2369
4160
  type: ViewChild,
2370
- args: ['activeObjectsRef', { static: true }]
4161
+ args: ['selectedFeatureInfoRef', { static: true }]
4162
+ }], sessionDone: [{
4163
+ type: Output
4164
+ }], featuresChanged: [{
4165
+ type: Output
2371
4166
  }] } });
2372
4167
  class CapabilitiesItem {
2373
4168
  url = '';