"The explosive growth of the GeoWeb and geographic information has made GIS powerful media for the general public to communicate, but perhaps more importantly, GIS have also become media for constructive dialogs and interactions about social issues." - Sui & Goodchild
OpenLayers offre un ensemble de fonctionnalités permettant d'interagir avec la carte. Par défaut, les interactions de zoom et pan sont actives mais il en existe d'autres (ol.control). En plus de la gestion événementielle offerte par ces contrôleurs, OpenLayers permet d'enregistrer des écouteurs sur de nombreux événements. Maintenant que les techniques de visualisation cartographique sont maîtrisées, tant côté serveur que client, il s'agit de découvrir comment interagir avec les objets géographiques représentés.
Cet exemple démontre qu'il est nécessaire qu'un gestionnaire s'occupe spécifiquement d'évenements cartographiques comme ceux en lien avec la vie d'un ol.Map (voir ol.MapBrowserEvents) ou ceux relatifs à la vie d'une ol.layer.Vector.
On reprend ici l'exemple 5A :
<html> <head> <title>ol3 - Ex7A - ol.ObjectEvent</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map, vectorLayer; $(document).ready(function () { map = new ol.Map({ view: new ol.View({ center: [0, 0], zoom: 2 }), target: 'map', layers: [ new ol.layer.Tile({ opacity: 0.5, // !! source: new ol.source.OSM() }) ] }); vectorLayer = new ol.layer.Vector({ source: new ol.source.Vector({ url: "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson", format: new ol.format.GeoJSON() }) }); map.addLayer(vectorLayer); $(document).keypress(function () { var fts = vectorLayer.getSource().getFeatures(); $("#info").html(""); for (var i = 0; i < fts.length; i++) { $("#info").append($("<p>").html(fts[i].get("title"))); } }); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } #info { position: absolute; top: 50px; left: 50px; background-color: #fff; padding: 5px; } p { font-size: small; margin: 0; } </style> </head> <body> <div id="map"></div> <div id="info"></div> </body> </html>
TODO
// Set listener on addfeature (ol.source.VectorEvent) vectorLayer.getSource().on("addfeature", function (e) { $("#info").append($("<p>").html(e.feature.get("title"))); });
// Set listener on change:center (ol.ObjectEvent) map.getView().on('change:center', function (e) { console.log(e.target.getCenter()); });
Le gestionnaire d'événements cartographiques va notamment servir à déployer la dernière fonctionnalité de base d'une application de webmapping, celle qui consiste à interroger les objets de la carte.
Avant d'aller plus loin, ajouter le serveur cartographique ci-dessous dans le fichier config.js (voir aussi https://github.com/camel113/geoinf15-ol3/blob/tutoOL3.9.0/js/config.js) :
/* * With the UNEP GRID GeoServer you can play with layers * (see the Global Risk Data Platform map at http://preview.grid.unep.ch/index.php?preview=map): * - World * - NationalParks * - Cities_esri * - etc (see http://preview.grid.unep.ch/geoserver/wms?REQUEST=GetCapabilities) */ var unWMS = "http://preview.grid.unep.ch/geoserver/wms"; var unWFS = "http://preview.grid.unep.ch/geoserver/wfs";
Cet exemple illustre l'interrogation des objets de la carte par le simple clic d'un pixel sur la carte. On enregistre un écouteur sur la map à l'aide de la méthode ol.Map.on afin d'exécuter du code lors d'un click sur la map. On utilise ensuite la méthode ol.source.ImageWMS.getGetFeatureInfoUrl pour construire l'URL qui permet d'interroger le WMS. Enfin, la fonction getFeatureInfo(url) permet d'envoyer une requête au serveur WMS à l'aide de l'URL précédemment construite. C'est l'équivalent bien mieux structuré que http://www.mediamaps.ch/oltuto/Ex0c_navmap.html.
<html> <head> <title>ol3 - Ex7B - interaction with WMS GetFeatureInfo control</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map, vectors, wmsLayer; $(document).ready(function () { var wmsSource = new ol.source.ImageWMS({ url: unWMS, params: { VERSION: "1.1.1", LAYERS: 'World', FORMAT: 'image/png', STYLES: 'polygon' } }); wmsLayer = new ol.layer.Image({ source: wmsSource }); map = new ol.Map({ target: 'map', view: new ol.View({ center: ol.proj.transform([6.7, 46.7], 'EPSG:4326', 'EPSG:3857'), zoom: 4 }) }); // Create/Add a WMS baselayer wmsLayer = new ol.layer.Image({ source: wmsSource }); map.addLayer(wmsLayer); map.on('singleclick', function (evt) { var url = wmsSource.getGetFeatureInfoUrl( evt.coordinate, map.getView().getResolution(), "EPSG:3857", { INFO_FORMAT: "text/plain" } ); if (url) getFeatureInfo(url); }); function getFeatureInfo(url) { var url2 = encodeURIComponent(url); var request = $.ajax({ url: "php/proxy2.php?url=" + url2, dataType: "text", }); request.done(function (data) { $("#info").html(data); }); request.fail(function (jqXHR, textStatus) { alert("Request failed: " + textStatus); }); } }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } #info { position: absolute; top: 20px; left: 50px; background-color: white; border: solid gray 1px; padding: 5px; font-size: smaller; z-index: 1000000000; } </style> </head> <body> <div id="map"></div> <div id="info">Click on the map to get feature info</div> </body> </html>
TODO
{ INFO_FORMAT: "text/html", FEATURE_COUNT: 50 }
et cliquer la carte aux alentours du Liechtenstein (sans changer de zoom !).
INFO_FORMAT: "application/json",
que se passe-t-il à l'interrogation de la carte ? Regarder le flux client/serveur …
function getFeatureInfo(url) { var url2 = encodeURIComponent(url); var request = $.ajax({ url: "php/proxy2.php?url=" + url2, dataType: "json", }); request.done(function (data) { decoder = new ol.format.GeoJSON(); features = decoder.readFeatures(data, { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" }); //console.log(features); if (features.length > 0) { sldUrl = "http://www.mediamaps.ch/oltuto/php/GetWorldSLD.php?ID=" + features[0].get("terr_id"); console.log(sldUrl); wmsLayer.getSource().updateParams({ VERSION: "1.1.1", FORMAT: 'image/png', SLD: sldUrl }); $("#info").html(features[0].get("terr_name")); } }); request.fail(function (jqXHR, textStatus) { alert("Request failed: " + textStatus); }); }
La réponse de la requète GetFeature étant un flux GeoJSON complet, le navigateur reçoit la géométrie de l'entité “sous le clique cartographique”. On peut l'exploiter et interagir comme suit :
<html> <head> <title>ol3 - Ex7C - interaction with WMS GetFeatureInfo + vector loading</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map, vectors, wmsLayer; $(document).ready(function () { var wmsSource = new ol.source.ImageWMS({ url: unWMS, params: { VERSION: "1.1.1", LAYERS: 'World', FORMAT: 'image/png', STYLES: 'polygon' } }); map = new ol.Map({ target: 'map', view: new ol.View({ center: ol.proj.transform([6.7, 46.7], 'EPSG:4326', 'EPSG:3857'), zoom: 4 }) }); // Create/Add a WMS baselayer wmsLayer = new ol.layer.Image({ source: wmsSource }); map.addLayer(wmsLayer); // Create/add an empty vector layer vectors = new ol.layer.Vector({ source: new ol.source.Vector() }); map.addLayer(vectors); map.on('singleclick', function (evt) { var url = wmsSource.getGetFeatureInfoUrl( evt.coordinate, map.getView().getResolution(), "EPSG:3857", { INFO_FORMAT: "application/json", FEATURE_COUNT: 1 } ); if (url) getFeatureInfo(url); }); function getFeatureInfo(url) { var url2 = encodeURIComponent(url); var request = $.ajax({ url: "php/proxy2.php?url=" + url2, dataType: "json", }); request.done(function (data) { decoder = new ol.format.GeoJSON(); features = decoder.readFeatures(data, { dataProjection: "EPSG:4326", featureProjection: "EPSG:3857" }); if (features.length > 0) { vectors.getSource().clear(); vectors.getSource().addFeatures(features); $("#info").html(features[0].get("terr_name")); } }); request.fail(function (jqXHR, textStatus) { alert("Request failed: " + textStatus); }); } }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } #info { position: absolute; top: 20px; left: 50px; background-color: white; border: solid gray 1px; padding: 5px; font-size: smaller; z-index: 1000000000; } </style> </head> <body> <div id="map"></div> <div id="info">Click on the map to get feature info</div> </body> </html>
TODO
var selectPointerMove = new ol.interaction.Select({ condition: ol.events.condition.pointerMove, layers: [vectors] // in our case this is optional as we only one vector layer in the map }); map.addInteraction(selectPointerMove); selectPointerMove.on('select', function (e) { if (e.target.getFeatures().getLength() > 0) { $("#info").html(e.target.getFeatures().item(0).get('terr_name')); } else { $("#info").empty(); } });
vectors.getSource().clear();
Le chargement en mode vector d'entités géographiques ouvre des possibilités intéressantes en termme d'interaction utilisateur au point d'être tenté de toujours tout charger dans le navigateur. Une telle approche peut s'avérer piégeuse et néfaste en terme d'expérience utilisateur du fait d'une latence provoquée (1) par le volume important de données à charger pour une bande passante donnée et (2) par les performances et l'aptitude du moteur de rendu cartographique à digérer ces données. Des stratégies de chargement peuvent alors s'avérer utiles afin d'éviter des écueils.
Avant d'aller plus loin, charger le script proxy1.php dans le dossier php du projet : https://github.com/camel113/geoinf15-ol3/blob/tutoOL3.9.0/php/proxy1.php.
L'exemple ci-dessous utilise la stratégie ol.loadingstrategy.all qui charge simplement tous les objets d'une couche donnée (autrement dit c'est la stratégie “no strategy” !).
<html> <head> <title>Ex7D - Vector overlay loading strategies</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function () { map = new ol.Map({ target: 'map', view: new ol.View({ center: ol.proj.transform([8, 47], "EPSG:4326", "EPSG:3857"), zoom: 5 }) }); var osmLayer = new ol.layer.Tile({ source: new ol.source.OSM(), opacity: 0.3 }); map.addLayer(osmLayer); var vectorSource = new ol.source.Vector({ loader: function (extent, resolution, projection) { // Web Feature Service parameters url = unWFS + "?" + $.param({ service: 'WFS', version: '1.1.0', request: 'GetFeature', typename: 'Cities_esri', srsname: 'EPSG:3857' }); // Pass it through the proxy $.ajax({ url: "php/proxy1.php?url=" + url, dataType: 'xml', success: function (response) { var wfsFormat = new ol.format.WFS(); vectorSource.addFeatures(wfsFormat.readFeatures(response)); }, error: function (jqXHR, textStatus, errorThrown) { alert("The request has failed: " + textStatus + errorThrown); } }); }, strategy: ol.loadingstrategy.all, projection: "EPSG:3857" }); var lyr = new ol.layer.Vector({ source: vectorSource }); map.addLayer(lyr); }) </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> <div id="info"></div> </body> </html>
TODO
ol.loadingstrategy.bbox
ajouter le paramètre suivant aux paramètres Web Feature Service
bbox: extent.join(',') + ',EPSG:3857'
Tout en naviguant dans la carte, rescruter le flux client/serveur.
Quelques autres stratégies qui cherchent à contrôler le flux client/serveur et l'expérience utilisateur selon les besoins d'interactivité de l'interface homme-machine :