Source: viewer/loader/Map.js

import { WMSLayer } from '../../map/layer/WMSLayer/WMSLayer.js';
import { Map2D } from '../../map/Map2D.js';
import { MapPopup } from '../../map/popup/MapPopup.js';
import { BaselayerSwitcher } from '../../map/tools/BaselayerSwitcher.js';
import { ProjectionSwitcher } from '../../map/tools/ProjectionSwitcher.js';
import { CoordinateControl } from '../../map/tools/CoordinateControl.js';
import { DrawTools } from '../../map/tools/DrawTools.js';
import { FitToScreenButton } from '../../map/tools/FitToScreenButton.js';
import { LayerLegendButton } from '../../map/tools/LayerLegendButton.js';
import { ScreenshotButton } from '../../map/tools/ScreenshotButton.js';
import { ZoomButton } from '../../map/tools/ZoomButton.js';
import { Attribution } from '../../map/tools/Attribution.js';
import { ScaleControl } from '../../map/tools/ScaleControl.js';

/**
 * Loader for the Map module
 * 
 * @author sjaswal <shahzeib.jaswal@awi.de>
 * @author rhess <robin.hess@awi.de> 
 */

/**
 * A function that loads the Map
 * 
 * @memberof vef.viewer.loader
 * 
 * @param {object} options 
 * @param {Viewer} viewer 
 */
export function Map(options, viewer) {
    const map = new Map2D(null, options.options);

    initActiveArea_(map, options, viewer);
    if (options.popup) initPopup_(map, options, viewer);
    setBaseLayer_(map, viewer);
    initTools_(map, options.tools, viewer);

    viewer.addElement(options.id, map);
};

function initPopup_(map, options, viewer) {
    let sidebar = null;
    let sidebarGroup = null;
    let clickPopupVisible = false;

    const popup = new MapPopup(map, {
        showMore: !!(options.popup.targetSidebar && options.popup.sidebarGroup)
    });

    const clickListener = e => {
        if (map.editing) return;

        // sort layers according to LayerTree order (Feature based layers are always on top)
        const layers = map.getLayers();
        layers.sort((a, b) => {
            if ((a instanceof WMSLayer) && !(b instanceof WMSLayer)) return 1;
            if (!(a instanceof WMSLayer) && (b instanceof WMSLayer)) return -1;
            // index order needs to be inverted, because higher index layers are on top
            if ((a.zIndex_ < b.zIndex_)) return 1;
            if ((a.zIndex_ > b.zIndex_)) return -1;

            return 0;
        });

        popup.requestPopup(layers, e.lat, e.lng);
    }

    // dont open popup when clicking too fast (moslty double click)
    let previousClick = performance.now();
    let popupTimeout = null;
    map.on("click", e => {
        if (popupTimeout) {
            clearTimeout(popupTimeout);
            popupTimeout = null;
        }

        map.closePopup();
        const currentClick = performance.now();
        if ((currentClick - previousClick) > 500) {
            popupTimeout = setTimeout(() => clickListener(e), 200);
        }

        previousClick = currentClick;
    });

    // init the popup sidebar
    if (options?.popup?.targetSidebar && options?.popup?.sidebarGroup) {
        sidebar = viewer.elements[options.popup.targetSidebar];
        sidebarGroup = options.popup.sidebarGroup;
    }

    popup.on("show_popup", item => {
        if (sidebar && sidebarGroup) {
            sidebar.setContent(sidebarGroup, item.sidebar, true);
            sidebar.setTitle(sidebarGroup, item.sidebar.dataset.title || item.content.title || item.content.layer.title);
            item.onShowMore = () => sidebar.show(sidebarGroup);
        }
        clickPopupVisible = true;
    });

    // highlighting selected feature
    let popupVisible = false;
    map.addLayer(popup.selectedFeature);
    map.on("hide_popup", () => {
        if (!sidebar?.isOpen()) popup.selectedFeature.setData(null);
        popupVisible = false;
        clickPopupVisible = false;
    });
    map.on("show_popup", () => {
        popupVisible = true;
    });
    sidebar?.on("close", () => {
        if (!popupVisible) popup.selectedFeature.setData(null);
    })


    // feature hover popup
    const hoverPopup = new MapPopup(map, { showMore: false });
    viewer.layerManager.on("layermanager_layer_mouseover", e => {
        if (e.layer.hoverEvents && !clickPopupVisible) {
            const latlng = e.mouseEvent.latlng;
            hoverPopup.requestPopup([e.layer], latlng.lat, latlng.lng, map);
        }
    });

    viewer.layerManager.on("layermanager_layer_mouseout", e => {
        if (e.layer.hoverEvents && !clickPopupVisible) map.closePopup();
    });

    viewer.filters.on("change", () => map.closePopup());
    viewer.filters.setMap(map);
}

function initTools_(map, tools, viewer) {
    // push to the end of the current eventqueue to allow loading all other events
    // because a some tools rely on other Ui Elements already being initialized
    setTimeout(() => {
        for (let i = 0; i < tools.length; ++i) {
            switch (tools[i].type.toLowerCase()) {
                case "attribution":
                    new Attribution(map, tools[i].position);
                    break;
                case "zoom":
                    new ZoomButton(map, tools[i].position);
                    break;
                case "fittoscreen":
                    new FitToScreenButton(map, tools[i].position);
                    break;
                case "layerlegends":
                    if (viewer.elements[tools[i].targetTree]) {
                        new LayerLegendButton(map, tools[i].position, viewer.elements[tools[i].targetTree]);
                    }
                    break;
                case "screenshot":
                    new ScreenshotButton(map, tools[i].position);
                    break;
                case "drawtools":
                    new DrawTools(map, tools[i].position);
                    break;
                case "coordinatecontrol":
                    new CoordinateControl(map, tools[i].position);
                    break;
                case "baselayerswitcher":
                    const baselayerSwitcher = new BaselayerSwitcher(map, tools[i].position, viewer.baseLayers);
                    const infoSidebar = viewer.elements[tools[i].infoSidebar];

                    if (infoSidebar && tools[i].infoSidebarGroup) {
                        baselayerSwitcher.enableInfoButton();
                        baselayerSwitcher.on("show_info", (layer) => {
                            infoSidebar.setContent(tools[i].infoSidebarGroup, layer.printMetadata());
                            infoSidebar.setTitle(tools[i].infoSidebarGroup, layer.title);
                            infoSidebar.show(tools[i].infoSidebarGroup);
                        });
                    }
                    break;
                case "projectionswitcher":
                    new ProjectionSwitcher(map, tools[i].position, viewer.baseLayers, tools[i].projections);
                    break;
                case "scalecontrol":
                    new ScaleControl(map, tools[i].position);
                    break;
            }
        }
    }, 0);
}

function initActiveArea_(map, options, viewer) {
    if (options.activeAreaSidebar) {
        let sidebar = viewer.elements[options.activeAreaSidebar];
        let sidebarOpen = !sidebar.getElement().classList.contains("collapsed");

        const showActiveArea = (ignoreSidebarOpen = false, centerMap = false) => {
            if (ignoreSidebarOpen || !sidebarOpen) {
                map.setActiveArea("sidebar-active-area", centerMap);
            }
            sidebarOpen = true;
        };
        const showActiveAreaAlt = (ignoreSidebarOpen = false, centerMap = false) => {
            if (ignoreSidebarOpen || sidebarOpen) {
                map.setActiveArea("sidebar-active-area-alt", centerMap);
            }
            sidebarOpen = false;
        }

        sidebar.on("show", () => showActiveArea());
        sidebar.on("close", () => showActiveAreaAlt());
        sidebarOpen ? showActiveArea(true, true) : showActiveAreaAlt(true, true);
    }
}

// set initial baselayer as defined in the map config
function setBaseLayer_(map, viewer) {
    const key = map.options.baseLayer || Object.keys(viewer.baseLayers)[0];
    const crs = map.options.crs;

    if ((key in viewer.baseLayers) && (crs in viewer.baseLayers[key])) {
        const layer = viewer.baseLayers[key][crs];
        map.setBaseLayer(layer, null);
    }
}