OpenLayers Tutorials
OpenLayers Tutorials
Objectives
With version 3, the OpenLayers web mapping library was fundamentally redesigned.
The widely used version 2 dates from the early days of Javascript development, and
was increasingly showing its age. So it has been rewritten from the ground up to use
modern design patterns.
The initial release aims to support much of the functionality provided by version 2,
with support for a wide range of commercial and free tile sources, and the most
popular open-source vector data formats. As with version 2, data can be in any
projection. The initial release also adds some additional functionality, such as the
ability to easily rotate or animate maps.
It is also designed such that major new features, such as displaying 3D maps, or
using WebGL to quickly display large vector data sets, can be added in later
releases.
Google Closure
OpenLayers was written in a way so it can be compiled with Closure Compiler. Its
'advanced' compilation mode offers a level of compression that exceeds anything
else available.
Public API
Using the advanced optimizations of the Closure Compiler means that properties and
methods are renamed longMeaningfulName might become xB and so are
effectively unusable in applications using the library. To be usable, they have to be
explicitly exported. This means the exported names, those not renamed, effectively
become the public API of the library. These exportable properties and methods are
marked in the source, and documented in the API docs. This is the officially
supported API of the library. A build containing all these exportable names is known
as a full build. A hosted version of this is available, which can be used by any
application.
Custom Builds
Unlike in, say, Node, where a module's exports are fixed in the source, with Closure
Compiler, exports can be defined at compile time. This makes it easy to create builds
that are customized to the needs of a particular site or application: a custom
build only exports those properties and methods needed by the site or application.
As the full build is large, and will probably become larger as new features are added
to the API, it's recommended that sites create a custom build for production software.
Class namespaces, such as ol.layer have a base class type with the same name,
such as ol.layer.Layer. These are mainly abstract classes, from which the other
subclasses inherit.
Source files are similarly organised, with a directory for each class namespace.
Names are however all lower-case, for example, ol/layer/vector.js.
OpenLayers follows the convention that the names of private properties and
methods, that is, those that are not part of the API, end in an underscore. In general,
instance properties are private and accessed using accessors.
Basic Concepts
Map
The core component of OpenLayers is the map (ol.Map). It is rendered to
a target container (e.g. a div element on the web page that contains the map). All
map properties can either be configured at construction time, or by using setter
methods, e.g. setTarget().
View
ol.Map is not responsible for things like center, zoom level and projection of the map.
Instead, these are properties of an ol.View instance.
map.setView(new ol.View({
center: [0, 0],
zoom: 2
}));
An ol.View also has a projection. The projection determines the coordinate system
of the center and the units for map resolution calculations. If not specified (like in the
above snippet), the default projection is Spherical Mercator (EPSG:3857), with
meters as map units.
The zoom option is a convenient way to specify the map resolution. The available
zoom levels are determined by maxZoom (default: 28), zoomFactor (default: 2)
and maxResolution (default is calculated in such a way that the projection's validity
extent fits in a 256x256 pixel tile). Starting at zoom level 0 with a resolution
of maxResolution units per pixel, subsequent zoom levels are calculated by dividing
the previous zoom level's resolution by zoomFactor, until zoom level maxZoom is
reached.
Source
To get remote data for a layer, OpenLayers uses ol.source.Source subclasses.
These are available for free and commercial map tile services like OpenStreetMap or
Bing, for OGC sources like WMS or WMTS, and for vector data in formats like
GeoJSON or KML.
Layer
A layer is a visual representation of data from a source. OpenLayers has three basic
types of layers: ol.layer.Tile, ol.layer.Image and ol.layer.Vector.
ol.layer.Tile is for layer sources that provide pre-rendered, tiled images in grids
that are organized by zoom levels for specific resolutions.
ol.layer.Image is for server rendered images that are available for arbitrary extents
and resolutions.
ol.layer.Vector is for vector data that is rendered client-side.
Requirements
OpenLayers's build tools use Node and Java, so you need to have Node and Java
installed on your machine. You can run node --version and java -version to test
that Node and Java are installed, respectively. See developing guide for minimum
version numbers required.
Download OpenLayers
Obviously, creating a custom build requires the OpenLayers source and specific build
scripts.
To get the OpenLayers source and the build scripts you can clone the
openlayers repository, or you can download one of the release archives. You can
also download the openlayers Node package from the Node package registry, using
NPM (the Node Package Manager). This is the method we are going to use in this
tutorial.
Create a directory:
$ mkdir openlayers
This will download the latest stable version of OpenLayers, and install it
under node_modules. You can list the content of node_modules to verify that it
effectively contains a directory named "openlayers".
The Node packages onto which the openlayers package depends are installed
under node_modules/openlayers/node_modules . That directory should, for example,
include closure-util, which is the utility library OpenLayers uses for Closure.
You should now have everything you need to create custom builds of OpenLayers!
{
"exports": [
"ol.Map",
"ol.View",
"ol.control.defaults",
"ol.layer.Tile",
"ol.source.OSM"
],
"compile": {
"externs": [
"externs/bingmaps.js",
"externs/cartodb.js",
"externs/closure-compiler.js",
"externs/esrijson.js",
"externs/geojson.js",
"externs/oli.js",
"externs/olx.js",
"externs/proj4js.js",
"externs/tilejson.js",
"externs/topojson.js"
],
"extra_annotation_name": [
"api", "observable"
],
"compilation_level": "ADVANCED",
"manage_closure_dependencies": true,
"rewrite_polyfills": false
}
}
Create a file named ol-custom.json with that content, and save it under
the node_modules/openlayers/build directory. (You can save it to any location
really.)
The most relevant part of this configuration object is the "exports" array. This array
declares the functions/constructors you use in your JavaScript code. For example,
the above configuration file is what you'd use for the following JavaScript code:
$ cd node_modules/openlayers
$ node tasks/build.js build/ol-custom.json build/ol-custom.js
The build command may take some time, but it should end with an output in the
console such as the following:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>OpenLayers example</title>
<link rel="stylesheet" href="node_modules/openlayers/css/ol.css" />
<style>
#map {
width: 600px;
height: 400px;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="node_modules/openlayers/build/ol-custom.js"></script>
<script>
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: [0, 0],
zoom: 4
})
});
</script>
</body>
</html>
"define": [
"ol.ENABLE_WEBGL=false",
"ol.ENABLE_PROJ4JS=false",
"ol.ENABLE_RASTER_REPROJECTION=false"
],
and re-run the build script. The build size should now be smaller.
Externs
The Closure documentation explains that "externs" are for external names used in
the code being compiled. The compiler includes externs for built-ins such
as document. The externs directory of the OpenLayers code includes files for all
those used in some part of the library. For example, if you use Bing Maps, you
should include the Bing externs file in the externs section of the config file.
oli.js and olx.js are externs files for the OpenLayers API. For
example olx.js includes extern definitions for OpenLayers's constructor
options. closure-compiler.js fixes any issues that may arise with a specific compiler
version. You should always use these three files as externs when creating custom
builds.
Other compiler options
There are a couple of other compiler options in the config file
above. manage_closure_dependencies and rewrite_polyfills should always be
used.
You can specify any of the other compiler options here as needed, such as the
renaming reports, output manifest, or source maps. There is a full list of available
options in closure-util.
Note that build.js currently requires you to enter an output file and will write the
output from the compiler to it; it does not use the js_output_file compiler option. If
you specify this in the config file, there will be no compiler output, so build.js's
output file will be empty.
{
"exports": [
"ol.layer.Heatmap",
"ol.source.Vector",
"ol.format.KML",
"ol.layer.Heatmap#getSource",
"ol.source.Vector#on",
"ol.source.Vector.Event#feature",
"ol.Feature#get",
"ol.Feature#set",
"ol.layer.Tile",
"ol.source.Stamen",
"ol.Map",
"ol.View",
"ol.layer.Heatmap#setRadius",
"ol.layer.Heatmap#setBlur"
],
"compile": {
"externs": [
"externs/bingmaps.js",
"externs/cartodb.js",
"externs/closure-compiler.js",
"externs/esrijson.js",
"externs/geojson.js",
"externs/olx.js",
"externs/oli.js",
"externs/proj4js.js",
"externs/tilejson.js",
"externs/topojson.js"
],
"define": [
"ol.ENABLE_WEBGL=false",
"ol.ENABLE_PROJ4JS=false",
"ol.ENABLE_RASTER_REPROJECTION=false"
],
"compilation_level": "ADVANCED",
"manage_closure_dependencies": true,
"rewrite_polyfills": false
}
}
The exports are given here in the order in which they occur in the heatmaps-
earthquakes example's JavaScript code. In this example we not only use
the ol.functions and constructors, but also prototype methods where
the ol namespace is not directly used. In the code, we have for
example vector.getSource().on(). This means we are using the getSource method
of layer.Heatmap and the on method of source.KML, so this is what has to be
exported. Similarly, event.feature.get() means we are using the feature property
of source.Vector.Event and the get method of Feature. If any of these names are
left out, the compile will complete successfully, but the missing names will be
obfuscated and you will get a 'property undefined' error when you try and run the
script.
Conclusion
This tutorial should have given you the information you need to create custom builds,
i.e. builds tailored to your application. See the tasks readme for more information on
the build scripts and the properties you can use in the build configuration file.
Code licensed under the 2-Clause BSD. All documentation C
Introduction
When going beyond modifying existing examples you might be looking for a way to
setup your own code with dependency management together with external
dependencies like OpenLayers.
This tutorial serves as a suggested project setup using NPM and Browserify for the
most basic needs. There are several other options, and in particular you might be
interested in a more modern one (ES2015) using Webpack with OpenLayers.
Initial steps
Create a new empty directory for your project and navigate to it by running mkdir
new-project && cd new-project. Initialize your project using npm init and answer
the questions asked.
Add OpenLayers as dependency to your application with npm install --save ol.
At this point you can ask NPM to add required development dependencies by
running
We will be using cssify to include the css definitions required by OpenLayers in our
bundle. watchify, http-server and uglify-js are used to monitor for changes and
to build into a minified bundle. babelify and babel-plugin-transform-es2015-
modules-commonjs are used to make the ol package, which was created using
ES2015 modules, work with CommonJS.
require('ol/ol.css');
var ol_Map = require('ol/map').default;
var ol_layer_Tile = require('ol/layer/tile').default;
var ol_source_OSM = require('ol/source/osm').default;
var ol_View = require('ol/view').default;
You will also need an ndex.html file that will use your bundle. Here is a simple
example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Using Browserify with OpenLayers</title>
<style>
#map {
width: 400px;
height: 250px;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="bundle.js"></script>
</body>
</html>
Creating a bundle
With simple scripts you can introduce the commands npm run build and npm
start to manually build your bundle and watch for changes, respectively. Add the
following to the script section in package.json:
"scripts": {
"start": "watchify index.js -g cssify --outfile bundle.js & http-server",
"build": "browserify -g cssify index.js | uglifyjs --compress --output
bundle.js"
}
Now to test your application open http://localhost:8080/ in your browser. watchify will
update bundle.js whenever you change something. You simply need to reload the
page in your browser to see the changes.
$ npm start
Note that bundle.js will contain your application code and all dependencies used in
your application. From OpenLayers, it only contains the required components.
$ mkdir openlayers-closure-application
$ cd openlayers-closure-application
Our application will be a node application, and the openlayers and closure-
util node packages will be downloaded from the node package registry using
the npm command line tool.
So we're going to create a package.json file for the application, which every node
application includes. This file basically includes metadata for the application.
Create the application's package.json file:
$ npm init
You can pretty much use the default answers to the questions npm init asks you.
Now install OpenLayers using:
$ ./node_modules/openlayers/node_modules/.bin/closure-util
command
update-compiler Update the Compiler
update-library Update the Library
update Update both the Library and the Compiler
build Build with Closure Compiler
serve Start the development server
Options:
-l LEVEL, --loglevel LEVEL Log level [info]
$ mkdir src
goog.provide('app');
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.layer.Tile');
goog.require('ol.source.OSM');
/**
* @type {ol.Map}
*/
app.map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: [0, 0],
zoom: 4
})
});
{
"lib": [
"node_modules/openlayers/src/**/*.js",
"node_modules/openlayers/build/ol.ext/**/*.js",
"src/**/*.js"
],
"compile": {
"closure_entry_point": "app",
"externs": [
"node_modules/openlayers/externs/bingmaps.js",
"node_modules/openlayers/externs/cartodb.js",
"node_modules/openlayers/externs/closure-compiler.js",
"node_modules/openlayers/externs/esrijson.js",
"node_modules/openlayers/externs/geojson.js",
"node_modules/openlayers/externs/proj4js.js",
"node_modules/openlayers/externs/tilejson.js",
"node_modules/openlayers/externs/topojson.js"
],
"define": [
"ol.ENABLE_WEBGL=false"
],
"js": [
"node_modules/openlayers/src/ol/typedefs.js",
"node_modules/openlayers/externs/olx.js",
"node_modules/openlayers/externs/oli.js"
],
"extra_annotation_name": [
"api", "observable"
],
"rewrite_polyfills": "false",
"compilation_level": "ADVANCED",
"warning_level": "VERBOSE",
"output_wrapper": "(function(){%output%})();",
"use_types_for_optimization": true
}
}
Create a config.json file with the above content at the root of the application
directory.
We can now use closure-util to compile the code:
$ ./node_modules/openlayers/node_modules/.bin/closure-util build
config.json app.js
The resulting app.js file, which you can view in your editor if you're curious, includes
a minified version of the application code (main.js), and the OpenLayers code that
the application code uses.
Here is a version of config.json with more compilation checks enabled:
{
"lib": [
"node_modules/openlayers/src/**/*.js",
"node_modules/openlayers/build/ol.ext/**/*.js",
"src/**/*.js"
],
"compile": {
"closure_entry_point": "app",
"externs": [
"node_modules/openlayers/externs/bingmaps.js",
"node_modules/openlayers/externs/cartodb.js",
"node_modules/openlayers/externs/closure-compiler.js",
"node_modules/openlayers/externs/esrijson.js",
"node_modules/openlayers/externs/geojson.js",
"node_modules/openlayers/externs/proj4js.js",
"node_modules/openlayers/externs/tilejson.js",
"node_modules/openlayers/externs/topojson.js"
],
"define": [
"ol.ENABLE_WEBGL=false"
],
"js": [
"node_modules/openlayers/src/ol/typedefs.js",
"node_modules/openlayers/externs/olx.js",
"node_modules/openlayers/externs/oli.js"
],
"jscomp_error": [
"*"
],
"jscomp_off": [
"unknownDefines",
"lintChecks",
"analyzerChecks"
],
"extra_annotation_name": [
"api", "observable"
],
"compilation_level": "ADVANCED",
"warning_level": "VERBOSE",
"output_wrapper": "(function(){%output%})();",
"use_types_for_optimization": true
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no,
width=device-width">
<link rel="stylesheet" href="node_modules/openlayers/css/ol.css"
type="text/css">
<title>Simple example</title>
<style>
#map {
width: 600px;
height: 400px;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="app.js" type="text/javascript"></script>
</body>
</html>
Note that the page includes a script tag referencing the app.js file, which is the file
resulting from the compilation.
You are done!
$ ./node_modules/openlayers/node_modules/.bin/closure-util serve
config.json
Now change the script tag to the following in the index.html file:
Reload the page in your browser and you should see that scripts are now loaded
individually, making debugging much easier.
Raster Reprojection
OpenLayers has an ability to display raster data from WMS, WMTS, static images
and many other sources in a different coordinate system than delivered from the
server. Transformation of the map projections of the image happens directly in a web
browser. The view in any Proj4js supported coordinate reference system is possible
and previously incompatible layers can now be combined and overlaid.
Usage
The API usage is very simple. Just specify proper projection (using EPSG code)
on ol.View:
var map = new ol.Map({
target: 'map',
view: new ol.View({
projection: 'EPSG:3857', //HERE IS THE VIEW PROJECTION
center: [0, 0],
zoom: 2
}),
layers: [
new ol.layer.Tile({
source: new ol.source.TileWMS({
projection: 'EPSG:4326', //HERE IS THE DATA SOURCE PROJECTION
url: 'http://demo.boundlessgeo.com/geoserver/wms',
params: {
'LAYERS': 'ne:NE1_HR_LC_SR_W_DR'
}
})
})
]
});
Examples
Raster reprojection demo
OpenStreetMap to WGS84 reprojection
Reprojection with EPSG.io database search
Image reprojection
Custom projection
The easiest way to use a custom projection is to add the Proj4js library to your
project and then define the projection using a proj4 definition string. Following
example shows definition of a British National Grid:
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.4.4/proj4.js"></scrip
t>
proj4.defs('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 '
+
'+x_0=400000 +y_0=-100000 +ellps=airy ' +
'+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' +
'+units=m +no_defs');
var proj27700 = ol.proj.get('EPSG:27700');
proj27700.setExtent([0, 0, 700000, 1300000]);
map.setView(new ol.View({
projection: 'EPSG:27700',
center: [400000, 650000],
zoom: 4
}));
How it works
The reprojection process is based on triangles -- the target raster is divided into a
limited number of triangles with vertices transformed using ol.proj capabilities
(proj4js is usually utilized to define custom transformations). The reprojection of
pixels inside the triangle is approximated with an affine transformation (with rendering
hardware-accelerated by the canvas 2d context):
This way we can support a wide range of projections from proj4js (or even custom
transformation functions) on almost any hardware (with canvas 2d support) with a
relatively small number of actual transformation calculations.
The precision of the reprojection is then limited by the number of triangles.
The reprojection process preserves transparency on the raster data supplied from
the source (png or gif) and the gaps and no-data pixels generated by reprojection are
automatically transparent.
Dynamic triangulation
The above image above shows a noticeable error (especially on the edges) when the
original image (left; EPSG:27700) is transformed with only a limited number of
triangles (right; EPSG:3857). The error can be minimized by increasing the number
of triangles used.
Since some transformations require a more detail triangulation network, the dynamic
triangulation process automatically measures reprojection error and iteratively
subdivides to meet a specific error threshold:
For debugging, rendering of the reprojection edges can be enabled
by ol.source.TileImage#setRenderReprojectionEdges(true).
Advanced
Disabling reprojection
In case you are creating a custom build of OpenLayers and do not need the
reprojection code, you can reduce the build size by
setting ol.ENABLE_RASTER_REPROJECTION to false, which completely disables the
reprojection support. See Custom builds tutorial on how to do this.
Resolution calculation
When determining source tiles to load, the ideal source resolution needs to be
calculated. The ol.reproj.calculateSourceResolution(sourceProj, targetProj,
targetCenter, targetResolution) function calculates the ideal value in order to
achieve pixel mapping as close as possible to 1:1 during reprojection, which is then
used to select proper zoom level from the source.
It is, however, generally not practical to use the same source zoom level for the
whole target zoom level -- different projections can have significantly different
resolutions in different parts of the world (e.g. polar regions in EPSG:3857 vs
EPSG:4326) and enforcing a single resolution for the whole zoom level would result
in some tiles being scaled up/down, possibly requiring a huge number of source tiles
to be loaded. Therefore, the resolution mapping is calculated separately for each
reprojected tile (in the middle of the tile extent).