diff --git a/src/js/immersive-position.js b/src/js/immersive-position.js new file mode 100644 index 00000000..e059591e --- /dev/null +++ b/src/js/immersive-position.js @@ -0,0 +1,196 @@ +/** + * Copyright (c) Institut national de l'information géographique et forestière + * + * This program and the accompanying materials are made available under the terms of the GPL License, Version 3.0. + */ + +import proj4 from "proj4"; +proj4.defs("EPSG:2154","+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs"); + +const queryConfig = [ + { + layer: "LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:commune", + attributes: ["nom", "population"], + }, + { + layer: "LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:departement", + attributes: ["nom"], + }, + { + layer: "BDTOPO_V3:parc_ou_reserve", + attributes: ["nature", "toponyme"], + geom_name: "geometrie", + }, + { + layer: "BDTOPO_V3:foret_publique", + attributes: ["toponyme"], + geom_name: "geometrie", + around: 5, + }, + { + layer: "BDTOPO_V3:toponymie_lieux_nommes", + attributes: ["graphie_du_toponyme"], + geom_name: "geometrie", + around: 5, + additional_cql: "AND nature_de_l_objet='Bois'", + }, + { + layer: "LANDCOVER.FORESTINVENTORY.V2:formation_vegetale", + attributes: ["essence"], + around: 5, + epsg: 2154, + }, + { + layer: "RPG.LATEST:parcelles_graphiques", + attributes: ["code_cultu"], + around: 5, + }, + { + layer: "BDTOPO_V3:zone_d_activite_ou_d_interet", + attributes: ["nature", "toponyme"], + geom_name: "geometrie", + around: 5, + additional_cql: "AND categorie='Culture et loisirs'", + }, + { + layer: "BDTOPO_V3:cours_d_eau", + attributes: ["toponyme"], + geom_name: "geometrie", + around: 5, + }, + { + layer: "BDTOPO_V3:plan_d_eau", + attributes: ["nature", "toponyme"], + geom_name: "geometrie", + around: 5, + }, +]; + +/** + * Gestion de la "position immersive" avec des requêtes faites aux données autour d'une position + * @fires dataLoaded + */ +class ImmersivePosion extends EventTarget { + /** + * constructeur + * @param {*} options - + * @param {*} options.lat - latitude + * @param {*} options.lng - longitude + */ + constructor(options) { + super(); + this.options = options || { + lat : 0, + lng : 0, + }; + this.lat = this.options.lat; + this.lng = this.options.lng; + + this.data = {}; + + // récupération des codes culture pour RPG + this.codes_culture = {}; + fetch( + "https://data.geopf.fr/wfs/ows?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typename=RPG.LATEST:codes_cultures&outputFormat=json&count=1000" + ).then((resp) => resp.json()).then( (resp) => { + resp.features.forEach((feature) => { + this.codes_culture[feature.properties.code] = feature.properties.libelle; + }); + }); + } + + /** + * Computes html string from availmable data + */ + computeHtml() { + const htmlTemplate = ` +
Ville : ${this.data["LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:commune"] ? this.data["LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:commune"][0][0] : "chargement..."}, ${this.data["LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:commune"] ? this.data["LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:commune"][0][1] : "chargement..."} habitants
+Département : ${this.data["LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:departement"] ? this.data["LIMITES_ADMINISTRATIVES_EXPRESS.LATEST:departement"][0][0] : "chargement..."}
+Parcs naturels : ${this.data["BDTOPO_V3:parc_ou_reserve"] ? JSON.stringify(this.data["BDTOPO_V3:parc_ou_reserve"]) : "aucun"}
+Foret : ${this.data["BDTOPO_V3:foret_publique"] ? JSON.stringify(this.data["BDTOPO_V3:foret_publique"]) : "..."} ${this.data["BDTOPO_V3:toponymie_lieux_nommes"] ? JSON.stringify(this.data["BDTOPO_V3:toponymie_lieux_nommes"]) : "..."}
+Essence principale : ${this.data["LANDCOVER.FORESTINVENTORY.V2:formation_vegetale"] ? JSON.stringify(this.data["LANDCOVER.FORESTINVENTORY.V2:formation_vegetale"]) : "..."}
+Cultures : ${this.data["RPG.LATEST:parcelles_graphiques"] ? JSON.stringify(this.data["RPG.LATEST:parcelles_graphiques"]) : "..."}
+ZAI loisirs : ${this.data["BDTOPO_V3:zone_d_activite_ou_d_interet"] ? JSON.stringify(this.data["BDTOPO_V3:zone_d_activite_ou_d_interet"]) : "..."}
+Cours d'eau : ${this.data["BDTOPO_V3:cours_d_eau"] ? JSON.stringify(this.data["BDTOPO_V3:cours_d_eau"]) : "..."}
+Plans d'eau : ${this.data["BDTOPO_V3:plan_d_eau"] ? JSON.stringify(this.data["BDTOPO_V3:plan_d_eau"]) : "..."}
+ `; + return htmlTemplate; + } + + /** + * Computes all data queries + */ + computeAll() { + queryConfig.forEach( (config) => { + this.#computeFromConfig(config); + }); + } + + /** + * Queries GPF's WFS for info defined in the config + */ + async #computeFromConfig(config) { + const result = await this.#computeGenericGPFWFS( + config.layer, + config.attributes, + config.around || 0, + config.geom_name || "geom", + config.additional_cql || "", + config.epsg || 4326, + ); + + this.data[config.layer] = result; + + this.dispatchEvent( + new CustomEvent("dataLoaded", { + bubbles: true, + }) + ); + } + + /** + * Computes data for a given layer of Geoplateforme's WFS + * @param {string} layer name of the WFS layer + * @param {Array} attributes list of strings of the relevant attributes to return + * @param {number} around distance around the point in km for the query, default 0 + * @param {string} geom_name name of the geometry column, default "geom" + * @param {string} additional_cql cql filter needed other than geometry, e.g. "AND nature_de_l_objet='Bois'", default "" + * @param {number} epsg epsg number of the layer's CRS, default 4326 + */ + async #computeGenericGPFWFS(layer, attributes, around=0, geom_name="geom", additional_cql="", epsg=4326) { + let coord1 = this.lat; + let coord2 = this.lng; + if (epsg !== 4326) { + [coord1, coord2] = proj4(proj4.defs("EPSG:4326"), proj4.defs(`EPSG:${epsg}`), [this.lng, this.lat]); + } + let cql_filter = `INTERSECTS(${geom_name},Point(${coord1}%20${coord2}))`; + if (around > 0) { + cql_filter = `DWITHIN(${geom_name},Point(${coord1}%20${coord2}),${around},kilometers)`; + } + if (additional_cql) { + cql_filter += ` ${additional_cql}`; + } + + const results = await fetch( + `https://data.geopf.fr/wfs/ows?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typename=${layer}&outputFormat=json&count=10&CQL_FILTER=${cql_filter}` + ); + const json = await results.json(); + + const results_attributes = []; + json.features.forEach((feature) => { + const feature_attributes = []; + attributes.forEach((attribute) => { + // Cas particulier du RPG : décodage de la culture en libellé + if (layer === "RPG.LATEST:parcelles_graphiques" && attribute === "code_cultu" && Object.keys(this.codes_culture).length) { + feature.properties[attribute] = this.codes_culture[feature.properties[attribute]]; + } + feature_attributes.push(feature.properties[attribute]); + }); + results_attributes.push(feature_attributes); + }); + + return results_attributes; + } +} + +export default ImmersivePosion; diff --git a/src/js/layer-manager/layer-manager.js b/src/js/layer-manager/layer-manager.js index 9ba2ce47..a21fe98a 100644 --- a/src/js/layer-manager/layer-manager.js +++ b/src/js/layer-manager/layer-manager.js @@ -44,24 +44,24 @@ import LayersConfig from "./layer-config"; */ class LayerManager extends EventTarget { /** - * constructeur - * @param {*} options - - * @param {*} options.target - ... - * @param {*} options.layers - ... - * @example - * new LayerManger({ - * layers : [ - * layers : "couche1, couche2, ...", - * type : "base" // data ou thematic - * ] - * }); - */ + * constructeur + * @param {*} options - + * @param {*} options.target - ... + * @param {*} options.layers - ... + * @example + * new LayerManger({ + * layers : [ + * layers : "couche1, couche2, ...", + * type : "base" // data ou thematic + * ] + * }); + */ constructor(options) { super(); this.options = options || { /** - * ["layerid", "layer2id"] - */ + * ["layerid", "layer2id"] + */ layers : [], target : null }; diff --git a/src/js/my-account/my-account.js b/src/js/my-account/my-account.js index 904d19e1..3d304f1b 100644 --- a/src/js/my-account/my-account.js +++ b/src/js/my-account/my-account.js @@ -92,7 +92,6 @@ class MyAccount { Preferences.get( { key: "savedRoutes"} ).then( (resp) => { if (resp.value) { var localRoutes = JSON.parse(resp.value); - console.log(localRoutes); this.routes = this.routes.concat(localRoutes.filter( route => !route.type)); this.__updateAccountRoutesContainerDOMElement(this.routes); this.#updateSources(); diff --git a/src/js/position.js b/src/js/position.js index 97c690ad..c2cb5854 100644 --- a/src/js/position.js +++ b/src/js/position.js @@ -18,6 +18,7 @@ import ActionSheet from "./action-sheet"; import PopupUtils from "./utils/popup-utils"; import LoadingDark from "../css/assets/loading-darkgrey.svg"; +import ImmersivePosion from "./immersive-position"; /** * Permet d'afficher ma position sur la carte @@ -83,6 +84,8 @@ class Position { popup: null }; + this.immersivePosition = null; + return this; } @@ -417,7 +420,7 @@ class Position { async compute(options = {}) { const lngLat = options.lngLat || false; const text = options.text || "Repère placé"; - const html = options.html || ""; + let html = options.html || ""; const html2 = options.html2 || ""; const hideCallback = options.hideCallback || null; const type = options.type || "default"; @@ -438,6 +441,12 @@ class Position { text: text }; } + this.coordinates = position.coordinates; + + if (type === "myposition") { + this.immersivePosition = new ImmersivePosion({lat: this.coordinates.lat, lng: this.coordinates.lon}); + html = `