UNPKG

@asymmetrik/ngx-leaflet

Version:
881 lines (866 loc) 37.3 kB
import * as i0 from '@angular/core'; import { EventEmitter, Directive, Input, Output, HostListener, NgModule } from '@angular/core'; import { latLng, map, control, tileLayer } from 'leaflet'; class LeafletUtil { static mapToArray(map) { const toReturn = []; for (const k in map) { if (map.hasOwnProperty(k)) { toReturn.push(map[k]); } } return toReturn; } static handleEvent(zone, eventEmitter, event) { // Don't want to emit if there are no observers if (0 < eventEmitter.observers.length) { zone.run(() => { eventEmitter.emit(event); }); } } } class LeafletDirective { constructor(element, zone) { this.element = element; this.zone = zone; this.DEFAULT_ZOOM = 1; this.DEFAULT_CENTER = latLng(38.907192, -77.036871); this.DEFAULT_FPZ_OPTIONS = {}; this.fitBoundsOptions = this.DEFAULT_FPZ_OPTIONS; this.panOptions = this.DEFAULT_FPZ_OPTIONS; this.zoomOptions = this.DEFAULT_FPZ_OPTIONS; this.zoomPanOptions = this.DEFAULT_FPZ_OPTIONS; // Default configuration this.options = {}; // Configure callback function for the map this.mapReady = new EventEmitter(); this.zoomChange = new EventEmitter(); this.centerChange = new EventEmitter(); // Mouse Map Events this.onClick = new EventEmitter(); this.onDoubleClick = new EventEmitter(); this.onMouseDown = new EventEmitter(); this.onMouseUp = new EventEmitter(); this.onMouseMove = new EventEmitter(); this.onMouseOver = new EventEmitter(); this.onMouseOut = new EventEmitter(); // Map Move Events this.onMapMove = new EventEmitter(); this.onMapMoveStart = new EventEmitter(); this.onMapMoveEnd = new EventEmitter(); // Map Zoom Events this.onMapZoom = new EventEmitter(); this.onMapZoomStart = new EventEmitter(); this.onMapZoomEnd = new EventEmitter(); // Nothing here } ngOnInit() { // Create the map outside of angular so the various map events don't trigger change detection this.zone.runOutsideAngular(() => { // Create the map with some reasonable defaults this.map = map(this.element.nativeElement, this.options); this.addMapEventListeners(); }); // Only setView if there is a center/zoom if (null != this.center && null != this.zoom) { this.setView(this.center, this.zoom); } // Set up all the initial settings if (null != this.fitBounds) { this.setFitBounds(this.fitBounds); } if (null != this.maxBounds) { this.setMaxBounds(this.maxBounds); } if (null != this.minZoom) { this.setMinZoom(this.minZoom); } if (null != this.maxZoom) { this.setMaxZoom(this.maxZoom); } this.doResize(); // Fire map ready event this.mapReady.emit(this.map); } ngOnChanges(changes) { /* * The following code is to address an issue with our (basic) implementation of * zooming and panning. From our testing, it seems that a pan operation followed * by a zoom operation in the same thread will interfere with eachother. The zoom * operation interrupts/cancels the pan, resulting in a final center point that is * inaccurate. The solution seems to be to either separate them with a timeout or * to collapse them into a setView call. */ // Zooming and Panning if (changes['zoom'] && changes['center'] && null != this.zoom && null != this.center) { this.setView(changes['center'].currentValue, changes['zoom'].currentValue); } // Set the zoom level else if (changes['zoom']) { this.setZoom(changes['zoom'].currentValue); } // Set the map center else if (changes['center']) { this.setCenter(changes['center'].currentValue); } // Other options if (changes['fitBounds']) { this.setFitBounds(changes['fitBounds'].currentValue); } if (changes['maxBounds']) { this.setMaxBounds(changes['maxBounds'].currentValue); } if (changes['minZoom']) { this.setMinZoom(changes['minZoom'].currentValue); } if (changes['maxZoom']) { this.setMaxZoom(changes['maxZoom'].currentValue); } } ngOnDestroy() { // If this directive is destroyed, the map is too if (null != this.map) { this.map.remove(); } } getMap() { return this.map; } onResize() { this.delayResize(); } addMapEventListeners() { const registerEventHandler = (eventName, handler) => { this.map.on(eventName, handler); }; // Add all the pass-through mouse event handlers registerEventHandler('click', (e) => LeafletUtil.handleEvent(this.zone, this.onClick, e)); registerEventHandler('dblclick', (e) => LeafletUtil.handleEvent(this.zone, this.onDoubleClick, e)); registerEventHandler('mousedown', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseDown, e)); registerEventHandler('mouseup', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseUp, e)); registerEventHandler('mouseover', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseOver, e)); registerEventHandler('mouseout', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseOut, e)); registerEventHandler('mousemove', (e) => LeafletUtil.handleEvent(this.zone, this.onMouseMove, e)); registerEventHandler('zoomstart', (e) => LeafletUtil.handleEvent(this.zone, this.onMapZoomStart, e)); registerEventHandler('zoom', (e) => LeafletUtil.handleEvent(this.zone, this.onMapZoom, e)); registerEventHandler('zoomend', (e) => LeafletUtil.handleEvent(this.zone, this.onMapZoomEnd, e)); registerEventHandler('movestart', (e) => LeafletUtil.handleEvent(this.zone, this.onMapMoveStart, e)); registerEventHandler('move', (e) => LeafletUtil.handleEvent(this.zone, this.onMapMove, e)); registerEventHandler('moveend', (e) => LeafletUtil.handleEvent(this.zone, this.onMapMoveEnd, e)); // Update any things for which we provide output bindings const outputUpdateHandler = () => { const zoom = this.map.getZoom(); if (zoom !== this.zoom) { this.zoom = zoom; LeafletUtil.handleEvent(this.zone, this.zoomChange, zoom); } const center = this.map.getCenter(); if (null != center || null != this.center) { if (((null == center || null == this.center) && center !== this.center) || (center.lat !== this.center.lat || center.lng !== this.center.lng)) { this.center = center; LeafletUtil.handleEvent(this.zone, this.centerChange, center); } } }; registerEventHandler('moveend', outputUpdateHandler); registerEventHandler('zoomend', outputUpdateHandler); } /** * Resize the map to fit it's parent container */ doResize() { // Run this outside of angular so the map events stay outside of angular this.zone.runOutsideAngular(() => { // Invalidate the map size to trigger it to update itself if (null != this.map) { this.map.invalidateSize({}); } }); } /** * Manage a delayed resize of the component */ delayResize() { if (null != this.resizeTimer) { clearTimeout(this.resizeTimer); } this.resizeTimer = setTimeout(this.doResize.bind(this), 200); } /** * Set the view (center/zoom) all at once * @param center The new center * @param zoom The new zoom level */ setView(center, zoom) { if (null != this.map && null != center && null != zoom) { this.map.setView(center, zoom, this.zoomPanOptions); } } /** * Set the map zoom level * @param zoom the new zoom level for the map */ setZoom(zoom) { if (null != this.map && null != zoom) { this.map.setZoom(zoom, this.zoomOptions); } } /** * Set the center of the map * @param center the center point */ setCenter(center) { if (null != this.map && null != center) { this.map.panTo(center, this.panOptions); } } /** * Fit the map to the bounds * @param latLngBounds the boundary to set */ setFitBounds(latLngBounds) { if (null != this.map && null != latLngBounds) { this.map.fitBounds(latLngBounds, this.fitBoundsOptions); } } /** * Set the map's max bounds * @param latLngBounds the boundary to set */ setMaxBounds(latLngBounds) { if (null != this.map && null != latLngBounds) { this.map.setMaxBounds(latLngBounds); } } /** * Set the map's min zoom * @param number the new min zoom */ setMinZoom(zoom) { if (null != this.map && null != zoom) { this.map.setMinZoom(zoom); } } /** * Set the map's min zoom * @param number the new min zoom */ setMaxZoom(zoom) { if (null != this.map && null != zoom) { this.map.setMaxZoom(zoom); } } } LeafletDirective.ɵ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 }); LeafletDirective.ɵ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 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletDirective, decorators: [{ type: Directive, args: [{ selector: '[leaflet]' }] }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { fitBoundsOptions: [{ type: Input, args: ['leafletFitBoundsOptions'] }], panOptions: [{ type: Input, args: ['leafletPanOptions'] }], zoomOptions: [{ type: Input, args: ['leafletZoomOptions'] }], zoomPanOptions: [{ type: Input, args: ['leafletZoomPanOptions'] }], options: [{ type: Input, args: ['leafletOptions'] }], mapReady: [{ type: Output, args: ['leafletMapReady'] }], zoom: [{ type: Input, args: ['leafletZoom'] }], zoomChange: [{ type: Output, args: ['leafletZoomChange'] }], center: [{ type: Input, args: ['leafletCenter'] }], centerChange: [{ type: Output, args: ['leafletCenterChange'] }], fitBounds: [{ type: Input, args: ['leafletFitBounds'] }], maxBounds: [{ type: Input, args: ['leafletMaxBounds'] }], minZoom: [{ type: Input, args: ['leafletMinZoom'] }], maxZoom: [{ type: Input, args: ['leafletMaxZoom'] }], onClick: [{ type: Output, args: ['leafletClick'] }], onDoubleClick: [{ type: Output, args: ['leafletDoubleClick'] }], onMouseDown: [{ type: Output, args: ['leafletMouseDown'] }], onMouseUp: [{ type: Output, args: ['leafletMouseUp'] }], onMouseMove: [{ type: Output, args: ['leafletMouseMove'] }], onMouseOver: [{ type: Output, args: ['leafletMouseOver'] }], onMouseOut: [{ type: Output, args: ['leafletMouseOut'] }], onMapMove: [{ type: Output, args: ['leafletMapMove'] }], onMapMoveStart: [{ type: Output, args: ['leafletMapMoveStart'] }], onMapMoveEnd: [{ type: Output, args: ['leafletMapMoveEnd'] }], onMapZoom: [{ type: Output, args: ['leafletMapZoom'] }], onMapZoomStart: [{ type: Output, args: ['leafletMapZoomStart'] }], onMapZoomEnd: [{ type: Output, args: ['leafletMapZoomEnd'] }], onResize: [{ type: HostListener, args: ['window:resize', []] }] } }); class LeafletDirectiveWrapper { constructor(leafletDirective) { this.leafletDirective = leafletDirective; } init() { // Nothing for now } getMap() { return this.leafletDirective.getMap(); } } /** * Layer directive * * This directive is used to directly control a single map layer. The purpose of this directive is to * be used as part of a child structural directive of the map element. * */ class LeafletLayerDirective { constructor(leafletDirective, zone) { this.zone = zone; // Layer Events this.onAdd = new EventEmitter(); this.onRemove = new EventEmitter(); this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective); } ngOnInit() { // Init the map this.leafletDirective.init(); } ngOnDestroy() { if (null != this.layer) { // Unregister the event handlers this.removeLayerEventListeners(this.layer); // Remove the layer from the map this.layer.remove(); } } ngOnChanges(changes) { if (changes['layer']) { // Update the layer const p = changes['layer'].previousValue; const n = changes['layer'].currentValue; this.zone.runOutsideAngular(() => { if (null != p) { this.removeLayerEventListeners(p); p.remove(); } if (null != n) { this.addLayerEventListeners(n); this.leafletDirective.getMap().addLayer(n); } }); } } addLayerEventListeners(l) { this.onAddLayerHandler = (e) => LeafletUtil.handleEvent(this.zone, this.onAdd, e); l.on('add', this.onAddLayerHandler); this.onRemoveLayerHandler = (e) => LeafletUtil.handleEvent(this.zone, this.onRemove, e); l.on('remove', this.onRemoveLayerHandler); } removeLayerEventListeners(l) { l.off('add', this.onAddLayerHandler); l.off('remove', this.onRemoveLayerHandler); } } LeafletLayerDirective.ɵ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 }); LeafletLayerDirective.ɵ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 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayerDirective, decorators: [{ type: Directive, args: [{ selector: '[leafletLayer]' }] }], ctorParameters: function () { return [{ type: LeafletDirective }, { type: i0.NgZone }]; }, propDecorators: { layer: [{ type: Input, args: ['leafletLayer'] }], onAdd: [{ type: Output, args: ['leafletLayerAdd'] }], onRemove: [{ type: Output, args: ['leafletLayerRemove'] }] } }); /** * Layers directive * * This directive is used to directly control map layers. As changes are made to the input array of * layers, the map is synched to the array. As layers are added or removed from the input array, they * are also added or removed from the map. The input array is treated as immutable. To detect changes, * you must change the array instance. * * Important Note: The input layers array is assumed to be immutable. This means you need to use an * immutable array implementation or create a new copy of your array when you make changes, otherwise * this directive won't detect the change. This is by design. It's for performance reasons. Change * detection of mutable arrays requires diffing the state of the array on every DoCheck cycle, which * is extremely expensive from a time complexity perspective. * */ class LeafletLayersDirective { constructor(leafletDirective, differs, zone) { this.differs = differs; this.zone = zone; this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective); this.layersDiffer = this.differs.find([]).create(); } // Set/get the layers set layers(v) { this.layersValue = v; // Now that we have a differ, do an immediate layer update this.updateLayers(); } get layers() { return this.layersValue; } ngDoCheck() { this.updateLayers(); } ngOnInit() { // Init the map this.leafletDirective.init(); // Update layers once the map is ready this.updateLayers(); } ngOnDestroy() { this.layers = []; } /** * Update the state of the layers. * We use an iterable differ to synchronize the map layers with the state of the bound layers array. * This is important because it allows us to react to changes to the contents of the array as well * as changes to the actual array instance. */ updateLayers() { const map = this.leafletDirective.getMap(); if (null != map && null != this.layersDiffer) { const changes = this.layersDiffer.diff(this.layersValue); if (null != changes) { // Run outside angular to ensure layer events don't trigger change detection this.zone.runOutsideAngular(() => { changes.forEachRemovedItem((c) => { map.removeLayer(c.item); }); changes.forEachAddedItem((c) => { map.addLayer(c.item); }); }); } } } } LeafletLayersDirective.ɵ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 }); LeafletLayersDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.4", type: LeafletLayersDirective, selector: "[leafletLayers]", inputs: { layers: ["leafletLayers", "layers"] }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayersDirective, decorators: [{ type: Directive, args: [{ selector: '[leafletLayers]' }] }], ctorParameters: function () { return [{ type: LeafletDirective }, { type: i0.IterableDiffers }, { type: i0.NgZone }]; }, propDecorators: { layers: [{ type: Input, args: ['leafletLayers'] }] } }); class LeafletControlLayersChanges { constructor() { this.layersRemoved = 0; this.layersChanged = 0; this.layersAdded = 0; } changed() { return !(this.layersRemoved === 0 && this.layersChanged === 0 && this.layersAdded === 0); } } class LeafletControlLayersWrapper { constructor(zone, layersControlReady) { this.zone = zone; this.layersControlReady = layersControlReady; } getLayersControl() { return this.layersControl; } init(controlConfig, controlOptions) { const baseLayers = controlConfig.baseLayers || {}; const overlays = controlConfig.overlays || {}; // Create the control outside of angular to ensure events don't trigger change detection this.zone.runOutsideAngular(() => { this.layersControl = control.layers(baseLayers, overlays, controlOptions); }); this.layersControlReady.emit(this.layersControl); return this.layersControl; } applyBaseLayerChanges(changes) { let results = new LeafletControlLayersChanges(); if (null != this.layersControl) { results = this.applyChanges(changes, this.layersControl.addBaseLayer); } return results; } applyOverlayChanges(changes) { let results = new LeafletControlLayersChanges(); if (null != this.layersControl) { results = this.applyChanges(changes, this.layersControl.addOverlay); } return results; } applyChanges(changes, addFn) { const results = new LeafletControlLayersChanges(); if (null != changes) { // All layer management is outside angular to avoid layer events from triggering change detection this.zone.runOutsideAngular(() => { changes.forEachChangedItem((c) => { this.layersControl.removeLayer(c.previousValue); addFn.call(this.layersControl, c.currentValue, c.key); results.layersChanged++; }); changes.forEachRemovedItem((c) => { this.layersControl.removeLayer(c.previousValue); results.layersRemoved++; }); changes.forEachAddedItem((c) => { addFn.call(this.layersControl, c.currentValue, c.key); results.layersAdded++; }); }); } return results; } } class LeafletControlLayersConfig { constructor() { this.baseLayers = {}; this.overlays = {}; } } /** * Layers Control * * This directive is used to configure the layers control. The input accepts an object with two * key-value maps of layer name -> layer. Mutable changes are detected. On changes, a differ is * used to determine what changed so that layers are appropriately added or removed. * * To specify which layer to show as the 'active' baselayer, you will want to add it to the map * using the layers directive. Otherwise, the last one it sees will be used. */ class LeafletLayersControlDirective { constructor(leafletDirective, differs, zone) { this.differs = differs; this.zone = zone; this.layersControlReady = new EventEmitter(); this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective); this.controlLayers = new LeafletControlLayersWrapper(this.zone, this.layersControlReady); // Generate differs this.baseLayersDiffer = this.differs.find({}).create(); this.overlaysDiffer = this.differs.find({}).create(); } set layersControlConfig(v) { // Validation/init stuff if (null == v) { v = new LeafletControlLayersConfig(); } if (null == v.baseLayers) { v.baseLayers = {}; } if (null == v.overlays) { v.overlays = {}; } // Store the value this.layersControlConfigValue = v; // Update the map this.updateLayers(); } get layersControlConfig() { return this.layersControlConfigValue; } ngOnInit() { // Init the map this.leafletDirective.init(); // Set up control outside of angular to avoid change detection when using the control this.zone.runOutsideAngular(() => { // Set up all the initial settings this.controlLayers .init({}, this.layersControlOptions) .addTo(this.leafletDirective.getMap()); }); this.updateLayers(); } ngOnDestroy() { this.layersControlConfig = { baseLayers: {}, overlays: {} }; this.controlLayers.getLayersControl().remove(); } ngDoCheck() { this.updateLayers(); } updateLayers() { const map = this.leafletDirective.getMap(); const layersControl = this.controlLayers.getLayersControl(); if (null != map && null != layersControl) { // Run the baselayers differ if (null != this.baseLayersDiffer && null != this.layersControlConfigValue.baseLayers) { const changes = this.baseLayersDiffer.diff(this.layersControlConfigValue.baseLayers); this.controlLayers.applyBaseLayerChanges(changes); } // Run the overlays differ if (null != this.overlaysDiffer && null != this.layersControlConfigValue.overlays) { const changes = this.overlaysDiffer.diff(this.layersControlConfigValue.overlays); this.controlLayers.applyOverlayChanges(changes); } } } } LeafletLayersControlDirective.ɵ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 }); LeafletLayersControlDirective.ɵ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 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletLayersControlDirective, decorators: [{ type: Directive, args: [{ selector: '[leafletLayersControl]' }] }], ctorParameters: function () { return [{ type: LeafletDirective }, { type: i0.KeyValueDiffers }, { type: i0.NgZone }]; }, propDecorators: { layersControlConfig: [{ type: Input, args: ['leafletLayersControl'] }], layersControlOptions: [{ type: Input, args: ['leafletLayersControlOptions'] }], layersControlReady: [{ type: Output, args: ['leafletLayersControlReady'] }] } }); /** * Baselayers directive * * This directive is provided as a convenient way to add baselayers to the map. The input accepts * a key-value map of layer name -> layer. Mutable changed are detected. On changes, a differ is * used to determine what changed so that layers are appropriately added or removed. This directive * will also add the layers control so users can switch between available base layers. * * To specify which layer to show as the 'active' baselayer, you will want to add it to the map * using the layers directive. Otherwise, the plugin will use the last one it sees. */ class LeafletBaseLayersDirective { constructor(leafletDirective, differs, zone) { this.differs = differs; this.zone = zone; // Output for once the layers control is ready this.layersControlReady = new EventEmitter(); this.leafletDirective = new LeafletDirectiveWrapper(leafletDirective); this.controlLayers = new LeafletControlLayersWrapper(this.zone, this.layersControlReady); this.baseLayersDiffer = this.differs.find({}).create(); } // Set/get baseLayers set baseLayers(v) { this.baseLayersValue = v; this.updateBaseLayers(); } get baseLayers() { return this.baseLayersValue; } ngOnDestroy() { this.baseLayers = {}; if (null != this.controlLayers.getLayersControl()) { this.controlLayers.getLayersControl().remove(); } } ngOnInit() { // Init the map this.leafletDirective.init(); // Create the control outside angular to prevent events from triggering chnage detection this.zone.runOutsideAngular(() => { // Initially configure the controlLayers this.controlLayers .init({}, this.layersControlOptions) .addTo(this.leafletDirective.getMap()); }); this.updateBaseLayers(); } ngDoCheck() { this.updateBaseLayers(); } updateBaseLayers() { const map = this.leafletDirective.getMap(); const layersControl = this.controlLayers.getLayersControl(); if (null != map && null != layersControl && null != this.baseLayersDiffer) { const changes = this.baseLayersDiffer.diff(this.baseLayersValue); const results = this.controlLayers.applyBaseLayerChanges(changes); if (results.changed()) { this.syncBaseLayer(); } } } /** * Check the current base layer and change it to the new one if necessary */ syncBaseLayer() { const map = this.leafletDirective.getMap(); const layers = LeafletUtil.mapToArray(this.baseLayers); let foundLayer; // Search all the layers in the map to see if we can find them in the baselayer array map.eachLayer((l) => { foundLayer = layers.find((bl) => (l === bl)); }); // Did we find the layer? if (null != foundLayer) { // Yes - set the baselayer to the one we found this.baseLayer = foundLayer; } else { // No - set the baselayer to the first in the array and add it to the map if (layers.length > 0) { this.baseLayer = layers[0]; // Add layers outside of angular to prevent events from triggering change detection this.zone.runOutsideAngular(() => { this.baseLayer.addTo(map); }); } } } } LeafletBaseLayersDirective.ɵ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 }); LeafletBaseLayersDirective.ɵ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 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletBaseLayersDirective, decorators: [{ type: Directive, args: [{ selector: '[leafletBaseLayers]' }] }], ctorParameters: function () { return [{ type: LeafletDirective }, { type: i0.KeyValueDiffers }, { type: i0.NgZone }]; }, propDecorators: { baseLayers: [{ type: Input, args: ['leafletBaseLayers'] }], layersControlOptions: [{ type: Input, args: ['leafletLayersControlOptions'] }], layersControlReady: [{ type: Output, args: ['leafletLayersControlReady'] }] } }); class LeafletModule { } LeafletModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); LeafletModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule, declarations: [LeafletDirective, LeafletLayerDirective, LeafletLayersDirective, LeafletLayersControlDirective, LeafletBaseLayersDirective], exports: [LeafletDirective, LeafletLayerDirective, LeafletLayersDirective, LeafletLayersControlDirective, LeafletBaseLayersDirective] }); LeafletModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.4", ngImport: i0, type: LeafletModule, decorators: [{ type: NgModule, args: [{ exports: [ LeafletDirective, LeafletLayerDirective, LeafletLayersDirective, LeafletLayersControlDirective, LeafletBaseLayersDirective ], declarations: [ LeafletDirective, LeafletLayerDirective, LeafletLayersDirective, LeafletLayersControlDirective, LeafletBaseLayersDirective ] }] }] }); class LeafletTileLayerDefinition { constructor(type, url, options) { this.type = type; this.url = url; this.options = options; } /** * Creates a TileLayer from the provided definition. This is a convenience function * to help with generating layers from objects. * * @param layerDef The layer to create * @returns {TileLayer} The TileLayer that has been created */ static createTileLayer(layerDef) { let layer; switch (layerDef.type) { case 'xyz': layer = tileLayer(layerDef.url, layerDef.options); break; case 'wms': default: layer = tileLayer.wms(layerDef.url, layerDef.options); break; } return layer; } /** * Creates a TileLayer for each key in the incoming map. This is a convenience function * for generating an associative array of layers from an associative array of objects * * @param layerDefs A map of key to tile layer definition * @returns {{[p: string]: TileLayer}} A new map of key to TileLayer */ static createTileLayers(layerDefs) { const layers = {}; for (const k in layerDefs) { if (layerDefs.hasOwnProperty(k)) { layers[k] = (LeafletTileLayerDefinition.createTileLayer(layerDefs[k])); } } return layers; } /** * Create a Tile Layer from the current state of this object * * @returns {TileLayer} A new TileLayer */ createTileLayer() { return LeafletTileLayerDefinition.createTileLayer(this); } } /** * Generated bundle index. Do not edit. */ export { LeafletBaseLayersDirective, LeafletControlLayersChanges, LeafletControlLayersConfig, LeafletControlLayersWrapper, LeafletDirective, LeafletDirectiveWrapper, LeafletLayerDirective, LeafletLayersControlDirective, LeafletLayersDirective, LeafletModule, LeafletTileLayerDefinition, LeafletUtil }; //# sourceMappingURL=asymmetrik-ngx-leaflet.mjs.map