OpenLayers Tutorial - Part 3 - Controls

Posted on Sunday, May 02 2010 in openlayers

Controls allow interaction with OpenLayer maps. Panning, zooming, switching layers, etc, are all handled via controls. Some controls, such as dragging the map around to pan it, while others, like the overview map, do have a visual appearance.

There are cases where you might not want any controls - these cases are actually fairly common; for instance, if you wanted to show a small section of a map, mimic a static image, allow only logged in users to navigate around the map, etc. then you might want to have no controls on your map.

This tutorial is broken down and aims to teach you how to do the following:

1. Adding Controls
2. Customizing Controls - Passing properties and customizing control appearances
3. Adding Controls / Panels Outside of the Map >

We'll show how to create a map without controls shortly, but what kind of stuff can we do with controls? There are many more examples on OpenLayer.org's Examples page, simply search for "control" to get an idea of what various types of controls there are. There are lots of simple ones, ranging from scale bars to full vector layer manipulations.

While there are too many controls to cover in depth here in this post, I'll be focusing on a few different controls throughout this tutorial to exemplify how flexible OpenLayers is and give you an idea of what can be done.

Adding Controls

Most controls that are 'visible' (e.g. the layer switcher control) can be placed either inside or outside of the map. The 'invisible' controls, such the control that allows you to navigate the map when you drag the map with your mouse, are either there are they aren't - so we can't really 'place' them outside the map. Placing controls inside the map is the default behavior and is a bit simpler than placing it outside (although both are easy once you know how), so we'll cover that first.

Adding controls to a map can be done in two different ways.

1. When the map is created, it can accept an array of OpenLayers.Control objects to be passed in when creating the map object, and controls can be added to the map after it has been created. If no array of control objects are passed in, (like we've been doing in prior tutorials) OpenLayers creates the map with various default controls. The basic format (psuedo code follows) is:
controls: [OpenLayers.Control, OpenLayers.Control, etc...]
2. After the map is created, you can add controls individually by calling map.addControl(new OpenLayers.Control).

Before we get into that though, let's demonstrate how to add a map with no controls.

Map Without Controls

By default, OpenLayers adds a few controls to your map, such as mouse navigation and the pan / zoom bar, unless you tell it not to. (The full list of default controls added are ArgParser, Attribution, Navigation, and PanZoom

Let's first create a map with no controls to demonstrate. The following code will create a map object with no controls. The following line is the main relevant bit of code.

var map = new OpenLayers.Map('map', { controls: [] });

To see an implmentation of this code, view OpenLayers Control Part 3 - No controls.

What we're doing here is creating a new OpenLayers.Map object and passing in the ID of the element (typically a div element) that we want the map to appear in. The next bit
,{ controls: [] }
is where we specify additional parameters to pass in to create the map. Keep in mind these parameters are called in such a way of key: value, with multiple 'keys' separated by commas. The curly brackets denote that we're passing in key:value pair(s) (in this case, just one key:value pair).
The key is controls, and the value is [], which is an empty array element in Javascript. We are passing in no OpenLayers.Controls - basically, we're telling OpenLayers that we want this map object we're creating to have an empty array of controls; i.e. this map will have no controls.

Map With Controls

To add controls, we can either
1. pass in an array of controls when we instantiate the map (create the map object from the OpenLayers.Map class, like we did above). We do this by passing in the controls parameters such as { controls: [control1, control2, etc..] } when creating our map,
or
2. add controls after the map has been created. We do this by called map.addControl() and inside the paranthesis we pass in an OpenLayers Control object - e.g. new OpenLayers.Control.LayerSwitcher()

The second method is what I typically do when creating my maps; it comes in handy especially when you want to allow controls to be added or removed dynamically. Let's cover how to add a control to the map using both methods. I'll be using the OpenLayers Overview Map control (yes, the overview map is considered a control) to demonstrate.

Method 1 - Passing in controls on map creation

There are really two ways to do this. Because OpenLayers expects you to pass in an array, you can build the array before you actually pass it in, making the code a bit easier to read. Trying to keep this as simple as possible to those not much experienced with Javascript, we'll use the above example code to demonstrate. So, let's take a look:

var map = new OpenLayers.Map('map', { controls: [] });

The line here is
controls: []
Again, we're passing in an empty array for the controls, so no controls will show up. We can subsitute a previously created list here instead though, as follows:

var my_controls = []

var map = new OpenLayers.Map('map', { controls: my_controls });

Here we set my_controls equal to an empty array, and pass in my_controls to controls. Although we have an extra line, we're doing exactly the same thing we did above, only this time we're passing in a previously created array instead of creating it in the map object call. It doesn't make a lot of sense to do it this way in this example, but once you start adding more than one control to the map, you'll find it makes it much easier to read your code.
Example
Alright, let's make an actual control now. We're going to add it to the map the way we just described, by creating a list of controls first. This will make it easier demonstrate how we can add controls after the map has been created too. Now, doing it this method we will only have the controls we add to the map, so panning / zooming will not be enabled by default as we are overriding the default controls passed in. We'll be using the Overview Map control here to demonstrate.
This is the relevant code.

map_controls = [ new OpenLayers.Control.OverviewMap() ];

map = new OpenLayers.Map('map', {controls: map_controls});

The full code can be found at OpenLayers Overview Map Control.

Notice the only control is the Overview Map. We can add more controls by simply seperating them commas.

map_controls = [ new OpenLayers.Control.OverviewMap(), new OpenLayers.Control.LayerSwitcher() ];

map = new OpenLayers.Map('map', {controls: map_controls});

A demo with this code can be found at OpenLayers Overview Map and Layer Switcher Control.
Here we have both the overview map and layer switcher; but nothing else. Finally, before we move on, let's add those two controls and some of the defaul controls OpenLayers add to have a more functional map.

    map_controls = [ new OpenLayers.Control.OverviewMap(), 

                    new OpenLayers.Control.LayerSwitcher(),

                    new OpenLayers.Control.PanZoomBar(),

                    new OpenLayers.Control.MouseToolbar(), 

                    new OpenLayers.Control.KeyboardDefaults()];

    

    map = new OpenLayers.Map('map', {controls: map_controls});

Notice that the controls are seperated by commas, but I have also added new lines for readability. The code will work the same if there are no line breaks, but it won't be as readable. Again, you can see the demo at at OpenLayers Adding Base Controls.

Now that we know how this works though, let's move on to something a bit more common.

Method 2 - Adding controls after map has been created

We're going to let OpenLayers add some of the basic controls for us. You may not want to do this, depending on your own needs for the map - if you dont want to allow users to navigate around the map, for instance, then you should probably add controls manually like the method detailed above.
Let's set up a map using the default OpenLayers controls and we'll add the Overview and Layer Switcher control to it.

    map = new OpenLayers.Map('map');

    map.addControl(new OpenLayers.Control.OverviewMap());

You can view the demo at OpenLayers Adding Controls.

Since we didn't pass in controls to the map object, the default controls are added automatically. The next line is where we actually add the control to the map.
map.addControl(new OpenLayers.Control.OverviewMap()); 
map is our map object we just created, and addControl is a function call we make that -takes in- an OpenLayers Control object and adds it to the map. In this case, we are passing in new OpenLayers.Control.OverviewMap(). OverviewMap is the class, and we are creating an object from the class by typing new before it.
If this doesn't make too much sense, don't worry much about it for now - this concept is called Object Orientented Programming, and it is outside the scope of this tutorial. Without trying to further confuse things, think of a class as a blueprint for a house, and an object as a house created by the blueprint. This idea applies here; OpenLayers.Control.OverviewMap is a class, which is sort of like the blueprint for an overview map control. We can't actually do anything with the blueprint though, to manipulate it we need to create an object by called new OpenLayers.Control.OverviewMap(), which basically takes the blueprint and generates an object from it - this object is what we pass into the map.addControl( ) function call, as passing in just the blueprint won't do much good (it'd be similar to opening a door of a house - you can't open a door on the blueprint (the class), but you can open a door on the house (the object)).
We can also make as many calls to addControl as we like, and we can use it in conjunction with any controls we pass in when we create the map...OpenLayers is pretty flexible like that.

Recap

To recap, we can add controls by
1. Passing in an array of controls creating the map
2. Calling the function addControl(Control) and passing in a single OpenLayer control object
Example
Adding controls when map is created
map = new OpenLayers.Map('map', {controls: [new OpenLayers.Control.OverviewMap()] });
Adding controls to an already created map
map.addControl(new OpenLayers.Control.OverviewMap());
There are a plethora of controls we can add to the map, and I won't cover them all, but you can see a list of them at OpenLayer's Doc Site.

Customizing Controls

So, adding controls is pretty easy, but often we want to customize them a bit. This part can be a little tricky, as each control has its own set of configuration options - for instance, a permalink control and overview map control do not share the same configuration options. Some controls can also be placed in an HTML element outside of the map, such as the layer switcher or vector editing toolbar. We'll first cover configuration basics, and then discuss how to add a control outside a map.

Let's walk through how to customize an overview map control, and by doing so you'll have the necessary knowledge to customize any other control (hopefully). I'm going to break down control customization into two parts:
1. Instantiation (creation) parameters - handles the actual control options
2. Style (CSS) customization - handles the look / feel / position / style of the control
For this section, I'll use the Overview Map control as an example. The overview map control does not have the same properties of other controls, so be cognizant of this when adding your own controls. The best place to find out what we can do when calling an OpenLayer control is at the OpenLayer documentation site, and for this we'll be using OpenLayers Overview Map Control Doc.

1. Customization on Instantiation (creation)

When we create a control, we define what options we want it to be created with. We can, however, manipulate the control after it is created, but for now let's just stick to defining everything when we create the control.
Here's some code to create an overview map control with a couple properties defined:

    map.addControl(new OpenLayers.Control.OverviewMap(

                {autoPan:true,

                layers: [new OpenLayers.Layer.WMS("OverviewMap",

                    "http://labs.metacarta.com/wms/vmap0", 

                    {layers: 'country_01'})]

                }

                ));

These are the most relevant lines, but you can take a look at the customizing overview map control demo to see it in action.

This may look intimidating, but let's break it down.
We already know map.addControl(new OpenLayers.Control.OverviewMap( this line. map is our map object, and addControl is the same function we've been using. Notice that we are calling the function add just one control, so we only pass one control into the function. We're creating a new OverviewMap control object and passing it in - still, nothing new here. The next line is new though.
{autoPan:true,
Notice the curly brackets - this denotes that we're going to start passing in key:value pairs, which are the options we want to set for this control. autoPan is the key, and true is the value. Notice the comma afterwards, which means we're going to pass in another key:value pair next. Before we go to the next line, let's briefly discuss what autoPan:true means.

I'm going to quote the OpenLayer Overview Control Documentation page directly so you get an idea how to use it.
autoPan - "{Boolean} Always pan the overview map, so the extent marker remains in the center. Default is false. If true, when you drag the extent marker, the overview map will update itself so the marker returns to the center."
What this means is that {Boolean} is the -type- of value that is expected in the key:value pair. Thus, this means that autoPan expects to receive either true or false for its value. If we don't pass in autoPan:true when we create our overview control, OpenLayers assumes autoPan is set as false. We could also explicitly say autoPan:false but this is not necessary, as we know from looking at the docs that OpenLayers assumes the value is false unless otherwise specified.
Ok - so moving along. The next line looks a bit hairy

layers: [new OpenLayers.Layer.WMS("OverviewMap",

                    "http://labs.metacarta.com/wms/vmap0", 

                    {layers: 'country_01'}) ]

But let's take a look. All that's going on here is that layers is the key and an array containing a single layer object is the value. From the OpenLayer docs, layers expects an {Array(OpenLayers.Layer)}.

What's this mean? Well, the overview map expects an array of OpenLayers Layer objects to be passed in. Layers are discussed in detail in the previous tutorial, part 2 - layers. So without getting too detailed here, we're basically passing in a single OpenLayers WMS Layer. This layer is the only layer in our array, but if we wanted another we could just add a comma and specify as many more layers as we want. We are also passing in parameters to this new OpenLayers Layer object so we get a different layer for the overview map. By default, the overview map uses the maps' base layer.

So, my lengthy explanations aside, we see that it isn't too tricky to pass in construction parameters. We do it when we create our layers, so it's not new - but there are quite a lot of options, and the official OpenLayers docs describe all the options. All other controls are called and defined the way we just did above, so it's only a matter of figuring out what control and what options you want to specify. But after we do this, we might want to give it a custom style. OpenLayers is generally pretty good with allowing customizable controls with CSS style sheets.

2.Customizing OpenLayers Controls with CSS

Cascading Style Sheets (CSS) allow us to specify how HTML elements look and feel, their size, position, font size, etc. etc. Anything related to the way the page looks can pretty much be handled via CSS; CSS is a way to customize appearance. OpenLayers allows us to style our controls with CSS, which makes creating a slick looking map much, much easier.

All OpenLayers controls have an associated CSS class where we can specify a style we want to apply to it.
I won't cover much CSS in this post, but CSS basically expects an element with property : value pairs, which can be views as key:value pairs, separated by a semi-colon ( ; ). Here's the basic structure:
element { key: value; }
Where element is an HTML element (can be an element like div (would apply to all div elements) or an ID (single element) or class (multiple elements) ), key is an attribute of the element (such as width or height) and value is, surprise, the value. For things like width or height, the value is either a pixel value (like 500px) or a percentage (like 50%).

To define a style for an OpenLayers element, we must access it by class name. The class names are set up in form of .olControlControlName.
.olControl is always at the beginning of the classname, and the control name follows. So, for our overview map, the classname we would need to override the default CSS style is
.olControlOverviewMap

Notice the period at the beginning, to those unfamiliar with CSS that denotes that what follows the period is a class name.
Now we can override the default styles and make it look like however we want. Here's an example CSS declaration you can add that will style the overview map on the page; feel free to mess around with the declaration and style it however you would like.

<style>

    .olControlOverviewMap {

        background:#ff0000;

        width:200px;

    }

    .olControlOverviewMapElement {

        background:#00ff00 !important;

    }

</style>

View the overview map css control demo.
Now, this is a good example although it is a little tricky, because styling the overview map is a little tricky.
You'll notice we have the olControlOverviewMap element's background set as red (#ff0000) but the olControlOverviewMapElement element's background as green (#00ff00) and has !important after it. This is to ensure that the overview map element's background is not overwritten - if we don't include any background, it is blue by default. This is because OpenLayer's Overview Map control contains another CSS element, namely the OverviewMapElement, and we need to define it also. Most controls are not set up this way, but the overview map is fairly common and does not exactly follow the same style guidelines as other controls.

Adding Controls Outside of Map

The last topic we'll cover is how to add a control outside the map. With the previously discussed code, this should be quite an easy feat to accomplish.
There are many cases where you might want a control outside the map - a toolbar of buttons in a div that sits on top the map, for instance. You can also create custom buttons, which will be discussed in depth in a future tutorial.
There are two main ways to place controls outside the map
1. Place the control itself outside the map
2. Place the control in a "panel" outside the map. The panel contains a group of controls.

Placing a control outside the map

To place a control outside the map, we need an element to place it in (we'll use a div with an id of "external_control") and we specify a div parameter when calling the control. To demonstrate:
HTML Code

<div id="external_control"></div>


Javascript Code

var external_control = new OpenLayers.Control.MousePosition({

        div: document.getElementById('external_control') });



map.addControl(external_control);

You can view the external control demo here.

We're basically creating a MousePosition control and setting the value of div to equal an HTML element with the element ID of 'external_control'. The mouse position control is outside the div, in it's own div. We could apply whatever style we want, either by styling the div itself (without using OpenLayers style classes) or style the control itself (using the .olControlMousePosition class), or both.

Placing a control outside the map in a panel

Panels are basically a collection of controls which can be placed inside or outside of the map. These are useful for grouping related buttons together, such as pan buttons, zoom buttons, etc. To demonstrate, we'll build a panel from scratch and add some controls as buttons to it. We need an HTML element to hold our panel, so we'll do what we did above, but we'll get it an ID of 'panel'

<div id="panel"></div>


Now we just need to create the Panel control itself

    var external_panel = new OpenLayers.Control.Panel({

                    div: document.getElementById('panel') });

    map.addControl(external_panel);


The demo code will come shortly. Right now, this does nothing but create an empty Panel control attached to our 'panel' div element. We need to now add controls to it.

Control Types
There are three 'types' of controls OpenLayers provides for use in a panel.
1. TYPE_BUTTON
2. TYPE_TOOL
3. TYPE_TOGGLE

TYPE_BUTTON
These controls call an event / trigger an action when clicked. An example would be a zoom in / out button; once clicked, an event is triggered to zoom the map in

TYPE_TOOL
These controls 'turn on' when clicked and 'turn off' when clicked again. An example would be the editing buttons on OpenLayer's Editing Toolbar Example. Once the control is activated (e.g. the pan button - the button with the hand icon), it stays activated until it is either clicked off or another control is activaed.

TYPE_TOGGLE
These controls are similiar to the TYPE_TOOL, as they are activated and deactived on mouse click; however, they do not affect the state of other controls - meaning, that if a TYPE_TOGGLE button is activated, it will not deactivate other controls. Only one TYPE_TOOL button can be active at a type, but TYPE_TOGGLE buttons do not carry this restriction.

Adding controls to panels

Alright, so let's get to it. We're going to put together what we've learned so far to add zoom in and out buttons to the panel outside the map and edit their class to make them look something like a button. The first step, creating the panel, was done above. The next step is to simply add two more controls now - one will be a zoom in control, the other a zoom out control. By default, there is no style associated with these controls when we place them outside the map, as they are called normally by methods such as scrolling in or out.
Before we describe exactly what we need to do, let's take a look at the docs for the zoom in and out controls - Zoom In control. You'll notice there's not much there. It doesn't have any special properties, although it does inherit everything from the base OpenLayers.Control class, so anything we can do to that class we can do to the ZoomIn class. The ZoomOut class is the same way. So, we don't need to worry about passing in anything extra really, we'll just create the two controls without passing anything in; here's how we do it.
1. Create and add a panel to the map
2. Create and add a zoom in and a zoom out control to the map
3. Add the zoom in and zoom out controls to the panel
4. Add a style to the two controls
1. Create and add a panel
We've done this above. We need to create a div with an ID of "panel" and add the corresponding Javascript code.

    var external_panel = new OpenLayers.Control.Panel({

                    div: document.getElementById('panel') });

    map.addControl(external_panel);


2. Create and add a zoom in and a zoom out control to the map

All we have to do is create the controls

    var control_zoom_in = new OpenLayers.Control.ZoomIn();

    var control_zoom_out = new OpenLayers.Control.ZoomOut();

and then add them to the map

    map.addControl(control_zoom_in);

    map.addControl(control_zoom_out);


3. Add the zoom in and zoom out controls to the panel
Alright, so we have those two controls added to the map; we now need to add the controls to the panel.

    external_panel.addControls([control_zoom_in, control_zoom_out]);

You'll notice we call the function addControls. To add any controls to a panel, we must call this function and pass in an array of controls - even if we only want to add one control (there is no "addControl" function, only addControls)
If we take a look at the map now, we can see that it doesn't look like there is a panel because we haven't applied any styles yet. Controls outside map demo in panel - no style
This is because by default, there is no style associated with the zoomin or zoomout controls. Let's finish up this demo though by adding some CSS

Adding style to controls in panels

The naming convention is the same as we mentioned above for naming controls inside panels, however, as these are controls inside a panel, they have 'states' attached attached to them. ItemActive or ItemInactive is added to the end of the control's class name. For example, our zoom in button has the class name
.olControlZoomInItemInactive

Because of the type of this control is a button, its only relevant class is olControlZoomInItemInactive. When the button is clicked, it does not become active or inactive, but simply triggers the zoomIn event.
So, let's add some basic style to our controls to make them look a little bit like buttons. Here's the relevant CSS code I use in the demo below:

.olControlZoomInItemInactive {

    background:url(http://dev.openlayers.org/releases/OpenLayers-2.8/img/zoom-plus-mini.png) no-repeat;

    border:2px solid #232323;

    float:left;

    height:18px;

    margin-right:5px;

    width:18px;

}

.olControlZoomOutItemInactive {

    background:url(http://dev.openlayers.org/releases/OpenLayers-2.8/img/zoom-minus-mini.png) no-repeat;

    border:2px solid #232323;

    float:left;

    height:18px;

    margin-right:5px;

    width:18px;

}

I'm just using the zoom-minus and zoom-plus images from OpenLayer's website to represent buttons, but you stlye them however you'd like.
Take a look at the finalized example here - Controls outside map demo in panel with CSS style

Conclusion

With this tutorial, hopefully you've learned how to add controls to an openlayers map, customize them a bit, give them some style, and add controls and panels outside a map. Although this tutorial was a bit lengthy, it really just scratches the surface of what is possible to do with OpenLayers controls. In the next tutorial, I plan to discuss how to use the OpenLayers Event system and create your own custom controls. Stay tuned.

Comments

blog comments powered by Disqus