Tile layer rendering with the composite renderer
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 101 KiB |
@@ -2,33 +2,34 @@ import Map from '../../../src/ol/Map.js';
|
|||||||
import View from '../../../src/ol/View.js';
|
import View from '../../../src/ol/View.js';
|
||||||
import {Vector as VectorLayer, Tile as TileLayer} from '../../../src/ol/layer.js';
|
import {Vector as VectorLayer, Tile as TileLayer} from '../../../src/ol/layer.js';
|
||||||
import {Vector as VectorSource, XYZ} from '../../../src/ol/source.js';
|
import {Vector as VectorSource, XYZ} from '../../../src/ol/source.js';
|
||||||
import Point from '../../../src/ol/geom/Point.js';
|
import GeoJSON from '../../../src/ol/format/GeoJSON.js';
|
||||||
import Feature from '../../../src/ol/Feature.js';
|
import {Style, Stroke} from '../../../src/ol/style.js';
|
||||||
import {fromLonLat} from '../../../src/ol/proj.js';
|
|
||||||
|
|
||||||
const center = fromLonLat([-111, 45.7]);
|
|
||||||
|
|
||||||
new Map({
|
new Map({
|
||||||
layers: [
|
layers: [
|
||||||
new TileLayer({
|
new TileLayer({
|
||||||
source: new XYZ({
|
source: new XYZ({
|
||||||
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg'
|
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg',
|
||||||
|
maxZoom: 3
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
new VectorLayer({
|
new VectorLayer({
|
||||||
|
style: new Style({
|
||||||
|
stroke: new Stroke({
|
||||||
|
color: 'rgba(255,255,255,0.5)',
|
||||||
|
width: 0.75
|
||||||
|
})
|
||||||
|
}),
|
||||||
source: new VectorSource({
|
source: new VectorSource({
|
||||||
features: [
|
url: '/data/countries.json',
|
||||||
new Feature(
|
format: new GeoJSON()
|
||||||
new Point(center)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
target: 'map',
|
target: 'map',
|
||||||
view: new View({
|
view: new View({
|
||||||
center: center,
|
center: [0, 0],
|
||||||
zoom: 3
|
zoom: 2
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
181
rendering/data/countries.json
Normal file
181
rendering/data/countries.json
Normal file
File diff suppressed because one or more lines are too long
@@ -4,9 +4,9 @@
|
|||||||
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 ViewHint from '../../ViewHint.js';
|
import {createEmpty, equals, getIntersection, isEmpty} from '../../extent.js';
|
||||||
import {containsExtent, createEmpty, equals, getIntersection, isEmpty} from '../../extent.js';
|
import {createCanvasContext2D} from '../../dom.js';
|
||||||
import IntermediateCanvasRenderer from './IntermediateCanvas.js';
|
import CanvasLayerRenderer from './Layer.js';
|
||||||
import {create as createTransform, compose as composeTransform} from '../../transform.js';
|
import {create as createTransform, compose as composeTransform} from '../../transform.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,15 +14,28 @@ import {create as createTransform, compose as composeTransform} from '../../tran
|
|||||||
* Canvas renderer for tile layers.
|
* Canvas renderer for tile layers.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
class CanvasTileLayerRenderer extends CanvasLayerRenderer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../../layer/Tile.js").default|import("../../layer/VectorTile.js").default} tileLayer Tile layer.
|
* @param {import("../../layer/Tile.js").default|import("../../layer/VectorTile.js").default} tileLayer Tile layer.
|
||||||
* @param {boolean=} opt_noContext Skip the context creation.
|
|
||||||
*/
|
*/
|
||||||
constructor(tileLayer, opt_noContext) {
|
constructor(tileLayer) {
|
||||||
|
super(tileLayer);
|
||||||
|
|
||||||
super(tileLayer, opt_noContext);
|
/**
|
||||||
|
* @protected
|
||||||
|
* @type {CanvasRenderingContext2D}
|
||||||
|
*/
|
||||||
|
this.context = createCanvasContext2D();
|
||||||
|
|
||||||
|
const canvas = this.context.canvas;
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* @type {import("../../transform.js").Transform}
|
||||||
|
*/
|
||||||
|
this.coordinateToCanvasPixelTransform = createTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -125,13 +138,13 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
prepareFrame(frameState, layerState) {
|
prepareFrame(frameState, layerState) {
|
||||||
|
|
||||||
const pixelRatio = frameState.pixelRatio;
|
|
||||||
const size = frameState.size;
|
const size = frameState.size;
|
||||||
const viewState = frameState.viewState;
|
const viewState = frameState.viewState;
|
||||||
const projection = viewState.projection;
|
const projection = viewState.projection;
|
||||||
const viewResolution = viewState.resolution;
|
const viewResolution = viewState.resolution;
|
||||||
const viewCenter = viewState.center;
|
const viewCenter = viewState.center;
|
||||||
|
const rotation = viewState.rotation;
|
||||||
|
const pixelRatio = frameState.pixelRatio;
|
||||||
|
|
||||||
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
|
const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer());
|
||||||
const tileSource = /** @type {import("../../source/Tile.js").default} */ (tileLayer.getSource());
|
const tileSource = /** @type {import("../../source/Tile.js").default} */ (tileLayer.getSource());
|
||||||
@@ -139,7 +152,6 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
|||||||
const tileGrid = tileSource.getTileGridForProjection(projection);
|
const tileGrid = tileSource.getTileGridForProjection(projection);
|
||||||
const z = tileGrid.getZForResolution(viewResolution, this.zDirection);
|
const z = tileGrid.getZForResolution(viewResolution, this.zDirection);
|
||||||
const tileResolution = tileGrid.getResolution(z);
|
const tileResolution = tileGrid.getResolution(z);
|
||||||
let oversampling = Math.round(viewResolution / tileResolution) || 1;
|
|
||||||
let extent = frameState.extent;
|
let extent = frameState.extent;
|
||||||
|
|
||||||
if (layerState.extent !== undefined) {
|
if (layerState.extent !== undefined) {
|
||||||
@@ -149,34 +161,49 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
|||||||
// Return false to prevent the rendering of the layer.
|
// Return false to prevent the rendering of the layer.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// TODO: clip by layer extent
|
||||||
const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
|
|
||||||
const imageExtent = tileGrid.getTileRangeExtent(z, tileRange);
|
|
||||||
|
|
||||||
const tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio);
|
const tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio);
|
||||||
|
|
||||||
|
// desired dimensions of the canvas in pixels
|
||||||
|
let width = Math.round(frameState.size[0] * tilePixelRatio);
|
||||||
|
let height = Math.round(frameState.size[1] * tilePixelRatio);
|
||||||
|
if (tileResolution < viewResolution) {
|
||||||
|
const scale = tileResolution / tileGrid.getResolution(z + 1);
|
||||||
|
width *= scale;
|
||||||
|
height *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotation) {
|
||||||
|
const size = Math.round(Math.sqrt(width * width + height * height));
|
||||||
|
width = height = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dx = tileResolution * width / 2;
|
||||||
|
const dy = tileResolution * height / 2;
|
||||||
|
const canvasExtent = [
|
||||||
|
viewCenter[0] - dx,
|
||||||
|
viewCenter[1] - dy,
|
||||||
|
viewCenter[0] + dx,
|
||||||
|
viewCenter[1] + dy
|
||||||
|
];
|
||||||
|
|
||||||
|
const tileRange = tileGrid.getTileRangeForExtentAndZ(canvasExtent, z);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Object<number, Object<string, import("../../Tile.js").default>>}
|
* @type {Object<number, Object<string, import("../../Tile.js").default>>}
|
||||||
*/
|
*/
|
||||||
const tilesToDrawByZ = {};
|
const tilesToDrawByZ = {};
|
||||||
tilesToDrawByZ[z] = {};
|
tilesToDrawByZ[z] = {};
|
||||||
|
|
||||||
const findLoadedTiles = this.createLoadedTileFinder(
|
const findLoadedTiles = this.createLoadedTileFinder(tileSource, projection, tilesToDrawByZ);
|
||||||
tileSource, projection, tilesToDrawByZ);
|
|
||||||
|
|
||||||
const hints = frameState.viewHints;
|
|
||||||
const animatingOrInteracting = hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING];
|
|
||||||
|
|
||||||
const tmpExtent = this.tmpExtent;
|
const tmpExtent = this.tmpExtent;
|
||||||
const tmpTileRange = this.tmpTileRange_;
|
const tmpTileRange = this.tmpTileRange_;
|
||||||
this.newTiles_ = false;
|
this.newTiles_ = false;
|
||||||
let tile, x, y;
|
for (let x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
for (let y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
const tile = this.getTile(z, x, y, pixelRatio, projection);
|
||||||
if (Date.now() - frameState.time > 16 && animatingOrInteracting) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
tile = this.getTile(z, x, y, pixelRatio, projection);
|
|
||||||
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) {
|
||||||
@@ -192,45 +219,28 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const childTileRange = tileGrid.getTileCoordChildTileRange(
|
const childTileRange = tileGrid.getTileCoordChildTileRange(tile.tileCoord, tmpTileRange, tmpExtent);
|
||||||
tile.tileCoord, tmpTileRange, tmpExtent);
|
|
||||||
let covered = false;
|
let covered = false;
|
||||||
if (childTileRange) {
|
if (childTileRange) {
|
||||||
covered = findLoadedTiles(z + 1, childTileRange);
|
covered = findLoadedTiles(z + 1, childTileRange);
|
||||||
}
|
}
|
||||||
if (!covered) {
|
if (!covered) {
|
||||||
tileGrid.forEachTileCoordParentTileRange(
|
tileGrid.forEachTileCoordParentTileRange(tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
|
||||||
tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling;
|
if (this.newTiles_ || !this.renderedExtent_ || !equals(canvasExtent, this.renderedExtent_)) {
|
||||||
if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && (
|
|
||||||
this.newTiles_ ||
|
|
||||||
!(this.renderedExtent_ && containsExtent(this.renderedExtent_, extent)) ||
|
|
||||||
this.renderedRevision != sourceRevision ||
|
|
||||||
oversampling != this.oversampling_ ||
|
|
||||||
!animatingOrInteracting && renderedResolution != this.renderedResolution
|
|
||||||
)) {
|
|
||||||
|
|
||||||
const context = this.context;
|
const context = this.context;
|
||||||
if (context) {
|
const canvas = context.canvas;
|
||||||
const tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection);
|
if (canvas.width != width || canvas.height != height) {
|
||||||
const width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling);
|
canvas.width = width;
|
||||||
const height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling);
|
canvas.height = height;
|
||||||
const canvas = context.canvas;
|
} else {
|
||||||
if (canvas.width != width || canvas.height != height) {
|
context.clearRect(0, 0, width, height);
|
||||||
this.oversampling_ = oversampling;
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
} else {
|
|
||||||
if (this.renderedExtent_ && !equals(imageExtent, this.renderedExtent_)) {
|
|
||||||
context.clearRect(0, 0, width, height);
|
|
||||||
}
|
|
||||||
oversampling = this.oversampling_;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderedTiles.length = 0;
|
this.renderedTiles.length = 0;
|
||||||
@@ -245,39 +255,43 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
|||||||
return a > b ? 1 : a < b ? -1 : 0;
|
return a > b ? 1 : a < b ? -1 : 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii;
|
|
||||||
let tileExtent, tileGutter, tilesToDraw, w, h;
|
for (let i = 0, ii = zs.length; i < ii; ++i) {
|
||||||
for (i = 0, ii = zs.length; i < ii; ++i) {
|
const currentZ = zs[i];
|
||||||
currentZ = zs[i];
|
const currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
|
||||||
currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection);
|
const currentResolution = tileGrid.getResolution(currentZ);
|
||||||
currentResolution = tileGrid.getResolution(currentZ);
|
const currentScale = currentResolution / tileResolution;
|
||||||
currentScale = currentResolution / tileResolution;
|
const tileGutter = tilePixelRatio * tileSource.getGutterForProjection(projection);
|
||||||
tileGutter = tilePixelRatio * tileSource.getGutterForProjection(projection);
|
const tilesToDraw = tilesToDrawByZ[currentZ];
|
||||||
tilesToDraw = tilesToDrawByZ[currentZ];
|
|
||||||
for (const tileCoordKey in tilesToDraw) {
|
for (const tileCoordKey in tilesToDraw) {
|
||||||
tile = tilesToDraw[tileCoordKey];
|
const tile = tilesToDraw[tileCoordKey];
|
||||||
tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent);
|
const tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent);
|
||||||
x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio / oversampling;
|
const x = (tileExtent[0] - canvasExtent[0]) / tileResolution;
|
||||||
y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling;
|
const y = (canvasExtent[3] - tileExtent[3]) / tileResolution;
|
||||||
w = currentTilePixelSize[0] * currentScale / oversampling;
|
const w = currentTilePixelSize[0] * currentScale;
|
||||||
h = currentTilePixelSize[1] * currentScale / oversampling;
|
const h = currentTilePixelSize[1] * currentScale;
|
||||||
this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ);
|
this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ);
|
||||||
this.renderedTiles.push(tile);
|
this.renderedTiles.push(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.renderedRevision = sourceRevision;
|
this.renderedRevision = sourceRevision;
|
||||||
this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling;
|
this.renderedResolution = tileResolution;
|
||||||
this.renderedExtent_ = imageExtent;
|
this.renderedExtent_ = canvasExtent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scale = this.renderedResolution / viewResolution;
|
const scale = this.renderedResolution / frameState.viewState.resolution;
|
||||||
const transform = composeTransform(this.imageTransform_,
|
const halfWidth = width / 2;
|
||||||
pixelRatio * size[0] / 2, pixelRatio * size[1] / 2,
|
const halfHeight = height / 2;
|
||||||
|
|
||||||
|
const transform = composeTransform(
|
||||||
|
this.imageTransform_,
|
||||||
|
halfWidth, halfHeight,
|
||||||
scale, scale,
|
scale, scale,
|
||||||
0,
|
rotation,
|
||||||
(this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio,
|
-halfWidth, -halfHeight
|
||||||
(viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio);
|
);
|
||||||
|
|
||||||
composeTransform(this.coordinateToCanvasPixelTransform,
|
composeTransform(this.coordinateToCanvasPixelTransform,
|
||||||
pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5],
|
pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5],
|
||||||
pixelRatio / viewResolution, -pixelRatio / viewResolution,
|
pixelRatio / viewResolution, -pixelRatio / viewResolution,
|
||||||
@@ -293,6 +307,35 @@ class CanvasTileLayerRenderer extends IntermediateCanvasRenderer {
|
|||||||
return this.renderedTiles.length > 0;
|
return this.renderedTiles.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
renderFrame(frameState, layerState) {
|
||||||
|
const context = this.context;
|
||||||
|
this.preRender(context, frameState);
|
||||||
|
|
||||||
|
// consider moving work from prepareFrame to here
|
||||||
|
|
||||||
|
this.postRender(context, frameState, layerState);
|
||||||
|
|
||||||
|
const canvas = context.canvas;
|
||||||
|
|
||||||
|
const opacity = layerState.opacity;
|
||||||
|
if (opacity !== canvas.style.opacity) {
|
||||||
|
canvas.style.opacity = opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rotation = frameState.viewState.rotation;
|
||||||
|
const scale = this.renderedResolution / frameState.viewState.resolution;
|
||||||
|
|
||||||
|
const transform = 'rotate(' + rotation + 'rad) scale(' + scale + ')';
|
||||||
|
if (transform !== canvas.style.transform) {
|
||||||
|
canvas.style.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../../Tile.js").default} tile Tile.
|
* @param {import("../../Tile.js").default} tile Tile.
|
||||||
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
* @param {import("../../PluggableMap.js").FrameState} frameState Frame state.
|
||||||
|
|||||||
Reference in New Issue
Block a user