UNPKG

37.3 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { EventEmitter, Directive, Input, Output, HostListener, NgModule } from '@angular/core';
3import { latLng, map, control, tileLayer } from 'leaflet';
4
5class 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
25class 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}
258LeafletDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletDirective, deps: [{ token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
259LeafletDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.4", 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 });
260i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletDirective, decorators: [{
261 type: Directive,
262 args: [{
263 selector: '[leaflet]'
264 }]
265 }], ctorParameters: function () { return [{ 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
351class 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 */
370class 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}
418LeafletLayerDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayerDirective, deps: [{ token: LeafletDirective }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
419LeafletLayerDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.4", type: LeafletLayerDirective, selector: "[leafletLayer]", inputs: { layer: ["leafletLayer", "layer"] }, outputs: { onAdd: "leafletLayerAdd", onRemove: "leafletLayerRemove" }, usesOnChanges: true, ngImport: i0 });
420i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayerDirective, decorators: [{
421 type: Directive,
422 args: [{
423 selector: '[leafletLayer]'
424 }]
425 }], ctorParameters: function () { return [{ 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 */
451class LeafletLayersDirective {
452 constructor(leafletDirective, differs, zone) {
453 this.differs = differs;
454 this.zone = zone;
455 this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
456 this.layersDiffer = this.differs.find([]).create();
457 }
458 // Set/get the layers
459 set layers(v) {
460 this.layersValue = v;
461 // Now that we have a differ, do an immediate layer update
462 this.updateLayers();
463 }
464 get layers() {
465 return this.layersValue;
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}
503LeafletLayersDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayersDirective, deps: [{ token: LeafletDirective }, { token: i0.IterableDiffers }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
504LeafletLayersDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.4", type: LeafletLayersDirective, selector: "[leafletLayers]", inputs: { layers: ["leafletLayers", "layers"] }, ngImport: i0 });
505i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayersDirective, decorators: [{
506 type: Directive,
507 args: [{
508 selector: '[leafletLayers]'
509 }]
510 }], ctorParameters: function () { return [{ type: LeafletDirective }, { type: i0.IterableDiffers }, { type: i0.NgZone }]; }, propDecorators: { layers: [{
511 type: Input,
512 args: ['leafletLayers']
513 }] } });
514
515class 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
526class 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
582class 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 */
599class LeafletLayersControlDirective {
600 constructor(leafletDirective, differs, zone) {
601 this.differs = differs;
602 this.zone = zone;
603 this.layersControlReady = new EventEmitter();
604 this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
605 this.controlLayers = new LeafletControlLayersWrapper(this.zone, this.layersControlReady);
606 // Generate differs
607 this.baseLayersDiffer = this.differs.find({}).create();
608 this.overlaysDiffer = this.differs.find({}).create();
609 }
610 set layersControlConfig(v) {
611 // Validation/init stuff
612 if (null == v) {
613 v = new LeafletControlLayersConfig();
614 }
615 if (null == v.baseLayers) {
616 v.baseLayers = {};
617 }
618 if (null == v.overlays) {
619 v.overlays = {};
620 }
621 // Store the value
622 this.layersControlConfigValue = v;
623 // Update the map
624 this.updateLayers();
625 }
626 get layersControlConfig() {
627 return this.layersControlConfigValue;
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}
665LeafletLayersControlDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayersControlDirective, deps: [{ token: LeafletDirective }, { token: i0.KeyValueDiffers }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
666LeafletLayersControlDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.4", type: LeafletLayersControlDirective, selector: "[leafletLayersControl]", inputs: { layersControlConfig: ["leafletLayersControl", "layersControlConfig"], layersControlOptions: ["leafletLayersControlOptions", "layersControlOptions"] }, outputs: { layersControlReady: "leafletLayersControlReady" }, ngImport: i0 });
667i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayersControlDirective, decorators: [{
668 type: Directive,
669 args: [{
670 selector: '[leafletLayersControl]'
671 }]
672 }], ctorParameters: function () { return [{ 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 */
694class LeafletBaseLayersDirective {
695 constructor(leafletDirective, differs, zone) {
696 this.differs = differs;
697 this.zone = zone;
698 // Output for once the layers control is ready
699 this.layersControlReady = new EventEmitter();
700 this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective);
701 this.controlLayers = new LeafletControlLayersWrapper(this.zone, this.layersControlReady);
702 this.baseLayersDiffer = this.differs.find({}).create();
703 }
704 // Set/get baseLayers
705 set baseLayers(v) {
706 this.baseLayersValue = v;
707 this.updateBaseLayers();
708 }
709 get baseLayers() {
710 return this.baseLayersValue;
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}
772LeafletBaseLayersDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletBaseLayersDirective, deps: [{ token: LeafletDirective }, { token: i0.KeyValueDiffers }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Directive });
773LeafletBaseLayersDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.4", type: LeafletBaseLayersDirective, selector: "[leafletBaseLayers]", inputs: { baseLayers: ["leafletBaseLayers", "baseLayers"], layersControlOptions: ["leafletLayersControlOptions", "layersControlOptions"] }, outputs: { layersControlReady: "leafletLayersControlReady" }, ngImport: i0 });
774i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletBaseLayersDirective, decorators: [{
775 type: Directive,
776 args: [{
777 selector: '[leafletBaseLayers]'
778 }]
779 }], ctorParameters: function () { return [{ 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
790class LeafletModule {
791}
792LeafletModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
793LeafletModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule, declarations: [LeafletDirective,
794 LeafletLayerDirective,
795 LeafletLayersDirective,
796 LeafletLayersControlDirective,
797 LeafletBaseLayersDirective], exports: [LeafletDirective,
798 LeafletLayerDirective,
799 LeafletLayersDirective,
800 LeafletLayersControlDirective,
801 LeafletBaseLayersDirective] });
802LeafletModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule });
803i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", 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
823class 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
879export { LeafletBaseLayersDirective, LeafletControlLayersChanges, LeafletControlLayersConfig, LeafletControlLayersWrapper, LeafletDirective, LeafletDirectiveWrapper, LeafletLayerDirective, LeafletLayersControlDirective, LeafletLayersDirective, LeafletModule, LeafletTileLayerDefinition, LeafletUtil };
880//# sourceMappingURL=asymmetrik-ngx-leaflet.mjs.map