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
|
### 28
|
||||||
|
|
||||||
`renderMode` must be `'image'` or `'hybrid'`.
|
`renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
|
||||||
|
|
||||||
### 29
|
### 29
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ shortdesc: Select features from vector tiles.
|
|||||||
docs: >
|
docs: >
|
||||||
<p>
|
<p>
|
||||||
Click a rendered vector-tile feature to highlight it on the map. Click on an empty spot (ocean) to reset the selection.
|
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>
|
</p>
|
||||||
tags: "vector tiles, selection"
|
tags: "vector tiles, selection"
|
||||||
---
|
---
|
||||||
@@ -15,5 +19,6 @@ tags: "vector tiles, selection"
|
|||||||
<select id="type" class="form-control">
|
<select id="type" class="form-control">
|
||||||
<option value="singleselect" selected>Single Select</option>
|
<option value="singleselect" selected>Single Select</option>
|
||||||
<option value="multiselect">Multi Select</option>
|
<option value="multiselect">Multi Select</option>
|
||||||
|
<option value="singleselect-hover">Single Select on hover</option>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -8,6 +8,25 @@ import {Fill, Stroke, Style} from '../src/ol/style.js';
|
|||||||
// lookup for selection objects
|
// lookup for selection objects
|
||||||
let selection = {};
|
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({
|
const vtLayer = new VectorTileLayer({
|
||||||
declutter: true,
|
declutter: true,
|
||||||
source: new VectorTileSource({
|
source: new VectorTileSource({
|
||||||
@@ -18,18 +37,7 @@ const vtLayer = new VectorTileLayer({
|
|||||||
url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' +
|
url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' +
|
||||||
'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf'
|
'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf'
|
||||||
}),
|
}),
|
||||||
style: function(feature) {
|
style: country
|
||||||
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)'
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
@@ -39,18 +47,34 @@ const map = new Map({
|
|||||||
target: 'map',
|
target: 'map',
|
||||||
view: new View({
|
view: new View({
|
||||||
center: [0, 0],
|
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');
|
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) {
|
vtLayer.getFeatures(event.pixel).then(function(features) {
|
||||||
if (!features.length) {
|
if (!features.length) {
|
||||||
selection = {};
|
selection = {};
|
||||||
// force redraw of layer style
|
selectionLayer.changed();
|
||||||
vtLayer.setStyle(vtLayer.getStyle());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const feature = features[0];
|
const feature = features[0];
|
||||||
@@ -59,14 +83,12 @@ map.on('click', function(event) {
|
|||||||
}
|
}
|
||||||
const fid = feature.getId();
|
const fid = feature.getId();
|
||||||
|
|
||||||
if (selectElement.value === 'singleselect') {
|
if (selectElement.value.startsWith('singleselect')) {
|
||||||
selection = {};
|
selection = {};
|
||||||
}
|
}
|
||||||
// add selected feature to lookup
|
// add selected feature to lookup
|
||||||
selection[fid] = feature;
|
selection[fid] = feature;
|
||||||
|
|
||||||
// force redraw of layer style
|
selectionLayer.changed();
|
||||||
vtLayer.setStyle(vtLayer.getStyle());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
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
|
* * `'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
|
* animations. Point symbols and texts are accurately rendered as vectors and can stay upright on
|
||||||
* rotated views.
|
* 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("../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
|
* @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
|
* 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;
|
const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
|
||||||
assert(renderMode == undefined ||
|
assert(renderMode == undefined ||
|
||||||
renderMode == VectorTileRenderType.IMAGE ||
|
renderMode == VectorTileRenderType.IMAGE ||
|
||||||
renderMode == VectorTileRenderType.HYBRID,
|
renderMode == VectorTileRenderType.HYBRID ||
|
||||||
28); // `renderMode` must be `'image'` or `'hybrid'`
|
renderMode == VectorTileRenderType.VECTOR,
|
||||||
|
28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
|
|||||||
@@ -11,9 +11,14 @@
|
|||||||
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
|
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
|
||||||
* are scaled during zoom animations. Point symbols and texts are accurately
|
* are scaled during zoom animations. Point symbols and texts are accurately
|
||||||
* rendered as vectors and can stay upright on rotated views.
|
* 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
|
* @api
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
IMAGE: 'image',
|
IMAGE: 'image',
|
||||||
HYBRID: 'hybrid'
|
HYBRID: 'hybrid',
|
||||||
|
VECTOR: 'vector'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdet
|
|||||||
const IMAGE_REPLAYS = {
|
const IMAGE_REPLAYS = {
|
||||||
'image': [ReplayType.POLYGON, ReplayType.CIRCLE,
|
'image': [ReplayType.POLYGON, ReplayType.CIRCLE,
|
||||||
ReplayType.LINE_STRING, ReplayType.IMAGE, ReplayType.TEXT],
|
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 = {
|
const VECTOR_REPLAYS = {
|
||||||
'image': [ReplayType.DEFAULT],
|
'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;
|
tile.wantedResolution = resolution;
|
||||||
}
|
}
|
||||||
const render = this.prepareTile(tile, pixelRatio, projection, false);
|
const render = this.prepareTile(tile, pixelRatio, projection, false);
|
||||||
if (render) {
|
if (render && this.getLayer().getRenderMode() !== VectorTileRenderType.VECTOR) {
|
||||||
this.renderTileImage_(tile, frameState);
|
this.renderTileImage_(tile, frameState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +169,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
isDrawableTile(tile) {
|
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();
|
const executorGroupInstructions = builderGroup.finish();
|
||||||
// no need to clip when the render tile is covered by a single source tile
|
// 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 :
|
null :
|
||||||
sharedExtent;
|
sharedExtent;
|
||||||
const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution,
|
const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution,
|
||||||
|
|||||||
@@ -122,6 +122,22 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
|
|||||||
spy.restore();
|
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() {
|
it('renders both replays and images for hybrid rendering', function() {
|
||||||
const spy1 = sinon.spy(CanvasVectorTileLayerRenderer.prototype,
|
const spy1 = sinon.spy(CanvasVectorTileLayerRenderer.prototype,
|
||||||
'getRenderTransform');
|
'getRenderTransform');
|
||||||
|
|||||||
Reference in New Issue
Block a user