Table of Contents for
Mastering OpenLayers 3

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Mastering OpenLayers 3 by Gábor Farkas Published by Packt Publishing, 2016
  1. Cover
  2. Table of Contents
  3. Mastering OpenLayers 3
  4. Mastering OpenLayers 3
  5. Credits
  6. About the Author
  7. About the Reviewer
  8. www.PacktPub.com
  9. Preface
  10. What you need for this book
  11. Who this book is for
  12. Conventions
  13. Reader feedback
  14. Customer support
  15. 1. Creating Simple Maps with OpenLayers 3
  16. Structure of OpenLayers 3
  17. Building the layout
  18. Using the API documentation
  19. Debugging the code
  20. Summary
  21. 2. Applying Custom Styles
  22. Customizing the default appearance
  23. Styling vector layers
  24. Customizing the appearance with JavaScript
  25. Creating a WebGIS client layout
  26. Summary
  27. 3. Working with Layers
  28. Building a layer tree
  29. Adding layers dynamically
  30. Adding vector layers with the File API
  31. Adding vector layers with a library
  32. Removing layers dynamically
  33. Changing layer attributes
  34. Changing the layer order with the Drag and Drop API
  35. Clearing the message bar
  36. Summary
  37. 4. Using Vector Data
  38. Accessing attributes
  39. Setting attributes
  40. Validating attributes
  41. Creating thematic layers
  42. Saving vector data
  43. Saving with WFS-T
  44. Modifying the geometry
  45. Summary
  46. 5. Creating Responsive Applications with Interactions and Controls
  47. Building the toolbar
  48. Mapping interactions to controls
  49. Building a set of feature selection controls
  50. Adding new vector layers
  51. Building a set of drawing tools
  52. Modifying and snapping to features
  53. Creating new interactions
  54. Building a measuring control
  55. Summary
  56. 6. Controlling the Map – View and Projection
  57. Customizing a view
  58. Constraining a view
  59. Creating a navigation history
  60. Working with extents
  61. Rotating a view
  62. Changing the map's projection
  63. Creating custom animations
  64. Summary
  65. 7. Mastering Renderers
  66. Using different renderers
  67. Creating a WebGL map
  68. Drawing lines and polygons with WebGL
  69. Blending layers
  70. Clipping layers
  71. Exporting a map
  72. Creating a raster calculator
  73. Creating a convolution matrix
  74. Clipping a layer with WebGL
  75. Summary
  76. 8. OpenLayers 3 for Mobile
  77. Responsive styling with CSS
  78. Generating geocaches
  79. Adding device-dependent controls
  80. Vectorizing the mobile version
  81. Making the mobile application interactive
  82. Summary
  83. 9. Tools of the Trade – Integrating Third-Party Applications
  84. Exporting a QGIS project
  85. Importing shapefiles
  86. Spatial analysis with Turf
  87. Spatial analysis with JSTS
  88. 3D rendering with Cesium
  89. Summary
  90. 10. Compiling Custom Builds with Closure
  91. Configuring Node JS
  92. Compiling OpenLayers 3
  93. Bundling an application with OpenLayers 3
  94. Extending OpenLayers 3
  95. Creating rich documentation with JSDoc
  96. Summary
  97. Index

Changing the map's projection

In the next example, called ch06_projection, we close our basic WebGIS client with a final control, which is able to change the map's projection dynamically. First, as we will need the PROJ4JS library for this example, we include it in the HTML file:

<script type="text/javascript" src="../../js/proj4js-2.3.10/proj4.js"></script>

Next, we style the control with CSS:

.notification-bar #projection {
    text-align: center;
}
.notification-bar #projection select {
    background: none;
    border: none;
    outline: 0;
}

The scheme is the same as the one in the previous example. We will strip the natural look of the select element, which will contain the possible projection options. Next, we start building the control by creating two options for the two default projections:

ol.control.Projection = function (opt_options) {
    var options = opt_options || {};
    var _this = this;
    var projSwitcher = document.createElement('select');
    var webMercator = document.createElement('option');
    webMercator.value = 'EPSG:3857';
    webMercator.textContent = 'EPSG:3857';
    projSwitcher.appendChild(webMercator);
    var plateCarree = document.createElement('option');
    plateCarree.value = 'EPSG:4326';
    plateCarree.textContent = 'EPSG:4326';
    projSwitcher.appendChild(plateCarree);

The concept here is that we store the projection's string representation as the option's value, and request the appropriate projection from the library. As HTML elements can be overridden in the browser, further checks might be necessary. Next, we add an event listener to the control element and close our control:

    projSwitcher.addEventListener('change', function (evt) {
        var view = _this.getMap().getView();
        var oldProj = view.getProjection();
        var newProj = ol.proj.get(this.value);
        var newView = new ol.View({
            center: ol.proj.transform(view.getCenter(), oldProj, newProj),
            zoom: view.getZoom(),
            projection: newProj,
            extent: newProj.getExtent()
        });
        _this.getMap().setView(newView);
        _this.getMap().getLayers().forEach(function (layer) {
            _this.changeLayerProjection(layer, oldProj, newProj);
        });
    });
    ol.control.Control.call(this, {
        element: projSwitcher,
        target: options.target
    });
    this.set('element', projSwitcher);
};
ol.inherits(ol.control.Projection, ol.control.Control);

When we change the projection, we simply construct a new view object with the appropriate projection and a transformed center in order to preserve the previous view. We also loop through the layers and apply a method on them, which we will discuss soon. For now, we set the control element's default value to the current projection:

ol.control.Projection.prototype.setMap = function (map) {
    ol.control.Control.prototype.setMap.call(this, map);
    if (map !== null) {
        this.get('element').value = map.getView().getProjection().getCode();
    }
};

As we need to transform vector layers to the new projection and also request new tiles, if available, we will create a changeLayerProjection method, which can deal with every case:

ol.control.Projection.prototype.changeLayerProjection = function (layer, oldProj, newProj) {
    if (layer instanceof ol.layer.Group) {
        layer.getLayers().forEach(function (subLayer) {
            this.changeLayerProjection(subLayer, oldProj, newProj);
        });
    } else if (layer instanceof ol.layer.Tile) {
        var tileLoadFunc = layer.getSource().getTileLoadFunction();
        layer.getSource().setTileLoadFunction(tileLoadFunc);
    } else if (layer instanceof ol.layer.Vector) {
        var features = layer.getSource().getFeatures();
        for (var i = 0; i < features.length; i += 1) {
            features[i].getGeometry().transform(oldProj, newProj);
        }
    }
};

The method itself is recursive, as it needs to deal with grouped layers, too. If it finds one, it simply calls itself on every member of the group.

The second case is the that of the tile layers. WMS servers often offer tiles in multiple projections. However, there is a truly wonderful but also very sneaky feature in OpenLayers 3, called tile cache. If we do not clear the cache, the application renders tiles from the old projection to the new one's tile grid, which yields bad results. As the tile cache is completely hidden from us, our only chance to tackle this problem is to call a method, which clears the cache as a side effect. There are two such methods: setTileLoadFunction and setTileUrlFunction. We save the old function, set it back, and thus clear the cache without any other change in the layer.

The last case is the case of vector layers. This is quite straightforward, as our only task is iterating through the features and transforming their geometries from the old projection to the new one.

Tip

OpenLayers 3 is capable of warping raster layers; you just have to provide a projection parameter to the given image source. Note that if you reset the cache of a tile layer and the server cannot give out tiles in the destination projection, you will end up with a blank layer. Sometimes, blurred and ugly labels are better than nothing.

We also need a method to add projections dynamically to the control. Here's how we can do this:

ol.control.Projection.prototype.addProjection = function (projection) {
    ol.proj.addProjection(projection);
    var projSwitcher = this.get('element');
    var newProjOption = document.createElement('option');
    newProjOption.value = projection.getCode();
    newProjOption.textContent = projection.getCode();
    projSwitcher.appendChild(newProjOption);
};

Note

A good implementation should also have a method that can remove a projection. Note that ol.proj does not have a removeProjection method, though.

Next, we will construct our new control in the init function, create a new projection with PROJ4JS, and add the projection to the control:

var projControl = new ol.control.Projection({
    target: 'projection'
});
proj4.defs('EPSG:3995', '+proj=stere +lat_0=90 +lat_ts=71 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
var polarProj = new ol.proj.Projection({
    code: 'EPSG:3995',
    extent: [-12382000, -12382000, 12382000, 12382000],
    worldExtent: [-180, 60, 180, 90]
});
projControl.addProjection(polarProj);

Tip

Do not confuse PROJ.4 with PROJ4JS. PROJ4JS is a JavaScript port of the PROJ.4 desktop application, and it does not contain every transformation from the original software. For example, it cannot recognize the Robinson projection.

OpenLayers 3 and PROJ4JS are so closely integrated that we do not need to implement any wrapper functions; we can construct an OpenLayers 3 projection object directly from a PROJ4JS definition. PROJ4JS definitions can be made with proj4.defs, which are called with two arguments. The first one represents a name, with which the projection will be saved. The second one is the projection's PROJ.4 definition.

Tip

Wondering how to get definition strings and extents? The database behind http://www.epsg.io offers many definitions along with recommended extents. Do not rely blindly on those extents, though. They can fail, especially when the projection is not cylindrical.

Finally, we add the control to our map:

var map = new ol.Map({
    […]
    controls: [
        […]
        projControl
    […]
});

If you save the code and look it up, you can try out our new projection. Note that the Arctic Polar Stereographic projection's validity extent only covers the northern hemisphere. For features with lower latitudes, the distortion will be ludicrously high.

This is the better case, though, as exceeding some projections' validity extent results in an erroneous transform, destroying the feature's geometry. To make your application more stable, you shouldn't leave geometry handling entirely to the library.

Changing the map's projection