Working with projections

In contrast to other JavaScript mapping libraries, OpenLayers allows working with a great number of projections.

Usually, we specify the desired projection for the map. Later when adding a vector layer to the map, we need to specify to the layer projection so that OpenLayers transforms features from the layer's projection to the map's projection.

But, by default, OpenLayers has a great limitation on projections: we can only use EPSG:4326 and EPSG:900913. Why? Because transforming between projections is not a simple task and there are other great projects that can make it.

So, when we want to work with projections other than EPSG:4326 and EPSG:900913, OpenLayers uses Proj4js Library (http://trac.osgeo.org/proj4js).

Note

Teaching about projections is out of the scope of this book. The EPSG codes are simply a standardized way to classify and identify the great amount of available projections. EPSG:4326 corresponds to the WGS84 (World Geodetic System) and EPSG:900913 is the Spherical Mercator projection popularized by their use in Google Maps.

Let's go to see how we can integrate Proj4js with OpenLayers and how easy it is to make use of it. The idea is to create an application that shows a map and a text area that will show the coordinates of the clicked location:

Working with projections

Getting ready

We must place some of the available Proj4js files at our web application directory. To do so, perform the following steps:

  • Go to the Proj4js project's web page and download the distribution ZIP file (for this recipe we have used http://download.osgeo.org/proj4js/proj4js-1.1.0.zip)
  • Uncompress the downloaded file and copy the proj4js-compressed.js file and defs folder within your web application folder

How to do it...

  1. Create an HTML file and add the OpenLayers dependencies. As a dependency, also include the Proj4js library:
    <script type="text/javascript" src="./js/proj4js-1.1.0/proj4js-compressed.js"></script>
  2. Now add the code for the text area and the map:
    <textarea id="textarea" name="textarea" data-dojo-type="dijit.form.SimpleTextarea" rows="4" cols="80"></textarea>
    <br/><br/>
    <div id="ch08_projections" style="width: 100%; height: 85%;"></div>
  3. In the JavaScript section, create a new control to manage the click event:
    <script type="text/javascript">
        // Create the click control
        OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {    
        
            defaultHandlerOptions: {
                'single': true,
                'double': false,
                'pixelTolerance': 0,
                'stopSingle': false,
                'stopDouble': false
            },
    
            initialize: function(options) {
                this.handlerOptions = OpenLayers.Util.extend({}, this.defaultHandlerOptions);
                OpenLayers.Control.prototype.initialize.apply(this, arguments); 
                this.handler = new OpenLayers.Handler.Click(
                this, {
                    'click': this.trigger
                }, 
                this.handlerOptions);
            }, 
  4. On the trigger function, add the following code to transform and show the coordinates in the textarea object:
            trigger: function(e) {
                var lonlatS = map.getLonLatFromViewPortPx(e.xy);
                var lonlatT1 = lonlatS.clone().transform( map.getProjectionObject(), new OpenLayers.Projection("EPSG:41001") );
                var lonlatT2 = lonlatS.clone().transform( map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326") );
                
                var message = "Click at: 
    "+
                    "Lon: " + lonlatS.lon + " , Lat: "+lonlatS.lat + " ("+map.getProjection()+")
    " +
                    "Lon: " + lonlatT2.lon + " , Lat: "+lonlatT2.lat + " (EPSG:4326) 
    " +
                    "Lon: " + lonlatT1.lon + " , Lat: "+lonlatT1.lat + " (EPSG:41001) 
    ";
                
                dijit.byId("textarea").set('value', message);
            },
        
            CLASS_NAME: "OpenLayers.Control.Click"
        });
  5. Create the map instance, add a base layer, and center the viewport:
        var map = new OpenLayers.Map("ch08_projections");
        var osm = new OpenLayers.Layer.OSM();
        map.addLayer(osm);
        map.setCenter(new OpenLayers.LonLat(0,0), 2);
  6. Finally, create a new click control instance and add it to the map:
        var click = new OpenLayers.Control.Click();
        map.addControl(click);
        click.activate();
    </script>

How it works...

OpenLayers makes use of the Proj4js code internally when available. So as OpenLayers developers we do not need to use the Proj4js API directly, the only requirement is to add the Proj4js dependency in our application:

<script type="text/javascript" src="./js/proj4js-1.1.0/proj4js-compressed.js"></script>

When the user clicks at some place on the map, the click control (that we will see later) executes the trigger function. The e variable contains all the click event's information that includes the pixel's xy position.

        trigger: function(e) {
            var lonlatS = map.getLonLatFromViewPortPx(e.xy);
            var lonlatT1 = lonlatS.clone().transform( map.getProjectionObject(), new OpenLayers.Projection("EPSG:41001") );
            var lonlatT2 = lonlatS.clone().transform( map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326") );
            …
            ...

Note

Ensure that the projection definition you are using, is defined within the defs folder. Otherwise you will need to create a new file with the transformation expressed in the proj4 notation.

Given an OpenLayers.LonLat instance, we can translate among projections using the tranform() method.

Note

We always can make use of the transform() method but without including the Proj4js dependencies, they will only translate between EPSG:4326 and EPSG:900913.

Thanks to the OpenLayers.Map.getLonLatFromViewPortPx() method we can go from OpenLayers.Pixel to the OpenLayers.LonLat instance.

Because the transform method modifies the current instance, we create a new one using the clone() method to avoid modifying the source variable.

At this point, the trigger method can construct a message string and place it within the text area.

Finally, let's briefly describe the click control used in the recipe.

The first step is to define the new control as a subclass of the OpenLayers.Control class:

    OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {    

The control will use an OpenLayers.Handler, so here we will define some options:

        defaultHandlerOptions: {
            'single': true,
            'double': false,
            'pixelTolerance': 0,
            'stopSingle': false,
            'stopDouble': false
        },

The initialize method is responsible to initialize the control instance. First, we create a set of options as a combination (using OpenLayers.Util.extend() method) of the previously defined object and options passed by the user as arguments:

        initialize: function(options) {
            this.handlerOptions = OpenLayers.Util.extend({}, this.defaultHandlerOptions);
            OpenLayers.Control.prototype.initialize.apply(this, arguments); 
            this.handler = new OpenLayers.Handler.Click(
            this, {
                'click': this.trigger
            }, 
            this.handlerOptions);
        }, 

We have initialized an OpenLayers.Handler.Click instance to execute the trigger listener function every time it detects that the user has pressed the mouse button.

Finally, as a good practice we set the CLASS_NAME attribute with a string identifying our new control class:

        CLASS_NAME: "OpenLayers.Control.Click"
    });

See also

  • The Playing with the map's options recipe in Chapter 1, Web Mapping Basics
  • The Creating features programmatically recipe in Chapter 3, Working with Vector Layers
..................Content has been hidden....................

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