import { Clock } from "../../Source/Cesium.js"; import { ClockStep } from "../../Source/Cesium.js"; import { Credit } from "../../Source/Cesium.js"; import { GeographicTilingScheme } from "../../Source/Cesium.js"; import { JulianDate } from "../../Source/Cesium.js"; import { objectToQuery } from "../../Source/Cesium.js"; import { queryToObject } from "../../Source/Cesium.js"; import { Request } from "../../Source/Cesium.js"; import { RequestScheduler } from "../../Source/Cesium.js"; import { RequestState } from "../../Source/Cesium.js"; import { Resource } from "../../Source/Cesium.js"; import { TimeIntervalCollection } from "../../Source/Cesium.js"; import { WebMercatorTilingScheme } from "../../Source/Cesium.js"; import { Imagery } from "../../Source/Cesium.js"; import { ImageryLayer } from "../../Source/Cesium.js"; import { ImageryProvider } from "../../Source/Cesium.js"; import { ImageryState } from "../../Source/Cesium.js"; import { WebMapTileServiceImageryProvider } from "../../Source/Cesium.js"; import pollToPromise from "../pollToPromise.js"; import { Uri } from "../../Source/Cesium.js"; describe("Scene/WebMapTileServiceImageryProvider", function () { beforeEach(function () { RequestScheduler.clearForSpecs(); }); afterEach(function () { Resource._Implementations.createImage = Resource._DefaultImplementations.createImage; }); it("conforms to ImageryProvider interface", function () { expect(WebMapTileServiceImageryProvider).toConformToInterface( ImageryProvider ); }); it("generates expected tile urls", function () { var options = { url: "http://wmts.invalid", format: "image/png", layer: "someLayer", style: "someStyle", tileMatrixSetID: "someTMS", tileMatrixLabels: ["first", "second", "third"], }; var provider = new WebMapTileServiceImageryProvider(options); spyOn(ImageryProvider, "loadImage"); var tilecol = 12; var tilerow = 5; var level = 1; provider.requestImage(tilecol, tilerow, level); var uri = new Uri(ImageryProvider.loadImage.calls.mostRecent().args[1].url); var queryObject = queryToObject(uri.query()); expect(queryObject.request).toEqual("GetTile"); expect(queryObject.service).toEqual("WMTS"); expect(queryObject.version).toEqual("1.0.0"); expect(queryObject.format).toEqual(options.format); expect(queryObject.layer).toEqual(options.layer); expect(queryObject.style).toEqual(options.style); expect(parseInt(queryObject.tilecol, 10)).toEqual(tilecol); expect(queryObject.tilematrixset).toEqual(options.tileMatrixSetID); expect(queryObject.tilematrix).toEqual(options.tileMatrixLabels[level]); expect(parseInt(queryObject.tilerow, 10)).toEqual(tilerow); tilecol = 1; tilerow = 3; level = 2; provider.requestImage(tilecol, tilerow, level); uri = new Uri(ImageryProvider.loadImage.calls.mostRecent().args[1].url); queryObject = queryToObject(uri.query()); expect(queryObject.request).toEqual("GetTile"); expect(queryObject.service).toEqual("WMTS"); expect(queryObject.version).toEqual("1.0.0"); expect(queryObject.format).toEqual(options.format); expect(queryObject.layer).toEqual(options.layer); expect(queryObject.style).toEqual(options.style); expect(parseInt(queryObject.tilecol, 10)).toEqual(tilecol); expect(queryObject.tilematrixset).toEqual(options.tileMatrixSetID); expect(queryObject.tilematrix).toEqual(options.tileMatrixLabels[level]); expect(parseInt(queryObject.tilerow, 10)).toEqual(tilerow); }); it("generates expected tile urls for subdomains", function () { var options = { url: "http://wmts{s}.invalid", format: "image/png", layer: "someLayer", style: "someStyle", tileMatrixSetID: "someTMS", tileMatrixLabels: ["first", "second", "third"], }; var provider = new WebMapTileServiceImageryProvider(options); spyOn(ImageryProvider, "loadImage"); var tilecol = 12; var tilerow = 5; var level = 1; provider.requestImage(tilecol, tilerow, level); var uri = new Uri(ImageryProvider.loadImage.calls.mostRecent().args[1].url); var queryObject = queryToObject(uri.query()); expect(queryObject.request).toEqual("GetTile"); expect(queryObject.service).toEqual("WMTS"); expect(queryObject.version).toEqual("1.0.0"); expect(queryObject.format).toEqual(options.format); expect(queryObject.layer).toEqual(options.layer); expect(queryObject.style).toEqual(options.style); expect(parseInt(queryObject.tilecol, 10)).toEqual(tilecol); expect(queryObject.tilematrixset).toEqual(options.tileMatrixSetID); expect(queryObject.tilematrix).toEqual(options.tileMatrixLabels[level]); expect(parseInt(queryObject.tilerow, 10)).toEqual(tilerow); expect(uri.authority()).toEqual("wmtsa.invalid"); tilecol = 2; tilerow = 3; level = 2; provider.requestImage(tilecol, tilerow, level); uri = new Uri(ImageryProvider.loadImage.calls.mostRecent().args[1].url); queryObject = queryToObject(uri.query()); expect(queryObject.request).toEqual("GetTile"); expect(queryObject.service).toEqual("WMTS"); expect(queryObject.version).toEqual("1.0.0"); expect(queryObject.format).toEqual(options.format); expect(queryObject.layer).toEqual(options.layer); expect(queryObject.style).toEqual(options.style); expect(parseInt(queryObject.tilecol, 10)).toEqual(tilecol); expect(queryObject.tilematrixset).toEqual(options.tileMatrixSetID); expect(queryObject.tilematrix).toEqual(options.tileMatrixLabels[level]); expect(parseInt(queryObject.tilerow, 10)).toEqual(tilerow); expect(uri.authority()).toEqual("wmtsb.invalid"); }); it("supports subdomains string urls", function () { var options = { url: "{s}", layer: "", style: "", subdomains: "123", tileMatrixSetID: "", }; var provider = new WebMapTileServiceImageryProvider(options); spyOn(ImageryProvider, "loadImage"); var tilecol = 1; var tilerow = 1; var level = 1; provider.requestImage(tilecol, tilerow, level); var url = ImageryProvider.loadImage.calls .mostRecent() .args[1].getUrlComponent(); expect("123".indexOf(url)).toBeGreaterThanOrEqualTo(0); }); it("supports subdomains array urls", function () { var options = { url: "{s}", layer: "", style: "", subdomains: ["foo", "bar"], tileMatrixSetID: "", }; var provider = new WebMapTileServiceImageryProvider(options); spyOn(ImageryProvider, "loadImage"); var tilecol = 1; var tilerow = 1; var level = 1; provider.requestImage(tilecol, tilerow, level); var url = ImageryProvider.loadImage.calls .mostRecent() .args[1].getUrlComponent(); expect(["foo", "bar"].indexOf(url)).toBeGreaterThanOrEqualTo(0); }); it("generates expected tile urls from template", function () { var options = { url: "http://wmts.invalid/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png", format: "image/png", layer: "someLayer", style: "someStyle", tileMatrixSetID: "someTMS", tileMatrixLabels: ["first", "second", "third"], }; var provider = new WebMapTileServiceImageryProvider(options); spyOn(ImageryProvider, "loadImage"); var tilecol = 12; var tilerow = 5; var level = 1; provider.requestImage(tilecol, tilerow, level); var uri = new Uri( ImageryProvider.loadImage.calls.mostRecent().args[1].getUrlComponent() ); expect(uri.toString()).toEqual( "http://wmts.invalid/someStyle/someTMS/second/5/12.png" ); }); it("requires the url to be specified", function () { function createWithoutUrl() { return new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", tileMatrixSetID: "someTMS", }); } expect(createWithoutUrl).toThrowDeveloperError(); }); it("requires the layer to be specified", function () { function createWithoutLayer() { return new WebMapTileServiceImageryProvider({ url: "http://wmts.invalid", style: "someStyle", tileMatrixSetID: "someTMS", }); } expect(createWithoutLayer).toThrowDeveloperError(); }); it("requires the style to be specified", function () { function createWithoutStyle() { return new WebMapTileServiceImageryProvider({ layer: "someLayer", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", }); } expect(createWithoutStyle).toThrowDeveloperError(); }); it("requires the tileMatrixSetID to be specified", function () { function createWithoutTMS() { return new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", }); } expect(createWithoutTMS).toThrowDeveloperError(); }); it("requires clock if times is specified", function () { function createWithoutClock() { return new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", times: new TimeIntervalCollection(), }); } expect(createWithoutClock).toThrowDeveloperError(); }); it("resolves readyPromise", function () { var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", }); return provider.readyPromise.then(function (result) { expect(result).toBe(true); expect(provider.ready).toBe(true); }); }); it("resolves readyPromise with Resource", function () { var resource = new Resource({ url: "http://wmts.invalid", }); var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: resource, tileMatrixSetID: "someTMS", }); return provider.readyPromise.then(function (result) { expect(result).toBe(true); expect(provider.ready).toBe(true); }); }); // default parameters values it("uses default values for undefined parameters", function () { var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", }); expect(provider.format).toEqual("image/jpeg"); expect(provider.tileWidth).toEqual(256); expect(provider.tileHeight).toEqual(256); expect(provider.minimumLevel).toEqual(0); expect(provider.maximumLevel).toBeUndefined(); expect(provider.tilingScheme).toBeInstanceOf(WebMercatorTilingScheme); expect(provider.rectangle).toEqual(provider.tilingScheme.rectangle); expect(provider.credit).toBeUndefined(); expect(provider.proxy).toBeUndefined(); }); // non default parameters values it("uses parameters passed to constructor", function () { var tilingScheme = new GeographicTilingScheme(); var rectangle = new WebMercatorTilingScheme().rectangle; var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", format: "someFormat", tileWidth: 512, tileHeight: 512, tilingScheme: tilingScheme, minimumLevel: 0, maximumLevel: 12, rectangle: rectangle, credit: "Thanks for using our WMTS server.", }); expect(provider.format).toEqual("someFormat"); expect(provider.tileWidth).toEqual(512); expect(provider.tileHeight).toEqual(512); expect(provider.minimumLevel).toEqual(0); expect(provider.maximumLevel).toEqual(12); expect(provider.tilingScheme).toEqual(tilingScheme); expect(provider.credit).toBeDefined(); expect(provider.credit).toBeInstanceOf(Credit); expect(provider.rectangle).toEqual(rectangle); }); it("doesn't care about trailing question mark at the end of URL", function () { var provider1 = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", }); var provider2 = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid?", tileMatrixSetID: "someTMS", }); return pollToPromise(function () { return provider1.ready && provider2.ready; }).then(function () { spyOn(Resource._Implementations, "createImage").and.callFake(function ( request, crossOrigin, deferred ) { // Just return any old image. Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }); return provider1.requestImage(0, 0, 0).then(function (image) { return provider2.requestImage(0, 0, 0).then(function (image) { expect(Resource._Implementations.createImage.calls.count()).toEqual( 2 ); //expect the two image URLs to be the same between the two providers var allCalls = Resource._Implementations.createImage.calls.all(); expect(allCalls[1].args[0].url).toEqual(allCalls[0].args[0].url); }); }); }); }); it("requestImage returns a promise for an image and loads it for cross-origin use", function () { var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", }); return pollToPromise(function () { return provider.ready; }).then(function () { spyOn(Resource._Implementations, "createImage").and.callFake(function ( request, crossOrigin, deferred ) { // Just return any old image. Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }); return provider.requestImage(0, 0, 0).then(function (image) { expect(Resource._Implementations.createImage).toHaveBeenCalled(); expect(image).toBeImageOrImageBitmap(); }); }); }); it("raises error event when image cannot be loaded", function () { var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid", tileMatrixSetID: "someTMS", }); var layer = new ImageryLayer(provider); var tries = 0; provider.errorEvent.addEventListener(function (error) { expect(error.timesRetried).toEqual(tries); ++tries; if (tries < 3) { error.retry = true; } setTimeout(function () { RequestScheduler.update(); }, 1); }); Resource._Implementations.createImage = function ( request, crossOrigin, deferred ) { if (tries === 2) { // Succeed after 2 tries Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); } else { // fail setTimeout(function () { deferred.reject(); }, 1); } }; return pollToPromise(function () { return provider.ready; }).then(function () { var imagery = new Imagery(layer, 0, 0, 0); imagery.addReference(); layer._requestImagery(imagery); RequestScheduler.update(); return pollToPromise(function () { return imagery.state === ImageryState.RECEIVED; }).then(function () { expect(imagery.image).toBeImageOrImageBitmap(); expect(tries).toEqual(2); imagery.releaseReference(); }); }); }); it("tiles preload on requestImage as we approach the next time interval", function () { var times = TimeIntervalCollection.fromIso8601({ iso8601: "2017-04-26/2017-04-30/P1D", dataCallback: function (interval, index) { return { Time: JulianDate.toIso8601(interval.start), }; }, }); var clock = new Clock({ currentTime: JulianDate.fromIso8601("2017-04-26"), shouldAnimate: true, }); var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid/{Time}", tileMatrixSetID: "someTMS", clock: clock, times: times, }); Resource._Implementations.createImage = function ( request, crossOrigin, deferred ) { Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }; var entry; return pollToPromise(function () { return provider.ready; }) .then(function () { clock.currentTime = JulianDate.fromIso8601("2017-04-26T23:59:56Z"); return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { RequestScheduler.update(); // Test tile 0,0,0 was prefetched var cache = provider._timeDynamicImagery._tileCache; expect(cache["1"]).toBeDefined(); entry = cache["1"]["0-0-0"]; expect(entry).toBeDefined(); expect(entry.promise).toBeDefined(); return entry.promise; }) .then(function () { expect(entry.request).toBeDefined(); expect(entry.request.state).toEqual(RequestState.RECEIVED); }); }); it("tiles preload onTick event as we approach the next time interval", function () { var times = TimeIntervalCollection.fromIso8601({ iso8601: "2017-04-26/2017-04-30/P1D", dataCallback: function (interval, index) { return { Time: JulianDate.toIso8601(interval.start), }; }, }); var clock = new Clock({ currentTime: JulianDate.fromIso8601("2017-04-26"), shouldAnimate: true, }); var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid/{Time}", tileMatrixSetID: "someTMS", clock: clock, times: times, }); Resource._Implementations.createImage = function ( request, crossOrigin, deferred ) { Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }; var entry; return pollToPromise(function () { return provider.ready; }) .then(function () { return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { // Test tile 0,0,0 wasn't prefetched var cache = provider._timeDynamicImagery._tileCache; expect(cache["1"]).toBeUndefined(); // Update the clock and process any requests clock.currentTime = JulianDate.fromIso8601("2017-04-26T23:59:55Z"); clock.tick(); RequestScheduler.update(); // Test tile 0,0,0 was prefetched expect(cache["1"]).toBeDefined(); entry = cache["1"]["0-0-0"]; expect(entry).toBeDefined(); expect(entry.promise).toBeDefined(); return entry.promise; }) .then(function () { expect(entry.request).toBeDefined(); expect(entry.request.state).toEqual(RequestState.RECEIVED); }); }); it("reload is called once we cross into next interval", function () { var times = TimeIntervalCollection.fromIso8601({ iso8601: "2017-04-26/2017-04-30/P1D", dataCallback: function (interval, index) { return { Time: JulianDate.toIso8601(interval.start), }; }, }); var clock = new Clock({ currentTime: JulianDate.fromIso8601("2017-04-26"), clockStep: ClockStep.TICK_DEPENDENT, shouldAnimate: true, }); Resource._Implementations.createImage = function ( request, crossOrigin, deferred ) { Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }; var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid/{Time}", tileMatrixSetID: "someTMS", clock: clock, times: times, }); provider._reload = jasmine.createSpy(); spyOn(provider._timeDynamicImagery, "getFromCache").and.callThrough(); return pollToPromise(function () { return provider.ready; }) .then(function () { clock.currentTime = JulianDate.fromIso8601("2017-04-26T23:59:59Z"); return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { RequestScheduler.update(); clock.tick(); return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { expect(provider._reload.calls.count()).toEqual(1); var calls = provider._timeDynamicImagery.getFromCache.calls.all(); expect(calls.length).toBe(2); expect(calls[0].returnValue).toBeUndefined(); expect(calls[1].returnValue).toBeDefined(); }); }); it("dimensions work with RESTful requests", function () { var lastUrl; Resource._Implementations.createImage = function ( request, crossOrigin, deferred ) { lastUrl = request.url; Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }; var provider = new WebMapTileServiceImageryProvider({ layer: "someLayer", style: "someStyle", url: "http://wmts.invalid/{FOO}", tileMatrixSetID: "someTMS", dimensions: { FOO: "BAR", }, }); provider._reload = jasmine.createSpy(); return pollToPromise(function () { return provider.ready; }) .then(function () { return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { expect(lastUrl).toStartWith("http://wmts.invalid/BAR"); expect(provider._reload.calls.count()).toEqual(0); provider.dimensions = { FOO: "BAZ", }; expect(provider._reload.calls.count()).toEqual(1); return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { expect(lastUrl).toStartWith("http://wmts.invalid/BAZ"); }); }); it("dimensions work with KVP requests", function () { var lastUrl; Resource._Implementations.createImage = function ( request, crossOrigin, deferred ) { lastUrl = request.url; Resource._DefaultImplementations.createImage( new Request({ url: "Data/Images/Red16x16.png" }), crossOrigin, deferred ); }; var uri = new Uri("http://wmts.invalid/kvp"); var query = { tilematrix: 0, layer: "someLayer", style: "someStyle", tilerow: 0, tilecol: 0, tilematrixset: "someTMS", format: "image/jpeg", FOO: "BAR", service: "WMTS", version: "1.0.0", request: "GetTile", }; var provider = new WebMapTileServiceImageryProvider({ layer: query.layer, style: query.style, url: uri.toString(), tileMatrixSetID: query.tilematrixset, dimensions: { FOO: query.FOO, }, }); provider._reload = jasmine.createSpy(); return pollToPromise(function () { return provider.ready; }) .then(function () { return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { // Verify request is correct uri.query(objectToQuery(query)); expect(lastUrl).toEqual(uri.toString()); expect(provider._reload.calls.count()).toEqual(0); // Change value of FOO dimension query.FOO = "BAZ"; provider.dimensions = { FOO: query.FOO, }; expect(provider._reload.calls.count()).toEqual(1); return provider.requestImage(0, 0, 0, new Request()); }) .then(function () { // Verify request changed uri.query(objectToQuery(query)); expect(lastUrl).toEqual(uri.toString()); }); }); });