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 layer order with the Drag and Drop API

In the final step, we implement some logic to change the layer order. The code for this example is named ch03_layerorder. As we want to have a convenient and full GUI implementation, we use the HTML5 Drag and Drop API to achieve our task.

This API needs a simple draggable declaration on an element and a set of dragging-related events. The browser needs to know what to do when an element is dragged; when it is over, another element is declared draggable in a case where it is dropped due to such an element. First, we prepare our layer elements by creating a CSS class to give visual feedback when a layer is dragged over another one:

.layer.over {
    border-top: 3px solid black;
}

Next, we extend our createRegistry function to make this process possible:

this.createRegistry = function (layer, buffer) {
    […]
    var _this = this;
    layerDiv.draggable = true;
    layerDiv.addEventListener('dragstart', function (evt) {
        evt.dataTransfer.effectAllowed = 'move';
        evt.dataTransfer.setData('Text', this.id);
    });
    layerDiv.addEventListener('dragenter', function (evt) {
        this.classList.add('over');
    });
    layerDiv.addEventListener('dragleave', function (evt) {
        this.classList.remove('over');
    });
    layerDiv.addEventListener('dragover', function (evt) {
        evt.preventDefault();
        evt.dataTransfer.dropEffect = 'move';
    });

The events that are associated with the Drag and Drop API may need some explanation. The first event is fired when we drag the element. All other events fire when the element acts as a target. Note that only those elements that also bear the draggable attribute are recognized as target elements.

As the main concept behind the API is pure data transfer with the help of DOM elements, we need to define the data we would like to transfer in the dragstart event. As we don't have to deal with data from other sources, the simplest approach for this is to send the element's id as a string. We also notify the browser that we will move the element; therefore, it can assign a move cursor to the dragging process.

Note

We defined our type of transferred data as Text, unlike the general text/plain type definition. Can you guess why? Yes, I can hear you mumbling Internet Explorer, and you are totally right!

The dragenter and dragleave events are quite unequivocal ones. If we drag over a possible target, it will have a wider top border, while if we drag out of it, it returns to its normal state. There is some magic behind the dragover event, though. It has the default behavior of canceling the drop; thus, we have to prevent it. We also have to state that we would like to do this in a completely valid operation, which is in the list of allowed effects. Most of the magic is due to the drop event, though, where we change the order of the layers:

    layerDiv.addEventListener('drop', function (evt) {
        evt.preventDefault();
        this.classList.remove('over');
        var sourceLayerDiv = document.getElementById(evt.dataTransfer.getData('Text'));
        if (sourceLayerDiv !== this) {
            _this.layerContainer.removeChild(sourceLayerDiv);
            _this.layerContainer.insertBefore(sourceLayerDiv, this);
            var htmlArray = [].slice.call(_this.layerContainer.children);
            var index = htmlArray.length - htmlArray.indexOf(sourceLayerDiv) - 1;
            var sourceLayer = _this.getLayerById(sourceLayerDiv.id);
            var layers = _this.map.getLayers().getArray();
            layers.splice(layers.indexOf(sourceLayer), 1);
            layers.splice(index, 0, sourceLayer);
            _this.map.render();
        }
    });

It also has a nasty default behavior in some browsers, which is to redirect based on the given information in the dataTransfer object; therefore, our first task is defusing the bomb. Now we can go on and grab a reference to the dragged element based on id. If the source and its target elements are not the same, we can talk about a valid order change and go on with our logic.

First, we insert the dragged element before the target (that is why we have highlighted the target's top border to indicate the destination). Then, we calculate the layer's position in the layer stack from the array of layer elements. As the layer stack has an inverse order and starts with 0, we use the preceding formula.

Note

Querying the child elements of a node returns an HTML collection, not an array. To check the index of a given element in the collection, though, we need the indexOf method, which is exclusive to arrays. The JavaScript array object's slice method is perfect to convert array-like objects to arrays when called without any arguments. The full call would have been Array.prototype.slice.call, but instead, we used a shortcut.

Finally, we grab a reference to the map's layer collection's internal array, cut it out from its original position, and insert it into its a new position. We also have to call a rendering frame as performing operations on the internal array of an ol.Collection object does not fire any events.

Note

This is the other valid case for using a collection's array instead of the collection's methods. As using the methods would fire a remove and an add event, the layer tree would recreate the layer registry on the top of the stack, crashing our logic.

Now, we only have to solve one final problem. As the elements can be dragged, we can't change the opacity of the layers with the slider because we would drag the entire element. We can solve this by registering two events to the slider. One of them disables dragging when we keep our mouse button on the slider, while the other one reactivates it when we release our mouse button:

    opacityHandler.addEventListener('mousedown', function () {
        layerDiv.draggable = false;
    });
    opacityHandler.addEventListener('mouseup', function () {
        layerDiv.draggable = true;
    });

Tip

The code we've just created is operational but far from perfect. For example, it has a weakness of not letting you place a layer at the bottom of the stack. You can fix this by adding an empty dummy layer element to the bottom of the stack and defining every drag event on it except for dragstart.

If you save the code and look it up in your favorite browser, you can try out our dragging mechanism:

Changing the layer order with the Drag and Drop API