RSS Libre@vous
RSS de la rubrique Géomatique

Dernière mise à jour
le 14/01/2016 à 15h43
Les contenus de ce site sont publiés sous la licence CC by-sa, sauf mention contraire.
licence_CC-by-sa
Copyright © 2019 Libre @ vous. Tous droits réservés.
Joomla! est un logiciel libre sous licence GNU/GPL.
11
Mar
2011
OpenLayers : limiter l'affichage d'une couche à certains niveaux de zoom Imprimer
Pour les besoins d'une interface web cartographique que je suis en train de réaliser avec OpenLayers, je dois afficher par dessus le fond OpenStreetMap une couche TMS qui contient les ombrages du relief issue du SRTM de la NASA. Mon problème c'est que je ne dois afficher cette couche que entre certains niveaux de zoom, la couche n'étant pas disponible sur la Terre entière ni au très forts niveaux de zooms. Je vais expliquer comment j'ai géré ce problème.

Pour un zoom plus petit que 5 ou plus grand que 15, la couche ne devait pas s'afficher. Je suis allé jeter un coup d'oeil sur la page http://trac.osgeo.org/openlayers/wiki/S … oomLevels, mais ce n'était vraiment pas très clair pour moi... Mais un exemple sur le site OpenLayers m'avait donné une (mauvaise) idée : http://openlayers.org/dev/examples/events.html Dans cette première version, j'avais utilisé un eventListener sur l'événement zoomend déclenché à chaque fois qu'un changement zoom était réalisé par l'utilisateur. JF Gigand a soulevé avec raison plusieurs problèmes posés par cette solution (voir le premier commentaire ci-dessous).

J'ai donc changé la méthode pour limiter l'affichage en fonction du zoom. En fait c'était plus simple et j'étais complètement passé à côté dans la doc d'OpenLayers (que je trouve d'ailleurs très mal faite, mais ceci est un autre débat...). Suivant les conseils de JF, j'ai fait une deuxième version beaucoup plus propre en utilisant les propriétés de la couche alwaysInRange, maxResolution et minResolution, ainsi que la fonction getResolutionForZoom.

Je laisse la première version, juste pour que l'on puisse encore comprendre le commentaire de JF (ne faites pas ça !) :

<script type="text/javascript">
function osm_getTileURL(bounds) {
 var res = this.map.getResolution();
 var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
 var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
 var z = this.map.getZoom();
 var limit = Math.pow(2, z);

 if (y < 0 || y >= limit) {
 return OpenLayers.Util.getImagesLocation() + "404.png";
 } else {
 x = ((x % limit) + limit) % limit;
 return this.url + z + "/" + x + "/" + y + "." + this.type;
 }
 }

function layerVisibility(event) {
 map = event.object;
 layer = map.getLayersByName('Relief (NASA SRTM3)')[0];
 if (map.getZoom() >= 5 || map.getZoom() <= 15) {
   layer.setVisibility(true);
  }
 else {
   layer.setVisibility(false);
  }
}

function init() {
options = {
   projection: epsg900913,
   displayProjection: epsg4326,
   units: "m",
   maxResolution: 156543.0339,
   maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
   eventListeners: {
    "zoomend": layerVisibility
  }
};
 var map = new OpenLayers.Map('',options);
 var osmlayer = new OpenLayers.Layer.OSM("OpenStreetMap");
 var hill = new OpenLayers.Layer.TMS("Relief (NASA SRTM3)",
 "http://toolserver.org/~cmarqu/hill/",
 {
   type: 'png', getURL: osm_getTileURL,
   displayOutsideMaxExtent: true, isBaseLayer: false,
   transparent: true, "visibility": true
 }
 );
 map.addLayers([osmlayer,hill]);
}
</script>



Et la deuxième version qui est la bonne :
 <script type="text/javascript">
function osm_getTileURL(bounds) {
var res = this.map.getResolution();
var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
var z = this.map.getZoom();
var limit = Math.pow(2, z);

if (y < 0 || y >= limit) {
return OpenLayers.Util.getImagesLocation() + "404.png";
} else {
x = ((x % limit) + limit) % limit;
return this.url + z + "/" + x + "/" + y + "." + this.type;
}
}

function init() {
 options = {
  projection: epsg900913,
  displayProjection: epsg4326,
  units: "m",
  maxResolution: 156543.0339,
  maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
 };
var map = new OpenLayers.Map('',options);
 var osmlayer = new OpenLayers.Layer.OSM("OpenStreetMap");
 var hill = new OpenLayers.Layer.TMS("Relief (NASA SRTM3)",
 "http://toolserver.org/~cmarqu/hill/",
 {
   type: 'png', getURL: osm_getTileURL,
   displayOutsideMaxExtent: true, isBaseLayer: false,
   transparent: true, "visibility": true,
   alwaysInRange: false,
   maxResolution: map.getResolutionForZoom(5),
   minResolution: map.getResolutionForZoom(15)
 }
 );
 map.addLayers([osmlayer,hill]);
}
</script>


Je tiens bien entendu à remercier JF pour son retour et son aide qui m'ont permis de faire quelque chose de propre. Complice
 

Commentaires  

 
#1 JF Gigand 14-03-2011 01:29
Salut,

C'est pas très propre de modifier la visibilité de layers au zoomend. Peut-être que les requêtes HTTP sont faites pour les tuiles, brièvement avant que la visibilité soit set false. Le nom de la couche est en dur pour getLayerByName, et le pire, le setVisibility() est statique. Donc la couche sera ré-affichée si le zoom passe de 4 à 5 même si l'utilisateur souhaite qu'elle reste invisible...

Le problème vient de la confusion du concept "visibility" avec le concept "inrange".

OL gère nativement ça. Si tu définis minResolution et maxResolution, la propriété "inrange" sera mise à jour pendant le niveau de zoom, et le layer sera invisible si !inrange, alors que "visibility" reste à true.

Et pour les zooms et résolution, même si la résolution te semble plus pratique, elle est moins "portable", dans le sens où c'est une logique pyramidale qui dépend d'un extent de base, des coordonnées d'origine et de la projection. Par ex, sur l'API du géoportail les zooms ont des résolutions différentes de celles de GMaps. Intrinsèquement, la carte a seulement une résolution courante. Le zoom est calculé par la couche de base : map.getZoomForResolution().

Bonne continuation et merci quand même pour ces articles !

JF
Citer
 
 
#2 Nicolas Moyroud 14-03-2011 09:16
Bonjour JF,

Merci pour ces précisions, j'avoue que je me doutais bien que ma méthode n'était pas optimale. :-) Mais étant donné que je n'avais à peu près rien compris à la page "Settings zoom levels" sur le wiki OpenLayers, je n'avais pas réussi à faire mieux...
Pour être plus précis, pourrais-tu me donner un exemple de ce que deviendrait mon code avec les modifications que tu me proposes ?
Citer
 
 
#3 JF 14-03-2011 20:07
Principalement, il faudrait définir les propriétés "minResolution", "maxResolution", ainsi que "alwaysInRange" (false).

Je crois que c'est tout.

Bonne chance !

JF
Citer
 
 
#4 Nicolas Moyroud 15-03-2011 10:02
Merci JF pour ton aide. Dès que j'aurai réussi à faire quelque chose de correct, je corrigerai le code ci-dessus. En attendant, ne faites pas ce que j'ai proposé, c'est mal ! :P
Citer
 
 
#5 Nicolas Moyroud 17-03-2011 12:39
J'ai corrigé avec les modifications proposées par JF. Ça marche à la perfection ! 8)
Citer
 
 
#6 JF Gigand 17-03-2011 13:06
Formidable !

Effectivement c'est simple une fois qu'on l'a fait, mais pas évident à comprendre en même temps que tout le reste. J'y ai aussi passé pas mal d'heures !

Si on souhaite surveiller la propriété "inRange", par exemple pour un gestionnaire de couche qui indique quand la couche n'est pas dispo pour le zoom courant (titre barré ou icône "invisible" par ex), c'est bien l'évènement "zoomend" (de la carte) qu'il faut intercepter.

Une autre contrainte d'affichage sont les propriétés "maxExtent" et "displayOutsideMaxExtent" pour les couches d'emprise restreinte, qui d'après le code d'OL n'influent pas sur "inRange" (méthode "calculateInRange"), alors qu'il serait bien pratique de détecter que la couche n'est pas disponible pour l'extent courant, alors que la résolution est gérée. Cette problématique est surtout liée à l'ergonomie, pour ne pas laisser l'utilisateur perplexe de ne pas comprendre pourquoi la couche ne s'affiche pas.
Citer
 
 
#7 Nicolas Moyroud 17-03-2011 14:38
Merci pour ces précisions, effectivement ça pourra me servir. Pour l'instant ça ira je vais laisser comme ça sur mon interface. J'utilise GeoExt+OpenLayers+ExtJS et la liste des couches est affichée par ExtJS. Pour changer la disponibilité de la couche, il faudrait que je fasse remonter l'événement zoomend d'OpenLayers au niveau de ExtJS et ça risque de ne pas être trivial. Je n'ai pas vraiment le temps de me pencher là dessus pour le moment ;-)
Citer
 

Ajouter un Commentaire

Tout les contenus de ce site sont publiés selon les termes de la licence Creative Commons by sa. En ajoutant votre commentaire, vous acceptez implicitement sa mise à disposition selon les termes de cette licence.


Code de sécurité
Rafraîchir