Source: map/Map.js

import { UiElement } from '../ui/UiElement.js';
import "./Map.css";

export { Map };

/**
 * Base class for specific map implementations
 *
 * @author sjaswal <shahzeib.jaswal@awi.de>
 * @author rkoppe <roland.koppe@awi.de>
 * @author rhess <robin.hess@awi.de>
 * 
 * @memberof vef.map
 */
class Map extends UiElement {

    /** 
     * @param {string/HTMLElement} target id or HTMLElement
     * @param {Object} options options object
     */
    constructor(target, options) {
        super(target, {
            "map_add_layer": [],
            "map_remove_layer": [],
            "projection_change": [],
            "baselayer_change": [],
            "create_layer": [],
            "show_popup": [],
            "hide_popup": [],
            "zoom_end": [],
            "click": [],
            "mousemove": [],
            "move_end": []
        });

        this.options = Object.assign({
            crs: "EPSG:4326",
            baseLayer: null,
            initialZoom: 2,
            initialCenter: { lat: 0, lng: -25 },
            minZoom: null,
            maxZoom: 30,
            maxBounds: null,
            autoClosePopups: true,
            ctrlToZoom: false,
            responsivePopup: true,
            doubleClickZoom: true
        }, options);

        this.layers_ = [];
        this.baseLayer_ = null;
        this.mapContainer_ = null;
        this.toolOverlay_ = null;

        this.editing = false;
        this.shapeDrawer_ = null;

        this.initContainers_();
    }

    /**
     * initialize div containers
     */
    initContainers_() {
        // init containers
        const element = this.getElement();
        element.classList.add("vef-map");

        this.mapContainer_ = document.createElement("div");
        this.mapContainer_.classList.add("map-container");

        this.toolOverlay_ = document.createElement("div");
        this.toolOverlay_.classList.add("tool-container");
        this.toolOverlay_.innerHTML = `
            <div class="tools-top-left"></div>
            <div class="tools-top-right"></div>
            <div class="tools-bottom-left"></div>
            <div class="tools-bottom-right"></div>
        `;

        element.appendChild(this.mapContainer_);
        element.appendChild(this.toolOverlay_);
    }

    static getFitToScreenBounds(crs) {
        const bounds = {
            min: { x: -1, y: -90 },
            max: { x: 1, y: 90 },
            crs: "CRS:84"
        };

        switch (crs.replace(":", "")) {
            case "EPSG3995":
                bounds.min = { x: -179, y: 30 };
                bounds.max = { x: 1, y: 30 };
                break;
            case "EPSG3031":
                bounds.min = { x: -1, y: -30 };
                bounds.max = { x: 179, y: -30 };
                break;
        }

        return bounds
    }

    /**
     * Fill the entire screen (map height) with the full map extend
     */
    fitToScreen() {
        const bounds = Map.getFitToScreenBounds(this.options.crs);
        this.fitBounds(bounds);
    }

    /**
     * Add a tool to the tool overlay
     * 
     * @param {UiElement | HTMLElement} tool
     * @param {string} position top-left, top-right, bottom-left, bottom-right
     */
    addTool(tool, position) {
        const target = this.toolOverlay_.querySelector(".tools-" + position);
        if (target) {
            target.appendChild((tool instanceof UiElement) ? tool.getElement() : tool);
        }
    }

    /**
     * Wrapper method for showing a popup on the map
     * @param {number} lat 
     * @param {number} lng 
     * @param {string} content 
     */
    showPopup(lat, lng, content) { }

    /**
     * Close the currently opened popup
     */
    closePopup() { }

    /**
     * @param {object} bbox { min: {x, y}, max: {x, y}, crs: "CRS:84" }
     */
    fitBounds(bbox) { }

    /**
     * @param {object} bbox { min: {x, y}, max: {x, y}, crs: "CRS:84" }
     */
    setMaxBounds(bbox) { }

    /**
     * @param {number[]} latLng
     * @param {number} zoom 
     */
    setView(latLng, zoom) { }

    /**
     * Get the current bounds of the map
     * 
     * @returns {object} { min: {x, y}, max: {x, y}, crs: "CRS:84" }
     */
    getBounds() { }

    /**
     * Wrapper method to zoom in one step
     */
    zoomIn() { }

    /**
     * Wrapper method to zoom out one step
     */
    zoomOut() { }

    /**
     * Wrapper method to get the current zoom of the map
     * 
     * @returns {number} zoom level
     */
    getZoom() { }

    /**
     * Wrapper method to get the min zoom of the map
     * 
     * @returns {number} min zoom level
     */
    getMinZoom() { }

    /**
     * Wrapper method to get the max zoom of the map
     * 
     * @returns {number} max zoom level
     */
    getMaxZoom() { }

    /**
     * Set baselayer and projection. Crs and layer need to match.
     * Either option can be ommited, if the current baselayer/crs
     * matches the changed property
     * 
     * @param {Layer} layer 
     * @param {string} crs 
     */
    setBaseLayer(layer, crs) { }

    /**
     * Generic Add Layer method. Only adds a layer if the map crs code matches
     * 
     * @param {Layer} layer
     */
    addLayer(layer) { }

    /**
     * Generic Remove Layer method
     * 
     * @param {Layer} layer
     */
    removeLayer(layer) { }

    /**
     * Get the current map options
     * 
     * @returns  {object} options
     */
    getOptions() {
        return {
            options: Object.assign({}, this.options)
        };
    }

    /**
     * sets the active area div
     * 
     * @param {string} className css class name
     * @param {boolean} centerMap automatically re-center the map (default = false)
     */
    setActiveArea(className, centerMap) { }

    /**
     * Build a bounding box from a click location
     * for getFeatureInfo requests
     * 
     * @param {number} lat click lat
     * @param {number} lng click lng
     * @param {number} width in pixels
     * @param {number} height in pixels
     */
    getFeatureInfoOptions(lat, lng) { }

    /**
     * Start drawing shapes on the map
     * 
     * @param {string} shape rectangle, polygon, line, point
     */
    startDrawing(shape) { }

    /**
     * Abort drawing
     */
    stopDrawing() { }

    /**
     * Get layers added to the map
     * @returns {Layer[]} list of layers
     */
    getLayers() {
        // return shallow copy
        return [...this.layers_]
    }

    /**
     * Get the current baselayer
     * @returns {Layer} baseLayer
     */
    getBaseLayer() {
        return (this.baseLayer_) ? this.baseLayer_ : null;
    }

}