Merge pull request #9357 from ahocevar/vectortile-view-resolution-2
Render vector tiles at the view resolution
This commit is contained in:
@@ -12,8 +12,10 @@ import {createCanvasContext2D} from './dom.js';
|
|||||||
* @property {boolean} dirty
|
* @property {boolean} dirty
|
||||||
* @property {null|import("./render.js").OrderFunction} renderedRenderOrder
|
* @property {null|import("./render.js").OrderFunction} renderedRenderOrder
|
||||||
* @property {number} renderedTileRevision
|
* @property {number} renderedTileRevision
|
||||||
|
* @property {number} renderedResolution
|
||||||
* @property {number} renderedRevision
|
* @property {number} renderedRevision
|
||||||
* @property {number} renderedZ
|
* @property {number} renderedZ
|
||||||
|
* @property {number} renderedTileResolution
|
||||||
* @property {number} renderedTileZ
|
* @property {number} renderedTileZ
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -64,6 +66,11 @@ class VectorRenderTile extends Tile {
|
|||||||
*/
|
*/
|
||||||
this.replayState_ = {};
|
this.replayState_ = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.wantedResolution;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {!function(import("./VectorRenderTile.js").default):Array<import("./VectorTile.js").default>}
|
* @type {!function(import("./VectorRenderTile.js").default):Array<import("./VectorTile.js").default>}
|
||||||
*/
|
*/
|
||||||
@@ -156,7 +163,9 @@ class VectorRenderTile extends Tile {
|
|||||||
this.replayState_[key] = {
|
this.replayState_[key] = {
|
||||||
dirty: false,
|
dirty: false,
|
||||||
renderedRenderOrder: null,
|
renderedRenderOrder: null,
|
||||||
|
renderedResolution: NaN,
|
||||||
renderedRevision: -1,
|
renderedRevision: -1,
|
||||||
|
renderedTileResolution: NaN,
|
||||||
renderedTileRevision: -1,
|
renderedTileRevision: -1,
|
||||||
renderedZ: -1,
|
renderedZ: -1,
|
||||||
renderedTileZ: -1
|
renderedTileZ: -1
|
||||||
@@ -167,10 +176,9 @@ class VectorRenderTile extends Tile {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
* @return {Array<import("./VectorTile.js").default>} Source tiles for this tile.
|
|
||||||
*/
|
*/
|
||||||
load() {
|
load() {
|
||||||
return this.getSourceTiles_(this);
|
this.getSourceTiles_(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {lineStringLength} from '../../geom/flat/length.js';
|
|||||||
import {drawTextOnPath} from '../../geom/flat/textpath.js';
|
import {drawTextOnPath} from '../../geom/flat/textpath.js';
|
||||||
import {transform2D} from '../../geom/flat/transform.js';
|
import {transform2D} from '../../geom/flat/transform.js';
|
||||||
import {isEmpty} from '../../obj.js';
|
import {isEmpty} from '../../obj.js';
|
||||||
import {drawImage, resetTransform, defaultPadding, defaultTextBaseline} from '../canvas.js';
|
import {drawImage, defaultPadding, defaultTextBaseline} from '../canvas.js';
|
||||||
import CanvasInstruction from './Instruction.js';
|
import CanvasInstruction from './Instruction.js';
|
||||||
import {TEXT_ALIGN} from './TextBuilder.js';
|
import {TEXT_ALIGN} from './TextBuilder.js';
|
||||||
import {
|
import {
|
||||||
@@ -382,12 +382,13 @@ class Executor extends Disposable {
|
|||||||
if (this.alignFill_) {
|
if (this.alignFill_) {
|
||||||
const origin = applyTransform(this.renderedTransform_, [0, 0]);
|
const origin = applyTransform(this.renderedTransform_, [0, 0]);
|
||||||
const repeatSize = 512 * this.pixelRatio;
|
const repeatSize = 512 * this.pixelRatio;
|
||||||
|
context.save();
|
||||||
context.translate(origin[0] % repeatSize, origin[1] % repeatSize);
|
context.translate(origin[0] % repeatSize, origin[1] % repeatSize);
|
||||||
context.rotate(this.viewRotation_);
|
context.rotate(this.viewRotation_);
|
||||||
}
|
}
|
||||||
context.fill();
|
context.fill();
|
||||||
if (this.alignFill_) {
|
if (this.alignFill_) {
|
||||||
context.setTransform.apply(context, resetTransform);
|
context.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import {getUid} from '../../util.js';
|
import {getUid} from '../../util.js';
|
||||||
import TileRange from '../../TileRange.js';
|
import TileRange from '../../TileRange.js';
|
||||||
import TileState from '../../TileState.js';
|
import TileState from '../../TileState.js';
|
||||||
import {createEmpty, getIntersection, getTopLeft} from '../../extent.js';
|
import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js';
|
||||||
import CanvasLayerRenderer from './Layer.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
|
import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
|
||||||
|
|
||||||
@@ -21,6 +21,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
constructor(tileLayer) {
|
constructor(tileLayer) {
|
||||||
super(tileLayer);
|
super(tileLayer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rendered extent has changed since the previous `renderFrame()` call
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this.extentChanged = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {import("../../extent.js").Extent}
|
* @type {import("../../extent.js").Extent}
|
||||||
@@ -82,11 +88,12 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
* @param {number} z Tile coordinate z.
|
* @param {number} z Tile coordinate z.
|
||||||
* @param {number} x Tile coordinate x.
|
* @param {number} x Tile coordinate x.
|
||||||
* @param {number} y Tile coordinate y.
|
* @param {number} y Tile coordinate y.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
* @param {import("../../proj/Projection.js").default} projection Projection.
|
|
||||||
* @return {!import("../../Tile.js").default} Tile.
|
* @return {!import("../../Tile.js").default} Tile.
|
||||||
*/
|
*/
|
||||||
getTile(z, x, y, pixelRatio, projection) {
|
getTile(z, x, y, frameState) {
|
||||||
|
const pixelRatio = frameState.pixelRatio;
|
||||||
|
const projection = frameState.viewState.projection;
|
||||||
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
|
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
|
||||||
const tileSource = tileLayer.getSource();
|
const tileSource = tileLayer.getSource();
|
||||||
let tile = tileSource.getTile(z, x, y, pixelRatio, projection);
|
let tile = tileSource.getTile(z, x, y, pixelRatio, projection);
|
||||||
@@ -187,7 +194,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
this.newTiles_ = false;
|
this.newTiles_ = false;
|
||||||
for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
const tile = this.getTile(z, x, y, pixelRatio, projection);
|
const tile = this.getTile(z, x, y, frameState);
|
||||||
if (this.isDrawableTile(tile)) {
|
if (this.isDrawableTile(tile)) {
|
||||||
const uid = getUid(this);
|
const uid = getUid(this);
|
||||||
if (tile.getState() == TileState.LOADED) {
|
if (tile.getState() == TileState.LOADED) {
|
||||||
@@ -301,6 +308,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
|||||||
|
|
||||||
this.renderedRevision = sourceRevision;
|
this.renderedRevision = sourceRevision;
|
||||||
this.renderedResolution = tileResolution;
|
this.renderedResolution = tileResolution;
|
||||||
|
this.extentChanged = !this.renderedExtent_ || !equals(this.renderedExtent_, canvasExtent);
|
||||||
this.renderedExtent_ = canvasExtent;
|
this.renderedExtent_ = canvasExtent;
|
||||||
|
|
||||||
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
|
this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
makeInverse
|
makeInverse
|
||||||
} from '../../transform.js';
|
} from '../../transform.js';
|
||||||
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
||||||
import {clear} from '../../obj.js';
|
import {clear, isEmpty} from '../../obj.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,15 +179,25 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
getTile(z, x, y, pixelRatio, projection) {
|
getTile(z, x, y, frameState) {
|
||||||
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection));
|
const tile = /** @type {import("../../VectorRenderTile.js").default} */ (super.getTile(z, x, y, frameState));
|
||||||
|
const pixelRatio = frameState.pixelRatio;
|
||||||
|
const viewState = frameState.viewState;
|
||||||
|
const resolution = viewState.resolution;
|
||||||
|
const projection = viewState.projection;
|
||||||
if (tile.getState() < TileState.LOADED) {
|
if (tile.getState() < TileState.LOADED) {
|
||||||
|
tile.wantedResolution = resolution;
|
||||||
const tileUid = getUid(tile);
|
const tileUid = getUid(tile);
|
||||||
if (!(tileUid in this.tileListenerKeys_)) {
|
if (!(tileUid in this.tileListenerKeys_)) {
|
||||||
const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection));
|
const listenerKey = listen(tile, EventType.CHANGE, this.prepareTile.bind(this, tile, pixelRatio, projection));
|
||||||
this.tileListenerKeys_[tileUid] = listenerKey;
|
this.tileListenerKeys_[tileUid] = listenerKey;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const viewHints = frameState.viewHints;
|
||||||
|
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
|
||||||
|
if (hifi || !tile.wantedResolution) {
|
||||||
|
tile.wantedResolution = resolution;
|
||||||
|
}
|
||||||
this.prepareTile(tile, pixelRatio, projection);
|
this.prepareTile(tile, pixelRatio, projection);
|
||||||
}
|
}
|
||||||
return tile;
|
return tile;
|
||||||
@@ -230,8 +240,10 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
const revision = layer.getRevision();
|
const revision = layer.getRevision();
|
||||||
const renderOrder = layer.getRenderOrder() || null;
|
const renderOrder = layer.getRenderOrder() || null;
|
||||||
|
|
||||||
|
const resolution = tile.wantedResolution;
|
||||||
const builderState = tile.getReplayState(layer);
|
const builderState = tile.getReplayState(layer);
|
||||||
if (!builderState.dirty && builderState.renderedRevision == revision &&
|
if (!builderState.dirty && builderState.renderedResolution === resolution &&
|
||||||
|
builderState.renderedRevision == revision &&
|
||||||
builderState.renderedRenderOrder == renderOrder && builderState.renderedZ === tile.sourceZ) {
|
builderState.renderedRenderOrder == renderOrder && builderState.renderedZ === tile.sourceZ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -239,11 +251,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
const source = layer.getSource();
|
const source = layer.getSource();
|
||||||
const sourceTileGrid = source.getTileGrid();
|
const sourceTileGrid = source.getTileGrid();
|
||||||
const tileGrid = source.getTileGridForProjection(projection);
|
const tileGrid = source.getTileGridForProjection(projection);
|
||||||
const zoom = tile.tileCoord[0];
|
|
||||||
const resolution = tileGrid.getResolution(zoom);
|
|
||||||
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
||||||
|
|
||||||
const sourceTiles = tile.load();
|
const sourceTiles = source.getSourceTiles(pixelRatio, projection, tile);
|
||||||
const layerUid = getUid(layer);
|
const layerUid = getUid(layer);
|
||||||
const executorGroups = tile.executorGroups[layerUid];
|
const executorGroups = tile.executorGroups[layerUid];
|
||||||
if (executorGroups) {
|
if (executorGroups) {
|
||||||
@@ -306,6 +316,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
builderState.renderedRevision = revision;
|
builderState.renderedRevision = revision;
|
||||||
builderState.renderedZ = tile.sourceZ;
|
builderState.renderedZ = tile.sourceZ;
|
||||||
builderState.renderedRenderOrder = renderOrder;
|
builderState.renderedRenderOrder = renderOrder;
|
||||||
|
builderState.renderedResolution = resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -421,6 +432,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
return this.container_;
|
return this.container_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isEmpty(this.renderTileImageQueue_) && !this.extentChanged) {
|
||||||
|
this.renderTileImages_(hifi, frameState);
|
||||||
|
return this.container_;
|
||||||
|
}
|
||||||
|
|
||||||
const context = this.overlayContext_;
|
const context = this.overlayContext_;
|
||||||
const declutterReplays = layer.getDeclutter() ? {} : null;
|
const declutterReplays = layer.getDeclutter() ? {} : null;
|
||||||
const source = layer.getSource();
|
const source = layer.getSource();
|
||||||
@@ -539,7 +555,11 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
if (this.declutterTree_ && layer.getRenderMode() === VectorTileRenderType.IMAGE) {
|
if (this.declutterTree_ && layer.getRenderMode() === VectorTileRenderType.IMAGE) {
|
||||||
this.declutterTree_.clear();
|
this.declutterTree_.clear();
|
||||||
}
|
}
|
||||||
this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection);
|
const viewState = frameState.viewState;
|
||||||
|
const tileGrid = layer.getSource().getTileGridForProjection(viewState.projection);
|
||||||
|
const tileResolution = tileGrid.getResolution(tile.tileCoord[0]);
|
||||||
|
const renderPixelRatio = frameState.pixelRatio / tile.wantedResolution * tileResolution;
|
||||||
|
this.renderTileImage_(tile, frameState.pixelRatio, renderPixelRatio, viewState.projection);
|
||||||
}
|
}
|
||||||
clear(this.renderTileImageQueue_);
|
clear(this.renderTileImageQueue_);
|
||||||
}
|
}
|
||||||
@@ -582,16 +602,18 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
const replayState = tile.getReplayState(layer);
|
const replayState = tile.getReplayState(layer);
|
||||||
const revision = layer.getRevision();
|
const revision = layer.getRevision();
|
||||||
const sourceZ = tile.sourceZ;
|
const sourceZ = tile.sourceZ;
|
||||||
return replayState.renderedTileRevision !== revision || replayState.renderedTileZ !== sourceZ;
|
const resolution = tile.wantedResolution;
|
||||||
|
return replayState.renderedTileResolution !== resolution || replayState.renderedTileRevision !== revision || replayState.renderedTileZ !== sourceZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../../VectorRenderTile.js").default} tile Tile.
|
* @param {import("../../VectorRenderTile.js").default} tile Tile.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {number} renderPixelRatio Render pixel ratio.
|
||||||
* @param {import("../../proj/Projection.js").default} projection Projection.
|
* @param {import("../../proj/Projection.js").default} projection Projection.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
renderTileImage_(tile, pixelRatio, projection) {
|
renderTileImage_(tile, pixelRatio, renderPixelRatio, projection) {
|
||||||
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
|
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
|
||||||
const replayState = tile.getReplayState(layer);
|
const replayState = tile.getReplayState(layer);
|
||||||
const revision = layer.getRevision();
|
const revision = layer.getRevision();
|
||||||
@@ -607,15 +629,20 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
const size = source.getTilePixelSize(z, pixelRatio, projection);
|
const size = source.getTilePixelSize(z, pixelRatio, projection);
|
||||||
context.canvas.width = size[0];
|
context.canvas.width = size[0];
|
||||||
context.canvas.height = size[1];
|
context.canvas.height = size[1];
|
||||||
|
const canvasTransform = resetTransform(this.tmpTransform_);
|
||||||
|
const renderScale = pixelRatio / renderPixelRatio;
|
||||||
|
scaleTransform(canvasTransform, renderScale, renderScale);
|
||||||
|
context.setTransform.apply(context, canvasTransform);
|
||||||
const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
|
const tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent);
|
||||||
for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
|
for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
|
||||||
const executorGroup = executorGroups[i];
|
const executorGroup = executorGroups[i];
|
||||||
const pixelScale = pixelRatio / resolution;
|
const pixelScale = renderPixelRatio / resolution;
|
||||||
const transform = resetTransform(this.tmpTransform_);
|
const transform = resetTransform(this.tmpTransform_);
|
||||||
scaleTransform(transform, pixelScale, -pixelScale);
|
scaleTransform(transform, pixelScale, -pixelScale);
|
||||||
translateTransform(transform, -tileExtent[0], -tileExtent[3]);
|
translateTransform(transform, -tileExtent[0], -tileExtent[3]);
|
||||||
executorGroup.execute(context, transform, 0, {}, true, IMAGE_REPLAYS[layer.getRenderMode()]);
|
executorGroup.execute(context, transform, 0, {}, true, IMAGE_REPLAYS[layer.getRenderMode()]);
|
||||||
}
|
}
|
||||||
|
replayState.renderedTileResolution = tile.wantedResolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ describe('ol.source.VectorTile', function() {
|
|||||||
tile.load();
|
tile.load();
|
||||||
const key = listen(tile, 'change', function(e) {
|
const key = listen(tile, 'change', function(e) {
|
||||||
if (tile.getState() === TileState.LOADED) {
|
if (tile.getState() === TileState.LOADED) {
|
||||||
const sourceTile = tile.load()[0];
|
const sourceTile = source.getSourceTiles(1, source.getProjection(), tile)[0];
|
||||||
expect(sourceTile.getFeatures().length).to.be.greaterThan(0);
|
expect(sourceTile.getFeatures().length).to.be.greaterThan(0);
|
||||||
unlistenByKey(key);
|
unlistenByKey(key);
|
||||||
done();
|
done();
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ describe('ol.VectorRenderTile', function() {
|
|||||||
const key = listen(tile, EventType.CHANGE, function() {
|
const key = listen(tile, EventType.CHANGE, function() {
|
||||||
if (tile.getState() === TileState.LOADED) {
|
if (tile.getState() === TileState.LOADED) {
|
||||||
unlistenByKey(key);
|
unlistenByKey(key);
|
||||||
const sourceTiles = tile.load();
|
const sourceTiles = source.getSourceTiles(1, source.getProjection(), tile);
|
||||||
expect(sourceTiles.length).to.be(1);
|
expect(sourceTiles.length).to.be(1);
|
||||||
expect(sourceTiles[0].tileCoord).to.eql([0, 16, 9]);
|
expect(sourceTiles[0].tileCoord).to.eql([0, 16, 9]);
|
||||||
done();
|
done();
|
||||||
@@ -126,7 +126,7 @@ describe('ol.VectorRenderTile', function() {
|
|||||||
listenOnce(tile, 'change', function() {
|
listenOnce(tile, 'change', function() {
|
||||||
expect(tile.getState()).to.be(TileState.LOADED);
|
expect(tile.getState()).to.be(TileState.LOADED);
|
||||||
expect(tile.loadingSourceTiles).to.be(0);
|
expect(tile.loadingSourceTiles).to.be(0);
|
||||||
const sourceTiles = tile.load();
|
const sourceTiles = source.getSourceTiles(1, source.getProjection(), tile);
|
||||||
expect(sourceTiles.length).to.be(4);
|
expect(sourceTiles.length).to.be(4);
|
||||||
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
|
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
|
||||||
expect(sourceTiles[i].consumers).to.be(1);
|
expect(sourceTiles[i].consumers).to.be(1);
|
||||||
Reference in New Issue
Block a user