Bring back vector render mode for vector tile layers
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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 Sslect 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>
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
BIN
rendering/cases/layer-vectortile-rendermode-vector/expected.png
Normal file
BIN
rendering/cases/layer-vectortile-rendermode-vector/expected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
31
rendering/cases/layer-vectortile-rendermode-vector/main.js
Normal file
31
rendering/cases/layer-vectortile-rendermode-vector/main.js
Normal 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
|
||||
});
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user