@bluehalo/ngx-leaflet 18.0.2

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 (34) hide show
  1. package/CHANGES.md +89 -0
  2. package/LICENSE +22 -0
  3. package/README.md +728 -0
  4. package/esm2022/bluehalo-ngx-leaflet.mjs +5 -0
  5. package/esm2022/lib/core/leaflet.directive.mjs +330 -0
  6. package/esm2022/lib/core/leaflet.directive.wrapper.mjs +12 -0
  7. package/esm2022/lib/core/leaflet.util.mjs +20 -0
  8. package/esm2022/lib/layers/base/leaflet-baselayers.directive.mjs +113 -0
  9. package/esm2022/lib/layers/control/leaflet-control-layers-changes.model.mjs +11 -0
  10. package/esm2022/lib/layers/control/leaflet-control-layers-config.model.mjs +7 -0
  11. package/esm2022/lib/layers/control/leaflet-control-layers.directive.mjs +100 -0
  12. package/esm2022/lib/layers/control/leaflet-control-layers.wrapper.mjs +58 -0
  13. package/esm2022/lib/layers/leaflet-layer.directive.mjs +78 -0
  14. package/esm2022/lib/layers/leaflet-layers.directive.mjs +83 -0
  15. package/esm2022/lib/layers/leaflet-tile-layer-definition.model.mjs +53 -0
  16. package/esm2022/lib/leaflet.module.mjs +40 -0
  17. package/esm2022/public-api.mjs +13 -0
  18. package/fesm2022/bluehalo-ngx-leaflet.mjs +880 -0
  19. package/fesm2022/bluehalo-ngx-leaflet.mjs.map +1 -0
  20. package/index.d.ts +5 -0
  21. package/lib/core/leaflet.directive.d.ts +94 -0
  22. package/lib/core/leaflet.directive.wrapper.d.ts +8 -0
  23. package/lib/core/leaflet.util.d.ts +7 -0
  24. package/lib/layers/base/leaflet-baselayers.directive.d.ts +45 -0
  25. package/lib/layers/control/leaflet-control-layers-changes.model.d.ts +6 -0
  26. package/lib/layers/control/leaflet-control-layers-config.model.d.ts +9 -0
  27. package/lib/layers/control/leaflet-control-layers.directive.d.ts +35 -0
  28. package/lib/layers/control/leaflet-control-layers.wrapper.d.ts +14 -0
  29. package/lib/layers/leaflet-layer.directive.d.ts +30 -0
  30. package/lib/layers/leaflet-layers.directive.d.ts +41 -0
  31. package/lib/layers/leaflet-tile-layer-definition.model.d.ts +33 -0
  32. package/lib/leaflet.module.d.ts +11 -0
  33. package/package.json +34 -0
  34. package/public-api.d.ts +12 -0
@@ -0,0 +1,880 @@
1
+ import * as i0 from '@angular/core';
2
+ import { EventEmitter, Directive, Input, Output, HostListener, NgModule } from '@angular/core';
3
+ import { latLng, map, control, tileLayer } from 'leaflet';
4
+
5
+ class LeafletUtil {
6
+ static mapToArray(map) {
7
+ const toReturn = [];
8
+ for (const k in map) {
9
+ if (map.hasOwnProperty(k)) {
10
+ toReturn.push(map[k]);
11
+ }
12
+ }
13
+ return toReturn;
14
+ }
15
+ static handleEvent(zone, eventEmitter, event) {
16
+ // Don't want to emit if there are no observers
17
+ if (0 < eventEmitter.observers.length) {
18
+ zone.run(() => {
19
+ eventEmitter.emit(event);
20
+ });
21
+ }
22
+ }
23
+ }
24
+
25
+ class LeafletDirective {
26
+ constructor(element, zone) {
27
+ this.element = element;
28
+ this.zone = zone;
29
+ this.DEFAULT_ZOOM = 1;
30
+ this.DEFAULT_CENTER = latLng(38.907192, -77.036871);
31
+ this.DEFAULT_FPZ_OPTIONS = {};
32
+ this.fitBoundsOptions = this.DEFAULT_FPZ_OPTIONS;
33
+ this.panOptions = this.DEFAULT_FPZ_OPTIONS;
34
+ this.zoomOptions = this.DEFAULT_FPZ_OPTIONS;
35
+ this.zoomPanOptions = this.DEFAULT_FPZ_OPTIONS;
36
+ // Default configuration
37
+ this.options = {};
38
+ // Configure callback function for the map
39
+ this.mapReady = new EventEmitter();
40
+ this.zoomChange = new EventEmitter();
41
+ this.centerChange = new EventEmitter();
42
+ // Mouse Map Events
43
+ this.onClick = new EventEmitter();
44
+ this.onDoubleClick = new EventEmitter();
45
+ this.onMouseDown = new EventEmitter();
46
+ this.onMouseUp = new EventEmitter();
47
+ this.onMouseMove = new EventEmitter();
48
+ this.onMouseOver = new EventEmitter();
49
+ this.onMouseOut = new EventEmitter();
50
+ // Map Move Events
51
+ this.onMapMove = new EventEmitter();
52
+ this.onMapMoveStart = new EventEmitter();
53
+ this.onMapMoveEnd = new EventEmitter();
54
+ // Map Zoom Events
55
+ this.onMapZoom = new EventEmitter();
56
+ this.onMapZoomStart = new EventEmitter();
57
+ this.onMapZoomEnd = new EventEmitter();
58
+ // Nothing here
59
+ }
60
+ ngOnInit() {
61
+ // Create the map outside of angular so the various map events don't trigger change detection
62
+ this.zone.runOutsideAngular(() => {
63
+ // Create the map with some reasonable defaults
64
+ this.map = map(this.element.nativeElement, this.options);
65
+ this.addMapEventListeners();
66
+ });
67
+ // Only setView if there is a center/zoom
68
+ if (null != this.center && null != this.zoom) {
69
+ this.setView(this.center, this.zoom);
70
+ }
71
+ // Set up all the initial settings
72
+ if (null != this.fitBounds) {
73
+ this.setFitBounds(this.fitBounds);
74
+ }
75
+ if (null != this.maxBounds) {
76
+ this.setMaxBounds(this.maxBounds);
77
+ }
78
+ if (null != this.minZoom) {
79
+ this.setMinZoom(this.minZoom);
80
+ }
81
+ if (null != this.maxZoom) {
82
+ this.setMaxZoom(this.maxZoom);
83
+ }
84
+ this.doResize();
85
+ // Fire map ready event
86
+ this.mapReady.emit(this.map);
87
+ }
88
+ ngOnChanges(changes) {
89
+ /*
90
+ * The following code is to address an issue with our (basic) implementation of
91
+ * zooming and panning. From our testing, it seems that a pan operation followed
92
+ * by a zoom operation in the same thread will interfere with eachother. The zoom
93
+ * operation interrupts/cancels the pan, resulting in a final center point that is
94
+ * inaccurate. The solution seems to be to either separate them with a timeout or
95
+ * to collapse them into a setView call.
96
+ */
97
+ // Zooming and Panning
98
+ if (changes['zoom'] && changes['center'] && null != this.zoom && null != this.center) {
99
+ this.setView(changes['center'].currentValue, changes['zoom'].currentValue);
100
+ }
101
+ // Set the zoom level
102
+ else if (changes['zoom']) {
103
+ this.setZoom(changes['zoom'].currentValue);
104
+ }
105
+ // Set the map center
106
+ else if (changes['center']) {
107
+ this.setCenter(changes['center'].currentValue);
108
+ }
109
+ // Other options
110
+ if (changes['fitBounds']) {
111
+ this.setFitBounds(changes['fitBounds'].currentValue);
112
+ }
113
+ if (changes['maxBounds']) {
114
+ this.setMaxBounds(changes['maxBounds'].currentValue);
115
+ }
116
+ if (changes['minZoom']) {
117
+ this.setMinZoom(changes['minZoom'].currentValue);
118
+ }
119
+ if (changes['maxZoom']) {
120
+ this.setMaxZoom(changes['maxZoom'].currentValue);
121
+ }
122
+ }
123
+ ngOnDestroy() {
124
+ // If this directive is destroyed, the map is too
125
+ if (null != this.map) {
126
+ this.map.remove();
127
+ }
128
+ }
129
+ getMap() {
130
+ return this.map;
131
+ }
132
+ onResize() {
133
+ this.delayResize();
134
+ }
135
+ addMapEventListeners() {
136
+ const registerEventHandler = (eventName, handler) => {
137
+ this.map.on(eventName, handler);
138
+ };
139
+ // Add all the pass-through mouse event handlers
140
+ registerEventHandler('click', (e) => LeafletUtil.handleEvent(this.zone, this.onClick, e));
141
+ registerEventHandler('dblclick', (e) => LeafletUtil.handleEvent(this.zone, this.onDoubleClick, e));
142
+ registerEventHandler('mousedown', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseDown, e));
143
+ registerEventHandler('mouseup', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseUp, e));
144
+ registerEventHandler('mouseover', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseOver, e));
145
+ registerEventHandler('mouseout', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseOut, e));
146
+ registerEventHandler('mousemove', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseMove, e));
147
+ registerEventHandler('zoomstart', (e) => LeafletUtil.handleEvent(this.zone, this.onMapZoomStart, e));
148
+ registerEventHandler('zoom', (e) => LeafletUtil.handleEvent(this.zone, this.onMapZoom, e));
149
+ registerEventHandler('zoomend', (e) => LeafletUtil.handleEvent(this.zone, this.onMapZoomEnd, e));
150
+ registerEventHandler('movestart', (e) => LeafletUtil.handleEvent(this.zone, this.onMapMoveStart, e));
151
+ registerEventHandler('move', (e) => LeafletUtil.handleEvent(this.zone, this.onMapMove, e));
152
+ registerEventHandler('moveend', (e) => LeafletUtil.handleEvent(this.zone, this.onMapMoveEnd, e));
153
+ // Update any things for which we provide output bindings
154
+ const outputUpdateHandler = () => {
155
+ const zoom = this.map.getZoom();
156
+ if (zoom !== this.zoom) {
157
+ this.zoom = zoom;
158
+ LeafletUtil.handleEvent(this.zone, this.zoomChange, zoom);
159
+ }
160
+ const center = this.map.getCenter();
161
+ if (null != center || null != this.center) {
162
+ if (((null == center || null == this.center) && center !== this.center)
163
+ || (center.lat !== this.center.lat || center.lng !== this.center.lng)) {
164
+ this.center = center;
165
+ LeafletUtil.handleEvent(this.zone, this.centerChange, center);
166
+ }
167
+ }
168
+ };
169
+ registerEventHandler('moveend', outputUpdateHandler);
170
+ registerEventHandler('zoomend', outputUpdateHandler);
171
+ }
172
+ /**
173
+ * Resize the map to fit it's parent container
174
+ */
175
+ doResize() {
176
+ // Run this outside of angular so the map events stay outside of angular
177
+ this.zone.runOutsideAngular(() => {
178
+ // Invalidate the map size to trigger it to update itself
179
+ if (null != this.map) {
180
+ this.map.invalidateSize({});
181
+ }
182
+ });
183
+ }
184
+ /**
185
+ * Manage a delayed resize of the component
186
+ */
187
+ delayResize() {
188
+ if (null != this.resizeTimer) {
189
+ clearTimeout(this.resizeTimer);
190
+ }
191
+ this.resizeTimer = setTimeout(this.doResize.bind(this), 200);
192
+ }
193
+ /**
194
+ * Set the view (center/zoom) all at once
195
+ * @param center The new center
196
+ * @param zoom The new zoom level
197
+ */
198
+ setView(center, zoom) {
199
+ if (null != this.map && null != center && null != zoom) {
200
+ this.map.setView(center, zoom, this.zoomPanOptions);
201
+ }
202
+ }
203
+ /**
204
+ * Set the map zoom level
205
+ * @param zoom the new zoom level for the map
206
+ */
207
+ setZoom(zoom) {
208
+ if (null != this.map && null != zoom) {
209
+ this.map.setZoom(zoom, this.zoomOptions);
210
+ }
211
+ }
212
+ /**
213
+ * Set the center of the map
214
+ * @param center the center point
215
+ */
216
+ setCenter(center) {
217
+ if (null != this.map && null != center) {
218
+ this.map.panTo(center, this.panOptions);
219
+ }
220
+ }
221
+ /**
222
+ * Fit the map to the bounds
223
+ * @param latLngBounds the boundary to set
224
+ */
225
+ setFitBounds(latLngBounds) {
226
+ if (null != this.map && null != latLngBounds) {
227
+ this.map.fitBounds(latLngBounds, this.fitBoundsOptions);
228
+ }
229
+ }
230
+ /**
231
+ * Set the map's max bounds
232
+ * @param latLngBounds the boundary to set
233
+ */
234
+ setMaxBounds(latLngBounds) {
235
+ if (null != this.map && null != latLngBounds) {
236
+ this.map.setMaxBounds(latLngBounds);
237
+ }
238
+ }
239
+ /**
240
+ * Set the map's min zoom
241
+ * @param number the new min zoom
242
+ */
243
+ setMinZoom(zoom) {
244
+ if (null != this.map && null != zoom) {
245
+ this.map.setMinZoom(zoom);
246
+ }
247
+ }
248
+ /**
249
+ * Set the map's min zoom
250
+ * @param number the new min zoom
251
+ */
252
+ setMaxZoom(zoom) {
253
+ if (null != this.map && null != zoom) {
254
+ this.map.setMaxZoom(zoom);
255
+ }
256
+ }
257
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
258
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: LeafletDirective, selector: "[leaflet]", inputs: { fitBoundsOptions: ["leafletFitBoundsOptions", "fitBoundsOptions"], panOptions: ["leafletPanOptions", "panOptions"], zoomOptions: ["leafletZoomOptions", "zoomOptions"], zoomPanOptions: ["leafletZoomPanOptions", "zoomPanOptions"], options: ["leafletOptions", "options"], zoom: ["leafletZoom", "zoom"], center: ["leafletCenter", "center"], fitBounds: ["leafletFitBounds", "fitBounds"], maxBounds: ["leafletMaxBounds", "maxBounds"], minZoom: ["leafletMinZoom", "minZoom"], maxZoom: ["leafletMaxZoom", "maxZoom"] }, outputs: { mapReady: "leafletMapReady", zoomChange: "leafletZoomChange", centerChange: "leafletCenterChange", onClick: "leafletClick", onDoubleClick: "leafletDoubleClick", onMouseDown: "leafletMouseDown", onMouseUp: "leafletMouseUp", onMouseMove: "leafletMouseMove", onMouseOver: "leafletMouseOver", onMouseOut: "leafletMouseOut", onMapMove: "leafletMapMove", onMapMoveStart: "leafletMapMoveStart", onMapMoveEnd: "leafletMapMoveEnd", onMapZoom: "leafletMapZoom", onMapZoomStart: "leafletMapZoomStart", onMapZoomEnd: "leafletMapZoomEnd" }, host: { listeners: { "window:resize": "onResize()" } }, usesOnChanges: true, ngImport: i0 }); }
259
+ }
260
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletDirective, decorators: [{
261
+ type: Directive,
262
+ args: [{
263
+ selector: '[leaflet]'
264
+ }]
265
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { fitBoundsOptions: [{
266
+ type: Input,
267
+ args: ['leafletFitBoundsOptions']
268
+ }], panOptions: [{
269
+ type: Input,
270
+ args: ['leafletPanOptions']
271
+ }], zoomOptions: [{
272
+ type: Input,
273
+ args: ['leafletZoomOptions']
274
+ }], zoomPanOptions: [{
275
+ type: Input,
276
+ args: ['leafletZoomPanOptions']
277
+ }], options: [{
278
+ type: Input,
279
+ args: ['leafletOptions']
280
+ }], mapReady: [{
281
+ type: Output,
282
+ args: ['leafletMapReady']
283
+ }], zoom: [{
284
+ type: Input,
285
+ args: ['leafletZoom']
286
+ }], zoomChange: [{
287
+ type: Output,
288
+ args: ['leafletZoomChange']
289
+ }], center: [{
290
+ type: Input,
291
+ args: ['leafletCenter']
292
+ }], centerChange: [{
293
+ type: Output,
294
+ args: ['leafletCenterChange']
295
+ }], fitBounds: [{
296
+ type: Input,
297
+ args: ['leafletFitBounds']
298
+ }], maxBounds: [{
299
+ type: Input,
300
+ args: ['leafletMaxBounds']
301
+ }], minZoom: [{
302
+ type: Input,
303
+ args: ['leafletMinZoom']
304
+ }], maxZoom: [{
305
+ type: Input,
306
+ args: ['leafletMaxZoom']
307
+ }], onClick: [{
308
+ type: Output,
309
+ args: ['leafletClick']
310
+ }], onDoubleClick: [{
311
+ type: Output,
312
+ args: ['leafletDoubleClick']
313
+ }], onMouseDown: [{
314
+ type: Output,
315
+ args: ['leafletMouseDown']
316
+ }], onMouseUp: [{
317
+ type: Output,
318
+ args: ['leafletMouseUp']
319
+ }], onMouseMove: [{
320
+ type: Output,
321
+ args: ['leafletMouseMove']
322
+ }], onMouseOver: [{
323
+ type: Output,
324
+ args: ['leafletMouseOver']
325
+ }], onMouseOut: [{
326
+ type: Output,
327
+ args: ['leafletMouseOut']
328
+ }], onMapMove: [{
329
+ type: Output,
330
+ args: ['leafletMapMove']
331
+ }], onMapMoveStart: [{
332
+ type: Output,
333
+ args: ['leafletMapMoveStart']
334
+ }], onMapMoveEnd: [{
335
+ type: Output,
336
+ args: ['leafletMapMoveEnd']
337
+ }], onMapZoom: [{
338
+ type: Output,
339
+ args: ['leafletMapZoom']
340
+ }], onMapZoomStart: [{
341
+ type: Output,
342
+ args: ['leafletMapZoomStart']
343
+ }], onMapZoomEnd: [{
344
+ type: Output,
345
+ args: ['leafletMapZoomEnd']
346
+ }], onResize: [{
347
+ type: HostListener,
348
+ args: ['window:resize', []]
349
+ }] } });
350
+
351
+ class LeafletDirectiveWrapper {
352
+ constructor(leafletDirective) {
353
+ this.leafletDirective = leafletDirective;
354
+ }
355
+ init() {
356
+ // Nothing for now
357
+ }
358
+ getMap() {
359
+ return this.leafletDirective.getMap();
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Layer directive
365
+ *
366
+ * This directive is used to directly control a single map layer. The purpose of this directive is to
367
+ * be used as part of a child structural directive of the map element.
368
+ *
369
+ */
370
+ class LeafletLayerDirective {
371
+ constructor(leafletDirective, zone) {
372
+ this.zone = zone;
373
+ // Layer Events
374
+ this.onAdd = new EventEmitter();
375
+ this.onRemove = new EventEmitter();
376
+ this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
377
+ }
378
+ ngOnInit() {
379
+ // Init the map
380
+ this.leafletDirective.init();
381
+ }
382
+ ngOnDestroy() {
383
+ if (null != this.layer) {
384
+ // Unregister the event handlers
385
+ this.removeLayerEventListeners(this.layer);
386
+ // Remove the layer from the map
387
+ this.layer.remove();
388
+ }
389
+ }
390
+ ngOnChanges(changes) {
391
+ if (changes['layer']) {
392
+ // Update the layer
393
+ const p = changes['layer'].previousValue;
394
+ const n = changes['layer'].currentValue;
395
+ this.zone.runOutsideAngular(() => {
396
+ if (null != p) {
397
+ this.removeLayerEventListeners(p);
398
+ p.remove();
399
+ }
400
+ if (null != n) {
401
+ this.addLayerEventListeners(n);
402
+ this.leafletDirective.getMap().addLayer(n);
403
+ }
404
+ });
405
+ }
406
+ }
407
+ addLayerEventListeners(l) {
408
+ this.onAddLayerHandler = (e) => LeafletUtil.handleEvent(this.zone, this.onAdd, e);
409
+ l.on('add', this.onAddLayerHandler);
410
+ this.onRemoveLayerHandler = (e) => LeafletUtil.handleEvent(this.zone, this.onRemove, e);
411
+ l.on('remove', this.onRemoveLayerHandler);
412
+ }
413
+ removeLayerEventListeners(l) {
414
+ l.off('add', this.onAddLayerHandler);
415
+ l.off('remove', this.onRemoveLayerHandler);
416
+ }
417
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletLayerDirective, deps: [{ token: LeafletDirective }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
418
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: LeafletLayerDirective, selector: "[leafletLayer]", inputs: { layer: ["leafletLayer", "layer"] }, outputs: { onAdd: "leafletLayerAdd", onRemove: "leafletLayerRemove" }, usesOnChanges: true, ngImport: i0 }); }
419
+ }
420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletLayerDirective, decorators: [{
421
+ type: Directive,
422
+ args: [{
423
+ selector: '[leafletLayer]'
424
+ }]
425
+ }], ctorParameters: () => [{ type: LeafletDirective }, { type: i0.NgZone }], propDecorators: { layer: [{
426
+ type: Input,
427
+ args: ['leafletLayer']
428
+ }], onAdd: [{
429
+ type: Output,
430
+ args: ['leafletLayerAdd']
431
+ }], onRemove: [{
432
+ type: Output,
433
+ args: ['leafletLayerRemove']
434
+ }] } });
435
+
436
+ /**
437
+ * Layers directive
438
+ *
439
+ * This directive is used to directly control map layers. As changes are made to the input array of
440
+ * layers, the map is synched to the array. As layers are added or removed from the input array, they
441
+ * are also added or removed from the map. The input array is treated as immutable. To detect changes,
442
+ * you must change the array instance.
443
+ *
444
+ * Important Note: The input layers array is assumed to be immutable. This means you need to use an
445
+ * immutable array implementation or create a new copy of your array when you make changes, otherwise
446
+ * this directive won't detect the change. This is by design. It's for performance reasons. Change
447
+ * detection of mutable arrays requires diffing the state of the array on every DoCheck cycle, which
448
+ * is extremely expensive from a time complexity perspective.
449
+ *
450
+ */
451
+ class LeafletLayersDirective {
452
+ // Set/get the layers
453
+ set layers(v) {
454
+ this.layersValue = v;
455
+ // Now that we have a differ, do an immediate layer update
456
+ this.updateLayers();
457
+ }
458
+ get layers() {
459
+ return this.layersValue;
460
+ }
461
+ constructor(leafletDirective, differs, zone) {
462
+ this.differs = differs;
463
+ this.zone = zone;
464
+ this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
465
+ this.layersDiffer = this.differs.find([]).create();
466
+ }
467
+ ngDoCheck() {
468
+ this.updateLayers();
469
+ }
470
+ ngOnInit() {
471
+ // Init the map
472
+ this.leafletDirective.init();
473
+ // Update layers once the map is ready
474
+ this.updateLayers();
475
+ }
476
+ ngOnDestroy() {
477
+ this.layers = [];
478
+ }
479
+ /**
480
+ * Update the state of the layers.
481
+ * We use an iterable differ to synchronize the map layers with the state of the bound layers array.
482
+ * This is important because it allows us to react to changes to the contents of the array as well
483
+ * as changes to the actual array instance.
484
+ */
485
+ updateLayers() {
486
+ const map = this.leafletDirective.getMap();
487
+ if (null != map && null != this.layersDiffer) {
488
+ const changes = this.layersDiffer.diff(this.layersValue);
489
+ if (null != changes) {
490
+ // Run outside angular to ensure layer events don't trigger change detection
491
+ this.zone.runOutsideAngular(() => {
492
+ changes.forEachRemovedItem((c) => {
493
+ map.removeLayer(c.item);
494
+ });
495
+ changes.forEachAddedItem((c) => {
496
+ map.addLayer(c.item);
497
+ });
498
+ });
499
+ }
500
+ }
501
+ }
502
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletLayersDirective, deps: [{ token: LeafletDirective }, { token: i0.IterableDiffers }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
503
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: LeafletLayersDirective, selector: "[leafletLayers]", inputs: { layers: ["leafletLayers", "layers"] }, ngImport: i0 }); }
504
+ }
505
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletLayersDirective, decorators: [{
506
+ type: Directive,
507
+ args: [{
508
+ selector: '[leafletLayers]'
509
+ }]
510
+ }], ctorParameters: () => [{ type: LeafletDirective }, { type: i0.IterableDiffers }, { type: i0.NgZone }], propDecorators: { layers: [{
511
+ type: Input,
512
+ args: ['leafletLayers']
513
+ }] } });
514
+
515
+ class LeafletControlLayersChanges {
516
+ constructor() {
517
+ this.layersRemoved = 0;
518
+ this.layersChanged = 0;
519
+ this.layersAdded = 0;
520
+ }
521
+ changed() {
522
+ return !(this.layersRemoved === 0 && this.layersChanged === 0 && this.layersAdded === 0);
523
+ }
524
+ }
525
+
526
+ class LeafletControlLayersWrapper {
527
+ constructor(zone, layersControlReady) {
528
+ this.zone = zone;
529
+ this.layersControlReady = layersControlReady;
530
+ }
531
+ getLayersControl() {
532
+ return this.layersControl;
533
+ }
534
+ init(controlConfig, controlOptions) {
535
+ const baseLayers = controlConfig.baseLayers || {};
536
+ const overlays = controlConfig.overlays || {};
537
+ // Create the control outside of angular to ensure events don't trigger change detection
538
+ this.zone.runOutsideAngular(() => {
539
+ this.layersControl = control.layers(baseLayers, overlays, controlOptions);
540
+ });
541
+ this.layersControlReady.emit(this.layersControl);
542
+ return this.layersControl;
543
+ }
544
+ applyBaseLayerChanges(changes) {
545
+ let results = new LeafletControlLayersChanges();
546
+ if (null != this.layersControl) {
547
+ results = this.applyChanges(changes, this.layersControl.addBaseLayer);
548
+ }
549
+ return results;
550
+ }
551
+ applyOverlayChanges(changes) {
552
+ let results = new LeafletControlLayersChanges();
553
+ if (null != this.layersControl) {
554
+ results = this.applyChanges(changes, this.layersControl.addOverlay);
555
+ }
556
+ return results;
557
+ }
558
+ applyChanges(changes, addFn) {
559
+ const results = new LeafletControlLayersChanges();
560
+ if (null != changes) {
561
+ // All layer management is outside angular to avoid layer events from triggering change detection
562
+ this.zone.runOutsideAngular(() => {
563
+ changes.forEachChangedItem((c) => {
564
+ this.layersControl.removeLayer(c.previousValue);
565
+ addFn.call(this.layersControl, c.currentValue, c.key);
566
+ results.layersChanged++;
567
+ });
568
+ changes.forEachRemovedItem((c) => {
569
+ this.layersControl.removeLayer(c.previousValue);
570
+ results.layersRemoved++;
571
+ });
572
+ changes.forEachAddedItem((c) => {
573
+ addFn.call(this.layersControl, c.currentValue, c.key);
574
+ results.layersAdded++;
575
+ });
576
+ });
577
+ }
578
+ return results;
579
+ }
580
+ }
581
+
582
+ class LeafletControlLayersConfig {
583
+ constructor() {
584
+ this.baseLayers = {};
585
+ this.overlays = {};
586
+ }
587
+ }
588
+
589
+ /**
590
+ * Layers Control
591
+ *
592
+ * This directive is used to configure the layers control. The input accepts an object with two
593
+ * key-value maps of layer name -> layer. Mutable changes are detected. On changes, a differ is
594
+ * used to determine what changed so that layers are appropriately added or removed.
595
+ *
596
+ * To specify which layer to show as the 'active' baselayer, you will want to add it to the map
597
+ * using the layers directive. Otherwise, the last one it sees will be used.
598
+ */
599
+ class LeafletLayersControlDirective {
600
+ set layersControlConfig(v) {
601
+ // Validation/init stuff
602
+ if (null == v) {
603
+ v = new LeafletControlLayersConfig();
604
+ }
605
+ if (null == v.baseLayers) {
606
+ v.baseLayers = {};
607
+ }
608
+ if (null == v.overlays) {
609
+ v.overlays = {};
610
+ }
611
+ // Store the value
612
+ this.layersControlConfigValue = v;
613
+ // Update the map
614
+ this.updateLayers();
615
+ }
616
+ get layersControlConfig() {
617
+ return this.layersControlConfigValue;
618
+ }
619
+ constructor(leafletDirective, differs, zone) {
620
+ this.differs = differs;
621
+ this.zone = zone;
622
+ this.layersControlReady = new EventEmitter();
623
+ this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
624
+ this.controlLayers = new LeafletControlLayersWrapper(this.zone, this.layersControlReady);
625
+ // Generate differs
626
+ this.baseLayersDiffer = this.differs.find({}).create();
627
+ this.overlaysDiffer = this.differs.find({}).create();
628
+ }
629
+ ngOnInit() {
630
+ // Init the map
631
+ this.leafletDirective.init();
632
+ // Set up control outside of angular to avoid change detection when using the control
633
+ this.zone.runOutsideAngular(() => {
634
+ // Set up all the initial settings
635
+ this.controlLayers
636
+ .init({}, this.layersControlOptions)
637
+ .addTo(this.leafletDirective.getMap());
638
+ });
639
+ this.updateLayers();
640
+ }
641
+ ngOnDestroy() {
642
+ this.layersControlConfig = { baseLayers: {}, overlays: {} };
643
+ this.controlLayers.getLayersControl().remove();
644
+ }
645
+ ngDoCheck() {
646
+ this.updateLayers();
647
+ }
648
+ updateLayers() {
649
+ const map = this.leafletDirective.getMap();
650
+ const layersControl = this.controlLayers.getLayersControl();
651
+ if (null != map && null != layersControl) {
652
+ // Run the baselayers differ
653
+ if (null != this.baseLayersDiffer && null != this.layersControlConfigValue.baseLayers) {
654
+ const changes = this.baseLayersDiffer.diff(this.layersControlConfigValue.baseLayers);
655
+ this.controlLayers.applyBaseLayerChanges(changes);
656
+ }
657
+ // Run the overlays differ
658
+ if (null != this.overlaysDiffer && null != this.layersControlConfigValue.overlays) {
659
+ const changes = this.overlaysDiffer.diff(this.layersControlConfigValue.overlays);
660
+ this.controlLayers.applyOverlayChanges(changes);
661
+ }
662
+ }
663
+ }
664
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletLayersControlDirective, deps: [{ token: LeafletDirective }, { token: i0.KeyValueDiffers }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
665
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: LeafletLayersControlDirective, selector: "[leafletLayersControl]", inputs: { layersControlConfig: ["leafletLayersControl", "layersControlConfig"], layersControlOptions: ["leafletLayersControlOptions", "layersControlOptions"] }, outputs: { layersControlReady: "leafletLayersControlReady" }, ngImport: i0 }); }
666
+ }
667
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletLayersControlDirective, decorators: [{
668
+ type: Directive,
669
+ args: [{
670
+ selector: '[leafletLayersControl]'
671
+ }]
672
+ }], ctorParameters: () => [{ type: LeafletDirective }, { type: i0.KeyValueDiffers }, { type: i0.NgZone }], propDecorators: { layersControlConfig: [{
673
+ type: Input,
674
+ args: ['leafletLayersControl']
675
+ }], layersControlOptions: [{
676
+ type: Input,
677
+ args: ['leafletLayersControlOptions']
678
+ }], layersControlReady: [{
679
+ type: Output,
680
+ args: ['leafletLayersControlReady']
681
+ }] } });
682
+
683
+ /**
684
+ * Baselayers directive
685
+ *
686
+ * This directive is provided as a convenient way to add baselayers to the map. The input accepts
687
+ * a key-value map of layer name -> layer. Mutable changed are detected. On changes, a differ is
688
+ * used to determine what changed so that layers are appropriately added or removed. This directive
689
+ * will also add the layers control so users can switch between available base layers.
690
+ *
691
+ * To specify which layer to show as the 'active' baselayer, you will want to add it to the map
692
+ * using the layers directive. Otherwise, the plugin will use the last one it sees.
693
+ */
694
+ class LeafletBaseLayersDirective {
695
+ // Set/get baseLayers
696
+ set baseLayers(v) {
697
+ this.baseLayersValue = v;
698
+ this.updateBaseLayers();
699
+ }
700
+ get baseLayers() {
701
+ return this.baseLayersValue;
702
+ }
703
+ constructor(leafletDirective, differs, zone) {
704
+ this.differs = differs;
705
+ this.zone = zone;
706
+ // Output for once the layers control is ready
707
+ this.layersControlReady = new EventEmitter();
708
+ this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
709
+ this.controlLayers = new LeafletControlLayersWrapper(this.zone, this.layersControlReady);
710
+ this.baseLayersDiffer = this.differs.find({}).create();
711
+ }
712
+ ngOnDestroy() {
713
+ this.baseLayers = {};
714
+ if (null != this.controlLayers.getLayersControl()) {
715
+ this.controlLayers.getLayersControl().remove();
716
+ }
717
+ }
718
+ ngOnInit() {
719
+ // Init the map
720
+ this.leafletDirective.init();
721
+ // Create the control outside angular to prevent events from triggering chnage detection
722
+ this.zone.runOutsideAngular(() => {
723
+ // Initially configure the controlLayers
724
+ this.controlLayers
725
+ .init({}, this.layersControlOptions)
726
+ .addTo(this.leafletDirective.getMap());
727
+ });
728
+ this.updateBaseLayers();
729
+ }
730
+ ngDoCheck() {
731
+ this.updateBaseLayers();
732
+ }
733
+ updateBaseLayers() {
734
+ const map = this.leafletDirective.getMap();
735
+ const layersControl = this.controlLayers.getLayersControl();
736
+ if (null != map && null != layersControl && null != this.baseLayersDiffer) {
737
+ const changes = this.baseLayersDiffer.diff(this.baseLayersValue);
738
+ const results = this.controlLayers.applyBaseLayerChanges(changes);
739
+ if (results.changed()) {
740
+ this.syncBaseLayer();
741
+ }
742
+ }
743
+ }
744
+ /**
745
+ * Check the current base layer and change it to the new one if necessary
746
+ */
747
+ syncBaseLayer() {
748
+ const map = this.leafletDirective.getMap();
749
+ const layers = LeafletUtil.mapToArray(this.baseLayers);
750
+ let foundLayer;
751
+ // Search all the layers in the map to see if we can find them in the baselayer array
752
+ map.eachLayer((l) => {
753
+ foundLayer = layers.find((bl) => (l === bl));
754
+ });
755
+ // Did we find the layer?
756
+ if (null != foundLayer) {
757
+ // Yes - set the baselayer to the one we found
758
+ this.baseLayer = foundLayer;
759
+ }
760
+ else {
761
+ // No - set the baselayer to the first in the array and add it to the map
762
+ if (layers.length > 0) {
763
+ this.baseLayer = layers[0];
764
+ // Add layers outside of angular to prevent events from triggering change detection
765
+ this.zone.runOutsideAngular(() => {
766
+ this.baseLayer.addTo(map);
767
+ });
768
+ }
769
+ }
770
+ }
771
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletBaseLayersDirective, deps: [{ token: LeafletDirective }, { token: i0.KeyValueDiffers }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive }); }
772
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.1", type: LeafletBaseLayersDirective, selector: "[leafletBaseLayers]", inputs: { baseLayers: ["leafletBaseLayers", "baseLayers"], layersControlOptions: ["leafletLayersControlOptions", "layersControlOptions"] }, outputs: { layersControlReady: "leafletLayersControlReady" }, ngImport: i0 }); }
773
+ }
774
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletBaseLayersDirective, decorators: [{
775
+ type: Directive,
776
+ args: [{
777
+ selector: '[leafletBaseLayers]'
778
+ }]
779
+ }], ctorParameters: () => [{ type: LeafletDirective }, { type: i0.KeyValueDiffers }, { type: i0.NgZone }], propDecorators: { baseLayers: [{
780
+ type: Input,
781
+ args: ['leafletBaseLayers']
782
+ }], layersControlOptions: [{
783
+ type: Input,
784
+ args: ['leafletLayersControlOptions']
785
+ }], layersControlReady: [{
786
+ type: Output,
787
+ args: ['leafletLayersControlReady']
788
+ }] } });
789
+
790
+ class LeafletModule {
791
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
792
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.1", ngImport: i0, type: LeafletModule, declarations: [LeafletDirective,
793
+ LeafletLayerDirective,
794
+ LeafletLayersDirective,
795
+ LeafletLayersControlDirective,
796
+ LeafletBaseLayersDirective], exports: [LeafletDirective,
797
+ LeafletLayerDirective,
798
+ LeafletLayersDirective,
799
+ LeafletLayersControlDirective,
800
+ LeafletBaseLayersDirective] }); }
801
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletModule }); }
802
+ }
803
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.1", ngImport: i0, type: LeafletModule, decorators: [{
804
+ type: NgModule,
805
+ args: [{
806
+ exports: [
807
+ LeafletDirective,
808
+ LeafletLayerDirective,
809
+ LeafletLayersDirective,
810
+ LeafletLayersControlDirective,
811
+ LeafletBaseLayersDirective
812
+ ],
813
+ declarations: [
814
+ LeafletDirective,
815
+ LeafletLayerDirective,
816
+ LeafletLayersDirective,
817
+ LeafletLayersControlDirective,
818
+ LeafletBaseLayersDirective
819
+ ]
820
+ }]
821
+ }] });
822
+
823
+ class LeafletTileLayerDefinition {
824
+ constructor(type, url, options) {
825
+ this.type = type;
826
+ this.url = url;
827
+ this.options = options;
828
+ }
829
+ /**
830
+ * Creates a TileLayer from the provided definition. This is a convenience function
831
+ * to help with generating layers from objects.
832
+ *
833
+ * @param layerDef The layer to create
834
+ * @returns {TileLayer} The TileLayer that has been created
835
+ */
836
+ static createTileLayer(layerDef) {
837
+ let layer;
838
+ switch (layerDef.type) {
839
+ case 'xyz':
840
+ layer = tileLayer(layerDef.url, layerDef.options);
841
+ break;
842
+ case 'wms':
843
+ default:
844
+ layer = tileLayer.wms(layerDef.url, layerDef.options);
845
+ break;
846
+ }
847
+ return layer;
848
+ }
849
+ /**
850
+ * Creates a TileLayer for each key in the incoming map. This is a convenience function
851
+ * for generating an associative array of layers from an associative array of objects
852
+ *
853
+ * @param layerDefs A map of key to tile layer definition
854
+ * @returns {{[p: string]: TileLayer}} A new map of key to TileLayer
855
+ */
856
+ static createTileLayers(layerDefs) {
857
+ const layers = {};
858
+ for (const k in layerDefs) {
859
+ if (layerDefs.hasOwnProperty(k)) {
860
+ layers[k] = (LeafletTileLayerDefinition.createTileLayer(layerDefs[k]));
861
+ }
862
+ }
863
+ return layers;
864
+ }
865
+ /**
866
+ * Create a Tile Layer from the current state of this object
867
+ *
868
+ * @returns {TileLayer} A new TileLayer
869
+ */
870
+ createTileLayer() {
871
+ return LeafletTileLayerDefinition.createTileLayer(this);
872
+ }
873
+ }
874
+
875
+ /**
876
+ * Generated bundle index. Do not edit.
877
+ */
878
+
879
+ export { LeafletBaseLayersDirective, LeafletControlLayersChanges, LeafletControlLayersConfig, LeafletControlLayersWrapper, LeafletDirective, LeafletDirectiveWrapper, LeafletLayerDirective, LeafletLayersControlDirective, LeafletLayersDirective, LeafletModule, LeafletTileLayerDefinition, LeafletUtil };
880
+ //# sourceMappingURL=bluehalo-ngx-leaflet.mjs.map