"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
Dans OpenLayers, une carte est un ensemble de couches associée à des contrôles pour gérer l'interaction avec l'utilisateur. Une carte se construit avec trois ingrédients de base : du balisage HTML, des déclarations de style CSS, et du code JavaScript d'initialisation.
var myWMS = "https://eu1.mapcentia.com/wms/oertz/public"; var myWFS = "https://eu1.mapcentia.com/wfs/oertz/public/4326";
<html> <head> <title>ol3 -Ex1A - create a first map with a WMS layer</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> $(document).ready(function(){ var map = new ol.Map({ view: new ol.View({ projection: "EPSG:4326" }), target: 'map' }); var wmsImageLayer = new ol.layer.Image({ source: new ol.source.ImageWMS({ url: "https://eu1.mapcentia.com/wms/oertz/public", params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple', FORMAT: 'image/png' } }), }) map.addLayer(wmsLayer) // get the extent for the entire view var extent = map.getView().getProjection().getExtent() //fit to max extent map.getView().fitExtent(extent, map.getSize()); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
.ol-zoom:hover { background: lightskyblue; } .ol-zoom .ol-zoom-in, .ol-zoom .ol-zoom-out { background-color: tomato; border-radius: 50%; }
TODO
map.getView().fitExtent([-18,38,38,55],map.getSize()); console.log(map.getView().getProjection());
var wmsTileLayer = new ol.layer.Tile({ source: new ol.source.TileWMS({ url: "https://eu1.mapcentia.com/wms/oertz/public", params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple', FORMAT: 'image/png' } }), })
border: 1px solid coral;
Avec singleTile à false, le client contrôle la grille de tuiles qu'il peut garder en cache. Néanmoins, à chaque requête initiale de tuile, tout le processus de rendu cartographique se fait. Mais puisqu'une couche de tuiles fait des requêtes d'images selon une grille régulière, il est possible pour le serveur de préparer un cache de ces images.
Le service cartographique WMS permettant une grande flexibilité en terme de ce que le client peut demander. Côté serveur, cela rend difficile la mise en cache. A l'extrême opposé, un service peut aussi offrir des tuiles pour un ensemble prédéfini de niveaux de zoom et pour une grille régulière prédéfinie. On parle de couches de tuiles avec une source XYZ - avec X et Y indiquant la colonne et la rangée de la grille et Z le niveau de zoom. Il faut voir cela comme une pyramide de tuiles.
<html> <head> <title>ol3 -Ex2A - webmap with tiled layer</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', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ] }); var extent = map.getView().getProjection().getExtent() map.getView().fitExtent(extent, map.getSize()); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
//map.getView().fitExtent(extent, map.getSize()); map.getView().setCenter(ol.proj.transform([6.15,46.2],"EPSG:4326","EPSG:3857")) map.getView().setZoom(10)
console.log("Map projection: " + map.getView().getProjection().getCode()); console.log("Map extent: " + map.getView().calculateExtent(map.getSize())); console.log("LonLat Mercator: " + new ol.proj.transform([6.15,46.2],"EPSG:4326","EPSG:3857")); console.log("LonLat Google: " + new ol.proj.transform([6.15,46.2],"EPSG:4326","EPSG:3857"));
ol.source.OSM({ url: "http://api.tiles.mapbox.com/v3/oertz.map-i2ak2ozc/{z}/{x}/{y}.png" })
ol.source. url:"http://urbangene.heig-vd.ch/tilecache/{z}/{x}/{y}. attributions: [ new ol.Attribution({ html: "Based on <a href='www.openstreetmap.org'>OpenStreetMap</a> / MapBox restyled by oertz :: HEIG-VD" }) ] })
et ajouter l'instruction CSS suivante :
.ol-attribution { background-color: CornflowerBlue !important; right: 50px; left: initial; bottom: initial; top: 0px; }
L'utilisation d'un cache serveur de tuiles a largement été popularisé par des services propriétaires comme Bing Maps. OpenLayers étant indépendant de toute source de données, l'API offre également un type de couche pour dialoguer avec de tels services.
TODO
map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.BingMaps({ key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3', imagerySet: 'AerialWithLabels' }) }) ] }); map.getView().setCenter(ol.proj.transform([6.15,46.2],"EPSG:4326","EPSG:3857")) map.getView().setZoom(6)
Swisstopo offre un service de tuiles basé sur le standard WMTS et que l'on peut exploiter comme suit.
<html> <html> <head> <title>ol3 -Ex2C - webmap with tiled layer</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript" src="js/swissProjection.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ var projection = new ol.proj.Projection({ code: 'EPSG:21781', // The extent is used to determine zoom level 0. Recommended values for a // projection's validity extent can be found at http://epsg.io/. extent: [485869.5728, 76443.1884, 837076.5648, 299941.7864], units: 'm' }); ol.proj.addProjection(projection); var resolutions = [ 4000, 3750, 3500, 3250, 3000, 2750, 2500, 2250, 2000, 1750, 1500, 1250, 1000, 750, 650, 500, 250, 100, 50, 20, 10, 5, 2.5, 2, 1.5, 1, 0.5 ]; var tileGrid = new ol.tilegrid.WMTS({ origin: [420000, 350000], resolutions: resolutions, matrixIds: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25] }); var wmtsSource = new ol.source.WMTS({ tileGrid: tileGrid, url: 'http://wmts.geo.admin.ch/1.0.0/{Layer}/default/20120809/21781/{TileMatrix}/{TileRow}/{TileCol}.jpeg', // template http://wmts.geo.admin.ch/1.0.0/ch.swisstopo.pixelkarte-farbe/default/{Time}/21781/{TileMatrix}/{TileRow}/{TileCol}.jpeg layer: 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale', requestEncoding: 'REST' }) var layer = new ol.layer.Tile({ source: wmtsSource, extent: [420000, 30000, 900000, 350000] }); map = new ol.Map({ target: 'map', view: new ol.View({ projection: projection, center: [646000, 249500], zoom: 8 }) }); map.addLayer(layer) var extent = map.getView().getProjection().getExtent() map.getView().fitExtent(extent, map.getSize()); }) </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
ol.proj.addCoordinateTransforms('EPSG:4326', projection, //projection: la projection définie précédemment function(coordinate) { return [ WGStoCHy(coordinate[1], coordinate[0]), WGStoCHx(coordinate[1], coordinate[0]) ]; }, function(coordinate) { return [ CHtoWGSlng(coordinate[0], coordinate[1]), CHtoWGSlat(coordinate[0], coordinate[1]) ]; });
map.getView().setCenter(ol.proj.transform([7.658, 45.98], 'EPSG:4326', 'EPSG:21781'));
map.getView().setCenter([600000, 200000]); map.getView().setZoom(8);
Pour le moment, c'est le serveur qui fait tourner son moteur cartographique pour créer une représentation image des données géographiques et ceci sur la base de styles prédéfinis. Voyons comment le client peut dire son mot et piloter le style “à distance”.
Le standard WMS offre beaucoup de libertés, parfois au détriment de la performance. Comme il permet de demander ce que l'on veut en termes d'enveloppe géographique (paramètre bbox) et de style parmis un ensemble de styles pré-configurer, il est possible d'envoyer au serveur cartographique des instructions personnalisées de style.
<html> <head> <title>ol3 - Ex3A - use of a custom SLD style</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ map = new ol.Map({ view: new ol.View({ projection: "EPSG:4326" }), target: 'map' }); layer = new ol.layer.Image({ source: new ol.source.ImageWMS({ url: "https://eu1.mapcentia.com/wms/oertz/public", params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple', FORMAT: 'image/png', SLD: 'http://ogo.heig-vd.ch/ol2014/sld/pinkWorld.sld.xml' } }), }) map.addLayer(layer) var extent = map.getView().getProjection().getExtent() map.getView().fitExtent(extent, map.getSize()); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
<?xml version="1.0" encoding="UTF-8"?> <DescribeFeatureType version="1.1.0" service="WFS" xmlns="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"> <TypeName>geoinf:world_simple</TypeName> </DescribeFeatureType>
puis lancer
<?xml version="1.0" encoding="UTF-8"?> <GetFeature version="1.1.0" service="WFS" xmlns="http://www.opengis.net/wfs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"> <Query typeName="geoinf:world_simple"> <PropertyName>REGION</PropertyName> </Query> </GetFeature>
... <Rule> <Filter xmlns="http://www.opengis.net/ogc"> <PropertyIsEqualTo> <PropertyName>region</PropertyName> <Literal>NorthAfrica</Literal> </PropertyIsEqualTo> </Filter> <PolygonSymbolizer> ...
... </Rule> <Rule> <ElseFilter/> <PolygonSymbolizer> <Fill> <CssParameter name="fill">#DDDDDD</CssParameter> <CssParameter name="fill-opacity">1.0</CssParameter> </Fill> <Stroke> <CssParameter name="stroke">#000000</CssParameter> <CssParameter name="stroke-width">1</CssParameter> </Stroke> </PolygonSymbolizer> </Rule> </FeatureTypeStyle> ...
<html> <head> <title>ol3 - Ex3C - webmap with your own MapBox basemap</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function() { var token = "pk.eyJ1IjoiamZyZXlyZSIsImEiOiIwMnZKUHR3In0.8YWGPOTZ3bFglVmnCvWjvw"; var styleId = "jfreyre.c74df3e5"; map = new ol.Map({ layers: [ new ol.layer.Tile({ source: new ol.source.XYZ({ url: 'http://api.tiles.mapbox.com/v4/'+styleId+'/{z}/{x}/{y}.png?access_token=' + token }) }) ], target: 'map', view: new ol.View({ center: ol.proj.transform([6.7, 46.7], 'EPSG:4326', 'EPSG:3857'), zoom: 10 }) }); }) </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
Un intérêt d'une carte en ligne est de pouvoir la composer à partir de plusieurs couches.
La composition peut se faire par un assemblage côté serveur. L'Ex3C a déjà bien montré comment préparer un tel assemblage. Ci-dessous un assemblage “à la demande”. Notez bien, plus il y a de couches disponibles sur le serveur cartographique, plus il y a de combinaisons…. Problèmes… Impossible de retourner la map demandée
<html> <head> <title>ol3 - Ex4A - one WMS request for two layers</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', layers: [ new ol.layer.Image({ source: new ol.source.ImageWMS({ url: "https://eu1.mapcentia.com/wms/oertz/public", params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple,public.cities', FORMAT: 'image/png' } }), }) ] }); map.getView().setCenter(ol.proj.transform([7, 47],"EPSG:4326","EPSG:3857")) map.getView().setZoom(5); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
Un cadre applicatif de cartographie en ligne se doit de fournir la capacité de gérer la composition côté client. Openlayers 3 ne fournit pour l'instant pas d'outils pour gérer cela. Toutefois il existe des controls développé en externe comme ol3-layerswitcher utilisé dans l'exemple ci-dessous :
<html> <head> <title>ol3 - Ex4B - two WMS base layers</title> <script type="text/javascript" src="js/config.js"></script> <link rel="stylesheet" href="lib/layerswitcher/ol3-layerswitcher.css" type="text/css"> <script src="lib/layerswitcher/ol3-layerswitcher.js" type="text/javascript"></script> <script type="text/javascript"> var map; $(document).ready(function(){ map = new ol.Map({ target: 'map', layers: [ new ol.layer.Group({ 'title': 'Base maps', layers: [ new ol.layer.Image({ 'type': 'base', 'title': 'Base maps', source: new ol.source.ImageWMS({ url: "https://eu1.mapcentia.com/wms/oertz/public", title: 'World simple', type:'base', params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple', FORMAT: 'image/png' } }) }) ] }), new ol.layer.Group({ 'title': 'Overlays', layers: [ new ol.layer.Image({ title: 'Cities', source: new ol.source.ImageWMS({ url: "https://eu1.mapcentia.com/wms/oertz/public", params:{ VERSION: "1.1.1", LAYERS: 'public.cities', FORMAT: 'image/png' } }) }) ] }) ] }); var layerSwitcher = new ol.control.LayerSwitcher(); map.addControl(layerSwitcher); map.getView().setCenter(ol.proj.transform([7, 47],"EPSG:4326","EPSG:3857")) map.getView().setZoom(5); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
Jusqu'à présent, les styles CartoCSS devaient être préparés et chargés avec le Studio vers un compte MapBox. La suite montre une approche plus souple permettant de définir “programmatiquement” le style à appliquer sur une couche de données chargées sur un compte CartoDB.
<html> <head> <title>ol3 - Ex4C - webmap with tiled layer</title> <script src="http://libs.cartocdn.com/cartodb.js/v3/3.11/cartodb.core.js"></script> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ map = new ol.Map({ view: new ol.View({ center:ol.proj.transform([7,47], 'EPSG:4326', 'EPSG:3857'), zoom: 13 }), target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ] }) cartodb.Tiles.getTiles({ type: 'cartodb', user_name: 'ogo', sublayers: [{ sql: 'SELECT * FROM g4districts98', cartocss: "#g4districts98 { line-color: red; line-width: 2;}" }] }, function(tileTemplate) { // create layer with generated tiles URL tilesUrl = tileTemplate.tiles[0] .replace('{s}', 'a') .replace('{z}', '{z}') .replace('{x}', '{x}') .replace('{y}', '{y}'); admLyr = new ol.layer.Tile({ source: new ol.source.XYZ({ url: tilesUrl }) }) map.addLayer(admLyr); }); }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
Dans les sections précédentes nous avons inséré des couches cartographique sous la forme d'images construites par le serveur avec son moteur cartographique. Un serveur cartographique était donc nécessaire.
A l'opposé, pour une couche OpenLayers.Layer.Vector c'est le client qui joue le rôle de moteur cartographique pour créer la visualisation d'entités géographiques (Features en anglais).
Une couche ol.layer.Vector peut être alimentée par une source de données au format ol.source.GeoJSON (voir aussi http://geojson.org). Il existe de nombreux autres formats : ol.source.GPX, ol.source.TopoJSON, ol.source.KML, …)
<html> <head> <title>ol3 - Ex5A - GeoJSON vector overlay</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ map = new ol.Map({ view: new ol.View({ center:[738600, 5840171], zoom: 4 }), target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ] }) var vectorLayer = new ol.layer.Vector({ source: new ol.source.GeoJSON({ projection: 'EPSG:3857', url: "data/4capitals.json" }) }) map.addLayer(vectorLayer) }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson
url: 'http://www.visugpx.com/files/download.php?id=1416844831'
en recentrant la carte par ici :
view: new ol.View({ center:[766225, 5908730], zoom: 10 }),
Pourquoi cela ne fonctionne pas?
L'exemple ci-dessous décrit en détail ce qu'est une source de donnée ol.source.Vector pour une couche ol.layer.Vector en montrant comment la créer et l'alimenter à partir d'un format de source de données “fait maison”.
En résumé, une source ol.source.Vector est composée d'entités géographiques (ol.Feature) chacune composée d'une géométrie (ol.geom) et d'une liste d'attributs.
La source de données est un fichier texte formaté comme suit:
2.3332999999999999 48.866700000000002 Paris,7.4333 46.950000000000003 Bern,12.5 41.883299999999998 Rome,-3.71 40.409999999999997 Madrid
La structure de formatage est simple : c'est une suite de triplets “Longitude Latitude Nom” dont le séparateur interne est l'espace. Le séparateur entre triplets est la virgule. Chaque triplet correspond à une entité géographique.
Le code ci-dessous déchiffre cette structure et transforme les triplets en OpenLayers.Feature.Vector.
<html> <head> <title>ol3 - Ex5B - custom format for vector overlay</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ map = new ol.Map({ view: new ol.View({ center:ol.proj.transform([2,48], 'EPSG:4326', 'EPSG:3857'), zoom: 5 }), target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }), ] }) $.ajax({ type: "GET", url: "../data/4capitals.txt", dataType: "text", success: createCustomOverlay }); function createCustomOverlay(response){ // Créer/ajouter une nouvelle couche vide vectors = new ol.layer.Vector({ source: new ol.source.Vector() }) map.addLayer(vectors) // Préparer un tableau pour accueillir des entités géographiques (des "features") features = new Array(); // On décompose notre flux géographique encodé "maison" tabCapitals = response.split(","); for(i = 0; i < tabCapitals.length; i++){ tabCapital = tabCapitals[i].split(" "); // on construit et ajoute au tableau une feature composé d'une géométrie (ici Point) var feature = new ol.Feature({ // que le constructeur utilise pour alimenter la propriété feature.geometry geometry: new ol.geom.Point(ol.proj.transform([parseFloat(tabCapital[0]),parseFloat(tabCapital[1])],'EPSG:4326', 'EPSG:3857')), name: tabCapital[2] }); features.push(feature) } vectors.getSource().addFeatures(features) } }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
2.3332999999999999 48.866700000000002 Paris France,7.4333 46.950000000000003 Bern Suisse,12.5 41.883299999999998 Rome Italy,-3.71 40.409999999999997 Madrid Spain
OpenLayers met à disposition une classe ol.style permettant d'appliquer des styles aux features (entités géographiques) d'une couche ol.vector.
Openlayers applique un style par défaut aux features, mais il est possible de redéfinir ce style (forme, couleur, etc.).
<html> <head> <title>ol3 - Ex6A - One feature, one style</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ map = new ol.Map({ view: new ol.View({ center:ol.proj.transform([2,48], 'EPSG:4326', 'EPSG:3857'), zoom: 5 }), target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ] }) vectLayer = new ol.layer.Vector({ source: new ol.source.Vector() }) map.addLayer(vectLayer) var stroke = new ol.style.Stroke({ color: '#ff0000', width: 3 }); var style = new ol.style.Style({ image: new ol.style.RegularShape({ points: 4, stroke: stroke, radius: 10, radius1:0, radius2:0 }), stroke: stroke }) var feature = new ol.Feature({ geometry: new ol.geom.Point([738600, 5840171]) }); feature.setStyle(style) vectLayer.getSource().addFeature(feature) }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
var style = new ol.style.Style({ image: new ol.style.Icon({ src:"http://www.cretasolaris.gr/gfx/marker.png", size:[36,33], opacity:1 }) })
L'approche la plus répandue consiste à associer un style à une couche.
<html> <head> <title>ol3 - Ex6B - One layer, one style</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ var fill = new ol.style.Fill({ color: '#acc000' }); var stroke = new ol.style.Stroke({ color: '#ff0000', width: 3 }); var style = new ol.style.Style({ image: new ol.style.Icon({ src:"http://www.cretasolaris.gr/gfx/marker.png", size:[36,33], opacity:1 }) }) map = new ol.Map({ view: new ol.View({ center:ol.proj.transform([2,48], 'EPSG:4326', 'EPSG:3857'), zoom: 5 }), target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ] }) vecLayer = new ol.layer.Vector({ source: new ol.source.Vector({}), style: style }) var feature = new ol.Feature({ geometry: new ol.geom.Point([738600, 5840171]), name: "The precious is here!", author: "Gollum" }); map.addLayer(vecLayer) vecLayer.getSource().addFeature(feature) }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
var feature2 = new ol.Feature({ geometry: new ol.geom.Point([1938600, 6840171]), name: "The hobbits are far away...", author: "Gandalf" }); vecLayer.getSource().addFeature(feature2)
Plutôt que d'appliquer un style identique à toutes les entités d'une couche, il est possible d'appliquer un style différents basé sur les propriétés de chacune des entités. Pour cela, Openlayers propose de remplacer l'objet ol.style.Style par une fonction ol.style.StyleFunction(). Dans l'exemple ci-dessous on affiche un titre différent en fonction de la propriété “name” de chaque entité.
<html> <head> <title>ol3 - Ex6C - Styling function</title> <script type="text/javascript" src="js/config.js"></script> <script type="text/javascript"> var map; $(document).ready(function(){ var fill = new ol.style.Fill({ color: '#acc000' }); var stroke = new ol.style.Stroke({ color: '#ff0000', width: 3 }); var icon = new ol.style.Icon({ src:"http://www.cretasolaris.gr/gfx/marker.png", size:[36,33], opacity:1 }) function createLordStyle(){ return function(feature, resolution){ var style = new ol.style.Style({ image: icon, text: new ol.style.Text({ text:feature.get('name'), offsetY: 25, font: '15px Calibri,sans-serif', fill: new ol.style.Fill({ color: "#000" }) }) }) return [style] } } map = new ol.Map({ view: new ol.View({ center:ol.proj.transform([2,48], 'EPSG:4326', 'EPSG:3857'), zoom: 5 }), target: 'map', layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }) ] }) vecLayer = new ol.layer.Vector({ source: new ol.source.Vector(), style: createLordStyle() }) var feature = new ol.Feature({ geometry: new ol.geom.Point([738600, 5840171]), name: "The precious is here!", author: "Gollum" }); map.addLayer(vecLayer) var feature2 = new ol.Feature({ geometry: new ol.geom.Point([1938600, 6840171]), name: "The hobbits are far away...", author: "Gandalf" }); vecLayer.getSource().addFeatures([feature,feature2]) }); </script> <style type="text/css"> #map { width: 100%; height: 100%; } </style> </head> <body> <div id="map"></div> </body> </html>
TODO
var wfsFormat = new ol.format.WFS({ featureNS:"http://mediamaps.ch/geoinf", featureType:"cities" }) var vectorSource = new ol.source.ServerVector({ format: new ol.format.WFS({ featureNS:"http://twitter/oertz", featureType:"cities" }), loader: function(extent, resolution, projection) { var url = 'http://geoserver-heig.rhcloud.com/geoinf/ows?service=WFS&version=1.1.0&request=GetFeature&typeName=geoinf:cities&srsname=EPSG:3857' $.ajax({ url: url }) .done(function(response) { vectorSource.addFeatures(wfsFormat.readFeatures(response)); }); }, strategy: ol.loadingstrategy.all, projection: 'EPSG:4326' }); var citiesLayer = new ol.layer.Vector({ source: vectorSource })
Openlayers applique un style par défaut aux features ol.style.
TODO
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, comme ceux en lien avec la vie d'un ol.Map (voir ol.MapBrowserEvents).
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 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.
<html> <head> <title>ol3 - 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(){ var wmsSource = new ol.source.ImageWMS({ url: 'https://eu1.mapcentia.com/wms/oertz/public', params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple', FORMAT: 'image/png' }, serverType: 'mapserver', }); var wmsLayer = new ol.layer.Image({ source: wmsSource }); var view = new ol.View({ center: [0, 0], zoom: 1 }); map = new ol.Map({ layers: [wmsLayer], target: 'map', view: view }); map.on('singleclick', function(evt) { var viewResolution = (view.getResolution()); var url = wmsSource.getGetFeatureInfoUrl( evt.coordinate, viewResolution, 'EPSG:3857', {'INFO_FORMAT': 'text/plain'}); if (url) { getFeatureInfo(url); } }); function getFeatureInfo(url){ var url2 = encodeURIComponent(url); var request = $.ajax({ url: "http://localhost:8888/openlayers/ol2014-workshop/proxy.php?url="+url2, dataType: 'text', contentType: 'text/plain', method: "GET" }); request.done(function( data ) { console.log(data) $("#info").html(data.replace(":", "<br>")); }); request.fail(function( jqXHR, textStatus ) { alert( "Request failed: " + textStatus ); }); } map.zoomToMaxExtent(); }); </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 url = wmsSource.getGetFeatureInfoUrl( evt.coordinate, viewResolution, 'EPSG:3857', {'INFO_FORMAT': 'text/plain','radius':50} );
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(){ var wmsSource = new ol.source.ImageWMS({ url: 'https://eu1.mapcentia.com/wms/oertz/public', params:{ VERSION: "1.1.1", LAYERS: 'public.world_simple', FORMAT: 'image/png' }, serverType: 'mapserver', }); var wmsLayer = new ol.layer.Image({ source: wmsSource }); var view = new ol.View({ center: [0, 0], zoom: 1, projection: "EPSG:4326" }); map = new ol.Map({ layers: [wmsLayer], target: 'map', view: view }); map.on('singleclick', function(evt) { getFeatureInfo(evt.coordinate); }); function getFeatureInfo(coordinates){ var request = $.ajax({ url: getInfo = "php/GetCountriesByXY.php?x=" + coordinates[0] + "&y=" + coordinates[1], dataType: 'json', method: "GET" }); request.done(function( data ) { $("#info").html(data.features[0].properties.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>
Côté serveur:
<?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
$("#info").html("Loading... please wait...");
var vectorLayer = new ol.layer.Vector({source: new ol.source.Vector({})}) map.addLayer(vectorLayer)
et remplacer l'appel AJAX comme suit :
var request = $.ajax({ url: getInfo = "php/GetCountriesByXY.php?x=" + coordinates[0] + "&y=" + coordinates[1], dataType: 'json', method: "GET" }); request.done(function( data ) { var js = new ol.format.GeoJSON() $("#info").html(data.features[0].properties.name); var feature = js.readFeature(data.features[0]) vectorLayer.getSource().addFeature(feature) }); request.fail(function( jqXHR, textStatus ) { alert( "Request failed: " + textStatus ); });
var selectPointerMove = new ol.interaction.Select({ condition: ol.events.condition.pointerMove }); map.addInteraction(selectPointerMove) selectPointerMove.on('select',function(e){ if(e.target.getFeatures().getLength() > 0){ $("#info").html(e.target.getFeatures().item(0).get('name')); } }) map.addInteraction(selectPointerMove)
Les interactions permettent de récupérer et d'exploiter des actions déclenchées par l'utilisateur (via son clavier ou sa souris). Ces actions peuvent ensuite être utilisées pour modifier les différents éléments sur la map ou la map elle même. Dans notre cas, notre interaction permet de sélectionner (ol.interaction.Select) les features de notre carte au passage de la souris (condition: ol.events.condition.pointerMove) sur celles-ci.
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 ol.loadingstrategy.all 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; $(document).ready(function(){ var osmLayer = new ol.layer.Tile({ source: new ol.source.OSM() }); var view = new ol.View({ center: ol.proj.transform([8,47],"EPSG:4326","EPSG:3857"), zoom: 5 }); map = new ol.Map({ layers: [osmLayer], target: 'map', view: view }); var wfsFormat = new ol.format.WFS({ featureNS:"http://mediamaps.ch/geoinf", featureType:"cities" }) var vectorSource = new ol.source.ServerVector({ format: wfsFormat, loader: function(extent, resolution, projection) { var url = 'http://geoserver-heig.rhcloud.com/geoinf/ows' $.ajax({ url: url, data:{ service: 'WFS', version: '1.1.0', request: 'GetFeature', typename: 'cities', srsname: 'EPSG:3857' }, dataType: 'text', success: function(response) { 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
featureType:"european_nuts"
pour le format et
typename: 'european_nuts'
dans la requête ajax.
ol.loadingstrategy.bbox
ajouter le paramètre suivant à la requête ajax
bbox: extent.join(',') + ',EPSG:3857'
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.
var lyr = new ol.layer.Vector({ source: vectorSource, visible: false })
view.on('change:resolution',function(){ if(view.getZoom() > 8){ lyr.setVisible(true) }else{ lyr.setVisible(false) } })
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
var fill = new ol.style.Fill({color: 'rgba(0,0,255,0.6)'}); var selectFill = new ol.style.Fill({color: 'rgba(255,0,0,0.6)'}); var defaultPoint = new ol.style.Style({ image: new ol.style.Circle({ fill:fill, radius: 6 }) }) var selectPoint = new ol.style.Style({ image: new ol.style.Circle({ selectFill, radius: 6 }) })
puis les connecter en ajoutant un styleMap sur la couche Vector comme suit :
var lyr = new ol.layer.Vector({ source: vectorSource, style: defaultPoint })
var selectPointerMove = new ol.interaction.Select({ condition: ol.events.condition.pointerMove, style: selectPoint }); map.addInteraction(selectPointerMove) selectPointerMove.on('select',function(e){ if(e.target.getFeatures().getLength() > 0){ $("#info").html(e.target.getFeatures().item(0).get('WUP_AGGL')); } })
ol.Overlay(...)
feature.getGeometry().getCoordinates()
map.addOverlay(...)