GIS and Media fusion

"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

User Tools

Site Tools


geoinf14:oltuto2

Workshop OpenLayers v2 ... la suite

OpenLayers offre un ensemble de fonctionnalités permettant d'interagir avec la carte. Par défaut, les interactions de zoom et pan sont actives, et nous avons entre aperçu le contrôle OpenLayers.Control.LayerSwitcher. Il existe de nombreux autres OpenLayers.Control (voir aussi http://docs.openlayers.org/library/controls.html et la section 8).

En plus de la gestion événementielle offerte par ces contrôleurs, OpenLayers permet d'enregistrer des écouteurs sur de nombreux événements, comme ceux en lien avec la vie d'un OpenLayers.Map (voir OpenLayers.Map.events, OpenLayers.Layer.Vector.events).

Voir aussi un autre workshop sur le sujet : http://softlibre.gloobe.org/openlayers/workshop/introduction/module3.

7. Feature access and controls

A présent 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.

Ex7A : contrôle d'interrogation en mode image (WMS GetFeatureInfo)

Cet exemple illustre l'interrogation des objets de la carte par le simple clic d'un pixel sur la carte. On utilise le contrôle OpenLayers.Control.WMSGetFeatureInfo qui permet d'enregistrer un écouteur sur l'événement onGetFeatureInfo.

<html>
    <head>
        <title>Ex7A - interaction with WMS GetFeatureInfo control</title>
        <script type="text/javascript" src="js/config.js"></script>
        <script type="text/javascript">
            var map;
            $(document).ready(function() {
                OpenLayers.ProxyHost = myProxy;
                
                map = new OpenLayers.Map('map');
                wms = new OpenLayers.Layer.WMS("World boundaries", myWMS, {
                            layers: 'public.world_simple',
                            format: 'image/png'
                        }, { singleTile: true }
                );
                map.addLayer(wms);
                map.zoomToMaxExtent();

                info = new OpenLayers.Control.WMSGetFeatureInfo({
                    url: myWMS,                    
                    infoFormat: 'text/plain'
                });
                map.addControl(info);
                info.events.register('getfeatureinfo', info, onGetFeatureInfo);
                info.activate();
            });

            function onGetFeatureInfo(event) {
                $("#info").html(event.text.replace(":", "<br>"));
            }
        </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

  1. Analyser l'échange client/serveur (ex. avec Firebug). Comment est-ce possible ?
  2. Ajouter des paramètres dits “vendor” comme ci-dessous. Quel est l'effet ?
       info = new OpenLayers.Control.WMSGetFeatureInfo({
                        url: myWMS,                    
                        infoFormat: 'text/plain',
                        vendorParams: { radius: 5 }
                    });

Ex7B : un contrôle d'interrogation image "fait maison"

Dans Ex7A, c'est une version standardisé du contrôle d'interrogation lié au standard OGC WMS. L'exemple suivant montre comment mettre cela en oeuvre par nous-même. On s'assure ainsi de maîtriser tout le processus client/serveur.

Côté client :

<html>
    <head>
        <title>Ex7B - homemade GetFeatureInfo</title>
        <script type="text/javascript" src="js/config.js"></script>
        <script type="text/javascript">
            var map;
            $(document).ready(function() {               
                map = new OpenLayers.Map('map');
                wms = new OpenLayers.Layer.WMS("World boundaries", myWMS, {
                            layers: 'public.world_simple',
                            format: 'image/png'
                        }, { singleTile: true }
                );
                map.addLayer(wms);
                map.zoomToMaxExtent();
                
                map.events.register('click', map, getFeatureInfo);
            });

            function getFeatureInfo(e) {             
                // Conversion du graphique (Pixel) au géographique (LonLat)
                px = new OpenLayers.Pixel(e.xy.x,e.xy.y);
                xy = map.getLonLatFromPixel(e.xy);
          
                // On forme et lance la requête en veillant à enregistrer la function de callback
                getInfo = "GetCountriesByXY.php?x=" + xy.lon + "&y=" + xy.lat;
                $.ajax({
                    type: "GET",
                    url: getInfo,
                    success: function(response){
                        $("#info").html(response.features[0].properties.name);
                    }
                });
            }
        </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>

Côté serveur : GetCountriesByXY.php

<?php
require_once 'GeoManager.php';

$XY = $_GET["x"] . " " . $_GET["y"];

$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$api_key = "2e957be5f1e9c0793ca1725aab08274a795f2c3f";
$q = "SELECT name, ST_AsGeoJSON(the_geom) AS geom FROM world_simple"
        . " WHERE ST_Intersects(the_geom, ST_GeomFromText('POINT(" . $XY . ")', 4326))";

$url = "http://oertz.cartodb.com/api/v2/sql?api_key=" . $api_key. "&q=" . urlencode($q);
curl_setopt($ch, CURLOPT_URL, $url);

$result = curl_exec($ch);
$rows = json_decode($result)->rows;

header("Content-type: application/json");

$i = 0;
$fc = new FeatureCollection();
foreach ($rows as $row) {
    $fc->addFeature(new Feature($i++, json_decode($row->geom), array("name" => $row->name)));
}

echo json_encode($fc);
?>

TODO

  1. Côté client, ajouter la ligne ci-dessous au bon endroit :
    $("#info").html("Loading... please wait...");
  2. Ajouter en fin de fonction ready une couche OpenLayers.Layer.Vector comme suit :
    vectors = new OpenLayers.Layer.Vector("FeatureInfo overlay");
    map.addLayer(vectors);

    et remplacer l'appel AJAX comme suit :

                    $.ajax({
                        type: "GET",
                        url: getInfo,
                        dataType: "text",
                        success: function(response){
                            json = new OpenLayers.Format.GeoJSON();
                            fc = json.read(response);
                            $("#info").html(fc[0].attributes.name);
                            vectors.addFeatures(fc);
                        }
                    }); 
  3. Ajouter en fin de fonction ready le contrôle suivant avec son écouteur :
                    selectControl = new OpenLayers.Control.SelectFeature(vectors, {hover:true});
                    map.addControl(selectControl);
                    selectControl.activate();
              
                    vectors.events.register("featureselected", vectors, function(e){
                        $("#info").html(e.feature.attributes["name"]);
                    });
  4. Enregistrer un écouteur sur le bon événement pour vider la <div> info quand le curseur sort d'un feature.

Ex7C : stratégies de chargement en mode vector

Le chargement d'entités géographiques peut s'avérer 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.

L'exemple suivant montre déjà des limites avec la stratégie OpenLayers.Strategy.Fixed() qui charge simplement tous les objets dans l'enveloppe de visualisation. Il s'agit alors d'opter pour des stratégies afin d'éviter ces écueils.

<html>
    <head>
        <title>Ex7C - Vector overlay loading strategies</title>
        <script type="text/javascript" src="js/config.js"></script>
        <script type="text/javascript">
            var map, lyr;
            $(document).ready(function() {
                OpenLayers.ProxyHost = myProxy;

                map = new OpenLayers.Map('map');
                map.addControl(new OpenLayers.Control.LayerSwitcher());

                osm = new OpenLayers.Layer.OSM("Simple OSM Map");
                map.addLayer(osm);

                lyr = new OpenLayers.Layer.Vector("WFS - cities (capitals)", {
                    protocol: new OpenLayers.Protocol.WFS({
                        version: "1.0.0",
                        url: myWFS,
                        featureType: "cities"
                    }),
                    strategies: [new OpenLayers.Strategy.Fixed()],
                    projection: new OpenLayers.Projection("EPSG:4326")
                });
                map.addLayer(lyr);

                map.setCenter(new OpenLayers.LonLat(8, 47).transform("EPSG:4326", "EPSG:3857"), 5);
            });
        </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"></div>
    </body>
</html>

TODO

  1. Compter les appels au serveur géographique et vérifier le(s) volume(s) des entités géographiques transférées.
  2. Faire de même avec le chargement de la couche world_simple en modifiant le paramètre ci-dessous :
                    featureType: "world_simple"
  3. Modifier la stratégie comme suit :
                    strategies: [new OpenLayers.Strategy.BBOX()],

    et tout en naviguant dans la carte, compter les appels au serveur géographique et revérifier le(s) volume(s) des entités géographiques transférées.

  4. Rendre la couche vector invisible au chargement de l'application en insérant l'instruction comme indiqué ci-dessous :
                    ...
                    lyr.setVisibility(false);
                    map.addLayer(lyr);
                    ...

    de plus, ajouter les instructions ci-dessous en fin de fonction ready :

                    ...
                    map.myZoom = map.zoomTo;
                    map.zoomTo = function(zoom, xy) {
                        lyr.setVisibility(zoom >= 8 ? true : false);
                        map.myZoom(zoom, xy);
                    };
  5. Quelles autres stratégies peut-on imaginer pour assurer une bonne expérience utilisateur ?

Ex7D : contrôles en mode vector

Le chargement d'entités géographiques (plutôt que leur représentation graphique image) offre des possibilités d'interaction plus directe sur les objets vectoriels qui les représentent. Voyons quelques exemples.

TODO

  1. Sur la base de Ex7C avec la couche cities, définir les symbolizers defaultPoint et selectPoint suivant :
                    defaultPoint = new OpenLayers.Symbolizer.Point({
                                                graphicName: 'circle',
                                                pointRadius: 6,
                                                fillColor: '#0000ff',
                                                fillOpacity: 0.8
                                        });
                    selectPoint = defaultPoint.clone();                       
                    selectPoint.fillColor = '#ff0000';

    puis les connecter en ajoutant un styleMap sur la couche Vector comme suit :

                    ...
                    styleMap: new OpenLayers.StyleMap({
                        "default": new OpenLayers.Style(defaultPoint),
                        "select": new OpenLayers.Style(selectPoint)
                    })
                    ...

    enfin ajouter et activer un contrôle SelectFeature sur cette couche Vector comme suit :

                    selectControl = new OpenLayers.Control.SelectFeature(lyr, {hover:true});
                    map.addControl(selectControl);
                    selectControl.activate();
  2. Ajouter encore les instructions suivantes pour interroger les objets :
                    lyr.events.register("featureselected", lyr, function(event) {
                        var feature = event.feature;
                        $("#info").html(feature.attributes.wup_aggl);
                    });
  3. Faire le nécessaire pour améliorer l'interaction utilisateur :
    1. afficher aussi le nom du pays dans la boîte d'information;
    2. la boîte doit être vide quand aucun objet n'est sélectionné.
  4. Que peut-on faire avec :
                    feature.geometry.getBounds().getCenterLonLat()

    et une OpenLayers.Popup http://dev.openlayers.org/docs/files/OpenLayers/Popup-js.html ?

Autres interactions et contrôles

geoinf14/oltuto2.txt · Last modified: 2018/05/16 10:05 (external edit)