UNPKG

@ignf-geoportal/sdk-2d

Version:
1,238 lines (1,176 loc) 50.8 kB
import { OlMap } from "./OlMapBase"; import { IMap } from "../Interface/IMap"; import { // Protocols, olExtended as Ol } from "geoportal-extensions-openlayers"; import VectorLayer from "ol/layer/Vector"; import ImageLayer from "ol/layer/Image"; import TileLayer from "ol/layer/Tile"; import VectorSource from "ol/source/Vector"; import TileWMSSource from "ol/source/TileWMS"; import ImageWMSSource from "ol/source/ImageWMS"; import OSMSource from "ol/source/OSM"; import GML2 from "ol/format/GML2"; import GML3 from "ol/format/GML3"; import Overlay from "ol/Overlay"; import { transform as olTransformProj // get as olGetProj } from "ol/proj"; import WMTSTileGrid from "ol/tilegrid/WMTS"; import { Fill as FillStyle, Icon as IconStyle, Stroke as StrokeStyle, Style, Text as TextStyle } from "ol/style"; import Collection from "ol/Collection"; import { unByKey as olObservableUnByKey } from "ol/Observable"; import { bbox as olStrategyBBOX } from "ol/loadingstrategy"; /** * Proprietes observables des couches pour le SDK */ OlMap.LAYERPROPERTIES = { visible : "visibility", opacity : "opacity", zIndex : "position", grayScaled : "grayScaled", minResolution : "maxZoom", maxResolution : "minZoom" }; OlMap.LAYERPROPERTIES["mapbox-status"] = "mapbox-status"; /** * Remove the layers listed to the map. * * @param {Array.<String>} layerIds - A list of layer's id or null. */ OlMap.prototype.removeLayers = function (layerIds) { if (!IMap.prototype.removeLayers.apply(this, arguments)) { return; } // ici on sait que layerIds est un tableau var _layers = this._getLayersObj(layerIds); _layers.forEach(function (_layer, i, array) { this.libMap.removeLayer(_layer.obj); }, this); }; /** * Modify the layers'options listed to the map * * @param {Object} layersOptions - Layers to add to the map and their options. Associative array mapping official name of the Geoportal layer or the id of a personal layer (keys) with their properties (values given as {@link Gp.LayerOptions}). */ OlMap.prototype.modifyLayers = function (layersOptions) { if (!IMap.prototype.modifyLayers.apply(this, arguments)) { return; } var layerIds = Object.keys(layersOptions); // on recupere les objets correspondants dejà sur la carte var _layerObjs = this._getLayersObj(layerIds); _layerObjs.forEach(function (_layerObj) { this.logger.trace("[modifyLayers] : modifying : [" + _layerObj.id + "]"); // traduction options ahn => options OlMap var commonOpts = this._applyCommonLayerParams(layersOptions[_layerObj.id]); // application des options OL aux couches OL // l'objet _layerObj.options sera mis à jour par le mécanisme des evenements. if (commonOpts.hasOwnProperty("opacity")) { this.logger.trace("[modifyLayers] : setting opacity of : [" + _layerObj.id + "] to : " + commonOpts.opacity); _layerObj.obj.setOpacity(commonOpts.opacity); } if (commonOpts.hasOwnProperty("visible")) { this.logger.trace("[modifyLayers] : setting visible of : [" + _layerObj.id + "] to : " + commonOpts.visible); _layerObj.obj.setVisible(commonOpts.visible); } if (commonOpts.hasOwnProperty("zIndex")) { this.logger.trace("[modifyLayers] : setting zIndex of : [" + _layerObj.id + "] to : " + commonOpts.zIndex); _layerObj.obj.setZIndex(commonOpts.zIndex); // pour forcer la prise en compte par le LayerSwitcher du zIndex quand il vaut zéro (extension OL) (cf. issue #12) if (commonOpts.zIndex === 0) { _layerObj.obj._forceNullzIndex = true; } // ou inversement pour ne plus forcer le zIndex à zéro lorsque ce n'est pas le cas if (commonOpts.zIndex !== 0 && _layerObj.obj._forceNullzIndex) { _layerObj.obj._forceNullzIndex = false; } } if (commonOpts.hasOwnProperty("minResolution")) { this.logger.trace("[modifyLayers] : setting minResolution of : [" + _layerObj.id + "] to : " + commonOpts.minResolution); _layerObj.obj.setMinResolution(commonOpts.minResolution); } if (commonOpts.hasOwnProperty("maxResolution")) { this.logger.trace("[modifyLayers] : setting maxResolution of : [" + _layerObj.id + "] to : " + commonOpts.maxResolution); _layerObj.obj.setMaxResolution(commonOpts.maxResolution); } if (commonOpts.hasOwnProperty("grayScaled")) { this.logger.trace("[modifyLayers] : setting grayScaled of : [" + _layerObj.id + "] to : " + commonOpts.grayScaled); this._changeLayerColor(_layerObj.id, commonOpts.grayScaled); } }, this); }; /** * Add a Raster Layer to the map * * @param {Object} layerObj - geoportalLayer to add. * @param {Gp.LayerOptions} layerObj.geoportalLayerID - options of the layer * * @private */ OlMap.prototype._addRasterLayer = function (layerObj) { var layerId = Object.keys(layerObj)[0]; var layerOpts = layerObj[layerId]; // au cas où la couche ajoutée est un MNT, on essaye pas de l'ajouter en 2D if (layerOpts.type !== "elevation") { var constructorOpts = this._applyCommonLayerParams(layerOpts); switch (layerOpts.format.toUpperCase()) { case "WMS": { this.logger.trace("[_addRasterLayer] : ajout d'une couche WMS"); var params = {}; params.LAYERS = layerOpts.layers.join(","); if (layerOpts.version) { params.VERSION = layerOpts.version; } // au cas ou maintien de l'ancien nom de paramètre : // layerOpts.styleName (sans "s") layerOpts.stylesNames = layerOpts.stylesNames || layerOpts.stylesName; if (layerOpts.stylesNames) { if (Array.isArray(layerOpts.stylesNames)) { params.STYLES = layerOpts.stylesNames.join(); } else { this.logger.info("[_addRasterLayer] : 'stylesNames' parameter should be an array of style names (string)"); } } if (layerOpts.outputFormat) { params.FORMAT = layerOpts.outputFormat; } if (layerOpts.backgroundColor) { params.BGCOLOR = layerOpts.backgroundColor; params.TRANSPARENT = "FALSE"; } else { params.TRANSPARENT = "TRUE"; } var sourceWmsOpts = { url : layerOpts.url, params : params }; if (layerOpts.hasOwnProperty("projection")) { sourceWmsOpts.projection = layerOpts.projection; } if (layerOpts.hasOwnProperty("tiled") && layerOpts.tiled === true) { constructorOpts.source = new TileWMSSource(sourceWmsOpts); } else { constructorOpts.source = new ImageWMSSource(sourceWmsOpts); } break; } case "WMTS": { this.logger.trace("[_addRasterLayer] : ajout d'une couche WMTS"); // chargement des options par defaut var lOpts = this._getWMTSDefaultOpts(); // surcharge avec les options utilisateurs for (var opt in layerOpts) { lOpts[opt] = layerOpts[opt]; } layerOpts = lOpts; var sourceWmtsOpts = { url : layerOpts.url, layer : layerOpts.layer, matrixSet : layerOpts.tileMatrixSet, format : layerOpts.outputFormat, version : layerOpts.version, style : layerOpts.styleName, projection : layerOpts.projection, tileGrid : new WMTSTileGrid({ origin : [ layerOpts.topLeftCorner.x, layerOpts.topLeftCorner.y ], resolutions : layerOpts.resolutions, matrixIds : layerOpts.matrixIds }) }; // RESTFul or KVP encoding ? if (layerOpts.url.indexOf("{TileMatrixSet}") > 0 || layerOpts.url.indexOf("{TileRow}") > 0 || layerOpts.url.indexOf("{TileCol}") > 0) { // its an url template => RESTFul sourceWmtsOpts.requestEncoding = "REST"; } else { sourceWmtsOpts.requestEncoding = "KVP"; } constructorOpts.source = new Ol.source.WMTSExtended(sourceWmtsOpts); break; } case "OSM": this.logger.trace("[_addRasterLayer] : ajout d'une couche OSM"); constructorOpts.source = new OSMSource({ url : layerOpts.url }); break; default: } if (constructorOpts.hasOwnProperty("source")) { // le controle geoportalAttribution exploite la propriete _originators if (layerOpts.hasOwnProperty("originators")) { constructorOpts.source._originators = layerOpts.originators; } var layer = null; if (layerOpts.format.toUpperCase() === "WMS" && (!layerOpts.hasOwnProperty("tiled") || layerOpts.tiled !== true) ) { layer = new ImageLayer(constructorOpts); } else { layer = new TileLayer(constructorOpts); } // pour forcer la prise en compte par le LayerSwitcher du zIndex quand il vaut zéro (extension OL) (cf. issue #12) if (constructorOpts.hasOwnProperty("zIndex") && constructorOpts.zIndex === 0) { layer._forceNullzIndex = true; } var gpLayer = { id : layerId, obj : layer, options : layerOpts }; if (layerOpts.hasOwnProperty("grayScaled") && layerOpts.grayScaled) { this._colorGrayscaleLayerSwitch(gpLayer, true); } this._layers.push(gpLayer); this.libMap.addLayer(gpLayer.obj); this._addLayerConfToLayerSwitcher(gpLayer.obj, layerOpts); } } else { // dans ce cas, on est sur une couche d'élévation // on la sauvegarde dans le tableau 3Dlayers pour l'ajouter si on passe en contexte 3D var elevationLayer = { id : layerId, obj : "3D-only-layer", options : layerOpts }; if (!this._3Dlayers) { this._3Dlayers = []; } this._3Dlayers.push(elevationLayer); } }; /** * Add a vector Layer to the map * * @param {Object} layerObj - geoportalLayer to add. * @param {Gp.LayerOptions} layerObj.geoportalLayerID - options of the layer * * @private */ OlMap.prototype._addVectorLayer = function (layerObj) { // FIXME : ajout d'un parametre projection pour les donnees var layerId = Object.keys(layerObj)[0]; var layerOpts = layerObj[layerId]; var constructorOpts = this._applyCommonLayerParams(layerOpts); var layerStyleOptions = layerOpts.styleOptions || {}; var defaultMapOptions = this.mapOptions.defaultFeaturesStyle || {}; var defaultOptions = IMap.DEFAULT_VECTORLAYERS_STYLES; var styleOptions = {}; styleOptions.image = new IconStyle({ src : layerStyleOptions.markerSrc || defaultMapOptions.markerSrc || defaultOptions.markerSrc, anchor : [ layerStyleOptions.markerXAnchor || defaultMapOptions.markerXAnchor || defaultOptions.markerXAnchor, layerStyleOptions.markerYAnchor || defaultMapOptions.markerYAnchor || defaultOptions.markerYAnchor ], anchorOrigin : "top-left", anchorXUnits : "pixels", anchorYUnits : "pixels" }); styleOptions.stroke = new StrokeStyle({ color : IMap.prototype._hexToRgba.call(this, layerStyleOptions.strokeColor || defaultMapOptions.strokeColor || defaultOptions.strokeColor, layerStyleOptions.strokeOpacity || defaultMapOptions.strokeOpacity || defaultOptions.strokeOpacity), width : layerStyleOptions.strokeWidth || defaultMapOptions.strokeWidth || defaultOptions.strokeWidth }); styleOptions.fill = new FillStyle({ color : IMap.prototype._hexToRgba.call(this, layerStyleOptions.polyFillColor || defaultMapOptions.polyFillColor || defaultOptions.polyFillColor, layerStyleOptions.polyFillOpacity || defaultMapOptions.polyFillOpacity || defaultOptions.polyFillOpacity) }); styleOptions.text = new TextStyle({ font : "16px Sans", textAlign : "left", fill : new FillStyle({ color : IMap.prototype._hexToRgba.call(this, layerStyleOptions.textColor || defaultMapOptions.textColor || defaultOptions.textColor, 1) }) }); if (layerStyleOptions.textStrokeColor) { styleOptions.text.stroke = new StrokeStyle({ color : IMap.prototype._hexToRgba.call(this, layerStyleOptions.textStrokeColor || defaultMapOptions.textStrokeColor || defaultOptions.textStrokeColor, 1), width : 1 }); } var vectorStyle = new Style(styleOptions); switch (layerOpts.format.toUpperCase()) { case "KML": this.logger.trace("[_addVectorLayer] : ajout d'une couche KML"); if (layerOpts.url) { constructorOpts.source = new VectorSource({ url : this.setProxy(layerOpts.url), format : new Ol.format.KMLExtended({ extractStyles : layerOpts.extractStyles, showPointNames : layerOpts.showPointNames }) }); } else if (layerOpts.data) { var formatKml = new Ol.format.KMLExtended({ extractStyles : layerOpts.extractStyles, showPointNames : layerOpts.showPointNames, defaultStyle : [vectorStyle] }); constructorOpts.source = new VectorSource({ features : new Collection(), // features loader loader : function (extent, resolution, projectionFeature) { var projectionData = formatKml.readProjection(layerOpts.data); var features = formatKml.readFeatures(layerOpts.data, { dataProjection : projectionData, featureProjection : projectionFeature }); if (features.length > 0) { constructorOpts.source.addFeatures(features); } } }); } constructorOpts.style = vectorStyle; // utile ? break; case "GPX": this.logger.trace("[_addVectorLayer] : ajout d'une couche GPX"); if (layerOpts.url) { constructorOpts.source = new VectorSource({ url : this.setProxy(layerOpts.url), format : new Ol.format.GPXExtended({ defaultStyle : vectorStyle }) }); } else if (layerOpts.data) { var formatGpx = new Ol.format.GPXExtended({ defaultStyle : vectorStyle }); constructorOpts.source = new VectorSource({ features : new Collection(), // features loader loader : function (extent, resolution, projectionFeature) { var projectionData = formatGpx.readProjection(layerOpts.data); var features = formatGpx.readFeatures(layerOpts.data, { dataProjection : projectionData, featureProjection : projectionFeature }); if (features.length > 0) { constructorOpts.source.addFeatures(features); } } }); } constructorOpts.style = vectorStyle; // utile ? break; case "GEORSS": // TODO GeoRSS break; case "COMPUTE": case "GEOJSON": // INFO // par defaut, les couches de calculs sont au format GeoJSON depuis l'espace personnel. this.logger.trace("[_addVectorLayer] : ajout d'une couche GeoJSON"); if (layerOpts.url) { constructorOpts.source = new VectorSource({ url : this.setProxy(layerOpts.url), format : new Ol.format.GeoJSONExtended({ defaultStyle : vectorStyle }) }); } else if (layerOpts.data) { var formatGeojson = new Ol.format.GeoJSONExtended({ defaultStyle : vectorStyle }); constructorOpts.source = new VectorSource({ features : new Collection(), // features loader loader : function (extent, resolution, projectionFeature) { var projectionData = formatGeojson.readProjection(layerOpts.data); var features = formatGeojson.readFeatures(layerOpts.data, { dataProjection : projectionData, featureProjection : projectionFeature }); if (features.length > 0) { constructorOpts.source.addFeatures(features); } } }); } constructorOpts.style = vectorStyle; // utile ? break; case "WFS": // TODO : gestion des valeurs par defaut this.logger.trace("[_addVectorLayer] : ajout d'une couche WFS"); var maxFeatures = ""; var sld = ""; if (!layerOpts.version) { layerOpts.version = "2.0.0"; } if (!layerOpts.outputFormat) { layerOpts.outputFormat = "application/json"; } if (layerOpts.maxFeatures) { if (parseInt(layerOpts.version.charAt(0)) < 2) { maxFeatures = "&maxFeatures=" + layerOpts.maxFeatures; } else { maxFeatures = "&count=" + layerOpts.maxFeatures; } } if (layerOpts.sld) { sld = "&sld=" + layerOpts.sld; } var layerUrl = this.setProxy(layerOpts.url); var oflc = layerOpts.outputFormat.toLowerCase(); var format = null; var formatOptions = {}; // FIXME : useless projection param ? if (layerOpts.projection) { // param for GeoJSON format // FIXME // cf. https://github.com/openlayers/openlayers/commit/cbaa9a7567ae32629241bab4721bc429940c942e#diff-2734579f56e71d2f13d47cd894a3fe68 // defaultDataProjection to dataProjection version > 5.0.0 ! formatOptions["defaultDataProjection"] = layerOpts.projection; // param for GMLx format formatOptions["srsName"] = layerOpts.projection; } // FIXME à revoir... if (oflc.indexOf("gml") > 0 || oflc.indexOf("xml") > 0 ) { // GML => look for version indication if (oflc.indexOf("2.1") > 0 || oflc.indexOf("gml2") > 0 ) { format = new GML2(formatOptions); } else { // GML => defaults to GML3 format = new GML3(formatOptions); } } else { // Defaults format to GeoJSON format = new Ol.format.GeoJSONExtended(formatOptions); } constructorOpts.source = new VectorSource({ format : format, // envoi de la requête WFS url : function (extent, resolution, projection) { return layerUrl + "?service=WFS&request=GetFeature" + "&version=" + layerOpts.version + "&typename=" + layerOpts.typeNames + "&outputFormat=" + layerOpts.outputFormat + "&srsname=" + projection.getCode() + "&bbox=" + extent.join(",") + "," + projection.getCode() + maxFeatures + sld; }, strategy : olStrategyBBOX }); break; case "DRAWING": // à tester avec les outils de dessins this.logger.trace("[_addVectorLayer] : ajout d'une couche de dessin"); constructorOpts.source = new VectorSource({}); break; default: break; } if (constructorOpts.hasOwnProperty("source")) { // le controle geoportalAttribution exploite la propriete _originators if (layerOpts.hasOwnProperty("originators")) { constructorOpts.source._originators = layerOpts.originators; } var layer = new VectorLayer(constructorOpts); this._layers.push({ id : layerId, obj : layer, options : layerOpts }); this.libMap.addLayer(layer); this._addLayerConfToLayerSwitcher(layer, layerOpts); var _map = this.libMap; var _vectorSource = constructorOpts.source; // "getExtent" pour les vecteurs if (_map.getView() && _map.getSize() && _vectorSource.getExtent) { var _fit = layerOpts.zoomToExtent || false; if (_fit) { var key = _vectorSource.on("change", function () { var _sourceExtent = _vectorSource.getExtent(); var _stateExtent = _vectorSource.getState(); if (_stateExtent === "ready" && _sourceExtent[0] !== Infinity) { olObservableUnByKey(key); _map.getView().fit(_sourceExtent, { // size : _map.getSize(), maxZoom : 18 }); } }); setTimeout(function () { _vectorSource.dispatchEvent("change"); }, 100); } } } }; /** * Add a geoportal Layer to the map * * @param {Object} layerObj - geoportalLayer to add. * @param {Object} layerConf - options of the layer conf (Gp.Config) * * @private */ OlMap.prototype._addGeoportalLayer = function (layerObj, layerConf) { var layerId = Object.keys(layerObj)[0]; var layerOpts = layerObj[layerId]; // parametres additionnels eventuels var olParams = {}; if (layerOpts.hasOwnProperty("opacity")) { olParams.opacity = layerOpts.opacity; } if (layerOpts.hasOwnProperty("visibility")) { olParams.visible = layerOpts.visibility; } if (layerOpts.hasOwnProperty("position")) { olParams.zIndex = layerOpts.position; } // transmission du style demandé au LayerWMTS/LayerWMS // WMTS : styleName (string) if (layerOpts.hasOwnProperty("styleName")) { olParams.sourceParams = {}; olParams.sourceParams.style = layerOpts.styleName; } // WMS : styleNames (array[string]) --> on prend le premier style spécifié if (layerOpts.hasOwnProperty("styleNames")) { olParams.sourceParams = {}; olParams.sourceParams.params = {}; olParams.sourceParams.params.STYLES = layerOpts.styleNames[0]; } if (layerOpts.hasOwnProperty("minZoom") && layerOpts.minZoom >= 0 && layerOpts.minZoom <= 28) { olParams.maxResolution = this._getResolutionFromZoomLevel(layerOpts.minZoom); this.logger.trace("[_addGeoportalLayer] : apply MaxResolution : " + olParams.maxResolution); } if (layerOpts.hasOwnProperty("maxZoom") && layerOpts.maxZoom >= 0 && layerOpts.maxZoom <= 28) { olParams.minResolution = this._getResolutionFromZoomLevel(layerOpts.maxZoom); this.logger.trace("[_addGeoportalLayer] : apply minResolution : " + olParams.minResolution); } var LayerClass = null; switch (layerOpts.format.toUpperCase()) { case "MAPBOX" : // Il n'est pas possible de creer une instance pour le format MAPBOX // car le type n'est determiné qu'après le téléchargement du style... // Donc, il nous faut les infos suivantes : // format: "MapBox", // url : "", // -> config // urlService : "", // -> opts // projection : "", // -> config / opts // outputFormat : "", // -> config / opts // defaultStyleThumbnail : "", // -> config / opts // defaultStyleName : "", // -> config / opts // defaultStyleDescription : "", // -> config / opts // stylesSummary : "", // not used ! // styles : [ // -> config / opts // { // thumbnail : "", // name : "", // description : "", // url : "" // } // ], // filtersSummary : "", // not used ! // filters : [ // -> opts // { // propertyName : "", // filterName : "", // configuration : "" // } // ], // opacity : 1, / opts // visibility : true, / opts // queryable : true, / opts // position : 0, / opts // title : "", // -> autoconf / opts // description : "", // -> autoconf / opts // quicklookurl : "", // -> autoconf / opts // metadata : [], // -> autoconf / opts // legends : [], // -> autoconf / opts // originators: [] // -> autoconf / opts this.logger.info("[_addGeoportalLayer] --> [_addMapBoxLayer]..."); if (layerConf) { var bfirst = true; for (var i = 0; i < layerConf.styles.length; i++) { var s = layerConf.styles[i]; if (s.current) { if (!layerOpts.hasOwnProperty("url")) { layerObj[layerId].url = s.url; } if (!layerOpts.hasOwnProperty("defaultStyleName")) { layerObj[layerId].defaultStyleName = s.name; } if (!layerOpts.hasOwnProperty("defaultStyleDescription")) { layerObj[layerId].defaultStyleDescription = s.title; } if (!layerOpts.hasOwnProperty("defaultStyleThumbnail")) { layerObj[layerId].defaultStyleThumbnail = s.thumbnail; } } else { // 1. false true -> true // true false -> true ... // 2. true true -> false if ((function (a, b) { return (a || b) && !(a && b); })(layerOpts.hasOwnProperty("styles"), bfirst)) { bfirst = false; if (!layerObj[layerId].styles) { layerObj[layerId].styles = []; } layerObj[layerId].styles.push({ thumbnail : s.thumbnail, name : s.name, description : s.title, url : s.url }); } } } } this._addMapBoxLayer(layerObj); break; case "WMTS" : LayerClass = Ol.layer.GeoportalWMTS; break; case "WMS" : LayerClass = Ol.layer.GeoportalWMS; break; default : this.logger.info("[_addGeoportalLayer] : no class found for " + layerOpts.format); } // au cas ou... if (LayerClass === null) { return; } var opts = { layer : layerId, olParams : olParams }; if (!this._isConfLoaded) { opts.apiKey = this.apiKey; } // instance var olLayer = new LayerClass(opts); // le controle geoportalAttribution exploite la propriete _originators // les originators doivent être passés par l'utilisateur (non présents dans la configuration) if (layerOpts.hasOwnProperty("originators")) { olLayer.getSource()._originators = layerOpts.originators; } // pour forcer la prise en compte par le LayerSwitcher du zIndex quand il vaut zéro (extension OL) (cf. issue #12) if (olParams.hasOwnProperty("zIndex") && olParams.zIndex === 0) { olLayer._forceNullzIndex = true; } this._layers.push({ id : layerId, obj : olLayer, options : layerOpts }); this.libMap.addLayer(olLayer); this._addLayerConfToLayerSwitcher(olLayer, layerOpts); }; /** * Trouve l'objet layerOpts correspondant au layer OL * * @param {Object} layerObj - Ol layer * @param {Object} layersStack - tableau des couches où chercher * * @return {Object} layer options */ OlMap.prototype._getLayerOpts = function (layerObj, layersStack) { var layerOpts = null; layersStack = layersStack || this._layers; for (var i = 0; i < layersStack.length; i++) { var l = layersStack[i]; if (l.obj === layerObj) { layerOpts = {}; layerOpts[l.id] = l.options; break; } } return layerOpts; }; /** * Registers unknown unregistered layer * * @param {Object} layerObj - Ol layer * @returns {Object} - new layer index in this._layers */ OlMap.prototype._registerUnknownLayer = function (layerObj) { // couches de résultats (imports) ou de calcul (itineraire, isochrone, ...) var layerId = "unknownLayer"; if (layerObj.hasOwnProperty("gpResultLayerId")) { layerId = layerObj.gpResultLayerId; } // on rajoute des infos quand on en a var options = {}; switch (layerId.toLowerCase()) { case "measure:profil": // INFO // C'est une couche issue de l'outil d'import options.format = "COMPUTE"; options.graph = null; options.control = "elevationpath"; options.title = "Profil altimétrique"; options.name = "profil altimetrique"; options.description = "Profil altimétrique"; options.controlOptions = this.getLibMapControl(options.control.toLowerCase()).getData(); options.data = this.getLibMapControl(options.control.toLowerCase()).getGeoJSON(); break; case "drawing": options.format = "drawing"; break; case "layerimport:kml": options.format = "KML"; break; case "layerimport:gpx": options.format = "GPX"; break; case "layerimport:geojson": options.format = "GeoJSON"; break; case "layerimport:wms": options.format = "WMS"; if (layerObj.gpGFIparams) { if (layerObj.gpGFIparams.queryable) { options.queryable = true; } if (Array.isArray(layerObj.gpGFIparams.formats)) { // par défaut on prend le premier options.gfiFormat = layerObj.gpGFIparams.formats[0]; // si on trouve "text/html" dans les formats disponibles, on prend "text/html" par défaut for (var i = 0; i < layerObj.gpGFIparams.formats.length; i++) { if (layerObj.gpGFIparams.formats[i] === "text/html") { options.gfiFormat = "text/html"; break; } } } } break; case "layerimport:wmts": options.format = "WMTS"; break; case "layerimport:mapbox": options.format = "MAPBOX"; break; case "layerimport:compute": // INFO // C'est une couche issue de l'outil d'import. // Les étapes de l'import : // * recherche si le fichier KML, GeoJSON ou GPX est un fichier de calcul avec la lecture de la balise 'geoportail:compute'. // * modification de la property 'gpResultLayerId' -> 'layerimport:COMPUTE' // * ajout des options du calcul dans les properties de la couche // * initialisation du contrôle avec les params de calcul options.format = "COMPUTE"; var prop = layerObj.getProperties(); options.graph = prop.graph; options.name = prop.name; options.control = prop.control; options.title = prop.title; options.controlOptions = prop.data || {}; options.data = prop.geojson || {}; // description by default if (options.name === "isocurve") { options.description = "Isochrone basé sur un graphe"; } if (options.name === "itineraire") { options.description = "Itinéraire basé sur un graphe"; } if (options.name === "profil altimetrique") { options.description = "Profil altimétrique"; } break; case "voiture$ogc:openls;isocurve": case "voiture$geoportail:gpp:isocurve": case "voiture$ogc:openls;itineraire": case "voiture$geoportail:gpp:itineraire": case "pieton$ogc:openls;isocurve": case "pieton$geoportail:gpp:isocurve": case "pieton$ogc:openls;itineraire": case "pieton$geoportail:gpp:itineraire": // INFO // C'est une couche de calcul en cours de traitement avec les widgets : // - isocurve // - route var key = layerId.toLowerCase(); // result layer name options.format = "COMPUTE"; // graph name (voiture / pieton) options.graph = key.split(/[$:;]/)[0]; // control name (isocurve / itineraire) options.name = key.split(/[$:;]/).slice(-1)[0]; // control real name options.control = options.name; if (options.name === "isocurve") { // description by default options.description = "Isochrone basé sur un graphe"; } if (options.name === "itineraire") { // real control name options.control = "route"; // description by default options.description = "Itinéraire basé sur un graphe"; } // title by default options.title = options.name + " (" + options.graph + ")"; // options control options.controlOptions = this.getLibMapControl(options.control.toLowerCase()).getData(); // features to geojson options.data = this.getLibMapControl(options.control.toLowerCase()).getGeoJSON(); break; default: options.format = ""; // ??? break; } // Et, si la couche est toujours non reconnue !? if (layerId === "unknownLayer") { // la couche est-elle une couche openlayers ? if (layerObj.getProperties) { // le layerId est l'id renseigné par l'utilisateur layerId = layerObj.getProperties().id; } } else { // dans le cas des couches import/compute // on rajoute au layer Id un timestamp pour utilisation sur le portail layerId += "-" + Date.now(); } this._layers.push({ id : layerId, obj : layerObj, options : options }); var layerOpts = {}; layerOpts[layerId] = options; return layerOpts; }; /** * Check params for layerDisplay * * @param {Gp.LayerOptions} layerOpts - options of the layer * @returns {Boolean} true if parameters are OK, false otherwise * * @private */ OlMap.prototype._checkLayerParams = function (layerOpts) { // verifications de base de la classe mère if (!IMap.prototype._checkLayerParams.apply(this, arguments)) { return false; } // verifications specifiques pour affichage dans OL // TODO return true; }; /** * * @param {String} layerId - layer identifier * @param {Boolean} toGrayScale - indicate transformation direction (from or to grayscale) * @fires change:grayScaled * @private */ OlMap.prototype._changeLayerColor = function (layerId, toGrayScale) { var layerIndex = this._getLayerIndexByLayerId(layerId); var gpLayer = this._layers[layerIndex]; switch (gpLayer.options.format.toUpperCase()) { case "MAPBOX": // VectorTile n'est pas une image ? case "KML": case "GPX": case "WFS": case "COMPUTE": case "DRAWING": this.logger.warn("[_changeLayerColor] : _changeLayerColor not allowed on vector layers (layer id: " + layerId + ")"); return; } this._colorGrayscaleLayerSwitch(gpLayer, toGrayScale); var event = IMap.CustomEvent( "change:grayScaled", { detail : { oldValue : !toGrayScale, newValue : toGrayScale } } ); // La propriéte "target" de CustomEvent est en "read-only" !? // Et, on la surcharge afin d'eviter des exceptions dy type : // TypeError: Cannot assign to read only property 'target' of object '#<CustomEvent>' Object.defineProperty(event, "target", { writable : true }); gpLayer.obj.dispatchEvent(event); }; /** * Function to switch layer display mode between color and grayscale. * * @param {Object} gpLayer - gp layer object * @param {String} gpLayer.id - layer identifier * @param {Ol.layer.Layer} gpLayer.obj - implementation layer object (here openlayers) * @param {Object} gpLayer.options - layer properties (of type layerOptions) * @param {Boolean} toGrayScale - indicates conversion direction. * * @private */ OlMap.prototype._colorGrayscaleLayerSwitch = function (gpLayer, toGrayScale) { // fonction de conversion d'une image en n/b function getGrayScaledDataUrl (img) { // FIXME : patch pour safari !? // ce patch cause des problemes sur Chrome v83+ // img.crossOrigin = null; img.crossOrigin = "anonymous"; var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); // si la taille est nulle, on force à une taille de tuile par defaut // afin d'eviter une exception ! img.width = img.width || 256; img.height = img.height || 256; canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); var imageData = ctx.getImageData(0, 0, img.width, img.height); var data = imageData.data; for (var i = 0; i < data.length; i += 4) { var avg = (data[i] + data[i + 1] + data[i + 2]) / 3; data[i] = avg; // red data[i + 1] = avg; // green data[i + 2] = avg; // blue } ctx.putImageData(imageData, 0, 0); return canvas.toDataURL(); }; // fonction de conversion et de chargement d'une image en n/b function convertImagetoGrayScale (image, context) { // conversion en n/b var dataUrl = getGrayScaledDataUrl(image); // chargement d'une image vide intermediaire pour eviter // l'affichage d'images couleurs (pour certains navigateurs // le chargement de l'image n/b et plus long et l'image originale // apparait de manière transitoire) image.src = ""; // forcer le raffraichissement de l'affichage a l'issu // du chargement de l'image n/b /** onload */ image.onload = function () { context.changed(); }; // chargement image n/b image.src = dataUrl; } // handler for event 'imageloadstart' function imageloadstartHandler (evt) { evt.image.getImage().crossOrigin = "Anonymous"; }; // handler for event 'tileloadstart' function tileloadstartHandler (evt) { evt.tile.getImage().crossOrigin = "Anonymous"; }; // handler for event 'imageloadend' function imageloadendHandler (evt) { convertImagetoGrayScale(evt.image.getImage(), evt.target); }; // handler for event 'tileloadend' function tileloadendHandler (evt) { convertImagetoGrayScale(evt.tile.getImage(), evt.target); }; // abonnement/desabonnement aux evenements permettant la conversion en n/b var source = gpLayer.obj.getSource(); if (toGrayScale) { if (source instanceof ImageWMSSource) { source.loadstartListenerKey = source.on("imageloadstart", imageloadstartHandler); source.loadendListenerKey = source.on("imageloadend", imageloadendHandler); } else { source.loadstartListenerKey = source.on("tileloadstart", tileloadstartHandler); source.loadendListenerKey = source.on("tileloadend", tileloadendHandler); } } else { olObservableUnByKey(source.loadstartListenerKey); olObservableUnByKey(source.loadendListenerKey); source.loadstartListenerKey = null; source.loadendListenerKey = null; } // maj du cache source.refresh(); }; /** * Apply params common to all kind of layers : * opacity, visibility, minZoom, maxZoom * * @param {Gp.LayerOptions} layerOpts - options of the layer * @returns {Object} a Ol.layer.Layer constructor options object * * @private */ OlMap.prototype._applyCommonLayerParams = function (layerOpts) { var commonOpts = {}; this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams "); if (layerOpts.hasOwnProperty("opacity")) { this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams - opacity : " + layerOpts.opacity); commonOpts.opacity = layerOpts.opacity; } if (layerOpts.hasOwnProperty("visibility")) { this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams - visibility : " + layerOpts.visibility); commonOpts.visible = layerOpts.visibility; } if (layerOpts.hasOwnProperty("position")) { this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams - position : " + layerOpts.position); commonOpts.zIndex = layerOpts.position; } if (layerOpts.hasOwnProperty("maxZoom") && layerOpts.maxZoom >= 0 && layerOpts.maxZoom <= 20) { var minRes = this._getResolutionFromZoomLevel(layerOpts.maxZoom); this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams - minRes : " + minRes); commonOpts.minResolution = minRes; } if (layerOpts.hasOwnProperty("minZoom") && layerOpts.minZoom >= 0 && layerOpts.minZoom <= 20) { var maxRes = this._getResolutionFromZoomLevel(layerOpts.minZoom); this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams - maxRes : " + maxRes); commonOpts.maxResolution = maxRes; } if (layerOpts.hasOwnProperty("grayScaled")) { this.logger.trace("[_applyCommonLayerParams] : _applyCommonLayerParams - grayScaled : " + layerOpts.grayScaled); commonOpts.grayScaled = layerOpts.grayScaled; } return commonOpts; }; /** * Adds the markers to the map * FIXME : make it public ? * * @param {Array.<Gp.MarkerOptions>} markersOptions - Markers to add to the Map. * @private */ OlMap.prototype._addMarkers = function (markersOptions) { this.logger.trace("[OlMap] : _addMarkers"); if (!Array.isArray(markersOptions)) { this.logger.info("Can not process markersOptions. It is not an Array."); return; } var mo = null; var context = this; var ii = 0; // marker open popup function // this == img element associated to the marker var fopenPopup = function (evt) { var evtPx = context.getLibMap().getEventPixel(evt); context.logger.trace("[OlMap] : _addMarkers : display content : " + mo.content); Ol.gp.GfiUtils.displayInfo( context.getLibMap(), context.getLibMap().getCoordinateFromPixel([ evtPx[0] + this.mo.ppoffset[0], evtPx[1] + this.mo.ppoffset[1] ]), this.mo.content, this.mo.contentType, this.mo.autoPanOptions ); }; for (ii = 0; ii < markersOptions.length; ii++) { mo = markersOptions[ii]; // complete missing properties with default if (!mo.hasOwnProperty("content")) { mo.content = ""; // empty string to avoid errors on display } if (!mo.hasOwnProperty("url")) { mo.url = Ol.control.DefaultMarkers["lightOrange"]; } // image offset if (!mo.hasOwnProperty("offset")) { mo.offset = Ol.control.DefaultMarkers["defaultOffset"]; } this.logger.trace("[OlMap] : _addMarkers : offset [" + mo.offset[0] + "," + mo.offset[1] + "]"); // popup offset (from mouse click) if (!mo.hasOwnProperty("ppoffset")) { // default popup has a 15px of y offset (see .gp-feature-info-div::before css class) mo.ppoffset = [0, -15]; } var needsLocated = false; if (!mo.hasOwnProperty("position")) { // no position given : use map center mo.position = { x : this.libMap.getView().getCenter()[0], y : this.libMap.getView().getCenter()[1] }; // when "located" or "geolocated", the center is not yet the final one (returned by located or geolocated event). needsLocated = true; } if (!mo.hasOwnProperty("contentType")) { mo.contentType = "text/html"; } // autoPan Options if (!mo.hasOwnProperty("autoPanOptions")) { // by default : autoPan true mo.autoPanOptions = { autoPan : IMap.DEFAULT_AUTOPAN_OPTIONS.autoPan, // properties of autoPanAnimation : https://openlayers.org/en/latest/apidoc/module-ol_Overlay.html#~PanOptions autoPanAnimation : { duration : IMap.DEFAULT_AUTOPAN_OPTIONS.duration }, autoPanMargin : IMap.DEFAULT_AUTOPAN_OPTIONS.margin }; } // create overlay var fcoords = [mo.position.x, mo.position.y]; if (mo.position.hasOwnProperty("projection")) { fcoords = olTransformProj(fcoords, mo.position.projection, this.getProjection()); } this.logger.trace("[OlMap] : _addMarkers : coords [" + fcoords[0] + ", " + fcoords[1] + "]"); var mrkImg = document.createElement("img"); mrkImg.src = mo.url; // mrkImg.setAttribute("content", mo.content); mrkImg.mo = mo; var mrk = new Overlay({ position : fcoords, offset : mo.offset, element : mrkImg }); this.libMap.addOverlay(mrk); // on tient à jour le tableau des markersOptions this._markers.push(mo); // listen to geolocated and located events (if marker has to be positionned to the center of the map if (needsLocated === true) { var gpMap = this; /** * Change markerPositon - this == mrk overlay object * * @param {Object} locEvt - Gp.Located or Gp.GeolocatedEvent */ var changeCenter = function (locEvt) { var fcoords = [ locEvt.position.x, locEvt.position.y ]; if (locEvt.position.hasOwnProperty("projection")) { fcoords = olTransformProj(fcoords, locEvt.position.projection, gpMap.getProjection()); } this.setPosition(fcoords); }; this.listen("located", changeCenter, mrk); this.listen("geolocated", changeCenter, mrk); } // listen to markerEvents mrkImg.addEventListener("click", fopenPopup, true); // TODO : test if useless or not mrkImg.addEventListener("touchend", fopenPopup, true); // TODO : // - add option for making popup opened or not at startup } }; /** * Removes map overlays * FIXME : make it public ? * * @private */ OlMap.prototype._removeMarkers = function () { var currentOverlays = this.libMap.getOverlays()