Skip to content

Commit

Permalink
Better feature inspection for the vector tiles excercise
Browse files Browse the repository at this point in the history
  • Loading branch information
ahocevar committed Dec 5, 2024
1 parent a131cf9 commit 0dc3d7a
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 94 deletions.
Binary file modified doc/en/vectortile/interact.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 22 additions & 4 deletions doc/en/vectortile/interact.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
75 changes: 0 additions & 75 deletions src/en/examples/vectortile/bright.html

This file was deleted.

41 changes: 28 additions & 13 deletions src/en/examples/vectortile/interact.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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]
14 changes: 13 additions & 1 deletion src/en/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,20 @@
<head>
<meta charset="utf-8">
<title>OpenLayers</title>
<style>
@import "node_modules/ol/ol.css";
</style>
<style>
html, body, #map-container {
margin: 0;
height: 300px;
width: 500px;
font-family: sans-serif;
}
</style>
</head>
<body>
<div id="map-container"></div>
<script src="./main.js" type="module"></script>
</body>
</html>
</html>
67 changes: 66 additions & 1 deletion src/en/main.js
Original file line number Diff line number Diff line change
@@ -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]

0 comments on commit 0dc3d7a

Please sign in to comment.