Book Image

OpenLayers 3: Beginner's Guide

By : Thomas Gratier, Paul Spencer, Erik Hazzard
Book Image

OpenLayers 3: Beginner's Guide

By: Thomas Gratier, Paul Spencer, Erik Hazzard

Overview of this book

<p>This book is a practical, hands-on guide that provides you with all the information you need to get started with mapping using the OpenLayers 3 library.</p> <p>The book starts off by showing you how to create a simple map. Through the course of the book, we will review each component needed to make a map in OpenLayers 3, and you will end up with a full-fledged web map application. You will learn the key role of each OpenLayers 3 component in making a map, and important mapping principles such as projections and layers. You will create your own data files and connect to backend servers for mapping. A key part of this book will also be dedicated to building a mapping application for mobile devices and its specific components.</p>
Table of Contents (22 chapters)
OpenLayers 3 Beginner's Guide
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Time for action – first steps with Closure Library


Although we can download the library on our computer, to remain simple, we will use a remote JavaScript library version.

  1. Create an HTML page using your text editor and cut and paste the following code:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Dom manipulation</title>
        <script src='https://rawgit.com/google/closure-library/master/closure/goog/base.js'></script>
        <script>
          goog.require('goog.dom');
    
         </script>
      </head>
      <body>
        <ul id="my_layers_list">
          <li>My first layer</li>
    
          <li>My second layer</li>
          <li>My third layer</li>
          <li>My background or base layer</li>
        </ul>
        <script>
          var my_layers_list = goog.dom.getElement('my_layers_list');
          var myTitle = goog.dom.createDom('h1');
          var myText = goog.dom.createTextNode("My simple layers list");
          goog.dom.append(myTitle, myText);
          goog.dom.insertSiblingBefore(myTitle, my_layers_list);
        </script>
      </body>
    </html>
  2. Open your browser to see the following result:

What just happened?

In order to understand the code, let's review the lines related to the Google Library:

<script src='https://rawgit.com/google/closure-library/master/closure/goog/base.js'>
</script>

Load the library base from a remote file. The code hosted on Github, needs to use a third-party website https://rawgit.com to be able to serve the .js files:

goog.require('goog.dom'); 

This line is the way to say make available the function from goog.dom namespace of the library. If you forget this call, every function call starting with goog.dom will fail. This call adds a call to <script src='https://rawgit.com/google/closure-library/master/closure/goog/base.js'> and must be separated in a dedicated <script> tag.

The goal here is to load only required functions and to keep your JavaScript clean with namespacing. Namespace enables separation of functions based on a common name. The chosen namespace for the Google Library is goog, so every function based on the library will start with goog. In this case, the goog.dom namespace is created. To discover the available functions in the namespace, you can use your JavaScript debugger and type goog.dom var my_layers_list = goog.dom.getElement('my_layers_list');. This line selects from DOM the element with an attribute ID, 'my_layer_lists'.

var myTitle = goog.dom.createDom('h1'); creates an <h1> tag in an HTML fragment, an element separated from the DOM, which you plan to add later to the web page DOM:

var myText = goog.dom.createTextNode("My simple layers list");

The following code line adds a text node, the visible element in HTML you will see in a web page.

goog.dom.append(myTitle, myText);

Add the text node to the <h1> tag. That is, the two previous floating elements <h1> </h1> and My simple layers list became: <h1>My simple layers list</h1>

goog.dom.insertSiblingBefore(myTitle, my_layers_list); 

Add the combined fragment to the DOM: it will be visible in your browser. We choose to use the insertSiblingBefore method. Its purpose is to add a fragment in the DOM before a reference DOM element. So, the text with <h1> tags will appear before the list.

With this example, we have reviewed a small subset of the goog.dom functions.

You will need them, for example, for interactivity like displaying an element with a color change, or for having an application that reacts to a click of a button.

Note

Google library is an ever evolving JavaScript library

In a real context, outside experimentations such as the first Closure Library code example, you will need to retrieve it using an SCM (Source Code Management) software. Its goal is to follow every change in the code. It is one of the most useful tools for developers. The one you need is called Git. Don't worry about it at the moment, we will need it in the Installing the OpenLayers development environment section. For now, just remember the URL to get the code from https://github.com/google/closure-library

To give you an overview of the most useful functions, we have mentioned below some statistics on the most used namespaces and sub-namespaces' functions in the OpenLayers 3 library. We also chose to keep the functions at the first level such as goog.require(. You can make the distinction between functions and namespaces with the open parenthesis. We also ordered the list in the following table:

Namespace and sub-namespaces

Numbers of occurrences

goog.require(

1436

goog.asserts

812

goog.isDef(

765

goog.isNull(

349

goog.provide(

295

goog.object

289

goog.base(

196

goog.inherits(

168

goog.events, goog.vec

136

goog.dom

113

goog.array

100

goog.exportProperty(

81

goog.math

72

goog.isDefAndNotNull(

68

goog.getUid(

39

goog.style

35

goog.isString(

22

goog.bind(

21

goog.string

14

goog.partial(

13

goog.isArray

12

goog.global

11

goog.addSingletonGetter(, goog.dispose(

9

goog.log, goog.uri.utils

5

goog.functions, goog.Uri, goog.now(

4

goog.net, goog.isFunction(, goog.isObject(

3

goog.async, goog.isNumber(

2

goog.color, goog.debug, goog.fs, goog.fx, goog.json, goog.userAgent, goog.isBoolean(, goog.Uri(

1

Let's talk about goog.require and goog.provide. The Google Library, and hence the OpenLayers internal code, manages dependencies using these two declarations. In a file, goog.require helps declaring required functions needed in the application code, whereas the goog.provide is the opposite: it permits declaring that some functions are within a file. These declarations combined with Closure Compiler usage help solve dependencies between the various library files and also with the application code. Reusing these dependencies will enable combining code for production.

Other important functions are goog.inherits and goog.base: you will find them to apply inheritance concepts already evoked in Appendix A, Object-oriented Programming – Introduction and Concepts.

With previous functions, you might think that some functions not at the top of the list are not useful, but you shouldn't. In fact, we invite you to review them because the code for the core of a JavaScript library differs from the code application to use it. In particular, look at goog.userAgent functions or goog.style.

Note

To have an overview of the available functions, the recommended way is to visit the official website, https://developers.google.com/closure/library/, to review the API, http://docs.closure-library.googlecode.com/git/index.html. For samples, look at the available demos you have to use at https://github.com/google/closure-library/tree/master/closure/goog/demos/. It's a great complementary help to the API, in particular to get an overview of the goog.ui components.

Next, let's head to an example to make your own component using Closure Library.

Custom components

When a component is not available, you will have to write some application code or for reuse purpose, make your own. In this section, we will first review some of the concepts of JavaScript applied with Google Closure Library. After this, we will see one of the official sample code to understand how to use the library to create your own customized component.

Inheritance, dependencies, and annotations

Let's start with the inheritance. It should ring a bell; otherwise, you should review Appendix A, Object-oriented Programming – Introduction and Concepts. This example will be enough to introduce you to other key concepts of the library:

<!DOCTYPE html>
<html>
  <head>
    <title>Inheritance</title>
    <script src='https://rawgit.com/google/closure-library/master/closure/goog/base.js'></script>
  </head>
  <body>
  <script>
    //Parent Class
    goog.provide('myNamespace.layer.Layer');
    /**
     * @constructor
     */
    myNamespace.layer.Layer = function (options) {
        this.color_ = options.color || 'grey';
    }
    myNamespace.layer.Layer.prototype.getColor = function () {
        return this.color_;
    }
    //Sub Class
    goog.provide('myNamespace.layer.Vector');
    /**
     * @constructor
     * @extends {myNamespace.layer.Layer}
     */
    myNamespace.layer.Vector = function (options) {
        goog.base(this, options);
        if (options.style) {
           this.style_ = options.style;
        }
    }
    goog.inherits(myNamespace.layer.Vector, myNamespace.layer.Layer);
    //Create a new instance of Vector layer and call to method from parent class.
    var myVector = new myNamespace.layer.Vector({
       color: 'white',
       style: 'myStyle'
    });
    console.log(myVector.getColor());
  </script>
  </body>
</html>

Launch it in your browser with the debugger, like Google Developer Tools, opened.

Let's review the code for the main lines:

<script src='http://closure-library.googlecode.com/svn/trunk/closure/goog/base.js'>
</script>

This inserts the call to Closure Library:

goog.provide('myNamespace.layer.Layer');

This declares the parent namespace (and class) with goog.provide:

/**
* @constructor
*/
myNamespace.layer.Layer = function (options) {
    this.color_ = options.color || 'grey';
}

This declares the parent constructor:

myNamespace.layer.Layer.prototype.getColor = function () {
    return this.color_;
}

This adds a method to the prototype of the object:

goog.provide('myNamespace.layer.Vector');

This declares the children's namespace (and class) with goog.provide:

/**
* @constructor
* @extends {myNamespace.layer.Layer}
*/
myNamespace.layer.Vector = function (options) {
  goog.base(this, options);
  if (options.style) {
    this.style_ = options.style;
  }
}

This declares the children's constructor and specifies who the parent is, in comments with @extends.

In the function, goog.base(this, options); can be replaced with myNamespace.layer.Layer.call(this, options); for a pure JavaScript alternative. It says to call the options from the current constructor:

goog.inherits(myNamespace.layer.Vector, myNamespace.layer.Layer);

This makes myNamespace.layer.Vector inherit from its parent myNamespace.layer.Layer.

var myVector = new myNamespace.layer.Vector({
    color: 'white',
    style: 'myStyle'
});
console.log(myVector.getColor());

This instantiates myNamespace.layer.Vector with options and makes a console call to get the method call to the parent class in order to retrieve the color.

Until now, we mainly covered Closure Library, but this knowledge can be reused in OpenLayers. You will see that OpenLayers application code can really look like Closure Library.

Have a go hero – analyze a real OpenLayers case

To see the similarity between Closure Library and OpenLayers application code, we will review inheritance in a real OpenLayers context. So, we will ask you to review an official example available at http://openlayers.org/en/v3.0.0/examples/custom-controls.html.

It will also be a good opportunity to review inheritance knowledge from Chapter 2, Key Concepts in OpenLayers; Chapter 9, Taking Control of Controls and Appendix A, Object-oriented Programming – Introduction and Concepts.

This example contains the following JavaScript content:

/**
 * Define a namespace for the application.
 */
window.app = {};
var app = window.app;

//
// Define rotate to north control.
//

/**
 * @constructor
 * @extends {ol.control.Control}
 * @param {Object=} opt_options Control options.
 */
app.RotateNorthControl = function(opt_options) {

  var options = opt_options || {};

  var anchor = document.createElement('a');
  anchor.href = '#rotate-north';
  anchor.innerHTML = 'N';

  var this_ = this;
  var handleRotateNorth = function(e) {
    // prevent #rotate-north anchor from getting appended to the url
    e.preventDefault();
    this_.getMap().getView().setRotation(0);
  };

  anchor.addEventListener('click', handleRotateNorth, false);
  anchor.addEventListener('touchstart', handleRotateNorth, false);

  var element = document.createElement('div');
  element.className = 'rotate-north ol-unselectable';
  element.appendChild(anchor);

  ol.control.Control.call(this, {
    element: element,
    target: options.target
  });

};
ol.inherits(app.RotateNorthControl, ol.control.Control);

//
// Create map, giving it a rotate to north control.
//

var map = new ol.Map({
  controls: ol.control.defaults({
    attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
      collapsible: false
    })
  }).extend([
    new app.RotateNorthControl()
  ]),
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM()
    })
  ],
  renderer: exampleNS.getRendererFromQueryString(),
  target: 'map',
  view: new ol.View({
    center: [0, 0],
    zoom: 2,
    rotation: 1
  })
});

These questions are for improvement:

  • Where does the code instantiate the RotateNorthControl component?

  • What is the parent class?

  • Where are the constructors and its methods?

  • What is the difference between the two methods (hint in the comments)?

  • Where do you declare inheritance (hint: ol.inherits is an alias to goog.inherits)?

Until now, we focused on Closure Library. However, as we mentioned at the start of this chapter, for performance, we need to use Closure Compiler as well. It is time to do it!