The goal of this chapter is to create a simple web based user interface to pgRouting based on OpenLayers 3. The user will be able to choose the start and destination location of the routing by clicking on the map.
The general workflow of this application is to wait until we have the start and destination points, then we send these values to the WMS server who will query the database for a routing result. The result is represented as an image by the WMS server and returned to our application and displayed.
OpenLayers 3 is a complete rewrite of OpenLayers 2, it uses modern javascript and HTML5 technologies such as Canvas and WebGL. At the time of writing not all of the features of the version 2 have been ported but the core features are here.
The new code is based on the Google Closure Tools, this allows us to use a comprehensive and well-tested library (the Closure Library, also used to build Gmail, Google Maps and most of the Google web applications). But the most powerful tool is the Closure Compiler; a java based compiler who can remove dead code, optimize and minimize javascript. These tools are completely optional for the OpenLayers library users: they only need to download the compiled code and use it, that’s what we will do now.
Let’s explore some key concepts of OpenLayers 3:
At the heart of the library we have the map (ol.Map class), responsible for managing the layers, the controls, the view and the renderer.
Each map has a renderer who is responsible to draw the layers into the HTML element. They are three different type of renderer:
The view (ol.View class) represents what’s displayed in the map: this geographic center of the map, the resolution but also the map rotation. Unlike others library, these values are separated from the map object; one advantage is to allows two maps to share the same view (for example in this example)
Let’s start our first OpenLayers 3 map: open a text editor and copy this code into a file named ol3.html. You can save this file into the Desktop and open it with a web browser.
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 26 27 28 29 30 31 32 33 34 35 36 | <!DOCTYPE html>
<html>
<head>
<title>ol3 pgRouting client</title>
<meta charset="utf-8">
<link href="ol3/ol.css" rel="stylesheet">
<style>
#ol-map {
width: 100%;
height: 400px;
}
</style>
<script src="ol3/ol.js"></script>
</head>
<body>
<div id="ol-map"></div>
<script type="text/javascript">
var map = new ol.Map({
target: 'ol-map',
renderer: ol.RendererHint.CANVAS,
layers: [
new ol.layer.TileLayer({
source: new ol.source.OSM()
})
],
view: new ol.View2D({
center: [-127998, 6974591],
zoom: 10
})
});
</script>
</body>
</html>
|
This code displays a map with an OpenStreetMap layer centered to Nottingham.
At the moment there’s not routing related code; only standard navigation.
The rest of the file (inside the script tag) will contain our javascript code to query the server for a routing and display the result.
Once the page is open in a web browser, try to open the javascript consoleand interact with the map object:
map.getView().getCenter();
map.getView().setCenter([-29686, 6700403]);
map.getView().setRotation(Math.PI);
Note
If you inspect an OpenLayers object using the console, you can see that most of the properties and functions have a short (and cryptic) name; that’s because the Google Closure Compiler renames the original names. The goal of this compilation is to product the smaller library as possible.
Add this code a the end of the script tag:
var params = {
LAYERS: 'pgrouting:pgrouting',
FORMAT: 'image/png'
};
The params object holds the WMS GET parameters that will be sent to GeoServer. Here we set the values that will never change: the layer name and the output format.
We want to allow the users to set the start and destination position by clicking on the map.
Add two divs inside the map element
<div id="ol-map">
<div id="start-point">start</div>
<div id="final-point">final</div>
</div>
Add this code a the end of the script tag:
var startPoint = new ol.Overlay({
map: map,
element: document.getElementById('start-point')
});
var finalPoint = new ol.Overlay({
map: map,
element: document.getElementById('final-point')
});
It’s creates two overlays, they get a reference to the map because they need to update their position according to the view (in short: they move when the map moves).
They are invisible because the position is not set (equal to undefined).
Add this code a the end of the script tag:
var transform = ol.proj.getTransform('EPSG:3857', 'EPSG:4326');
map.on('click', function(event) {
var coordinate = event.getCoordinate();
if (startPoint.getPosition() == undefined) {
// first click
startPoint.setPosition(coordinate);
} else if (finalPoint.getPosition() == undefined) {
// second click
finalPoint.setPosition(coordinate);
// transform the coordinates from the map projection (EPSG:3857)
// into the server projection (EPSG:4326)
var startCoord = transform(startPoint.getPosition());
var finalCoord = transform(finalPoint.getPosition());
var viewparams = [
'x1:' + startCoord[0], 'y1:' + startCoord[1],
'x2:' + finalCoord[0], 'y2:' + finalCoord[1]
];
params.viewparams = viewparams.join(';');
// we now have the two points, create the result layer and add it to the map
result = new ol.layer.ImageLayer({
source: new ol.source.SingleImageWMS({
url: 'http://localhost:8082/geoserver/pgrouting/wms',
params: params
})
});
map.addLayer(result);
}
});
When the map is clicked, this function is executed. The geographical position of the cursor is stored into the overlays; this has the side effect of displaying them.
Once we have the start and destination points (after two clicks); the two pairs of coordinates are transformed from the map projection (EPSG:3857) into the server projection (EPSG:4326) using the transform function.
The viewparams property is set on WMS GET parameters object. The value of this property has a special meaning: GeoServer will substitute the value before executing the SQL query for the layer. For example, if we have:
SELECT * FROM ways WHERE maxspeed_forward BETWEEN %min% AND %max%
And viewparams is viewparams=min:20;max:120 then the query sent to PostGIS will be:
SELECT * FROM ways WHERE maxspeed_forward BETWEEN 20 AND 120
Finally, a new OpenLayers WMS layer is created an added to the map, the param object is passed to it.
Add this code between the map’s html div and the script tag:
<button id="clear">clear</button>
This create a button to allow the user to clear the routing points and start a new routing query.
Add this code a the end of the script tag:
document.getElementById('clear').addEventListener('click', function(event) {
// hide the overlays
startPoint.setPosition(undefined);
finalPoint.setPosition(undefined);
// hide the result layer
result.setVisible(false);
});
When the button is clicked, this function is executed. The routing points and the result layer are hidden.