Measuring distances and areas

The capability to measure distances or areas is an important thing on many GIS applications.

In this recipe, we are going to see in action what the Measure control in OpenLayers offers to the developer.

The application will show a simple map with some buttons on top, as shown in the following screenshot. The Measure toggle button activates or deactivates the control, while the radio buttons bring us the possibility to select what to measure: a path or an area:

Measuring distances and areas

In addition, we can set two control options. The Geodesic control indicates if the distance of the area computation must be in geodesic metrics instead of planar. The Immediate option is useful to update the measure every time we move the mouse.

How to do it...

Here, we are going to write not the whole source code but only those pieces of code that are important for the recipe. So, we are avoiding putting here the HTML code required to build the measure button, checkboxes, options radio buttons, and the div element that holds the map instance.

  1. Let's take a look at the JavaScript code. First, instantiate the map, add a base layer, and center the map display:
        var map = new OpenLayers.Map("ch05_measure");    
        var osm = new OpenLayers.Layer.OSM();        
        map.addLayer(osm);
        map.addControl(new OpenLayers.Control.LayerSwitcher());
        map.setCenter(new OpenLayers.LonLat(0, 0), 3);
  2. Now, add the Measure control. Note that we are registering two listener functions for the events measure and measurepartial:
        var measureControl = new OpenLayers.Control.Measure(OpenLayers.Handler.Path, {
            persist: true,
            eventListeners: {
                'measure': measure,
                'measurepartial': measurepartial
            }
        });
  3. Next, place the code for the Measure toggle button that activates or deactivates th control:
        function measureClick(checked) {
            var path = dijit.byId('path').get('checked'),
            var polygon = dijit.byId('polygon').get('checked'),
            var regular = dijit.byId('regular').get('checked'),
            
            if(checked){
                if(path) {
                    measureControl.updateHandler(OpenLayers.Handler.Path, {persist: true});
                } else if(polygon) {
                    measureControl.updateHandler(OpenLayers.Handler.Polygon, {persist: true});
                } else if(regular) {
                    measureControl.updateHandler(OpenLayers.Handler.RegularPolygon, {persist: true});
                }
                map.addControl(measureControl);
                measureControl.activate();
            } else {
                measureControl.deactivate();
                map.removeControl(measureControl);
            }
            
            dojo.byId('value').innerHTML = "";
        }
  4. Implement the listener functions for the measure and measurepartial control events:
        function measure(event) {
            var message = event.measure + " " + event.units;
            if(event.order>1) {
                message += "2";
            }
            dojo.byId('value').innerHTML = message;
        }
        
        function measurepartial(event) {
            var message = event.measure + " " + event.units;
            dojo.byId('value').innerHTML = message;
        }
  5. Finally, place the code for the functions that change the Geodesic and Immediate options:
        function changeImmediate(checked) {
            measureControl.setImmediate(checked);
        }
        function changeGeodesic(checked) {
            measureControl.geodesic = checked;
        }

How it works...

Let's start analyzing how we initialized the measure control:

    var measureControl = new OpenLayers.Control.Measure(OpenLayers.Handler.Path, {
        persist: true,
        eventListeners: {
            'measure': measure,
            'measurepartial': measurepartial
        }
    });

The only parameter we need to pass to the control is a handler to be used.

Like many other controls, the OpenLayers.Control.Measure class makes use of handlers to interact with the map. In this case, the measure control can make use of any handler that allows to draw geometries. To summarize, the flow is as follows:

  • The control is activated and it delegates to a handler the task of drawing some geometry in the map
  • Once the handler has drawn the desired geometry, (such as a path or a polygon) the feature is returned to the control
  • The control computes the distance or area of the geometry and triggers an event

Note

Actually, we have a limitation to use handlers that return geometries that implement the getArea() or getLength() methods. For example, if you try to use the OpenLayers.Handler.Box handler with the measure control, once you activate the control and draw a box, you will get an error in the browser console. This is because the box handler returns an OpenLayers.Bounds instance that neither has a getLength nor getArea method.

In our code we have initialized the measure control setting as follows:

  • The persist property to true. This property indicates that the geometry created by the handler must remain on the map until a new measure starts.
  • Two event listeners, for the events measure and measurepartial. The measure event is triggered once the measure action has been finished. The measurepartial is triggered on any measure update (only if the immediate property is true).

When the Measure toggle button is pressed, the measureClick function is executed. This function checks what kind of handler must be used for the measurement and sets it on the control.

This can be done by the updateHandler method on the Measure control. For example:

measureControl.updateHandler(OpenLayers.Handler.Polygon, {persist: true});

In addition, the measureClick function adds the control to the map and activates when the button is toggled on, or deactivates and removes the control from the map when the button is toggled off.

For the control options buttons, we have set two listening functions associated to the checkboxes.

When the Immediate checkbox changes, the changeImmediate function is executed. This, using the setImmediate method, changes the immediate property of the control, which allows triggering events every time the measure updates with a mouse movement:

    function changeImmediate(checked) {
        measureControl.setImmediate(checked);
    }

The Geodesic checkbox sets the value of the geodesic property. This time we can modify the property directly without the need of a setter function:

    function changeGeodesic(checked) {
        measureControl.geodesic = checked;
    }

With the geodesic property set to true, the control will use a geodesic metric instead of a planar metric to compute the measures.

There's more...

An important part of the measurement is done with the geometric instances.

All the geometry classes, such as OpenLayers.Geometry.Polygon or OpenLayers.Geometry.LineString, contain methods to compute their area or length.

Looking at the measure control source code, we can see how once its associated handler returns a geometry, it simply calls the geometry methods to get the area or length and triggers an event.

See also

  • The Working with geolocation recipe
  • The Editing features on multiple vector layers recipe
  • The Modifying features recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.10.45