Skip to content

Commit

Permalink
feat(gfi): feature highlight
Browse files Browse the repository at this point in the history
  • Loading branch information
azarz committed Dec 21, 2023
1 parent 1d5522e commit d58d1ca
Show file tree
Hide file tree
Showing 11 changed files with 6,102 additions and 3,595 deletions.
9,393 changes: 5,857 additions & 3,536 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@mapbox/mapbox-gl-sync-move": "^0.3.1",
"@maplibre/maplibre-gl-compare": "^0.5.0",
"@maplibre/maplibre-gl-directions": "^0.4.2",
"@turf/union": "^6.5.0",
"chart.js": "^4.4.0",
"maplibre-gl": "^3.3.1"
},
Expand Down
2 changes: 1 addition & 1 deletion src/js/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Search from "./search";
import Compare from './compare';
import POI from './poi';
import RouteDraw from './route-draw/route-draw';
import MapInteractivity from './map-interactivity';
import MapInteractivity from './map-interactivity/map-interactivity';


/**
Expand Down
2 changes: 1 addition & 1 deletion src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import LayersConfig from './layer-config';
import Controls from './controls';
import RecentSearch from "./search-recent";
import MenuNavigation from './nav';
import InteractivityIndicator from './interactivity-indicator';
import InteractivityIndicator from './map-interactivity/interactivity-indicator';

// import CSS
import '@maplibre/maplibre-gl-compare/dist/maplibre-gl-compare.css';
Expand Down
2 changes: 1 addition & 1 deletion src/js/map-buttons-listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const addListeners = () => {
DOM.$routeDrawDelete.addEventListener("click", () => { Globals.routeDraw.toggleDelete(); });

// Indicateur d'interactivité
DOM.$interactivityBtn.addEventListener("click", () => { Globals.interactivity.showPopup(); });
DOM.$interactivityBtn.addEventListener("click", () => { Globals.interactivityIndicator.showPopup(); });

// Bouton Retour
DOM.$backTopLeftBtn.addEventListener("click", () => { State.onBackKeyDown(); });
Expand Down
15 changes: 15 additions & 0 deletions src/js/map-interactivity/feature-property-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
*
* @param {feature} feature maplibre issue de tuile vecteur
* @returns chaine de caractère HTML qui décrit la feature
*/
const featurePropertyFilter = (feature) => {
let result = ``;
Object.entries(feature.properties).forEach((prop) => {
result = result + `${prop[0]} : ${prop[1]} <br />`
})

return result;
}

export default featurePropertyFilter;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Globals from './globals';
import DOM from './dom';
import Globals from '../globals';
import DOM from '../dom';

import MapLibreGL from "maplibre-gl";

Expand All @@ -26,6 +26,9 @@ class InteractivityIndicator {

this.#listen();

// Do not clear highlighted feature this time
this.dontClear = false;

this.pii = false; // couche PII chargée ?
this.thematic = false; // couche thematic chargée ?
this.position = false; // couche en position max ?
Expand Down Expand Up @@ -56,6 +59,7 @@ class InteractivityIndicator {
if (this.pii && this.position && Math.round(e.target.getZoom()) > this.piiMinZoom) {
this.active();
} else {
this.dontClear = true;
(this.thematic && this.position) ? this.active() : this.disable();
}
});
Expand Down Expand Up @@ -105,6 +109,10 @@ class InteractivityIndicator {
* @param {boolean} hard l'interactivité reste désactivée tant qu'active() n'est pas appelée
*/
disable () {
if (!this.dontClear) {
Globals.mapInteractivity.clear();
}
this.dontClear = false;
this.shown = false;
DOM.$interactivityBtn.style.display = "none";
}
Expand Down
50 changes: 50 additions & 0 deletions src/js/map-interactivity/map-interactivity-styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// styles personnalisés
const layers = {
"line": {
id: "map-interactivity-line",
type: "line",
source: "",
layout: {
"line-cap": "butt",
"line-join": "round",
},
paint: {
"line-color": "#26a581",
"line-opacity": 0.85,
"line-width": 12,
}
},
"point": {
id: "map-interactivity-point",
type: "circle",
source: "",
paint: {
"circle-radius": 12,
"circle-color": "#26a581",
"circle-opacity": 0.85,
}
},
"polygon": {
id: "map-interactivity-polygon",
type: "fill",
source: "",
layout: {},
paint: {
"fill-color": "#26a581",
"fill-opacity": 0.65,
}
},
"polygon-outline": {
id: "map-interactivity-polygon-outline",
type: "line",
source: "",
layout: {},
paint: {
"line-color": "#26a581",
"line-opacity": 1,
"line-width": 3,
}
}
};

export default layers;
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import Globals from "./globals";
import Globals from "../globals";
import MapInteractivityLayers from "./map-interactivity-styles";
import featurePropertyFilter from "./feature-property-filter"

import Union from "@turf/union";


/**
* Interface sur l'interaction avec la carte
Expand All @@ -19,9 +24,9 @@ class MapInteractivity {

// configuration
this.configuration = this.options.configuration || {
linesource: "map-interactivity-line",
pointsource: "map-interactivity-point",
polygonsource: "map-interactivity-point",
linesource: "map-interactivity-line",
polygonsource: "map-interactivity-polygon",
}

// style
Expand All @@ -37,13 +42,24 @@ class MapInteractivity {
// carte
this.map = map;

this.#addSourcesAndLayers();

// fonction d'event avec bind
this.handleInfoOnMap = this.#getInfoOnMap.bind(this);

// annulation de la reqête fetch
this.controller = new AbortController();

// requête en cours d'execution ?
this.loading = false;

this.#listeners();

return this;
}

#listeners() {
this.map.on("click", (ev) => {this.#getInfoOnMap(ev); });
this.map.on("click", this.handleInfoOnMap);
}

#getInfoOnMap(ev) {
Expand All @@ -54,16 +70,18 @@ class MapInteractivity {
Globals.menu.close("position");
}
let features = this.map.queryRenderedFeatures(ev.point);
// TODO: Patience
this.map.off("click", this.handleInfoOnMap);
// On clique sur une feature tuile vectorielle
let featureHTML;
if (features.length > 0) {
console.log(features)
featureHTML = JSON.stringify(features[0].properties);
if (features.length > 0 && features[0].source == "bdtopo") {
featureHTML = featurePropertyFilter(features[0]);
if (features[0].source === "poi_osm") {
let featureName = features[0].properties.texte;
Globals.position.compute(ev.lngLat, featureName, featureHTML).then(() => {
Globals.menu.open("position");
});
this.map.on("click", this.handleInfoOnMap);
return;
}
}
Expand Down Expand Up @@ -94,16 +112,46 @@ class MapInteractivity {

this.#multipleGFI(layersForGFI)
.then((resp) => {
this.loading = false;
Globals.position.compute(ev.lngLat, resp.layer, resp.html).then(() => {
Globals.menu.open("position");
});
this.map.on("click", this.handleInfoOnMap);
return;
}).catch((err) => {
this.loading = false;
if (featureHTML && featureHTML != '{}') {
this.#clearSources();
Globals.position.compute(ev.lngLat, features[0].sourceLayer, featureHTML).then(() => {
Globals.menu.open("position");
let source;
const mapFeatures = this.map.queryRenderedFeatures();
let toFuse = [];
mapFeatures.forEach(feat => {
if (feat.source == "bdtopo" && feat.properties.cleabs == features[0].properties.cleabs) {
toFuse.push(feat);
}
});
let union = toFuse[0];
if (toFuse.length > 1) {
for (let i = 1; i < toFuse.length - 1; i++) {
union = Union(union, toFuse[i], {properties: union.properties})
}
}
if (features[0].geometry.type == "Point") {
source = this.map.getSource(this.configuration.pointsource);
} else if (features[0].geometry.type == "LineString") {
source = this.map.getSource(this.configuration.linesource);
} else {
source = this.map.getSource(this.configuration.polygonsource);
}
source.setData({
'type': 'FeatureCollection',
'features': [union],
});
});
}
this.map.on("click", this.handleInfoOnMap);
return;
});

Expand Down Expand Up @@ -131,16 +179,20 @@ class MapInteractivity {
}

async #multipleGFI(layerArray) {
let GFIArray = layerArray.filter(layer => layer[1].visibility == true)
this.loading = true;

let GFIArray = layerArray.filter(layer => layer[1].visibility == true);
GFIArray = GFIArray.filter(layer => layer[1].base == false);

// On récupère la liste des indices des layers non requêtables
let indexbase = []
for (var index = 0; index < layerArray.length; index++) {
if(layerArray[index][1].visibility && layerArray[index][1].base)
{
indexbase.push(index)
indexbase.push(index);
}
}
GFIArray = GFIArray.filter(layer => GFIArray.indexOf(layer) < Math.min(...indexbase));

let promisesArray = GFIArray.map((layer) => {
const response = fetch(
Expand All @@ -149,7 +201,8 @@ class MapInteractivity {
`LAYER=${layer[0].split('$')[0]}` +
`&TILECOL=${layer[1].tiles.tile.x}&TILEROW=${layer[1].tiles.tile.y}&TILEMATRIX=${layer[1].computeZoom}&TILEMATRIXSET=PM` +
`&FORMAT=${layer[1].format}` +
`&STYLE=${layer[1].style}&INFOFORMAT=text/html&I=${layer[1].tiles.tilePixel.x}&J=${layer[1].tiles.tilePixel.y}`
`&STYLE=${layer[1].style}&INFOFORMAT=text/html&I=${layer[1].tiles.tilePixel.x}&J=${layer[1].tiles.tilePixel.y}`,
{ signal: this.controller.signal }
).then((response => {return response.text()})
,
() => {
Expand All @@ -176,24 +229,83 @@ class MapInteractivity {
let response = (await responsesArray).find(r => r.status == "fulfilled");
if (response) {
let i = (await responsesArray).indexOf(response);
const isAboveThreshold = (v) => v > i;
// on ne retourne une infobulle que si la couche n'est pas recouverte par une couche non requêtable
if (indexbase.every(isAboveThreshold)) {
const result = {
layer: layerArray[i][1].title,
html: response.value,
};
return result;
}
else {
throw new Error("Under non requestable layer");
}
const result = {
layer: layerArray[i][1].title,
html: response.value,
};
return result;
}
else {
throw new Error(emptyError);
}
}
}


/**
* ajoute la source et le layer à la carte pour affichage du tracé
*/
#addSourcesAndLayers() {
this.map.addSource(this.configuration.pointsource, {
"type": "geojson",
"data": {
'type': 'FeatureCollection',
'features': []
},
});
this.map.addSource(this.configuration.linesource, {
"type": "geojson",
"data": {
'type': 'FeatureCollection',
'features': []
},
});
this.map.addSource(this.configuration.polygonsource, {
"type": "geojson",
"data": {
'type': 'FeatureCollection',
'features': []
},
});
MapInteractivityLayers["line"].source = this.configuration.linesource;
this.map.addLayer(MapInteractivityLayers["line"]);
MapInteractivityLayers["point"].source = this.configuration.pointsource;
this.map.addLayer(MapInteractivityLayers["point"]);
MapInteractivityLayers["polygon"].source = this.configuration.polygonsource;
this.map.addLayer(MapInteractivityLayers["polygon"]);
MapInteractivityLayers["polygon-outline"].source = this.configuration.polygonsource;
this.map.addLayer(MapInteractivityLayers["polygon-outline"]);
}

/**
* Supprime les donnés dans les sources
*/
#clearSources() {
this.map.getSource(this.configuration.pointsource).setData({
'type': 'FeatureCollection',
'features': []
});
this.map.getSource(this.configuration.linesource).setData({
'type': 'FeatureCollection',
'features': []
});
this.map.getSource(this.configuration.polygonsource).setData({
'type': 'FeatureCollection',
'features': []
});
}

/**
* nettoyage de la mise en surbrillance
* @public
*/
clear () {
if (this.loading) {
this.controller.abort();
this.controller = new AbortController();
this.loading = false;
}
this.#clearSources();
}
}

export default MapInteractivity;
Loading

0 comments on commit d58d1ca

Please sign in to comment.