diff --git a/doc/en/vectortile/interact.gif b/doc/en/vectortile/interact.gif index c2d9ce55..e8bf2e88 100644 Binary files a/doc/en/vectortile/interact.gif and b/doc/en/vectortile/interact.gif differ diff --git a/doc/en/vectortile/interact.md b/doc/en/vectortile/interact.md index 130b1443..7311ba9e 100644 --- a/doc/en/vectortile/interact.md +++ b/doc/en/vectortile/interact.md @@ -4,26 +4,44 @@ The nice thing about vector tiles is that we can interact with features, because For this exercise, we're going to draw a box around the features at the pointer's location when hovering over them. -## Adding a vector layer for displaying bounding boxes +## Adding a vector layer for exploring the data -We will be drawing the bounding boxes of the hovered features on a separate layer. The following imports are needed, and we add them next to the other imports in `main.js`: +We will be highlighting the geometries of the hovered features and show a label with their source layers on a separate vector layer. The following imports are needed, and we add them next to the other imports in `main.js`: [import:'import-layer'](../../../src/en/examples/vectortile/interact.js) -Next, we can create a source for the layer, the layer, and assign it to the map: +Next, we can create a source for the layer, the layer, and add it to the map: [import:'layer'](../../../src/en/examples/vectortile/interact.js) +Here we are using flat styles, which were added to the library not so long ago. As you can see, it is also possible to access feature properties: + +[import:'coalesce'](../../../src/en/examples/vectortile/interact.js) + +This retrieves the value of the `layers` property, which we will be showing as label. The `coalesce` expression allows to provide a fallback value when the first expression does not return a value. This comes in handy because only the point feature we add in the next step will have a `features` property, but not the features copied over from the `VectorTile` layers. + ## Interacting with the map Now it is time to add a `pointermove` listener to the map, which gets all the features at the pointer location and adds their bounding boxes to the layer. We need two additional imports for that: [import:'import-interaction'](../../../src/en/examples/vectortile/interact.js) -Finally we can add the code that clears the current contents of the source and adds the bounding boxes for the features at the pointer location as new content: +Finally we can add the code that clears the current contents of the source and adds the features at the pointer location and a feature for a label listing the layers of these features as new content: [import:'interaction'](../../../src/en/examples/vectortile/interact.js) +Let's dissect this huge code snippet a bit: + +[import:'get-features'](../../../src/en/examples/vectortile/interact.js) + +This gets the features under the pointer, but excluding the `info` layer, which is the layer we're copying vector tile features from. This is because we do not want to get duplicated results in the next rendering cycle. + +[import:'layers-label'](../../../src/en/examples/vectortile/interact.js) + +Here we get the source layers from the features we copied over, and create a new feature with a point at the pointer coordinate, and the list of source layers as `layers` property. + +`Array.from(new Set(layers))` takes the layers array, which may contain duplicate layer entries, turns it into a `Set`, and converts it back to an array. This is an easy way to remove duplicates from an array in JavaScript. + Now when hovering over the map, the result should look like this: ![Hovering over the map](interact.gif) diff --git a/src/en/examples/vectortile/bright.html b/src/en/examples/vectortile/bright.html deleted file mode 100644 index a6a61628..00000000 --- a/src/en/examples/vectortile/bright.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - OpenLayers - - - - -
- - - - - diff --git a/src/en/examples/vectortile/interact.js b/src/en/examples/vectortile/interact.js index c8279d65..b80e017c 100644 --- a/src/en/examples/vectortile/interact.js +++ b/src/en/examples/vectortile/interact.js @@ -4,11 +4,10 @@ import {fromLonLat} from 'ol/proj'; //! [import-layer] import VectorLayer from 'ol/layer/Vector'; import VectorSource from 'ol/source/Vector'; -import {Stroke, Style} from 'ol/style'; //! [import-layer] //! [import-interaction] import Feature from 'ol/Feature'; -import {fromExtent} from 'ol/geom/Polygon'; +import Point from 'ol/geom/Point'; //! [import-interaction] const map = new Map({ @@ -29,23 +28,39 @@ map.addLayer(layer); //! [layer] const source = new VectorSource(); -new VectorLayer({ - map: map, +const info = new VectorLayer({ source: source, - style: new Style({ - stroke: new Stroke({ - color: 'red', - width: 4, - }), - }), + style: { + 'stroke-color': 'red', + 'stroke-width': 4, + //! [coalesce] + 'text-value': ['coalesce', ['get', 'layers'], ''], + //! [coalesce] + 'text-padding': [2, 2, 2, 2], + 'text-offset-y': -15, + 'text-font': '16px sans-serif', + 'text-background-fill-color': 'gray', + }, }); +map.addLayer(info); //! [layer] //! [interaction] map.on('pointermove', function (event) { source.clear(); - map.forEachFeatureAtPixel(event.pixel, function (feature) { - const geometry = feature.getGeometry(); - source.addFeature(new Feature(fromExtent(geometry.getExtent()))); + //! [get-features] + const features = map.getFeaturesAtPixel(event.pixel, { + layerFilter: (layer) => layer !== info, }); + //! [get-features] + source.addFeatures(features); + //! [layers-label] + const layers = features.map((feature) => feature.get('layer')); + source.addFeature( + new Feature({ + geometry: new Point(event.coordinate), + layers: Array.from(new Set(layers)).join(', '), + }) + ); + //! [layers-label] }); //! [interaction] diff --git a/src/en/index.html b/src/en/index.html index b2fad5b2..bd1b77cf 100644 --- a/src/en/index.html +++ b/src/en/index.html @@ -3,8 +3,20 @@ OpenLayers + + +
- \ No newline at end of file + diff --git a/src/en/main.js b/src/en/main.js index 87436931..b80e017c 100644 --- a/src/en/main.js +++ b/src/en/main.js @@ -1 +1,66 @@ -alert('Hello Workshop'); +import {Map, View} from 'ol'; +import {MapboxVectorLayer} from 'ol-mapbox-style'; +import {fromLonLat} from 'ol/proj'; +//! [import-layer] +import VectorLayer from 'ol/layer/Vector'; +import VectorSource from 'ol/source/Vector'; +//! [import-layer] +//! [import-interaction] +import Feature from 'ol/Feature'; +import Point from 'ol/geom/Point'; +//! [import-interaction] + +const map = new Map({ + target: 'map-container', + view: new View({ + center: fromLonLat([0, 0]), + zoom: 2, + }), +}); + +const layer = new MapboxVectorLayer({ + styleUrl: 'https://tiles.openfreemap.org/styles/bright', + // or, instead of the above, try + // styleUrl: 'mapbox://styles/mapbox/bright-v9', + // accessToken: 'Your token from https://mapbox.com/' +}); +map.addLayer(layer); + +//! [layer] +const source = new VectorSource(); +const info = new VectorLayer({ + source: source, + style: { + 'stroke-color': 'red', + 'stroke-width': 4, + //! [coalesce] + 'text-value': ['coalesce', ['get', 'layers'], ''], + //! [coalesce] + 'text-padding': [2, 2, 2, 2], + 'text-offset-y': -15, + 'text-font': '16px sans-serif', + 'text-background-fill-color': 'gray', + }, +}); +map.addLayer(info); +//! [layer] +//! [interaction] +map.on('pointermove', function (event) { + source.clear(); + //! [get-features] + const features = map.getFeaturesAtPixel(event.pixel, { + layerFilter: (layer) => layer !== info, + }); + //! [get-features] + source.addFeatures(features); + //! [layers-label] + const layers = features.map((feature) => feature.get('layer')); + source.addFeature( + new Feature({ + geometry: new Point(event.coordinate), + layers: Array.from(new Set(layers)).join(', '), + }) + ); + //! [layers-label] +}); +//! [interaction]