Merge pull request #10309 from ahocevar/vectortile-rendermode-vector

Bring back vector render mode for vector tile layers
This commit is contained in:
Andreas Hocevar
2019-11-29 11:51:38 +01:00
committed by GitHub
9 changed files with 116 additions and 31 deletions

View File

@@ -111,7 +111,7 @@ Features for `updates` must have an id set by the feature reader or `ol.Feature#
### 28
`renderMode` must be `'image'` or `'hybrid'`.
`renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
### 29

View File

@@ -5,7 +5,11 @@ shortdesc: Select features from vector tiles.
docs: >
<p>
Click a rendered vector-tile feature to highlight it on the map. Click on an empty spot (ocean) to reset the selection.
By changing the action type to "Multi Select" you can select multiple features at a time.
By changing the action type to "Multi Select" you can select multiple features at a time. With "Single Select on hover",
features will be higlighted when the pointer is above them.
</p><p>
The selection layer is configured with `renderMode: 'vector'` for better performance on frequent redraws,
i.e. when 'Single select on hover' is selected.
</p>
tags: "vector tiles, selection"
---
@@ -15,5 +19,6 @@ tags: "vector tiles, selection"
<select id="type" class="form-control">
<option value="singleselect" selected>Single Select</option>
<option value="multiselect">Multi Select</option>
<option value="singleselect-hover">Single Select on hover</option>
</select>
</form>

View File

@@ -8,6 +8,25 @@ import {Fill, Stroke, Style} from '../src/ol/style.js';
// lookup for selection objects
let selection = {};
const country = new Style({
stroke: new Stroke({
color: 'gray',
width: 1
}),
fill: new Fill({
color: 'rgba(20,20,20,0.9)'
})
});
const selectedCountry = new Style({
stroke: new Stroke({
color: 'rgba(200,20,20,0.8)',
width: 2
}),
fill: new Fill({
color: 'rgba(200,20,20,0.4)'
})
});
const vtLayer = new VectorTileLayer({
declutter: true,
source: new VectorTileSource({
@@ -18,18 +37,7 @@ const vtLayer = new VectorTileLayer({
url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' +
'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf'
}),
style: function(feature) {
const selected = !!selection[feature.getId()];
return new Style({
stroke: new Stroke({
color: selected ? 'rgba(200,20,20,0.8)' : 'gray',
width: selected ? 2 : 1
}),
fill: new Fill({
color: selected ? 'rgba(200,20,20,0.2)' : 'rgba(20,20,20,0.9)'
})
});
}
style: country
});
const map = new Map({
@@ -39,18 +47,34 @@ const map = new Map({
target: 'map',
view: new View({
center: [0, 0],
zoom: 2
zoom: 2,
multiWorld: true
})
});
// Selection
const selectionLayer = new VectorTileLayer({
map: map,
renderMode: 'vector',
source: vtLayer.getSource(),
style: function(feature) {
if (feature.getId() in selection) {
return selectedCountry;
}
}
});
const selectElement = document.getElementById('type');
map.on('click', function(event) {
map.on(['click', 'pointermove'], function(event) {
if (selectElement.value === 'singleselect-hover' && event.type !== 'pointermove' ||
selectElement.value !== 'singleselect-hover' && event.type === 'pointermove') {
return;
}
vtLayer.getFeatures(event.pixel).then(function(features) {
if (!features.length) {
selection = {};
// force redraw of layer style
vtLayer.setStyle(vtLayer.getStyle());
selectionLayer.changed();
return;
}
const feature = features[0];
@@ -59,14 +83,12 @@ map.on('click', function(event) {
}
const fid = feature.getId();
if (selectElement.value === 'singleselect') {
if (selectElement.value.startsWith('singleselect')) {
selection = {};
}
// add selected feature to lookup
selection[fid] = feature;
// force redraw of layer style
vtLayer.setStyle(vtLayer.getStyle());
selectionLayer.changed();
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@@ -0,0 +1,31 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import VectorTileSource from '../../../src/ol/source/VectorTile.js';
import MVT from '../../../src/ol/format/MVT.js';
import {createXYZ} from '../../../src/ol/tilegrid.js';
import VectorTileLayer from '../../../src/ol/layer/VectorTile.js';
import VectorTileRenderType from '../../../src/ol/layer/VectorTileRenderType.js';
new Map({
layers: [
new VectorTileLayer({
renderMode: VectorTileRenderType.VECTOR,
source: new VectorTileSource({
format: new MVT(),
tileGrid: createXYZ(),
url: '/data/tiles/mapbox-streets-v6/{z}/{x}/{y}.vector.pbf',
transition: 0
})
})
],
target: 'map',
view: new View({
center: [1825927.7316762917, 6143091.089223046],
zoom: 14
})
});
render({
message: 'Vector tile layer renders with vector render mode',
tolerance: 0.01
});

View File

@@ -40,7 +40,9 @@ import {assign} from '../obj.js';
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels are scaled during zoom
* animations. Point symbols and texts are accurately rendered as vectors and can stay upright on
* rotated views.
*
* * `'vector'`: Everything is rendered as vectors. Use this mode for improved performance on vector
* tile layers with only a few rendered features (e.g. for highlighting a subset of features of
* another layer with the same source).
* @property {import("../source/VectorTile.js").default} [source] Source.
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
@@ -92,8 +94,9 @@ class VectorTileLayer extends BaseVectorLayer {
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
assert(renderMode == undefined ||
renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID,
28); // `renderMode` must be `'image'` or `'hybrid'`
renderMode == VectorTileRenderType.HYBRID ||
renderMode == VectorTileRenderType.VECTOR,
28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
/**
* @private

View File

@@ -11,9 +11,14 @@
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
* are scaled during zoom animations. Point symbols and texts are accurately
* rendered as vectors and can stay upright on rotated views.
* * `'vector'`: Everything is rendered as vectors. Use this mode for improved
* performance on vector tile layers with only a few rendered features (e.g.
* for highlighting a subset of features of another layer with the same
* source).
* @api
*/
export default {
IMAGE: 'image',
HYBRID: 'hybrid'
HYBRID: 'hybrid',
VECTOR: 'vector'
};

View File

@@ -33,7 +33,8 @@ import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdet
const IMAGE_REPLAYS = {
'image': [ReplayType.POLYGON, ReplayType.CIRCLE,
ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT],
'hybrid': [ReplayType.POLYGON, ReplayType.LINE_STRING]
'hybrid': [ReplayType.POLYGON, ReplayType.LINE_STRING],
'vector': []
};
@@ -42,7 +43,8 @@ const IMAGE_REPLAYS = {
*/
const VECTOR_REPLAYS = {
'image': [ReplayType.DEFAULT],
'hybrid': [ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT]
'hybrid': [ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT],
'vector': [ReplayType.POLYGON, ReplayType.CIRCLE, ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT, ReplayType.DEFAULT]
};
@@ -156,7 +158,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
tile.wantedResolution = resolution;
}
const render = this.prepareTile(tile, pixelRatio, projection, false);
if (render) {
if (render && this.getLayer().getRenderMode() !== VectorTileRenderType.VECTOR) {
this.renderTileImage_(tile, frameState);
}
}
@@ -167,7 +169,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
* @inheritdoc
*/
isDrawableTile(tile) {
return super.isDrawableTile(tile) && tile.hasContext(this.getLayer());
const layer = this.getLayer();
return super.isDrawableTile(tile) && layer.getRenderMode() === VectorTileRenderType.VECTOR || tile.hasContext(layer);
}
/**
@@ -267,7 +270,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
}
const executorGroupInstructions = builderGroup.finish();
// no need to clip when the render tile is covered by a single source tile
const replayExtent = layer.getDeclutter() && sourceTiles.length === 1 ?
const replayExtent = layer.getRenderMode() !== VectorTileRenderType.VECTOR && layer.getDeclutter() && sourceTiles.length === 1 ?
null :
sharedExtent;
const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution,

View File

@@ -122,6 +122,22 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
spy.restore();
});
it('does not render images for pure vector rendering', function() {
const testLayer = new VectorTileLayer({
renderMode: VectorTileRenderType.VECTOR,
source: source,
style: layerStyle
});
map.removeLayer(layer);
map.addLayer(testLayer);
const spy = sinon.spy(CanvasVectorTileLayerRenderer.prototype,
'renderTileImage_');
map.renderSync();
expect(spy.callCount).to.be(0);
spy.restore();
});
it('renders both replays and images for hybrid rendering', function() {
const spy1 = sinon.spy(CanvasVectorTileLayerRenderer.prototype,
'getRenderTransform');