import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Directive, EventEmitter, Component, Inject, ViewChild, ContentChild, Input, Output, SimpleChange, NgModule } from '@angular/core';
import * as i1$1 from '@c8y/ngx-components';
import { gettext, sortByPriority, ManagedObjectRealtimeService, globalAutoRefreshLoading, CountdownIntervalComponent, CommonModule as CommonModule$1, FormsModule as FormsModule$1, RealtimeModule, CoreModule } from '@c8y/ngx-components';
import * as i3 from '@ngx-translate/core';
import * as i4 from 'rxjs';
import { of, combineLatest, defer, NEVER, BehaviorSubject, Subject, fromEvent, EMPTY, merge, from } from 'rxjs';
import { map, first, takeUntil, scan, filter, switchMap, tap, mergeMap, catchError, debounceTime, skip } from 'rxjs/operators';
import * as i1 from '@c8y/client';
import { remove, isUndefined, flatten, every, isNull, isEmpty, cloneDeep } from 'lodash-es';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i1$2 from '@angular/common';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

function getC8yMarker(marker, asset, event) {
    marker.asset = asset;
    marker.event = event;
    return marker;
}
const MAP_TILE_LAYER = new InjectionToken('MAP_TILE_LAYER');
var ClusterSize;
(function (ClusterSize) {
    ClusterSize[ClusterSize["NONE"] = 0] = "NONE";
    ClusterSize[ClusterSize["FOUR"] = 1] = "FOUR";
    ClusterSize[ClusterSize["SIXTEEN"] = 2] = "SIXTEEN";
})(ClusterSize || (ClusterSize = {}));
var MapTenantOptionKeys;
(function (MapTenantOptionKeys) {
    MapTenantOptionKeys["CONFIG"] = "map-config";
    MapTenantOptionKeys["LAYERS"] = "map-layers";
})(MapTenantOptionKeys || (MapTenantOptionKeys = {}));
const MAP_DEFAULT_CONFIG = new InjectionToken('MAP_DEFAULT_CONFIG');
const defaultLayer = {
    layerUrl: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
    label: 'OpenStreetMap',
    priority: 1000,
    options: {
        maxZoom: 18,
        minZoom: 2,
        attribution: '&copy;<a href="http://www.openstreetmap.org/copyright" rel="noreferrer nofollow">OpenStreetMap</a>',
        noWrap: true
    }
};
const defaultMapConfig = {
    center: [51.23544, 6.79599], // Düsseldorf
    zoomLevel: 2
};

class MapService {
    /**
     * Returns asset icon status for highest alarm severity found in device object.
     * @param device Device that contains alarms information.
     * @returns Status string according to alarm severity
     */
    static getStatus(device) {
        if (!device.c8y_ActiveAlarmsStatus) {
            return 'text-muted';
        }
        if (device.c8y_ActiveAlarmsStatus.critical) {
            return 'status critical';
        }
        if (device.c8y_ActiveAlarmsStatus.major) {
            return 'status major';
        }
        if (device.c8y_ActiveAlarmsStatus.minor) {
            return 'status minor';
        }
        if (device.c8y_ActiveAlarmsStatus.warning) {
            return 'status warning';
        }
        return 'text-muted';
    }
    /**
     * @ignore: Only DI.
     */
    constructor(inventory, options, serviceRegistry) {
        this.inventory = inventory;
        this.options = options;
        this.serviceRegistry = serviceRegistry;
        /**
         * The devices that are maximal displayed in one cluster.
         */
        this.MAX_DEVICE_PER_CLUSTER = 200;
        /**
         * The count until the cluster is sized. There are a maximum of
         * three clusters: 1, 4 or 16.
         */
        this.CLUSTER_LEVEL_THRESHOLD = 500;
    }
    /**
     * Returns the leaflet instance used by the cumulocity core.
     */
    async getLeaflet() {
        const originalLeflet = window.L;
        const c8yLeafletInstance = (await import('leaflet')).default;
        c8yLeafletInstance.noConflict();
        window.L = originalLeflet;
        return c8yLeafletInstance;
    }
    /**
     * Verifies if a given managed object is a device with a position fragment.
     * @param mo The given managed object.
     */
    isPositionedDevice(mo) {
        return !!(mo?.c8y_IsDevice && this.hasPosition(mo));
    }
    /**
     * Verifies if a given managed object has a position fragment.
     * @param mo The given managed object.
     */
    hasPosition(mo) {
        return mo?.c8y_Position;
    }
    getMapTileLayerProviders() {
        const layerProviders = this.serviceRegistry.get('mapTileLayerHook');
        return layerProviders;
    }
    getMapTileLayersFromHookedProviders$(layerProviders) {
        if (!layerProviders.length) {
            return of([]);
        }
        const layers = combineLatest(layerProviders.map(provider => provider.getMapTileLayers$()));
        return layers.pipe(map(layers => layers.flat()));
    }
    /**
     * Returns the layers available in this application.
     * Layers are taken from plugins installed to this application.
     * In case none of the plugins override the default layers, the default layers are also considered.
     * @returns The layers.
     */
    getMapTileLayers$() {
        return defer(() => {
            const layerProviders = this.getMapTileLayerProviders();
            const overridesDefaultLayer = layerProviders.some(provider => provider.overridesDefaultLayer?.());
            const layersFromProviders = defer(() => this.getMapTileLayersFromHookedProviders$(layerProviders));
            if (overridesDefaultLayer) {
                return layersFromProviders;
            }
            return combineLatest([this.getDefaultLayers(), layersFromProviders]).pipe(map(layers => {
                return layers.flat();
            }));
        });
    }
    /**
     * Returns the layers configured in the current platform via tenant options.
     * @returns The layers. If not set in tenant options the default layers.
     */
    getDefaultLayers() {
        return this.getMapOption(MapTenantOptionKeys.LAYERS, this.options.mapLayers || [defaultLayer]);
    }
    /**
     * Returns the map configuration configured on the tenant.
     * @returns The configuration. If not set in tenant options the default configuration.
     */
    getDefaultConfig() {
        return this.getMapOption(MapTenantOptionKeys.CONFIG, this.options.mapConfig || defaultMapConfig);
    }
    /**
     * Counts all managed objects in a given bound with a c8y_Position fragment.
     * @param bound The lat lng bound to request the managed objects for.
     * @param byGroupIdMO The group managed object of which direct children should be searched for.
     * @returns The number of all position managed objects in the given bound (and group).
     */
    async getPositionMOsFromBoundCount(bound, byGroupIdMO) {
        return this.getPositionMOsFromBound(bound, byGroupIdMO, true);
    }
    async getPositionMOsFromBound(bound, byGroupIdMO, count = false) {
        const { lat: latMin, lng: lngMin } = bound.getSouthWest();
        const { lat: latMax, lng: lngMax } = bound.getNorthEast();
        const byGroupIdFilter = byGroupIdMO
            ? `(bygroupid(${byGroupIdMO.id}) or id eq '${byGroupIdMO.id}') and `
            : '';
        const boundFilter = `$filter=${byGroupIdFilter}has(c8y_Position) and c8y_Position.lng gt ${lngMin}d and c8y_Position.lat gt ${latMin}d and c8y_Position.lng lt ${lngMax}d and c8y_Position.lat lt ${latMax}d`;
        const { paging, data } = await this.inventory.list({
            pageSize: count ? 1 : this.MAX_DEVICE_PER_CLUSTER,
            withTotalPages: count,
            query: boundFilter
        });
        if (count) {
            return paging.totalPages;
        }
        return data;
    }
    /**
     * Returns all devices with c8y_Position.
     * @param pageSize The page size to return.
     * @param count Switches to counting only.
     * @returns All devices or the device count with a c8y_Position fragment.
     */
    async getPositionDevices(pageSize = this.MAX_DEVICE_PER_CLUSTER, count) {
        const { paging, data } = await this.inventory.list({
            pageSize: count ? 1 : pageSize,
            withTotalPages: !!count,
            query: '$filter=has(c8y_Position) and has(c8y_IsDevice)'
        });
        if (count) {
            return paging.totalPages;
        }
        return data;
    }
    /**
     * Returns all managed object with a c8y_Position fragment.
     * @param byGroupIdMO The group managed object of which direct children should be searched for.
     * @param pageSize Defines how many results should be returned.
     * @returns The managed objects with position.
     */
    async getAllPositionMOs(byGroupIdMO, pageSize = 500) {
        const filter = {
            pageSize,
            withTotalPages: true,
            query: 'has(c8y_Position)'
        };
        if (byGroupIdMO) {
            filter.query = `$filter=(bygroupid(${byGroupIdMO.id}) or id eq '${byGroupIdMO.id}') and has(c8y_Position)`;
        }
        const { paging, data, res } = await this.inventory.list(filter);
        return {
            res,
            paging: paging,
            data: data
        };
    }
    /**
     * Returns the cluster size for clustered maps. Counting the position MOs in a bounding
     * and if it reach a threshold, returning a [[ClusterSize]].
     * @param bound The bounding to check for cluster size.
     * @returns The cluster size, can be NONE, FOUR or SIXTEEN.
     */
    async getClusterSize(bound) {
        const count = await this.getPositionMOsFromBoundCount(bound);
        let clusterSize = ClusterSize.NONE;
        if (count > this.CLUSTER_LEVEL_THRESHOLD) {
            clusterSize = ClusterSize.SIXTEEN;
        }
        else if (count > this.MAX_DEVICE_PER_CLUSTER) {
            clusterSize = ClusterSize.FOUR;
        }
        return clusterSize;
    }
    getMapOption(key, defaultValue) {
        return defer(() => this.options.getTenantOption('configuration', key, defaultValue)).pipe(map(config => {
            if (typeof config === 'string') {
                console.error(`The tenant option for maps 'configuration.${key}' is not a valid JSON structure.`);
                return defaultValue;
            }
            return config;
        }));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapService, deps: [{ token: i1.InventoryService }, { token: i1$1.OptionsService }, { token: i1$1.ServiceRegistry }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root'
                }]
        }], ctorParameters: () => [{ type: i1.InventoryService }, { type: i1$1.OptionsService }, { type: i1$1.ServiceRegistry }] });

class ClusterMap {
    set clusterMarker(item) {
        this.removeClusterToBigMarker();
        this._clusterMarker = item;
    }
    get clusterMarker() {
        return this._clusterMarker;
    }
    set rect(item) {
        if (this._rect) {
            this._rect.remove();
        }
        this._rect = item;
    }
    get rect() {
        return this._rect;
    }
    constructor(iterable, addAssetCallback, translateService) {
        this.iterable = iterable;
        this.addAssetCallback = addAssetCallback;
        this.translateService = translateService;
        this.markers = [];
        this.positions = [];
        this.iterableDiffer = this.iterable.find(this.positions).create(this.trackBy);
    }
    render(map) {
        if (this._rect) {
            this._rect.addTo(map);
        }
        this.updateChanges(map);
        if (this._clusterMarker) {
            this._clusterMarker.addTo(map);
        }
    }
    clear(map) {
        this.removeClusterToBigMarker();
        this._rect.remove();
        this.positions = [];
        this.updateChanges(map);
    }
    removeClusterToBigMarker() {
        if (this._clusterMarker) {
            this._clusterMarker.remove();
            this._clusterMarker = null;
        }
    }
    addMarkerToMap(device, map) {
        const marker = this.addAssetCallback(device);
        this.markers.push(marker);
        marker.addTo(map);
    }
    setClusterToBigMarker(map, count, leaflet) {
        const bound = this.rect.getBounds();
        const text = this.translateService.instant(gettext('Zoom in'));
        const divMarker = leaflet.divIcon({
            html: `<div class="c8y-map-marker-count" data-count="${count}" title="${text}"></div>`
        });
        const labelIcon = leaflet.marker(bound.getCenter(), {
            icon: divMarker
        });
        labelIcon.addTo(map);
        labelIcon.on('click', () => {
            map.fitBounds(bound);
        });
        this.clusterMarker = labelIcon;
    }
    updateChanges(map) {
        const changes = this.iterableDiffer.diff(this.positions);
        if (changes) {
            changes.forEachRemovedItem((record) => {
                this.removeMarkerFromMap(record.item);
            });
            changes.forEachAddedItem((record) => {
                this.addMarkerToMap(record.item, map);
            });
        }
    }
    trackBy(index, item) {
        const trackItems = [
            item.id,
            item.c8y_Position.lat,
            item.c8y_Position.lng,
            MapService.getStatus(item)
        ];
        return trackItems.join('');
    }
    removeMarkerFromMap(device) {
        const markers = this.markers.filter((marker) => marker.asset?.id === device.id);
        markers.forEach(marker => marker.remove());
    }
}

class MapPopupDirective {
    constructor(template, elementRef, viewContainer) {
        this.template = template;
        this.elementRef = elementRef;
        this.viewContainer = viewContainer;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapPopupDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ElementRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
    static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.9", type: MapPopupDirective, selector: "[c8yMapPopup]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapPopupDirective, decorators: [{
            type: Directive,
            args: [{
                    selector: '[c8yMapPopup]'
                }]
        }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ElementRef }, { type: i0.ViewContainerRef }] });

class MapComponent {
    constructor(moRealtimeService, mapService, layers$, defaultConfig$, translateService, geo, datePipe, widgetGlobalAutoRefreshService) {
        this.moRealtimeService = moRealtimeService;
        this.mapService = mapService;
        this.layers$ = layers$;
        this.defaultConfig$ = defaultConfig$;
        this.translateService = translateService;
        this.geo = geo;
        this.datePipe = datePipe;
        this.widgetGlobalAutoRefreshService = widgetGlobalAutoRefreshService;
        /**
         * The markers placed on the map.
         */
        this.markers = [];
        /**
         * Tells if the map was already initialized.
         */
        this.isInit = false;
        this.config = {};
        this.polyline$ = NEVER;
        this.onRealtimeUpdate = new EventEmitter();
        this.onMap = new BehaviorSubject(null);
        this.onInit = new EventEmitter();
        this.unsubscribeTrigger$ = new Subject();
        this.destroy$ = new Subject();
        this.initOutputs();
    }
    startRealtime() {
        if (!this.assets || (Array.isArray(this.assets) && this.assets.length > 1)) {
            this.config.realtime = false;
            this.stopRealtime();
            return;
        }
        const asset = Array.isArray(this.assets) ? this.assets[0] : this.assets;
        this.realtimeSubscription = this.moRealtimeService
            .onUpdate$(asset)
            .subscribe((asset) => {
            const marker = this.findMarker(asset.id);
            const icon = this.getAssetIcon(asset);
            marker.setIcon(icon);
            marker.setLatLng(this.geo.getLatLong(asset));
            if (Array.isArray(this.assets)) {
                this.assets[0] = asset;
            }
            else {
                this.assets = asset;
            }
            this.moveToPositionOfMo(asset);
            this.onRealtimeUpdate.emit(asset);
        });
    }
    moveToPositionOfMo(positions) {
        const position = Array.isArray(positions) ? positions[0] : positions;
        if (this.config.follow) {
            this.map.setView([position.c8y_Position.lat, position.c8y_Position.lng]);
        }
    }
    stopRealtime() {
        if (this.realtimeSubscription) {
            this.realtimeSubscription.unsubscribe();
        }
    }
    findMarker(moOrId) {
        const getId = moOrId => (typeof moOrId === 'string' ? moOrId : moOrId?.id);
        return this.markers.find((marker) => marker.asset?.id === getId(moOrId) || marker.event?.id === getId(moOrId));
    }
    addMarkerToMap(marker) {
        this.markers.push(marker);
        marker.addTo(this.map);
    }
    getAssetMarker(asset) {
        if (!asset) {
            return;
        }
        const icon = this.getAssetIcon(asset);
        const leafletMarker = this.leaflet.marker(this.geo.getLatLong(asset), {
            icon
        });
        const marker = getC8yMarker(leafletMarker, asset);
        this.bindPopup(marker, asset);
        return marker;
    }
    getTrackingMarker(event) {
        if (!event) {
            return;
        }
        const icon = this.getTrackingIcon();
        const leafletMarker = this.leaflet.marker(this.geo.getLatLong(event), { icon });
        const marker = getC8yMarker(leafletMarker, null, event);
        this.bindPopup(marker, event);
        return marker;
    }
    getAssetIcon(asset) {
        const assetTypeIcon = this.config.icon || asset.icon?.name;
        const status = MapService.getStatus(asset);
        const color = this.config.color ? `style='color: ${this.config.color};'` : '';
        const icon = this.leaflet.divIcon({
            html: `<div class="dlt-c8y-icon-marker icon-3x ${status}" ${color}><i class="dlt-c8y-icon-${assetTypeIcon || 'data-transfer'}" /></div>`,
            className: 'c8y-map-marker-icon',
            // iconAnchor is used to set the marker accurately on click
            iconAnchor: [8, 8]
        });
        return icon;
    }
    getTrackingIcon() {
        const icon = this.leaflet.divIcon({
            html: `<div class="dlt-c8y-icon-marker icon-3x text-muted"></div>`,
            className: 'c8y-map-marker-icon',
            // iconAnchor is used to set the marker accurately on click
            iconAnchor: [8, 8]
        });
        return icon;
    }
    removeMarker(marker) {
        if (marker) {
            marker.remove();
            remove(this.markers, m => m === marker);
        }
    }
    clearMarkers(fragment) {
        const matchingMarkers = marker => !fragment || !!marker[fragment];
        this.markers.filter(matchingMarkers).forEach(marker => marker.remove());
        this.markers = this.markers.filter(marker => !matchingMarkers(marker));
    }
    refreshMarkers() {
        this.clearMarkers();
        let assets = [];
        if (!isUndefined(this.assets)) {
            assets = Array.isArray(this.assets) ? this.assets : [this.assets];
        }
        assets.forEach(asset => {
            const marker = this.getAssetMarker(asset);
            this.addMarkerToMap(marker);
        });
        if (!this.config.center) {
            this.zoomToBound(assets);
        }
        this.toggleControls();
    }
    center() {
        this.map?.setView(this.config.center);
    }
    refresh() {
        if (this.isInit) {
            this.refreshMarkers();
        }
    }
    async ngAfterViewInit() {
        this.leaflet = await this.mapService.getLeaflet();
        const initialized$ = combineLatest([this.layers$, this.defaultConfig$]).pipe(first(), takeUntil(this.unsubscribeTrigger$));
        initialized$.subscribe(([layers, defaultConfig]) => {
            this.initMap(layers, defaultConfig);
            this.refreshMarkers();
        });
        combineLatest([this.polyline$, initialized$])
            .pipe(map(([expressions]) => this.leaflet.polyline(expressions, this.polylineOptions)), scan((oldPolyline, newPolyline) => {
            if (!!oldPolyline) {
                this.map.removeLayer(oldPolyline);
            }
            if (!!newPolyline) {
                newPolyline.addTo(this.map);
                this.fitBounds(newPolyline.getBounds());
            }
            return newPolyline;
        }, null), takeUntil(this.unsubscribeTrigger$))
            .subscribe();
    }
    ngOnChanges(changes) {
        if (!this.map) {
            return;
        }
        if (changes.assets?.currentValue && !changes.assets?.firstChange) {
            this.refreshMarkers();
        }
        if (changes.config?.currentValue && !changes.config?.firstChange) {
            this.changeConfig(changes.config);
        }
    }
    ngOnDestroy() {
        this.unsubscribeAllListeners();
        this.destroy$.next();
        this.destroy$.complete();
    }
    unsubscribeAllListeners() {
        this.unsubscribeTrigger$.next();
        this.stopRealtime();
    }
    initOutputs() {
        const getMapEventObservable = eventName => {
            return this.onMap.pipe(filter(map => !!map), switchMap(map => fromEvent(map, eventName)), takeUntilDestroyed());
        };
        const dragStart$ = getMapEventObservable('dragstart');
        const move$ = getMapEventObservable('move');
        const dragEnd$ = getMapEventObservable('dragend');
        this.onMove = dragStart$.pipe(switchMap(() => move$.pipe(takeUntil(dragEnd$))), takeUntil(this.unsubscribeTrigger$));
        this.onMoveEnd = getMapEventObservable('moveend');
        this.onZoomEnd = getMapEventObservable('zoomend');
        this.onZoomStart = getMapEventObservable('zoomstart');
    }
    initMap(layers, defaultConfig) {
        const defaultOptions = {
            center: this.config.center || defaultConfig.center,
            zoomSnap: 0,
            zoom: this.config.zoomLevel || defaultConfig.zoomLevel
        };
        if (this.map) {
            this.map.remove();
        }
        this.map = this.leaflet.map(this.mapElement.nativeElement, defaultOptions);
        this.map.attributionControl.setPrefix('');
        this.addLayers(layers);
        this.handleMobile();
        this.onMap.next(this.map);
        if (this.config.realtime) {
            this.startRealtime();
        }
        this.isInit = true;
        this.onInit.emit(this.leaflet);
    }
    handleMobile() {
        // adding event listener to do mobile 2 finger scrolling
        if (this.leaflet.Browser.mobile) {
            const touchMsg = this.translateService.instant(gettext('Use two fingers to move the map.'));
            this.map.dragging.disable();
            const container = this.map.getContainer();
            container.setAttribute('data-touch-warning-content', touchMsg);
            container.addEventListener('touchstart', event => this.handleTouch(event));
            container.addEventListener('touchmove', event => this.handleTouch(event));
            container.addEventListener('touchend', event => this.handleTouch(event));
            container.addEventListener('touchcancel', event => this.handleTouch(event));
            container.addEventListener('click', event => this.handleTouch(event));
        }
    }
    addLayers(layers) {
        const flattenLayers = flatten(layers);
        const baseLayers = {};
        const overlays = {};
        let firstLayer = true;
        for (const layer of sortByPriority(flattenLayers)) {
            const objectToAddTo = layer.isOverlay ? overlays : baseLayers;
            if (objectToAddTo[layer.label]) {
                continue;
            }
            const tiles = this.leaflet.tileLayer(layer.layerUrl, layer.options);
            if (!layer.isOverlay && firstLayer) {
                firstLayer = false;
                tiles.addTo(this.map);
            }
            objectToAddTo[layer.label] = tiles;
        }
        if (flattenLayers.length > 1) {
            this.leaflet.control.layers(baseLayers, overlays, { position: 'bottomleft' }).addTo(this.map);
        }
    }
    changeConfig(change) {
        if (this.hasChanged(change, 'zoomLevel')) {
            this.map.setZoom(this.config.zoomLevel);
        }
        if (this.hasChanged(change, 'center') && every(change.currentValue.center, p => !isNull(p))) {
            this.map.setView(change.currentValue.center);
        }
        if (this.hasChanged(change, 'icon') || this.hasChanged(change, 'color')) {
            this.refreshMarkers();
        }
        if (this.hasChanged(change, 'realtime') && change.currentValue.realtime) {
            this.startRealtime();
        }
        if (change.currentValue.realtime === false) {
            this.stopRealtime();
        }
        if (this.hasChanged(change, 'follow')) {
            this.moveToPositionOfMo(this.assets);
        }
        if (this.hasChanged(change, 'disablePan') || this.hasChanged(change, 'disableZoom')) {
            this.toggleControls();
        }
    }
    hasChanged(change, prop) {
        return change.currentValue[prop] !== change.previousValue[prop];
    }
    toggleControls() {
        if (this.config.disableZoom) {
            this.map.removeControl(this.map.zoomControl);
            this.map.scrollWheelZoom.disable();
        }
        else {
            this.map.addControl(this.map.zoomControl);
            this.map.scrollWheelZoom.enable();
        }
        if (this.config.disablePan) {
            this.map.dragging.disable();
        }
        else {
            this.map.dragging.enable();
        }
    }
    handleTouch(e) {
        // Disregard touch events on the minimap if present
        const ignoreList = [
            'leaflet-control-minimap',
            'leaflet-interactive',
            'leaflet-popup-content',
            'leaflet-popup-content-wrapper',
            'leaflet-popup-close-button',
            'leaflet-control-zoom-in',
            'leaflet-control-zoom-out'
        ];
        let ignoreElement = false;
        for (let i = 0; i < ignoreList.length; i++) {
            if (this.leaflet.DomUtil.hasClass(e.target, ignoreList[i])) {
                ignoreElement = true;
            }
        }
        const container = this.map.getContainer();
        if (ignoreElement) {
            if (this.leaflet.DomUtil.hasClass(e.target, 'leaflet-interactive') &&
                e.type === 'touchmove' &&
                e.touches.length === 1) {
                this.leaflet.DomUtil.addClass(container, 'touch-warning');
                this.map.dragging.disable();
            }
            else {
                this.leaflet.DomUtil.removeClass(container, 'touch-warning');
            }
            return;
        }
        if (e.type !== 'touchmove' && e.type !== 'touchstart') {
            this.leaflet.DomUtil.removeClass(container, 'touch-warning');
            return;
        }
        if (e.touches.length === 1) {
            this.leaflet.DomUtil.addClass(container, 'touch-warning');
            this.map.dragging.disable();
        }
        else {
            this.map.dragging.enable();
            this.leaflet.DomUtil.removeClass(container, 'touch-warning');
        }
    }
    zoomToBound(assets) {
        if (isEmpty(assets))
            return;
        const bounds = assets.map(asset => [
            asset.c8y_Position.lat,
            asset.c8y_Position.lng
        ]);
        if (assets.length > 1) {
            this.map.flyToBounds(bounds, { animate: false });
            return;
        }
        this.map.flyTo([assets[0].c8y_Position.lat, assets[0].c8y_Position.lng], this.map.options.zoom, {
            animate: false
        });
    }
    fitBounds(bounds) {
        if (bounds?.isValid()) {
            this.map.fitBounds(bounds, this.config.fitBoundsOptions);
        }
    }
    bindPopup(marker, context) {
        if (this.popup) {
            marker.on('click', () => {
                this.popup.viewContainer.clear();
                const view = this.popup.viewContainer.createEmbeddedView(this.popup.template, {
                    $implicit: context
                });
                view.detectChanges();
                marker
                    .unbindPopup()
                    .bindPopup(this.popup.elementRef.nativeElement.previousSibling, {
                    offset: [-3, -40],
                    maxWidth: 140,
                    autoPan: true,
                    closeButton: false
                })
                    .openPopup();
            });
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapComponent, deps: [{ token: i1$1.ManagedObjectRealtimeService }, { token: MapService }, { token: MAP_TILE_LAYER }, { token: MAP_DEFAULT_CONFIG }, { token: i3.TranslateService }, { token: i1$1.GeoService }, { token: i1$1.DatePipe }, { token: i1$1.WidgetGlobalAutoRefreshService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: MapComponent, selector: "c8y-map", inputs: { config: "config", assets: "assets", polyline$: "polyline$", polylineOptions: "polylineOptions" }, outputs: { onRealtimeUpdate: "onRealtimeUpdate", onMove: "onMove", onMoveEnd: "onMoveEnd", onZoomStart: "onZoomStart", onZoomEnd: "onZoomEnd", onMap: "onMap", onInit: "onInit" }, providers: [ManagedObjectRealtimeService], queries: [{ propertyName: "popup", first: true, predicate: MapPopupDirective, descendants: true }], viewQueries: [{ propertyName: "mapElement", first: true, predicate: ["map"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"c8y-map\">\n  <div #map></div>\n</div>\n<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-map', providers: [ManagedObjectRealtimeService], template: "<div class=\"c8y-map\">\n  <div #map></div>\n</div>\n<ng-content></ng-content>\n" }]
        }], ctorParameters: () => [{ type: i1$1.ManagedObjectRealtimeService }, { type: MapService }, { type: i4.Observable, decorators: [{
                    type: Inject,
                    args: [MAP_TILE_LAYER]
                }] }, { type: i4.Observable, decorators: [{
                    type: Inject,
                    args: [MAP_DEFAULT_CONFIG]
                }] }, { type: i3.TranslateService }, { type: i1$1.GeoService }, { type: i1$1.DatePipe }, { type: i1$1.WidgetGlobalAutoRefreshService }], propDecorators: { mapElement: [{
                type: ViewChild,
                args: ['map']
            }], popup: [{
                type: ContentChild,
                args: [MapPopupDirective]
            }], config: [{
                type: Input
            }], assets: [{
                type: Input
            }], polyline$: [{
                type: Input
            }], polylineOptions: [{
                type: Input
            }], onRealtimeUpdate: [{
                type: Output
            }], onMove: [{
                type: Output
            }], onMoveEnd: [{
                type: Output
            }], onZoomStart: [{
                type: Output
            }], onZoomEnd: [{
                type: Output
            }], onMap: [{
                type: Output
            }], onInit: [{
                type: Output
            }] } });

class ClusterMapComponent extends MapComponent {
    constructor(moRealtimeService, mapService, layers$, defaultConfig$, translateService, widgetGlobalAutoRefreshService, iterable, colorService, geo, datePipe) {
        super(moRealtimeService, mapService, layers$, defaultConfig$, translateService, geo, datePipe, widgetGlobalAutoRefreshService);
        this.moRealtimeService = moRealtimeService;
        this.mapService = mapService;
        this.layers$ = layers$;
        this.defaultConfig$ = defaultConfig$;
        this.translateService = translateService;
        this.widgetGlobalAutoRefreshService = widgetGlobalAutoRefreshService;
        this.iterable = iterable;
        this.colorService = colorService;
        this.isLoading$ = new BehaviorSubject(false);
        this.showClusterColor = false;
        this.mapChange = new EventEmitter();
        this.errorNotifier = new BehaviorSubject(null);
        this.reloadTrigger$ = new BehaviorSubject(false);
        this.clusters = [];
        this.EVENT_THROTTLE_TIME = 750;
    }
    async ngOnChanges(changes) {
        if (changes.config?.firstChange) {
            return;
        }
        if (changes.rootNode?.previousValue !== changes.rootNode?.currentValue) {
            this.changeRootNode(changes.rootNode.currentValue);
        }
        if (changes.config?.currentValue) {
            this.changeConfig(changes.config);
        }
    }
    changeConfig(change) {
        // on following, cancel reload to avoid stale state
        if (change.currentValue.follow === true) {
            this.cancelReload();
            this.isLoading$.next(false);
        }
        if (change.currentValue.refreshInterval !== change.previousValue.refreshInterval) {
            this.reload();
        }
        super.changeConfig(change);
    }
    async ngAfterViewInit() {
        if (!this.leaflet) {
            this.leaflet = await this.mapService.getLeaflet();
        }
        if (this.config.widgetInstanceGlobalAutoRefreshContext) {
            this.handleGlobalRefreshLoading();
        }
        combineLatest([this.layers$, this.defaultConfig$])
            .pipe(takeUntil(this.unsubscribeTrigger$))
            .subscribe(([layers, defaultConfig]) => {
            this.initMap(layers, defaultConfig);
            this.changeRootNode(this.rootNode);
            this.changeConfig(new SimpleChange({}, this.config, false));
        });
    }
    async reset() {
        this.ngOnDestroy();
        await this.ngAfterViewInit();
    }
    reload() {
        this.reloadTrigger$.next(true);
    }
    cancelReload() {
        this.reloadTrigger$.next(false);
    }
    listenToClusterAndIntervalChanges() {
        const countdownEnded$ = this.config.widgetInstanceGlobalAutoRefreshContext
            ? this.widgetGlobalAutoRefreshService.countdownActions.countdownEnded$.pipe(takeUntil(this.destroy$))
            : this.countdownIntervalComp
                ? this.countdownIntervalComp.countdownEnded.pipe(takeUntil(this.unsubscribeTrigger$))
                : EMPTY;
        const mapChange$ = this.getMapChangeObservable();
        merge(this.reloadTrigger$, mapChange$, countdownEnded$)
            .pipe(tap(value => {
            if (this.config.widgetInstanceGlobalAutoRefreshContext) {
                !this.isLeafletEventInterface(value) && this.isLoading$.next(true);
                return;
            }
            this.isLoading$.next(true);
            this.countdownIntervalComp?.stop(true);
        }), switchMap(value => value === false
            ? of([])
            : from(this.mapService.getClusterSize(this.map.getBounds())).pipe(mergeMap((clusterSize) => this.getClusterRects(clusterSize, this.map.getBounds())), mergeMap(rects => this.createOrUpdateCluster(rects)), catchError(error => {
                this.errorNotifier.next(error);
                return of([]);
            }))), takeUntil(this.unsubscribeTrigger$))
            .subscribe((clusters) => {
            clusters.forEach(cluster => cluster.render(this.map));
            this.isLoading$.next(false);
            if (this.config.widgetInstanceGlobalAutoRefreshContext) {
                return;
            }
            this.countdownIntervalComp?.start();
        });
    }
    listenToClusterMapChanges() {
        this.getMapChangeObservable().subscribe();
    }
    refreshMarkers() {
        if (this.assets) {
            super.refreshMarkers();
            return;
        }
        this.clusters.forEach(cluster => {
            cluster.clear(this.map);
        });
        this.reload();
    }
    changeRootNode(mo) {
        this.unsubscribeAllListeners();
        this.clearMarkers();
        this.clearClusters();
        const isPositionDevice = mo?.c8y_Position && mo?.c8y_IsDevice;
        if (isPositionDevice) {
            this.assets = mo;
            this.refreshMarkers();
            this.listenToClusterMapChanges();
        }
        else {
            this.assets = null;
            this.listenToClusterAndIntervalChanges();
            this.reload();
        }
    }
    async getClusterRects(levelThreshold = ClusterSize.FOUR, viewBounds, level = 0) {
        let rects = [];
        if (levelThreshold === ClusterSize.NONE) {
            const rect = await this.getRect(viewBounds);
            rects.push(rect);
            return rects;
        }
        if (level >= levelThreshold) {
            return rects;
        }
        level++;
        const { lat: x1, lng: y1 } = viewBounds.getSouthWest();
        const { lat: x2, lng: y2 } = viewBounds.getNorthEast();
        const newX2 = (x1 + x2) / 2;
        const newY2 = (y1 + y2) / 2;
        const bounds = [
            [
                [x1, y1],
                [newX2, newY2]
            ],
            [
                [newX2, newY2],
                [x2, y2]
            ],
            [
                [x1, newY2],
                [newX2, y2]
            ],
            [
                [newX2, y1],
                [x2, newY2]
            ]
        ];
        for (const bound of bounds) {
            const latLngBound = this.leaflet.latLngBounds(bound);
            const rect = await this.getRect(latLngBound);
            rects = [...rects, ...(await this.getClusterRects(levelThreshold, latLngBound, level))];
            if (level === levelThreshold) {
                rects.push(rect);
            }
        }
        return rects;
    }
    async getRect(latLngBound) {
        let color = 'none';
        if (this.showClusterColor) {
            color = await this.colorService.generateColor(latLngBound.toBBoxString());
        }
        const rect = this.leaflet.rectangle(latLngBound, {
            color,
            weight: color === 'none' ? 0 : 1,
            interactive: false
        });
        return rect;
    }
    clearClusters() {
        this.clusters.forEach(cluster => {
            cluster.clear(this.map);
        });
        this.clusters = [];
    }
    async updateCluster(cluster) {
        const clusterCount = await this.mapService.getPositionMOsFromBoundCount(cluster.rect.getBounds(), this.rootNode);
        if (clusterCount > this.mapService.MAX_DEVICE_PER_CLUSTER) {
            cluster.setClusterToBigMarker(this.map, clusterCount, this.leaflet);
            cluster.positions = [];
            return cluster;
        }
        cluster.removeClusterToBigMarker();
        cluster.positions = await this.mapService.getPositionMOsFromBound(cluster.rect.getBounds(), this.rootNode);
        return cluster;
    }
    createOrUpdateCluster(rects) {
        const isNew = rects.length !== this.clusters.length;
        if (isNew) {
            this.clearClusters();
        }
        const updatePromise = rects.map((rect, index) => {
            if (isNew) {
                const cluster = new ClusterMap(this.iterable, asset => this.getAssetMarker(asset), this.translateService);
                this.clusters.push(cluster);
            }
            this.clusters[index].rect = rect;
            return this.updateCluster(this.clusters[index]);
        });
        return Promise.all(updatePromise);
    }
    getMapChangeObservable() {
        return merge(fromEvent(this.map, 'move'), fromEvent(this.map, 'moveend')).pipe(debounceTime(this.EVENT_THROTTLE_TIME), tap(event => this.mapChange.emit(event)), takeUntil(this.unsubscribeTrigger$));
    }
    isLeafletEventInterface(LeafletEventObject) {
        return LeafletEventObject?.type !== undefined;
    }
    handleGlobalRefreshLoading() {
        this.isLoading$
            .pipe(skip(1), globalAutoRefreshLoading(this.widgetGlobalAutoRefreshService), takeUntil(this.destroy$))
            .subscribe();
        this.destroy$.subscribe({
            complete: () => this.isLoading$.value && this.widgetGlobalAutoRefreshService.decrementLoading()
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ClusterMapComponent, deps: [{ token: i1$1.ManagedObjectRealtimeService }, { token: MapService }, { token: MAP_TILE_LAYER }, { token: MAP_DEFAULT_CONFIG }, { token: i3.TranslateService }, { token: i1$1.WidgetGlobalAutoRefreshService }, { token: i0.IterableDiffers }, { token: i1$1.ColorService }, { token: i1$1.GeoService }, { token: i1$1.DatePipe }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: ClusterMapComponent, selector: "c8y-cluster-map", inputs: { config: "config", rootNode: "rootNode", assets: ["asset", "assets"], showClusterColor: "showClusterColor" }, outputs: { mapChange: "mapChange" }, providers: [ManagedObjectRealtimeService], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"c8y-map\">\n  <div #map></div>\n</div>\n<ng-content></ng-content>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: ClusterMapComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-cluster-map', providers: [ManagedObjectRealtimeService], template: "<div class=\"c8y-map\">\n  <div #map></div>\n</div>\n<ng-content></ng-content>\n" }]
        }], ctorParameters: () => [{ type: i1$1.ManagedObjectRealtimeService }, { type: MapService }, { type: i4.Observable, decorators: [{
                    type: Inject,
                    args: [MAP_TILE_LAYER]
                }] }, { type: i4.Observable, decorators: [{
                    type: Inject,
                    args: [MAP_DEFAULT_CONFIG]
                }] }, { type: i3.TranslateService }, { type: i1$1.WidgetGlobalAutoRefreshService }, { type: i0.IterableDiffers }, { type: i1$1.ColorService }, { type: i1$1.GeoService }, { type: i1$1.DatePipe }], propDecorators: { config: [{
                type: Input
            }], rootNode: [{
                type: Input
            }], assets: [{
                type: Input,
                args: ['asset']
            }], showClusterColor: [{
                type: Input
            }], mapChange: [{
                type: Output
            }] } });

class MapStatusComponent {
    constructor() {
        this.configChange = new EventEmitter();
        this.onUnfollow = new EventEmitter();
        this.buttonsConfig = {};
        this.centerMapButtonDisabled = true;
        this.destroy$ = new Subject();
    }
    ngOnInit() {
        this.initConfig = cloneDeep(this.config);
        this.buttonsConfig = Object.assign(this.defaultButtonsConfig(), this.buttonsConfig);
    }
    ngAfterViewInit() {
        if (!this.config.widgetInstanceGlobalAutoRefreshContext) {
            this.clusterMap.countdownIntervalComp = this.countdownIntervalComp;
        }
    }
    ngOnChanges(changes) {
        if (changes.clusterMap?.previousValue !== changes.clusterMap?.currentValue) {
            this.checkIfMapIsAlreadyCentered();
        }
    }
    center() {
        this.clusterMap.center();
    }
    reload() {
        this.clusterMap.reload();
    }
    cancelReload() {
        this.clusterMap.cancelReload();
    }
    toggleRealtime() {
        this.config = {
            ...this.config,
            realtime: !this.config.realtime
        };
        this.configChange.emit(this.config);
    }
    unfollow() {
        this.config = {
            ...this.clusterMap.config,
            follow: false
        };
        this.configChange.emit(this.config);
        this.onUnfollow.emit(this.config);
    }
    follow() {
        this.config = {
            ...this.config,
            follow: true
        };
        this.configChange.emit(this.config);
    }
    ngOnDestroy() {
        this.destroy$.next();
    }
    checkIfMapIsAlreadyCentered() {
        this.clusterMap.mapChange.pipe(takeUntil(this.destroy$)).subscribe((event) => {
            if (this.config?.center && event.sourceTarget?.getBounds) {
                const bounds = event.sourceTarget.getBounds();
                this.centerMapButtonDisabled = this.shouldDisableCenterButton(bounds);
            }
        });
    }
    /**
     * Checks if Center button should be disabled according to provided bounds.
     * Provided bounds contain coordinates of current map rectangle corners.
     * Center button should be disabled if distance between center coordinates (from config) and center of current bounds
     * is less than 1/4 of bounds dimensions.
     * To achieve it we just need to check if center coordinates (from config) are contained in the boundaries of
     * current bounds shrunk by 25%.
     * @param bounds Current map view boundaries.
     * @returns True if distance between config center and current boundaries center is bigger than 1/4 of boundaries dimensions
     */
    shouldDisableCenterButton(bounds) {
        const shrunkBounds = bounds.pad(-0.25);
        return shrunkBounds.contains(this.config.center);
    }
    defaultButtonsConfig() {
        return {
            realtime: { show: this.config.realtime || this.clusterMap.config.follow, disabled: false }
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapStatusComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.9", type: MapStatusComponent, selector: "c8y-map-status", inputs: { config: "config", clusterMap: "clusterMap", buttonsConfig: "buttonsConfig" }, outputs: { configChange: "configChange", onUnfollow: "onUnfollow" }, viewQueries: [{ propertyName: "countdownIntervalComp", first: true, predicate: CountdownIntervalComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"c8y-map-status\">\n  <div class=\"leaflet-touch\">\n    <div\n      class=\"leaflet-bar\"\n      role=\"group\"\n    >\n      <button\n        class=\"c8y-realtime\"\n        title=\"{{ 'Realtime' | translate }}\"\n        type=\"button\"\n        *ngIf=\"buttonsConfig.realtime.show\"\n        (click)=\"toggleRealtime()\"\n        [disabled]=\"buttonsConfig.realtime.disabled\"\n      >\n        <span\n          class=\"c8y-pulse\"\n          [ngClass]=\"{\n            active: clusterMap?.config.realtime,\n            inactive: !clusterMap?.config.realtime\n          }\"\n        ></span>\n      </button>\n\n      <c8y-countdown-interval\n        *ngIf=\"\n          !config.widgetInstanceGlobalAutoRefreshContext &&\n          clusterMap?.config.refreshInterval &&\n          !clusterMap?.config.follow\n        \"\n        [countdownInterval]=\"clusterMap?.config.refreshInterval\"\n      ></c8y-countdown-interval>\n\n      <button\n        title=\"{{ 'Cancel reload' | translate }}\"\n        type=\"button\"\n        *ngIf=\"(clusterMap?.isLoading$ | async) && !config.widgetInstanceGlobalAutoRefreshContext\"\n        (click)=\"cancelReload()\"\n      >\n        <i\n          class=\"icon-spin\"\n          c8yIcon=\"refresh\"\n        ></i>\n      </button>\n      <button\n        [title]=\"'Reload' | translate\"\n        type=\"button\"\n        *ngIf=\"\n          !clusterMap?.config.realtime &&\n          !clusterMap?.assets &&\n          !(clusterMap?.isLoading$ | async) &&\n          !config.widgetInstanceGlobalAutoRefreshContext\n        \"\n        (click)=\"reload()\"\n      >\n        <i c8yIcon=\"refresh\"></i>\n      </button>\n      <button\n        title=\"{{ 'Center map' | translate }}\"\n        type=\"button\"\n        (click)=\"center()\"\n        [disabled]=\"centerMapButtonDisabled || clusterMap?.config.follow\"\n      >\n        <i c8yIcon=\"target1\"></i>\n      </button>\n      <button\n        title=\"{{ 'Unfollow' | translate }}\"\n        type=\"button\"\n        *ngIf=\"clusterMap?.config.follow\"\n        (click)=\"unfollow()\"\n      >\n        <i c8yIcon=\"marker-off\"></i>\n      </button>\n\n      <button\n        title=\"{{ 'Follow' | translate }}\"\n        type=\"button\"\n        *ngIf=\"initConfig.follow && !clusterMap?.config.follow\"\n        (click)=\"follow()\"\n      >\n        <i c8yIcon=\"marker\"></i>\n      </button>\n    </div>\n  </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: i1$1.CountdownIntervalComponent, selector: "c8y-countdown-interval", inputs: ["countdownInterval"], outputs: ["countdownEnded"] }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapStatusComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-map-status', template: "<div class=\"c8y-map-status\">\n  <div class=\"leaflet-touch\">\n    <div\n      class=\"leaflet-bar\"\n      role=\"group\"\n    >\n      <button\n        class=\"c8y-realtime\"\n        title=\"{{ 'Realtime' | translate }}\"\n        type=\"button\"\n        *ngIf=\"buttonsConfig.realtime.show\"\n        (click)=\"toggleRealtime()\"\n        [disabled]=\"buttonsConfig.realtime.disabled\"\n      >\n        <span\n          class=\"c8y-pulse\"\n          [ngClass]=\"{\n            active: clusterMap?.config.realtime,\n            inactive: !clusterMap?.config.realtime\n          }\"\n        ></span>\n      </button>\n\n      <c8y-countdown-interval\n        *ngIf=\"\n          !config.widgetInstanceGlobalAutoRefreshContext &&\n          clusterMap?.config.refreshInterval &&\n          !clusterMap?.config.follow\n        \"\n        [countdownInterval]=\"clusterMap?.config.refreshInterval\"\n      ></c8y-countdown-interval>\n\n      <button\n        title=\"{{ 'Cancel reload' | translate }}\"\n        type=\"button\"\n        *ngIf=\"(clusterMap?.isLoading$ | async) && !config.widgetInstanceGlobalAutoRefreshContext\"\n        (click)=\"cancelReload()\"\n      >\n        <i\n          class=\"icon-spin\"\n          c8yIcon=\"refresh\"\n        ></i>\n      </button>\n      <button\n        [title]=\"'Reload' | translate\"\n        type=\"button\"\n        *ngIf=\"\n          !clusterMap?.config.realtime &&\n          !clusterMap?.assets &&\n          !(clusterMap?.isLoading$ | async) &&\n          !config.widgetInstanceGlobalAutoRefreshContext\n        \"\n        (click)=\"reload()\"\n      >\n        <i c8yIcon=\"refresh\"></i>\n      </button>\n      <button\n        title=\"{{ 'Center map' | translate }}\"\n        type=\"button\"\n        (click)=\"center()\"\n        [disabled]=\"centerMapButtonDisabled || clusterMap?.config.follow\"\n      >\n        <i c8yIcon=\"target1\"></i>\n      </button>\n      <button\n        title=\"{{ 'Unfollow' | translate }}\"\n        type=\"button\"\n        *ngIf=\"clusterMap?.config.follow\"\n        (click)=\"unfollow()\"\n      >\n        <i c8yIcon=\"marker-off\"></i>\n      </button>\n\n      <button\n        title=\"{{ 'Follow' | translate }}\"\n        type=\"button\"\n        *ngIf=\"initConfig.follow && !clusterMap?.config.follow\"\n        (click)=\"follow()\"\n      >\n        <i c8yIcon=\"marker\"></i>\n      </button>\n    </div>\n  </div>\n</div>\n" }]
        }], propDecorators: { config: [{
                type: Input
            }], configChange: [{
                type: Output
            }], onUnfollow: [{
                type: Output
            }], clusterMap: [{
                type: Input
            }], buttonsConfig: [{
                type: Input
            }], countdownIntervalComp: [{
                type: ViewChild,
                args: [CountdownIntervalComponent]
            }] } });

class MapModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.9", ngImport: i0, type: MapModule, declarations: [MapComponent, MapStatusComponent, ClusterMapComponent, MapPopupDirective], imports: [CommonModule, FormsModule, CommonModule$1, FormsModule$1, RealtimeModule, CoreModule], exports: [MapComponent, MapStatusComponent, ClusterMapComponent, MapPopupDirective] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapModule, providers: [
            {
                provide: MAP_TILE_LAYER,
                useFactory: (mapService) => {
                    return mapService.getMapTileLayers$();
                },
                deps: [MapService]
            },
            {
                provide: MAP_DEFAULT_CONFIG,
                useFactory: (mapService) => {
                    return mapService.getDefaultConfig();
                },
                deps: [MapService]
            }
        ], imports: [CommonModule, FormsModule, CommonModule$1, FormsModule$1, RealtimeModule, CoreModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImport: i0, type: MapModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [MapComponent, MapStatusComponent, ClusterMapComponent, MapPopupDirective],
                    imports: [CommonModule, FormsModule, CommonModule$1, FormsModule$1, RealtimeModule, CoreModule],
                    exports: [MapComponent, MapStatusComponent, ClusterMapComponent, MapPopupDirective],
                    providers: [
                        {
                            provide: MAP_TILE_LAYER,
                            useFactory: (mapService) => {
                                return mapService.getMapTileLayers$();
                            },
                            deps: [MapService]
                        },
                        {
                            provide: MAP_DEFAULT_CONFIG,
                            useFactory: (mapService) => {
                                return mapService.getDefaultConfig();
                            },
                            deps: [MapService]
                        }
                    ]
                }]
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { ClusterMap, ClusterMapComponent, ClusterSize, MAP_DEFAULT_CONFIG, MAP_TILE_LAYER, MapComponent, MapModule, MapPopupDirective, MapService, MapStatusComponent, MapTenantOptionKeys, defaultLayer, defaultMapConfig, getC8yMarker };
//# sourceMappingURL=c8y-ngx-components-map.mjs.map
