UNPKG

geopf-extensions-openlayers

Version:

French Geoportal Extensions for OpenLayers libraries

1,216 lines (1,135 loc) 106 kB
// import CSS import "../../CSS/Controls/LayerSwitcher/GPFlayerSwitcher.css"; // import "../../CSS/Controls/LayerSwitcher/GPFlayerSwitcherStyle.css"; // import OpenLayers // import Control from "ol/control/Control"; import Widget from "../Widget"; import Control from "../Control"; import Map from "ol/Map"; import Layer from "ol/layer/Layer"; import WMTSSource from "ol/source/WMTS"; import TileWMSSource from "ol/source/TileWMS"; import ImageSource from "ol/source/Image"; import { unByKey as olObservableUnByKey } from "ol/Observable"; import { intersects as olIntersects } from "ol/extent"; import { transformExtent as olTransformExtentProj } from "ol/proj"; import VectorLayer from "ol/layer/Vector"; import TileLayer from "ol/layer/Tile"; import VectorTileLayer from "ol/layer/VectorTile"; import VectorTileSource from "ol/source/VectorTile"; import { applyStyle } from "ol-mapbox-style"; // import local import Utils from "../../Utils/Helper"; import SelectorID from "../../Utils/SelectorID"; import Logger from "../../Utils/LoggerByDefault"; import Config from "../../Utils/Config"; import ToolTips from "../../Utils/ToolTips"; // DOM import LayerSwitcherDOM from "./LayerSwitcherDOM"; var logger = Logger.getLogger("layerswitcher"); /** * @typedef {Object} LayerSwitcherOptions * @property {string} [id] - Identifiant unique du widget. * @property {boolean} [collapsed=true] - Définit si le widget est replié au chargement. * @property {boolean} [draggable=false] - Permet de déplacer le panneau du LayerSwitcher. * @property {boolean} [counter=false] - Affiche un compteur du nombre de couches visibles. * @property {boolean} [panel=false] - Affiche un en-tête (header) dans le panneau du LayerSwitcher. * @property {boolean} [gutter=false] - Ajoute ou retire l’espace autour du panneau. * @property {boolean} [allowEdit=true] - Affiche le bouton d’édition pour les couches éditables (vecteur). * @property {boolean} [allowGrayScale=true] - Affiche le bouton N&B (niveaux de gris) pour les couches compatibles. * @property {boolean} [allowDraggable=true] - Permet de déplacer les couches. * @property {boolean} [allowDelete=true] - Affiche le bouton de suppression de la couche. * @property {boolean} [allowTooltips=false] - Active l’affichage des info-bulles (tooltips) sur les éléments du widget. * @property {string} [position] - Position CSS du widget sur la carte. * @property {Array<HeaderButton>} [headerButtons] - Liste d’outils personnalisés à afficher pour chaque couche. * @property {Array<AdvancedToolOption>} [advancedTools] - Liste d’outils personnalisés à afficher pour chaque couche. * Par défaut, les boutons d'info, de style, noir et blanc et recentrer sont ajoutés. */ /** * Option d'un outil personnalisé * @typedef {Object} AdvancedToolOption * @property {String} [label] - Optionnel. Label du bouton * @property {String} [key] - Optionnel. Mot clé indiquant qu'il s'agit d'une fonctionnalité native du layer switcher * @property {String} [icon] - Optionnel. Icône de l'outil. Peut être un lien html, svg ou une classe. * @property {String} [className] - Optionnel. Classes à appliquer en plus sur le bouton. * @property {Object} [attributes] - Optionnel. Attributs additionnels à ajouter au bouton. * Attributs ajoutés avec la méthode `setAttribute`. * @property {Array<import("ol/layer/Base").default|String>} [accepted] - Optionnel. Définit les types de couche pour lesquelles l'outil fonctionne. * Par défaut, ne filtre pas sur le type de couche. * Le constructeur ou le nom du constructeur peut être donné en argument. * Ne fonctionne pas pour les fonctionnalités déjà existantes. * @property {AdvancedToolCallback} [cb] - Optionnel. Callback au click sur l'outil. * @property {Object} [styles] - Optionnel. Styles à appliquer. */ /** * Callback au clic sur un outil personnalisé. * @callback AdvancedToolCallback * @param {PointerEvent} e Événement générique au clic sur l'outil * @param {LayerSwitcher} layerSwitcher instance du gestionnaire de couche * @param {import('ol/layer').Layer} layer Couche associée à l'outil * @param {Object} options Options de la couche associée */ /** * Bouton pour le gestionnaire de couche * @typedef {Object} HeaderButton * @property {String} label - Label du bouton. * @property {HeaderButtonCallback} cb - Callback au click sur l'outil. * @property {String} [id] - Id à ajouter sur le bouton. * @property {String} [className] - Optionnel. Classes à appliquer en plus sur le bouton. * @property {Object} [attributes] - Optionnel. Attributs additionnels à ajouter au bouton. * Attributs ajoutés avec la méthode `setAttribute`. * @property {String} [title] - Optionnel. Titre du bouton. Aucun par défaut. * @property {String} [icon] - Optionnel. Icône de l'outil. Classe à ajouter au bouton. */ /** * Callback au clic sur un bouton du header. * @callback HeaderButtonCallback * @param {PointerEvent} e Événement générique au clic sur l'outil * @param {LayerSwitcher} layerSwitcher instance du gestionnaire de couche */ /** * @typedef {Object} LayerSwitcherLayersConfig * @property {Layer} layer - Objet couche OpenLayers à gérer. * @property {Object} [config] - Métadonnées associées à la couche. * @property {string} [config.title] - Titre de la couche. * @property {string} [config.producer] - Producteur de la couche. * @property {string} [config.thumbnail] - Pictogramme de la couche. * @property {string} [config.description] - Description de la couche. * @property {string} [config.quicklookUrl] - URL d’aperçu rapide. * @property {Array<Object>} [config.legends] - Légendes associées à la couche. * @property {Array<Object>} [config.metadata] - Métadonnées associées à la couche. * @property {boolean} [config.locked] - Indique si la couche est verrouillée. */ /** * @classdesc * OpenLayers Control to manage map layers : their order, visibility and opacity, and display their informations (title, description, legends, metadata...) * * @module LayerSwitcher * @alias ol.control.LayerSwitcher */ class LayerSwitcher extends Control { /* * @param {Layer} [options.layers.layer] - ol.layer.Layer layer to be configured (that has been added to map) * @param {Object} [options.layers.config] - custom configuration object for layer information (title, description, legends, metadata, quicklook url), with following properties : * @param {String} [options.layers.config.title] - layer alias, to be displayed in widget layer list. E.g. : "Cartes IGN" * @param {String} [options.layers.config.description] - layer description, to be displayed on title hover, or in layer information panel. * @param {String} [options.layers.config.quicklookUrl] - link to a quick look image for this layer. * @param {Array} [options.layers.config.legends] - array of layer legends. Each array element is an object, with following properties : * - url (String, mandatory) : link to a legend * - minScaleDenominator (Number, optional) : min scale denominator for legend validity. * @param {Array} [options.layers.config.metadata] - array of layer metadata. Each array element is an object, with property url (String, mandatory) : link to a metadata */ /* * @param {Number} [options.options.id] - Ability to add an identifier on the widget (advanced option) * @param {Boolean} [options.options.collapsed = true] - Specify if widget has to be collapsed (true) or not (false) on map loading. Default is true. * @param {Boolean} [options.options.panel = false] - Specify if widget has to have a panel header. Default is false. * @param {Boolean} [options.options.counter = false] - Specify if widget has to have a counter. Default is false. * @param {Boolean} [options.options.allowEdit = true] - Specify if widget has to have an edit button (available only for vector layers). Default is true. * @param {Boolean} [options.options.allowGrayScale = true] - Specify if widget has to have an grayscale button (not available for vector layers). Default is true. * @param {Array} [options.options.advancedTools] - ... * @param {String} [options.options.advancedTools.label] - Specify the label name of the button * @param {String} [options.options.advancedTools.icon] - icon (optionnal) * @param {Function} [options.options.advancedTools.cb] - callback (optionnal) * @param {Object} [options.options.advancedTools.styles] - styles (optionnal) */ /** * @constructor * @param {Object} options - control options * @param {Array<LayerSwitcherLayersConfig>} [options.layers] - list of layers to be configured. Each array element is an object, with following properties : * @param {LayerSwitcherOptions} [options.options] - ol.control.Control options (see {@link http://openlayers.org/en/latest/apidoc/ol.control.Control.html ol.control.Control}) * @fires layerswitcher:add * @fires layerswitcher:remove * @fires layerswitcher:lock * @fires layerswitcher:extent * @fires layerswitcher:edit * @fires layerswitcher:changeproperty * @fires layerswitcher:change:selected * @fires layerswitcher:change:opacity * @fires layerswitcher:change:visibility * @fires layerswitcher:change:position * @fires layerswitcher:change:grayscale * @fires layerswitcher:change:style * @fires layerswitcher:change:locked * @fires layerswitcher:custom * @fires layerswitcher:header:button * @example * map.addControl(new ol.control.LayerSwitcher( * [ * { * layer : wms1, * config : { * title : "test layer name 1", * description : "test layer desc 1", * } * } * ], * { * collapsed : true, * panel : false, * counter : false, * position : "top-left", * allowEdit : true, * allowGrayScale : true, * headerButtons : [ * { * label: 'Ajouter', * title: 'Ajouter une couche', * icon: "svg | http", * cb: (e, switcher) => {}, * }, * ], * advancedTools : [ * { * label = 'Bouton', * icon = "svg | http", * cb = (e, LayerSwitcher, layer, options) => {}, * styles = {}, * } * ] * } * )); * * LayerSwitcher.on("layerswitcher:add", function (e) { * console.warn("layer", e.layer); * }); * LayerSwitcher.on("layerswitcher:remove", function (e) { * console.warn("layer", e.layer); * }); * LayerSwitcher.on("layerswitcher:extent", function (e) { * console.warn("layer", e.layer); * }); * LayerSwitcher.on("layerswitcher:edit", function (e) { * console.warn("layer", e.layer); * }); * LayerSwitcher.on("layerswitcher:change:selected", function (e) { * console.warn("layer", e.layer, e.previous); * }); * LayerSwitcher.on("layerswitcher:change:opacity", function (e) { * console.warn("layer", e.layer, e.opacity); * }); * LayerSwitcher.on("layerswitcher:change:visibility", function (e) { * console.warn("layer", e.layer, e.visibility); * }); * LayerSwitcher.on("layerswitcher:change:position", function (e) { * console.warn("layer", e.layer, e.position); * }); * LayerSwitcher.on("layerswitcher:change:grayscale", function (e) { * console.warn("layer", e.layer, e.grayscale); * }); * LayerSwitcher.on("layerswitcher:change:style", function (e) { * console.warn("layer", e.layer, e.name, e.url); * }); * LayerSwitcher.on("layerswitcher:change:locked", function (e) { * console.warn("layer", e.layer, e.locked); * }); * LayerSwitcher.on("layerswitcher:custom", function (e) { * console.warn("layer", e.action, e.layer); * }) * LayerSwitcher.on("layerswitcher:header:button", function (e) { * console.warn("Action", e.action, e.target); * }) * LayerSwitcher.on("layerswitcher:propertychange", function (e) { * console.warn("layer", e.layer, e.key, e.value); * }); */ constructor (options) { options = options || {}; var _options = options.options || {}; var _layers = options.layers || []; // call ol.control.Control constructor super(_options); if (!(this instanceof LayerSwitcher)) { throw new TypeError("ERROR CLASS_CONSTRUCTOR"); } if (!Array.isArray(_layers)) { throw new Error("ERROR WRONG_TYPE : layers should be an array"); } if (typeof _options !== "object") { throw new Error("ERROR WRONG_TYPE : options should be an object"); } /** * Nom de la classe * @private */ this.CLASSNAME = "LayerSwitcher"; this._initialize(_options, _layers); this.container = this._initContainer(_options); // ajout du container (this.element) ? this.element.appendChild(this.container) : this.element = this.container; return this; } static switcherButtons = { INFO : "info", EDIT : "edition", GREYSCALE : "greyscale", EXTENT : "extent", }; // ################################################################### // // ############## public methods (getters, setters) ################## // // ################################################################### // /** * Overload setMap function, that enables to catch map events, such as movend events. * @inheritdoc {@link https://openlayers.org/en/latest/apidoc/module-ol_control_Control-Control.html#setMap} * @param {Map} map - Map. */ setMap (map) { // INFO // cette méthode est appelée // après un map.addControl() ou map.removeControl() if (map) { // dans le cas de l'ajout du contrôle à la map // on ajoute les couches this._addMapLayers(map); // mode "collapsed" if (!this.collapsed) { this._showLayerSwitcherButton.setAttribute("aria-pressed", true); } // At every map movement, layer switcher may be updated, // according to layers on map, and their range. this._listeners.onMoveListener = map.on( "moveend", () => this._onMapMoveEnd(map) ); // add event listeners when a new layer is added to map, to add it in LayerSwitcher control (and DOM) this._listeners.onAddListener = map.getLayers().on( "add", (evt) => { logger.debug("LayerSwitcher:onAddListener", evt); var layer = evt.element; var id; // on attribue un nouvel identifiant à cette couche, // sauf si c'est une couche qui a déjà été ajoutée dans le LayerSwitcher au préalable (si gpLayerId existe) if (!layer.hasOwnProperty("gpLayerId")) { id = this._layerId; layer.gpLayerId = id; this._layerId++; } else { id = layer.gpLayerId; } if (!this._layers[id]) { this.addLayer(layer); } } ); // add event listeners when a layer is removed from map, to remove it from LayerSwitcher control (and DOM) this._listeners.onRemoveListener = map.getLayers().on( "remove", (evt) => { logger.debug("LayerSwitcher:onRemoveListener", evt); var layer = evt.element; var id = layer.gpLayerId; if (this._layers[id]) { this.removeLayer(layer); } } ); } else { // we are in a setMap(null) case // we forget the listeners linked to the layerSwitcher olObservableUnByKey(this._listeners.onMoveListener); olObservableUnByKey(this._listeners.onAddListener); olObservableUnByKey(this._listeners.onRemoveListener); // we put all the layers at Zindex = 0, without changing the visual order // in order that the next added layers are not hidden by layers with Zindex > 0 for (var i = this._layersOrder.length - 1; i >= 0; i--) { // this._layersOrder[i].layer.setZIndex(0); } } // on appelle la méthode setMap originale d'OpenLayers super.setMap(map); // position if (this.options.position) { this.setPosition(this.options.position); } // reunion du bouton avec le précédent if (this.options.gutter === false) { this.getContainer().classList.add("gpf-button-no-gutter"); } // initialize tooltips if (this.options.allowTooltips) { ToolTips.init(); } } /** * Add a new layer to control (when added to map) or add new layer configuration * * @param {Layer} layer - layer to add to layer switcher * @param {Object} [config] - additional options for layer configuration * @param {Object} [config.title] - layer title (default is layer identifier) * @param {Object} [config.description] - layer description (default is null) * @param {Object} [config.legends] - layer legends (default is an empty array) * @param {Object} [config.metadata] - layer metadata (default is an empty array) * @param {Object} [config.quicklookUrl] - layer quicklookUrl (default is null) * @fires layerswitcher:add {@link LayerSwitcher#ADD_LAYER_EVENT} * @example * layerSwitcher.addLayer( * gpParcels, * { * title : "Parcelles cadastrales", * description : "description de la couche", * quicklookUrl : "http://quicklookUrl.fr" * } * ) */ addLayer (layer, config) { var map = this.getMap(); config = config || layer.config || {}; if (!layer) { logger.log("[ERROR] LayerSwitcher:addLayer - missing layer parameter"); return; } var id = layer.gpLayerId; if (typeof id === "undefined") { logger.trace("[WARN] LayerSwitcher:addLayer - configuration cannot be set for this layer (layer id not found)", layer); return; } // make sure layer is in map layers var isLayerInMap = false; map.getLayers().forEach( (lyr) => { if (lyr.gpLayerId === id) { isLayerInMap = true; } } ); if (!isLayerInMap) { logger.log("[ERROR] LayerSwitcher:addLayer - configuration cannot be set for ", layer, " layer (layer is not in map.getLayers() )"); return; } // if layer is not already in layers list, add it to control (layers list and container div) if (!this._layers[id]) { // 1. add layer to layers list var layerInfos = this.getLayerInfo(layer) || {}; var opacity = layer.getOpacity(); var visibility = layer.getVisible(); var grayscale = layer.get("grayscale"); var locked = layer.get("locked"); var isInRange = this.isInRange(layer, map); var layerOptions = { layer : layer, id : id, name : layer.name, // only geoportal layers service : layer.service, // only geoportal layers type : "", // only geoportal website : ie 'feature' opacity : opacity != null ? opacity : 1, visibility : visibility != null ? visibility : true, grayscale : grayscale, locked : locked, inRange : isInRange != null ? isInRange : true, producer : config.producer != null ? config.producer : (layerInfos._producer || null), title : config.title != null ? config.title : (layerInfos._title || id), thumbnail : config.thumbnail != null ? config.thumbnail : (layerInfos._thumbnail || null), description : config.description || layerInfos._description || null, legends : config.legends || layerInfos._legends || [], metadata : config.metadata || layerInfos._metadata || [], quicklookUrl : config.quicklookUrl || layerInfos._quicklookUrl || null }; this._layers[id] = layerOptions; // 2. create layer div (to be added to control main container) // Création de la div correspondante à cette couche var layerDiv = this._createLayerDiv(layerOptions); // on stocke la div dans les options de la couche, pour une éventuelle réorganisation (setZIndex par ex) this._layers[id].div = layerDiv; // 3. réorganisation des couches si un zIndex est spécifié // FIXME : // _forceNullzIndex !? // getZIndex() retourne undefined au lieu de 0 !? if ((layer.getZIndex && layer.getZIndex() !== 0 && typeof layer.getZIndex() !== "undefined") || layer._forceNullzIndex) { // réorganisation des couches si un zIndex est spécifié this._updateLayersOrder(); } else { // sinon on ajoute la couche au dessus des autres this._layersOrder.unshift(layerOptions); this._lastZIndex++; layer.setZIndex(this._lastZIndex); layerDiv.dataset.sortableId = this._layerId; this._layerListContainer.insertBefore(layerDiv, this._layerListContainer.firstChild); // this._layerListContainer.insertBefore(layerDiv, // (this.options.panel) ? // this._layerListContainer.childNodes[1] : this._layerListContainer.firstChild); } // 3. Add listeners for opacity and visibility changes this._listeners.updateLayerOpacity = layer.on( "change:opacity", (e) => this._updateLayerOpacity(e) ); this._listeners.updateLayerVisibility = layer.on( "change:visible", (e) => this._updateLayerVisibility(e) ); this._listeners.updateLayerGrayScale = layer.on( "change:grayscale", (e) => this._updateLayerGrayScale(e) ); this._listeners.updateLayerLocked = layer.on( "change:locked", (e) => this._updateLayerLocked(e) ); this._listeners.updateProperties = layer.on( "propertychange", (e) => this._updateGenericProperty(e) ); if (this._layers[id].onZIndexChangeEvent == null) { this._layers[id].onZIndexChangeEvent = layer.on( "change:zIndex", () => this._updateLayersOrder() ); } // user may also add a new configuration for an already added layer } else { // add new configuration parameters to layer informations for (var prop in config) { if (config.hasOwnProperty(prop)) { this._layers[id][prop] = config[prop]; } } // set new title in layer div if (config.title) { var nameDiv = document.getElementById(this._addUID("GPname_ID_" + id)); if (nameDiv) { nameDiv.innerHTML = config.title; nameDiv.title = config.description || config.title; } } // set new title in layer div if (config.producer) { let producerDiv = document.getElementById(this._addUID("GPlayerProducer_ID_" + id)); if (producerDiv) { producerDiv.innerHTML = config.producer; } else { this.setLayerProducer(id, config.producer); } } // add layer info picto if necessary var infodiv = document.getElementById(this._addUID("GPinfo_ID_" + id)); if (!document.getElementById(this._addUID("GPinfo_ID_" + id)) && config.description) { var advancedTools = document.getElementById(this._addUID("GPadvancedTools_ID_" + id)); if (advancedTools) { advancedTools.appendChild( this._createInformationElement({ id : id }) ); } } // close layer info element if open, to update information. if (infodiv && infodiv.className === "GPlayerInfoOpened") { document.getElementById(this._addUID("GPlayerInfoPanel")).classList.add("GPlayerInfoPanelClosed", "gpf-hidden"); // infodiv.className = "GPlayerInfo"; } } // on met à jour le compteur this._updateLayerCounter(); var self = this; setTimeout(() => { self._updateLayerGrayScale({ target : { gpLayerId : id } }); }, 0); /** * event triggered when a layer is added * @event layerswitcher:add */ this.dispatchEvent({ type : this.ADD_LAYER_EVENT, layer : this._layers[id] }); }; /** * Remove a layer from control * * @param {Layer} layer - layer. * @fires layerswitcher:remove {@link LayerSwitcher#REMOVE_LAYER_EVENT} * @deprecated on the future version ... */ removeLayer (layer) { if (!layer) { return; } olObservableUnByKey(this._listeners.updateLayerOpacity); olObservableUnByKey(this._listeners.updateLayerVisibility); olObservableUnByKey(this._listeners.updateLayerGrayScale); olObservableUnByKey(this._listeners.updateLayerLocked); olObservableUnByKey(this._listeners.updateProperties); // olObservableUnByKey(this._listeners.updateLayersOrder); logger.trace(layer); var layerID = layer.gpLayerId; // var layerList = document.getElementById(this._addUID("GPlayersList")).firstChild; // close layer info element if open. var infodiv = document.getElementById(this._addUID("GPinfo_ID_" + layerID)); if (infodiv && infodiv.className === "GPlayerInfoOpened") { document.getElementById(this._addUID("GPlayerInfoPanel")).classList.add("GPlayerInfoPanelClosed", "gpf-hidden"); // infodiv.className = "GPlayerInfo"; } var stylediv = document.getElementById(this._addUID("GPedit_ID_" + layerID)); if (stylediv && stylediv.classList.contains("GPlayerStyleOpened")) { document.getElementById(this._addUID("GPlayerStylePanel")).classList.add("GPlayerStylePanelClosed", "gpf-hidden"); } // remove layer div var layerDiv = document.getElementById(this._addUID("GPlayerSwitcher_ID_" + layerID)); if (layerDiv) { this._layerListContainer.removeChild(layerDiv); } var layerIndex = Math.abs(layer.getZIndex() - this._lastZIndex); // on retire la couche de la liste ordonnée des layers this._layersOrder.splice(layerIndex, 1); this._lastZIndex--; // on met à jour les zindex des couches restantes var layerOrderTemp = this._layersOrder; for (var i = 0; i < layerOrderTemp.length; i++) { layerOrderTemp[i].layer.setZIndex(this._lastZIndex - i); } /** * event triggered when a layer is removed * @event layerswitcher:remove */ this.dispatchEvent({ type : this.REMOVE_LAYER_EVENT, layer : this._layers[layerID] }); if (layer === this.getSelectedLayer()) { // Réinitialise la couche sélectionnée this.setSelectedLayer(); } // on retire la couche de la liste des layers delete this._layers[layerID]; // on met à jour le compteur this._updateLayerCounter(); } /** * Lock a layer, so it cannot be removed or modified from layerSwitcher * @param {Layer} layer - layer to be locked * @param {Boolean} locked - true if locked * @fires layerswitcher:lock {@link LayerSwitcher#LOCK_LAYER_EVENT} */ lockLayer (layer, locked) { if (!layer) { return; } var layerID = layer.gpLayerId; var layerDiv = document.getElementById(this._addUID("GPlayerSwitcher_ID_" + layerID)); if (layerDiv) { locked ? layerDiv.setAttribute("disabled", true) : layerDiv.removeAttribute("disabled"); } layer.set("locked", locked); /** * event triggered when a layer is locked or unlocked * @event layerswitcher:lock */ this.dispatchEvent({ type : this.LOCK_LAYER_EVENT, layer : this._layers[layerID], locked : locked }); } /** * Collapse or display control main container * * @param {Boolean} collapsed - True to collapse control, False to display it */ setCollapsed (collapsed) { if (collapsed === undefined) { logger.log("[ERROR] LayerSwitcher:setCollapsed - missing collapsed parameter"); return; } var isCollapsed = !document.getElementById(this._addUID("GPshowLayersList")).checked; if ((collapsed && isCollapsed) || (!collapsed && !isCollapsed)) { return; } // on simule l'ouverture du panneau après un click if (!isCollapsed) { // var layers = document.getElementsByClassName("GPlayerInfoOpened"); // for (var i = 0; i < layers.length; i++) { // layers[i].className = "GPlayerInfo"; // } document.getElementById(this._addUID("GPlayerInfoPanel")).classList.add("GPlayerInfoPanelClosed", "gpf-hidden"); document.getElementById(this._addUID("GPlayerStylePanel")).classList.add("GPlayerStylePanelClosed", "gpf-hidden"); } document.getElementById(this._addUID("GPshowLayersList")).checked = !collapsed; } /** * Returns true if widget is collapsed (minimize), false otherwise * @returns {Boolean} is collapsed */ getCollapsed () { return this.collapsed; } /** * Display or hide removeLayerPicto from layerSwitcher for this layer * * @param {Layer} layer - ol.layer to be configured * @param {Boolean} removable - specify if layer can be remove from layerSwitcher (true) or not (false). Default is true */ setRemovable (layer, removable) { if (!layer) { return; } var layerID = layer.gpLayerId; if (layerID == null) { // on teste si layerID est null ou undefined logger.log("[LayerSwitcher:setRemovable] layer should be added to map before calling setRemovable method"); return; } var removalDiv = document.getElementById(this._addUID("GPremove_ID_" + layerID)); if (removalDiv) { if (removable === false) { removalDiv.style.display = "none"; } else if (removable === true) { removalDiv.style.display = "block"; } else { } } } /** * Get container * * @returns {HTMLElement} container */ getContainer () { return this.container; } /** * Forget add listener added to the control */ forget () { // on supprime les listeners d'ajout de couches olObservableUnByKey(this._listeners.onAddListener); } /** * Add listeners to catch map layers addition */ listen () { // on ajoute les listeners d'ajout de couches var map = this.getMap(); if (map) { this._listeners.onAddListener = map.getLayers().on( "add", (evt) => { logger.debug("LayerSwitcher:onAddListener", evt); var layer = evt.element; var id; // on attribue un nouvel identifiant à cette couche, // sauf si c'est une couche qui a déjà été ajoutée dans le LayerSwitcher au préalable (si gpLayerId existe) if (!layer.hasOwnProperty("gpLayerId")) { id = this._layerId; layer.gpLayerId = id; this._layerId++; } else { id = layer.gpLayerId; } if (!this._layers[id]) { this.addLayer(layer); } } ); } } // ################################################################### // // ##################### init component ############################## // // ################################################################### // /** * Initialize LayerSwitcher control (called by constructor) * * @param {Object} options - ol.control.Control options (see {@link http://openlayers.org/en/latest/apidoc/ol.control.Control.html ol.control.Control}) * @param {Array} layers - list of layers to be configured. Each array element is an object, with following properties : * @private */ _initialize (options, layers) { // options par defaut this.options = { id : "", collapsed : true, draggable : false, counter : false, panel : false, gutter : false, allowEdit : true, allowGrayScale : true, allowDraggable : true, allowDelete : true, allowTooltips : false, headerButtons : [], advancedTools : null, }; // merge with user options Utils.assign(this.options, options); this.options.layers = layers; /** * identifiant du contrôle * utile pour suffixer les identifiants CSS * (pour gérer le cas où il y en a plusieurs dans la même page) * @type {String} * @private */ this._uid = this.options.id || SelectorID.generate(); /** * Control layers list. * ach key is a layer id, and its value is an object of layers options (layer, id, opacity, visibility, title, description...) * @type {Object} * @private */ this._layers = {}; /** * array of ordered control layers * @type {Array} * @private */ this._layersOrder = []; /** * associative array of layers ordered by zindex (keys are zindex values, and corresponding values are arrays of layers at this zindex) * @type {Object} * @private */ this._layersIndex = {}; /** * layers max z index, to order layers using their z index * @type {Number} * @private */ this._lastZIndex = 0; /** * layers max id, incremented when a new layer is added * @type {Number} * @private */ this._layerId = 0; /** * collapse mode * true if widget is collapsed, false otherwise */ this.collapsed = (this.options.collapsed !== undefined) ? this.options.collapsed : true; /** * Layer list (DOM). * @type {HTMLElement} * @private */ this._layerListContainer = null; /** * listeners added to the layerSwitcher saved here in order to delete them if we remove the control from the map) * @type {Object} * @private */ this._listeners = {}; // add options layers to layerlist. // (seulement les couches configurées dans les options du layerSwitcher par l'utilisateur), // les autres couches de la carte seront ajoutées dans la méthode setMap for (var i = 0; i < layers.length; i++) { // recup la layer, son id, var layer = layers[i].layer; if (layer) { var id; // si elles ont déjà un identifiant (gpLayerId), on le récupère, sinon on en crée un nouveau, en incrémentant this_layerId. if (!layer.hasOwnProperty("gpLayerId")) { id = this._layerId; layer.gpLayerId = id; this._layerId++; } else { id = layer.gpLayerId; } // et les infos de la conf si elles existent (title, description, legends, quicklook, metadata) var conf = layers[i].config || {}; var layerInfo = this.getLayerInfo(layer); var opacity = layer.getOpacity(); var visibility = layer.getVisible(); var grayscale = layer.get("grayscale"); var layerOptions = { layer : layer, // la couche ol.layer concernée id : id, name : layer.name, // only geoportal layers service : layer.service, // only geoportal layers opacity : opacity != null ? opacity : 1, visibility : visibility != null ? visibility : true, grayscale : grayscale, title : conf.title || layerInfo._title, description : conf.description || layerInfo._description, legends : conf.legends || layerInfo._legends, metadata : conf.metadata || layerInfo._metadata, quicklookUrl : conf.quicklookUrl || layerInfo._quicklookUrl, thumbnail : conf.thumbnail || layerInfo._thumbnail, producer : conf.producer || layerInfo._producer }; this._layers[id] = layerOptions; } } /** * div that will contain layers list * @private */ this._layerListContainer = null; /** * counter of layers in layerSwitcher control * @private */ this._layerSwitcherCounter = null; /** * button to show/hide layerSwitcher control * @private */ this._showLayerSwitcherButton = null; /** * event triggered when a property is modified * @event layerswitcher:propertychange * @defaultValue "layerswitcher:propertychange" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:propertychange", function (e) { * console.log(e.layer); * }) */ this.PROPERTY_CHANGE_EVENT = "layerswitcher:propertychange"; /** * event triggered when a layer is added * @event layerswitcher:add * @defaultValue "layerswitcher:add" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:add", function (e) { * console.log(e.layer); * }) */ this.ADD_LAYER_EVENT = "layerswitcher:add"; /** * event triggered when a layer is removed * @event layerswitcher:remove * @defaultValue "layerswitcher:remove" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:remove", function (e) { * console.log(e.layer); * }) */ this.REMOVE_LAYER_EVENT = "layerswitcher:remove"; /** * event triggered when a layer is locked * @event layerswitcher:lock * @defaultValue "layerswitcher:lock" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:lock", function (e) { * console.log(e.layer); * }) */ this.LOCK_LAYER_EVENT = "layerswitcher:lock"; /** * event triggered when a layer extent is changed * @event layerswitcher:extent * @defaultValue "layerswitcher:extent" * @group Events * @param {Object} extent - extent (map projection) * @param {Object} layer - layer * @param {String} error - error * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:extent", function (e) { * console.log(e.layer); * }) */ this.EXTENT_LAYER_EVENT = "layerswitcher:extent"; /** * event triggered when a layer is edited * @event layerswitcher:edit * @defaultValue "layerswitcher:edit" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} options - layer options * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:edit", function (e) { * console.log(e.layer); * }) */ this.EDIT_LAYER_EVENT = "layerswitcher:edit"; /** * event triggered when a custom action is called * @event layerswitcher:custom * @defaultValue "layerswitcher:custom" * @group Events * @param {Object} type - event * @param {String} action - label name * @param {Object} layer - layer * @param {Object} options - layer options * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:custom", function (e) { * console.log(e.layer); * }) */ this.CUSTOM_LAYER_EVENT = "layerswitcher:custom"; /** * event triggered when an header button is clicked * @event layerswitcher:header:button * @defaultValue "layerswitcher:header:button" * @group Events * @param {Object} type - event * @param {String} action - label name * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:header:button", function (e) { * console.log(e.action, e.target); * }) */ this.HEADER_BUTTON_EVENT = "layerswitcher:header:button"; /** * event triggered when a layer opacity is changed * @event layerswitcher:change:opacity * @defaultValue "layerswitcher:change:opacity" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} opacity - new opacity value * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:change:opacity", function (e) { * console.log(e.layer, e.opacity); * }) */ this.CHANGE_LAYER_OPACITY_EVENT = "layerswitcher:change:opacity"; /** * event triggered when a layer visibility is changed * @event layerswitcher:change:visibility * @defaultValue "layerswitcher:change:visibility" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} visibility - new visibility value * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:change:visibility", function (e) { * console.log(e.layer, e.visibility); * }) */ this.CHANGE_LAYER_VISIBILITY_EVENT = "layerswitcher:change:visibility"; /** * event triggered when a layer grayscale is changed * @event layerswitcher:change:grayscale * @defaultValue "layerswitcher:change:grayscale" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} grayscale - new grayscale value * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:change:grayscale", function (e) { * console.log(e.layer, e.grayscale); * }) */ this.CHANGE_LAYER_GRAYSCALE_EVENT = "layerswitcher:change:grayscale"; /** * event triggered when a layer is locked or unlocked * @event layerswitcher:change:locked * @defaultValue "layerswitcher:change:locked" * @group Events * @param {Object} type - event * @param {Object} layer - layer * @param {Object} locked - new locked value * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:change:locked", function (e) { * console.log(e.layer, e.locked); * }) */ this.CHANGE_LAYER_LOCKED_EVENT = "layerswitcher:change:locked"; /** * event triggered when a layer is selected * @event layerswitcher:change:selected * @defaultValue "layerswitcher:change:selected" * @group Events * @param {Object} type - event * @param {Object} layer - new selected layer * @param {Object} previous - old selected layer (null if there was no selected layer before) * @param {Object} target - instance LayerSwitcher * @public * @example * LayerSwitcher.on("layerswitcher:change:selected", function (e) { * console.log(e, e.layer, e.previous); * }) */ this.CHANGE_LAYER_SELECTED_EVENT = "layerswitcher:change:selected"; } /** * Create control main container (called by constructor) * * @returns {HTMLElement} container - control container * @private */ _initContainer () { // creation du container principal var container = this._createMainContainerElement(); // ajout dans le container principal d'affichage des layers var input = this._createMainLayersShowElement(); container.appendChild(input); // gestion du mode "collapsed" if (!this.collapsed) { input.checked = "checked"; this.collapsed = false; } else { this.collapsed = true; } // on ajoute un écouteur d'évènement sur le bouton (checkbox) de dépliement/repliement des couches, // pour modifier la propriété this.collapsed quand on clique dessus var context = this; // event listener var changeCollapsed = function (e) { this.collapsed = !e.target.checked; // on génère nous même l'evenement OpenLayers de changement de pté // (utiliser layerSwitcher.on("change:collapsed", function ) pour s'abonner à cet évènement) this.dispatchEvent("change:collapsed"); }; input.addEventListener( "click", function (e) { changeCollapsed.call(context, e); } ); // ajout dans le container principal du picto du controle var picto = this._showLayerSwitcherButton = this._createMainPictoElement(); container.appendChild(picto); // ajout du compteur de couches container.classList.add("GplayerSwitcher-counterRemoved"); if (this.options.counter) { container.classList.remove("GplayerSwitcher-counterRemoved"); container.classList.add("GplayerSwitcher-counterAdded"); var counter = this._layerSwitcherCounter = this._createMainCounterLayersElement(); picto.appendChild(counter); } // ajout dans le container principal de la liste des layers var divL = this._createMainLayersElement(); container.appendChild(divL); // header ? if (this.options.panel) { // header var panelHeader = this._createLayersPanelHeaderElement(); divL.appendChild(panelHeader); // icon var panelIcon = this._createLayersPanelIconElement(); panelHeader.appendChild(panelIcon); // title var panelTitle = this._createLayersPanelTitleElement(); panelHeader.appendChild(panelTitl