Files
openlayers/test/browser/spec/ol/source/Tile.test.js
2021-12-27 15:30:48 -07:00

369 lines
11 KiB
JavaScript

import Map from '../../../../../src/ol/Map.js';
import Projection from '../../../../../src/ol/proj/Projection.js';
import Source from '../../../../../src/ol/source/Source.js';
import Tile from '../../../../../src/ol/Tile.js';
import TileDebugSource from '../../../../../src/ol/source/TileDebug.js';
import TileGrid from '../../../../../src/ol/tilegrid/TileGrid.js';
import TileLayer from '../../../../../src/ol/layer/Tile.js';
import TileRange from '../../../../../src/ol/TileRange.js';
import TileSource from '../../../../../src/ol/source/Tile.js';
import View from '../../../../../src/ol/View.js';
import {getKeyZXY} from '../../../../../src/ol/tilecoord.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
/**
* Tile source for tests that uses a EPSG:4326 based grid with 4 resolutions and
* 256x256 tiles.
*
* @param {Object<string, ol.TileState>} tileStates Lookup of tile key to
* tile state.
*/
class MockTile extends TileSource {
constructor(tileStates) {
const tileGrid = new TileGrid({
resolutions: [360 / 256, 180 / 256, 90 / 256, 45 / 256],
origin: [-180, -180],
tileSize: 256,
});
super({
projection: getProjection('EPSG:4326'),
tileGrid: tileGrid,
});
for (const key in tileStates) {
this.tileCache.set(key, new Tile(key.split('/'), tileStates[key]));
}
}
}
MockTile.prototype.getTile = function (z, x, y) {
const key = getKeyZXY(z, x, y);
if (this.tileCache.containsKey(key)) {
return /** @type {!ol.Tile} */ (this.tileCache.get(key));
} else {
const tile = new Tile(key, 0); // IDLE
this.tileCache.set(key, tile);
return tile;
}
};
describe('ol/source/Tile', function () {
describe('constructor', function () {
it('returns a tile source', function () {
const source = new TileSource({
projection: getProjection('EPSG:4326'),
});
expect(source).to.be.a(Source);
expect(source).to.be.a(TileSource);
});
it('sets 0 as initial cache size', function () {
const source = new TileSource({});
expect(source.tileCache.highWaterMark).to.be(0);
});
it('grows the cache', function () {
const source = new TileDebugSource();
const layer = new TileLayer({
source: source,
});
const target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
const map = new Map({
layers: [layer],
view: new View({
center: [0, 0],
zoom: 2,
}),
target: target,
});
map.renderSync();
expect(
source.getTileCacheForProjection(map.getView().getProjection())
.highWaterMark
).to.be(4);
map.setTarget(null);
document.body.removeChild(target);
});
it('sets a custom cache size', function () {
const projection = getProjection('EPSG:4326');
const source = new TileSource({
projection: projection,
cacheSize: 442,
});
expect(source.getTileCacheForProjection(projection).highWaterMark).to.be(
442
);
});
});
describe('#setKey()', function () {
it('sets the source key', function () {
const source = new TileSource({});
expect(source.getKey()).to.equal('');
const key = 'foo';
source.setKey(key);
expect(source.getKey()).to.equal(key);
});
});
describe('#getInterpolate()', function () {
it('is false by default', function () {
const source = new TileSource({});
expect(source.getInterpolate()).to.be(false);
});
it('is true if constructed with interpolate: true', function () {
const source = new TileSource({interpolate: true});
expect(source.getInterpolate()).to.be(true);
});
});
describe('#setKey()', function () {
it('dispatches a change event', function (done) {
const source = new TileSource({});
const key = 'foo';
source.once('change', function () {
done();
});
source.setKey(key);
});
it('does not dispatch change if key does not change', function (done) {
const source = new TileSource({});
const key = 'foo';
source.once('change', function () {
source.once('change', function () {
done(new Error('Unexpected change event after source.setKey()'));
});
setTimeout(function () {
done();
}, 10);
source.setKey(key); // this should not result in a change event
});
source.setKey(key); // this should result in a change event
});
});
describe('#forEachLoadedTile()', function () {
let callback;
beforeEach(function () {
callback = sinon.spy();
});
it('does not call the callback if no tiles are loaded', function () {
const source = new MockTile({});
const grid = source.getTileGrid();
const extent = [-180, -180, 180, 180];
const zoom = 3;
const range = grid.getTileRangeForExtentAndZ(extent, zoom);
source.forEachLoadedTile(source.getProjection(), zoom, range, callback);
expect(callback.callCount).to.be(0);
});
it('does not call getTile() if no tiles are loaded', function () {
const source = new MockTile({});
sinon.spy(source, 'getTile');
const grid = source.getTileGrid();
const extent = [-180, -180, 180, 180];
const zoom = 3;
const range = grid.getTileRangeForExtentAndZ(extent, zoom);
source.forEachLoadedTile(source.getProjection(), zoom, range, callback);
expect(source.getTile.callCount).to.be(0);
source.getTile.restore();
});
it('calls callback for each loaded tile', function () {
const source = new MockTile({
'1/0/0': 2, // LOADED
'1/0/1': 2, // LOADED
'1/1/0': 1, // LOADING,
'1/1/1': 2, // LOADED
});
const zoom = 1;
const range = new TileRange(0, 1, 0, 1);
source.forEachLoadedTile(source.getProjection(), zoom, range, callback);
expect(callback.callCount).to.be(3);
});
it('returns true if range is fully loaded', function () {
// a source with no loaded tiles
const source = new MockTile({
'1/0/0': 2, // LOADED,
'1/0/1': 2, // LOADED,
'1/1/0': 2, // LOADED,
'1/1/1': 2, // LOADED
});
const zoom = 1;
const range = new TileRange(0, 1, 0, 1);
const covered = source.forEachLoadedTile(
source.getProjection(),
zoom,
range,
function () {
return true;
}
);
expect(covered).to.be(true);
});
it('returns false if range is not fully loaded', function () {
// a source with no loaded tiles
const source = new MockTile({
'1/0/0': 2, // LOADED,
'1/0/1': 2, // LOADED,
'1/1/0': 1, // LOADING,
'1/1/1': 2, // LOADED
});
const zoom = 1;
const range = new TileRange(0, 1, 0, 1);
const covered = source.forEachLoadedTile(
source.getProjection(),
zoom,
range,
function () {
return true;
}
);
expect(covered).to.be(false);
});
it('allows callback to override loaded check', function () {
// a source with no loaded tiles
const source = new MockTile({
'1/0/0': 2, // LOADED,
'1/0/1': 2, // LOADED,
'1/1/0': 2, // LOADED,
'1/1/1': 2, // LOADED
});
const zoom = 1;
const range = new TileRange(0, 1, 0, 1);
const covered = source.forEachLoadedTile(
source.getProjection(),
zoom,
range,
function () {
return false;
}
);
expect(covered).to.be(false);
});
});
describe('#getTileCoordForTileUrlFunction()', function () {
it('returns the expected tile coordinate - {wrapX: true}', function () {
const tileSource = new TileSource({
projection: 'EPSG:3857',
wrapX: true,
});
let tileCoord = tileSource.getTileCoordForTileUrlFunction([6, -31, 22]);
expect(tileCoord).to.eql([6, 33, 22]);
tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 33, 22]);
expect(tileCoord).to.eql([6, 33, 22]);
tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 97, 22]);
expect(tileCoord).to.eql([6, 33, 22]);
});
it('returns the expected tile coordinate - {wrapX: false}', function () {
const tileSource = new TileSource({
projection: 'EPSG:3857',
wrapX: false,
});
let tileCoord = tileSource.getTileCoordForTileUrlFunction([6, -31, 22]);
expect(tileCoord).to.eql(null);
tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 33, 22]);
expect(tileCoord).to.eql([6, 33, 22]);
tileCoord = tileSource.getTileCoordForTileUrlFunction([6, 97, 22]);
expect(tileCoord).to.eql(null);
});
it('works with wrapX and custom projection without extent', function () {
const tileSource = new TileSource({
projection: new Projection({
code: 'foo',
global: true,
units: 'm',
}),
wrapX: true,
});
const tileCoord = tileSource.getTileCoordForTileUrlFunction([6, -31, 22]);
expect(tileCoord).to.eql([6, 33, 22]);
});
});
describe('#refresh()', function () {
it('checks clearing of internal state', function () {
// create a source with one loaded tile
const source = new MockTile({
'1/0/0': 2, // LOADED
});
// check the loaded tile is there
const tile = source.getTile(1, 0, 0);
expect(tile).to.be.a(Tile);
// check tile cache is filled
expect(source.tileCache.getCount()).to.eql(1);
// refresh the source
source.refresh();
// check tile cache after refresh (should be empty)
expect(source.tileCache.getCount()).to.eql(0);
});
});
});
describe('MockTile', function () {
describe('constructor', function () {
it('creates a tile source', function () {
const source = new MockTile({});
expect(source).to.be.a(TileSource);
expect(source).to.be.a(MockTile);
});
});
describe('#getTile()', function () {
it('returns a tile with state based on constructor arg', function () {
const source = new MockTile({
'0/0/0': 2, // LOADED,
'1/0/0': 2, // LOADED
});
let tile;
// check a loaded tile
tile = source.getTile(0, 0, 0);
expect(tile).to.be.a(Tile);
expect(tile.state).to.be(2); // LOADED
// check a tile that is not loaded
tile = source.getTile(1, 0, -1);
expect(tile).to.be.a(Tile);
expect(tile.state).to.be(0); // IDLE
// check another loaded tile
tile = source.getTile(1, 0, 0);
expect(tile).to.be.a(Tile);
expect(tile.state).to.be(2); // LOADED
});
});
});