Organize tests

This commit is contained in:
Tim Schaub
2021-04-27 15:41:14 -07:00
parent 278e355795
commit 490cfabe91
599 changed files with 12374 additions and 1603 deletions
@@ -0,0 +1,95 @@
import BingMaps, {quadKey} from '../../../../../src/ol/source/BingMaps.js';
import {unByKey} from '../../../../../src/ol/Observable.js';
describe('ol.source.BingMaps', function () {
describe('quadKey()', function () {
it('returns expected string', function () {
const tileCoord = [3, 3, 5];
const s = quadKey(tileCoord);
expect(s).to.eql('213');
});
});
describe('#tileUrlFunction()', function () {
let source, tileGrid;
beforeEach(function (done) {
source = new BingMaps({
imagerySet: 'AerialWithLabelsOnDemand',
key: '',
});
const client = new XMLHttpRequest();
client.open('GET', 'spec/ol/data/bing_aerialwithlabels.json', true);
client.onload = function () {
source.handleImageryMetadataResponse(JSON.parse(client.responseText));
};
client.send();
const key = source.on('change', function () {
if (source.getState() === 'ready') {
unByKey(key);
tileGrid = source.getTileGrid();
done();
}
});
});
it('getImagerySet works correctly', function () {
expect(source.getImagerySet()).to.equal('AerialWithLabelsOnDemand');
});
it('getApiKey works correctly', function () {
expect(source.getApiKey()).to.equal('');
});
it('returns the expected URL', function () {
const coordinate = [829330.2064098881, 5933916.615134273];
const projection = source.getProjection();
const regex = /\/tiles\/h(.*)\.jpeg/;
let tileUrl;
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 1),
1,
projection
);
expect(tileUrl.match(regex)[1]).to.equal(quadKey([1, 1, 0]));
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 2),
1,
projection
);
expect(tileUrl.match(regex)[1]).to.equal(quadKey([2, 2, 1]));
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 3),
1,
projection
);
expect(tileUrl.match(regex)[1]).to.equal(quadKey([3, 4, 2]));
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 4),
1,
projection
);
expect(tileUrl.match(regex)[1]).to.equal(quadKey([4, 8, 5]));
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 5),
1,
projection
);
expect(tileUrl.match(regex)[1]).to.equal(quadKey([5, 16, 11]));
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 6),
1,
projection
);
expect(tileUrl.match(regex)[1]).to.equal(quadKey([6, 33, 22]));
});
});
});
@@ -0,0 +1,15 @@
import CartoDB from '../../../../../src/ol/source/CartoDB.js';
import XYZ from '../../../../../src/ol/source/XYZ.js';
describe('ol.source.CartoDB', function () {
describe('constructor', function () {
it('returns a CartoDB source', function () {
const source = new CartoDB({
account: 'documentation',
config: {},
});
expect(source).to.be.a(XYZ);
expect(source).to.be.a(CartoDB);
});
});
});
+119
View File
@@ -0,0 +1,119 @@
import Cluster from '../../../../../src/ol/source/Cluster.js';
import EventType from '../../../../../src/ol/events/EventType.js';
import Feature from '../../../../../src/ol/Feature.js';
import LineString from '../../../../../src/ol/geom/LineString.js';
import Point from '../../../../../src/ol/geom/Point.js';
import Polygon from '../../../../../src/ol/geom/Polygon.js';
import Source from '../../../../../src/ol/source/Source.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.Cluster', function () {
describe('constructor', function () {
it('returns a cluster source', function () {
const source = new Cluster({
projection: getProjection('EPSG:4326'),
source: new VectorSource(),
});
expect(source).to.be.a(Source);
expect(source).to.be.a(Cluster);
expect(source.getDistance()).to.be(20);
});
});
describe('#loadFeatures', function () {
const extent = [-1, -1, 1, 1];
const projection = getProjection('EPSG:3857');
it('clusters a source with point features', function () {
const source = new Cluster({
source: new VectorSource({
features: [
new Feature(new Point([0, 0])),
new Feature(new Point([0, 0])),
],
}),
});
source.loadFeatures(extent, 1, projection);
expect(source.getFeatures().length).to.be(1);
expect(source.getFeatures()[0].get('features').length).to.be(2);
});
it('clusters with a custom geometryFunction', function () {
const source = new Cluster({
geometryFunction: function (feature) {
const geom = feature.getGeometry();
if (geom.getType() == 'Point') {
return geom;
} else if (geom.getType() == 'Polygon') {
return geom.getInteriorPoint();
}
return null;
},
source: new VectorSource({
features: [
new Feature(new Point([0, 0])),
new Feature(
new LineString([
[0, 0],
[1, 1],
])
),
new Feature(
new Polygon([
[
[-1, -1],
[-1, 1],
[1, 1],
[1, -1],
[-1, -1],
],
])
),
],
}),
});
source.loadFeatures(extent, 1, projection);
expect(source.getFeatures().length).to.be(1);
expect(source.getFeatures()[0].get('features').length).to.be(2);
});
});
describe('#setDistance', function () {
it('changes the distance value', function () {
const source = new Cluster({
distance: 100,
source: new VectorSource(),
});
expect(source.getDistance()).to.be(100);
source.setDistance(10);
expect(source.getDistance()).to.be(10);
});
});
});
describe('#setSource', function () {
it('removes the change listener from the old source', function () {
const source = new VectorSource();
const clusterSource = new Cluster({
source: source,
});
expect(source.hasListener(EventType.CHANGE)).to.be(true);
clusterSource.setSource(null);
expect(source.hasListener(EventType.CHANGE)).to.be(false);
});
it('properly removes the previous features', function () {
const source = new Cluster({
source: new VectorSource({
features: [new Feature(new Point([0, 0]))],
}),
});
const projection = getProjection('EPSG:3857');
const extent = [-1, -1, 1, 1];
source.loadFeatures(extent, 1, projection);
expect(source.features.length).to.be(1);
source.setSource(null);
expect(source.features.length).to.be(0);
});
});
+574
View File
@@ -0,0 +1,574 @@
import IIIF from '../../../../../src/ol/source/IIIF.js';
import {DEFAULT_TILE_SIZE} from '../../../../../src/ol/tilegrid/common.js';
import {Versions} from '../../../../../src/ol/format/IIIFInfo.js';
describe('ol.source.IIIF', function () {
const width = 2000,
height = 1500,
size = [width, height],
url = 'http://iiif.test/image-id';
function getMinimalSource() {
return new IIIF({
size: size,
});
}
function getSource(additionalOptions) {
const options = Object.assign(
{},
{
size: size,
url: url,
},
additionalOptions === undefined ? {} : additionalOptions
);
return new IIIF(options);
}
describe('constructor', function () {
it('requires valid size option', function () {
expect(function () {
new IIIF();
}).to.throwException();
expect(function () {
new IIIF({});
}).to.throwException();
expect(function () {
new IIIF({
size: [],
});
}).to.throwException();
expect(function () {
new IIIF({
size: 100,
});
}).to.throwException();
expect(function () {
new IIIF({
size: [100],
});
}).to.throwException();
expect(function () {
new IIIF({
size: [null, 100],
});
}).to.throwException();
expect(function () {
new IIIF({
size: ['very wide', 100],
});
}).to.throwException();
expect(function () {
new IIIF({
size: [0, 100],
});
}).to.throwException();
expect(function () {
new IIIF({
size: [100, null],
});
}).to.throwException();
expect(function () {
new IIIF({
size: [100, 0],
});
}).to.throwException();
expect(function () {
new IIIF({
size: [100, 'not that high'],
});
}).to.throwException();
expect(function () {
new IIIF({
size: [100, 200, 300],
});
}).to.throwException();
let source;
expect(function () {
source = new IIIF({
size: [100, 200],
});
}).to.not.throwException();
expect(source).to.be.a(IIIF);
expect(function () {
getMinimalSource();
}).to.not.throwException();
});
it('uses empty base URL, default quality, jpg format as default', function () {
const tileUrlFunction = getMinimalSource().getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be('full/full/0/default.jpg');
});
it('uses native as default quality for version 1', function () {
const tileUrlFunction = new IIIF({
size: size,
version: Versions.VERSION1,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be('full/full/0/native.jpg');
});
it('corrects non empty base URL if trailing slash is missing', function () {
// missing trailing slash is added
let tileUrlFunction = getSource().getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/full/0/default.jpg'
);
// existent trailing slash isn't doubled
tileUrlFunction = getSource({
url: 'http://iiif.test/other-image-id/',
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/other-image-id/full/full/0/default.jpg'
);
});
});
describe('tileUrlFunction', function () {
it('has only one resolution and one tile if no tiles, resolutions, sizes and supported features are given', function () {
let tileUrlFunction = getSource().getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/full/0/default.jpg'
);
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
tileUrlFunction = getSource({
version: Versions.VERSION1,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/full/0/native.jpg'
);
tileUrlFunction = getSource({
version: Versions.VERSION3,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/max/0/default.jpg'
);
});
it('constructs the same number of resolutions as distinguishable sizes are given', function () {
let tileUrlFunction = getSource({
sizes: [
[2000, 1500],
[1000, 750],
[500, 375],
],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/full/1000,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/full/full/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
expect(tileUrlFunction([1, 1, 0])).to.be(undefined);
expect(tileUrlFunction([1, 0, 1])).to.be(undefined);
tileUrlFunction = getSource({
sizes: [
[2000, 1500],
[1000, 750],
[500, 375],
],
version: Versions.VERSION3,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,375/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/full/1000,750/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/full/max/0/default.jpg'
);
tileUrlFunction = getSource({
sizes: [
[2000, 1500],
[1000, 749],
[1000, 750],
[500, 375],
[500, 374],
],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/full/1000,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/full/full/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
tileUrlFunction = getSource({
version: Versions.VERSION3,
sizes: [
[2000, 1500],
[1000, 750],
[500, 375],
],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,375/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/full/1000,750/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/full/max/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
expect(tileUrlFunction([1, 1, 0])).to.be(undefined);
expect(tileUrlFunction([1, 0, 1])).to.be(undefined);
});
it('cannot provide scaled tiles without provided tilesize or supported features', function () {
const tileUrlFunction = getSource({
resolutions: [16, 8, 4, 2, 1],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/full/0/default.jpg'
);
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
});
it('provides canonical tile URLs for all necessary resolutions if only a tileSize exists', function () {
let tileUrlFunction = getSource({
tileSize: 512,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,/0/default.jpg'
);
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,1024,1024/512,/0/default.jpg'
);
expect(tileUrlFunction([1, 1, 0])).to.be(
'http://iiif.test/image-id/1024,0,976,1024/488,/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 1])).to.be(
'http://iiif.test/image-id/0,1024,1024,476/512,/0/default.jpg'
);
expect(tileUrlFunction([1, 1, 1])).to.be(
'http://iiif.test/image-id/1024,1024,976,476/488,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,512,512/512,/0/default.jpg'
);
expect(tileUrlFunction([2, 3, 0])).to.be(
'http://iiif.test/image-id/1536,0,464,512/464,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 2])).to.be(
'http://iiif.test/image-id/0,1024,512,476/512,/0/default.jpg'
);
expect(tileUrlFunction([2, 3, 2])).to.be(
'http://iiif.test/image-id/1536,1024,464,476/464,/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
tileUrlFunction = getSource({
tileSize: 512,
version: Versions.VERSION3,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,375/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,1024,1024/512,512/0/default.jpg'
);
expect(tileUrlFunction([1, 1, 0])).to.be(
'http://iiif.test/image-id/1024,0,976,1024/488,512/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 1])).to.be(
'http://iiif.test/image-id/0,1024,1024,476/512,238/0/default.jpg'
);
expect(tileUrlFunction([1, 1, 1])).to.be(
'http://iiif.test/image-id/1024,1024,976,476/488,238/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,512,512/512,512/0/default.jpg'
);
expect(tileUrlFunction([2, 3, 0])).to.be(
'http://iiif.test/image-id/1536,0,464,512/464,512/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 2])).to.be(
'http://iiif.test/image-id/0,1024,512,476/512,476/0/default.jpg'
);
expect(tileUrlFunction([2, 3, 2])).to.be(
'http://iiif.test/image-id/1536,1024,464,476/464,476/0/default.jpg'
);
});
it('provides canonical tile URLs for all provided resolutions if a tileSize also exists', function () {
const tileUrlFunction = getSource({
tileSize: 512,
resolutions: [8, 4, 2, 1],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/250,/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,1024,1024/512,/0/default.jpg'
);
expect(tileUrlFunction([2, 1, 0])).to.be(
'http://iiif.test/image-id/1024,0,976,1024/488,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 1])).to.be(
'http://iiif.test/image-id/0,1024,1024,476/512,/0/default.jpg'
);
expect(tileUrlFunction([2, 1, 1])).to.be(
'http://iiif.test/image-id/1024,1024,976,476/488,/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,512,512/512,/0/default.jpg'
);
expect(tileUrlFunction([3, 3, 0])).to.be(
'http://iiif.test/image-id/1536,0,464,512/464,/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 2])).to.be(
'http://iiif.test/image-id/0,1024,512,476/512,/0/default.jpg'
);
expect(tileUrlFunction([3, 3, 2])).to.be(
'http://iiif.test/image-id/1536,1024,464,476/464,/0/default.jpg'
);
expect(tileUrlFunction([4, 0, 0])).to.be(undefined);
});
it('supports non square tiles', function () {
let tileUrlFunction = getSource({
tileSize: [1024, 512],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,2000,1024/1000,/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 1])).to.be(
'http://iiif.test/image-id/0,1024,2000,476/1000,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,1024,512/1024,/0/default.jpg'
);
expect(tileUrlFunction([2, 1, 0])).to.be(
'http://iiif.test/image-id/1024,0,976,512/976,/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 2])).to.be(
'http://iiif.test/image-id/0,1024,1024,476/1024,/0/default.jpg'
);
expect(tileUrlFunction([2, 1, 2])).to.be(
'http://iiif.test/image-id/1024,1024,976,476/976,/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
tileUrlFunction = getSource({
tileSize: [1024, 512],
version: Versions.VERSION3,
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/500,375/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,1024,512/1024,512/0/default.jpg'
);
});
it('provides tile URLs with default tile size if sufficient supported features are provided', function () {
let tileUrlFunction = getSource({
supports: ['regionByPx', 'sizeByW'],
}).getTileUrlFunction();
const maxZoom = Math.ceil(Math.log2(width / DEFAULT_TILE_SIZE));
expect(tileUrlFunction([maxZoom, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,' +
DEFAULT_TILE_SIZE +
',' +
DEFAULT_TILE_SIZE +
'/' +
DEFAULT_TILE_SIZE +
',/0/default.jpg'
);
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
tileUrlFunction = getSource({
supports: ['regionByPx', 'sizeByH'],
}).getTileUrlFunction();
expect(tileUrlFunction([maxZoom, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,' +
DEFAULT_TILE_SIZE +
',' +
DEFAULT_TILE_SIZE +
'/,' +
DEFAULT_TILE_SIZE +
'/0/default.jpg'
);
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
tileUrlFunction = getSource({
supports: ['regionByPx', 'sizeByWh'],
}).getTileUrlFunction();
expect(tileUrlFunction([maxZoom, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,' +
DEFAULT_TILE_SIZE +
',' +
DEFAULT_TILE_SIZE +
'/' +
DEFAULT_TILE_SIZE +
',' +
DEFAULT_TILE_SIZE +
'/0/default.jpg'
);
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
tileUrlFunction = getSource({
supports: ['regionByPct', 'sizeByPct'],
}).getTileUrlFunction();
const tileWPct = ((DEFAULT_TILE_SIZE / width) * 100).toLocaleString(
'en',
{maximumFractionDigits: 10}
),
tileHPct = ((DEFAULT_TILE_SIZE / height) * 100).toLocaleString('en', {
maximumFractionDigits: 10,
});
expect(tileUrlFunction([maxZoom, 0, 0])).to.be(
'http://iiif.test/image-id/pct:0,0,' +
tileWPct +
',' +
tileHPct +
'/pct:100/0/default.jpg'
);
expect(tileUrlFunction([maxZoom + 1, 0, 0])).to.be(undefined);
});
it('prefers canonical tile URLs', function () {
let tileUrlFunction = getSource({
tileSize: 512,
supports: [
'regionByPx',
'regionByPct',
'sizeByW',
'sizeByH',
'sizeByWh',
'sizeByPct',
],
}).getTileUrlFunction();
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,512,512/512,/0/default.jpg'
);
tileUrlFunction = getSource({
tileSize: 512,
version: Versions.VERSION3,
supports: [
'regionByPx',
'regionByPct',
'sizeByW',
'sizeByH',
'sizeByWh',
'sizeByPct',
],
}).getTileUrlFunction();
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/0,0,512,512/512,512/0/default.jpg'
);
});
it('provides correct tile URLs for percentage URL parameter values', function () {
const tileUrlFunction = getSource({
tileSize: 512,
supports: ['regionByPct', 'sizeByPct'],
}).getTileUrlFunction();
expect(tileUrlFunction([0, 0, 0])).to.be(
'http://iiif.test/image-id/full/pct:25/0/default.jpg'
);
expect(tileUrlFunction([-1, 0, 0])).to.be(undefined);
expect(tileUrlFunction([0, 1, 0])).to.be(undefined);
expect(tileUrlFunction([0, 0, 1])).to.be(undefined);
expect(tileUrlFunction([1, 0, 0])).to.be(
'http://iiif.test/image-id/pct:0,0,51.2,68.2666666667/pct:50/0/default.jpg'
);
expect(tileUrlFunction([1, 1, 0])).to.be(
'http://iiif.test/image-id/pct:51.2,0,48.8,68.2666666667/pct:50/0/default.jpg'
);
expect(tileUrlFunction([1, 0, 1])).to.be(
'http://iiif.test/image-id/pct:0,68.2666666667,51.2,31.7333333333/pct:50/0/default.jpg'
);
expect(tileUrlFunction([1, 1, 1])).to.be(
'http://iiif.test/image-id/pct:51.2,68.2666666667,48.8,31.7333333333/pct:50/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 0])).to.be(
'http://iiif.test/image-id/pct:0,0,25.6,34.1333333333/pct:100/0/default.jpg'
);
expect(tileUrlFunction([2, 3, 0])).to.be(
'http://iiif.test/image-id/pct:76.8,0,23.2,34.1333333333/pct:100/0/default.jpg'
);
expect(tileUrlFunction([2, 0, 2])).to.be(
'http://iiif.test/image-id/pct:0,68.2666666667,25.6,31.7333333333/pct:100/0/default.jpg'
);
expect(tileUrlFunction([2, 3, 2])).to.be(
'http://iiif.test/image-id/pct:76.8,68.2666666667,23.2,31.7333333333/pct:100/0/default.jpg'
);
expect(tileUrlFunction([3, 0, 0])).to.be(undefined);
});
});
});
@@ -0,0 +1,203 @@
import ImageArcGISRest from '../../../../../src/ol/source/ImageArcGISRest.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.ImageArcGISRest', function () {
let pixelRatio, options, projection, proj3857, resolution;
beforeEach(function () {
pixelRatio = 1;
projection = getProjection('EPSG:4326');
proj3857 = getProjection('EPSG:3857');
resolution = 0.1;
options = {
params: {},
url: 'http://example.com/MapServer',
};
});
describe('#getImage', function () {
it('returns a image with the expected URL', function () {
const source = new ImageArcGISRest(options);
const image = source.getImage(
[3, 2, -7, 1],
resolution,
pixelRatio,
proj3857
);
const uri = new URL(image.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/MapServer/export');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('5.5,2.25,-9.5,0.75');
expect(queryData.get('FORMAT')).to.be('PNG32');
expect(queryData.get('IMAGESR')).to.be('3857');
expect(queryData.get('BBOXSR')).to.be('3857');
expect(queryData.get('TRANSPARENT')).to.be('true');
});
it('returns a non floating point DPI value', function () {
const source = new ImageArcGISRest(options);
const image = source.getImage(
[3, 2, -7, 1.12],
resolution,
1.01,
proj3857
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('DPI')).to.be('91');
});
it('returns a image with the expected URL for ImageServer', function () {
options.url = 'http://example.com/ImageServer';
const source = new ImageArcGISRest(options);
const image = source.getImage(
[3, 2, -7, 1],
resolution,
pixelRatio,
proj3857
);
const uri = new URL(image.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/ImageServer/exportImage');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('5.5,2.25,-9.5,0.75');
expect(queryData.get('FORMAT')).to.be('PNG32');
expect(queryData.get('IMAGESR')).to.be('3857');
expect(queryData.get('BBOXSR')).to.be('3857');
expect(queryData.get('TRANSPARENT')).to.be('true');
});
it('allows various parameters to be overridden', function () {
options.params.FORMAT = 'png';
options.params.TRANSPARENT = false;
const source = new ImageArcGISRest(options);
const image = source.getImage(
[3, 2, -3, 1],
resolution,
pixelRatio,
projection
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('png');
expect(queryData.get('TRANSPARENT')).to.be('false');
});
it('allows adding rest option', function () {
options.params.LAYERS = 'show:1,3,4';
const source = new ImageArcGISRest(options);
const image = source.getImage(
[3, 2, -3, 1],
resolution,
pixelRatio,
proj3857
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('LAYERS')).to.be('show:1,3,4');
});
});
describe('#updateParams', function () {
it('add a new param', function () {
const source = new ImageArcGISRest(options);
source.updateParams({'TEST': 'value'});
const image = source.getImage(
[3, 2, -7, 1],
resolution,
pixelRatio,
proj3857
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('TEST')).to.be('value');
});
it('updates an existing param', function () {
options.params.TEST = 'value';
const source = new ImageArcGISRest(options);
source.updateParams({'TEST': 'newValue'});
const image = source.getImage(
[3, 2, -7, 1],
resolution,
pixelRatio,
proj3857
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('TEST')).to.be('newValue');
});
});
describe('#getParams', function () {
it('verify getting a param', function () {
options.params.TEST = 'value';
const source = new ImageArcGISRest(options);
const setParams = source.getParams();
expect(setParams).to.eql({TEST: 'value'});
});
it('verify on adding a param', function () {
options.params.TEST = 'value';
const source = new ImageArcGISRest(options);
source.updateParams({'TEST2': 'newValue'});
const setParams = source.getParams();
expect(setParams).to.eql({TEST: 'value', TEST2: 'newValue'});
});
it('verify on update a param', function () {
options.params.TEST = 'value';
const source = new ImageArcGISRest(options);
source.updateParams({'TEST': 'newValue'});
const setParams = source.getParams();
expect(setParams).to.eql({TEST: 'newValue'});
});
});
describe('#getUrl', function () {
it('verify getting url', function () {
options.url = 'http://test.com/MapServer';
const source = new ImageArcGISRest(options);
const url = source.getUrl();
expect(url).to.eql('http://test.com/MapServer');
});
});
describe('#setUrl', function () {
it('verify setting url when not set yet', function () {
const source = new ImageArcGISRest(options);
source.setUrl('http://test.com/MapServer');
const url = source.getUrl();
expect(url).to.eql('http://test.com/MapServer');
});
it('verify setting url with existing url', function () {
options.url = 'http://test.com/MapServer';
const source = new ImageArcGISRest(options);
source.setUrl('http://test2.com/MapServer');
const url = source.getUrl();
expect(url).to.eql('http://test2.com/MapServer');
});
});
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,93 @@
import Static from '../../../../../src/ol/source/ImageStatic.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.ImageStatic', function () {
let extent, pixelRatio, projection, resolution;
beforeEach(function () {
extent = [
-13637278.73946974,
4543799.13271362,
-13617443.330629736,
4553927.038961405,
];
pixelRatio = 1;
projection = getProjection('EPSG:3857');
resolution = 38;
});
describe('#getImage', function () {
it('scales image to fit imageExtent', function (done) {
const source = new Static({
url: 'spec/ol/source/images/12-655-1583.png',
imageExtent: [
-13629027.891360067,
4539747.983913189,
-13619243.951739565,
4559315.863154193,
],
projection: projection,
});
const image = source.getImage(extent, resolution, pixelRatio, projection);
source.on('imageloadend', function (event) {
expect(image.getImage().width).to.be(128);
expect(image.getImage().height).to.be(256);
done();
});
image.load();
});
it('respects imageSize', function (done) {
const source = new Static({
url: 'spec/ol/source/images/12-655-1583.png',
imageExtent: [
-13629027.891360067,
4539747.983913189,
-13619243.951739565,
4559315.863154193,
],
imageSize: [254, 254],
projection: projection,
});
const image = source.getImage(extent, resolution, pixelRatio, projection);
source.on('imageloadend', function (event) {
expect(image.getImage().width).to.be(127);
expect(image.getImage().height).to.be(254);
done();
});
image.load();
});
it('triggers image load events', function (done) {
const source = new Static({
url: 'spec/ol/source/images/12-655-1583.png',
imageExtent: [
-13629027.891360067,
4539747.983913189,
-13619243.951739565,
4549531.923533691,
],
projection: projection,
});
const imageloadstart = sinon.spy();
const imageloaderror = sinon.spy();
source.on('imageloadstart', imageloadstart);
source.on('imageloaderror', imageloaderror);
source.on('imageloadend', function (event) {
expect(imageloadstart.callCount).to.be(1);
expect(imageloaderror.callCount).to.be(0);
done();
});
const image = source.getImage(extent, resolution, pixelRatio, projection);
image.load();
});
});
});
@@ -0,0 +1,467 @@
import Image from '../../../../../src/ol/layer/Image.js';
import ImageState from '../../../../../src/ol/ImageState.js';
import ImageWMS from '../../../../../src/ol/source/ImageWMS.js';
import Map from '../../../../../src/ol/Map.js';
import View from '../../../../../src/ol/View.js';
import {getHeight, getWidth} from '../../../../../src/ol/extent.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.ImageWMS', function () {
let extent, pixelRatio, options, optionsReproj, projection, resolution;
beforeEach(function () {
extent = [10, 20, 30, 40];
pixelRatio = 1;
projection = getProjection('EPSG:4326');
resolution = 0.1;
options = {
params: {
'LAYERS': 'layer',
},
ratio: 1,
url: 'http://example.com/wms',
};
optionsReproj = {
params: {
'LAYERS': 'layer',
},
ratio: 1,
url: 'http://example.com/wms',
projection: 'EPSG:3857',
};
});
describe('#getImage', function () {
it('returns the expected image URL', function () {
[1, 1.5].forEach(function (ratio) {
options.ratio = ratio;
const source = new ImageWMS(options);
const viewExtent = [10, 20, 30.1, 39.9];
const viewWidth = getWidth(viewExtent);
const viewHeight = getHeight(viewExtent);
const image = source.getImage(
viewExtent,
resolution,
pixelRatio,
projection
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
const imageWidth = Number(queryData.get('WIDTH'));
const imageHeight = Number(queryData.get('HEIGHT'));
const bbox = queryData.get('BBOX').split(',').map(Number);
const bboxAspectRatio = (bbox[3] - bbox[1]) / (bbox[2] - bbox[0]);
const imageAspectRatio = imageWidth / imageHeight;
expect(imageWidth).to.be(Math.ceil((viewWidth / resolution) * ratio));
expect(imageHeight).to.be(Math.ceil((viewHeight / resolution) * ratio));
expect(bboxAspectRatio).to.roughlyEqual(imageAspectRatio, 1e-12);
});
});
it('uses correct WIDTH and HEIGHT for HiDPI devices', function () {
pixelRatio = 2;
options.serverType = 'geoserver';
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
const width = Number(queryData.get('WIDTH'));
const height = Number(queryData.get('HEIGHT'));
expect(width).to.be(400);
expect(height).to.be(400);
});
it('requests integer WIDTH and HEIGHT', function () {
options.ratio = 1.5;
const source = new ImageWMS(options);
const image = source.getImage(
[10, 20, 30.1, 39.9],
resolution,
pixelRatio,
projection
);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
const width = parseFloat(queryData.get('WIDTH'));
const height = parseFloat(queryData.get('HEIGHT'));
expect(width).to.be(Math.round(width));
expect(height).to.be(Math.round(height));
});
it('sets WIDTH and HEIGHT to match the aspect ratio of BBOX', function () {
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('20,10,40,30');
expect(queryData.get('CRS')).to.be('EPSG:4326');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('200');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetMap');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('200');
expect(uri.hash.replace('#', '')).to.be.empty();
});
it('sets the SRS query value instead of CRS if version < 1.3', function () {
options.params.VERSION = '1.2';
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('CRS')).to.be(null);
expect(queryData.get('SRS')).to.be('EPSG:4326');
});
it('allows various parameters to be overridden', function () {
options.params.FORMAT = 'image/jpeg';
options.params.TRANSPARENT = false;
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('image/jpeg');
expect(queryData.get('TRANSPARENT')).to.be('false');
});
it('does not add a STYLES= option if one is specified', function () {
options.params.STYLES = 'foo';
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('STYLES')).to.be('foo');
});
it('changes the BBOX order for EN axis orientations', function () {
const source = new ImageWMS(options);
projection = getProjection('CRS:84');
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('10,20,30,40');
});
it('uses EN BBOX order if version < 1.3', function () {
options.params.VERSION = '1.1.0';
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('10,20,30,40');
});
it('sets MAP_RESOLUTION when the server is MapServer', function () {
options.serverType = 'mapserver';
const source = new ImageWMS(options);
pixelRatio = 2;
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('MAP_RESOLUTION')).to.be('180');
});
it('sets FORMAT_OPTIONS when the server is GeoServer', function () {
options.serverType = 'geoserver';
const source = new ImageWMS(options);
pixelRatio = 2;
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT_OPTIONS')).to.be('dpi:180');
});
it('extends FORMAT_OPTIONS if it is already present', function () {
options.serverType = 'geoserver';
const source = new ImageWMS(options);
options.params.FORMAT_OPTIONS = 'param1:value1';
pixelRatio = 2;
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT_OPTIONS')).to.be('param1:value1;dpi:180');
});
it('rounds FORMAT_OPTIONS to an integer when the server is GeoServer', function () {
options.serverType = 'geoserver';
const source = new ImageWMS(options);
pixelRatio = 1.325;
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT_OPTIONS')).to.be('dpi:119');
});
it('sets DPI when the server is QGIS', function () {
options.serverType = 'qgis';
const source = new ImageWMS(options);
pixelRatio = 2;
const image = source.getImage(extent, resolution, pixelRatio, projection);
const uri = new URL(image.src_);
const queryData = uri.searchParams;
expect(queryData.get('DPI')).to.be('180');
});
it('creates an image with a custom imageLoadFunction', function () {
const imageLoadFunction = sinon.spy();
options.imageLoadFunction = imageLoadFunction;
const source = new ImageWMS(options);
const image = source.getImage(extent, resolution, pixelRatio, projection);
image.load();
expect(imageLoadFunction.called).to.be(true);
expect(imageLoadFunction.calledWith(image, image.src_)).to.be(true);
});
it('returns same image for consecutive calls with same args', function () {
const extent = [10.01, 20, 30.01, 40];
const source = new ImageWMS(options);
const image1 = source.getImage(
extent,
resolution,
pixelRatio,
projection
);
const image2 = source.getImage(
extent,
resolution,
pixelRatio,
projection
);
expect(image1).to.equal(image2);
});
it('returns same image for calls with similar extents', function () {
options.ratio = 1.5;
const source = new ImageWMS(options);
let extent = [10.01, 20, 30.01, 40];
const image1 = source.getImage(
extent,
resolution,
pixelRatio,
projection
);
extent = [10.01, 20.1, 30.01, 40.1];
const image2 = source.getImage(
extent,
resolution,
pixelRatio,
projection
);
expect(image1).to.equal(image2);
});
it('calculates correct image size with ratio', function () {
options.ratio = 1.5;
const source = new ImageWMS(options);
const extent = [10, 5, 30, 45];
source.getImage(extent, resolution, pixelRatio, projection);
expect(source.imageSize_).to.eql([300, 600]);
});
});
describe('#getFeatureInfoUrl', function () {
it('returns the expected GetFeatureInfo URL', function () {
const source = new ImageWMS(options);
const url = source.getFeatureInfoUrl([20, 30], resolution, projection, {
INFO_FORMAT: 'text/plain',
});
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('24.95,14.95,35.05,25.05');
expect(queryData.get('CRS')).to.be('EPSG:4326');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('101');
expect(queryData.get('I')).to.be('50');
expect(queryData.get('J')).to.be('50');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('QUERY_LAYERS')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetFeatureInfo');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('101');
expect(uri.hash.replace('#', '')).to.be.empty();
});
it("returns the expected GetFeatureInfo URL when source's projection is different from the parameter", function () {
const source = new ImageWMS(optionsReproj);
const url = source.getFeatureInfoUrl([20, 30], resolution, projection, {
INFO_FORMAT: 'text/plain',
});
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be(
'1577259.402312431,2854419.4299513334,2875520.229418512,4152680.2570574144'
);
expect(queryData.get('CRS')).to.be('EPSG:3857');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('101');
expect(queryData.get('I')).to.be('50');
expect(queryData.get('J')).to.be('50');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('QUERY_LAYERS')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetFeatureInfo');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('101');
expect(uri.hash.replace('#', '')).to.be.empty();
});
it('sets the QUERY_LAYERS param as expected', function () {
const source = new ImageWMS(options);
const url = source.getFeatureInfoUrl([20, 30], resolution, projection, {
INFO_FORMAT: 'text/plain',
QUERY_LAYERS: 'foo,bar',
});
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('24.95,14.95,35.05,25.05');
expect(queryData.get('CRS')).to.be('EPSG:4326');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('101');
expect(queryData.get('I')).to.be('50');
expect(queryData.get('J')).to.be('50');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('QUERY_LAYERS')).to.be('foo,bar');
expect(queryData.get('REQUEST')).to.be('GetFeatureInfo');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('101');
expect(uri.hash.replace('#', '')).to.be.empty();
});
});
describe('#getLegendUrl', function () {
it('returns the GetLegendGraphic url as expected', function () {
const source = new ImageWMS(options);
const url = source.getLegendUrl(resolution);
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('LAYER')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetLegendGraphic');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('SCALE')).to.be('357.14285714285717');
});
it('does not include SCALE if no resolution was provided', function () {
const source = new ImageWMS(options);
const url = source.getLegendUrl();
const uri = new URL(url);
const queryData = uri.searchParams;
expect(queryData.get('SCALE')).to.be(null);
});
it('adds additional params as expected', function () {
const source = new ImageWMS(options);
const url = source.getLegendUrl(resolution, {
STYLE: 'STYLE_VALUE',
FEATURETYPE: 'FEATURETYPE_VALUE',
RULE: 'RULE_VALUE',
SLD: 'SLD_VALUE',
SLD_BODY: 'SLD_BODY_VALUE',
FORMAT: 'FORMAT_VALUE',
WIDTH: 'WIDTH_VALUE',
HEIGHT: 'HEIGHT_VALUE',
EXCEPTIONS: 'EXCEPTIONS_VALUE',
LANGUAGE: 'LANGUAGE_VALUE',
LAYER: 'LAYER_VALUE',
});
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE');
expect(queryData.get('LAYER')).to.be('LAYER_VALUE');
expect(queryData.get('REQUEST')).to.be('GetLegendGraphic');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('SCALE')).to.be('357.14285714285717');
expect(queryData.get('STYLE')).to.be('STYLE_VALUE');
expect(queryData.get('FEATURETYPE')).to.be('FEATURETYPE_VALUE');
expect(queryData.get('RULE')).to.be('RULE_VALUE');
expect(queryData.get('SLD')).to.be('SLD_VALUE');
expect(queryData.get('SLD_BODY')).to.be('SLD_BODY_VALUE');
expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE');
expect(queryData.get('WIDTH')).to.be('WIDTH_VALUE');
expect(queryData.get('HEIGHT')).to.be('HEIGHT_VALUE');
expect(queryData.get('EXCEPTIONS')).to.be('EXCEPTIONS_VALUE');
expect(queryData.get('LANGUAGE')).to.be('LANGUAGE_VALUE');
});
});
describe('#refresh()', function () {
let map, source;
let callCount = 0;
beforeEach(function (done) {
source = new ImageWMS(options);
source.setImageLoadFunction(function (image) {
++callCount;
image.state = ImageState.LOADED;
source.loading = false;
});
const target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
layers: [
new Image({
source: source,
}),
],
view: new View({
center: [0, 0],
zoom: 0,
}),
});
map.once('rendercomplete', function () {
callCount = 0;
done();
});
});
afterEach(function () {
document.body.removeChild(map.getTargetElement());
map.setTarget(null);
});
it('reloads from server', function (done) {
map.once('rendercomplete', function () {
expect(callCount).to.be(1);
done();
});
source.refresh();
});
});
});
+701
View File
@@ -0,0 +1,701 @@
import Feature from '../../../../../src/ol/Feature.js';
import ImageLayer from '../../../../../src/ol/layer/Image.js';
import Map from '../../../../../src/ol/Map.js';
import Point from '../../../../../src/ol/geom/Point.js';
import Projection from '../../../../../src/ol/proj/Projection.js';
import RasterSource, {
Processor,
newImageData,
} from '../../../../../src/ol/source/Raster.js';
import Source from '../../../../../src/ol/source/Source.js';
import Static from '../../../../../src/ol/source/ImageStatic.js';
import TileSource from '../../../../../src/ol/source/Tile.js';
import TileState from '../../../../../src/ol/TileState.js';
import VectorImageLayer from '../../../../../src/ol/layer/VectorImage.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import View from '../../../../../src/ol/View.js';
import XYZ from '../../../../../src/ol/source/XYZ.js';
import {Circle, Fill, Style} from '../../../../../src/ol/style.js';
const red =
'data:image/gif;base64,R0lGODlhAQABAPAAAP8AAP///yH5BAAAAAAALAAAAAA' +
'BAAEAAAICRAEAOw==';
const green =
'data:image/gif;base64,R0lGODlhAQABAPAAAAD/AP///yH5BAAAAAAALAAAA' +
'AABAAEAAAICRAEAOw==';
where('Uint8ClampedArray').describe('ol.source.Raster', function () {
let map, target, redSource, greenSource, blueSource, raster;
beforeEach(function () {
target = document.createElement('div');
const style = target.style;
style.position = 'absolute';
style.left = '-1000px';
style.top = '-1000px';
style.width = '2px';
style.height = '2px';
document.body.appendChild(target);
const extent = [-1, -1, 1, 1];
redSource = new Static({
url: red,
imageExtent: extent,
attributions: ['red raster source'],
});
greenSource = new Static({
url: green,
imageExtent: extent,
attributions: ['green raster source'],
});
blueSource = new VectorImageLayer({
source: new VectorSource({
features: [new Feature(new Point([0, 0]))],
}),
style: new Style({
image: new Circle({
radius: 3,
fill: new Fill({color: 'blue'}),
}),
}),
});
raster = new RasterSource({
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function (inputs) {
return inputs[0];
},
});
map = new Map({
target: target,
view: new View({
resolutions: [1],
projection: new Projection({
code: 'image',
units: 'pixels',
extent: extent,
}),
}),
layers: [
new ImageLayer({
source: raster,
}),
],
});
});
afterEach(function () {
if (map) {
disposeMap(map);
}
map = null;
raster.dispose();
greenSource.dispose();
redSource.dispose();
blueSource.dispose();
});
describe('constructor', function () {
it('returns a raster source', function () {
const source = new RasterSource({
threads: 0,
sources: [new TileSource({})],
});
expect(source).to.be.a(Source);
expect(source).to.be.a(RasterSource);
});
it('defaults to "pixel" operation', function (done) {
const log = [];
const source = new RasterSource({
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function (inputs) {
log.push(inputs);
return inputs[0];
},
});
source.once('afteroperations', function () {
expect(log.length).to.equal(4);
const inputs = log[0];
const pixel = inputs[0];
expect(pixel).to.be.an('array');
done();
});
map.getLayers().item(0).setSource(source);
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
it('disposes the processor when disposed', function () {
const source = new RasterSource({
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function (inputs) {
return inputs[0];
},
});
source.dispose();
expect(source.processor_.disposed).to.be(true);
});
it('allows operation type to be set to "image"', function (done) {
const log = [];
const source = new RasterSource({
operationType: 'image',
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function (inputs) {
log.push(inputs);
return inputs[0];
},
});
source.once('afteroperations', function () {
expect(log.length).to.equal(1);
const inputs = log[0];
const imageData = inputs[0];
expect(imageData.data).to.be.a(Uint8ClampedArray);
expect(imageData.width).to.be(2);
expect(imageData.height).to.be(2);
done();
});
map.getLayers().item(0).setSource(source);
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
});
describe('config option `attributions`', function () {
it('handles empty attributions', function () {
const blue = new RasterSource({
operationType: 'image',
threads: 0,
sources: [blueSource],
operation: function (inputs) {
return inputs[0];
},
});
const blueAttributions = blue.getAttributions();
expect(blueAttributions()).to.be(null);
});
it('shows single attributions', function () {
const red = new RasterSource({
operationType: 'image',
threads: 0,
sources: [redSource],
operation: function (inputs) {
return inputs[0];
},
});
const redAttribtuions = red.getAttributions();
expect(redAttribtuions()).to.not.be(null);
expect(typeof redAttribtuions).to.be('function');
expect(redAttribtuions()).to.eql(['red raster source']);
});
it('concatinates multiple attributions', function () {
const redGreen = new RasterSource({
operationType: 'image',
threads: 0,
sources: [redSource, greenSource],
operation: function (inputs) {
return inputs[0];
},
});
const redGreenAttributions = redGreen.getAttributions();
expect(redGreenAttributions()).to.not.be(null);
expect(typeof redGreenAttributions).to.be('function');
expect(redGreenAttributions()).to.eql([
'red raster source',
'green raster source',
]);
});
});
describe('#setOperation()', function () {
it('allows operation to be set', function (done) {
let count = 0;
raster.setOperation(function (pixels) {
++count;
const redPixel = pixels[0];
const greenPixel = pixels[1];
const bluePixel = pixels[2];
expect(redPixel).to.eql([255, 0, 0, 255]);
expect(greenPixel).to.eql([0, 255, 0, 255]);
expect(bluePixel).to.eql([0, 0, 255, 255]);
return pixels[0];
});
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
raster.once('afteroperations', function (event) {
expect(count).to.equal(4);
done();
});
});
it('updates and re-runs the operation', function (done) {
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
let count = 0;
raster.on('afteroperations', function (event) {
++count;
if (count === 1) {
raster.setOperation(function (inputs) {
return inputs[0];
});
} else {
done();
}
});
});
it('disposes the previous processor', function () {
const previousProcessor = raster.processor_;
raster.setOperation(function (pixels) {
return pixels[0];
});
expect(previousProcessor.disposed).to.be(true);
expect(raster.processor_.disposed).to.be(false);
});
});
describe('beforeoperations', function () {
it('gets called before operations are run', function (done) {
let count = 0;
raster.setOperation(function (inputs) {
++count;
return inputs[0];
});
raster.once('beforeoperations', function (event) {
expect(count).to.equal(0);
expect(!!event).to.be(true);
expect(event.extent).to.be.an('array');
expect(event.resolution).to.be.a('number');
expect(event.data).to.be.an('object');
done();
});
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
it('allows data to be set for the operation', function (done) {
raster.setOperation(function (inputs, data) {
++data.count;
return inputs[0];
});
raster.on('beforeoperations', function (event) {
event.data.count = 0;
});
raster.once('afteroperations', function (event) {
expect(event.data.count).to.equal(4);
done();
});
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
});
describe('afteroperations', function () {
it('gets called after operations are run', function (done) {
let count = 0;
raster.setOperation(function (inputs) {
++count;
return inputs[0];
});
raster.once('afteroperations', function (event) {
expect(count).to.equal(4);
expect(!!event).to.be(true);
expect(event.extent).to.be.an('array');
expect(event.resolution).to.be.a('number');
expect(event.data).to.be.an('object');
done();
});
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
it('receives data set by the operation', function (done) {
raster.setOperation(function (inputs, data) {
data.message = 'hello world';
return inputs[0];
});
raster.once('afteroperations', function (event) {
expect(event.data.message).to.equal('hello world');
done();
});
const view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
});
describe('tile loading', function () {
let map2;
afterEach(function () {
disposeMap(map2);
map2 = null;
});
it('is initiated on the underlying source', function (done) {
const source = new XYZ({
url: 'spec/ol/data/osm-{z}-{x}-{y}.png',
});
raster = new RasterSource({
threads: 0,
sources: [source],
operation: function (inputs) {
return inputs[0];
},
});
map2 = new Map({
target: target,
view: new View({
center: [0, 0],
zoom: 0,
}),
layers: [
new ImageLayer({
source: raster,
}),
],
});
const tileCache = source.tileCache;
expect(tileCache.getCount()).to.equal(0);
map2.once('moveend', function () {
expect(tileCache.getCount()).to.equal(1);
const state = tileCache.peekLast().getState();
expect(state === TileState.LOADING || state === TileState.LOADED).to.be(
true
);
done();
});
});
});
});
where('Uint8ClampedArray').describe('Processor', function () {
const identity = function (inputs) {
return inputs[0];
};
describe('constructor', function () {
it('creates a new processor', function () {
const processor = new Processor({
operation: identity,
});
expect(processor).to.be.a(Processor);
});
});
describe('#process()', function () {
it('calls operation with input pixels', function (done) {
const processor = new Processor({
operation: function (inputs, meta) {
++meta.count;
const pixel = inputs[0];
for (let i = 0, ii = pixel.length; i < ii; ++i) {
meta.sum += pixel[i];
}
return pixel;
},
});
const array = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8]);
const input = newImageData(array, 1, 2);
processor.process([input], {count: 0, sum: 0}, function (err, output, m) {
if (err) {
done(err);
return;
}
expect(m.count).to.equal(2);
expect(m.sum).to.equal(36);
done();
});
});
it('calls callback with processed image data', function (done) {
const processor = new Processor({
operation: function (inputs) {
const pixel = inputs[0];
pixel[0] *= 2;
pixel[1] *= 2;
pixel[2] *= 2;
pixel[3] *= 2;
return pixel;
},
});
const array = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8]);
const input = newImageData(array, 1, 2);
processor.process([input], {}, function (err, output, m) {
if (err) {
done(err);
return;
}
expect(output).to.be.a(ImageData);
expect(output.data).to.eql(
new Uint8ClampedArray([2, 4, 6, 8, 10, 12, 14, 16])
);
done();
});
});
it('allows library functions to be called', function (done) {
const lib = {
sum: function (a, b) {
return a + b;
},
diff: function (a, b) {
return a - b;
},
};
const normalizedDiff = function (pixels) {
const pixel = pixels[0];
const r = pixel[0];
const g = pixel[1];
/* eslint-disable */
var nd = diff(r, g) / sum(r, g);
/* eslint-enable */
const index = Math.round((255 * (nd + 1)) / 2);
return [index, index, index, pixel[3]];
};
const processor = new Processor({
operation: normalizedDiff,
lib: lib,
});
const array = new Uint8ClampedArray([10, 2, 0, 0, 5, 8, 0, 1]);
const input = newImageData(array, 1, 2);
processor.process([input], {}, function (err, output, m) {
if (err) {
done(err);
return;
}
expect(output).to.be.a(ImageData);
const v0 = Math.round((255 * (1 + 8 / 12)) / 2);
const v1 = Math.round((255 * (1 + -3 / 13)) / 2);
expect(output.data).to.eql(
new Uint8ClampedArray([v0, v0, v0, 0, v1, v1, v1, 1])
);
done();
});
});
it('calls callbacks for each call', function (done) {
const processor = new Processor({
operation: identity,
});
let calls = 0;
function createCallback(index) {
return function (err, output, meta) {
if (err) {
done(err);
return;
}
expect(output).to.be.a(ImageData);
++calls;
};
}
for (let i = 0; i < 5; ++i) {
const input = newImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1);
processor.process([input], {}, createCallback(i));
}
setTimeout(function () {
expect(calls).to.be(5);
done();
}, 1000);
});
it('respects max queue length', function (done) {
const processor = new Processor({
queue: 1,
operation: identity,
});
const log = [];
function createCallback(index) {
return function (err, output, meta) {
if (err) {
done(err);
return;
}
log.push(output);
};
}
for (let i = 0; i < 5; ++i) {
const input = newImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1);
processor.process([input], {}, createCallback(i));
}
setTimeout(function () {
expect(log).to.have.length(5);
expect(log[0]).to.be(null);
expect(log[1]).to.be(null);
expect(log[2]).to.be(null);
expect(log[3]).to.be.a(ImageData);
expect(log[4]).to.be.a(ImageData);
done();
}, 1000);
});
it('can run on multiple threads', function (done) {
const processor = new Processor({
threads: 2,
operation: identity,
});
const input = newImageData(new Uint8ClampedArray([1, 2, 3, 4]), 1, 1);
processor.process([input], {}, function (err) {
if (err) {
done(err);
}
});
processor.dispose();
setTimeout(done, 20);
});
});
describe('#process() - faux worker', function () {
let identitySpy;
beforeEach(function () {
identitySpy = sinon.spy(identity);
});
it('calls operation with input pixels', function (done) {
const processor = new Processor({
threads: 0,
operation: identitySpy,
});
const array = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8]);
const input = newImageData(array, 1, 2);
processor.process([input], {}, function (err, output, m) {
if (err) {
done(err);
return;
}
expect(identitySpy.callCount).to.be(2);
const first = identitySpy.getCall(0);
expect(first.args).to.have.length(2);
done();
});
});
it('passes meta object to operations', function (done) {
const processor = new Processor({
threads: 0,
operation: identitySpy,
});
const array = new Uint8ClampedArray([1, 2, 3, 4]);
const input = newImageData(array, 1, 1);
const meta = {foo: 'bar'};
processor.process([input], meta, function (err, output, m) {
if (err) {
done(err);
return;
}
expect(m).to.eql(meta);
expect(identitySpy.callCount).to.be(1);
done();
});
});
});
describe('#dispose()', function () {
it('stops callbacks from being called', function (done) {
const processor = new Processor({
operation: identity,
});
const array = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8]);
const input = newImageData(array, 1, 2);
processor.process([input], {}, function () {
done(new Error('Expected abort to stop callback from being called'));
});
processor.dispose();
setTimeout(done, 500);
});
});
describe('#dispose() - faux worker', function () {
it('stops callbacks from being called', function (done) {
const processor = new Processor({
threads: 0,
operation: identity,
});
const array = new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8]);
const input = newImageData(array, 1, 2);
processor.process([input], {}, function () {
done(new Error('Expected abort to stop callback from being called'));
});
processor.dispose();
setTimeout(done, 20);
});
});
});
+133
View File
@@ -0,0 +1,133 @@
import Source from '../../../../../src/ol/source/Source.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.Source', function () {
describe('constructor', function () {
it('returns a source', function () {
const source = new Source({
projection: getProjection('EPSG:4326'),
});
expect(source).to.be.a(Source);
});
});
describe('config option `attributions`', function () {
it('accepts undefined', function () {
const source = new Source({});
const attributions = source.getAttributions();
expect(attributions).to.be(null);
});
it('accepts a single string', function () {
const source = new Source({
attributions: 'Humpty',
});
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql(['Humpty']);
});
it('accepts an array of strings', function () {
const source = new Source({
attributions: ['Humpty', 'Dumpty'],
});
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql(['Humpty', 'Dumpty']);
});
it('accepts a function that returns a string', function () {
const source = new Source({
attributions: function () {
return 'Humpty';
},
});
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.be('Humpty');
});
it('accepts a function that returns an array of strings', function () {
const source = new Source({
attributions: function () {
return ['Humpty', 'Dumpty'];
},
});
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql(['Humpty', 'Dumpty']);
});
});
describe('#refresh()', function () {
it('dispatches the change event', function () {
const source = new Source({
projection: getProjection('EPSG:4326'),
});
const changedSpy = sinon.spy();
source.on('change', changedSpy);
source.refresh();
expect(changedSpy.called).to.be.ok();
});
});
describe('#setAttributions()', function () {
let source = null;
beforeEach(function () {
source = new Source({
attributions: 'before',
});
});
afterEach(function () {
source = null;
});
it('accepts undefined', function () {
source.setAttributions();
const attributions = source.getAttributions();
expect(attributions).to.be(null);
});
it('accepts a single string', function () {
source.setAttributions('Humpty');
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql(['Humpty']);
});
it('accepts an array of strings', function () {
source.setAttributions(['Humpty', 'Dumpty']);
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql(['Humpty', 'Dumpty']);
});
it('accepts a function that returns a string', function () {
source.setAttributions(function () {
return 'Humpty';
});
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql('Humpty');
});
it('accepts a function that returns an array of strings', function () {
source.setAttributions(function () {
return ['Humpty', 'Dumpty'];
});
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
expect(attributions()).to.eql(['Humpty', 'Dumpty']);
});
});
});
@@ -0,0 +1,21 @@
import Stamen from '../../../../../src/ol/source/Stamen.js';
describe('ol.source.Stamen', function () {
describe('constructor', function () {
it('can be constructed with a custom minZoom', function () {
const source = new Stamen({
layer: 'watercolor',
minZoom: 10,
});
expect(source.getTileGrid().getMinZoom()).to.be(10);
});
it('can be constructed with a custom maxZoom', function () {
const source = new Stamen({
layer: 'watercolor',
maxZoom: 8,
});
expect(source.getTileGrid().getMaxZoom()).to.be(8);
});
});
});
+356
View File
@@ -0,0 +1,356 @@
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('#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
});
});
});
@@ -0,0 +1,266 @@
import ImageTile from '../../../../../src/ol/ImageTile.js';
import TileArcGISRest from '../../../../../src/ol/source/TileArcGISRest.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.TileArcGISRest', function () {
let options;
beforeEach(function () {
options = {
params: {},
url: 'http://example.com/MapServer',
};
});
describe('#getTile', function () {
it('returns a tile with the expected URL', function () {
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
expect(tile).to.be.an(ImageTile);
const uri = new URL(tile.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/MapServer/export');
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',').map(parseFloat);
expect(bbox[0]).roughlyEqual(-10018754.171394622, 1e-9);
expect(bbox[1]).roughlyEqual(-15028131.257091936, 1e-9);
expect(bbox[2]).roughlyEqual(-5009377.085697311, 1e-9);
expect(bbox[3]).roughlyEqual(-10018754.171394624, 1e-9);
expect(queryData.get('FORMAT')).to.be('PNG32');
expect(queryData.get('SIZE')).to.be('256,256');
expect(queryData.get('IMAGESR')).to.be('3857');
expect(queryData.get('BBOXSR')).to.be('3857');
expect(queryData.get('TRANSPARENT')).to.be('true');
});
it('returns a non floating point DPI value', function () {
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 6, 1.12, getProjection('EPSG:3857'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('DPI')).to.be('101');
});
it('takes DPI from params if specified', function () {
options.params.DPI = 96;
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 6, 1.12, getProjection('EPSG:3857'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('DPI')).to.be('108');
delete options.params.DPI;
});
it('returns a tile with the expected URL with url list', function () {
options.urls = [
'http://test1.com/MapServer',
'http://test2.com/MapServer',
];
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
expect(tile).to.be.an(ImageTile);
const uri = new URL(tile.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.match(/test[12]\.com/);
expect(uri.pathname).to.be('/MapServer/export');
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',').map(parseFloat);
expect(bbox[0]).roughlyEqual(-10018754.171394622, 1e-9);
expect(bbox[1]).roughlyEqual(-15028131.257091936, 1e-9);
expect(bbox[2]).roughlyEqual(-5009377.085697311, 1e-9);
expect(bbox[3]).roughlyEqual(-10018754.171394624, 1e-9);
expect(queryData.get('FORMAT')).to.be('PNG32');
expect(queryData.get('SIZE')).to.be('256,256');
expect(queryData.get('IMAGESR')).to.be('3857');
expect(queryData.get('BBOXSR')).to.be('3857');
expect(queryData.get('TRANSPARENT')).to.be('true');
});
it('returns a tile with the expected URL for ImageServer', function () {
options.url = 'http://example.com/ImageServer';
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
expect(tile).to.be.an(ImageTile);
const uri = new URL(tile.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/ImageServer/exportImage');
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',').map(parseFloat);
expect(bbox[0]).roughlyEqual(-10018754.171394622, 1e-9);
expect(bbox[1]).roughlyEqual(-15028131.257091936, 1e-9);
expect(bbox[2]).roughlyEqual(-5009377.085697311, 1e-9);
expect(bbox[3]).roughlyEqual(-10018754.171394624, 1e-9);
expect(queryData.get('FORMAT')).to.be('PNG32');
expect(queryData.get('SIZE')).to.be('256,256');
expect(queryData.get('IMAGESR')).to.be('3857');
expect(queryData.get('BBOXSR')).to.be('3857');
expect(queryData.get('TRANSPARENT')).to.be('true');
});
it('allows various parameters to be overridden', function () {
options.params.FORMAT = 'png';
options.params.TRANSPARENT = false;
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('EPSG:4326'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('png');
expect(queryData.get('TRANSPARENT')).to.be('false');
});
it('allows adding rest option', function () {
options.params.LAYERS = 'show:1,3,4';
const source = new TileArcGISRest(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('EPSG:4326'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('LAYERS')).to.be('show:1,3,4');
});
});
describe('#updateParams', function () {
it('add a new param', function () {
const source = new TileArcGISRest(options);
source.updateParams({'TEST': 'value'});
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('TEST')).to.be('value');
});
it('updates an existing param', function () {
options.params.TEST = 'value';
const source = new TileArcGISRest(options);
source.updateParams({'TEST': 'newValue'});
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('TEST')).to.be('newValue');
});
});
describe('#getParams', function () {
it('verify getting a param', function () {
options.params.TEST = 'value';
const source = new TileArcGISRest(options);
const setParams = source.getParams();
expect(setParams).to.eql({TEST: 'value'});
});
it('verify on adding a param', function () {
options.params.TEST = 'value';
const source = new TileArcGISRest(options);
source.updateParams({'TEST2': 'newValue'});
const setParams = source.getParams();
expect(setParams).to.eql({TEST: 'value', TEST2: 'newValue'});
});
it('verify on update a param', function () {
options.params.TEST = 'value';
const source = new TileArcGISRest(options);
source.updateParams({'TEST': 'newValue'});
const setParams = source.getParams();
expect(setParams).to.eql({TEST: 'newValue'});
});
});
describe('#getUrls', function () {
it('verify getting array of urls', function () {
options.urls = [
'http://test.com/MapServer',
'http://test2.com/MapServer',
];
const source = new TileArcGISRest(options);
const urls = source.getUrls();
expect(urls).to.eql([
'http://test.com/MapServer',
'http://test2.com/MapServer',
]);
});
});
describe('#setUrls', function () {
it('verify setting urls when not set yet', function () {
const source = new TileArcGISRest(options);
source.setUrls([
'http://test.com/MapServer',
'http://test2.com/MapServer',
]);
const urls = source.getUrls();
expect(urls).to.eql([
'http://test.com/MapServer',
'http://test2.com/MapServer',
]);
});
it('verify setting urls with existing list', function () {
options.urls = [
'http://test.com/MapServer',
'http://test2.com/MapServer',
];
const source = new TileArcGISRest(options);
source.setUrls([
'http://test3.com/MapServer',
'http://test4.com/MapServer',
]);
const urls = source.getUrls();
expect(urls).to.eql([
'http://test3.com/MapServer',
'http://test4.com/MapServer',
]);
});
});
describe('#setUrl', function () {
it('verify setting url with no urls', function () {
const source = new TileArcGISRest(options);
source.setUrl('http://test.com/MapServer');
const urls = source.getUrls();
expect(urls).to.eql(['http://test.com/MapServer']);
});
it('verify setting url with list of urls', function () {
options.urls = [
'http://test.com/MapServer',
'http://test2.com/MapServer',
];
const source = new TileArcGISRest(options);
source.setUrl('http://test3.com/MapServer');
const urls = source.getUrls();
expect(urls).to.eql(['http://test3.com/MapServer']);
const tileUrl = source.tileUrlFunction(
[0, 0, 0],
1,
getProjection('EPSG:4326')
);
expect(tileUrl.indexOf(urls[0])).to.be(0);
});
});
});
@@ -0,0 +1,275 @@
import ImageTile from '../../../../../src/ol/ImageTile.js';
import Projection from '../../../../../src/ol/proj/Projection.js';
import ReprojTile from '../../../../../src/ol/reproj/Tile.js';
import TileImage from '../../../../../src/ol/source/TileImage.js';
import TileState from '../../../../../src/ol/TileState.js';
import {WORLD_EXTENT} from '../../../../../src/ol/proj/epsg3857.js';
import {
addCommon,
clearAllProjections,
get as getProjection,
} from '../../../../../src/ol/proj.js';
import {
createForProjection,
createXYZ,
} from '../../../../../src/ol/tilegrid.js';
import {createFromTemplate} from '../../../../../src/ol/tileurlfunction.js';
import {getKeyZXY} from '../../../../../src/ol/tilecoord.js';
import {listen} from '../../../../../src/ol/events.js';
import {register} from '../../../../../src/ol/proj/proj4.js';
describe('ol.source.TileImage', function () {
function createSource(opt_proj, opt_tileGrid, opt_cacheSize) {
const proj = opt_proj || 'EPSG:3857';
return new TileImage({
cacheSize: opt_cacheSize,
projection: proj,
tileGrid: opt_tileGrid || createForProjection(proj, undefined, [2, 2]),
tileUrlFunction: createFromTemplate(
'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs='
),
});
}
describe('#getTileCacheForProjection', function () {
it('uses the cacheSize for reprojected tile caches', function () {
const source = createSource(undefined, undefined, 442);
const tileCache = source.getTileCacheForProjection(
getProjection('EPSG:4326')
);
expect(tileCache.highWaterMark).to.be(442);
expect(tileCache).to.not.equal(
source.getTileCacheForProjection(source.getProjection())
);
});
});
describe('#setTileGridForProjection', function () {
it('uses the tilegrid for given projection', function () {
const source = createSource();
const tileGrid = createForProjection('EPSG:4326', 3, [10, 20]);
source.setTileGridForProjection('EPSG:4326', tileGrid);
const retrieved = source.getTileGridForProjection(
getProjection('EPSG:4326')
);
expect(retrieved).to.be(tileGrid);
});
});
describe('#getTileInternal', function () {
let source, tile;
beforeEach(function () {
source = createSource();
expect(source.getKey()).to.be('');
source.getTileInternal(0, 0, 0, 1, getProjection('EPSG:3857'));
expect(source.tileCache.getCount()).to.be(1);
tile = source.tileCache.get(getKeyZXY(0, 0, 0));
});
it('gets the tile from the cache', function () {
const returnedTile = source.getTileInternal(
0,
0,
0,
1,
getProjection('EPSG:3857')
);
expect(returnedTile).to.be(tile);
});
describe('change a dynamic param', function () {
describe('tile is not loaded', function () {
it('returns a tile with no interim tile', function () {
source.getKey = function () {
return 'key0';
};
const returnedTile = source.getTileInternal(
0,
0,
0,
1,
getProjection('EPSG:3857')
);
expect(returnedTile).not.to.be(tile);
expect(returnedTile.key).to.be('key0');
expect(returnedTile.interimTile).to.be(null);
});
});
describe('tile is loaded', function () {
it('returns a tile with interim tile', function () {
source.getKey = function () {
return 'key0';
};
tile.state = 2; // LOADED
const returnedTile = source.getTileInternal(
0,
0,
0,
1,
getProjection('EPSG:3857')
);
expect(returnedTile).not.to.be(tile);
expect(returnedTile.key).to.be('key0');
expect(returnedTile.interimTile).to.be(tile);
});
});
describe('tile is not loaded but interim tile is', function () {
it('returns a tile with interim tile', function () {
let dynamicParamsKey, returnedTile;
source.getKey = function () {
return dynamicParamsKey;
};
dynamicParamsKey = 'key0';
tile.state = 2; // LOADED
returnedTile = source.getTileInternal(
0,
0,
0,
1,
getProjection('EPSG:3857')
);
dynamicParamsKey = 'key1';
returnedTile = source.getTileInternal(
0,
0,
0,
1,
getProjection('EPSG:3857')
);
expect(returnedTile).not.to.be(tile);
expect(returnedTile.key).to.be('key1');
expect(returnedTile.interimTile).to.be(tile);
});
});
});
});
describe('#getTile', function () {
it('does not do reprojection for identity', function () {
const source3857 = createSource('EPSG:3857');
const tile3857 = source3857.getTile(
0,
0,
0,
1,
getProjection('EPSG:3857')
);
expect(tile3857).to.be.a(ImageTile);
expect(tile3857).not.to.be.a(ReprojTile);
const projXXX = new Projection({
code: 'XXX',
units: 'degrees',
});
const sourceXXX = createSource(projXXX);
const tileXXX = sourceXXX.getTile(0, 0, 0, 1, projXXX);
expect(tileXXX).to.be.a(ImageTile);
expect(tileXXX).not.to.be.a(ReprojTile);
});
beforeEach(function () {
proj4.defs('4326_noextentnounits', '+proj=longlat +datum=WGS84 +no_defs');
register(proj4);
});
afterEach(function () {
delete proj4.defs['4326_noextentnounits'];
clearAllProjections();
addCommon();
});
it('can handle source projection without extent and units', function (done) {
const source = createSource(
'4326_noextentnounits',
createXYZ({
extent: [-180, -90, 180, 90],
tileSize: [2, 2],
})
);
const tile = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
expect(tile).to.be.a(ReprojTile);
listen(tile, 'change', function () {
if (tile.getState() == 2) {
// LOADED
done();
}
});
tile.load();
});
it('can handle target projection without extent and units', function (done) {
const proj = getProjection('4326_noextentnounits');
const source = createSource();
source.setTileGridForProjection(
proj,
createXYZ({
extent: WORLD_EXTENT,
tileSize: [2, 2],
})
);
const tile = source.getTile(0, 0, 0, 1, proj);
expect(tile).to.be.a(ReprojTile);
listen(tile, 'change', function () {
if (tile.getState() == 2) {
// LOADED
done();
}
});
tile.load();
});
});
describe('tile load events', function () {
let source;
beforeEach(function () {
source = new TileImage({
url: '{z}/{x}/{y}',
});
});
it('dispatches tileloadstart and tileloadend events', function () {
source.setTileLoadFunction(function (tile) {
tile.setState(TileState.LOADED);
});
const startSpy = sinon.spy();
source.on('tileloadstart', startSpy);
const endSpy = sinon.spy();
source.on('tileloadend', endSpy);
const tile = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
tile.load();
expect(startSpy.callCount).to.be(1);
expect(endSpy.callCount).to.be(1);
});
it('works for loading-error-loading-loaded sequences', function (done) {
source.setTileLoadFunction(function (tile) {
tile.setState(
tile.state == TileState.ERROR ? TileState.LOADED : TileState.ERROR
);
});
const startSpy = sinon.spy();
source.on('tileloadstart', startSpy);
const errorSpy = sinon.spy();
source.on('tileloaderror', function (e) {
setTimeout(function () {
e.tile.setState(TileState.LOADING);
e.tile.setState(TileState.LOADED);
}, 0);
errorSpy();
});
source.on('tileloadend', function () {
expect(startSpy.callCount).to.be(2);
expect(errorSpy.callCount).to.be(1);
done();
});
const tile = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
tile.load();
});
});
});
@@ -0,0 +1,223 @@
import Source from '../../../../../src/ol/source/Source.js';
import TileJSON from '../../../../../src/ol/source/TileJSON.js';
import {transformExtent} from '../../../../../src/ol/proj.js';
import {unByKey} from '../../../../../src/ol/Observable.js';
describe('ol.source.TileJSON', function () {
describe('constructor', function () {
it('returns a tileJSON source', function () {
const source = new TileJSON({
url: 'spec/ol/data/tilejson.json',
});
expect(source).to.be.a(Source);
expect(source).to.be.a(TileJSON);
});
});
describe('#getTileJSON', function () {
it('parses the tilejson file', function () {
const source = new TileJSON({
url: 'spec/ol/data/tilejson.json',
});
source.on('change', function () {
if (source.getState() === 'ready') {
const tileJSON = source.getTileJSON();
expect(tileJSON.name).to.eql('Geography Class');
expect(tileJSON.version).to.eql('1.0.0');
}
});
});
const tileJSON = {
attribution: 'TileMill',
bounds: [-180, -85.05112877980659, 180, 85.05112877980659],
center: [0, 0, 4],
created: 1322764050886,
description:
'One of the example maps that comes with TileMill - a bright & colorful world map that blends retro and high-tech with its folded paper texture and interactive flag tooltips. ',
download: 'https://a.tiles.mapbox.com/v3/mapbox.geography-class.mbtiles',
embed: 'https://a.tiles.mapbox.com/v3/mapbox.geography-class.html',
id: 'mapbox.geography-class',
mapbox_logo: true,
maxzoom: 8,
minzoom: 0,
name: 'Geography Class',
private: false,
scheme: 'xyz',
tilejson: '2.2.0',
tiles: [
'https://a.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
'https://b.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
'https://c.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
'https://d.tiles.mapbox.com/v3/mapbox.geography-class/{z}/{x}/{y}.png',
],
version: '1.0.0',
webpage: 'https://a.tiles.mapbox.com/v3/mapbox.geography-class/page.html',
};
it('parses inline TileJSON', function () {
const source = new TileJSON({
tileJSON: tileJSON,
});
expect(source.getState()).to.be('ready');
expect(source.getTileUrlFunction()([0, 0, 0])).to.be(
'https://a.tiles.mapbox.com/v3/mapbox.geography-class/0/0/0.png'
);
expect(source.getTileUrlFunction()([1, 0, 0])).to.be(
'https://a.tiles.mapbox.com/v3/mapbox.geography-class/1/0/0.png'
);
expect(source.getTileUrlFunction()([1, 0, 1])).to.be(
'https://b.tiles.mapbox.com/v3/mapbox.geography-class/1/0/1.png'
);
expect(source.getTileUrlFunction()([1, 1, 0])).to.be(
'https://c.tiles.mapbox.com/v3/mapbox.geography-class/1/1/0.png'
);
expect(source.getTileUrlFunction()([1, 1, 1])).to.be(
'https://d.tiles.mapbox.com/v3/mapbox.geography-class/1/1/1.png'
);
});
it('returns attributions, but not when outside bounds', function () {
tileJSON.bounds = [
-10.764179999935878,
49.528423000201656,
1.9134115551745678,
61.3311509999582,
];
const source = new TileJSON({
tileJSON: tileJSON,
});
expect(source.getState()).to.be('ready');
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
const frameState = {};
frameState.extent = transformExtent(
[1, 51, 2, 52],
'EPSG:4326',
'EPSG:3857'
);
expect(attributions(frameState)).to.eql(['TileMill']);
frameState.extent = transformExtent(
[2, 51, 3, 52],
'EPSG:4326',
'EPSG:3857'
);
expect(attributions(frameState)).to.be(null);
});
it('attributions bounds default to the tilegrid extent', function () {
delete tileJSON.bounds;
const source = new TileJSON({
tileJSON: tileJSON,
});
expect(source.getState()).to.be('ready');
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
const frameState = {};
frameState.extent = transformExtent(
[1, 51, 2, 52],
'EPSG:4326',
'EPSG:3857'
);
expect(attributions(frameState)).to.eql(['TileMill']);
frameState.extent = transformExtent(
[2, 51, 3, 52],
'EPSG:4326',
'EPSG:3857'
);
expect(attributions(frameState)).to.eql(['TileMill']);
});
});
describe('#getState', function () {
it('returns error on HTTP 404', function () {
const source = new TileJSON({
url: 'invalid.jsonp',
});
source.on('change', function () {
expect(source.getState()).to.eql('error');
expect(source.getTileJSON()).to.eql(null);
});
});
it('returns error on CORS issues', function () {
const source = new TileJSON({
url: 'http://example.com',
});
source.on('change', function () {
expect(source.getState()).to.eql('error');
expect(source.getTileJSON()).to.eql(null);
});
});
it('returns error on JSON parsing issues', function () {
const source = new TileJSON({
url: '/',
});
source.on('change', function () {
expect(source.getState()).to.eql('error');
expect(source.getTileJSON()).to.eql(null);
});
});
});
describe('tileUrlFunction', function () {
let source, tileGrid;
beforeEach(function (done) {
source = new TileJSON({
url: 'spec/ol/data/tilejson.json',
});
const key = source.on('change', function () {
if (source.getState() === 'ready') {
unByKey(key);
tileGrid = source.getTileGrid();
done();
}
});
});
it('uses the correct tile coordinates', function () {
const coordinate = [829330.2064098881, 5933916.615134273];
const regex = /\/([0-9]*\/[0-9]*\/[0-9]*)\.png$/;
let tileUrl;
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 0)
);
expect(tileUrl.match(regex)[1]).to.eql('0/0/0');
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 1)
);
expect(tileUrl.match(regex)[1]).to.eql('1/1/0');
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 2)
);
expect(tileUrl.match(regex)[1]).to.eql('2/2/1');
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 3)
);
expect(tileUrl.match(regex)[1]).to.eql('3/4/2');
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 4)
);
expect(tileUrl.match(regex)[1]).to.eql('4/8/5');
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 5)
);
expect(tileUrl.match(regex)[1]).to.eql('5/16/11');
tileUrl = source.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 6)
);
expect(tileUrl.match(regex)[1]).to.eql('6/33/22');
});
});
});
+415
View File
@@ -0,0 +1,415 @@
import ImageTile from '../../../../../src/ol/ImageTile.js';
import TileGrid from '../../../../../src/ol/tilegrid/TileGrid.js';
import TileWMS from '../../../../../src/ol/source/TileWMS.js';
import {createXYZ} from '../../../../../src/ol/tilegrid.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.TileWMS', function () {
let options, optionsReproj;
beforeEach(function () {
options = {
params: {
'LAYERS': 'layer',
},
url: 'http://example.com/wms',
};
optionsReproj = {
params: {
'LAYERS': 'layer',
},
url: 'http://example.com/wms',
projection: 'EPSG:4326',
};
});
describe('constructor', function () {
it('can be constructed without url or urls params', function () {
const source = new TileWMS({
projection: 'EPSG:3857',
tileGrid: createXYZ({maxZoom: 6}),
});
expect(source).to.be.an(TileWMS);
});
});
describe('#getTile', function () {
it('returns a tile with the expected URL', function () {
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
expect(tile).to.be.an(ImageTile);
const uri = new URL(tile.src_);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',').map(parseFloat);
expect(bbox[0]).roughlyEqual(-10018754.171394622, 1e-9);
expect(bbox[1]).roughlyEqual(-15028131.257091936, 1e-9);
expect(bbox[2]).roughlyEqual(-5009377.085697311, 1e-9);
expect(bbox[3]).roughlyEqual(-10018754.171394624, 1e-9);
expect(queryData.get('CRS')).to.be('EPSG:3857');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('256');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetMap');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('256');
expect(uri.hash.replace('#', '')).to.be.empty();
});
it('returns a larger tile when a gutter is specified', function () {
options.gutter = 16;
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 6, 1, getProjection('EPSG:3857'));
expect(tile).to.be.an(ImageTile);
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',');
const expected = [
-10331840.239250705,
-15341217.324948018,
-4696291.017841229,
-9705668.103538541,
];
for (let i = 0, ii = bbox.length; i < ii; ++i) {
expect(parseFloat(bbox[i])).to.roughlyEqual(expected[i], 1e-9);
}
expect(queryData.get('HEIGHT')).to.be('288');
expect(queryData.get('WIDTH')).to.be('288');
});
it('sets the SRS query value instead of CRS if version < 1.3', function () {
options.params.VERSION = '1.2';
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('EPSG:4326'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('CRS')).to.be(null);
expect(queryData.get('SRS')).to.be('EPSG:4326');
});
it('allows various parameters to be overridden', function () {
options.params.FORMAT = 'image/jpeg';
options.params.TRANSPARENT = false;
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('EPSG:4326'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('image/jpeg');
expect(queryData.get('TRANSPARENT')).to.be('false');
});
it('does not add a STYLES= option if one is specified', function () {
options.params.STYLES = 'foo';
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('EPSG:4326'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('STYLES')).to.be('foo');
});
it('changes the BBOX order for EN axis orientations', function () {
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('EPSG:4326'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('-45,-90,0,-45');
});
it('uses EN BBOX order if version < 1.3', function () {
options.params.VERSION = '1.1.0';
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 1, getProjection('CRS:84'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('-90,-45,-45,0');
});
it('sets FORMAT_OPTIONS when the server is GeoServer', function () {
options.serverType = 'geoserver';
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 2, getProjection('CRS:84'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT_OPTIONS')).to.be('dpi:180');
});
it('extends FORMAT_OPTIONS if it is already present', function () {
options.serverType = 'geoserver';
const source = new TileWMS(options);
options.params.FORMAT_OPTIONS = 'param1:value1';
const tile = source.getTile(3, 2, 2, 2, getProjection('CRS:84'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT_OPTIONS')).to.be('param1:value1;dpi:180');
});
it('rounds FORMAT_OPTIONS to an integer when the server is GeoServer', function () {
options.serverType = 'geoserver';
const source = new TileWMS(options);
const tile = source.getTile(3, 2, 2, 1.325, getProjection('CRS:84'));
const uri = new URL(tile.src_);
const queryData = uri.searchParams;
expect(queryData.get('FORMAT_OPTIONS')).to.be('dpi:119');
});
});
describe('#tileUrlFunction', function () {
it('can be used when obtained through #getTileUrlFunction', function () {
options.extent = [-80, -40, -50, -10];
const source = new TileWMS(options);
const tileCoord = [3, 2, 2];
expect(function () {
source.getTileUrlFunction()(tileCoord, 1, getProjection('EPSG:4326'));
}).to.not.throwException();
});
it('returns a tile if it is contained within layers extent', function () {
options.extent = [-80, -40, -50, -10];
const source = new TileWMS(options);
const tileCoord = [3, 2, 2];
const url = source.tileUrlFunction(
tileCoord,
1,
getProjection('EPSG:4326')
);
const uri = new URL(url);
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('-45,-90,0,-45');
});
it('returns a tile if it intersects layers extent', function () {
options.extent = [-80, -40, -40, -10];
const source = new TileWMS(options);
const tileCoord = [3, 3, 2];
const url = source.tileUrlFunction(
tileCoord,
1,
getProjection('EPSG:4326')
);
const uri = new URL(url);
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be('-45,-45,0,0');
});
it('works with non-square tiles', function () {
options.tileGrid = new TileGrid({
tileSize: [640, 320],
resolutions: [1.40625, 0.703125, 0.3515625, 0.17578125],
origin: [-180, -90],
});
const source = new TileWMS(options);
const tileCoord = [3, 3, 2];
const url = source.tileUrlFunction(
tileCoord,
1,
getProjection('EPSG:4326')
);
const uri = new URL(url);
const queryData = uri.searchParams;
expect(queryData.get('WIDTH')).to.be('640');
expect(queryData.get('HEIGHT')).to.be('320');
});
});
describe('#getFeatureInfoUrl', function () {
it('returns the expected GetFeatureInfo URL', function () {
const source = new TileWMS(options);
source.pixelRatio_ = 1;
const url = source.getFeatureInfoUrl(
[-7000000, -12000000],
19567.87924100512,
getProjection('EPSG:3857'),
{INFO_FORMAT: 'text/plain'}
);
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',').map(parseFloat);
expect(bbox[0]).roughlyEqual(-10018754.171394622, 1e-9);
expect(bbox[1]).roughlyEqual(-15028131.257091936, 1e-9);
expect(bbox[2]).roughlyEqual(-5009377.085697311, 1e-9);
expect(bbox[3]).roughlyEqual(-10018754.171394624, 1e-9);
expect(queryData.get('CRS')).to.be('EPSG:3857');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('256');
expect(queryData.get('I')).to.be('154');
expect(queryData.get('J')).to.be('101');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('QUERY_LAYERS')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetFeatureInfo');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('256');
expect(uri.hash.replace('#', '')).to.be.empty();
});
it("returns the expected GetFeatureInfo URL when source's projection is different from the parameter", function () {
const source = new TileWMS(optionsReproj);
source.pixelRatio_ = 1;
const url = source.getFeatureInfoUrl(
[-7000000, -12000000],
19567.87924100512,
getProjection('EPSG:3857'),
{INFO_FORMAT: 'text/plain'}
);
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('BBOX')).to.be(
'-79.17133464081945,-90,-66.51326044311186,-45'
);
expect(queryData.get('CRS')).to.be('EPSG:4326');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('256');
expect(queryData.get('I')).to.be('517');
expect(queryData.get('J')).to.be('117');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('QUERY_LAYERS')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetFeatureInfo');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('256');
expect(uri.hash.replace('#', '')).to.be.empty();
});
it('sets the QUERY_LAYERS param as expected', function () {
const source = new TileWMS(options);
source.pixelRatio_ = 1;
const url = source.getFeatureInfoUrl(
[-7000000, -12000000],
19567.87924100512,
getProjection('EPSG:3857'),
{INFO_FORMAT: 'text/plain', QUERY_LAYERS: 'foo,bar'}
);
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
const bbox = queryData.get('BBOX').split(',').map(parseFloat);
expect(bbox[0]).roughlyEqual(-10018754.171394622, 1e-9);
expect(bbox[1]).roughlyEqual(-15028131.257091936, 1e-9);
expect(bbox[2]).roughlyEqual(-5009377.085697311, 1e-9);
expect(bbox[3]).roughlyEqual(-10018754.171394624, 1e-9);
expect(queryData.get('CRS')).to.be('EPSG:3857');
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('HEIGHT')).to.be('256');
expect(queryData.get('I')).to.be('154');
expect(queryData.get('J')).to.be('101');
expect(queryData.get('LAYERS')).to.be('layer');
expect(queryData.get('QUERY_LAYERS')).to.be('foo,bar');
expect(queryData.get('REQUEST')).to.be('GetFeatureInfo');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('SRS')).to.be(null);
expect(queryData.get('STYLES')).to.be('');
expect(queryData.get('TRANSPARENT')).to.be('true');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('WIDTH')).to.be('256');
expect(uri.hash.replace('#', '')).to.be.empty();
});
});
describe('#getLegendGraphicUrl', function () {
it('returns the getLegenGraphic url as expected', function () {
const source = new TileWMS(options);
const url = source.getLegendUrl(0.1);
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('image/png');
expect(queryData.get('LAYER')).to.be('layer');
expect(queryData.get('REQUEST')).to.be('GetLegendGraphic');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('SCALE')).to.be('357.14285714285717');
});
it('does not include SCALE if no resolution was provided', function () {
const source = new TileWMS(options);
const url = source.getLegendUrl();
const uri = new URL(url);
const queryData = uri.searchParams;
expect(queryData.get('SCALE')).to.be(null);
});
it('adds additional params as expected', function () {
const source = new TileWMS(options);
const url = source.getLegendUrl(0.1, {
STYLE: 'STYLE_VALUE',
FEATURETYPE: 'FEATURETYPE_VALUE',
RULE: 'RULE_VALUE',
SLD: 'SLD_VALUE',
SLD_BODY: 'SLD_BODY_VALUE',
FORMAT: 'FORMAT_VALUE',
WIDTH: 'WIDTH_VALUE',
HEIGHT: 'HEIGHT_VALUE',
EXCEPTIONS: 'EXCEPTIONS_VALUE',
LANGUAGE: 'LANGUAGE_VALUE',
LAYER: 'LAYER_VALUE',
});
const uri = new URL(url);
expect(uri.protocol).to.be('http:');
expect(uri.hostname).to.be('example.com');
expect(uri.pathname).to.be('/wms');
const queryData = uri.searchParams;
expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE');
expect(queryData.get('LAYER')).to.be('LAYER_VALUE');
expect(queryData.get('REQUEST')).to.be('GetLegendGraphic');
expect(queryData.get('SERVICE')).to.be('WMS');
expect(queryData.get('VERSION')).to.be('1.3.0');
expect(queryData.get('SCALE')).to.be('357.14285714285717');
expect(queryData.get('STYLE')).to.be('STYLE_VALUE');
expect(queryData.get('FEATURETYPE')).to.be('FEATURETYPE_VALUE');
expect(queryData.get('RULE')).to.be('RULE_VALUE');
expect(queryData.get('SLD')).to.be('SLD_VALUE');
expect(queryData.get('SLD_BODY')).to.be('SLD_BODY_VALUE');
expect(queryData.get('FORMAT')).to.be('FORMAT_VALUE');
expect(queryData.get('WIDTH')).to.be('WIDTH_VALUE');
expect(queryData.get('HEIGHT')).to.be('HEIGHT_VALUE');
expect(queryData.get('EXCEPTIONS')).to.be('EXCEPTIONS_VALUE');
expect(queryData.get('LANGUAGE')).to.be('LANGUAGE_VALUE');
});
});
describe('#setUrl()', function () {
it('sets the correct url', function () {
const source = new TileWMS(options);
const url = 'http://foo/';
source.setUrl(url);
const tileUrl = source.tileUrlFunction(
[0, 0, 0],
1,
getProjection('EPSG:4326')
);
expect(tileUrl.indexOf(url)).to.be(0);
});
});
describe('#setUrls()', function () {
it('updates the source key', function () {
const source = new TileWMS({
urls: ['u1', 'u2'],
});
const originalKey = source.getKey();
source.setUrls(['u3', 'u4']);
expect(source.getKey() !== originalKey).to.be(true);
});
});
});
+210
View File
@@ -0,0 +1,210 @@
import UrlTile from '../../../../../src/ol/source/UrlTile.js';
import {createXYZ} from '../../../../../src/ol/tilegrid.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.UrlTile', function () {
describe('#setUrl()', function () {
it('sets the URL for the source', function () {
const source = new UrlTile({});
const url = 'https://example.com/';
source.setUrl(url);
expect(source.getUrls()).to.eql([url]);
});
it('updates the key for the source', function () {
const source = new UrlTile({});
const url = 'https://example.com/';
source.setUrl(url);
expect(source.getKey()).to.eql(url);
});
});
describe('#setUrls()', function () {
it('sets the URL for the source', function () {
const source = new UrlTile({});
const urls = [
'https://a.example.com/',
'https://b.example.com/',
'https://c.example.com/',
];
source.setUrls(urls);
expect(source.getUrls()).to.eql(urls);
});
it('updates the key for the source', function () {
const source = new UrlTile({});
const urls = [
'https://a.example.com/',
'https://b.example.com/',
'https://c.example.com/',
];
source.setUrls(urls);
expect(source.getKey()).to.eql(urls.join('\n'));
});
});
describe('url option', function () {
it('expands url template', function () {
const tileSource = new UrlTile({
url: '{1-3}',
});
const urls = tileSource.getUrls();
expect(urls).to.eql(['1', '2', '3']);
});
});
describe('tileUrlFunction', function () {
let tileSource, tileGrid;
beforeEach(function () {
tileSource = new UrlTile({
projection: 'EPSG:3857',
tileGrid: createXYZ({maxZoom: 6}),
url: '{z}/{x}/{y}',
wrapX: true,
});
tileGrid = tileSource.getTileGrid();
});
it('returns the expected URL', function () {
const coordinate = [829330.2064098881, 5933916.615134273];
let tileUrl;
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 0)
);
expect(tileUrl).to.eql('0/0/0');
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 1)
);
expect(tileUrl).to.eql('1/1/0');
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 2)
);
expect(tileUrl).to.eql('2/2/1');
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 3)
);
expect(tileUrl).to.eql('3/4/2');
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 4)
);
expect(tileUrl).to.eql('4/8/5');
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 5)
);
expect(tileUrl).to.eql('5/16/11');
tileUrl = tileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 6)
);
expect(tileUrl).to.eql('6/33/22');
});
describe('wrap x', function () {
it('returns the expected URL', function () {
const projection = tileSource.getProjection();
let tileUrl = tileSource.tileUrlFunction(
tileSource.getTileCoordForTileUrlFunction([6, -31, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
tileUrl = tileSource.tileUrlFunction(
tileSource.getTileCoordForTileUrlFunction([6, 33, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
tileUrl = tileSource.tileUrlFunction(
tileSource.getTileCoordForTileUrlFunction([6, 97, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
});
});
describe('crop y', function () {
it('returns the expected URL', function () {
const projection = tileSource.getProjection();
let tileUrl = tileSource.tileUrlFunction(
tileSource.getTileCoordForTileUrlFunction([6, 33, -1], projection)
);
expect(tileUrl).to.be(undefined);
tileUrl = tileSource.tileUrlFunction(
tileSource.getTileCoordForTileUrlFunction([6, 33, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
tileUrl = tileSource.tileUrlFunction(
tileSource.getTileCoordForTileUrlFunction([6, 33, 64], projection)
);
expect(tileUrl).to.be(undefined);
});
});
});
describe('#getUrls', function () {
let sourceOptions;
let source;
const url = 'http://geo.nls.uk/maps/towns/glasgow1857/{z}/{x}/{-y}.png';
beforeEach(function () {
sourceOptions = {
tileGrid: createXYZ({
extent: getProjection('EPSG:4326').getExtent(),
}),
};
});
describe('using a "url" option', function () {
beforeEach(function () {
sourceOptions.url = url;
source = new UrlTile(sourceOptions);
});
it('returns the XYZ URL', function () {
const urls = source.getUrls();
expect(urls).to.be.eql([url]);
});
});
describe('using a "urls" option', function () {
beforeEach(function () {
sourceOptions.urls = ['some_xyz_url1', 'some_xyz_url2'];
source = new UrlTile(sourceOptions);
});
it('returns the XYZ URLs', function () {
const urls = source.getUrls();
expect(urls).to.be.eql(['some_xyz_url1', 'some_xyz_url2']);
});
});
describe('using a "tileUrlFunction"', function () {
beforeEach(function () {
sourceOptions.tileUrlFunction = function () {
return 'some_xyz_url';
};
source = new UrlTile(sourceOptions);
});
it('returns null', function () {
const urls = source.getUrls();
expect(urls).to.be(null);
});
});
});
});
+282
View File
@@ -0,0 +1,282 @@
import TileGrid from '../../../../../src/ol/tilegrid/TileGrid.js';
import TileSource from '../../../../../src/ol/source/Tile.js';
import UTFGrid, {CustomTile} from '../../../../../src/ol/source/UTFGrid.js';
import {
fromLonLat,
get as getProjection,
transformExtent,
} from '../../../../../src/ol/proj.js';
describe('ol.source.UTFGrid', function () {
const url = 'spec/ol/data/utfgrid.json';
let tileJson = null;
// Load and parse the UTFGrid fixture
before(function (done) {
const client = new XMLHttpRequest();
client.addEventListener('load', function () {
tileJson = JSON.parse(this.responseText);
done();
});
client.addEventListener('error', function () {
done(new Error('Failed to fetch ' + url));
});
client.open('GET', url);
client.send();
});
after(function () {
tileJson = null;
});
function getUTFGrid() {
return new UTFGrid({
url: url,
});
}
describe('constructor', function () {
it('needs to be constructed with url option', function () {
const source = new UTFGrid({url: url});
expect(source).to.be.an(UTFGrid);
expect(source).to.be.an(TileSource);
expect(function () {
// no options: will throw
return new UTFGrid();
}).to.throwException();
expect(function () {
// no url-option: will throw
return new UTFGrid({});
}).to.throwException();
expect(getUTFGrid()).to.be.an(UTFGrid);
});
});
describe('change event (ready)', function () {
it('is fired when the source is ready', function (done) {
const source = new UTFGrid({
url: url,
});
expect(source.getState()).to.be('loading');
expect(source.tileGrid).to.be(null);
source.on('change', function (event) {
if (source.getState() === 'ready') {
expect(source.tileGrid).to.be.an(TileGrid);
done();
}
});
});
});
describe('change event (error)', function (done) {
it('is fired when the source fails to initialize', function (done) {
const source = new UTFGrid({
url: 'Bogus UTFGrid URL',
});
expect(source.getState()).to.be('loading');
expect(source.tileGrid).to.be(null);
source.on('change', function (event) {
if (source.getState() === 'error') {
expect(source.tileGrid).to.be(null);
done();
}
});
});
});
describe('#handleTileJSONResponse', function () {
it('sets up a tileGrid', function () {
const source = getUTFGrid();
expect(source.getTileGrid()).to.be(null);
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
const tileGrid = source.getTileGrid();
expect(tileGrid).to.not.be(null);
expect(tileGrid).to.be.an(TileGrid);
});
it('sets up a tilegrid with expected extent', function () {
const source = getUTFGrid();
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
const tileGrid = source.getTileGrid();
const extent = tileGrid.getExtent();
const proj4326 = getProjection('EPSG:4326');
const proj3857 = getProjection('EPSG:3857');
const expectedExtent4326 = tileJson.bounds;
const expectedExtent3857 = transformExtent(
expectedExtent4326,
proj4326,
proj3857
);
expect(extent).to.eql(proj3857.getExtent());
expect(extent[0]).to.roughlyEqual(expectedExtent3857[0], 1e-8);
expect(extent[1]).to.roughlyEqual(expectedExtent3857[1], 1e-8);
expect(extent[2]).to.roughlyEqual(expectedExtent3857[2], 1e-8);
expect(extent[3]).to.roughlyEqual(expectedExtent3857[3], 1e-8);
});
it('sets up a tilegrid with expected minZoom', function () {
const source = getUTFGrid();
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
const tileGrid = source.getTileGrid();
const minZoom = tileGrid.getMinZoom();
expect(minZoom).to.eql(tileJson.minzoom);
});
it('sets up a tilegrid with expected maxZoom', function () {
const source = getUTFGrid();
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
const tileGrid = source.getTileGrid();
const maxZoom = tileGrid.getMaxZoom();
expect(maxZoom).to.eql(tileJson.maxzoom);
});
it('sets up a template', function () {
const source = getUTFGrid();
expect(source.getTemplate()).to.be(undefined);
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
const template = source.getTemplate();
expect(template).to.not.be(undefined);
expect(template).to.be(tileJson.template);
});
it('sets up correct attribution', function () {
const source = getUTFGrid();
expect(source.getAttributions()).to.be(null);
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
const attributions = source.getAttributions();
expect(attributions).to.not.be(null);
expect(typeof attributions).to.be('function');
});
it('sets correct state', function () {
const source = getUTFGrid();
expect(source.getState()).to.be('loading');
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
expect(source.getState()).to.be('ready');
});
});
describe('#forDataAtCoordinateAndResolution', function () {
let source = null;
const bonn3857 = fromLonLat([7.099814, 50.733992]);
const noState3857 = [0, 0];
const resolutionZoom1 = 78271.51696402048;
let gridJson110 = null;
// Called once for this describe section, this method will request a local
// grid for one tile (1/1/0) and store the result in a variable. This allows
// us to overwrite getTile in a way that removes the dependency on an
// external service. See below in the `beforeEach`-method.
before(function (done) {
const client = new XMLHttpRequest();
client.addEventListener('load', function () {
gridJson110 = JSON.parse(this.responseText);
done();
});
client.addEventListener('error', function () {
done(new Error('Failed to fetch local grid.json'));
});
client.open('GET', 'spec/ol/data/mapbox-geography-class-1-1-0.grid.json');
client.send();
});
after(function () {
gridJson110 = null;
});
beforeEach(function () {
source = getUTFGrid();
// call the handleTileJSONResponse method with our
// locally available tileJson (from `before`)
source.handleTileJSONResponse(tileJson);
// Override getTile method to not depend on the external service. The
// signature of the method is kept the same, but the returned tile will
// always be for [1, 1, 0].
source.getTile = function (z, x, y, pixelRatio, projection) {
const tileCoord = [1, 1, 0]; // overwritten to match our stored JSON
const urlTileCoord = this.getTileCoordForTileUrlFunction(
tileCoord,
projection
);
const tileUrl = this.tileUrlFunction_(
urlTileCoord,
pixelRatio,
projection
);
const tile = new CustomTile(
tileCoord,
tileUrl !== undefined ? 0 : 4, // IDLE : EMPTY
tileUrl !== undefined ? tileUrl : '',
this.tileGrid.getTileCoordExtent(tileCoord),
true
); // always preemptive, so loading doesn't happen automatically
// manually call handleLoad_ with our local JSON data
tile.handleLoad_(gridJson110);
return tile;
};
});
afterEach(function () {
source = null;
});
it('calls callback with data if found', function (done) {
const callback = function (data) {
expect(arguments).to.have.length(1);
expect(data).to.not.be(null);
expect('admin' in data).to.be(true);
expect(data.admin).to.be('Germany');
done();
};
source.forDataAtCoordinateAndResolution(
bonn3857,
resolutionZoom1,
callback,
true
);
});
it('calls callback with `null` if not found', function (done) {
const callback = function (data) {
expect(arguments).to.have.length(1);
expect(data).to.be(null);
done();
};
source.forDataAtCoordinateAndResolution(
noState3857,
resolutionZoom1,
callback,
true
);
});
});
});
+941
View File
@@ -0,0 +1,941 @@
import Collection from '../../../../../src/ol/Collection.js';
import Feature from '../../../../../src/ol/Feature.js';
import GeoJSON from '../../../../../src/ol/format/GeoJSON.js';
import LineString from '../../../../../src/ol/geom/LineString.js';
import Map from '../../../../../src/ol/Map.js';
import Point from '../../../../../src/ol/geom/Point.js';
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import View from '../../../../../src/ol/View.js';
import {bbox as bboxStrategy} from '../../../../../src/ol/loadingstrategy.js';
import {
fromLonLat,
get as getProjection,
transformExtent,
} from '../../../../../src/ol/proj.js';
import {getUid} from '../../../../../src/ol/util.js';
import {listen} from '../../../../../src/ol/events.js';
describe('ol.source.Vector', function () {
let pointFeature;
let infiniteExtent;
beforeEach(function () {
pointFeature = new Feature(new Point([0, 0]));
infiniteExtent = [-Infinity, -Infinity, Infinity, Infinity];
});
describe('when empty', function () {
let vectorSource;
beforeEach(function () {
vectorSource = new VectorSource();
});
describe('#forEachFeatureInExtent', function () {
it('does not call the callback', function () {
const f = sinon.spy();
vectorSource.forEachFeatureInExtent(infiniteExtent, f);
expect(f.called).to.be(false);
});
});
describe('#getFeaturesInExtent', function () {
it('returns an empty array', function () {
const features = vectorSource.getFeaturesInExtent(infiniteExtent);
expect(features).to.be.an(Array);
expect(features).to.be.empty();
});
});
describe('#isEmpty', function () {
it('returns true', function () {
expect(vectorSource.isEmpty()).to.be(true);
});
});
describe('#addFeature', function () {
it('can add a single point feature', function () {
vectorSource.addFeature(pointFeature);
const features = vectorSource.getFeaturesInExtent(infiniteExtent);
expect(features).to.be.an(Array);
expect(features).to.have.length(1);
expect(features[0]).to.be(pointFeature);
});
it('fires a change event', function () {
const listener = sinon.spy();
listen(vectorSource, 'change', listener);
vectorSource.addFeature(pointFeature);
expect(listener.called).to.be(true);
});
it('adds same id features only once', function () {
const source = new VectorSource();
const feature1 = new Feature();
feature1.setId('1');
const feature2 = new Feature();
feature2.setId('1');
source.addFeature(feature1);
source.addFeature(feature2);
expect(source.getFeatures().length).to.be(1);
});
});
describe('#hasFeature', function () {
it('returns true for added feature without id', function () {
const feature = new Feature();
vectorSource.addFeature(feature);
expect(vectorSource.hasFeature(feature)).to.be(true);
});
it('returns true for added feature with id', function () {
const feature = new Feature();
feature.setId('1');
vectorSource.addFeature(feature);
expect(vectorSource.hasFeature(feature)).to.be(true);
});
it('return false for removed feature', function () {
const feature = new Feature();
vectorSource.addFeature(feature);
vectorSource.removeFeature(feature);
expect(vectorSource.hasFeature(feature)).to.be(false);
});
it('returns false for non-added feature', function () {
const feature = new Feature();
expect(vectorSource.hasFeature(feature)).to.be(false);
});
});
});
describe('when populated with 3 features', function () {
const features = [];
let vectorSource;
beforeEach(function () {
features.push(
new Feature(
new LineString([
[0, 0],
[10, 10],
])
)
);
features.push(new Feature(new Point([0, 10])));
features.push(new Feature(new Point([10, 5])));
vectorSource = new VectorSource({
features: features,
});
});
describe('#getClosestFeatureToCoordinate', function () {
it('returns the expected feature', function () {
const feature = vectorSource.getClosestFeatureToCoordinate([1, 9]);
expect(feature).to.be(features[1]);
});
it('returns the expected feature when a filter is used', function () {
const feature = vectorSource.getClosestFeatureToCoordinate(
[1, 9],
function (feature) {
return feature.getGeometry().getType() == 'LineString';
}
);
expect(feature).to.be(features[0]);
});
});
describe('#getFeatures', function () {
it('does not return the internal array when useSpatialIndex is false', function () {
const noSpatialIndexSource = new VectorSource({
useSpatialIndex: false,
features: vectorSource.getFeatures(),
});
expect(noSpatialIndexSource.getFeatures()).to.not.be(
noSpatialIndexSource.getFeaturesCollection().getArray()
);
});
});
});
describe('clear and refresh', function () {
let map, source, spy;
beforeEach(function (done) {
source = new VectorSource({
format: new GeoJSON(),
url: 'spec/ol/source/vectorsource/single-feature.json',
});
const target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
layers: [
new VectorLayer({
source: source,
}),
],
view: new View({
center: [0, 0],
zoom: 0,
}),
});
map.once('rendercomplete', function () {
spy = sinon.spy(source, 'loader_');
done();
});
});
afterEach(function () {
if (spy) {
source.loader_.restore();
}
document.body.removeChild(map.getTargetElement());
map.setTarget(null);
});
it('#refresh() reloads from server', function (done) {
expect(source.getFeatures()).to.have.length(1);
map.once('rendercomplete', function () {
expect(source.getFeatures()).to.have.length(1);
expect(spy.callCount).to.be(1);
done();
});
source.refresh();
});
it('#clear() removes all features from the source', function (done) {
expect(source.getFeatures()).to.have.length(1);
map.once('rendercomplete', function () {
expect(source.getFeatures()).to.have.length(0);
expect(spy.callCount).to.be(0);
done();
});
source.clear();
});
it('After #setUrl(), refresh() loads from the new url', function (done) {
source.loader_.restore();
spy = undefined;
expect(source.getFeatures()).to.have.length(1);
const oldCoordinates = source
.getFeatures()[0]
.getGeometry()
.getCoordinates();
map.on('rendercomplete', function () {
expect(source.getFeatures()).to.have.length(1);
const newCoordinates = source
.getFeatures()[0]
.getGeometry()
.getCoordinates();
expect(newCoordinates).to.not.eql(oldCoordinates);
done();
});
source.setUrl('spec/ol/data/point.json');
source.refresh();
});
});
describe('when populated with 10 random points and a null', function () {
let features;
let vectorSource;
beforeEach(function () {
features = [];
let i;
for (i = 0; i < 10; ++i) {
features[i] = new Feature(new Point([Math.random(), Math.random()]));
}
features.push(new Feature(null));
vectorSource = new VectorSource({
features: features,
});
});
describe('#clear', function () {
it('removes all features using fast path', function () {
const removeFeatureSpy = sinon.spy();
listen(vectorSource, 'removefeature', removeFeatureSpy);
const clearSourceSpy = sinon.spy();
listen(vectorSource, 'clear', clearSourceSpy);
vectorSource.clear(true);
expect(vectorSource.getFeatures()).to.eql([]);
expect(vectorSource.isEmpty()).to.be(true);
expect(removeFeatureSpy.called).to.be(false);
expect(removeFeatureSpy.callCount).to.be(0);
expect(clearSourceSpy.called).to.be(true);
expect(clearSourceSpy.callCount).to.be(1);
});
it('removes all features using slow path', function () {
const removeFeatureSpy = sinon.spy();
listen(vectorSource, 'removefeature', removeFeatureSpy);
const clearSourceSpy = sinon.spy();
listen(vectorSource, 'clear', clearSourceSpy);
vectorSource.clear();
expect(vectorSource.getFeatures()).to.eql([]);
expect(vectorSource.isEmpty()).to.be(true);
expect(removeFeatureSpy.called).to.be(true);
expect(removeFeatureSpy.callCount).to.be(features.length);
expect(clearSourceSpy.called).to.be(true);
expect(clearSourceSpy.callCount).to.be(1);
});
});
describe('#forEachFeatureInExtent', function () {
it('is called the expected number of times', function () {
const f = sinon.spy();
vectorSource.forEachFeatureInExtent(infiniteExtent, f);
expect(f.callCount).to.be(10);
});
it('allows breaking out', function () {
let count = 0;
const result = vectorSource.forEachFeatureInExtent(
infiniteExtent,
function (f) {
return ++count == 5;
}
);
expect(result).to.be(true);
expect(count).to.be(5);
});
});
describe('#getFeaturesInExtent', function () {
it('returns the expected number of features', function () {
expect(vectorSource.getFeaturesInExtent(infiniteExtent)).to.have.length(
10
);
});
});
describe('#isEmpty', function () {
it('returns false', function () {
expect(vectorSource.isEmpty()).to.be(false);
});
});
describe('#removeFeature', function () {
it('works as expected', function () {
let i;
for (i = features.length - 1; i >= 0; --i) {
vectorSource.removeFeature(features[i]);
expect(vectorSource.getFeaturesInExtent(infiniteExtent)).have.length(
i
);
}
});
it('fires a change event', function () {
const listener = sinon.spy();
listen(vectorSource, 'change', listener);
vectorSource.removeFeature(features[0]);
expect(listener.called).to.be(true);
});
it('fires a removefeature event', function () {
const listener = sinon.spy();
listen(vectorSource, 'removefeature', listener);
vectorSource.removeFeature(features[0]);
expect(listener.called).to.be(true);
});
});
describe("modifying a feature's geometry", function () {
it('keeps the R-Tree index up to date', function () {
expect(vectorSource.getFeaturesInExtent([0, 0, 1, 1])).to.have.length(
10
);
features[0].getGeometry().setCoordinates([100, 100]);
expect(vectorSource.getFeaturesInExtent([0, 0, 1, 1])).to.have.length(
9
);
features[0].getGeometry().setCoordinates([0.5, 0.5]);
expect(vectorSource.getFeaturesInExtent([0, 0, 1, 1])).to.have.length(
10
);
});
});
describe('setting a features geometry', function () {
it('keeps the R-Tree index up to date', function () {
expect(vectorSource.getFeaturesInExtent([0, 0, 1, 1])).to.have.length(
10
);
features[0].setGeometry(new Point([100, 100]));
expect(vectorSource.getFeaturesInExtent([0, 0, 1, 1])).to.have.length(
9
);
});
});
});
describe('tracking changes to features', function () {
let vectorSource;
beforeEach(function () {
vectorSource = new VectorSource();
});
it('keeps its index up-to-date', function () {
const feature = new Feature(new Point([1, 1]));
vectorSource.addFeature(feature);
expect(vectorSource.getFeaturesInExtent([0, 0, 2, 2])).to.eql([feature]);
feature.getGeometry().setCoordinates([3, 3]);
expect(vectorSource.getFeaturesInExtent([0, 0, 2, 2])).to.be.empty();
expect(vectorSource.getFeaturesInExtent([2, 2, 4, 4])).to.eql([feature]);
});
it('handles features with null geometries', function () {
const feature = new Feature(null);
vectorSource.addFeature(feature);
expect(vectorSource.getFeatures()).to.eql([feature]);
});
it('handles features with geometries changing from null', function () {
const feature = new Feature(null);
vectorSource.addFeature(feature);
expect(vectorSource.getFeatures()).to.eql([feature]);
feature.setGeometry(new Point([1, 1]));
expect(vectorSource.getFeaturesInExtent([0, 0, 2, 2])).to.eql([feature]);
expect(vectorSource.getFeatures()).to.eql([feature]);
});
it('handles features with geometries changing to null', function () {
const feature = new Feature(new Point([1, 1]));
vectorSource.addFeature(feature);
expect(vectorSource.getFeatures()).to.eql([feature]);
expect(vectorSource.getFeaturesInExtent([0, 0, 2, 2])).to.eql([feature]);
feature.setGeometry(null);
expect(vectorSource.getFeaturesInExtent([0, 0, 2, 2])).to.be.empty();
expect(vectorSource.getFeatures()).to.eql([feature]);
});
it("fires a change event when setting a feature's property", function () {
const feature = new Feature(new Point([1, 1]));
vectorSource.addFeature(feature);
const listener = sinon.spy();
listen(vectorSource, 'change', listener);
feature.set('foo', 'bar');
expect(listener.called).to.be(true);
});
it('fires a changefeature event when updating a feature', function () {
const feature = new Feature(new Point([1, 1]));
vectorSource.addFeature(feature);
const listener = sinon.spy(function (event) {
expect(event.feature).to.be(feature);
});
vectorSource.on('changefeature', listener);
feature.setStyle(null);
expect(listener.called).to.be(true);
});
});
describe('#getFeatureById()', function () {
let source;
beforeEach(function () {
source = new VectorSource();
});
it('returns a feature by id', function () {
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
expect(source.getFeatureById('foo')).to.be(feature);
});
it('returns a feature by id (set after add)', function () {
const feature = new Feature();
source.addFeature(feature);
expect(source.getFeatureById('foo')).to.be(null);
feature.setId('foo');
expect(source.getFeatureById('foo')).to.be(feature);
});
it('returns null when no feature is found', function () {
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
expect(source.getFeatureById('bar')).to.be(null);
});
it('returns null after removing feature', function () {
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
expect(source.getFeatureById('foo')).to.be(feature);
source.removeFeature(feature);
expect(source.getFeatureById('foo')).to.be(null);
});
it('returns null after unsetting id', function () {
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
expect(source.getFeatureById('foo')).to.be(feature);
feature.setId(undefined);
expect(source.getFeatureById('foo')).to.be(null);
});
it('returns null after clear', function () {
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
expect(source.getFeatureById('foo')).to.be(feature);
source.clear();
expect(source.getFeatureById('foo')).to.be(null);
});
it('returns null when no features are indexed', function () {
expect(source.getFeatureById('foo')).to.be(null);
source.addFeature(new Feature());
expect(source.getFeatureById('foo')).to.be(null);
});
it('returns correct feature after add/remove/add', function () {
expect(source.getFeatureById('foo')).to.be(null);
const first = new Feature();
first.setId('foo');
source.addFeature(first);
expect(source.getFeatureById('foo')).to.be(first);
source.removeFeature(first);
expect(source.getFeatureById('foo')).to.be(null);
const second = new Feature();
second.setId('foo');
source.addFeature(second);
expect(source.getFeatureById('foo')).to.be(second);
});
it('returns correct feature after add/change', function () {
expect(source.getFeatureById('foo')).to.be(null);
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
expect(source.getFeatureById('foo')).to.be(feature);
feature.setId('bar');
expect(source.getFeatureById('foo')).to.be(null);
expect(source.getFeatureById('bar')).to.be(feature);
});
});
describe('#getFeatureByUid()', function () {
let source;
beforeEach(function () {
source = new VectorSource();
});
it('returns a feature with an id', function () {
const feature = new Feature();
feature.setId('abcd');
source.addFeature(feature);
expect(source.getFeatureByUid(getUid(feature))).to.be(feature);
});
it('returns a feature without id', function () {
const feature = new Feature();
source.addFeature(feature);
expect(source.getFeatureByUid(getUid(feature))).to.be(feature);
});
it('returns null when no feature is found', function () {
const feature = new Feature();
feature.setId('abcd');
source.addFeature(feature);
const wrongId = 'abcd';
expect(source.getFeatureByUid(wrongId)).to.be(null);
});
it('returns null after removing feature', function () {
const feature = new Feature();
feature.setId('abcd');
source.addFeature(feature);
const uid = getUid(feature);
expect(source.getFeatureByUid(uid)).to.be(feature);
source.removeFeature(feature);
expect(source.getFeatureByUid(uid)).to.be(null);
});
it('returns null after clear', function () {
const feature = new Feature();
feature.setId('abcd');
source.addFeature(feature);
const uid = getUid(feature);
expect(source.getFeatureByUid(uid)).to.be(feature);
source.clear();
expect(source.getFeatureByUid(uid)).to.be(null);
});
it('returns null when no features are present', function () {
expect(source.getFeatureByUid('abcd')).to.be(null);
});
});
describe('#loadFeatures', function () {
it('fires the FEATURESLOADSTART event', function (done) {
const source = new VectorSource();
source.on('featuresloadstart', function () {
done();
});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
});
it('fires the FEATURESLOADEND event after the features are added', function (done) {
const source = new VectorSource({
format: new GeoJSON(),
url: 'spec/ol/source/vectorsource/single-feature.json',
});
source.on('featuresloadend', function () {
const features = source.getFeatures();
expect(features).to.be.an('array');
expect(features.length).to.be(1);
done();
});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
});
it('fires the FEATURESLOADEND event if the default load function is used', function (done) {
const source = new VectorSource({
format: new GeoJSON(),
url: 'spec/ol/source/vectorsource/single-feature.json',
});
source.on('featuresloadend', function (event) {
expect(event.features).to.be.an('array');
expect(event.features.length).to.be(1);
done();
});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
});
describe('with the "bbox" strategy', function () {
it('requests the view extent plus render buffer', function (done) {
const center = [-97.6114, 38.8403];
const source = new VectorSource({
strategy: bboxStrategy,
loader: function (extent) {
setTimeout(function () {
const lonLatExtent = transformExtent(
extent,
'EPSG:3857',
'EPSG:4326'
);
expect(lonLatExtent[0]).to.roughlyEqual(-99.259349218, 1e-9);
expect(lonLatExtent[2]).to.roughlyEqual(-95.963450781, 1e-9);
done();
}, 0);
},
});
const div = document.createElement('div');
div.style.width = '100px';
div.style.height = '100px';
document.body.appendChild(div);
const map = new Map({
target: div,
layers: [
new VectorLayer({
source: source,
}),
],
view: new View({
center: fromLonLat(center),
zoom: 7,
}),
});
map.renderSync();
map.setTarget(null);
document.body.removeChild(div);
});
});
describe('with no loader and the "all" strategy', function () {
it('stores the infinity extent in the Rtree', function () {
const source = new VectorSource();
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
const loadedExtents = source.loadedExtentsRtree_.getAll();
expect(loadedExtents).to.have.length(1);
expect(loadedExtents[0].extent).to.eql([
-Infinity,
-Infinity,
Infinity,
Infinity,
]);
});
});
describe('with setLoader', function () {
it('it will change the loader function', function () {
let count1 = 0;
const loader1 = function (bbox, resolution, projection) {
count1++;
};
let count2 = 0;
const loader2 = function (bbox, resolution, projection) {
count2++;
};
const source = new VectorSource({loader: loader1});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
source.setLoader(loader2);
source.refresh();
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
expect(count1).to.eql(1);
expect(count2).to.eql(1);
});
it('removes extents with #removeLoadedExtent()', function (done) {
const source = new VectorSource();
source.setLoader(function (bbox, resolution, projection) {
setTimeout(function () {
expect(source.loadedExtentsRtree_.getAll()).to.have.length(1);
source.removeLoadedExtent(bbox);
expect(source.loadedExtentsRtree_.getAll()).to.have.length(0);
done();
}, 0);
});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
});
it('fires the FEATURESLOADEND event if the load function uses the callback', function (done) {
const source = new VectorSource();
const spy = sinon.spy();
source.on('featuresloadend', spy);
const features = [new Feature(), new Feature()];
source.setLoader(function (bbox, resolution, projection, success) {
success(features);
setTimeout(function () {
expect(spy.calledOnce).to.be(true);
const event = spy.getCall(0).args[0];
expect(event.features).to.be(features);
done();
}, 0);
});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
});
it('fires the FEATURESLOADERROR event if the load function uses the callback', function (done) {
const source = new VectorSource();
const spy = sinon.spy();
source.on('featuresloaderror', spy);
source.setLoader(function (
bbox,
resolution,
projection,
success,
failure
) {
failure();
setTimeout(function () {
expect(spy.calledOnce).to.be(true);
done();
}, 0);
});
source.loadFeatures(
[-10000, -10000, 10000, 10000],
1,
getProjection('EPSG:3857')
);
});
});
});
describe('the feature id index', function () {
let source;
beforeEach(function () {
source = new VectorSource();
});
it('ignores features with the same id', function () {
const feature = new Feature();
feature.setId('foo');
source.addFeature(feature);
const dupe = new Feature();
dupe.setId('foo');
source.addFeature(dupe);
expect(source.getFeatures()).to.have.length(1);
expect(source.getFeatureById('foo')).to.be(feature);
});
it('allows changing feature and set the same id', function () {
const foo = new Feature();
foo.setId('foo');
source.addFeature(foo);
const bar = new Feature();
bar.setId('bar');
source.addFeature(bar);
bar.setId('foo');
expect(source.getFeatureById('foo')).to.be(bar);
});
});
describe('the undefined feature id index', function () {
let source;
beforeEach(function () {
source = new VectorSource();
});
it('disallows adding the same feature twice', function () {
const feature = new Feature();
source.addFeature(feature);
expect(function () {
source.addFeature(feature);
}).to.throwException();
});
});
describe('with useSpatialIndex set to false', function () {
let source;
beforeEach(function () {
source = new VectorSource({useSpatialIndex: false});
});
it('returns a features collection', function () {
expect(source.getFeaturesCollection()).to.be.a(Collection);
});
it('#forEachFeatureInExtent loops through all features', function () {
source.addFeatures([new Feature(), new Feature()]);
const spy = sinon.spy();
source.forEachFeatureInExtent([0, 0, 0, 0], spy);
expect(spy.callCount).to.be(2);
});
});
describe('with a collection of features', function () {
let collection, source;
beforeEach(function () {
source = new VectorSource({
useSpatialIndex: false,
});
collection = source.getFeaturesCollection();
});
it('creates a features collection', function () {
expect(source.getFeaturesCollection()).to.not.be(null);
});
it('adding/removing features keeps the collection in sync', function () {
const feature = new Feature();
source.addFeature(feature);
expect(collection.getLength()).to.be(1);
source.removeFeature(feature);
expect(collection.getLength()).to.be(0);
});
it('#clear() features keeps the collection in sync', function () {
const feature = new Feature();
source.addFeatures([feature]);
expect(collection.getLength()).to.be(1);
source.clear();
expect(collection.getLength()).to.be(0);
source.addFeatures([feature]);
expect(collection.getLength()).to.be(1);
source.clear(true);
expect(collection.getLength()).to.be(0);
});
it("keeps the source's features in sync with the collection", function () {
const feature = new Feature();
collection.push(feature);
expect(source.getFeatures().length).to.be(1);
collection.remove(feature);
expect(source.getFeatures().length).to.be(0);
collection.extend([feature]);
expect(source.getFeatures().length).to.be(1);
collection.clear();
expect(source.getFeatures().length).to.be(0);
});
it('prevents adding two features with a duplicate id in the collection', function () {
source = new VectorSource({
features: new Collection(),
});
const feature1 = new Feature();
feature1.setId('1');
const feature2 = new Feature();
feature2.setId('1');
const collection = source.getFeaturesCollection();
collection.push(feature1);
collection.push(feature2);
expect(collection.getLength()).to.be(1);
});
});
describe('with a collection of features plus spatial index', function () {
let collection, source;
beforeEach(function () {
collection = new Collection();
source = new VectorSource({
features: collection,
});
});
it('#getFeaturesCollection returns the configured collection', function () {
expect(source.getFeaturesCollection()).to.equal(collection);
});
it('adding/removing features keeps the collection in sync', function () {
const feature = new Feature();
source.addFeature(feature);
expect(collection.getLength()).to.be(1);
source.removeFeature(feature);
expect(collection.getLength()).to.be(0);
});
it('#clear() features keeps the collection in sync', function () {
const feature = new Feature();
source.addFeatures([feature]);
expect(collection.getLength()).to.be(1);
source.clear();
expect(collection.getLength()).to.be(0);
source.addFeatures([feature]);
expect(collection.getLength()).to.be(1);
source.clear(true);
expect(collection.getLength()).to.be(0);
});
it("keeps the source's features in sync with the collection", function () {
const feature = new Feature();
collection.push(feature);
expect(source.getFeatures().length).to.be(1);
collection.remove(feature);
expect(source.getFeatures().length).to.be(0);
collection.extend([feature]);
expect(source.getFeatures().length).to.be(1);
collection.clear();
expect(source.getFeatures().length).to.be(0);
});
});
});
@@ -0,0 +1,16 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "point_1",
"geometry": {
"coordinates": [1, 2],
"type": "Point"
},
"properties": {
"name": "point"
}
}
]
}
@@ -0,0 +1,471 @@
import Feature from '../../../../../src/ol/Feature.js';
import GeoJSON from '../../../../../src/ol/format/GeoJSON.js';
import MVT from '../../../../../src/ol/format/MVT.js';
import Map from '../../../../../src/ol/Map.js';
import TileGrid from '../../../../../src/ol/tilegrid/TileGrid.js';
import TileState from '../../../../../src/ol/TileState.js';
import VectorRenderTile from '../../../../../src/ol/VectorRenderTile.js';
import VectorTile from '../../../../../src/ol/VectorTile.js';
import VectorTileLayer from '../../../../../src/ol/layer/VectorTile.js';
import VectorTileSource from '../../../../../src/ol/source/VectorTile.js';
import View from '../../../../../src/ol/View.js';
import {createXYZ} from '../../../../../src/ol/tilegrid.js';
import {fromExtent} from '../../../../../src/ol/geom/Polygon.js';
import {get, get as getProjection} from '../../../../../src/ol/proj.js';
import {getCenter} from '../../../../../src/ol/extent.js';
import {listen, unlistenByKey} from '../../../../../src/ol/events.js';
import {unByKey} from '../../../../../src/ol/Observable.js';
describe('ol.source.VectorTile', function () {
let format, source;
beforeEach(function () {
format = new MVT();
source = new VectorTileSource({
format: format,
url: 'spec/ol/data/{z}-{x}-{y}.vector.pbf',
});
});
describe('constructor', function () {
it('sets the format on the instance', function () {
expect(source.format_).to.equal(format);
});
it('sets the default zDirection on the instance', function () {
expect(source.zDirection).to.be(1);
});
it('uses ol.VectorTile as default tileClass', function () {
expect(source.tileClass).to.equal(VectorTile);
});
it('creates a 512 XYZ tilegrid by default', function () {
const tileGrid = createXYZ({tileSize: 512});
expect(source.tileGrid.tileSize_).to.equal(tileGrid.tileSize_);
expect(source.tileGrid.extent_).to.equal(tileGrid.extent_);
});
});
describe('#getTile()', function () {
it('creates a tile with the correct tile class', function () {
const tile = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
expect(tile).to.be.a(VectorRenderTile);
expect(tile.getTileCoord()).to.eql([0, 0, 0]);
expect(source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'))).to.equal(
tile
);
});
it('loads source tiles', function (done) {
const source = new VectorTileSource({
format: new GeoJSON(),
url: 'spec/ol/data/point.json',
});
const tile = source.getTile(0, 0, 0, 1, source.getProjection());
tile.load();
const key = listen(tile, 'change', function (e) {
if (tile.getState() === TileState.LOADED) {
const sourceTile = source.getSourceTiles(
1,
source.getProjection(),
tile
)[0];
expect(sourceTile.getFeatures().length).to.be.greaterThan(0);
unlistenByKey(key);
done();
}
});
});
it('handles empty tiles', function () {
const source = new VectorTileSource({
format: new GeoJSON(),
url: '',
});
const tile = source.getTile(0, 0, 0, 1, source.getProjection());
expect(tile.getState()).to.be(TileState.EMPTY);
});
it('creates empty tiles outside the source extent', function () {
const fullExtent = get('EPSG:3857').getExtent();
const source = new VectorTileSource({
extent: [fullExtent[0], fullExtent[1], 0, 0],
});
const tile = source.getTile(1, 1, 1, 1, source.getProjection());
expect(tile.getState()).to.be(TileState.EMPTY);
});
it('creates empty tiles outside the world extent when wrapX === false', function () {
const source = new VectorTileSource({
wrapX: false,
});
const tile = source.getTile(0, -1, 0, 1, source.getProjection());
expect(tile.getState()).to.be(TileState.EMPTY);
});
it('creates empty tiles when the tileUrlFunction returns undefined', function () {
const source = new VectorTileSource({
tileUrlFunction: function (tileCoord) {
return;
},
});
const tile = source.getTile(1, 1, 1, 1, source.getProjection());
expect(tile.getState()).to.be(TileState.EMPTY);
});
it('creates non-empty tiles outside the world extent when wrapX === true', function () {
const source = new VectorTileSource({
url: '{z}/{x}/{y}.pbf',
});
const tile = source.getTile(0, -1, 0, 1, source.getProjection());
expect(tile.getState()).to.be(TileState.IDLE);
});
it('creates non-empty tiles for overzoomed resolutions', function () {
const source = new VectorTileSource({
url: '{z}/{x}/{y}.pbf',
tileLoadFunction: function (tile) {
tile.setLoader(function () {});
},
maxZoom: 16,
});
const tile = source.getTile(
24,
9119385,
5820434,
1,
source.getProjection()
);
tile.load();
expect(tile.getState()).to.be(TileState.LOADING);
});
it('creates new tile when source key changes', function () {
source.setKey('key1');
const tile1 = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
const tile2 = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
source.setKey('key2');
const tile3 = source.getTile(0, 0, 0, 1, getProjection('EPSG:3857'));
expect(tile1).to.equal(tile2);
expect(tile1.key).to.be('key1');
expect(tile3.key).to.be('key2');
});
});
describe('#getTileGridForProjection', function () {
it("creates a tile grid with the source tile grid's tile size", function () {
const tileGrid = source.getTileGridForProjection(
getProjection('EPSG:3857')
);
expect(tileGrid.getTileSize(0)).to.be(512);
});
});
describe('Tile load events', function () {
it('triggers tileloadstart and tileloadend with ol.VectorTile', function (done) {
const tile = source.getTile(
14,
8938,
5680,
1,
getProjection('EPSG:3857')
);
let started = false;
source.on('tileloadstart', function () {
started = true;
});
source.on('tileloadend', function (e) {
expect(started).to.be(true);
expect(e.tile).to.be.a(VectorTile);
expect(e.tile.getFeatures().length).to.be(1327);
done();
});
tile.load();
});
});
describe('different source and render tile grids', function () {
let source, map, loaded, requested, target;
beforeEach(function () {
loaded = [];
requested = 0;
function tileUrlFunction(tileCoord) {
++requested;
if (tileCoord.toString() == '5,13,-29') {
return tileCoord.join('/');
}
}
function tileLoadFunction(tile, src) {
tile.setLoader(function () {});
loaded.push(src);
}
const extent = [
665584.2026596286,
7033250.839875697,
667162.0221431496,
7035280.378636755,
];
source = new VectorTileSource({
tileGrid: new TileGrid({
origin: [218128, 6126002],
resolutions: [
4096,
2048,
1024,
512,
256,
128,
64,
32,
16,
8,
4,
2,
1,
0.5,
],
}),
tileUrlFunction: tileUrlFunction,
tileLoadFunction: tileLoadFunction,
});
target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
layers: [
new VectorTileLayer({
extent: extent,
source: source,
}),
],
target: target,
view: new View({
zoom: 11,
center: [666373.1624999996, 7034265.3572],
}),
});
});
afterEach(function () {
document.body.removeChild(target);
});
it('loads available tiles', function (done) {
map.renderSync();
setTimeout(function () {
expect(requested).to.be.greaterThan(1);
expect(loaded).to.eql(['5/13/-29']);
done();
}, 0);
});
});
it('does not fill up the tile queue', function (done) {
const target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
const urls = [
'spec/ol/data/14-8938-5680.vector.pbf?num=0&coord={z},{x},{y}',
'spec/ol/data/14-8938-5680.vector.pbf?num=1&coord={z},{x},{y}',
'spec/ol/data/14-8938-5680.vector.pbf?num=2&coord={z},{x},{y}',
'spec/ol/data/14-8938-5680.vector.pbf?num=3&coord={z},{x},{y}',
];
const source = new VectorTileSource({
format: new MVT(),
url: urls[0],
});
const map = new Map({
target: target,
layers: [
new VectorTileLayer({
source: source,
}),
],
view: new View({
center: [0, 0],
zoom: 0,
}),
});
map.renderSync();
const max = urls.length + 3;
let count = 0;
let tile = source.getTile(0, 0, 0, 1, map.getView().getProjection());
tile.addEventListener('change', function onTileChange(e) {
if (e.target.getState() !== TileState.LOADED && !e.target.hifi) {
return;
}
e.target.removeEventListener('change', onTileChange);
map.once('rendercomplete', function () {
expect(map.tileQueue_.getTilesLoading()).to.be(0);
++count;
if (count === max) {
document.body.removeChild(target);
map.dispose();
done();
return;
}
source.setUrl(urls[count % urls.length]);
tile = source.getTile(0, 0, 0, 1, map.getView().getProjection());
tile.addEventListener('change', onTileChange);
});
});
});
describe('interim tile handling', function () {
let map, source, target;
beforeEach(function () {
target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
const extent = [
1824704.739223726,
6141868.096770482,
1827150.7241288517,
6144314.081675608,
];
source = new VectorTileSource({
format: new MVT(),
url: 'spec/ol/data/14-8938-5680.vector.pbf',
minZoom: 14,
maxZoom: 14,
});
map = new Map({
pixelRatio: 1,
target: target,
layers: [
new VectorTileLayer({
extent: extent,
source: source,
}),
],
view: new View({
center: getCenter(extent),
zoom: 15,
}),
});
});
afterEach(function () {
map.setTarget(null);
document.body.removeChild(target);
});
it('re-renders when source changes', function (done) {
map.once('rendercomplete', function () {
const key = map.on('rendercomplete', function () {
const tile = source.getTile(
14,
8938,
5680,
1,
map.getView().getProjection()
);
expect(tile.getKey()).to.be(
'spec/ol/data/14-8938-5680.vector.pbf?new/14,8938,5680'
);
expect(tile.interimTile).to.be.a(VectorRenderTile);
expect(tile.interimTile.getKey()).to.be(
'spec/ol/data/14-8938-5680.vector.pbf/14,8938,5680'
);
const sourceTiles = source.getSourceTiles(
1,
map.getView().getProjection(),
tile
);
if (sourceTiles) {
expect(sourceTiles[0].getKey()).to.be(
'spec/ol/data/14-8938-5680.vector.pbf?new/14,8938,5680'
);
unByKey(key);
done();
}
});
source.setUrl('spec/ol/data/14-8938-5680.vector.pbf?new');
});
});
});
describe('getFeatuersInExtent', function () {
let map, source, target;
beforeEach(function () {
source = new VectorTileSource({
maxZoom: 15,
tileSize: 256,
url: '{z}/{x}/{y}',
tileLoadFunction: function (tile) {
const extent = source
.getTileGrid()
.getTileCoordExtent(tile.tileCoord);
const feature = new Feature(fromExtent(extent));
feature.set('z', tile.tileCoord[0]);
tile.setFeatures([feature]);
},
});
target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
layers: [
new VectorTileLayer({
source: source,
}),
],
view: new View({
center: [0, 0],
zoom: 0,
}),
});
});
afterEach(function () {
map.setTarget(null);
document.body.removeChild(target);
});
it('returns an empty array when no tiles are in the cache', function () {
source.tileCache.clear();
const extent = map.getView().calculateExtent(map.getSize());
expect(source.getFeaturesInExtent(extent).length).to.be(0);
});
it('returns features in extent for the last rendered z', function (done) {
map.getView().setZoom(15);
map.once('rendercomplete', function () {
const extent = map.getView().calculateExtent(map.getSize());
const features = source.getFeaturesInExtent(extent);
expect(features.length).to.be(4);
expect(features[0].get('z')).to.be(15);
map.getView().setZoom(0);
map.once('rendercomplete', function () {
const extent = map.getView().calculateExtent(map.getSize());
const features = source.getFeaturesInExtent(extent);
expect(features.length).to.be(1);
expect(features[0].get('z')).to.be(0);
done();
});
});
});
});
});
+653
View File
@@ -0,0 +1,653 @@
import Projection from '../../../../../src/ol/proj/Projection.js';
import WMTS, {
optionsFromCapabilities,
} from '../../../../../src/ol/source/WMTS.js';
import WMTSCapabilities from '../../../../../src/ol/format/WMTSCapabilities.js';
import WMTSTileGrid from '../../../../../src/ol/tilegrid/WMTS.js';
import {getBottomLeft, getTopRight} from '../../../../../src/ol/extent.js';
import {get as getProjection} from '../../../../../src/ol/proj.js';
describe('ol.source.WMTS', function () {
describe('when creating options from capabilities', function () {
const parser = new WMTSCapabilities();
let capabilities, content;
before(function (done) {
afterLoadText('spec/ol/format/wmts/ogcsample.xml', function (xml) {
try {
content = xml;
capabilities = parser.read(xml);
} catch (e) {
done(e);
}
done();
});
});
it('returns null if the layer was not found in the capabilities', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'invalid',
});
expect(options).to.be(null);
});
it('passes the crossOrigin option', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
matrixSet: 'google3857',
crossOrigin: '',
});
expect(options.crossOrigin).to.be.eql('');
});
it('can create KVP options from spec/ol/format/wmts/ogcsample.xml', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
matrixSet: 'google3857',
});
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?'
);
expect(options.layer).to.be.eql('BlueMarbleNextGeneration');
expect(options.matrixSet).to.be.eql('google3857');
expect(options.format).to.be.eql('image/jpeg');
expect(options.projection).to.be.a(Projection);
expect(options.projection).to.be.eql(getProjection('EPSG:3857'));
expect(options.requestEncoding).to.be.eql('KVP');
expect(options.tileGrid).to.be.a(WMTSTileGrid);
expect(options.style).to.be.eql('DarkBlue');
expect(options.dimensions).to.eql({Time: '20110805'});
expect(options.crossOrigin).to.be(undefined);
});
it('can create REST options from spec/ol/format/wmts/ogcsample.xml', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
matrixSet: 'google3857',
requestEncoding: 'REST',
});
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'http://www.example.com/wmts/coastlines/{TileMatrix}/{TileRow}/{TileCol}.png'
);
expect(options.layer).to.be.eql('BlueMarbleNextGeneration');
expect(options.matrixSet).to.be.eql('google3857');
expect(options.format).to.be.eql('image/png');
expect(options.projection).to.be.a(Projection);
expect(options.projection).to.be.eql(getProjection('EPSG:3857'));
expect(options.requestEncoding).to.be.eql('REST');
expect(options.tileGrid).to.be.a(WMTSTileGrid);
expect(options.style).to.be.eql('DarkBlue');
expect(options.dimensions).to.eql({Time: '20110805'});
});
it('can find a MatrixSet by SRS identifier', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
projection: 'EPSG:3857',
requestEncoding: 'REST',
});
expect(options.matrixSet).to.be.eql('google3857');
expect(options.projection.getCode()).to.be.eql('EPSG:3857');
});
it('can find a MatrixSet by equivalent SRS identifier', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
projection: 'EPSG:900913',
requestEncoding: 'REST',
});
expect(options.matrixSet).to.be.eql('google3857');
expect(options.projection.getCode()).to.be.eql('EPSG:900913');
});
it('can find the default MatrixSet', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
requestEncoding: 'REST',
});
expect(options.matrixSet).to.be.eql('BigWorldPixel');
expect(options.projection.getCode()).to.be.eql(
'urn:ogc:def:crs:OGC:1.3:CRS84'
);
});
it("uses the projection of the default MatrixSet if the config's projection is not supported", function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
projection: new Projection({
code: 'EPSG:2056',
units: 'm',
}),
});
expect(options.matrixSet).to.be.eql('BigWorldPixel');
expect(options.projection.getCode()).to.be.eql(
'urn:ogc:def:crs:OGC:1.3:CRS84'
);
});
it('uses extent of tile matrix instead of projection extent', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'BlueMarbleNextGeneration',
matrixSet: 'google3857subset',
});
// Since google3857subset defines subset of space defined by the google3857 matrix set:
// - top left corner: -10000000, 10000000
// - calculated grid extent: [-10000000, 9999694.25188686, -9999694.25188686, 10000000]
// then the tile grid extent is only a part of the full projection extent.
const gridExtent = options.tileGrid.getExtent();
const gridBottomLeft = getBottomLeft(gridExtent);
const gridTopRight = getTopRight(gridExtent);
expect(Math.round(gridBottomLeft[0])).to.be.eql(-10000000);
expect(Math.round(gridBottomLeft[1])).to.be.eql(9999847);
expect(Math.round(gridTopRight[0])).to.be.eql(-9999847);
expect(Math.round(gridTopRight[1])).to.be.eql(10000000);
const projExtent = options.projection.getExtent();
const projBottomLeft = getBottomLeft(projExtent);
const projTopRight = getTopRight(projExtent);
expect(Math.round(projBottomLeft[0])).to.be.eql(-20037508);
expect(Math.round(projBottomLeft[1])).to.be.eql(-20037508);
expect(Math.round(projTopRight[0])).to.be.eql(20037508);
expect(Math.round(projTopRight[1])).to.be.eql(20037508);
});
it("doesn't fail if the GetCap doesn't contains Constraint tags", function () {
const tmpXml = content.replace(
/<ows:Constraint[\s\S]*?<\/ows:Constraint>/g,
''
);
const tmpCapabilities = parser.read(tmpXml);
expect(
tmpCapabilities['OperationsMetadata']['GetTile']['DCP']['HTTP'][
'Get'
][0]['Constraint']
).to.be(undefined);
const options = optionsFromCapabilities(tmpCapabilities, {
layer: 'BlueMarbleNextGeneration',
matrixSet: 'google3857',
});
expect(options.layer).to.be.eql('BlueMarbleNextGeneration');
expect(options.matrixSet).to.be.eql('google3857');
});
it("set KVP as default request encoding if the GetCap doesn't contains Constraint and ResourceUrl tags", function () {
let tmpXml = content.replace(
/<ows:Constraint[\s\S]*?<\/ows:Constraint>/g,
''
);
tmpXml = tmpXml.replace(/<ResourceURL[\s\S]*?"\/>/g, '');
const tmpCapabilities = parser.read(tmpXml);
expect(
tmpCapabilities['OperationsMetadata']['GetTile']['DCP']['HTTP'][
'Get'
][0]['Constraint']
).to.be(undefined);
expect(tmpCapabilities['Contents']['Layer'][0]['ResourceURL']).to.be(
undefined
);
const options = optionsFromCapabilities(tmpCapabilities, {
layer: 'BlueMarbleNextGeneration',
matrixSet: 'google3857',
});
expect(options.layer).to.be.eql('BlueMarbleNextGeneration');
expect(options.matrixSet).to.be.eql('google3857');
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'http://www.maps.bob/cgi-bin/MiraMon5_0.cgi?'
);
});
});
describe('when creating tileUrlFunction', function () {
const defaultTileGrid = new WMTSTileGrid({
origin: [-20037508.342789244, 20037508.342789244],
resolutions: [
559082264.029 * 0.28e-3,
279541132.015 * 0.28e-3,
139770566.007 * 0.28e-3,
],
matrixIds: [0, 1, 2],
});
it('can replace lowercase REST parameters', function () {
const source = new WMTS({
layer: 'layer',
style: 'default',
urls: [
'http://host/{layer}/{style}/{tilematrixset}/{TileMatrix}/{TileCol}/{TileRow}.jpg',
],
matrixSet: 'EPSG:3857',
requestEncoding: 'REST',
tileGrid: defaultTileGrid,
});
const projection = getProjection('EPSG:3857');
const url = source.tileUrlFunction(
source.getTileCoordForTileUrlFunction([1, 1, 1]),
1,
projection
);
expect(url).to.be.eql('http://host/layer/default/EPSG:3857/1/1/1.jpg');
});
it('can replace camelcase REST parameters', function () {
const source = new WMTS({
layer: 'layer',
style: 'default',
urls: [
'http://host/{Layer}/{Style}/{tilematrixset}/{TileMatrix}/{TileCol}/{TileRow}.jpg',
],
matrixSet: 'EPSG:3857',
requestEncoding: 'REST',
tileGrid: defaultTileGrid,
});
const projection = getProjection('EPSG:3857');
const url = source.tileUrlFunction(
source.getTileCoordForTileUrlFunction([1, 1, 1]),
1,
projection
);
expect(url).to.be.eql('http://host/layer/default/EPSG:3857/1/1/1.jpg');
});
it('can replace dimensions', function () {
const source = new WMTS({
layer: 'layer',
style: 'default',
dimensions: {'Time': 42},
urls: [
'http://host/{Layer}/{Style}/{Time}/{tilematrixset}/{TileMatrix}/{TileCol}/{TileRow}.jpg',
],
matrixSet: 'EPSG:3857',
requestEncoding: 'REST',
tileGrid: defaultTileGrid,
});
const projection = getProjection('EPSG:3857');
const url = source.tileUrlFunction(
source.getTileCoordForTileUrlFunction([1, 1, 1]),
1,
projection
);
expect(url).to.be.eql('http://host/layer/default/42/EPSG:3857/1/1/1.jpg');
});
});
describe('when creating options from Esri capabilities', function () {
const parser = new WMTSCapabilities();
let capabilities;
before(function (done) {
afterLoadText('spec/ol/format/wmts/arcgis.xml', function (xml) {
try {
capabilities = parser.read(xml);
} catch (e) {
done(e);
}
done();
});
});
it('can create KVP options from spec/ol/format/wmts/arcgis.xml', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'Demographics_USA_Population_Density',
requestEncoding: 'KVP',
matrixSet: 'default028mm',
});
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'https://services.arcgisonline.com/arcgis/rest/services/' +
'Demographics/USA_Population_Density/MapServer/WMTS?'
);
});
it('can create REST options from spec/ol/format/wmts/arcgis.xml', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'Demographics_USA_Population_Density',
matrixSet: 'default028mm',
});
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'https://services.arcgisonline.com/arcgis/rest/services/' +
'Demographics/USA_Population_Density/MapServer/WMTS/' +
'tile/1.0.0/Demographics_USA_Population_Density/' +
'{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png'
);
});
});
describe('when creating options from wgs84 capabilities', function () {
const parser = new WMTSCapabilities();
let capabilities;
before(function (done) {
afterLoadText(
'spec/ol/format/wmts/capabilities_wgs84.xml',
function (xml) {
try {
capabilities = parser.read(xml);
} catch (e) {
done(e);
}
done();
}
);
});
it('returns correct bounding box', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'baselayer',
matrixSet: 'inspire_quad',
requestEncoding: 'REST',
});
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'https://example.com/wmts/baselayer/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png'
);
expect(options.layer).to.be.eql('baselayer');
expect(options.matrixSet).to.be.eql('inspire_quad');
expect(options.format).to.be.eql('image/png');
expect(options.projection).to.be.a(Projection);
expect(options.projection).to.be.eql(getProjection('EPSG:4326'));
expect(options.requestEncoding).to.be.eql('REST');
expect(options.tileGrid).to.be.a(WMTSTileGrid);
expect(options.style).to.be.eql('default');
const extent = options.tileGrid.getExtent();
// compare with delta, due to rounding not the exact bounding box is returned...
const expectDelta = (value, expected) =>
expect(Math.abs(value - expected)).to.below(1e-10);
expectDelta(extent[0], -180);
expectDelta(extent[1], -90);
expectDelta(extent[2], 180);
expectDelta(extent[3], 90);
});
});
describe('when creating options from wgs84 capabilities with BoundingBox', function () {
const parser = new WMTSCapabilities();
let capabilities;
before(function (done) {
afterLoadText(
'spec/ol/format/wmts/capabilities_wgs84_with_boundingbox.xml',
function (xml) {
try {
capabilities = parser.read(xml);
} catch (e) {
done(e);
}
done();
}
);
});
it('returns correct bounding box when the layer has BoundingBox', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'bmaphidpi',
matrixSet: 'google3857',
style: 'normal',
});
expect(options.layer).to.be.eql('bmaphidpi');
expect(options.matrixSet).to.be.eql('google3857');
expect(options.format).to.be.eql('image/jpeg');
expect(options.requestEncoding).to.be.eql('REST');
expect(options.tileGrid).to.be.a(WMTSTileGrid);
expect(options.style).to.be.eql('normal');
expect(options.projection).to.be.a(Projection);
expect(options.projection).to.be.eql(getProjection('EPSG:3857'));
const expectedMatrixSetExtend = [977650, 5838030, 1913530, 6281290];
const extent = options.tileGrid.getExtent();
// compare with delta, due to rounding not the exact bounding box is returned...
const expectDelta = (value, expected) =>
expect(Math.abs(value - expected)).to.below(1e-1);
expectDelta(extent[0], expectedMatrixSetExtend[0]);
expectDelta(extent[1], expectedMatrixSetExtend[1]);
expectDelta(extent[2], expectedMatrixSetExtend[2]);
expectDelta(extent[3], expectedMatrixSetExtend[3]);
});
});
describe('when creating options from capabilities with TileMatrixSetLink', function () {
const parser = new WMTSCapabilities();
let capabilities;
before(function (done) {
afterLoadText(
'spec/ol/format/wmts/capabilities_with_tilematrixsetlink.xml',
function (xml) {
try {
capabilities = parser.read(xml);
} catch (e) {
done(e);
}
done();
}
);
});
it('returns correct bounding box for a layer', function () {
const options = optionsFromCapabilities(capabilities, {
layer: 'mean_atlas_land',
matrixSet: 'inspire_quad',
requestEncoding: 'REST',
});
expect(options.urls).to.be.an('array');
expect(options.urls).to.have.length(1);
expect(options.urls[0]).to.be.eql(
'https://example.com/wmts/mean_atlas_land/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png'
);
expect(options.layer).to.be.eql('mean_atlas_land');
expect(options.matrixSet).to.be.eql('inspire_quad');
expect(options.format).to.be.eql('image/png');
expect(options.projection).to.be.a(Projection);
expect(options.projection).to.be.eql(getProjection('EPSG:4326'));
expect(options.requestEncoding).to.be.eql('REST');
expect(options.tileGrid).to.be.a(WMTSTileGrid);
expect(options.style).to.be.eql('default');
const extent = options.tileGrid.getExtent();
// calculate with of one tile, this will be used as tolerance for result extent
const tile_width =
((68247.34668319306 * 0.00028) /
getProjection('EPSG:4326').getMetersPerUnit()) *
256;
// compare with delta, due to rounding not the exact bounding box is returned...
const expectDelta = (value, expected) =>
expect(Math.abs(value - expected)).to.below(tile_width + 1e-10);
expectDelta(extent[0], -36);
expectDelta(extent[1], 15);
expectDelta(extent[2], 43);
expectDelta(extent[3], 90);
});
});
describe('#setUrls()', function () {
it('sets the URL for the source', function () {
const source = new WMTS({});
const urls = [
'https://a.example.com/',
'https://b.example.com/',
'https://c.example.com/',
];
source.setUrls(urls);
expect(source.getUrls()).to.eql(urls);
});
it('updates the key for the source', function () {
const source = new WMTS({});
const urls = [
'https://a.example.com/',
'https://b.example.com/',
'https://c.example.com/',
];
source.setUrls(urls);
expect(source.getKey()).to.eql(urls.join('\n'));
});
it('generates the correct tileUrlFunction during application of setUrl()', function () {
const projection = getProjection('EPSG:3857');
const source = new WMTS({
projection: projection,
requestEncoding: 'REST',
urls: [
'http://1.example.com/{TileMatrix}/{TileRow}/{TileCol}.jpeg',
'http://2.example.com/{TileMatrix}/{TileRow}/{TileCol}.jpeg',
],
tileGrid: new WMTSTileGrid({
matrixIds: [0, 1, 2, 3, 4, 5, 6, 7],
origin: [2690000, 1285000],
resolutions: [4000, 3750, 3500, 3250, 3000, 2750, 2500, 2250],
}),
});
const urls = [
'https://a.example.com/{TileMatrix}/{TileRow}/{TileCol}.jpg',
'https://b.example.com/{TileMatrix}/{TileRow}/{TileCol}.jpg',
];
source.setUrls(urls);
const tileUrl1 = source.tileUrlFunction([2, 9, -5], 1, projection);
expect(tileUrl1).to.match(/https\:\/\/[ab]\.example\.com\/2\/-5\/9\.jpg/);
});
});
describe('url option', function () {
it('expands url template', function () {
const tileSource = new WMTS({
url: '{1-3}',
});
const urls = tileSource.getUrls();
expect(urls).to.eql(['1', '2', '3']);
});
});
describe('#getUrls', function () {
let sourceOptions;
let source;
beforeEach(function () {
sourceOptions = {
layer: 'layer',
style: 'default',
matrixSet: 'foo',
requestEncoding: 'REST',
tileGrid: new WMTSTileGrid({
origin: [0, 0],
resolutions: [],
matrixIds: [],
}),
};
});
describe('using a "url" option', function () {
beforeEach(function () {
sourceOptions.url = 'some_wmts_url';
source = new WMTS(sourceOptions);
});
it('returns the WMTS URLs', function () {
const urls = source.getUrls();
expect(urls).to.be.eql(['some_wmts_url']);
});
});
describe('using a "urls" option', function () {
beforeEach(function () {
sourceOptions.urls = ['some_wmts_url1', 'some_wmts_url2'];
source = new WMTS(sourceOptions);
});
it('returns the WMTS URLs', function () {
const urls = source.getUrls();
expect(urls).to.be.eql(['some_wmts_url1', 'some_wmts_url2']);
});
});
});
describe('#getRequestEncoding', function () {
let source;
beforeEach(function () {
source = new WMTS({
layer: 'layer',
style: 'default',
matrixSet: 'foo',
requestEncoding: 'REST',
tileGrid: new WMTSTileGrid({
origin: [0, 0],
resolutions: [],
matrixIds: [],
}),
});
});
it('returns the request encoding', function () {
const requestEncoding = source.getRequestEncoding();
expect(requestEncoding).to.be.eql('REST');
});
});
});
+248
View File
@@ -0,0 +1,248 @@
import Map from '../../../../../src/ol/Map.js';
import TileImage from '../../../../../src/ol/source/TileImage.js';
import TileLayer from '../../../../../src/ol/layer/Tile.js';
import TileSource from '../../../../../src/ol/source/Tile.js';
import UrlTile from '../../../../../src/ol/source/UrlTile.js';
import View from '../../../../../src/ol/View.js';
import XYZ from '../../../../../src/ol/source/XYZ.js';
import {createXYZ} from '../../../../../src/ol/tilegrid.js';
describe('ol.source.XYZ', function () {
describe('constructor', function () {
it('can be constructed without options', function () {
const source = new XYZ();
expect(source).to.be.an(XYZ);
expect(source).to.be.an(TileImage);
expect(source).to.be.an(UrlTile);
expect(source).to.be.an(TileSource);
});
it('can be constructed with a custom zDirection', function () {
const source = new XYZ({
zDirection: -1,
});
expect(source.zDirection).to.be(-1);
});
it('can be constructed with a custom tile grid', function () {
const tileGrid = createXYZ();
const tileSource = new XYZ({
tileGrid: tileGrid,
});
expect(tileSource.getTileGrid()).to.be(tileGrid);
});
it('can be constructed with a custom tile size', function () {
const tileSource = new XYZ({
tileSize: 512,
});
expect(tileSource.getTileGrid().getTileSize(0)).to.be(512);
});
it('can be constructed with a custom min zoom', function () {
const tileSource = new XYZ({
minZoom: 2,
});
expect(tileSource.getTileGrid().getMinZoom()).to.be(2);
});
});
describe('tileUrlFunction', function () {
let xyzTileSource, tileGrid;
beforeEach(function () {
xyzTileSource = new XYZ({
maxZoom: 6,
url: '{z}/{x}/{y}',
});
tileGrid = xyzTileSource.getTileGrid();
});
it('returns the expected URL', function () {
const coordinate = [829330.2064098881, 5933916.615134273];
let tileUrl;
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 0)
);
expect(tileUrl).to.eql('0/0/0');
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 1)
);
expect(tileUrl).to.eql('1/1/0');
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 2)
);
expect(tileUrl).to.eql('2/2/1');
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 3)
);
expect(tileUrl).to.eql('3/4/2');
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 4)
);
expect(tileUrl).to.eql('4/8/5');
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 5)
);
expect(tileUrl).to.eql('5/16/11');
tileUrl = xyzTileSource.tileUrlFunction(
tileGrid.getTileCoordForCoordAndZ(coordinate, 6)
);
expect(tileUrl).to.eql('6/33/22');
});
describe('wrap x', function () {
it('returns the expected URL', function () {
const projection = xyzTileSource.getProjection();
let tileUrl = xyzTileSource.tileUrlFunction(
xyzTileSource.getTileCoordForTileUrlFunction([6, -31, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
tileUrl = xyzTileSource.tileUrlFunction(
xyzTileSource.getTileCoordForTileUrlFunction([6, 33, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
tileUrl = xyzTileSource.tileUrlFunction(
xyzTileSource.getTileCoordForTileUrlFunction([6, 97, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
});
});
describe('crop y', function () {
it('returns the expected URL', function () {
const projection = xyzTileSource.getProjection();
let tileUrl = xyzTileSource.tileUrlFunction(
xyzTileSource.getTileCoordForTileUrlFunction([6, 33, -1], projection)
);
expect(tileUrl).to.be(undefined);
tileUrl = xyzTileSource.tileUrlFunction(
xyzTileSource.getTileCoordForTileUrlFunction([6, 33, 22], projection)
);
expect(tileUrl).to.eql('6/33/22');
tileUrl = xyzTileSource.tileUrlFunction(
xyzTileSource.getTileCoordForTileUrlFunction([6, 33, 64], projection)
);
expect(tileUrl).to.be(undefined);
});
});
});
describe('#getUrls', function () {
let sourceOptions;
let source;
const url = 'http://geo.nls.uk/maps/towns/glasgow1857/{z}/{x}/{-y}.png';
beforeEach(function () {
sourceOptions = {
projection: 'EPSG:4326',
};
});
describe('using a "url" option', function () {
beforeEach(function () {
sourceOptions.url = url;
source = new XYZ(sourceOptions);
});
it('returns the XYZ URL', function () {
const urls = source.getUrls();
expect(urls).to.be.eql([url]);
});
});
describe('using a "urls" option', function () {
beforeEach(function () {
sourceOptions.urls = ['some_xyz_url1', 'some_xyz_url2'];
source = new XYZ(sourceOptions);
});
it('returns the XYZ URLs', function () {
const urls = source.getUrls();
expect(urls).to.be.eql(['some_xyz_url1', 'some_xyz_url2']);
});
});
describe('using a "tileUrlFunction"', function () {
beforeEach(function () {
sourceOptions.tileUrlFunction = function () {
return 'some_xyz_url';
};
source = new XYZ(sourceOptions);
});
it('returns null', function () {
const urls = source.getUrls();
expect(urls).to.be(null);
});
});
});
describe('clear and refresh', function () {
let map, source;
let callCount = 0;
beforeEach(function (done) {
source = new XYZ({
url: 'spec/ol/data/osm-{z}-{x}-{y}.png',
tileLoadFunction: function (image, src) {
++callCount;
image.getImage().src = src;
},
});
const target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
layers: [
new TileLayer({
source: source,
}),
],
view: new View({
center: [0, 0],
zoom: 0,
}),
});
map.once('rendercomplete', function () {
callCount = 0;
done();
});
});
afterEach(function () {
document.body.removeChild(map.getTargetElement());
map.setTarget(null);
});
it('#refresh() reloads from server', function (done) {
map.once('rendercomplete', function () {
expect(callCount).to.be(1);
done();
});
source.refresh();
});
it('#clear() clears the tile cache', function (done) {
map.once('rendercomplete', function () {
done(new Error('should not re-render'));
});
source.clear();
setTimeout(function () {
done();
}, 1000);
});
});
});
+359
View File
@@ -0,0 +1,359 @@
import Projection from '../../../../../src/ol/proj/Projection.js';
import TileGrid from '../../../../../src/ol/tilegrid/TileGrid.js';
import Zoomify, {CustomTile} from '../../../../../src/ol/source/Zoomify.js';
import {DEFAULT_TILE_SIZE} from '../../../../../src/ol/tilegrid/common.js';
import {listen} from '../../../../../src/ol/events.js';
describe('ol.source.Zoomify', function () {
const w = 1024;
const h = 512;
const size = [w, h];
const zoomifyUrl =
'spec/ol/source/images/zoomify/{TileGroup}/{z}-{x}-{y}.jpg';
const iipUrl = 'spec/ol/source/images/zoomify?JTL={z},{tileIndex}';
const proj = new Projection({
code: 'ZOOMIFY',
units: 'pixels',
extent: [0, 0, w, h],
});
function getZoomifySource() {
return new Zoomify({
url: zoomifyUrl,
size: size,
});
}
function getZoomifySourceWithExtentInFirstQuadrant() {
return new Zoomify({
url: zoomifyUrl,
size: size,
extent: [0, 0, size[0], size[1]],
});
}
function getIIPSource() {
return new Zoomify({
url: iipUrl,
size: size,
});
}
function getZoomifySourceWith1024pxTiles() {
return new Zoomify({
url: zoomifyUrl,
size: size,
tileSize: 1024,
});
}
describe('constructor', function () {
it('requires config "size" and "url"', function () {
let source;
// undefined config object
expect(function () {
source = new Zoomify();
}).to.throwException();
// empty object as config object
expect(function () {
source = new Zoomify({});
}).to.throwException();
// passing "url" in config object
expect(function () {
source = new Zoomify({
url: 'some-url',
});
}).to.throwException();
// passing "size" in config object
expect(function () {
source = new Zoomify({
size: [47, 11],
});
}).to.throwException();
// passing "size" and "url" in config object
expect(function () {
source = new Zoomify({
url: '',
size: [47, 11],
});
}).to.not.throwException();
// we got a source
expect(source).to.be.a(Zoomify);
// also test our helper methods from above
expect(function () {
source = getZoomifySource();
}).to.not.throwException();
expect(function () {
source = getIIPSource();
}).to.not.throwException();
// we got a source
expect(source).to.be.a(Zoomify);
});
it('does not need "tierSizeCalculation" option', function () {
expect(function () {
new Zoomify({
url: '',
size: [47, 11],
});
}).to.not.throwException();
});
it('accepts "tierSizeCalculation" option "default"', function () {
expect(function () {
new Zoomify({
url: '',
size: [47, 11],
tierSizeCalculation: 'default',
});
}).to.not.throwException();
});
it('accepts "tierSizeCalculation" option "truncated"', function () {
expect(function () {
new Zoomify({
url: '',
size: [47, 11],
tierSizeCalculation: 'truncated',
});
}).to.not.throwException();
});
it('throws on unexpected "tierSizeCalculation" ', function () {
// passing unknown string will throw
expect(function () {
new Zoomify({
url: '',
size: [47, 11],
tierSizeCalculation: 'ace-of-spades',
});
}).to.throwException();
});
it('creates a tileGrid for both protocols', function () {
const sources = [getZoomifySource(), getIIPSource()];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
expect(tileGrid).to.be.a(TileGrid);
}
});
});
describe('generated tileGrid', function () {
it('has expected extent', function () {
const sources = [getZoomifySource(), getIIPSource()];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
const expectedExtent = [0, -h, w, 0];
expect(tileGrid.getExtent()).to.eql(expectedExtent);
}
});
it('has expected origin', function () {
const sources = [getZoomifySource(), getIIPSource()];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
const expectedOrigin = [0, 0];
expect(tileGrid.getOrigin()).to.eql(expectedOrigin);
}
});
it('has expected resolutions', function () {
const sources = [getZoomifySource(), getIIPSource()];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
const expectedResolutions = [4, 2, 1];
expect(tileGrid.getResolutions()).to.eql(expectedResolutions);
}
});
it('has expected tileSize', function () {
const sources = [getZoomifySource(), getZoomifySourceWith1024pxTiles()];
const expectedTileSizes = [DEFAULT_TILE_SIZE, 1024];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
expect(tileGrid.getTileSize()).to.eql(expectedTileSizes[i]);
}
});
it('has expected extent', function () {
const sources = [
getZoomifySource(),
getZoomifySourceWithExtentInFirstQuadrant(),
];
const expectedExtents = [
[0, -size[1], size[0], 0],
[0, 0, size[0], size[1]],
];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
expect(tileGrid.getExtent()).to.eql(expectedExtents[i]);
}
});
it('has expected origin', function () {
const sources = [
getZoomifySource(),
getZoomifySourceWithExtentInFirstQuadrant(),
];
const expectedOrigins = [
[0, 0],
[0, size[1]],
];
for (let i = 0; i < sources.length; i++) {
const tileGrid = sources[i].getTileGrid();
expect(tileGrid.getOrigin()).to.eql(expectedOrigins[i]);
}
});
});
describe('tierSizeCalculation configuration', function () {
it('influences resolutions', function () {
// not configured at all
const source = new Zoomify({
url: zoomifyUrl,
size: [513, 256],
});
const tileGrid = source.getTileGrid();
// explicitly set as 'default'
const sourceDefault = new Zoomify({
url: zoomifyUrl,
size: [513, 256],
tierSizeCalculation: 'default',
});
const tileGridDefault = sourceDefault.getTileGrid();
// explicitly set to 'truncated'
const sourceTruncated = new Zoomify({
url: zoomifyUrl,
size: [513, 256],
tierSizeCalculation: 'truncated',
});
const tileGridTruncated = sourceTruncated.getTileGrid();
expect(tileGrid.getResolutions()).to.eql([4, 2, 1]);
expect(tileGridDefault.getResolutions()).to.eql([4, 2, 1]);
expect(tileGridTruncated.getResolutions()).to.eql([2, 1]);
});
});
describe('generated tileUrlFunction for zoomify protocol', function () {
it('creates an expected tileUrlFunction with zoomify template', function () {
const source = getZoomifySource();
const tileUrlFunction = source.getTileUrlFunction();
// zoomlevel 0
expect(tileUrlFunction([0, 0, 0])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/0-0-0.jpg'
);
// zoomlevel 1
expect(tileUrlFunction([1, 0, 0])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-0-0.jpg'
);
expect(tileUrlFunction([1, 1, 0])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-1-0.jpg'
);
expect(tileUrlFunction([1, 0, 1])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-0-1.jpg'
);
expect(tileUrlFunction([1, 1, 1])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-1-1.jpg'
);
});
it('creates an expected tileUrlFunction with IIP template', function () {
const source = getIIPSource();
const tileUrlFunction = source.getTileUrlFunction();
// zoomlevel 0
expect(tileUrlFunction([0, 0, 0])).to.eql(
'spec/ol/source/images/zoomify?JTL=0,0'
);
// zoomlevel 1
expect(tileUrlFunction([1, 0, 0])).to.eql(
'spec/ol/source/images/zoomify?JTL=1,0'
);
expect(tileUrlFunction([1, 1, 0])).to.eql(
'spec/ol/source/images/zoomify?JTL=1,1'
);
expect(tileUrlFunction([1, 0, 1])).to.eql(
'spec/ol/source/images/zoomify?JTL=1,2'
);
expect(tileUrlFunction([1, 1, 1])).to.eql(
'spec/ol/source/images/zoomify?JTL=1,3'
);
});
it('creates an expected tileUrlFunction without template', function () {
const source = new Zoomify({
url: 'spec/ol/source/images/zoomify/',
size: size,
});
const tileUrlFunction = source.getTileUrlFunction();
// zoomlevel 0
expect(tileUrlFunction([0, 0, 0])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/0-0-0.jpg'
);
// zoomlevel 1
expect(tileUrlFunction([1, 0, 0])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-0-0.jpg'
);
expect(tileUrlFunction([1, 1, 0])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-1-0.jpg'
);
expect(tileUrlFunction([1, 0, 1])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-0-1.jpg'
);
expect(tileUrlFunction([1, 1, 1])).to.eql(
'spec/ol/source/images/zoomify/TileGroup0/1-1-1.jpg'
);
});
it('returns undefined if no tileCoord passed', function () {
const source = getZoomifySource();
const tileUrlFunction = source.getTileUrlFunction();
expect(tileUrlFunction()).to.be(undefined);
});
});
describe('uses a custom tileClass', function () {
it('returns expected tileClass instances via "getTile"', function () {
const source = getZoomifySource();
const tile = source.getTile(0, 0, 0, 1, proj);
expect(tile).to.be.a(CustomTile);
});
it('"tile.getImage" returns and caches an unloaded image', function () {
const source = getZoomifySource();
const tile = source.getTile(0, 0, 0, 1, proj);
const img = tile.getImage();
const tile2 = source.getTile(0, 0, 0, 1, proj);
const img2 = tile2.getImage();
expect(img).to.be.a(HTMLImageElement);
expect(img).to.be(img2);
});
it('"tile.getImage" returns and caches a loaded canvas', function (done) {
const source = getZoomifySource();
const tile = source.getTile(0, 0, 0, 1, proj);
listen(tile, 'change', function () {
if (tile.getState() == 2) {
// LOADED
const img = tile.getImage();
expect(img).to.be.a(HTMLCanvasElement);
const tile2 = source.getTile(0, 0, 0, 1, proj);
expect(tile2.getState()).to.be(2); // LOADED
const img2 = tile2.getImage();
expect(img).to.be(img2);
done();
}
});
tile.load();
});
});
});