mviewer / mviewer

Visualiseur géographique thématique basé sur OpenLayers 8 et Bootstrap 3.3.6 / cartographic application based on OpenLayers and Bootstrap
https://mviewer.github.io/fr/
GNU General Public License v3.0
86 stars 99 forks source link

Ajout d'un nbFeatureCount sur les CustomLayers #731

Open bdavid-rennesmetrpole opened 1 year ago

bdavid-rennesmetrpole commented 1 year ago

Le paramètre de couche "nbFeatureCount permet de limiter le nombre de résultats retournés au clic sur cette couche. Cet attribut fonctionne très bien pour les couches de type WMS mais pas sur les CustomLayers. La différence entre les deux types de couche est que le WMS fait un appel au geoserver à chaque consultation de couche tandis que pour les customLayers, toutes les données sont chargées au démarrage de l'appli, et il n'y a plus d'appel serveur par la suite. Le geoserver gère le nombre de résultats retournés dans la limite du nbfeaturecount.

=> Pour les couches vectorielles, il faudrait que ce soit le mviewer qui limite le nombre de résultats en fonction de la position les features par rapport au coordonnées cliquées, tout en maintenant la tolérance par défaut du Mviewer. Seules les X features les plus proches des coordonnées sont retournées

La solution serait d'ajouter un champ dans les layers du XML nommé featureCount afin d'apporter la limite par couche et de limiter a partir des features retournées le nombre de features affichées.

Exemple: XML =>

<layer
   id="ete_glisse"
   name="Spots de glisse"
   type="customlayer"
   url="apps/site_internet/customlayer/ete-glisse.js"
   legendurl="apps/site_internet/img/legend/ete-glisse.svg"
   visible="true"
   tooltip="false"
   tooltipenabled="false"
   tooltipcontent=""
   metadata="undefined"
   metadata-csw="https://public.sig.rennesmetropole.fr/geonetwork/srv/fre/catalog.search#/metadata/46a39b8c-5ec4-449a-bfe1-8410d0b76c84"
   queryable="true"
   featurecount="6"
   tabcolor='#E44C2D'
   infopanel="right-panel"
   featureCount="3"
>

js/info.js =>

var layerCount = [];
var clickCoos = evt.coordinate;

_map.forEachFeatureAtPixel(pixel, function (feature, layer) {
  var l = layer.get("mviewerid");
  var featureCoos = feature.values_.geometry.flatCoordinates;
  var featureDistanceToClick = Math.sqrt(Math.pow((clickCoos[0] - featureCoos[0]),2) + Math.pow((clickCoos[1] - featureCoos[1]),2));
  feature.distance = featureDistanceToClick;
  feature.mviewerid = l;
  feature.infohighlight = layer.get("infohighlight");
  if (layerCount[l]) {
     layerCount[l].push(feature);
  else{
     feature.distance = featureDistanceToClick;
     layerCount[l] = [feature];

  layerCount[l].sort(compare);
  var layerIds = configuration.getConfiguration().themes.theme[0].layer;
  layerIds.forEach((item) => {
     if (item.id == l) {
        layerCount[l].splice(item.featureCount,layerCount[l].length);   
  });
});

for (var key in layerCount) {
 layerCount[key].forEach((feature) => {
  var l = feature.mviewerid;
  if (
     l &&
     l != "featureoverlay" &&
     l != "selectoverlay" &&
     l != "subselectoverlay" &&
     l != "elasticsearch"
     ) {
        var queryable = _overLayers[l].queryable;
        if (queryable) {
           if (feature.infohighlight) {
              _queriedFeatures.push(feature);
           } else {
             showPin = true;
           }
           if (vectorLayers[l] && vectorLayers[l].features) {
             vectorLayers[l].features.push(feature);
           } else {
             if (
               _overLayers[l] &&
               _panelsTemplate[_overLayers[l].infospanel] == "allintabs"
             ) {
               l = l + "_#" + f_idx;
               f_idx++;
             }
             vectorLayers[l] = { features: [] };
             vectorLayers[l].features.push(feature);
           }
         }
       }
     });
   };

et l'ajout d'une nouvelle fonction de comparaison au sein du mviewer (toujours dans js/infos.js)

  function compare( a, b ) {
    if ( a.distance < b.distance ){
      return -1;
    }
    if ( a.distance > b.distance ){
      return 1;
    }
    return 0;
  }

Petite note, de notre coté nous avons cherché dans openlayers si une fonction permettait de calculer la position entre un clic et une feature comme fait plus haut dans la variable 'featureDistanceToClick' mais aucune recherche n'a été fructueuse. Nous avons donc retenu de faire un calcul vectoriel pour ce faire.

Gaetanbrl commented 1 year ago

Si je comprend bien, votre besoin est de limiter le nombre de features vectorielles retournées au clic sur la carte. Pour ordonner la liste des features, vous calculez la distance entre chaque features retournées pour les ordonner et récupérer les <x="nombre souhaité"> premières.

C'est cela ?

bdavid-rennesmetrpole commented 1 year ago

Oui, c'est un bon résumé.

Gaetanbrl commented 1 year ago

nous avons cherché dans openlayers si une fonction permettait de calculer la position entre un clic et une feature

Il existe getDistance entre 2 coordonnées :

https://openlayers.org/en/latest/apidoc/module-ol_sphere.html#.getDistance

Gaetanbrl commented 1 year ago

Le code pourrait être optimisé mais je pense que c'est assez bien vu de limiter par couche. Je pense que si on clic a grande échelle sur une coordonnées avec une grande densité de point, dans tous les cas je ne vois pas si l'utilisateur a une idée précise de ce qu'il cherche. Donc retourner les N plus proches sera juste un moyen de limiter le nombre de retour le plus "logique possible".

Est-ce que ce serait pas aussi bien de juste limiter le nombre sans prendre en compte la distance ? Les 5 premiers rencontrés sont retournés. Si on veut plus de précision on zoom.