OpenLayers : Lister des attributs liés aux objets géographiques et zoomer sur la carte au survol de l’attribut

Aujourd’hui, je vous fais part d’un petit script webmapping avec OpenLayers (v4). On ne le redira jamais assez mais cette API permet une liberté de créer, d’ajouter de nouvelles fonctionnalités à votre carte en ligne. Et, dans le même esprit, le tutoriel du jour peut être reproduit avec n’importe quelle donnée vectorielle SIG. Le vecteur est composé d’objets géographiques (features) contenant une géométrie et des attributs. Alors, quel est l’objectif de ce tutoriel?
Les attributs du vecteur sont cartographiés et listés dans un conteneur séparément. Du côté de la carte, le clic sur la géométrie de l’objet permet de montrer un ou des attributs visualisés en haut de la liste. D’un autre côté, le survol d’un attribut dans le conteneur déclenche le zoom sur la géométrie de l’objet géographique associé.

1. Liste des objets géographiques du vecteur

Dans un premier temps, on se charge de créer les deux conteneurs en HTML et CSS avec Bootstrap : la carte et la liste des attributs avec un scroll. Le vecteur utilisé est de format GeoJSON et représente les pays du monde, il a déjà été utilisé dans plusieurs tutoriels de webmapping comme celui sur  l’affichage des informations géographiques d’un fichier GeoJSON dans une fenêtre modale. Il est bien entendu possible d’adapter ce code avec tous les formats de données vectorielles pris en charge par OpenLayers tels le GPX, le KML, les protocoles WFS. Un seul attribut est affecté à chaque objet géographique : “name”. On déclare la source de la donnée avec l’URL de la data puis la couche vectorielle stylisée avec la classe ol.layer.Vector que l’on ajoute à l’objet Map.

// Style de la couche vectorielle
var style = new ol.style.Style({
	stroke: new ol.style.Stroke({
		color: '#000',
		width: 3
	}),
	fill: new ol.style.Fill({
		color: '#fff',
	}),
});
// Déclaration de la source du vecteur de format GeoJSON
var urlData='pays.geojson';	
var sourceVecteur = new ol.source.Vector({
	url: urlData,
	format: new ol.format.GeoJSON()
});
// Déclaration de la couche vectorielle	
var vecteur = new ol.layer.Vector({
	title:"pays",
	source: sourceVecteur,
	style: style
});
// Déclaration de l'objet Map
var map = new ol.Map({
	layers: [vecteur],
	target: 'map',
	view: new ol.View({
		center: ol.proj.transform( [0, 48], 'EPSG:4326', 'EPSG:3857'),
		zoom: 3
	})
});

Listons les attributs des données géographiques. Deux classes d’OpenLayers en lien avec la source de la donnée sont importantes ici : getState() et getFeatures(). getState() permet de contrôler l’état de chargement de la source de la donnée et getFeatures() chargent la géométrie de chaque objet géographique et leurs attributs. A partir de la source du vecteur, on peut lister de deux manières les données par :

  • la fonction forEachFeature(function(feature) { }) avec feature l’objet contenant la géométrie et les attributs;
  • une boucle basique for sur le nombre d’objets géographiques;

Je choisis cette dernière solution pour injecter un nouvel attribut à chaque objet, un identifiant unique par la classe setId(). Pourquoi? Bonne question… 🙂 La donnée vectorielle n’a peut être pas d’identifiant unique, ce qui est le cas dans la donnée utilisée et plusieurs attributs du vecteur peuvent être identiques. Cela pose un problème pour associer une action unique à l’objet considéré. Voici la fonction listant les attributs dans le conteneur avec scroll :

// Liste des attributs du vecteur 
sourceVecteur.once('change', function(evt){
	// On vérifie que la source de la donnée est chargée
	if(sourceVecteur.getState() === 'ready'){
		// Si le vecteur possède au moins un objet géographique
		if(sourceVecteur.getFeatures().length >0){
			// On obtient les objets géographiques
			var features = sourceVecteur.getFeatures();
			var nbFeatures = features.length;
			// Pour chaque objet géographique
			for(var i = 0; i < features.length; i++){
				// On attribut un identifiant unique
				features[i].setId(i);
				// On déclare l'attribut : ici name. A modifier selon vos données
				var name = features[i].get('name');
				// On liste les attributs dans le conteneur avec la déclaration de la fonction zoomFeature par la méthode de survol de la souris
				$('.liste-objet').append('<p id="feature'+i+'" onmouseover="zoomFeature(\'' + i + '\'); return false;"><span class="badge">'+i+'</span> '+name+'</p>');
			}	
		}
	}	
});

On note l’utilisation de la classe append de JQuery  pour montrer les identifiants et les noms des pays les uns après les autres dans une balise <p>. Pour chacune d’elle, on associe un id en lien avec l’identifiant unique de l’attribut. Il faut bien entendu modifier le nom de l’attribut appelé par la méthode get() en lien avec votre couche SIG.

2. Mise en évidence de l’attribut associé à la géométrie sélectionnée sur la carte

2.1. ScrollTop de l’attribut de l’objet géographique sélectionné

Ici, on écrit une fonction qui permet de montrer l’attribut associé à la géométrie sélectionnée sur la carte en haut de la liste et d’une autre couleur. On retourne l’objet géographique sélectionné au clic sur la carte par la fonction :

// Sélection de la géométrie sur la carte
map.on('singleclick', function(evt){
	// Obtiention de l'objet géographique du vecteur affiché en dernier
	var featureSelect = map.forEachFeatureAtPixel(evt.pixel, function(feature){
		return feature;
	});
	// Si on obtient l'objet
	if(featureSelect){
		...
	}
});

Cette fonction est valable pour un vecteur, pour une donnée affiché via un protocole WM(T)S, il faut utiliser une autre fonction pour lire les attributs. A l’intérieur de cette fonction, une fois l’objet géographique obtenu, on récupère son identifiant unique défini précédemment. Puis la fonction animate de JQuery nous permet de faire dérouler la liste de noms de pays automatiquement et de cibler l’attribut de l’objet sélectionné en haut de la liste par la classe scrollTop. Ici, on voit bien l’importance de définir un identifiant unique pour chaque objet du vecteur! On a donc :

// Sélection de la géométrie sur la carte
map.on('singleclick', function(evt){
	// Obtiention de l'objet géographique du vecteur affiché en dernier
	var featureSelect = map.forEachFeatureAtPixel(evt.pixel, function(feature){
		return feature;
	});
	// Si on obtient l'objet
	if(featureSelect){
		// On déclare son identifiant unique
		var idFeature = featureSelect.getId();
		// L'attribut est affcihé en haut de la liste par un scroll automatique
		$('.liste-objet').animate({
			scrollTop: $('#feature'+idFeature+'').offset().top - $('.liste-objet').offset().top + $('.liste-objet').scrollTop()
		}, 500);
	}
});

Ok, c’est marrant mais j’aimerais que la géométrie et l’attribut sélectionnés aient une couleur différente! Pas de problème, on y vient.

2.2. Styles de la géométrie et de l’attribut?

Commençons par la géométrie observée sur la carte. Tout d’abord, on définit un nouveau style puis on affecte ce dernier à l’objet géographique par la classe setStyle(). Il faut aussi penser à “réinitialiser” toutes les couleurs des objets avec le style initial lors de chaque clic sur la carte. On procède de manière identique avec le style de liste grâce à la classe css de JQuery.

// Attribution des styles d'origine 
function setColor(){
	sourceVecteur.forEachFeature(function(feature) {
		feature.setStyle(style);
	});
	$('.liste-objet p').css('background-color','#fff');
}
// Sélection de la géométrie sur la carte
map.on('singleclick', function(evt){
	// On affecte les styles d'origine
	setColor();
	// Obtiention de l'objet géographique du vecteur affiché en dernier
	var featureSelect = map.forEachFeatureAtPixel(evt.pixel, function(feature){
		return feature;
	});
	// Si on obtient l'objet
	if(featureSelect){
		// On lui affecte le nouveau style
		featureSelect.setStyle(newStyle);
		// On déclare son identifiant unique
		var idFeature = featureSelect.getId();
		// L'attribut est affcihé en haut de la liste par un scroll automatique
		$('.liste-objet').animate({
			scrollTop: $('#feature'+idFeature+'').offset().top - $('.liste-objet').offset().top + $('.liste-objet').scrollTop()
		}, 500);
		$('#feature'+idFeature+'').css('background-color','#FF4000');
	}
});

La réinitialisation des styles est placée dans une fonction à part setColor() puisqu’elle sera réutilisée dans la section suivante.

3. Zoom sur la géométrie associée à l’attribut survolé dans la liste

L’idée est de zoomer sur la géométrie associée à l’attribut “survolé” dans la liste. La fonction zoomFeature(idFeature) est ajoutée dans chaque balise <p> (cf. $1.) avec idFeature l’identifiant unique. Elle est déclenchée par la méthode JavaScript onmouseover, on aurait pu utiliser d’autres méthodes comme onclick par exemple. Dans la fonction, on obtient l’objet géographique par la classe getFeatureById(idFeature) attachée à la source de la donnée. Une fois définie, on zoome sur l’extension de la géométrie associée à l’objet.

// Zoom sur l'objet géographique à partir de l'id de l'attribut survolé
function zoomFeature(idFeature){	
	// On réinitialise tous les styles
	setColor();
	$('#feature'+idFeature+'').css('background-color','#FF4000');	
	// Déclaration de l'objet géographique à partir de l'id de l'attribut survolé
	var feature = sourceVecteur.getFeatureById(idFeature);
	// On lui affecte le nouveau style
	feature.setStyle(newStyle);
	// On déclare l'étendue de la géométrie de l'objet géographique
	var geomFeature = feature.getGeometry().getExtent();
	// On zoom sur cette étendue
	map.getView().fit(geomFeature);
}

On n’oublie pas de réaffecter les styles (cf. 2.2.).

En conclusion, ce tutoriel de webmapping nous a montré comment interagir entre les géométries d’un vecteur observées sur la carte et les attributs associés contenus dans un bloc. La définition d’un identifiant unique est recommandée pour déclencher des actions spécifiques en lien avec l’objet géographique considéré. La manipulation des objets géographiques a fait appel principalement à des fonctions de sélection sur la carte, de zoom et de modification de style avec l’API d’OpenLayers. L’utilisation de JQuery a servi à cibler les attributs des objets géographiques sélectionnés dans la liste déroulante par des classes liées au scroll et au style css. Le script final est disponible pour les utilisateurs du site.

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *