diff --git a/DRAFT_CHANGELOG.md b/DRAFT_CHANGELOG.md
index 4955a6da9..24873d710 100644
--- a/DRAFT_CHANGELOG.md
+++ b/DRAFT_CHANGELOG.md
@@ -22,6 +22,7 @@ __DATE__
- ContextMenu: refacto et documentation du code du menu contextuel (#340)
- ContextMenu: Adresse : affichage du nom de commune quand il n'y a pas d'adresse (#351)
+ - Catalog : Ajout de plusieurs configurations thecniques avec possibilité de brancher le service de recherche de couches
* 🔥 [Deprecated]
diff --git a/package.json b/package.json
index 5ea89146f..70dd8848a 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "geopf-extensions-openlayers",
"description": "French Geoportal Extensions for OpenLayers libraries",
- "version": "1.0.0-beta.2-357",
- "date": "17/02/2025",
+ "version": "1.0.0-beta.2-356",
+ "date": "19/02/2025",
"module": "src/index.js",
"directories": {},
"engines": {
diff --git a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-default.html b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-default.html
index 6aad9e8f6..48c90fb68 100644
--- a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-default.html
+++ b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-default.html
@@ -39,6 +39,7 @@
Ajout du gestionnaire de catalogue avec options par defaut
Gp.Logger.enableAll();
}
var map;
+ var catalog;
var createMap = function() {
// on cache l'image de chargement du Géoportail.
document.getElementById('map').style.backgroundImage = 'none';
@@ -61,7 +62,7 @@
Ajout du gestionnaire de catalogue avec options par defaut
})
});
- var catalog = new ol.control.Catalog({
+ catalog = new ol.control.Catalog({
position: "top-left",
collapsed : false
});
diff --git a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-filter.html b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-filter.html
index 3ab6f94f2..165e9782b 100644
--- a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-filter.html
+++ b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-filter.html
@@ -58,13 +58,16 @@
Ajout du gestionnaire de catalogue avec option sur le filtrage des couches
var catalog = new ol.control.Catalog({
position: "top-left",
layerLabel : "name",
- layerFilter : [
+ layerFilters : [
{
field : "service",
- value : "WMTS"
+ logical : "=",
+ value : ["WMTS", "WMS", "TMS"]
},
{
-
+ field : "defaultProjection",
+ logical : "!",
+ value : ["EPSG:2154", "IGNF:LAMB93", "EPSG:4326"]
}
]
});
diff --git a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-data.html b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-config-data.html
similarity index 78%
rename from samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-data.html
rename to samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-config-data.html
index 30abfddfd..9391053d9 100644
--- a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-data.html
+++ b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-config-data.html
@@ -45,6 +45,7 @@
Ajout du gestionnaire de catalogue avec option sur la configuration
}
var data = getData();
var map;
+ var catalog;
var createMap = function() {
// on cache l'image de chargement du Géoportail.
document.getElementById('map').style.backgroundImage = 'none';
@@ -64,26 +65,28 @@
Ajout du gestionnaire de catalogue avec option sur la configuration
Ajout du gestionnaire de catalogue avec option sur la configuration (download)
+
+
+
+{{/content}}
+
+{{#content "js"}}
+
+{{/content}}
+
+{{/extend}}
diff --git a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-urls.html b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-config-urls.html
similarity index 89%
rename from samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-urls.html
rename to samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-config-urls.html
index 861990222..5466cf7c3 100644
--- a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-urls.html
+++ b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-config-urls.html
@@ -36,6 +36,7 @@
Ajout du gestionnaire de catalogue avec option sur la configuration (downloa
Gp.Logger.enableAll();
}
var map;
+ var catalog;
var createMap = function() {
// on cache l'image de chargement du Géoportail.
document.getElementById('map').style.backgroundImage = 'none';
@@ -55,16 +56,15 @@
Ajout du gestionnaire de catalogue avec option sur la configuration (downloa
})
});
- var catalog = new ol.control.Catalog({
+ catalog = new ol.control.Catalog({
position: "top-left",
- configuration : {
+ configurations : [{
type : "json",
urls : [
- // INFO facultatif si la config est déjà chargée
- // "https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/layers.json",
+ "https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/layers.json",
"https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/edito.json"
]
- }
+ }]
});
catalog.on("catalog:loaded", (e) => {
console.log(e);
diff --git a/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-configs.html b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-configs.html
new file mode 100644
index 000000000..e7d3f16ad
--- /dev/null
+++ b/samples-src/pages/tests/Catalog/pages-ol-catalog-modules-dsfr-type-configs.html
@@ -0,0 +1,114 @@
+{{#extend "ol-sample-modules-dsfr-layout"}}
+
+{{#content "vendor"}}
+
+
+
+{{/content}}
+
+{{#content "head"}}
+ Sample OpenLayers LayerCatalog
+{{/content}}
+
+{{#content "style"}}
+
+{{/content}}
+
+{{#content "body"}}
+
Ajout du gestionnaire de catalogue avec option sur la configuration (download)
+
+
+
+{{/content}}
+
+{{#content "js"}}
+
+{{/content}}
+
+{{/extend}}
diff --git a/samples-src/resources/data/configuration/sample.json b/samples-src/resources/data/configuration/sample.json
index 61efb4b88..8b2389753 100644
--- a/samples-src/resources/data/configuration/sample.json
+++ b/samples-src/resources/data/configuration/sample.json
@@ -12,6 +12,7 @@
"layers": {
"GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2$GEOPORTAIL:OGC:WMTS": {
"name": "GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2",
+ "overload" : true,
"title": "Plan IGN v2",
"description": "Cartographie multi-échelles sur le territoire national, issue des bases de données vecteur de l’IGN, mis à jour régulièrement et réalisée selon un processus entièrement automatisé.",
"globalConstraint": {
@@ -181,6 +182,7 @@
},
"GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2$GEOPORTAIL:OGC:WMS": {
"name": "GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2",
+ "overload" : true,
"title": "Plan IGN v2",
"description": "Cartographie multi-échelles sur le territoire national, issue des bases de données vecteur de l’IGN, mis à jour régulièrement et réalisée selon un processus entièrement automatisé.",
"globalConstraint": {
@@ -241,6 +243,7 @@
"PLAN.IGN$GEOPORTAIL:GPP:TMS": {
"hidden": true,
"queryable": false,
+ "overload" : true,
"serviceParams": {
"id": "GPP:TMS",
"version": "1.0.0",
@@ -322,6 +325,7 @@
"defaultProjection": "EPSG:3857"
},
"BDTOPO_V3:batiment$GEOPORTAIL:OGC:WFS": {
+ "overload" : true,
"name": "BDTOPO_V3:batiment",
"title": "BDTOPO : Bâtiments",
"description": "Bâtiments - BDTOPO",
diff --git a/src/packages/Controls/Catalog/Catalog.js b/src/packages/Controls/Catalog/Catalog.js
index 8ee8e9661..435f0a9c2 100644
--- a/src/packages/Controls/Catalog/Catalog.js
+++ b/src/packages/Controls/Catalog/Catalog.js
@@ -18,6 +18,9 @@ import GeoportalWMS from "../../Layers/LayerWMS";
import GeoportalWMTS from "../../Layers/LayerWMTS";
import GeoportalMapBox from "../../Layers/LayerMapBox";
+// Service
+import Search from "../../Services/Search";
+
// DOM
import CatalogDOM from "./CatalogDOM";
@@ -33,7 +36,7 @@ var logger = Logger.getLogger("widget");
* @type {ol.control.Catalog}
* @extends {ol.control.Control}
* @param {Object} options - options for function call.
- *
+ *
* @fires catalog:loaded
* @fires catalog:layer:add
* @fires catalog:layer:remove
@@ -46,7 +49,11 @@ var logger = Logger.getLogger("widget");
* titlePrimary : "",
* titleSecondary : "Gérer vos couches de données",
* layerLabel : "title",
- * layerFilter : [],
+ * layerFilters : [{
+ * field : "service",
+ * logical : "=",
+ * value : ["WMS"]
+ * }],
* search : {
* display : true,
* criteria : [
@@ -75,22 +82,35 @@ var logger = Logger.getLogger("widget");
* // ]
* }
* ],
- * configuration : {
- * type : "json", // type:"service"
- * urls : [ // data:{}
+ * configurations : [
+ * {
+ * type : "json",
+ * urls : [
* "https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/layers.json",
* "https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/edito.json"
* ]
- * }
+ * },
+ * {
+ * type : "data",
+ * data : {}
+ * },
+ * {
+ * type : "service",
+ * url : "https:// data.geopf.fr/recherche/api/indexes/geoplateforme/suggest",
+ * filter : {
+ * field : "theme",
+ * value : ["Administratif","Foncier","Agriculture","Forêt","Hydrographie, mer et littoral"]
+ * }
+ * }
+ * ]
* });
* widget.on("catalog:loaded", (e) => { console.log(e.data); });
* widget.on("catalog:layer:add", (e) => { console.log(e); });
* widget.on("catalog:layer:remove", (e) => { console.log(e); });
* map.addControl(widget);
*
- * @todo filtrage des couches
- * @todo type:service
* @todo validation du schema
+ * @todo conf tech du service de recherche
*/
var Catalog = class Catalog extends Control {
@@ -110,7 +130,17 @@ var Catalog = class Catalog extends Control {
* titlePrimary : "",
* titleSecondary : "Gérer vos couches de données",
* layerLabel : "title",
- * layerFilter : [],
+ * layerFilters : [
+ * {
+ * field : "service",
+ * value : ["WMTS", "TMS"],
+ * },
+ * {
+ * field : "defaultProjection",
+ * logical : "!",
+ * value : ["EPSG:2154", "IGNF:LAMB93"]
+ * }
+ * ],
* search : {
* display : true,
* criteria : [
@@ -139,13 +169,13 @@ var Catalog = class Catalog extends Control {
* // ]
* }
* ],
- * configuration : {
- * type : "json", // type:"service"
- * urls : [ // data:{}
+ * configurations : [{
+ * type : "json",
+ * urls : [
* "https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/layers.json",
* "https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/edito.json"
* ]
- * }
+ * }]
* });
* widget.on("catalog:loaded", (e) => { console.log(e.data); });
* widget.on("catalog:layer:add", (e) => { console.log(e); });
@@ -302,7 +332,7 @@ var Catalog = class Catalog extends Control {
titlePrimary : "Gérer vos couches de données",
titleSecondary : "",
layerLabel : "title",
- layerFilter : [], // TODO filtre
+ layerFilters : [],
search : {
display : true,
criteria : [
@@ -333,13 +363,14 @@ var Catalog = class Catalog extends Control {
// ]
}
],
- configuration : {
- type : "json", // TODO type:"service"
- urls : [ // data:{}
+ configurations : [{
+ type : "json",
+ default : true,
+ urls : [
"https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/layers.json",
"https://raw.githubusercontent.com/IGNF/cartes.gouv.fr-entree-carto/main/public/data/edito.json"
]
- }
+ }]
};
// merge with user options
@@ -522,7 +553,7 @@ var Catalog = class Catalog extends Control {
* @private
*/
async initLayersList () {
- var data = null; // reponse brute du service
+ var data = {}; // reponse
var self = this;
const createCatalogContentEntries = (layers) => {
@@ -583,128 +614,236 @@ var Catalog = class Catalog extends Control {
return layersCategorised;
};
- // TODO filtre sur la liste de couches à prendre en compte
- const getLayersByFilter = (filter, layers) => {
+ // filtre sur la liste de couches à prendre en compte
+ const getLayersByFilter = (filters, layers) => {
// INFO
// definir les filtres possibles :
// - sur un champ spécifique : ex field:"service"
// - sur des valeurs : ex. value:"[WMS,TMS,WMTS]" ou "*"
// - ...
+ if (filters && filters.length) {
+ var layersFilter = {...layers}; // clone
+ for (let i = 0; i < filters.length; i++) {
+ const filter = filters[i];
+ for (const key in layersFilter) {
+ if (Object.prototype.hasOwnProperty.call(layersFilter, key)) {
+ const layer = layers[key];
+ var include = Array.isArray(filter.value) ? filter.value.includes(layer[filter.field].toString()) : (filter.value === "*" || layer[filter.field].toString() === filter.value);
+ var exclude = (filter.logical && filter.logical === "!") ? true : false;
+ if ((exclude && include) || (!exclude && !include)) {
+ delete layersFilter[key];
+ }
+ }
+ }
+ }
+ return layersFilter;
+ }
return layers;
};
- if (this.options.configuration.data) {
- data = this.options.configuration.data || {};
-
- // TODO gestion du type service
+ // conversion de schema service vers data
+ const convertSchema = (extra) => {
+ return {};
+ };
- if (Config.isConfigLoaded()) {
- Utils.mergeParams(data, Config.configuration);
+ var ids = [];
+ const configs = this.options.configurations;
+ for (let i = 0; i < configs.length; i++) {
+ const config = configs[i];
+ config.ref = (i === 0); // reference
+
+ if (config.type === "data" && config.data) {
+ if (!config.ref) {
+ Object.keys(config.data.layers).forEach((id) => {
+ if (!ids.includes(id)) {
+ delete config.data.layers[id];
+ }
+ });
+ }
+ Utils.mergeParams(data, config.data || {});
+ console.debug("config.type === data finish !");
}
+
+ if (config.type === "json" && config.urls) {
+ var fetchUrls = [];
+ for (let i = 0; i < config.urls.length; i++) {
+ const url = config.urls[i];
+ const fetchUrl = function () {
+ return fetch(url, {})
+ .then(function (response) {
+ if (response.ok) {
+ return response.json()
+ .then(function (json) {
+ return json;
+ })
+ .catch(error => {
+ logger.warn("fetch json exception :", error);
+ });
+ } else {
+ var err = new Error("HTTP status code: " + response.status);
+ throw err;
+ }
+ })
+ .catch(error => {
+ return new Promise((resolve, reject) => {
+ logger.error("fetch json exception :", error);
+ reject(error);
+ });
+ });
+ };
+ fetchUrls.push(fetchUrl());
+ }
+
+ try {
+ const values = await Promise.all(fetchUrls);
+ for (let i = 0; i < values.length; i++) {
+ const value = values[i];
+ if (!config.ref) {
+ Object.keys(value.layers).forEach((id) => {
+ if (!ids.includes(id)) {
+ delete value.layers[id];
+ }
+ });
+ }
+ Utils.mergeParams(data, value);
+ }
+ console.debug("config.type === json finish !");
- // INFO
- // on en profite pour ajouter des properties :
- // - service : utile pour identifier la couche
- // de manière unique : name + service
- // - categories : utile pour definir l'appartenance d'une couche
- // à une ou plusieurs categories
- for (const key in data.layers) {
- if (Object.prototype.hasOwnProperty.call(data.layers, key)) {
- const layer = data.layers[key];
- var service = layer.serviceParams.id.split(":").slice(-1)[0]; // beurk!
- layer.service = service; // new proprerty !
- layer.categories = []; // new property ! vide pour le moment
+ } catch (e) {
+ return new Promise((resolve, reject) => {
+ reject(e);
+ });
}
}
+
+ if (config.type === "service" && config.url && config.filter) {
+ // on obtient une réponse du service de recherche selon un filtre :
+ // - ex. une liste de themes
+ // - ex. un producteur
+ // - ex. un texte libre
+ // et, on convertit la reponse du service de recherche vers le schema de 'data'
+ // puis, on procede à un merge des résultats
+
+ const filter = config.filter;
+ const url = filter.url;
+ if (url) {
+ Search.setUrl(url);
+ }
+ Search.setMaximumResponses(100);
+ // on utilise :
+ // soit un filtre ou
+ // soit un champ libre !
+ const field = filter.field;
+ const value = filter.value;
+ // filtre
+ Search.setFields(field);
+ if (field === "text") {
+ Search.setFields("layer_name");
+ }
+ // une valeur ou une liste de valeurs ?
+ var values = Array.isArray(value) ? value : [value];
+
+ var fetchSuggests = [];
+ for (let i = 0; i < values.length; i++) {
+ const text = values[i];
+ fetchSuggests.push(Search.suggest(text));
+ }
- // on applique un filtre sur la liste des couches
- var layers = getLayersByFilter(this.options.layerFilter, data.layers);
-
- // sauvegarde de la liste des couches
- this.layersList = layers;
-
- createCatalogContentEntries(layers);
- return new Promise((resolve, reject) => {
- resolve(data);
- });
- }
-
- if (this.options.configuration.urls) {
- var fetchUrls = [];
- for (let i = 0; i < this.options.configuration.urls.length; i++) {
- const url = this.options.configuration.urls[i];
- const fetchUrl = function () {
- return fetch(url, {})
- .then(function (response) {
- if (response.ok) {
- return response.json()
- .then(function (json) {
- return json;
- })
- .catch(error => {
- logger.warn("fetch json exception :", error);
- });
- } else {
- var err = new Error("HTTP status code: " + response.status);
- throw err;
+ try {
+ const lstValues = await Promise.all(fetchSuggests);
+ console.debug(lstValues);
+ // transformation de schema :
+ // values --> data.layers
+ var data_tmp = {
+ layers : {}
+ };
+ for (let i = 0; i < lstValues.length; i++) {
+ const values = lstValues[i]; // liste de reponse de chaque fetch
+ for (let j = 0; j < values.length; j++) {
+ const v = values[j];
+ // on ajoute les 2 properties pour le moment
+ var id = `${v.name}.$GEOPORTAIL:OGC:${v.service}`;
+ data_tmp.layers[id] = {
+ theme : v.theme || "",
+ producer : v.producer || ""
+ };
+ // TODO transformation de schema de la conf tech
+ // renvoyé par le service
+ if (v.extra) {
+ Utils.mergeParams(data_tmp.layers[id], convertSchema(v.extra));
+ }
+ }
+ }
+ if (!config.ref) {
+ Object.keys(data_tmp.layers).forEach((id) => {
+ if (!ids.includes(id)) {
+ delete data_tmp.layers[id];
}
- })
- .catch(error => {
- return new Promise((resolve, reject) => {
- logger.error("fetch json exception :", error);
- reject(error);
- });
});
- };
- fetchUrls.push(fetchUrl());
- }
+ }
+ Utils.mergeParams(data, data_tmp);
- try {
- const values = await Promise.all(fetchUrls);
+ console.debug("config.type === service finish !");
- data = values[0];
- for (let i = 1; i < values.length; i++) {
- const value = values[i];
- Utils.mergeParams(data, value);
+ // for (const id in data_tmp.layers) {
+ // if (Object.prototype.hasOwnProperty.call(data_tmp.layers, id)) {
+ // if (id in data.layers) {
+ // Utils.mergeParams(data.layers[id], data_tmp.layers[id]);
+ // }
+ // }
+ // }
+
+ } catch (e) {
+ return new Promise((resolve, reject) => {
+ reject(e);
+ });
}
+ }
- // TODO gestion du type service
+ // liste de couche de reference à traiter uniquement !
+ if (config.ref) {
+ ids = Object.keys(data.layers);
+ console.debug(ids);
+ }
+ }
- if (Config.isConfigLoaded()) {
- Utils.mergeParams(data, Config.configuration);
- }
+ // on choisie d'utiliser la config chargée par defaut
+ // si aucune configuration n'a été précisé !
+ if (Config.isConfigLoaded() && this.options.configurations[0].default) {
+ Utils.mergeParams(data, Config.configuration);
+ }
- // INFO
- // on en profite pour ajouter des properties :
- // - service : utile pour identifier la couche
- // de manière unique : name + service
- // - categories : utile pour definir l'appartenance d'une couche
- // à une ou plusieurs categories
- for (const key in data.layers) {
- if (Object.prototype.hasOwnProperty.call(data.layers, key)) {
- const layer = data.layers[key];
- var service = layer.serviceParams.id.split(":").slice(-1)[0]; // beurk!
- layer.service = service; // new proprerty !
- layer.categories = []; // new property ! vide pour le moment
- }
+ // INFO
+ // on en profite pour ajouter des properties :
+ // - service : utile pour identifier la couche
+ // de manière unique : name + service
+ // - categories : utile pour definir l'appartenance d'une couche
+ // à une ou plusieurs categories
+ for (const id in data.layers) {
+ if (Object.prototype.hasOwnProperty.call(data.layers, id)) {
+ const layer = data.layers[id];
+ // clean des conf partielles
+ if (!("serviceParams" in layer)) {
+ delete data.layers[id];
+ continue;
}
+ var service = layer.serviceParams.id.split(":").slice(-1)[0]; // beurk!
+ layer.service = service; // new proprerty !
+ layer.categories = []; // new property ! vide pour le moment
+ }
+ }
- // on applique un filtre sur la liste des couches
- var layers = getLayersByFilter(this.options.layerFilter, data.layers);
+ // on applique un filtre sur la liste des couches
+ var layers = getLayersByFilter(this.options.layerFilters, data.layers);
- // sauvegarde de la liste des couches
- this.layersList = layers;
+ // sauvegarde de la liste des couches
+ this.layersList = layers;
- createCatalogContentEntries(layers);
- return await new Promise((resolve, reject) => {
- resolve(data);
- });
- } catch (e) {
- return await new Promise((resolve, reject) => {
- reject(e);
- });
- }
- }
+ createCatalogContentEntries(layers);
+
+ return new Promise((resolve, reject) => {
+ resolve(data);
+ });
}
// ################################################################### //
diff --git a/src/packages/Controls/Catalog/config-sample.png b/src/packages/Controls/Catalog/config-sample.png
new file mode 100644
index 000000000..317867bc8
Binary files /dev/null and b/src/packages/Controls/Catalog/config-sample.png differ
diff --git a/src/packages/Controls/Catalog/doc.pdf b/src/packages/Controls/Catalog/doc.pdf
new file mode 100644
index 000000000..4c70d2697
Binary files /dev/null and b/src/packages/Controls/Catalog/doc.pdf differ