Bienvenue sur PostGIS.fr

Bienvenue sur PostGIS.fr , le site de la communauté des utilisateurs francophones de PostGIS.

PostGIS ajoute le support d'objets géographique à la base de données PostgreSQL. En effet, PostGIS "spatialise" le serverur PostgreSQL, ce qui permet de l'utiliser comme une base de données SIG.

Maintenu à jour, en fonction de nos disponibilités et des diverses sorties des outils que nous testons, nous vous proposons l'ensemble de nos travaux publiés en langue française.

source: trunk/workshop-routing-foss4g/chapters/geoext_client.rst @ 63

Revision 63, 14.5 KB checked in by djay, 12 years ago (diff)

Initial import of pgROuting workshop for translation. Section 1 to 3 translated, pleae review.

RevLine 
[63]1==============================================================================================================
2GeoExt Browser Client
3==============================================================================================================
4
5`GeoExt <http://www.geoext.org/>`_ is a *JavaScript Toolkit for Rich Web Mapping Applications*. GeoExt
6brings together the geospatial know how of `OpenLayers <http://www.openlayers.org>`_ with
7the user interface savvy of `Ext JS <http://www.sencha.com>`_ to help you build powerful desktop
8style GIS apps on the web with JavaScript.
9
10Let's start with a simple GeoExt example and extend it with routing functionality then:
11
12.. literalinclude:: ../../web/routing-0.html
13        :language: html
14
15In the header we include all the javascript and css needed for the application,
16we also define a function to be run when the page is loaded (Ext.onReady).
17
18This function creates a `GeoExt.MapPanel
19<http://www.geoext.org/lib/GeoExt/widgets/MapPanel.html>`_ with an
20OpenStreetMap layer centered to Denver. In this code, no OpenLayers.Map is
21explicitly created; the GeoExt.MapPanel do this under the hood: it takes the map options, the
22center and the zoom and create a map instance accordingly.
23
24To allow our users to get directions, we need to provide:
25 * a way to select the routing algorithm (Shortest path Dijkstra, A* or Shooting*),
26 * a way to select the start and final destination.
27
28.. note:: This chapter only show code snippets, the full source code of the
29 page can be found in ``pgrouting-workshop/web/routing-final.html`` that should
30 be on your desktop. The full listing can also be found at the end of this chapter.
31
32-------------------------------------------------------------------------------------------------------------
33Routing method selection
34-------------------------------------------------------------------------------------------------------------
35
36To select the routing method, we will use an `Ext.form.ComboBox
37<http://www.sencha.com/deploy/dev/docs/?class=Ext.form.ComboBox>`_: it
38behaves just like a normal html select but we can more easily control it.
39
40Just like the GeoExt.MapPanel, we need an html element to place our control,
41let's create a new div in the body (with 'method' as id):
42
43 .. code-block:: html
44
45   <body>
46     <div id="gxmap"></div>
47     <div id="method"></div>
48   </body>
49
50Then we create the combo itself:
51 .. code-block:: js
52
53   var method = new Ext.form.ComboBox({
54       renderTo: "method",
55       triggerAction: "all",
56       editable: false,
57       forceSelection: true,
58       store: [
59           ["SPD", "Shortest Path Dijkstra"],
60           ["SPA", "Shortest Path A*"],
61           ["SPS", "Shortest Path Shooting*"]
62       ]
63   });
64
65In the ``store`` option, we set all the possible values for the routing method;
66the format is an array of options where an option is in the form ``[key, name]``.
67The ``key`` will be send to the server (the php script in our case) and the
68``value`` displayed in the combo.
69
70The ``renderTo`` specify where the combo must be rendered, we use our new div here.
71
72And finally, a default value is selected:
73 .. code-block:: js
74
75    method.setValue("SPD");
76
77This part only uses ExtJS component: no OpenLayers or GeoExt code here.
78
79-------------------------------------------------------------------------------------------------------------
80Select the start and final destination
81-------------------------------------------------------------------------------------------------------------
82
83We want to allow the users to draw and move the start and final destination
84points. This is more or less the behavior of google maps and others: the user
85selects the points via a search box (address search) or by clicking the
86map. The system query the server and display the route on the map. The user
87can later move the start or final point and the route is updated.
88
89In this workshop, we will only implement the input via the map (draw points and
90drag-and-drop) but it's possible to implement the search box feature by using a
91web service like `GeoNames <http://www.geonames.org/>`_ or any other `geocoding
92<http://en.wikipedia.org/wiki/Geocoding>`_ service.
93
94To do this we will need a tool to draw points (we will use the
95`OpenLayers.Control.DrawFeatures
96<http://openlayers.org/dev/examples/draw-feature.html>`_ control) and a tool to
97move points (`OpenLayers.Control.DragFeatures
98<http://openlayers.org/dev/examples/drag-feature.html>`_ will be perfect for
99this job). As their name suggests these controls comes from OpenLayers.
100
101These two controls will need a place to draw and manipulate the points; we
102will also need an `OpenLayers.Layer.Vector
103<http://dev.openlayers.org/releases/OpenLayers-2.10/doc/apidocs/files/OpenLayers/Layer/Vector-js.html>`_
104layer. In OpenLayers, a vector layer in a place where features (a geometry and
105attributes) can be drawn programmatically (in contrast, the OSM layer is a
106raster layer).
107
108Because vector layers are cheap, we will use a second one to draw the route
109returned by the web service. The layers initialization is:
110
111 .. code-block:: js
112
113    // create the layer where the route will be drawn
114    var route_layer = new OpenLayers.Layer.Vector("route", {
115        styleMap: new OpenLayers.StyleMap(new OpenLayers.Style({
116            strokeColor: "#ff9933",
117            strokeWidth: 3
118        }))
119    });
120
121``"route"`` is the layer name, any string can be used.
122``styleMap`` gives the layer a bit of visual style with a custom stroke color and
123width (in pixel).
124
125The second layer initialization is simply:
126
127 .. code-block:: js
128
129    // create the layer where the start and final points will be drawn
130    var points_layer = new OpenLayers.Layer.Vector("points");
131
132The two layers are added to the OpenLayers.Map object with:
133
134 .. code-block:: js
135
136    // add the layers to the map
137    map.addLayers([points_layer, route_layer]);
138
139Let's look at the control to draw the points: because this component has
140special behavior it's more easy to create a new class based on the standard
141OpenLayers.Control.DrawFeatures control. This new control (named DrawPoints) is
142saved in a separated javascript file (``web/DrawPoints.js``):
143
144.. literalinclude:: ../../web/DrawPoints.js
145        :language: js
146
147In the ``initialize`` function (that's the class constructor) we set that
148this control can only draw points (handler variable is OpenLayers.Handler.Point).
149
150The special behavior is implemented in the ``drawFeature`` function: because we
151only need the start and final points the control deactivates itself when two
152points are drawn by counting how many features has the vector
153layer. Control deactivation is ``this.deactivate()``.
154
155Our control is then created with:
156
157 .. code-block:: js
158
159    // create the control to draw the points (see the DrawPoints.js file)
160    var draw_points = new DrawPoints(points_layer);
161
162``points_layer`` is the vector layer created earlier.
163
164And now for the DragFeature control:
165
166 .. code-block:: js
167
168    // create the control to move the points
169    var drag_points = new OpenLayers.Control.DragFeature(points_layer, {
170        autoActivate: true
171    });
172
173Again, ``points_layer`` is the vector layer, ``autoActivate: true`` tells
174OpenLayers that we want this control to be automatically activated.
175
176 .. code-block:: js
177
178    // add the controls to the map
179    map.addControls([draw_points, drag_points]);
180
181Adds the controls to the map.
182
183-------------------------------------------------------------------------------------------------------------
184Call and receive data from web service
185-------------------------------------------------------------------------------------------------------------
186
187The basic workflow to get a route from the web server is:
188
189#. transform our points coordinates from EPSG:900913 to EPSG:4326
190#. call the web service with the correct arguments (method name and two points coordinates)
191#. parse the web service response: transform GeoJSON to OpenLayers.Feature.Vector
192#. transform all the coordinates from EPSG:4326 to EPSG:900913
193#. add this result to a vector layer
194
195The first item is something new: our map uses the EPSG:900913 projection
196(because we use an OSM layer) but the web service expects coordinates in
197EPSG:4326: we have to re-project the data before sending them. This is not a
198big deal: we will simply use the `Proj4js <http://trac.osgeo.org/proj4js/>`_
199javascript library.
200
201(The second item *call the web service* is covered in the next chapter.)
202
203The routing web service in pgrouting.php returns a `GeoJSON
204<http://geojson.org/>`_ FeatureCollection object. A FeatureCollection is simply
205an array of features: one feature for each route segment. This is very convenient because
206OpenLayers and GeoExt have all what we need to handle this format. To make our
207live even easier, we are going to use the GeoExt.data.FeatureStore:
208
209 .. code-block:: js
210
211    var store = new GeoExt.data.FeatureStore({
212        layer: route_layer,
213        fields: [
214            {name: "length"}
215        ],
216        proxy: new GeoExt.data.ProtocolProxy({
217            protocol: new OpenLayers.Protocol.HTTP({
218                url: './php/pgrouting.php',
219                format: new OpenLayers.Format.GeoJSON({
220                    internalProjection: epsg_900913,
221                    externalProjection: epsg_4326
222                })
223            })
224        })
225    });
226
227A store is simply a container to store informations: we can push data into and
228get it back.
229
230Let's explain all the options:
231
232``layer``: the argument is a vector layer: by specifying a layer, the
233FeatureStore will automatically draw the data received into this
234layer. This is exactly what we need for the last item (*add this result to a
235vector layer*) in the list above.
236
237``fields``: lists all the attributes returned along with the geometry: pgrouting.php
238returns the segment length so we set it here. Note that this information is not
239used in this workshop.
240
241``proxy``: the proxy item specify where the data should be taken: in our case
242from a HTTP server. The proxy type is GeoExt.data.ProtocolProxy: this class
243connects the ExtJS world (the store) and the OpenLayers world (the protocol
244object).
245
246``protocol``: this OpenLayers component is able to make HTTP requests to an ``url``
247(our php script) and to parse the response (``format`` option). By adding the
248``internalProjection`` and ``externalProjection`` option, the coordinates
249re-projection in made by the format.
250
251We now have all what we need to handle the data returned by the web service: the next
252chapter will explain how and when to call the service.
253
254-------------------------------------------------------------------------------------------------------------
255Trigger the web service call
256-------------------------------------------------------------------------------------------------------------
257
258We need to call the web service when:
259 * the two points are drawn
260 * one of the point is moved
261 * the routing method has changed
262
263Our vector layer generates an event (called ``featureadded``) when a
264new feature is added, we can listen to this event and call to pgrouting
265function (this function will be presented shortly):
266
267 .. code-block:: js
268
269    draw_layer.events.on({
270        featureadded: function() {
271            pgrouting(store, draw_layer, method.getValue());
272        }
273    });
274
275.. note:: Before we continue some words about events: an event in OpenLayers
276  (the same apply for ExtJS and other frameworks), is a system to allow a
277  function to be called when *something* happened. For instance when a layer is
278  added to the map or when the mouse is over a feature. Multiple functions can
279  be connected to the same event.
280
281No event is generated when a point is moved, hopefully we can give a
282function to the DragFeature control to be called we the point is moved:
283
284 .. code-block:: js
285
286    drag_points.onComplete = function() {
287        pgrouting(store, draw_layer, method.getValue());
288    };
289
290For the *method* combo, we can add a listeners options to the constructor with
291a `select` argument (that's the event triggered when the user changes the value):
292
293 .. code-block:: js
294
295    var method = new Ext.form.ComboBox({
296        renderTo: "method",
297        triggerAction: "all",
298        editable: false,
299        forceSelection: true,
300        store: [
301            ["SPD", "Shortest Path Dijkstra"],
302            ["SPA", "Shortest Path A*"],
303            ["SPS", "Shortest Path Shooting*"]
304        ],
305        listeners: {
306            select: function() {
307                pgrouting(store, draw_layer, method.getValue());
308            }
309    });
310
311
312It's now time to present the pgrouting function:
313
314 .. code-block:: js
315
316   // global projection objects (uses the proj4js lib)
317   var epsg_4326 = new OpenLayers.Projection("EPSG:4326"),
318       epsg_900913 = new OpenLayers.Projection("EPSG:900913");
319
320   function pgrouting(store, layer, method) {
321         if (layer.features.length == 2) {
322             // erase the previous route
323             store.removeAll();
324
325             // transform the two geometries from EPSG:900913 to EPSG:4326
326             var startpoint = layer.features[0].geometry.clone();
327             startpoint.transform(epsg_900913, epsg_4326);
328             var finalpoint = layer.features[1].geometry.clone();
329             finalpoint.transform(epsg_900913, epsg_4326);
330
331             // load to route
332             store.load({
333                 params: {
334                     startpoint: startpoint.x + " " + startpoint.y,
335                     finalpoint: finalpoint.x + " " + finalpoint.y,
336                     method: method
337                 }
338             });
339        }
340    }
341
342The pgrouting function calls the web service through the store argument.
343
344At first, the function checks if two points are passed in
345argument. Then, ``store.removeAll()`` is called to erase a previous result
346from the layer (remember that the store and the vector layer are binded).
347The two points coordinates are then projected using OpenLayers.Projection
348instances.
349
350Finally, ``store.load()`` is called with a ``params`` representing the
351pgrouting.php arguments (they are passed to the HTTP GET call).
352
353-------------------------------------------------------------------------------------------------------------
354What's next ?
355-------------------------------------------------------------------------------------------------------------
356
357Possible enhancements:
358 * use a geocoding service to get start/final point
359 * way point support
360 * nice icons for the start and final points
361 * driving directions (road map): we already have the segment length
362
363-------------------------------------------------------------------------------------------------------------
364Full source code
365-------------------------------------------------------------------------------------------------------------
366
367.. literalinclude:: ../../web/routing-final.html
368        :language: html
369
Note: See TracBrowser for help on using the repository browser.