How the code works
Now, let's take a look at the code—line by line. Before we do that, let's include a quick reference to the line numbers at which the requirements from the previous section occur at. These are the core things that you need to do to have a functioning map.
Note
We'll denote line numbers with brackets—[x], where x is the line number.
Including the OpenLayers library files:
Line [6] <script type='text/javascript' src='OpenLayers.js'></script>
Creating an HTML element for our map:
Lines [30] and [31] <div id='map_element' style='width: 500px; height: 500px'> </div>
Creating a map object from the Map class:
Line [12] map = new OpenLayers.Map('map_element', { });
Creating a layer object from a Layer class:
Lines [13] to [18] var wms_layer = new OpenLayers.Layer.WMS( 'WMS Layer Title', 'http://vmap0.tiles.osgeo.org/wms/vmap0', {layers: 'basic'}, {} );
Adding the layer to the map:
Line [20] map.addLayer(wms_layer);
Defining the map's extent:
Lines [21] to [23] if(!map.getCenter()){ map.zoomToMaxExtent(); }
Understanding the code—Line by line
Lines [1] to [5]: Sets up the HTML page. Every HTML page needs an <html>
and <head>
tag, and the extraneous code you see specifies various settings that inform your browser that this is an HTML5 compliant page. For example, we include the DOCTYPE
declaration in line [1] to specify that the page conforms to standards set by the WC3. We also specify a <title>
tag, which contains the title that will be displayed on the page.
Note
This is the structure that all our code examples will follow, so this basic code template will be implicitly assumed in all examples that follow throughout the book.
Line [6]: <script type='text/javascript' src='OpenLayers.js'></script>
This includes the OpenLayers library. The location of the file is specified by the src='OpenLayers.js'
attribute. Here, we're using a relative path
. As the index.html
page is in the same folder as the OpenLayers.js
file, we don't have to worry about specifying the path to it. The file could be either on your computer or another computer—it doesn't matter much, as long as the browser can load it.
We can also use an absolute path
, which means we pass in a URL that the script is located at. OpenLayers.org
hosts the script file as well; we could use the following line of code to link to the library file directly:
<script type='text/javascirpt' src='http://openlayers.org/api/OpenLayers.js'></script>
Notice how the src specifies an actual URL—this is how we use absolute paths. Either way works, however, throughout the book we'll assume that you are using a relative path and have the OpenLayers library on your own computer/server. If you use the hosted OpenLayers library, you cannot be sure that it will always be available, and it may change overnight (and changes when the library is updated)—so using a local copy is recommended.
Line [7]: Starts a <script>
block. We'll set up all our code inside it to create our map. Since the OpenLayers library has been included in line [5], we are able to use all the classes and functions the library contains.
Line [8]: var map;
Here we create a global variable called map
. In JavaScript, anytime we create a variable we need to place var
in front of it to ensure that we don't run into scope issues (what functions can access which variables). When accessing a variable, you do not need to put var
in front of it.
Since we are defining map as a variable at the global level (outside of any functions), we can access it anywhere in our code. Soon we will make this map variable our map object, but right now it is just an empty global variable.
Line [11]: Creates a function called init
. When the page loads (via body onload='init();'
on line [29]), this function will get called. This function contains all of our code to set up our OpenLayers map. If you are familiar with JavaScript, you do not have to put all the code in a function call—you could, for instance, just put the code at the bottom of the page and avoid a function call all together. Creating a function that gets called when the page loads is a common practice and so we will be doing it throughout the book.
Line [12]: map = new OpenLayers.Map('map_element', { });
Remember that global map variable? Well, now we're making it a map object, created from the OpenLayers.Map
class. It is also referred to as an instance of the Map class. We'll talk about what this means later in this chapter in the Object Oriented Programming section. The map object is the crux of our OpenLayers application— we call its functions to tell the map to zoom to areas, fire off events, keep track of layers, etc.
Now, let's look at the right hand side of the equal sign (=
): new
means that we are creating a new object from the class that follows it. OpenLayers.Map
is the class name which we are creating an object from. Notice that something is inside the parenthesis: ('map_element', {})
. This means we are passing two things into the class (called arguments, and you pass them in separated by a comma). Every class in OpenLayers expects different arguments to be passed into it, and some classes don't expect anything.
The Map class expects two parameters. The first argument, map_element
, is the ID of the HTML element that the map will appear in. The second argument, { }
, are the map options, consisting of key:value
pairs (e.g., {key:value}
). This is also called JavaScript Object Notation, a way to create objects on the fly. We'll cover this in more depth very shortly in the next section. Also, you are not required to include this argument if it is empty (even though we just did it), but we are just doing it here for consistency.
Because we passed in map_element
as the first parameter, we will have an HTML element (almost always a <div>) with the ID of map_element
. The HTML element ID can be anything, but for the sake of clarity and to avoid confusion, we call it map_element
.
Line [13]: var wms = new OpenLayers.Layer.WMS(
Here, we create a layer object for the map to use from the WMS subclass of the Layer class. In OpenLayers, every map needs to have at least one layer. The layer points to the 'back end', or the server side map server, as we discussed earlier. The layer can be any of a multitude of different services, but we are using WMS here. WMS, which stands for Web Map Service , is an international standard defined by the Open Geospatial Consortium (OGC).
The arguments we can pass in for layers are dependent on the layer class—we cover layers in detail in Chapter 3. If you don't want to wait, you can also check out the documentation at http://dev.openlayers.org/docs/files/OpenLayers/Layer-js.html to see what arguments different layers of classes expect.
Notice we don't include everything on one line when creating our layer object—this improves readability, making it easier to see what we pass in. The only difference is that we are also adding a new line after the commas which separate arguments, which doesn't affect the code (but does make it easier to read).
Line [14]: 'WMS Layer Title',
This is the first parameter passed in; the layer's title. Most layer classes expect the first parameter passed in to be the title of the layer. This title can be anything you would like, the main purpose of it is for human readability—it is displayed in controls such as the layer list.
Line [15]: 'http://vmap0.tiles.osgeo.org/wms/vmap0',
The URL is the second parameter that the WMS layer class expects to receive. For now, we're using a publicly available WMS service from OSGeo. We will cover in depth the WMS in Chapter 3. For now, all you need to know is that this is the base URL, which the layer will be using.
Line [16]: {layers: 'basic'},
The third parameter is an anonymous object containing the layer properties (similar in format to the previous options object on line [12]), and is specific to the WMS layer class. These are the things that are actually added (more or less) straight into the GET call to the map server backend when OpenLayers makes requests for the map images.
JavaScript object notation
In OpenLayers, we pass in anonymous objects to classes a lot. In JavaScript, anonymous objects are comma separated key:value
pairs, and are set up in the format of {key1:value1, key2:value2}
. They are, basically, objects that are created without deriving from a class. This format is also referred to as JavaScript Object Notation.
When we say key1:value1
, it's similar to saying "key1 = value1"
, but we use a colon instead of an equals sign. We can also create an anonymous object and pass it in instead of creating it on the line, for example:
var layer_parameters = {layers: 'basic'}; var wms = new OpenLayers.Layer.WMS('layer_title', 'url',layer_parameters, …);
With a WMS layer, we need to pass in, at a minimum, a layers
key. In this case it has the value of 'basic'
. This layer
parameter specifies layers that exist on the map server. So, when you ask the WMS server from a map image with the layer 'basic'
, it sends you back an image that is composed of that layer. You can also ask for multiple layers from the map server. In this case, we only want the WMS service to give us back an image that contains a layer called 'basic'
.
Let's get back to the code.
Line [17]:
{ }
The fourth parameter is an optional options object, an anonymous object in the format we just discussed. These properties are generally shared by every OpenLayers Layer class. For instance, regardless of the Layer type (e.g., WMS or Google Layer), you can pass in an opacity setting (e.g., {opacity: .8}
for 80 percent opacity). So, regardless of whether you are working with a WMS or a Vector layer, this opacity property can apply to either layer.
Note
Since this is the last thing passed into the Layer object creation call, make sure there is not a leading trailing comma. Trailing commas are a common error and are often tedious to debug.
This options object is optional, but we will often use it, so it's a good habit to keep our code consistent and provide an empty object (by {}
), even if we aren't passing anything into it yet.
Line [18]: );
This simply finalizes the object creation call.
Line [20]:
map.addLayer(wms);
Now that we have a
wms_layer
object created, we need to add it to the map object. Notice we are calling a function of the map object. There are actually a few ways to go about adding a layer to a map object. We can use the above code (by calling map.addLayer
), where we pass in an individual layer, or we could use map.addLayers
:
map.addLayers( [layer1, layer2, ...] );
Here, we pass an array of layers. Both methods are equally valid, but it may be easier to pass in an array when you have multiple layers.
You can also create the layer objects before you create the map object and pass the layer objects into the map when you create it, for instance:
map = new OpenLayers.Map('map_element', {layers: [layer1, layer2, …]});
All ways are valid, but we will usually use addLayer
or addLayers
throughout the book.
Line [21] - [23]:
if(!map.getCenter()){ map.zoomToMaxExtent(); }
Finally, we must specify the map's viewable area. Here, the actual code that moves the map is map.zoomToMaxExtent()
, which zooms the map to the map's maximum extent. It is inside an if
statement. This if
statement checks to see whether the map already has a center point.
The reason why we add in this check is because, by default, your map can accept a specially formatted URL that can contain an extent and layers to turn on/off. This is, in more common terms, referred to as a permalink
. If we did not check to see if a center has already been set, permalinks would not work.
Note
By default, your map adds an argParser control which will try to pull information from a permalink. We cover this in Chapter 6, but to see it in action now you can simply add the following to your URL, which will zoom the map to the same coordinate and zoom level: ?zoom=4&lat=56&lon=-116
So, your URL might look like c:/code/index.html?zoom=4&lat=56&lon=-116
There are a few ways to set the map's extent. If you know you want to show everything, the map.zoomToMaxExtent()
function is a quick and good way to do it. There are other ways as well, such as
map.zoomToExtent(new OpenLayers.Bounds([minx,miny,maxx,maxy]);
There are even more ways though. If you know a specific location you want the map to start at, this is another way to do it:
map.setCenter(new OpenLayers.LonLat(x,y)); map.zoomTo(5);
Where x,y
are the Lon/Lat values, and 5
is the zoom level you wish to zoom to. By default, your map will have 16 zoom levels, which can be configured by setting the numZoomLevels
property when creating your map object.
More ways exist, but these are the most common strategies. The basic idea is that you need to specify a center location and zoom level—setting the extent accomplishes this, as does explicitly setting the center and zoom level.
Line [24]:
}
This simply finishes the init()
function.
Lines [26], [27]:
These lines close the script tag and head tag.
Line [29]:
<body onload='init();'>
This starts the body tag. When the page is finished loading, via the onload='init();'
attribute in the body
tag, it will call the JavaScript init()
function. We have to wait until the page loads to do this because we cannot use the map div (or any HTML element) until the page has been loaded. Another way to do this would be to put the init()
call in a JavaScript tag at the bottom of the page (which would not be called until the page loads), but both methods accomplish the same thing.
When browsers load a page, they load it from top to bottom. To use any DOM (Document Object Model) elements (any HTML element on your page) in JavaScript, they first have to be loaded by the browser. So, you cannot reference HTML with JavaScript before the browser sees the element. It'd be similar to trying to access a variable that hasn't yet been created. Even though we have JavaScript code that references the map_element
div at the top of the page, it is not actually executed until the page is loaded (hence the need for the onload
and init()
function call).
Line [30] and [31]:
<div id='map_element' style='width: 500px; height: 500px'></div>
To make an OpenLayers map, we need an HTML element where the map will be displayed in. Almost always this element will be a div
. You can give it whatever ID you would like, and the ID of this HTML element is passed into the call to create the map object. You can style the div however you would like—setting the width and height to be 100 percent, for instance, if you wanted a full page map. It would be best to style the elements using CSS, but styling the div in line like this works as well.
Lines [32] and [33]: These lines finalize the page by closing the remaining tags.