Merge pull request #13156 from tschaub/tile-range
Ensure that tile range covers all pixels
This commit is contained in:
@@ -294,7 +294,11 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer {
|
||||
const tileSource = tileLayer.getSource();
|
||||
const tileGrid = tileSource.getTileGridForProjection(viewState.projection);
|
||||
const tileTextureCache = this.tileTextureCache_;
|
||||
const tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z);
|
||||
const tileRange = tileGrid.getTileRangeForExtentAndZ(
|
||||
extent,
|
||||
z,
|
||||
this.tempTileRange_
|
||||
);
|
||||
|
||||
const tileSourceKey = getUid(tileSource);
|
||||
if (!(tileSourceKey in frameState.wantedTiles)) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import TileRange, {
|
||||
} from '../TileRange.js';
|
||||
import {DEFAULT_TILE_SIZE} from './common.js';
|
||||
import {assert} from '../asserts.js';
|
||||
import {clamp} from '../math.js';
|
||||
import {ceil, clamp, floor} from '../math.js';
|
||||
import {createOrUpdate, getTopLeft} from '../extent.js';
|
||||
import {createOrUpdate as createOrUpdateTileCoord} from '../tilecoord.js';
|
||||
import {isSorted, linearFindNearest} from '../array.js';
|
||||
@@ -18,6 +18,12 @@ import {toSize} from '../size.js';
|
||||
*/
|
||||
const tmpTileCoord = [0, 0, 0];
|
||||
|
||||
/**
|
||||
* Number of decimal digits to consider in integer values when rounding.
|
||||
* @type {number}
|
||||
*/
|
||||
const DECIMALS = 5;
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("../extent.js").Extent} [extent] Extent for the tile grid. No tiles outside this
|
||||
@@ -520,19 +526,15 @@ class TileGrid {
|
||||
const origin = this.getOrigin(z);
|
||||
const tileSize = toSize(this.getTileSize(z), this.tmpSize_);
|
||||
|
||||
const adjustX = reverseIntersectionPolicy ? 0.5 : 0;
|
||||
const adjustY = reverseIntersectionPolicy ? 0.5 : 0;
|
||||
const xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX);
|
||||
const yFromOrigin = Math.floor((origin[1] - y) / resolution + adjustY);
|
||||
let tileCoordX = (scale * xFromOrigin) / tileSize[0];
|
||||
let tileCoordY = (scale * yFromOrigin) / tileSize[1];
|
||||
let tileCoordX = (scale * (x - origin[0])) / resolution / tileSize[0];
|
||||
let tileCoordY = (scale * (origin[1] - y)) / resolution / tileSize[1];
|
||||
|
||||
if (reverseIntersectionPolicy) {
|
||||
tileCoordX = Math.ceil(tileCoordX) - 1;
|
||||
tileCoordY = Math.ceil(tileCoordY) - 1;
|
||||
tileCoordX = ceil(tileCoordX, DECIMALS) - 1;
|
||||
tileCoordY = ceil(tileCoordY, DECIMALS) - 1;
|
||||
} else {
|
||||
tileCoordX = Math.floor(tileCoordX);
|
||||
tileCoordY = Math.floor(tileCoordY);
|
||||
tileCoordX = floor(tileCoordX, DECIMALS);
|
||||
tileCoordY = floor(tileCoordY, DECIMALS);
|
||||
}
|
||||
|
||||
return createOrUpdateTileCoord(z, tileCoordX, tileCoordY, opt_tileCoord);
|
||||
@@ -558,19 +560,15 @@ class TileGrid {
|
||||
const resolution = this.getResolution(z);
|
||||
const tileSize = toSize(this.getTileSize(z), this.tmpSize_);
|
||||
|
||||
const adjustX = reverseIntersectionPolicy ? 0.5 : 0;
|
||||
const adjustY = reverseIntersectionPolicy ? 0.5 : 0;
|
||||
const xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX);
|
||||
const yFromOrigin = Math.floor((origin[1] - y) / resolution + adjustY);
|
||||
let tileCoordX = xFromOrigin / tileSize[0];
|
||||
let tileCoordY = yFromOrigin / tileSize[1];
|
||||
let tileCoordX = (x - origin[0]) / resolution / tileSize[0];
|
||||
let tileCoordY = (origin[1] - y) / resolution / tileSize[1];
|
||||
|
||||
if (reverseIntersectionPolicy) {
|
||||
tileCoordX = Math.ceil(tileCoordX) - 1;
|
||||
tileCoordY = Math.ceil(tileCoordY) - 1;
|
||||
tileCoordX = ceil(tileCoordX, DECIMALS) - 1;
|
||||
tileCoordY = ceil(tileCoordY, DECIMALS) - 1;
|
||||
} else {
|
||||
tileCoordX = Math.floor(tileCoordX);
|
||||
tileCoordY = Math.floor(tileCoordY);
|
||||
tileCoordX = floor(tileCoordX, DECIMALS);
|
||||
tileCoordY = floor(tileCoordY, DECIMALS);
|
||||
}
|
||||
|
||||
return createOrUpdateTileCoord(z, tileCoordX, tileCoordY, opt_tileCoord);
|
||||
|
||||
@@ -823,14 +823,14 @@ describe('ol/tilegrid/TileGrid.js', function () {
|
||||
expect(tileCoord[2]).to.eql(1);
|
||||
|
||||
// pixels are top aligned to the origin
|
||||
coordinate = [1280, -2559.999];
|
||||
coordinate = [1280, -2549.999];
|
||||
tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, 10);
|
||||
expect(tileCoord[0]).to.eql(0);
|
||||
expect(tileCoord[1]).to.eql(0);
|
||||
expect(tileCoord[2]).to.eql(0);
|
||||
|
||||
// pixels are left aligned to the origin
|
||||
coordinate = [2559.999, -1280];
|
||||
coordinate = [2549.999, -1280];
|
||||
tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, 10);
|
||||
expect(tileCoord[0]).to.eql(0);
|
||||
expect(tileCoord[1]).to.eql(0);
|
||||
@@ -947,6 +947,44 @@ describe('ol/tilegrid/TileGrid.js', function () {
|
||||
});
|
||||
|
||||
describe('getTileRangeForExtentAndZ', function () {
|
||||
it('includes a tile even if a fraction of a pixel is covered', function () {
|
||||
const tileGrid = new TileGrid({
|
||||
resolutions: [1],
|
||||
origin: [0, 10],
|
||||
tileSize: 10,
|
||||
});
|
||||
|
||||
let tileRange;
|
||||
|
||||
// overlaps to the right
|
||||
tileRange = tileGrid.getTileRangeForExtentAndZ([0, 0, 10.1, 10], 0);
|
||||
expect(tileRange.minX).to.be(0);
|
||||
expect(tileRange.maxX).to.be(1);
|
||||
expect(tileRange.minY).to.be(0);
|
||||
expect(tileRange.maxY).to.be(0);
|
||||
|
||||
// overlaps to the bottom
|
||||
tileRange = tileGrid.getTileRangeForExtentAndZ([0, -0.1, 10, 10], 0);
|
||||
expect(tileRange.minX).to.be(0);
|
||||
expect(tileRange.maxX).to.be(0);
|
||||
expect(tileRange.minY).to.be(0);
|
||||
expect(tileRange.maxY).to.be(1);
|
||||
|
||||
// overlaps to the left
|
||||
tileRange = tileGrid.getTileRangeForExtentAndZ([-0.1, 0, 10, 10], 0);
|
||||
expect(tileRange.minX).to.be(-1);
|
||||
expect(tileRange.maxX).to.be(0);
|
||||
expect(tileRange.minY).to.be(0);
|
||||
expect(tileRange.maxY).to.be(0);
|
||||
|
||||
// overlaps to the top
|
||||
tileRange = tileGrid.getTileRangeForExtentAndZ([0, 0, 10, 10.1], 0);
|
||||
expect(tileRange.minX).to.be(0);
|
||||
expect(tileRange.maxX).to.be(0);
|
||||
expect(tileRange.minY).to.be(-1);
|
||||
expect(tileRange.maxY).to.be(0);
|
||||
});
|
||||
|
||||
it('returns the expected TileRange', function () {
|
||||
const tileGrid = new TileGrid({
|
||||
resolutions: resolutions,
|
||||
|
||||
BIN
test/rendering/cases/webgl-tile-range/expected.png
Normal file
BIN
test/rendering/cases/webgl-tile-range/expected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 952 B |
67
test/rendering/cases/webgl-tile-range/main.js
Normal file
67
test/rendering/cases/webgl-tile-range/main.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import DataTile from '../../../../src/ol/source/DataTile.js';
|
||||
import Map from '../../../../src/ol/Map.js';
|
||||
import TileLayer from '../../../../src/ol/layer/WebGLTile.js';
|
||||
import View from '../../../../src/ol/View.js';
|
||||
import {Projection} from '../../../../src/ol/proj.js';
|
||||
|
||||
const size = 256;
|
||||
const data = new Uint8Array(size * size * 4);
|
||||
for (let row = 0; row < size; ++row) {
|
||||
for (let col = 0; col < size; ++col) {
|
||||
const index = (row * size + col) * 4;
|
||||
data[index] = 0;
|
||||
data[index + 1] = 255;
|
||||
data[index + 2] = 0;
|
||||
data[index + 3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
|
||||
const context = canvas.getContext('2d');
|
||||
context.strokeStyle = 'black';
|
||||
|
||||
function getLabels(z, x, y) {
|
||||
context.clearRect(0, 0, size, size);
|
||||
context.strokeRect(0, 0, size, size);
|
||||
const data = context.getImageData(0, 0, size, size).data;
|
||||
return new Uint8Array(data.buffer);
|
||||
}
|
||||
|
||||
document.getElementById('map').style.background = 'red';
|
||||
|
||||
const projection = new Projection({
|
||||
code: 'pixels',
|
||||
units: 'pixels',
|
||||
extent: [0, 0, 256, 256],
|
||||
});
|
||||
|
||||
new Map({
|
||||
target: 'map',
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new DataTile({
|
||||
projection: projection,
|
||||
maxZoom: 1,
|
||||
tileSize: size,
|
||||
loader: () => data,
|
||||
}),
|
||||
}),
|
||||
new TileLayer({
|
||||
source: new DataTile({
|
||||
projection: projection,
|
||||
loader: getLabels,
|
||||
transition: 0,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
view: new View({
|
||||
projection: projection,
|
||||
center: [127.7, 128.3],
|
||||
zoom: 8,
|
||||
}),
|
||||
});
|
||||
|
||||
render();
|
||||
Reference in New Issue
Block a user