Organize tests
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user