UNPKG

test-github-actions-geoportal-sdk-3d

Version:

French Geoportal SDK based on OpenLayers (2D) and iTowns (3D) libraries

1,122 lines (1,035 loc) 127 kB
// implementation du vecteur tuilé dans ce fichier afin d'isoler // la complexité du code de la fonction principale : _addVectorLayer(). import { OlMap } from "./OlMapBase"; import { olUtils as Utils } from "geoportal-extensions-openlayers"; import { // applyBackground as applyBackgroundOlms, applyStyle as applyStyleOlms } from "ol-mapbox-style"; import VectorTileLayer from "ol/layer/VectorTile"; import VectorTileSource from "ol/source/VectorTile"; import TileJSONSource from "ol/source/TileJSON"; import RenderFeature from "ol/render/Feature"; // FIXME !? import MVT from "ol/format/MVT"; import GeoJSON from "ol/format/GeoJSON"; import { createXYZ as olCreateXYZTileGrid } from "ol/tilegrid"; import { unByKey as olObservableUnByKey } from "ol/Observable"; import { transform as olTransformProj } from "ol/proj"; /****************************************************************************** * Internals functions *******************************************************************************/ /** * TODO ... * @param {Object} style - ... * @returns {Object} style ... * @private * @sample */ var _transformGrayStyle = function (style) { // modifier le style du layer ou des features avec une fonction de conversion des // couleurs en N/B var strStyle = JSON.stringify(style); // fonction de conversion decimal -> hexa function hex (number) { if (number > 255) { throw new Error("'" + number + "'' is greater than 255(0xff);"); } var str = Number(number).toString(16); return ("0" + str).slice(-2); } // fonction de conversion en NB function nb (col) { var r = col >> 16; var g = (col >> 8) & 0xff; var b = col & 0xff; // https://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale // https://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/ // luminosity : 0.21 R + 0.72 G + 0.07 B var bnw = (r * 0.2126 + g * 0.7152 + b * 0.0722) & 0xff; var num = (bnw << 16) | (bnw << 8) | bnw; return num.toString(16); } // recherche valeurs en rgba pour conversion en hexa var regex4rgba = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(0?.?\d+)\s*)?\)/gm; var style4hexa = strStyle.replace(regex4rgba, function (corespondance, p1, p2, p3, decalage) { var p = hex(p1) + hex(p2) + hex(p3); return "#" + p; }); // recherche des valeurs en hexa3 pour conversion en hexa var regex4hexa3 = /"#([a-f\d])([a-f\d])([a-f\d])"/igm; var style4hexa3 = style4hexa.replace(regex4hexa3, function (corespondance, p1, p2, p3, decalage) { var p = p1 + p1 + p2 + p2 + p3 + p3; return "\"#" + p + "\""; }); // recherche des valeurs en hexa pour conversion en NB var regex4hexa = /#([a-f\d]{2}[a-f\d]{2}[a-f\d]{2})/igm; var style4nb = style4hexa3.replace(regex4hexa, function (corespondance, p, decalage) { var subst4nb = nb(parseInt(p, 16)); return "#" + subst4nb; }); // style en NB try { return JSON.parse(style4nb); } catch (err) { return null; } }; /** * ... * @param {String} source - la source courante (id)... * @param {Array} urls - l'url du service... * @param {Object} filter - le filtre (option)... * @param {Object} tilejson - tileJson (metadata.json) * @param {Object} stylejson - les styles (json)... * @returns {Object} style... * @private * @sample * * var source = layer.get(mapbox-source); * var urls = layer.getSource().urls; * var filter = layer.get(mapbox-filters)["filters"][0]; * var tilejson = layer.get(mapbox-extensions); * var stylejson = style; // ceci est le style courant ! * // retourne les styles attributaires (json) * var customfilterjson = _createCustomFilterStyle(source, urls, filter, tilejson, stylejson); * */ var _createCustomFilterStyle = function (source, urls, filter, tilejson, stylejson) { // TODO // documenter le code pour la comprehension ! // les fonctions internes : var _updateFilterEntryToMainStyles = function (stylejson, filtername, layersId, layersIdx, selected) { // filtre inactif var _rgxFilternameInactive = RegExp("__" + filtername + "__"); // filtre trouvé var _bfound = false; for (var n = 0; n < layersIdx.length; n++) { var _mainlayer = stylejson.layers[layersIdx[n]]; if (_mainlayer && _mainlayer.id === layersId[n]) { // on recheche la valeur du filtre dans les filtres // des "layers" sous la forme : // - ["in", key, value, ...] // - ["===", key, value] // - ["all", expr, expr, ...] // FIXME // fonction reccursive ! if (_mainlayer.filter) { for (var k = 0; k < _mainlayer.filter.length; k++) { // on s'arrete au 1er filtre trouvé !? // if (_bfound) { // break; // } // cas où on a un ensemble de filtres // ["all", expr, expr, ...] if (Array.isArray(_mainlayer.filter[k])) { // à tester... for (var kk = 0; kk < _mainlayer.filter[k].length; kk++) { var _mainfiltername1 = _mainlayer.filter[k][kk]; // a t on trouvé quelque chose ? if (_mainfiltername1.indexOf(filtername) !== -1) { _bfound = true; // target.checked === true // > on active le filtre // target.checked === false // > on desactive le filtre if (selected) { // le filtre est inactif, on le reactive... if (_rgxFilternameInactive.test(_mainfiltername1)) { _mainlayer.filter[k][kk] = filtername; } } else { // le filtre est actif, on le desactive... _mainlayer.filter[k][kk] = "__" + filtername + "__"; } // on s'arrete au 1er trouvé !? // if (_bfound) { // break; // } } } } else { // la valeur recherché, c'est le nom du filtre, // ["in", key, value, ...] ou ["===", key, value] var _mainfiltername2 = _mainlayer.filter[k]; // a t on trouvé quelque chose ? if (_mainfiltername2.indexOf(filtername) !== -1) { _bfound = true; // target.checked === true // > on active le filtre // target.checked === false // > on desactive le filtre if (selected) { // le filtre est inactif, on le reactive... if (_rgxFilternameInactive.test(_mainfiltername2)) { _mainlayer.filter[k] = filtername; } } else { // le filtre est actif, on le desactive... _mainlayer.filter[k] = "__" + filtername + "__"; } // on s'arrete au 1er trouvé !? // if (_bfound) { // break; // } } } } } else { // FIXME il n'existe pas de filtre !? } } } // on n'a pas trouvé de filtre !? if (!_bfound) { // TODO un message ? } }; /** fonction pour generer un filtre * en fonction du typage postgres (string ou numerique), on le convertit en filtre * mapbox : * ex. filter : [ "==", {field}, {value} ] ou [ "in", {field}, {value} ] * @param {String} type - ... * @param {String} tjsonfield - ... * @param {String} tjsonvalue - ... * @param {String} comparaison - ... * @returns {Array} filter mapbox */ var _addFilterTagEntry = function (type, tjsonfield, tjsonvalue, comparaison) { var result = null; var _type = type.toLowerCase(); switch (_type) { case "string": case "text": case (_type.match(/^character/) || {}).input: result = [ comparaison || "in", // "==" tjsonfield, tjsonvalue ]; break; case "numeric": // evolution case "integer": result = [ comparaison || "<=", tjsonfield, parseInt(tjsonvalue, 10) ]; break; default: result = []; } return result; }; /** fonction pour generer une legende auto * en fonction du type de geometrie (postgres), on determine une legende mapbox * ex. pour un polygone * paint : {"fill-color" : {color}, "fill-opacity" : 0.5, "fill-width" : 1} * avec {color} en mode random * @param {String} tjsongeometry - geometry from tileJSON * @returns {Object} paint mapbox */ var _addPaintTagEntry = function (tjsongeometry) { var result = null; var _color = ("#" + Math.floor(Math.random() * 16777215).toString(16)).padEnd(7, "f"); var _geometry = tjsongeometry.toUpperCase(); switch (_geometry) { case "MULTIPOLYGON": case "POLYGON": result = { "fill-color" : _color, "fill-opacity" : 0.5, "fill-width" : 1 }; break; case "MULTILINE": case "LINE": case "LINESTRING": result = { "line-color" : _color, "line-width" : 1 }; break; case "MULTIPOINT": case "POINT": result = { "circle-color" : _color, "circle-radius" : 5, "circle-stroke-color" : "#FFFFFF", "circle-stroke-width" : 2 }; break; default: result = {}; } return result; }; /** fonction pour convertir le type postgres vers un format MapBox * ex. MULTIPOLYGON => fill * le type "symbol" ou "icon" est particulier à MapBox... * donc par defaut, le type postgres POINT => circle * @param {String} tjsongeometry - geometry from tileJSON * @returns {String} type mapbox */ var _addTypeGeometryTagEntry = function (tjsongeometry) { var result = null; // FIXME // Comment faire la difference entre symbol et circle ? // Sous postgres, le type est toujours un MULTIPOINT / POINT !? var _geometry = tjsongeometry.toUpperCase(); switch (_geometry) { case "MULTIPOLYGON": case "POLYGON": result = "fill"; break; case "MULTILINE": case "LINE": case "LINESTRING": result = "line"; break; case "MULTIPOINT": case "POINT": result = "circle"; break; default: result = "symbol"; // FIXME ? } return result; }; /* Filtre attributaire (style json) : // on construit un objet style pour chaque ensemble de filtres utilisateurs, // ils sont actifs (visibility:visible) ou inactifs (visibility:none) // par défaut selon la configuration demandée. // ex. // "id": {value}, // ex. Collège : valeur du champ "nature" de la table "etablissements" // "source": ..., // "source-layer": ..., // "layout" : {"visibility": "none"}, // "paint": {}, // "filter" : [ // "==", // "{field}", // ex. champ "nature" // "{value}" // ex. valeur "Collège" // ] */ // creation du style json pour un filtre attributaire var _style = {}; _style.version = 8; _style.name = filter.filterName; _style.center = tilejson.center || []; _style.bounds = tilejson.bounds || []; _style.layers = []; // liste des 'layers' _style.sources = {}; _style.sources[source] = { type : "vector", tiles : [urls] // l'url des services tuilés }; /* Extrait du "metadata.json" : // "vector_layers": [ // { // "id":"Enseignement", // "geometry":"POINT", // "maxzoom":"16", // "minzoom":"6", // "filedsCount":"2", // "fields":{ // "nature":{ // "attribute":"nature", // "count":"9", // "type":"character varying(80)", // "values":[ // "Collège", // "Autre", // "Science", // "Enseignement primaire", // "Université", // "Lycée", // "Enseignement secondaire", // "Enseignement supérieur" // ] // }, // "statut":{ // "attribute":"statut", // "count":"2", // "type":"character varying", // "values":["privées", "publiques"] // } // } // } // ] */ /* Le fichier "metadata.json" (tileJSON) : // il fournit une description des tables // le tag "vector_layers" permet d'obtenir les informations suivantes : // {id}, {type}, {fields}, {values}, {geometry}, ({minzoom} et {maxzoom}). // où // id : nom de la table (postgres) // => ex. "Etablissements" // => "source-layer" du fichier de style ! // => nom présent dans la tuile du service // type : typage des valeurs (postgres) // values : les valeurs // => nom des filtres ! // fields : liste des champs de la table(postgres) // => ex. "nature" // => options : filter.propertyName // geometry : type de geometrie (postgres) */ var _tjsonlayers = tilejson["vector_layers"]; if (_tjsonlayers) { var bFound = false; for (var i = 0; i < _tjsonlayers.length && !bFound; i++) { var o = _tjsonlayers[i]; for (var _tjsonfield in o.fields) { if (o.fields.hasOwnProperty(_tjsonfield)) { // on a trouvé le nom du champ... if (_tjsonfield === filter.propertyName) { // informations de la table (postgres) var _tjsonid = o.id; // la table (postgres) var _tjsongeometry = o.geometry; // le type de geometrie (postgres) var _tjsontype = o.fields[_tjsonfield].type; // typage des valeurs (postgres) var _tjsonvalues = o.fields[_tjsonfield].values; // les valeurs ! if (_tjsonvalues) { _tjsonvalues.sort(); // triées ! } // si on a un nom de table dans le nom du champs, // on recherche ce champ dans la bonne table. if (filter.tableName && _tjsonid !== filter.tableName) { continue; } // on verifie aussi que le nom de la table est bien present // dans le fichier de style, et on enregistre tous les "layers" // liés à cette table. var _mtdLayersId = []; // liste des "layers" (id dans le json) var _mtdLayersIdx = []; // liste des emplacements (ordre dans le json) var _nlayers = stylejson.layers.length; for (var k = 0; k < _nlayers; k++) { // on prend tous les styles où figurent cette table var l = stylejson.layers[k]; if (l["source-layer"] && l["source-layer"] === _tjsonid) { // attention, nous sommes dans un filtre utilisateur ! if (l.metadata && l.metadata["geoportail:entry"] && l.metadata["geoportail:index"]) { continue; } // FIXME cas particulier : // on ajoute une liste de filtres pour la configuration:0..., // si on a qu'un seul "layers"... if (_nlayers === 1 && filter.type && filter.type === 0) { if (!l.filter) { l.filter = []; l.filter.push("in"); l.filter.push(filter.propertyName); } for (var kk = 0; kk < _tjsonvalues.length; kk++) { var v = _tjsonvalues[kk]; // on evite d'ajouter plusieurs fois le filtre... if (l.filter.indexOf(v) === -1) { l.filter.push(v); } } } _mtdLayersId.push(l.id); _mtdLayersIdx.push(k); } } if (!_mtdLayersId) { // eslint-disable-next-line no-console console.warn("Filtres utilisateurs : auncune association possible (" + filter.filterName + ") !?"); break; } // on a trouvé les informations ! bFound = true; // mais, hummm..., il n'y a pas de valeurs pour ce champ... // c'est possible que les valeurs ne soient pas renseignées dans le metadata.json, // car trop de valeurs..., du coup, on ne peut pas gerer ce type d'informations. if (!_tjsonvalues) { // eslint-disable-next-line no-console console.warn("Filtres utilisateurs : auncune valeurs (" + filter.filterName + ") !?"); continue; } /* Le param "configurable" : // pour chaque valeur, on va construire un style avec un filtre (styles attributaires), // et on va chercher quelles sont les filtres qui peuvent impacté // quelles layers (association) du style principal differement. // en fonction du param "configurable", l'affichage graphique est differente. // les styles attributaires (filtres) sont ajoutés à la suite dans le style principale. // // - configurable=0 (par defaut) // ... // graphiquement, sur la carte, affichage multiple // des filtres. // // - configurable=1 // ... // graphiquement, sur la carte, affichage en superposition // sur les autres données presentes avec une une legende // automatique mise en place afin de differencier // et mettre en avant les objets sélectionnés... // // - configurable=2 // ... // graphiquement, sur la carte, un affichage uniquement // du filtre sélectionné. // la selection du filtre est de type radio-boutton, donc // une seule selection possible à la fois ! */ for (var j = 0; j < _tjsonvalues.length; j++) { var _tjsonvalue = _tjsonvalues[j]; // tag du style par defaut... var _tagPaint = null; var _tagFilter = _addFilterTagEntry(_tjsontype, _tjsonfield, _tjsonvalue); var _tagVisible = "visible"; // layout // tag "metadata:geoportail" par defaut... var _mtdFilterMode = 0; // configuration : 0, 1 ou 2 var _mtdFilterCategory = filter.filterName; if (filter.type === 0) { /* TODO Explication sur la configuration : // ... */ // gestion des filtres actifs // on applique le filtre sur le fichier de style afin // d'avoir un affichage correct... if (filter.selected && filter.selected.length) { _updateFilterEntryToMainStyles( stylejson, _tjsonvalue, _mtdLayersId, _mtdLayersIdx, filter.selected[j] ); } } else if (filter.type === 1) { /* TODO Explication sur la configuration : // ... */ _tagVisible = "none"; _tagPaint = _addPaintTagEntry(_tjsongeometry); _mtdFilterMode = 1; } else if (filter.type === 2) { /* TODO Explication sur la configuration : // ... */ _tagVisible = "none"; _mtdFilterMode = 2; // gestion des filtres actifs if (filter.selected && filter.selected.length) { // TODO... } } else {} // gestion des filtres actifs // on modifie la visibilité sur un filtre actif // (ex. oeil coché/décoché sur l'editeur graphique) if (filter.selected && filter.selected.length) { // 0 -> none, 1 -> visible _tagVisible = (filter.selected[j]) ? "visible" : "none"; } // finalisation du style attributaire _style.layers.push({ "id" : _tjsonvalue, "type" : _addTypeGeometryTagEntry(_tjsongeometry), // FIXME symbol ou circle ? "source" : source, "source-layer" : _tjsonid, /* tag metadata : // utiliser le tag metadata pour faire passer des informations // sur le layers // ex. // - la rubrique (category) // - son groupe (group), // - liste des layers, // - l'index des layers, // - type de configuration (filter): 0, 1 ou 2 // - l'ordre interne des filtres */ "metadata" : { "geoportail:category" : _mtdFilterCategory, "geoportail:filter" : _mtdFilterMode, // configuration : 0, 1 ou 2 "geoportail:group" : null, // notion de groupe renseignée via l'editeur "geoportail:entry" : _mtdLayersId, "geoportail:index" : _mtdLayersIdx, "geoportail:order" : null // notion d'ordre renseigné via l'editeur }, "layout" : { "visibility" : _tagVisible }, "paint" : _tagPaint, "filter" : _tagFilter }); } if (bFound) { break; } } } } } } if (_style.layers.length === 0) { // eslint-disable-next-line no-console console.warn("Filtres utilisateurs : aucun 'layers' (" + filter.filterName + ") !?"); } /* Exemple de style json pour un filtre attributaire (ex. Nature) : // { // "version": 8, // "name": "Nature", // "zoom": 14, // "center": [2.35,48.84], // "bounds": [], // "sources": { // "Enseignement": { // "type": "vector", // "tiles": ["https://vectortiles.ign.fr/rok4server/1.0.0/Enseignement/{z}/{x}/{y}.pbf"] // } // }, // "layers": [ // { // "id": "Collège", // "type": "circle", // "source": "Enseignement", // "source-layer": "Enseignement", // "layout": { // "visibility": "visible" // }, // "filter" : ["===", "nature", "Collège"], // "paint": null, // "metadata" : { // "geoportail:category" : "Nature", // "geoportail:filter" : 0, // "geoportail:group" : null, // "geoportail:entry" : "style enseignement", // "geoportail:index" : 0, // "geoportail:order" : null // } // } // // (...) // // "Autre", "Science", "Enseignement primaire", "Université", // // "Lycée", "Enseignement secondaire", "Enseignement supérieur", // ] // } */ return _style; }; /** * ... * @param {String} source - la source courante (id) * @param {Array} urls - l'url du service * @param {Object} filters - les filtres (option) * @param {Object} tilejson - le tileJson (metadata.json) * @param {Object} stylejson - les styles (json) * @returns {Object} style * @private * @sample * * var source = layer.get(mapbox-source); * var urls = layer.getSource().urls; * var filters = layer.get(mapbox-filters)["filters"]; * var tilejson = layer.get(mapbox-extensions); * var stylejson = style; // ceci est le style courant ! * // retourne les styles attributaires (json) * var customfiltersjson = _createCustomFiltersStyles(source, urls, filters, tilejson, stylejson); * */ var _createCustomFiltersStyles = function (source, urls, filters, tilejson, stylejson) { // on ajoute les filtres attributaires en fonction de la configuration demandée. // les filtres sont ajoutés directement dans l'objet _glStyle. // on peut aussi gérer les filtres sélectionnés. if (Array.isArray(filters)) { // on boucle sur les filtres utilisateurs for (var ii = 0; ii < filters.length; ii++) { var _filter = filters[ii]; var _conf = _filter.configuration || {}; // filtre courant var filter = { tableName : null, // recherche sur une table : ex. "table.champ" propertyName : _filter.propertyName, filterName : _filter.filterName, selected : (_conf && _conf.selected) ? _conf.selected : [], type : (_conf && _conf.type) ? _conf.type : 0 }; // le champ "propertyName" contient il le nom d'une table ? // ex. "table.champ" if (filter.propertyName.indexOf(".") !== -1) { var d = filter.propertyName.split("."); filter.tableName = d[0]; filter.propertyName = d[1]; } // on créé les styles (json) pour le filtre utilisateur courant. // cf. fonction _createCustomFilterStyle() pour plus d'informations var _customFilterStyle = _createCustomFilterStyle( source, // id de la source urls, // urls des services filter, // filtre : objet tilejson, // "metadata.json" stylejson // style json complet ); // some entries ? var _nlayers = _customFilterStyle.layers; if (!_nlayers) { break; } // // les selections des valeurs du filtre sont elles renseignées ? // // si oui, les valeurs sont donc déjà renseignées // // si non, il est utile de mettre les valeurs par defaut. // if (filter.selected && Array.isArray(filter.selected) && filter.selected.length) { // // nothing to do... // } else { // // il n'existe pas d'information sur les valeurs des filtres // // sélectionnées, on va donc mettre à jour cette information. // if (_selectedFilters) { // // maj des selections par defaut // // conf:undef -> (1) // // conf:0 -> (1) // // conf:1 -> (0) // // conf:2 -> (0) // for (var jj = 0; jj < _selectedFilters.length; jj++) { // if (_selectedFilters[jj].k === filter.filterName) { // // Array.fill() -> pas compatibilité IE 11 ! // _selectedFilters[jj].v = Array(_nlayers.length).fill((filter.type) ? 0 : 1); // break; // } // } // } // } // on ajoute les styles créés à partir du filtre attributaire // dans le style principale for (var kk = 0; kk < _customFilterStyle.layers.length; kk++) { // FIXME // si les filtres et les "layers" portent le même identifiant, // l'ajout des filtres va automatiquement doublonner // la properties interne : "mapbox-layers" stylejson.layers.push(_customFilterStyle.layers[kk]); } // FIXME // on se garde sous le coude le style brute pour un filtre attributaire // via les options, ce style sera archivé dans la properties "mapbox-filters"... _filter.style = _customFilterStyle; } } return stylejson; }; /****************************************************************************** * Internals properties observables *******************************************************************************/ /** * Les properties du vecteurs tuilés : * Elles permettent d'avoir une interactions avec le geoportail * ex. editeur * Liste : * - layer.set("mapbox-extensions") * objet * enregistrement du tileJSON * lecture seule * - layer.set("mapbox-layers") * tableau (string) * enregistrement des ID layers * enregistrement des ID styles attributaires (!?) * lecture seule * interne à openlayers * - layer.set("mapbox-filters") * objet * options:filters & options:filtersSummary * enregistrement des styles attributaires (json mapbox) * lecture/ecriture * maj à faire pour toute modification des styles attributaires ! * - map.set("mapbox-styles") * objet * enregistrement des styles mapbox de toutes les couches * (+ styles attributaires) * accès > map.get("mapbox-styles")[layerID] * lecture/ecriture * maj à faire pour toute modification du fichier de style d'une couche ! * - layer.set("mapbox-styles") * objet * enregistrement du style mapbox de la couche uniquement * lecture/ecriture * maj à faire pour toute modification du fichier de style de la couche ! * - layer.set("mapbox-themes") * objet * options:styles & options:stylesSummary * lecture seule * - layer.set("mapbox-status") * objet * enregistrement des statuts de la couche * lecture/ecriture * maj à faire si modification des selections du theme ou des filtres ! */ OlMap.MAPBOXPROPERTIES = { filters : "mapbox-filters", status : "mapbox-status", themes : "mapbox-themes", styles : "mapbox-styles", layers : "mapbox-layers", // property internal to olms source : "mapbox-source", // property internal to olms extensions : "mapbox-extensions", loaded : "mapbox-loaded" }; /** * callback sur la propriété observable : mapbox-filters * contexte (this) est un objet Gp.Map * @param {Object} e - {type, target, old, key} layer * @private */ OlMap.prototype._callbackMapBoxObservableFilters = function (e) { this.logger.warn("DEBUG:ObservableFilters", e, e.target.get(OlMap.MAPBOXPROPERTIES["filters"])); }; /** * callback sur la propriété observable : mapbox-status * contexte (this) est un objet Gp.Map * @param {Object} e - {type, target, old, key} layer * @private */ OlMap.prototype._callbackMapBoxObservableStatus = function (e) { this.logger.warn("DEBUG:ObservableStatus", e, e.target.get(OlMap.MAPBOXPROPERTIES["status"])); }; /** * callback sur la propriété observable : mapbox-themes * contexte (this) est un objet Gp.Map * @param {Object} e -{type, target, old, key} layer * @private */ OlMap.prototype._callbackMapBoxObservableThemes = function (e) { this.logger.warn("DEBUG:ObservableThemes", e, e.target.get(OlMap.MAPBOXPROPERTIES["themes"])); }; /** * callback sur la propriété observable sur la couche : mapbox-styles * contexte (this) est un objet Gp.Map * @param {Object} e - {type, target, old, key} layer * @private */ OlMap.prototype._callbackMapBoxObservableStyles = function (e) { this.logger.warn("DEBUG:ObservableStyles", e, e.target.get(OlMap.MAPBOXPROPERTIES["styles"])); }; /** * callback sur la propriété observable sur la carte : mapbox-styles * contexte (this) est un objet Gp.Map * @param {Object} e - {type, target, old, key} map * @private */ OlMap.prototype._callbackMapBoxObservableMapStyles = function (e) { this.logger.warn("DEBUG:ObservableMapStyles", e, e.target.get(OlMap.MAPBOXPROPERTIES["styles"])); }; /** * callback sur la propriété observable : mapbox-extensions * contexte (this) est un objet Gp.Map * @param {Object} e - {type, target, old, key} layer * @private */ OlMap.prototype._callbackMapBoxObservableExtensions = function (e) { this.logger.warn("DEBUG:ObservableExtensions", e, e.target.get(OlMap.MAPBOXPROPERTIES["extensions"])); }; /** * callback sur la propriété observable : mapbox-loaded * contexte (this) est un objet Gp.Map * @param {Object} e - {type, target, old, key} layer * @private */ OlMap.prototype._callbackMapBoxObservableLoaded = function (e) { this.logger.warn("DEBUG:ObservableLoaded", e, e.target.get(OlMap.MAPBOXPROPERTIES["loaded"])); }; /****************************************************************************** * Methods Object *******************************************************************************/ /** * Add a vector Layer MapBox to the map * * @param {Object} layerObj - geoportalLayer to add. * @param {Gp.LayerOptions} layerObj.geoportalLayerID - options of the layer * @returns {Object} Promise * * @sample * this._addMapBoxLayer({ geoportalLayerID : {} }) * .then(() => { ... }) * .catch ((e) => { ... }); * * @private */ OlMap.prototype._addMapBoxLayer = function (layerObj) { // carte courante var map = this.libMap; // "this" pour la closure var self = this; // closure avec "layerObj" en param : // layerObj = { // couche1 : {options} // couche2 : {options} // } return (function (_layerObj) { // options var layerId = Object.keys(_layerObj)[0]; var layerOpts = _layerObj[layerId]; // doit on utiliser cette méthode pour les options communes ? // self._applyCommonLayerParams(layerOpts); // url du style par defaut // avec proxification en fonction des parametres de proxy... var _urlDefaultOrSelected = self.setProxy(layerOpts.url); /* Gestion de l'url selectionnée : // Les options nous donnent l'url du style par defaut. // Mais, il faut aussi gerer le cas où un theme est sélectionné via les options, // car c'est bien l'url du style (ou theme) sélectionné qui doit être utilisé par la suite ! // De plus, on recherche si le style par defaut a été placé dans la liste des themes. // Si le style par defaut n'existe pas dans la liste des themes, on l'ajoute pour simplifier // les traitements ulterieurs... */ if (Array.isArray(layerOpts.styles)) { var foundDefaultStyle = false; // recherche du style par defaut var foundSelectedStyle = false; // recherche du theme sélectionné for (var i = 0; i < layerOpts.styles.length; i++) { var t = layerOpts.styles[i]; // algo assez simpliste... car on compare juste les urls // mais les urls devraient être uniques... if (t.url === layerOpts.url) { // le theme par defaut est dans la liste, // on prend donc en compte les valeurs // "name", "thumbnail", "url", "description" de la liste des // themes (à defaut des options "defaultTheme*") ! foundDefaultStyle = true; } if (t.selected) { // l'url selectionnée devient l'url par defaut _urlDefaultOrSelected = t.url; foundSelectedStyle = true; } } // le style par defaut n'est pas dans la liste, alors on l'ajoute dans // dans la liste des themes... if (!foundDefaultStyle) { var _url = layerOpts.url; var _thumbnail = layerOpts.defaultStyleThumbnail || null; var _name = layerOpts.defaultStyleName || "Style par défaut"; var _description = layerOpts.defaultStyleDescription || "Style par défaut"; layerOpts.styles.unshift({ thumbnail : _thumbnail, name : _name, url : _url, description : _description, selected : !foundSelectedStyle }); } } if (Array.isArray(layerOpts.filters)) { // cf. impl dans le tileJSON... } // traitements des styles/couches return fetch(_urlDefaultOrSelected, { credentials : "same-origin" }) .then(function (response) { if (response.ok) { response.json() .then(function (_glStyle) { // fusion des surcharges de l'option "mapboxOptions" var _overwrite = layerOpts.mapboxOptions; if (_overwrite && Object.keys(_overwrite).length) { var _lyrs = _overwrite.layers; // fusion des layers only ! if (_lyrs) { var _idx = _glStyle.layers.reduce((acc, it) => (acc[it.id] = it, acc), {}); // eslint-disable-line for (var i = 0; i < _lyrs.length; i++) { if (_idx[_lyrs[i].id]) { Utils.mergeParams(_idx[_lyrs[i].id], _lyrs[i]); } } } } // les sources var _glSources = _glStyle.sources; // les sources sont elles multiples ? var _multiSources = (Object.keys(_glSources).length > 1) ? 1 : 0; for (var _glSourceId in _glSources) { // format ol (mvt ou geojson) var vectorFormat = null; // source ol var vectorSource = null; // couche ol var vectorLayer = null; // tilejson ou metadata.json var vectorTileJson = null; if (_glSources.hasOwnProperty(_glSourceId)) { var _title = ""; var _description = ""; var _quicklookUrl = null; var _legends = null; var _metadata = null; var _originators = null; var _themes = null; var _filters = null; var _position = null; var _queryable = null; /* Gestion des metatdonnées du fichier de style (evolution) : // on recherche si des informations de metatdonnées sont disponibles // directement dans le fichier de style... // les informations de metadata ne viennent pas surchargées les options // déjà renseignées ! // lecture des informations de métadonnées dans le style : // ex. metadata : { // geoportail:[title | description | quicklookUrl | legends | originators | metadata | ...] // } */ if (_glStyle.metadata) { for (var ns in _glStyle.metadata) { if (_glStyle.metadata.hasOwnProperty(ns)) { var _keys = ns.split(":"); if (_keys[0] === "geoportail") { var key = _keys[1]; if (key === "title" && !layerOpts.title) { _title = layerOpts.title = _glStyle.metadata[ns]; continue; } if (key === "description" && !layerOpts.description) { _description = layerOpts.description = _glStyle.metadata[ns]; continue; } if (key === "quicklookUrl" && !layerOpts.quicklookUrl) { _quicklookUrl = layerOpts.quicklookUrl = _glStyle.metadata[ns]; continue; } if (key === "legends" && !layerOpts.legends) { _legends = layerOpts.legends = _glStyle.metadata[ns]; continue; } if (key === "metadata" && !layerOpts.metadata) { _metadata = layerOpts.metadata = _glStyle.metadata[ns]; continue; } if (key === "originators" && !layerOpts.originators) { _originators = layerOpts.originators = _glStyle.metadata[ns]; continue; } if (key === "styles" && !layerOpts.styles) { _themes = layerOpts.styles = _glStyle.metadata[ns]; continue; } if (key === "filters" && !layerOpts.filters) { _filters = layerOpts.filters = _glStyle.metadata[ns]; continue; } } } } } // ajout des informations issues des options : // titre et description par defaut if (!layerOpts.title) { layerOpts.title = "Couche MapBox"; } if (!layerOpts.description) { layerOpts.description = "Couche MapBox"; } // et le reste des informations issues des options : _title = (_multiSources) ? layerOpts.title + "(" + _glSourceId + ")" : layerOpts.title; _description = layerOpts.description; _quicklookUrl = layerOpts.quicklookUrl; _metadata = layerOpts.metadata; _legends = layerOpts.legends; _originators = layerOpts.originators; _position = layerOpts.position; _queryable = (typeof layerOpts.queryable === "undefined") ? true : layerOpts.queryable; // TODO // cas où les themes/filtres sont enregistrés sur une url // vers un fichier json par exemple. // gestion des themes avec les selections utilisateur. var _selectedTheme = {}; // options de themes (tableau) if (Array.isArray(layerOpts.styles)) { // enregistrement des options sur les themes _themes = layerOpts.styles; // selections utilisateurs (options selected) // > { key: null, index: 0 }