OpenLayers Cookbook

4.5 (2 reviews total)
By Antonio Santiago Perez
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Web Mapping Basics

About this book

Data visualization and analysis has become an important task for many companies. Understanding the basic concepts of GIS and knowing how to visualize data on a map is a required ability for many professionals today. OpenLayers is a JavaScript library to load, display, and render maps from multiple sources on web pages.

"OpenLayers Cookbook" teaches how to work with OpenLayers, one of the most important and complete open source JavaScript libraries.
Through an extensive set of recipes, this book shows how to work with the main concepts required to build a GIS web application– maps, raster and vector layers, styling, theming, and so on.

"OpenLayers Cookbook" includes problem solving and how-to recipes for the most common and important tasks. A wide range of topics are covered.

The range of recipes includes: creating basic maps, working with raster and vector layers, understanding events and working with main controls, reading features from different data sources, styling features, and understanding the underlying architecture.

"OpenLayers Cookbook" describes solutions and optimizations to problems commonly found.

Publication date:
August 2012
Publisher
Packt
Pages
300
ISBN
9781849517843

 

Chapter 1. Web Mapping Basics

In this chapter we cover:

  • Creating a simple full screen map

  • Different ways to include OpenLayers

  • Understanding base and non-base layers

  • Avoiding the need of a base layer

  • Playing with the map's options

  • Managing map's stack layers

  • Managing map's controls

  • Moving around the map view

  • Restricting the map extent

 

Introduction


Every history has a beginning, in the same way every recipe starts with the initial condiments.

This chapter shows us the basics and more important things that we need to know when we start creating our first web mapping applications with OpenLayers.

As we will see in this chapter and the following chapters, OpenLayers is a big and complex framework but, at the same time it is also very powerful and flexible.

In contrast to other libraries, such as the nice but much more simple Leaflet project (http://leaflet.cloudmade.com) library, OpenLayers tries to implement all the required things a developer could need to create a web GIS application. That is, not only GIS related concepts such as map, layer, or standard formats but also manipulation of document elements or helper functions to make asynchronous requests.

Trivializing, we have described a big picture of the framework in the next paragraph.

The main concept in OpenLayers is the map. It represents the view where information is rendered. The map can contain any number of layers, which can be the raster or vector layer. On its way, each layer has a data source that serves data with its own format: a PNG image, a KML file, and so on. In addition, the map can contain controls, which help to interact with the map and its contents: pan, zoom, feature selection, and so on.

Let's get started with learning OpenLayers by examples.

 

Creating a simple full screen map


When you work in mapping applications, the first and important task is the creation of the map itself. The map is the core of your application and it is where you will add and visualize data.

This recipe will guide you through the process of creating our first and very simple web map application.

Note

It is supposed that a web server is configured and ready. Remember our recipes are nothing more than HTML, CSS, and JavaScript code and because of this we need a web server that serves them to be interpreted on the browser's side.

Getting ready

Programming with OpenLayers is mainly related to writing HTML code and, of course, JavaScript code. So, we simply need a text editor to start coding our recipes.

There exist plenty of great text editors, many of them with web programming capabilities. Because we are going to start exploring an open source project such as OpenLayers we will refer to a couple of great open projects.

For Windows users, Notepad++ (http://notepad-plus-plus.org) is a great alternative to the default text editor. It is simple and quick, offers syntax highlighting, and addition of plugins to extend capabilities.

On the other hand, instead of text editors you can find complete development frameworks with support for web development, not only with syntax highlighting but with autocomplete, code navigation, and many more.

In this group, two projects are the stars within the open source projects universe: Eclipse (http://www.eclipse.org) and NetBeans (http://netbeans.org). Both are Java-based projects and run on any platform.

You can find the source code at recipe/ch01/ch01_simple_map_book.html file.

How to do it...

  1. Let's start by creating a new empty index.html file and inserting the following block of code in it. We will explain it step-by-step in the next section:

    <!DOCTYPE html> 
    <html> 
        <head> 
            <title>Creating a simple map</title> 
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
            
            <!-- Include OpenLayers library --> 
            <script type="text/javascript" src="http://openlayers.org/api/2.11/OpenLayers.js"></script> 
            <style> 
                html, body { 
                    width: 100%; 
                    height: 100%; 
                    margin: 0; 
                    padding: 0; 
                } 
            </style> 
    
            <!-- The magic comes here --> 
            <script type="text/javascript"> 
                function init() { 
                    // Create the map using the specified // DOM element 
                    var map = new OpenLayers.Map("rcp1_map"); 
        
                    // Create an OpenStreeMap raster layer // and add to the map 
                    var osm = new OpenLayers.Layer.OSM(); 
                    map.addLayer(osm); 
        
                    // Set view to zoom maximum map extent 
                    map.zoomToMaxExtent(); 
                } 
            </script> 
        </head> 
        <body onload="init()"> 
            <div id="rcp1_map" style="width: 100%; height: 100%;"></div> 
        </body> 
    </html>

    Tip

    Downloading the example code

    You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

  2. Open the file in your browser and see the result. You will see a whole screen map with some controls on the top-left corner, as shown in the following screenshot:

How it works...

Let us explain the mystery step-by-step. First, we created a HTML5 document, see the doctype declaration code <!DOCTYPE html>.

In the head section, we have included a reference to the OpenLayers library using a script element, as follows:

<script type="text/javascript" src="http://openlayers.org/api/2.11/OpenLayers.js"></script>

We have added a style element to force the document to occupy the whole page, as follows:

<style> 
    html, body { 
        width: 100%; 
        height: 100%; 
        margin: 0; 
        padding: 0; 
    } 
</style>

After the style element comes the script element with some JavaScript code, but we will explain it at the end.

After the head section starts the body section. Our body has an onload event associated to it. This means, once the whole content of the body section is completely loaded by the browser, the init() function will be called:

<body onload="init()">

Finally, within the body we have put a div element with the identifier rcp1_map, which will be used by OpenLayers to render the map.

Again, we force the element to fill the entire parent's space:

<div id="rcp1_map" style="width: 100%; height: 100%;"></div>

Tip

A word about styles...

Setting the div element width/height to 100% means it will fill 100 percent of the parent's space. In this case, the parent is the body element, which is also set to fill 100 percent of the page space. More and better information about CSS can be found at http://www.w3schools.com/css.

Now, let's take a look at the script element in the head section.

As we have mentioned previously, using the onload event we ensure the init function is executed once the entire body elements are loaded by the browser, which means we can access <div id="rcp1_map" ...> without any problem.

First we created an OpenLayers.Map object that will be rendered in the previously mentioned div element. This is achieved by passing the DOM element identifier in the constructor:

// Create the map using the specified DOM element 
var map = new OpenLayers.Map("rcp1_map"); 

Next, we created a new raster layer that will show imagery from the OpenStreetMaps project:

// Create an OpenStreetMap raster layer and add to the map 
var osm = new OpenLayers.Layer.OSM(); 

Once created we add it to the map:

map.addLayer(osm); 

Finally, set the map view to the maximum valid extent:

// Set view to zoom maximum map extent 
map.zoomToMaxExtent(); 

There's more...

Remember there is no one way to do things.

The recipes in this book have not been coded as standalone applications. Instead, to improve the user experience, we have created a rich application that allows you to choose and run the desired recipe, with the possibility to see the source code.

So the way to code the recipes in the book is slightly different, because they must be integrated with the application's design. For example, they do not require including the OpenLayers libraries because this is included in another place of the main application.

In addition, the way presented in the How to do it... section is more oriented toward standalone applications.

If you are looking at the source code of this recipe, located at recipes/ch01/ch01_simple_map.html, we will see a slightly different code:

<!-- Map DOM element -->
<div id="ch1_simple_map" style="width: 100%; height: 95%;"></div>

<!-- The magic comes here -->
<script type="text/javascript">

    // Create the map using the specified DOM element
    var map = new OpenLayers.Map("ch1_simple_map");
    
    // Create an OpenStreeMap raster layer and add to the map
    var osm = new OpenLayers.Layer.OSM();
    map.addLayer(osm);
    
    // Set view to zoom maximum map extent
    map.zoomToMaxExtent();
</script>

As we can see, it contains the main parts described in the previous sections. We have a div element to hold the map instance and a script element with all the required JavaScript code.

To create the rich application, we have to use the Dojo Toolkit framework (http://dojotoolkit.org), which offers almost any kind of required feature: access and modification of the document object structure, event handling, internationalization, and so on. But the main reason we have chosen it is because, in addition it offers a great set of homogeneous widgets (tabs, buttons, lists, and so on) to create applications with a great look and feel.

It is beyond the scope of this book to teach Dojo but its use is so easy and specific that it will not disturb the objective of this recipe, which is to teach OpenLayers.

See also

  • The Different ways to include OpenLayers recipe

  • The Understanding base and non-base layers recipe

 

Different ways to include OpenLayers


There are different ways we can include OpenLayers in our projects depending on the environment we are working in, that is development or production.

The environment refers to the server tier required for a stage in our process. In this way, the development environment is related to the development process, where programmers are working day to day, testing and checking.

Production environment refers to the final stage of the projects. It must run on stable servers and without dependency problems.

As we will see shortly, we can summarize the solutions to include OpenLayers JavaScript code in two groups, those with code hosted on a remote server or those with code hosted on our own server.

Let's start and see the pros and cons of each solution.

Getting ready

Create a folder called myProject that will contain all our project files and library dependencies. Then create an index.html file and continue with the code given in the Creating a simple full screen map recipe.

Note

It is supposed that the project folder resides within a folder accessible by the web server folder, so it can serve its content.

Now download OpenLayers code from the project's web page at http://www.openlayers.org.

Note

At the time of writing this book, OpenLayers version 2.11 is the stable release, which can be found at http://openlayers.org/download/OpenLayers-2.11.tar.gz.

Save the bundle in the myProject folder and uncompress it. We need to have a folder structure similar to the following screenshot:

How to do it...

We have three ways to include the OpenLayers library in our code:

  • <script type="text/javascript" src=" http://openlayers.org/api/2.11/OpenLayers.js "></script>

  • <sc ript type="text/javascript" src="../js/OpenLayers-2.11/OpenLayers.js"></script>

  • <sc ript type="text/javascript" src="../js/OpenLayers-2.11/lib/OpenLayers.js"></script>

How it works...

The first option includes an all-in-one compressed file hosted at the OpenLayers project server. It is simple to use but you cannot work locally in offline mode:

<script type="text/javascript" src="http://openlayers.org/api/2.11/OpenLayers.js"></script>

Note

The size of the compressed all-in-one file OpenLayers.js is nearly 1 MB, so in a production environment with lots of requests it is probably better to host this file in a Content Delivery Network or CDN (http://en.wikipedia.org/wiki/Content_delivery_network).

The second option is very similar to the first one, but the all-in-one compressed file is attached to the project. This option is suitable for cases in which you need OpenLayers to be in your own server with the code of your application.

<script type="text/javascript" src="../js/OpenLayers-2.11/OpenLayers.js"></script> 

Finally, the third option includes the uncompressed code of the OpenLayers library, which in fact includes many other files required by layers, controls, and so on.

<script type="text/javascript" src="../js/OpenLayers-2.11/lib/OpenLayers.js"></script> 

This option is mainly used by programmers who want to extend OpenLayers and need to debug the code.

Tip

I encourage you to work in this mode. Use some tool such as Firebug for Firefox web browser or the Chrome browser console and put breakpoints on OpenLayers classes to better understand what is happening.

It is worth saying when using this method lots of files are loaded from the server, one per class, which means many more server requests are made.

The most notable impact of this is that the page load time is much longer than with the previous options.

There's more...

If you choose to download OpenLayers and include it within your project, you don't need to put the whole uncompressed bundle. As you can see, it contains lots of files and folders: source code, building scripts, test code, and other tools, but only a few are really required.

In this case, the only things you need to attach are:

  • The all-in-one OpenLayers.js file

  • The theme and img folders

See also

  • The Understanding base and non-base layers recipe

  • The Creating a simple full screen map recipe

 

Understanding base and non-base layers


One of the first things you need to have clear when working with OpenLayers is the base layer concept.

A base layer is a special kind of layer, which is always visible and determines some map properties such as projection and zoom levels.

A map can have more than one base layer but only one of them can be active at a time.

In addition, if you add more than one flagged base layer to the map, the first base layer added will be used as the map's active base layer.

This recipe will show you how to add layers to the map flagging them to be base layers. We are going to create a page with two maps side-by-side and every map will have a layer switcher control that allows you to control the map layers.

Getting ready

We assume you have created an index.html file and included the OpenLayers library as we have seen in the Different ways to include OpenLayers recipe.

How to do it...

  1. Start by creating the necessary HTML code to have both our maps side-by-side:

    <table style="width: 100%; height: 100%;"> 
        <tr> 
            <td> 
                <p>Map with one non base layer:</p> 
                <div id="ch01_base_nonbase_map_a" style="width: 100%; height: 500px;"></div> 
            </td> 
            <td> 
                <p>Map with two base layers</p> 
                <div id="ch01_base_nonbase_map_b" style="width: 100%; height: 500px;"></div> 
            </td> 
        </tr> 
    </table>
  2. After this, add a script element (<script type="text/javascript"></script>) with the necessary code to initialize every map. The map on the left will contain two layers, one base layer and one non-base layer:

    // 
    // Initialize left map 
    // 
        
    // Create the map using the specified DOM element 
    var map_a = new OpenLayers.Map("ch01_base_nonbase_map_a"); 
    // Add a WMS layer 
    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0", 
    { 
        layers: 'basic' 
    }); 
    map_a.addLayer(wms); 
    // Add a WMS layer 
    var topo = new OpenLayers.Layer.WMS("USA Topo Maps", "http://terraservice.net/ogcmap.ashx", 
    { 
        layers: "DRG" 
    }, 
    { 
        opacity: 0.5, 
        isBaseLayer: false 
    }); 
    map_a.addLayer(topo); 
    // Add LayerSwitcher control 
    map_a.addControl(new OpenLayers.Control.LayerSwitcher()); 
               
    // Set view to zoom maximum map extent 
    // NOTE: This will fail if there is no base layer defined 
    map_a.setCenter(new OpenLayers.LonLat(-100, 40), 5); 
    
  3. The map on the right will contain two base layers:

    // 
    // Initialize right map 
    // 
        
    // Create the map using the specified DOM element 
    var map_b = new OpenLayers.Map("ch01_base_nonbase_map_b"); 
    // Add a WMS layer 
    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0", 
    { 
        layers: 'basic' 
    }); 
    map_b.addLayer(wms); 
    // Add a WMS layer 
    var topo = new OpenLayers.Layer.WMS("USA Topo Maps", "http://terraservice.net/ogcmap.ashx", 
    { 
        layers: "DRG" 
    }); 
    map_b.addLayer(topo); 
    // Add LayerSwitcher control 
    map_b.addControl(new OpenLayers.Control.LayerSwitcher()); 
               
    // Set view to zoom maximum map extent 
    // NOTE: This will fail if there is no base layer defined 
    map_b.setCenter(new OpenLayers.LonLat(-100, 40), 5);

How it works...

Let's take a look at the explanation for the map on the left. The first thing we have done is the creation of an OpenLayers.Map instance that will be rendered in the div element prepared for it, on the left side:

var map_a = new OpenLayers.Map("ch01_base_nonbase_map_a"); 

Next, we have created two layers and added them to the map. The magic to make the second layer a non-base layer comes with the properties specified in the constructor:

var topo = new OpenLayers.Layer.WMS("USA Topo Maps", "http://terraservice.net/ogcmap.ashx", 
{ 
    layers: "DRG" 
}, 
{ 
    opacity: 0.5, 
    isBaseLayer: false 
}); 

In OpenLayers, all layer classes are inherited from the base class OpenLayers.Layer. This class defines some properties common for all layers, such as opacity or isBaseLayer.

The Boolean isBaseLayer property is used by the map to know if a layer must act as a base or non-base layer.

Note

Non-base layers are also called overlays.

As you can imagine, the opacity property is a float value ranging from 0.0 to 1.0 and specifies the opacity of the layer. We have set it to 50% of the opacity to allow view through the overlay layer, that is, to be able to see the base layer.

For the right-hand map, we have added two layers without any specific property. This, by default, makes the WMS layer a base layer.

If you expand the layer switcher control, you will see that on the left map you can show/hide the overlay layer but you can't hide the base layer. In contrast, in the right map, both are base layers and they are mutually exclusive, which means only one can be active at a time.

There's more...

When you play with the layer switcher control, an internal call is made to the map's setBaseLayer(newBaseLayer) method. This method is responsible for changing the active base layer used by the map.

In addition to the specify properties at construction time, you can also use the setter methods setOpacity(opacity) and setIsBaseLayer(isBaseLayer) to change the values at runtime.

See also

  • The Avoiding the need of a base layer recipe

  • The Managing map's stack layers recipe

 

Avoiding the need of a base layer


There can be situations where you don't want a base layer and only want a bunch of layers to work on.

Imagine an online GIS editor where users can add and remove layers but they are not obligated to have an always visible one.

This recipe shows how we can easily avoid the requirement of setting a base layer within the map.

How to do it...

  1. As always, create a DOM element to render the map:

    <div id="ch1_avoid_baselayer" style="width: 100%; height: 100%;"></div>
  2. Now create a new OpenLayers.Map instance and set the allOverlays property to true:

    // Create the map using the specified DOM element 
    var map = new OpenLayers.Map("ch1_avoid_baselayer", { 
        allOverlays: true 
    });
  3. Add two layers to see a result. Also add the layer switcher control:

    // Add a WMS layer 
    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic","http://vmap0.tiles.osgeo.org/wms/vmap0", 
    { 
        layers: 'basic' 
    }); 
    map.addLayer(wms); 
    // Add a WMS layer 
    var topo = new OpenLayers.Layer.WMS("USA Topo Maps", "http://terraservice.net/ogcmap.ashx", 
    { 
        layers: "DRG" 
    },
    { 
        opacity: 0.5 
    }); 
    map.addLayer(topo); 
    // Add LayerSwitcher control 
    map.addControl(new OpenLayers.Control.LayerSwitcher()); 
  4. Center the map view to some nice place:

    // Set view to zoom maximum map extent 
    // NOTE: This will fail if there is no base layer defined 
    map.setCenter(new OpenLayers.LonLat(-100, 40), 5);

How it works...

When the map's property allOverlays is set to true, the map ignores the isBaseLayer property of the layers.

If you expand the layer switcher control, you will see that it contains two overlay layers, no base layer, which you can show or hide and, if desired, leave a blank map without layers.

In addition, in this recipe we have used the map.setCenter() method, which needs a position, an OpenLayers.LonLat instance, and a zoom level to work.

There's more...

When working in the allOverlays mode, the lowest layer will act as base layer, although all the layers will be flagged as isBaseLayer is set to false.

See also

  • The Understanding base and non-base layers recipe

  • The Moving around the map view recipe

  • The Restricting the map extent recipe

 

Playing with the map's options


When you create a map to visualize data, there are some important things you need to take into account: projection to use, available zoom levels, the default tile size to be used by the layer requests, and so on.

Most of these important things are enclosed in the so-called map properties and, if you work in the allOverlays mode, you need to take them specially into account.

This recipe shows you how to set some of the most common map properties.

Getting ready

Before you continue, it is important to note that instances of the OpenLayers.Map class can be created in three ways:

  • Indicating the identifier of the DOM element where the map will be rendered:

    var map = new OpenLayers.Map("map_id");
  • Indicating the identifier of the DOM element and also indicating a set of options:

    var map = new OpenLayers.Map("map_id", {some_options_here});
  • Only indicating a set of options. This way we can later set the DOM element where the map will be rendered:

    var map = new OpenLayers.Map({some_options_here});

How to do it...

Perform the following steps:

  1. Create a DOM element to render the map:

    <div id="ch1_map_options" style="width: 100%; height: 100%;"></div>
  2. Define some map options:

    var options = { 
        div: "ch1_map_options", 
        projection: "EPSG:4326", 
        units: "dd", 
        displayProjection: new OpenLayers.Projection("EPSG:900913"),
        numZoomLevels: 7
    };
  3. Create the map by passing options:

    var map = new OpenLayers.Map(options);
  4. Add the MousePosition control to see the mouse position over the map:

    map.addControl(new OpenLayers.Control.MousePosition());
  5. Add a WMS layer and set the map view on some desired place:

    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0", 
    { 
        layers: 'basic' 
    }); 
    map.addLayer(wms); 
    map.setCenter(new OpenLayers.LonLat(-100, 40), 5);

How it works...

In this case we have used five map options to initialize our OpenLayers.Map instance.

We have used the div option to pass the identifier of the DOM element where the map will be rendered: div: "ch1_map_options".

Note

The OpenLayers.Map class uses some default values for most of its options: projection="EPSG:4326", units="degrees", and so on.

The projection option is used to set the projection used by the map to render data from layers: projection: "EPSG:4326". Take into account it must be a string with the projection code. On other classes or options it can also be an OpenLayers.Projection instance.

There are some implications with the map's projection. Firstly, the tiles to fill WMS layers will be requested using the map's projection, if no other projection is explicitly used by the layer. So you need to be sure the WMS server accepts the projection. Secondly, data from vector layers will be translated from the specific projection of every vector layer to the map's projection, so you will need to set the vector layer's projection options while creating them.

Note

For translations other than EPSG:4326 and EPSG:900913, you need to include the Proj4js project (http://proj4js.org) in your web application.

Teaching map projections is beyond the scope of this book. A great description can be found on Wikipedia (http://en.wikipedia.org/wiki/Map_projection).

EPSG codes are a way to name and classify the set of available projections. The site Spatial Reference (http://spatialreference.org) is a great place to find more information about them.

The units option specifies that the units used by the map are decimal degrees: units: "dd". This option is related to some resolution options.

The displayProjection option allows us to specify the projection that must be used to show the mouse position: displayProjection: new OpenLayers.Projection("EPSG:900913"). In this case, our map is in the EPSG:4326 projection, also known as WGS84, with degree units but we show mouse position in EPSG:900913, also known as Spherical Mercator, which is in meter unit coordinates.

Finally, the numZoomLevels sets the number of available zoom levels the user can change. A value of 7 means the user can go from zoom level 0 to zoom level 6.

There's more...

Imagery from sources such as Google Maps or OpenStreetMap are special cases where the pyramid of images is previously created with the Spherical Mercator projection – EPSG:900913. This means you can't set the projection when requesting tiles because it is implicit.

If you put a layer in a different projection other than the one used by the map, it will be automatically disabled.

See also

  • The Understanding base and non-base layers, recipe

  • The Managing map's stack layers, recipe

  • The Managing map's controls, recipe

  • The Working with projections, recipe in Chapter 8, Beyond the Basics.

 

Managing map's stack layers


Map is the core concept in OpenLayers. It allows us to visualize information from different kinds of layers and brings us methods to manage layers attached to it.

In this recipe, we will learn how to control layers. This is important because add, remove, or reorder layers are very common operations we need to do on almost every web mapping application.

The application will show a map on the left and a control panel on the right, with some buttons to control the layers.

Note

Remember we have used the Dojo toolkit framework (http://dojotoolkit.org) to code a nicer and richer application to show the recipes of this book.

Because of this, you can see strange attributes in the HTML elements such as dojoType="dijit.form.Button" or onClick="topLayer". Do not worry about it, there is no impact in the OpenLayers code we are covering in this book.

How to do it...

  1. Start by creating an index.html file to put the code needed to create the application layout. We place it within a table. On the left we put the map:

    <table class="tm"> 
        <tr> 
            <td class="left"> 
                <div id="ch1_managing_layers" style="width: 100%; height: 500px;"></div> 
            </td> 
  2. And, on the right we put the controls:

            <td class="right"> 
                <p>Maximize the layer switcher control to see the map layers and move it clicking the buttons:</p> 
    
                <table class="tb"> 
                    <tr> 
                        <td>Select layer:</td> 
                        <td> 
                            <select id="layerSelection" data-dojo-type="dijit.form.Select"> 
                                <option value="JPL">JPL</option> 
                                <option value="WorldMap">WorldMap</option> 
                                <option value="Canada">Canada</option> 
                            </select> 
                        </td> 
                    </tr> 
                    <tr> 
                        <td>Move to top:</td> 
                        <td><button dojoType="dijit.form.Button" onClick="topLayer">Top</button></td> 
                    </tr> 
                    <tr> 
                        <td>Move up:</td> 
                        <td><button dojoType="dijit.form.Button" onClick="raiseLayer">Up</button></td> 
                    </tr> 
                    <tr> 
                        <td>Move down:</td> 
                        <td><button dojoType="dijit.form.Button" onClick="lowerLayer">Down</button></td> 
                    </tr> 
                    <tr> 
                        <td>Move to bottom:</td> 
                        <td><button dojoType="dijit.form.Button" onClick="bottomLayer">Bottom</button></td> 
                    </tr> 
                </table> 
    
            </td> 
        </tr> 
    </table>
  3. Create an OpenLayers.Map instance working in the allOverlays mode:

    var map = new OpenLayers.Map("ch1_managing_layers", {
        allOverlays: true
    });
  4. Add some layers to the map:

    var jpl = new OpenLayers.Layer.WMS("JPL", 
        [
        "http://t1.hypercube.telascience.org/tiles?",
        "http://t2.hypercube.telascience.org/tiles?",
        "http://t3.hypercube.telascience.org/tiles?",
        "http://t4.hypercube.telascience.org/tiles?"
        ], 
        {
            layers: 'landsat7'
        });
    var worldmap = new OpenLayers.Layer.WMS("WorldMap", "http://vmap0.tiles.osgeo.org/wms/vmap0", 
    {
        layers: 'basic', 
        format: 'image/png' 
    },
    {
        opacity: 0.5
    });
    var canada = new OpenLayers.Layer.WMS("Canada", "http://www2.dmsolutions.ca/cgi-bin/mswms_gmap",
    {
        layers: "bathymetry,land_fn,park",
        transparent: "true", 
        format: "image/png" 
    },
    {
        opacity: 0.5
    });
    map.addLayers([jpl, worldmap, canada]);
  5. Add a layers switcher control (to show the layers) and center the map view:

    map.addControl(new OpenLayers.Control.LayerSwitcher({
        ascending: false
    }));
    map.setCenter(new OpenLayers.LonLat(-100, 40), 4);
  6. Finally, add the JavaScript code that will react when the previous four buttons were clicked:

    function raiseLayer() {
        var layerName = dijit.byId('layerSelection').get('value');
        var layer = map.getLayersByName(layerName)[0];
        map.raiseLayer(layer, 1);
    }
    function lowerLayer() {
        var layerName = dijit.byId('layerSelection').get('value');
        var layer = map.getLayersByName(layerName)[0];
        map.raiseLayer(layer, -1);
    }
    function topLayer() {
        var layerName = dijit.byId('layerSelection').get('value');
        var layer = map.getLayersByName(layerName)[0];
        var lastIndex = map.getNumLayers()-1;
        map.setLayerIndex(layer, lastIndex);
    }
    function bottomLayer() {
        var layerName = dijit.byId('layerSelection').get('value');
        var layer = map.getLayersByName(layerName)[0];
        map.setLayerIndex(layer, 0);
    }

How it works...

There is not much to say about the HTML code for the layout. We have used a table to put the map on the left and the set of buttons on the right. In addition, we have associated actions to the buttons that will be executed when they are clicked.

With respect to the OpenLayers code, we have created the map instance working in the allOverlays mode. This will let us move any layer without being worried about a base layer:

var map = new OpenLayers.Map("ch1_managing_layers", {
    allOverlays: true
});

Later, we created three WMS layers and added them to the map. For some of them we have set the opacity property to 50% to see through them:

map.addLayers([jpl, worldmap, canada]);

It is very important to note that we have used the same name for the option's value attribute in the HTML select element as we have used for the layer. Later, this will let us select a map's layer by its name.

Next, we have added an OpenLayers.Control.LayerSwitcher control by setting its ascending property to false:

map.addControl(new OpenLayers.Control.LayerSwitcher({
    ascending: false
}));

You can think of the map as storing layers in a stack and they are rendered from bottom to top, so the above layers can hide beneath the below layers depending on its opacity and extent.

Tip

By default the ascending property is true, and the layer switcher control shows the layers of the map in the reverse order, that is, the bottom layer is drawn first in the control and the top layer is drawn last. You can avoid this by setting ascending to false.

Finally, the only thing we need to take a look at is the code responsible for button actions, which is the most interesting code in this recipe.

Let's take a look to the raiseLayer() action (which is very similar to lowerLayer() action):

function raiseLayer() {
    var layerName = dijit.byId('layerSelection').get('value');
    var layer = map.getLayersByName(layerName)[0];
    map.raiseLayer(layer, 1);
}

First, we get the name of the currently selected layer in the select element (don't worry if you don't understand that line completely, it is more related to the Dojo framework than to OpenLayers).

Then, we use the map.getLayersByName() method, which returns an array with all the layers that have the specified name. Because of this, we get the first element of the array.

Now we have a reference to the layer instance. We can raise it in the map using the map.raiseLayer() method. You can raise it by one or more positions indicating a delta number or, like in the lowerLayer() function, you can lower it by one or more positions indicating a negative value.

Internally OpenLayers.Map stores layers in an array (the layers attribute) and they are rendered in the order they are stored in the array (so the first element is the bottom layer).

The topLayer() and bottomLayer() actions are similar too, they move the specified layer to the top or bottom of the stack. They both work using the map.setLayerIndex() method, which is responsible to move a layer to a specified position.

Note

The method map.setLayerIndex() is used internally by map.raiseLayer() to move layers.

Because the bottom layer corresponds to the first layer in the array of layers, the bottomLayer() action is the easiest to implement because we simply need to move the layer to the first position:

function bottomLayer() {
    var layerName = dijit.byId('layerSelection').get('value');
    var layer = map.getLayersByName(layerName)[0];
    map.setLayerIndex(layer, 0);
}

For the topLayer() actions, we need to move the layer to the last position. To do this, we can get help from the map.getNumLayers() method, which returns the total number of layers in the map. In this way, if we have four layers in the map, the last corresponds to the index 3 (because the index value changes from 0 to 3).

function topLayer() {
    var layerName = dijit.byId('layerSelection').get('value');
    var layer = map.getLayersByName(layerName)[0];
    var lastIndex = map.getNumLayers()-1;
    map.setLayerIndex(layer, lastIndex);
}

There's more...

The OpenLayers.Map class has plenty of methods to manipulate the contained layers. We have seen a few in these recipes, to add, get layers by name, move up or down in the stack, and so on. But you can find more methods to remove layers, get layers by their position, and so on.

See also

  • The Managing map's controls recipe

  • The Moving around the map view recipe

  • The Restricting the map extent recipe

 

Managing map's controls


OpenLayers comes with lots of controls to interact with the map: pan, zoom, show overview map, edit features, and so on.

In the same way as layers, the OpenLayers.Map class has methods to manage the controls attached to the map.

How to do it...

Follow the given steps:

  1. Create a new HTML file and add the OpenLayers dependencies.

  2. Now, add the required code to create the buttons and div element to hold the map instance:

    <div class="sample_menu" dojoType="dijit.MenuBar">
        <span class="title">Controls: </span>
        <div dojoType="dijit.form.ToggleButton" iconClass="dijitCheckBoxIcon" onChange="updateMousePosition">MousePosition</div>
        <div dojoType="dijit.form.ToggleButton" iconClass="dijitCheckBoxIcon" onChange="updatePanPanel">PanPanel</div>
        <div dojoType="dijit.form.ToggleButton" iconClass="dijitCheckBoxIcon" onChange="updateZoomPanel">ZoomPanel</div>
    </div>
    <!-- Map DOM element -->
    <div id="ch1_managing_controls" style="width: 100%; height: 500px;"></div>
  3. Within the script element section, create the map instance:

    var map = new OpenLayers.Map("ch1_managing_controls", {
        controls: [
        new OpenLayers.Control.Navigation()
        ]
    });
  4. Add some layers to the map and center the view:

    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0",
    {
        layers: 'basic'
    },
    {
        wrapDateLine: false
    });
    map.addLayer(wms);
    // Center the view
    map.setCenter(OpenLayers.LonLat.fromString("0,0"),3);
  5. Finally, add the actions code associated to the buttons:

    function updateMousePosition(checked) {
        if(checked) {
            map.addControl(new OpenLayers.Control.MousePosition());
        } else {
            var controls = map.getControlsByClass("OpenLayers.Control.MousePosition");
            console.log(controls);
            map.removeControl(controls[0]);
        }
    }
    function updatePanPanel(checked) {
        if(checked) {
            map.addControl(new OpenLayers.Control.PanPanel());
        } else {
            var controls = map.getControlsByClass("OpenLayers.Control.PanPanel");
            map.removeControl(controls[0]);
        }
    }
    function updateZoomPanel(checked) {
        if(checked) {
            // Place Zoom control at specified pixel
            map.addControl(new OpenLayers.Control.ZoomPanel(), new OpenLayers.Pixel(50,10));
        } else {
            var controls = map.getControlsByClass("OpenLayers.Control.ZoomPanel");
            map.removeControl(controls[0]);
        }
    }

How it works...

Every button action function checks if the toggle button is checked or unchecked and depending on the value we add or remove the control to the map:

if(checked) {
    // Place Zoom control at specified pixel
    map.addControl(new OpenLayers.Control.ZoomPanel(), new OpenLayers.Pixel(50,10));
} else {
    var controls = map.getControlsByClass("OpenLayers.Control.ZoomPanel");
    map.removeControl(controls[0]);
}

Adding a control is fairly simple through the map.addControl() method, which, given a control instance—and, optionally a OpenLayers.Pixel instance—adds the control to the map at the specified position.

Note

Usually, a control position is controlled by modifying the top and left values in the CSS class used by the control. If you use a OpenLayers.Pixel value to position the control, then that value will overwrite the CSS ones.

To remove a control we need to have a reference to the instance that has to be removed. The method map.getControlsByClass() returns an array of controls of the specified class and helps us to get a reference to the desired control. Next, we can remove it with map.removeControl().

There's more...

Note, in this recipe we have centered the map's view passing a OpenLayers.LonLat instance created in a different way. Instead of using the new operator, we have used the method OpenLayers.LonLat.fromString, which created a new instance from a string:

map.setCenter(OpenLayers.LonLat.fromString("0,0"),3);

In addition, the map instance created in this recipe has initialized with only one control, OpenLayers.Control.Navigation(), which allows us to navigate the map using the mouse:

var map = new OpenLayers.Map("ch1_managing_controls", {
    controls: [
    new OpenLayers.Control.Navigation()
    ]
});

Note

Passing an empty array to the controls property creates a map instance without any control associated with it. In addition, without specifying the controls property, OpenLayers creates a set of default controls for the map, which includes the OpenLayers.Control.Navigation and OpenLayers.Control.PanZoom controls.

See also

  • The Managing map's stack layers recipe

  • The Moving around the map view recipe

 

Moving around the map view


Unless you want to create a completely static map, without the controls required for the user to pan or zoom, you would like the user to be able to navigate and explore the map.

There can be situations when the controls are not enough. Imagine a web application where the user can make a search, such as Everest, and the application must find its location and fly to it. In this case, you need to navigate by code and not by using a control.

This recipe shows you some of the OpenLayers.Map class methods that will allow you to improve the user's experience.

The application layout contains three main sections. At the top there is a label to show the current map center position and zoom level. It is automatically updated when the map is moved or the zoom is changed.

The map is in the center and there are a bunch of controls on the right to set and test the main map methods to interact with the view.

As you will see, the map has no control attached to it, so the only way to interact with it is through the right controls.

Note

We omit the HTML code necessary to create the application layout, so if you are interested in the HTML code you can take a look at the source code available on the Packt Publishing website.

How to do it...

  1. Create an HTML file with OpenLayers dependencies.

    Note

    The HTML code to create the buttons and layout of the previous screenshot is extensive and not related to the goal of the book, so here we avoid writing it.

  2. Add a div element to hold the map instance:

    <div id="ch1_moving_around" style="width: 100%; height: 500px;"></div>
  3. Create a map instance. This time we specify a listener for some events that will update the center and zoom values of the label on top of the map:

    var map = new OpenLayers.Map("ch1_moving_around", {
        controls: [],
        eventListeners: {
            "move": changeListener,
            "moveend": changeListener,
            "zoomend": changeListener
        }
    });
    function changeListener() {
        var center = map.getCenter();
        document.getElementById("center").innerHTML = "("+center.lon + " lon , " + center.lat + " lat)";
        var zoom = map.getZoom();
        document.getElementById("zoom").innerHTML = zoom + " level";
    }
  4. Add one layer and center the view:

    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0",
    {
        layers: 'basic'
    });
    map.addLayer(wms);
    map.setCenter(new OpenLayers.LonLat(0, 0), 2);
  5. Insert the code that will be executed by the button actions:

    function moveByPx() {
        var x = dijit.byId('movebyxpix').get('value');
        var y = dijit.byId('movebyypix').get('value');
            
        map.moveByPx(x,y);
    }
    function moveTo() {
        var lon = dijit.byId('movetolon').get('value');
        var lat = dijit.byId('movetolat').get('value');
        var zoom = dijit.byId('movetozoom').get('value');
        var force = dijit.byId('movetoforceZoomChange').get('checked');
        var drag = dijit.byId('movetodragging').get('checked');
            
        map.moveTo(new OpenLayers.LonLat(lon, lat), zoom, {
            forceZoomChange: force,
            dragging: drag
        });
    }
    function setCenter() {
        var lon = dijit.byId('setCenterlon').get('value');
        var lat = dijit.byId('setCenterlat').get('value');
        var zoom = dijit.byId('setCenterzoom').get('value');
        var force = dijit.byId('setCenterforceZoomChange').get('checked');
        var drag = dijit.byId('setCenterdragging').get('checked');
            
        map.setCenter(new OpenLayers.LonLat(lon, lat), zoom, {
            forceZoomChange: force,
            dragging: drag
        });
    }
    function pan() {
        var x = dijit.byId('panxpix').get('value');
        var y = dijit.byId('panypix').get('value');
        var anim = dijit.byId('pananimate').get('checked');
        var drag = dijit.byId('pandragging').get('checked');
            
        map.pan(x,y, {
            animate: anim,
            dragging: drag
        });
    }
    function panTo() {
        var lon = dijit.byId('panTolon').get('value');
        var lat = dijit.byId('panTolat').get('value');
            
        map.panTo(new OpenLayers.LonLat(lon, lat));
    }

How it works...

To update the center and zoom level values at the top, we have instantiated the Map object with some event listeners attached to it. Actually, the same listener function is attached to all three events:

var map = new OpenLayers.Map("ch1_moving_around", {
    controls: [],
    eventListeners: {
        "move": changeListener,
        "moveend": changeListener,
        "zoomend": changeListener
    }
});

Within the changeListener() function we use map.getCenter(), which returns an OpenLayers.LonLat object, and map.getZoom() to get the current values and update the top-left label.

function changeListener() {
    var center = map.getCenter();
    document.getElementById("center").innerHTML = "("+center.lon + " lon , " + center.lat + " lat)";
    var zoom = map.getZoom();
    document.getElementById("zoom").innerHTML = zoom + " level";
}

For every button, an action is executed. They are responsible to get the required values and invoke a map method.

The map.moveByPx() method moves the map by a delta value specified in pixels. Note, it moves the map; it doesn't pan, so don't expect any effect.

function moveByPx() {
    var x = dijit.byId('movebyxpix').get('value');
    var y = dijit.byId('movebyypix').get('value');
        
    map.moveByPx(x,y);
}

The map.moveTo() method is similar to the previous one but moves the view to a specified position (instead of an increment) and is specified with an OpenLayers.LonLat instance.

The map.setCenter() method is similar to map.moveTo() but it centers the view on the specified location.

Finally, there are two pan-related actions, which make nice smooth movements. The map.pan() method moves the view with a pan effect specified by a pixel delta. The map.panTo() method does something similar, it moves the view to a specified location.

See also

  • The Managing map's stack layers recipe

  • The Restricting the map extent recipe

 

Restricting the map extent


Often, there are situations where you are interested to show data to the user but only for a specific area, which your available data corresponds to (a country, a region, a city, and so on).

In this case, there is no point in allowing the user to explore the whole world, so you need to limit the extent the user can navigate.

In this recipe, we present some ways to limit the area a user can explore.

How to do it...

  1. Create a map instance. Take a look at the couple of properties used in the constructor:

    var map = new OpenLayers.Map("ch1_restricting_view", {
        maxExtent: OpenLayers.Bounds.fromString("-180,-90,180,90"),
        restrictedExtent: OpenLayers.Bounds.fromString("-180,-90,180,90")
    });
    
  2. As always, add some layer to see content and center the view:

    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0",
    {
        layers: 'basic'
    });
    map.addLayer(wms);
    map.setCenter(new OpenLayers.LonLat(0, 0), 2);
  3. Add the functions that will be executed when buttons are clicked:

    function updateMaxExtent() {
        var left = dijit.byId('left_me').get('value');
        var bottom = dijit.byId('bottom_me').get('value');
        var right = dijit.byId('rigth_me').get('value');
        var top = dijit.byId('top_me').get('value');      
        map.setOptions({
            maxExtent: new OpenLayers.Bounds(left, bottom, right, top)
        });
    }
    function updateRestrictedExtent() {
        var left = dijit.byId('left_re').get('value');
        var bottom = dijit.byId('bottom_re').get('value');
        var right = dijit.byId('rigth_re').get('value');
        var top = dijit.byId('top_re').get('value');
        map.setOptions({
            restrictedExtent: new OpenLayers.Bounds(left, bottom, right, top)
        });
    }

How it works...

As you have seen, the map has been instantiated using the two properties maxExtent and restrictedExtent, which are responsible for limiting the area of the map we can explore.

Although similar, these two properties have different meanings. Setting the maxExtent property limits the viewport so its center cannot go outside the specified bounds. By setting the restrictedExtent property the map itself cannot be panned beyond the given bounds.

The functions that react when buttons are clicked get the values from the input fields and apply the new values through the map.setOptions() method:

map.setOptions({
    maxExtent: new OpenLayers.Bounds(left, bottom, right, top)
});

We can pass the same properties we use when creating a new OpenLayers.Map instance to the map.setOptions() method and it will take care to update them.

There's more...

Limiting the map extent is not the only way to limit the information we show to the user. The layers have also similar properties to filter or limit the information they must render.

See also

  • The Moving around the map view recipe

About the Author

  • Antonio Santiago Perez

    Antonio Santiago Perez is a computer science professional with more than 10 years of experience in designing and implementing systems. Since the beginning of his professional life, his work has been always related to the world of meteorology while working for different companies as an employee or a freelancer. He has experience in development of systems that collect, store, transform, analyze, and visualize data, and he is actively interested in any GIS-related technology with a preference for data visualization. His main field of experience is the Java ecosystem, and he has also actively worked with many related web technologies while looking to improve the client side of web applications. He is a firm believer in software engineering practices and is a follower of agile methodologies, involving customers as the main key to the project's success.

    Browse publications by this author

Latest Reviews

(2 reviews total)
Excellent
Written, edited well (in contrast to some other Packt publications with weak English grammar--where some final corrective editing would have made a real difference). Sometimes the "logic" behind the sequencing of the chapter examples is hard to follow. Nonetheless, the book is well worth acquiring to learn OpenLayers3.
Book Title
Access this book and the full library for FREE
Access now