From 6527f9ce9ffe836ac092ea385c646cde6fd556eb Mon Sep 17 00:00:00 2001 From: Daniel Leone Date: Mon, 9 Dec 2019 12:28:54 +0800 Subject: [PATCH 1/4] Made the TileMapServiceImageryProvider reject the readyPromise if the metadata request fails. This brings it inline with all the other ImageryProviders that use a metadata resource. --- Source/Scene/TileMapServiceImageryProvider.js | 29 +- .../TileMapServiceImageryProviderSpec.js | 445 +++++++++--------- 2 files changed, 232 insertions(+), 242 deletions(-) diff --git a/Source/Scene/TileMapServiceImageryProvider.js b/Source/Scene/TileMapServiceImageryProvider.js index 302c3ebb7474..e9bc61d8de06 100644 --- a/Source/Scene/TileMapServiceImageryProvider.js +++ b/Source/Scene/TileMapServiceImageryProvider.js @@ -262,31 +262,10 @@ import UrlTemplateImageryProvider from './UrlTemplateImageryProvider.js'; }; TileMapServiceImageryProvider.prototype._metadataFailure = function(error) { - // Can't load XML, still allow options and defaults - var options = this._options; - var fileExtension = defaultValue(options.fileExtension, 'png'); - var tileWidth = defaultValue(options.tileWidth, 256); - var tileHeight = defaultValue(options.tileHeight, 256); - var minimumLevel = defaultValue(options.minimumLevel, 0); - var maximumLevel = options.maximumLevel; - var tilingScheme = defined(options.tilingScheme) ? options.tilingScheme : new WebMercatorTilingScheme({ellipsoid : options.ellipsoid}); - var rectangle = defaultValue(options.rectangle, tilingScheme.rectangle); - - var templateResource = this._tmsResource.getDerivedResource({ - url : '{z}/{x}/{reverseY}.' + fileExtension - }); - - this._deferred.resolve({ - url : templateResource, - tilingScheme : tilingScheme, - rectangle : rectangle, - tileWidth : tileWidth, - tileHeight : tileHeight, - minimumLevel : minimumLevel, - maximumLevel : maximumLevel, - tileDiscardPolicy : options.tileDiscardPolicy, - credit : options.credit - }); + // Can't load XML, reject the ready promise. + var message = 'An error occurred while accessing ' + this._xmlResource.url + '. Error: ' + error; + TileProviderError.handleError(error, this, this._errorEvent, message, undefined, undefined, undefined, null); + this._deferred.reject(new RuntimeError(message)); }; export default TileMapServiceImageryProvider; diff --git a/Specs/Scene/TileMapServiceImageryProviderSpec.js b/Specs/Scene/TileMapServiceImageryProviderSpec.js index 30c1e1f0a6bc..a1d5c393162c 100644 --- a/Specs/Scene/TileMapServiceImageryProviderSpec.js +++ b/Specs/Scene/TileMapServiceImageryProviderSpec.js @@ -19,6 +19,44 @@ import { when } from '../../Source/Cesium.js'; describe('Scene/TileMapServiceImageryProvider', function() { + var validSampleXmlString = + '' + + ' NE2_HR_LC_SR_W_DR_recolored.tif' + + ' ' + + ' EPSG:4326' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + + function patchRequestScheduler(xmlResponseString) { + Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + // We can't resolve the promise immediately, because then the error would be raised + // before we could subscribe to it. This a problem particular to tests. + setTimeout(function() { + var parser = new DOMParser(); + var xml = parser.parseFromString(xmlResponseString, 'text/xml'); + deferred.resolve(xml); + }, 1); + }; + } + + function patchRequestSchedulerToRejectRequest() { + Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { + // We can't resolve the promise immediately, because then the error would be raised + // before we could subscribe to it. This a problem particular to tests. + setTimeout(function() { + deferred.reject(new Error('whoops; rejecting xhr request')); + }, 1); + }; + } + beforeEach(function() { RequestScheduler.clearForSpecs(); }); @@ -36,6 +74,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('resolves readyPromise', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server/' }); @@ -47,6 +86,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('resolves readyPromise when promise url is used', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : when.resolve('made/up/tms/server/') }); @@ -58,6 +98,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('resolves readyPromise with Resource', function() { + patchRequestScheduler(validSampleXmlString); var resource = new Resource({ url : 'made/up/tms/server/' }); @@ -86,29 +127,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('rejects readyPromise on error', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - // We can't resolve the promise immediately, because then the error would be raised - // before we could subscribe to it. This a problem particular to tests. - setTimeout(function() { - var parser = new DOMParser(); - var xmlString = - '' + - ' ' + - ' <Abstract/>' + - ' <SRS>EPSG:4326</SRS>' + - ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + - ' <Origin x="-90.0" y="-180.0"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="foobar">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }, 1); - }; - + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:4326</SRS>' + + ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + + ' <Origin x="-90.0" y="-180.0"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="foobar">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' }); @@ -122,28 +154,19 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('rejects readyPromise on invalid xml', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - // We can't resolve the promise immediately, because then the error would be raised - // before we could subscribe to it. This a problem particular to tests. - setTimeout(function() { - var parser = new DOMParser(); - var xmlString = - '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + - ' <Title/>' + - ' <Abstract/>' + - ' <SRS>EPSG:4326</SRS>' + - ' <Origin x="-90.0" y="-180.0"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="foobar">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }, 1); - }; - + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:4326</SRS>' + + ' <Origin x="-90.0" y="-180.0"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="foobar">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' }); @@ -165,6 +188,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('returns valid value for hasAlphaChannel', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server/' }); @@ -177,6 +201,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports a slash at the end of the URL', function() { + patchRequestScheduler(validSampleXmlString); var baseUrl = 'made/up/tms/server/'; var provider = new TileMapServiceImageryProvider({ url : baseUrl @@ -200,6 +225,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports no slash at the endof the URL', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'http://made/up/tms/server' }); @@ -222,6 +248,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports a query string at the end of the URL', function() { + patchRequestScheduler(validSampleXmlString); var baseUrl = 'made/up/tms/server/'; var provider = new TileMapServiceImageryProvider({ url : baseUrl + '?a=some&b=query' @@ -245,6 +272,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('requestImage returns a promise for an image and loads it for cross-origin use', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server/' }); @@ -252,12 +280,10 @@ describe('Scene/TileMapServiceImageryProvider', function() { return pollToPromise(function() { return provider.ready; }).then(function() { - expect(provider.url).toEqual(getAbsoluteUri('made/up/tms/server/{z}/{x}/{reverseY}.png')); + // check some details about the tilemapresourcel.xml so we know we got parsed/configured properly + expect(provider.url).toEqual(getAbsoluteUri('made/up/tms/server/{z}/{x}/{reverseY}.jpg')); expect(provider.tileWidth).toEqual(256); expect(provider.tileHeight).toEqual(256); - expect(provider.maximumLevel).toBeUndefined(); - expect(provider.tilingScheme).toBeInstanceOf(WebMercatorTilingScheme); - expect(provider.rectangle).toEqual(new WebMercatorTilingScheme().rectangle); spyOn(Resource._Implementations, 'createImage').and.callFake(function(url, crossOrigin, deferred) { // Just return any old image. @@ -272,6 +298,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('when no credit is supplied, the provider has no logo', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' }); @@ -283,6 +310,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('turns the supplied credit into a logo', function() { + patchRequestScheduler(validSampleXmlString); var providerWithCredit = new TileMapServiceImageryProvider({ url : 'made/up/gms/server', credit : 'Thanks to our awesome made up source of this imagery!' @@ -313,6 +341,8 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('rectangle passed to constructor does not affect tile numbering', function() { + patchRequestScheduler(validSampleXmlString); + var rectangle = new Rectangle(0.1, 0.2, 0.3, 0.4); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server', @@ -322,10 +352,12 @@ describe('Scene/TileMapServiceImageryProvider', function() { return pollToPromise(function() { return provider.ready; }).then(function() { + // check some values coming from tilemapresource.xml expect(provider.tileWidth).toEqual(256); expect(provider.tileHeight).toEqual(256); - expect(provider.maximumLevel).toBeUndefined(); - expect(provider.tilingScheme).toBeInstanceOf(WebMercatorTilingScheme); + expect(provider.maximumLevel).toEqual(2); + expect(provider.tilingScheme).toBeInstanceOf(GeographicTilingScheme); + // check our rectangle from the constructor is correctly used expect(provider.rectangle.west).toEqualEpsilon(rectangle.west, CesiumMath.EPSILON14); expect(provider.rectangle.east).toEqualEpsilon(rectangle.east, CesiumMath.EPSILON14); expect(provider.rectangle.north).toEqualEpsilon(rectangle.north, CesiumMath.EPSILON14); @@ -347,6 +379,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('uses maximumLevel passed to constructor', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server', maximumLevel : 5 @@ -360,6 +393,7 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('raises error event when image cannot be loaded', function() { + patchRequestScheduler(validSampleXmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' }); @@ -409,24 +443,19 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('keeps the rectangle within the bounds allowed by the tiling scheme no matter what the tilemapresource.xml says.', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - "<TileMap version='1.0.0' tilemapservice='http://tms.osgeo.org/1.0.0'>" + - ' <Title>dnb_land_ocean_ice.2012.54000x27000_geo.tif' + - ' ' + - ' EPSG:900913' + - " " + - " " + - " " + - " " + - " " + - ' ' + - ''; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; - + var xmlString = + "" + + ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + + ' ' + + ' EPSG:900913' + + " " + + " " + + " " + + " " + + " " + + ' ' + + ''; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' }); @@ -446,24 +475,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('uses a minimum level if the tilemapresource.xml specifies one and it is reasonable', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - "" + - ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + - ' ' + - ' EPSG:900913' + - " " + - " " + - " " + - " " + - " " + - " " + - ' ' + - ''; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + "" + + ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + + ' ' + + ' EPSG:900913' + + " " + + " " + + " " + + " " + + " " + + " " + + ' ' + + ''; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' @@ -478,24 +503,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('ignores the minimum level in the tilemapresource.xml if it is unreasonable', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - "" + - ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + - ' ' + - ' EPSG:900913' + - " " + - " " + - " " + - " " + - " " + - " " + - ' ' + - ''; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + "" + + ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + + ' ' + + ' EPSG:900913' + + " " + + " " + + " " + + " " + + " " + + " " + + ' ' + + ''; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' @@ -510,24 +531,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('handles XML with casing differences', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - "" + - ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + - ' ' + - ' EPSG:900913' + - " " + - " " + - " " + - " " + - " " + - " " + - ' ' + - ''; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + "" + + ' dnb_land_ocean_ice.2012.54000x27000_geo.tif' + + ' ' + + ' EPSG:900913' + + " " + + " " + + " " + + " " + + " " + + " " + + ' ' + + ''; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' @@ -542,24 +559,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports the global-mercator profile with a non-flipped, mercator bounding box', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - '' + - ' ' + - ' <Abstract/>' + - ' <SRS>EPSG:900913</SRS>' + - ' <BoundingBox minx="-11877789.66764229300000" miny="1707163.75952051670000" maxx="-4696205.45407573510000" maxy="7952627.07365330120000"/>' + - ' <Origin x="-20037508.34278924400000" y="-20037508.34278924400000"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="global-mercator">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:900913</SRS>' + + ' <BoundingBox minx="-11877789.66764229300000" miny="1707163.75952051670000" maxx="-4696205.45407573510000" maxy="7952627.07365330120000"/>' + + ' <Origin x="-20037508.34278924400000" y="-20037508.34278924400000"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="global-mercator">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' @@ -583,24 +596,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports the global-geodetic profile with a non-flipped, geographic bounding box', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + - ' <Title/>' + - ' <Abstract/>' + - ' <SRS>EPSG:4326</SRS>' + - ' <BoundingBox minx="-123.0" miny="-10.0" maxx="-110.0" maxy="11.0"/>' + - ' <Origin x="-180.0" y="-90.0"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="global-geodetic">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:4326</SRS>' + + ' <BoundingBox minx="-123.0" miny="-10.0" maxx="-110.0" maxy="11.0"/>' + + ' <Origin x="-180.0" y="-90.0"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="global-geodetic">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' @@ -623,24 +632,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports the old mercator profile with a flipped, geographic bounding box', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + - ' <Title/>' + - ' <Abstract/>' + - ' <SRS>EPSG:900913</SRS>' + - ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + - ' <Origin x="-90.0" y="-180.0"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="mercator">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:900913</SRS>' + + ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + + ' <Origin x="-90.0" y="-180.0"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="mercator">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server', @@ -664,24 +669,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('supports the old geodetic profile with a flipped, geographic bounding box', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - var parser = new DOMParser(); - var xmlString = - '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + - ' <Title/>' + - ' <Abstract/>' + - ' <SRS>EPSG:4326</SRS>' + - ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + - ' <Origin x="-90.0" y="-180.0"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="geodetic">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }; + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:4326</SRS>' + + ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + + ' <Origin x="-90.0" y="-180.0"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="geodetic">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server', @@ -705,28 +706,20 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); it('raises an error if tilemapresource.xml specifies an unsupported profile', function() { - Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) { - // We can't resolve the promise immediately, because then the error would be raised - // before we could subscribe to it. This a problem particular to tests. - setTimeout(function() { - var parser = new DOMParser(); - var xmlString = - '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + - ' <Title/>' + - ' <Abstract/>' + - ' <SRS>EPSG:4326</SRS>' + - ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + - ' <Origin x="-90.0" y="-180.0"/>' + - ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + - ' <TileSets profile="foobar">' + - ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + - ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + - ' </TileSets>' + - '</TileMap>'; - var xml = parser.parseFromString(xmlString, 'text/xml'); - deferred.resolve(xml); - }, 1); - }; + var xmlString = + '<TileMap version="1.0.0" tilemapservice="http://tms.osgeo.org/1.0.0">' + + ' <Title/>' + + ' <Abstract/>' + + ' <SRS>EPSG:4326</SRS>' + + ' <BoundingBox minx="-10.0" miny="-123.0" maxx="11.0" maxy="-110.0"/>' + + ' <Origin x="-90.0" y="-180.0"/>' + + ' <TileFormat width="256" height="256" mime-type="image/png" extension="png"/>' + + ' <TileSets profile="foobar">' + + ' <TileSet href="2" units-per-pixel="39135.75848201024200" order="2"/>' + + ' <TileSet href="3" units-per-pixel="19567.87924100512100" order="3"/>' + + ' </TileSets>' + + '</TileMap>'; + patchRequestScheduler(xmlString); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server' @@ -744,4 +737,22 @@ describe('Scene/TileMapServiceImageryProvider', function() { expect(errorRaised).toBe(true); }); }); + + it('rejects readyPromise if the tilemapresource.xml request fails', function (done) { + patchRequestSchedulerToRejectRequest(); + var provider = new TileMapServiceImageryProvider({ + url : 'made/up/tms/server/', + maximumLevel : 10 + }); + + return provider.readyPromise.then(function() { + // we don't want to resolve this promise because the tilemapresource.xml data is required! + // if we start assuming default values for the xml and combined those defaults with passed in options from the constructor. + // we can end up with unintended and unexpected configurations that can hang the browser. + done.fail('Should not resolve.'); + }).otherwise(function(error) { + expect(error.message).toContain('An error occurred while accessing'); + expect(error.message).toContain('/made/up/tms/server/tilemapresource.xml.'); + }); + }); }); From daa371200bd6b207bbe3f7acf84f3eee02c1c34a Mon Sep 17 00:00:00 2001 From: Daniel Leone <mail.dleone@gmail.com> Date: Mon, 9 Dec 2019 13:10:33 +0800 Subject: [PATCH 2/4] Updated CONTRIBUTORS.md and CHANGES.md --- CHANGES.md | 1 + CONTRIBUTORS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index f4f0b807dff9..25a75e963707 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ Change Log ##### Fixes :wrench: * Fix Geocoder auto-complete suggestions when hosted inside Web Components. [#8425](https://github.com/AnalyticalGraphicsInc/cesium/pull/8425) +* `TileMapServiceImageryProvider` will now reject the `readyPromise` if the `tilemapresource.xml` metadata request fails [#8448](https://github.com/AnalyticalGraphicsInc/cesium/pull/8448) ### 1.64.0 - 2019-12-02 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6346f2b35a77..423ac2aca9ec 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -239,3 +239,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to Cesiu * [Tinco Andringa](https://github.com/tinco) * [André Borud](https://github.com/andreborud) * [Nathan Schulte](https://github.com/nmschulte) +* [Daniel Leone](https://github.com/danielleone) From daf0508e2114e2642483aaa25aed2dae5332c397 Mon Sep 17 00:00:00 2001 From: Daniel Leone <mail.dleone@gmail.com> Date: Thu, 13 Feb 2020 13:31:43 +0800 Subject: [PATCH 3/4] change the logic so instead of failing, minimum detail level will be forced to zero for time map service imagery provider updates CHANGES.md added another test --- CHANGES.md | 14 +-- Source/Scene/TileMapServiceImageryProvider.js | 93 +++++++++++++------ .../TileMapServiceImageryProviderSpec.js | 39 ++++++-- 3 files changed, 100 insertions(+), 46 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 94bb8455eb34..ff3c968213a2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,18 +4,8 @@ Change Log ### 1.65.0 - 2019-01-02 ##### Fixes :wrench: -* Fixed Geocoder auto-complete suggestions when hosted inside Web Components. [#8425](https://github.com/AnalyticalGraphicsInc/cesium/pull/8425) -* Fixed terrain tile culling problems when under ellipsoid. [#8397](https://github.com/AnalyticalGraphicsInc/cesium/pull/8397) -* Fixed primitive culling when below the ellipsoid but above terrain. [#8398](https://github.com/AnalyticalGraphicsInc/cesium/pull/8398) -* Improved the translucency calculation for the Water material type. [#8455](https://github.com/AnalyticalGraphicsInc/cesium/pull/8455) -* Fixed bounding volume calculation for `GroundPrimitive`. [#4883](https://github.com/AnalyticalGraphicsInc/cesium/issues/4483) -* Fixed `OrientedBoundingBox.fromRectangle` for rectangles with width greater than 180 degrees. [#8475](https://github.com/AnalyticalGraphicsInc/cesium/pull/8475) -* Fixed globe picking so that it returns the closest intersecting triangle instead of the first intersecting triangle. [#8390](https://github.com/AnalyticalGraphicsInc/cesium/pull/8390) -* Fixed horizon culling issues with large root tiles. [#8487](https://github.com/AnalyticalGraphicsInc/cesium/pull/8487) -* `TileMapServiceImageryProvider` will now reject the `readyPromise` if the `tilemapresource.xml` metadata request fails [#8448](https://github.com/AnalyticalGraphicsInc/cesium/pull/8448) - -##### Additions :tada: -* Added `Globe.backFaceCulling` to support viewing terrain from below the surface. [#8470](https://github.com/AnalyticalGraphicsInc/cesium/pull/8470) +* Fix Geocoder auto-complete suggestions when hosted inside Web Components. [#8425](https://github.com/AnalyticalGraphicsInc/cesium/pull/8425) +* `TileMapServiceImageryProvider` will now force `minimumLevel` to 0 if the `tilemapresource.xml` metadata request fails and the `rectangle` is too large for the given detail level [#8448](https://github.com/AnalyticalGraphicsInc/cesium/pull/8448) ### 1.64.0 - 2019-12-02 diff --git a/Source/Scene/TileMapServiceImageryProvider.js b/Source/Scene/TileMapServiceImageryProvider.js index e9bc61d8de06..75db1292daf5 100644 --- a/Source/Scene/TileMapServiceImageryProvider.js +++ b/Source/Scene/TileMapServiceImageryProvider.js @@ -113,6 +113,40 @@ import UrlTemplateImageryProvider from './UrlTemplateImageryProvider.js'; this._xmlResource.fetchXML().then(this._metadataSuccess).otherwise(this._metadataFailure); }; + /** + * Mutates the properties of a given rectangle so it does not extend outside of the given tiling scheme's rectangle + */ + function confineRectangleToTilingScheme(rectangle, tilingScheme) { + if (rectangle.west < tilingScheme.rectangle.west) { + rectangle.west = tilingScheme.rectangle.west; + } + if (rectangle.east > tilingScheme.rectangle.east) { + rectangle.east = tilingScheme.rectangle.east; + } + if (rectangle.south < tilingScheme.rectangle.south) { + rectangle.south = tilingScheme.rectangle.south; + } + if (rectangle.north > tilingScheme.rectangle.north) { + rectangle.north = tilingScheme.rectangle.north; + } + return rectangle; + } + + function calculateSafeMinimumDetailLevel(tilingScheme, rectangle, minimumLevel) { + console.log(`checking safe minimum detail for ${minimumLevel}`, rectangle); + // Check the number of tiles at the minimum level. If it's more than four, + // try requesting the lower levels anyway, because starting at the higher minimum + // level will cause too many tiles to be downloaded and rendered. + var swTile = tilingScheme.positionToTileXY(Rectangle.southwest(rectangle), minimumLevel); + var neTile = tilingScheme.positionToTileXY(Rectangle.northeast(rectangle), minimumLevel); + var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1); + console.log(`tile count was ${tileCount}`, tileCount < 4); + if (tileCount > 4) { + return 0; + } + return minimumLevel; + } + TileMapServiceImageryProvider.prototype._metadataSuccess = function(xml) { var tileFormatRegex = /tileformat/i; var tileSetRegex = /tileset/i; @@ -221,28 +255,9 @@ import UrlTemplateImageryProvider from './UrlTemplateImageryProvider.js'; } // The rectangle must not be outside the bounds allowed by the tiling scheme. - if (rectangle.west < tilingScheme.rectangle.west) { - rectangle.west = tilingScheme.rectangle.west; - } - if (rectangle.east > tilingScheme.rectangle.east) { - rectangle.east = tilingScheme.rectangle.east; - } - if (rectangle.south < tilingScheme.rectangle.south) { - rectangle.south = tilingScheme.rectangle.south; - } - if (rectangle.north > tilingScheme.rectangle.north) { - rectangle.north = tilingScheme.rectangle.north; - } - - // Check the number of tiles at the minimum level. If it's more than four, - // try requesting the lower levels anyway, because starting at the higher minimum - // level will cause too many tiles to be downloaded and rendered. - var swTile = tilingScheme.positionToTileXY(Rectangle.southwest(rectangle), minimumLevel); - var neTile = tilingScheme.positionToTileXY(Rectangle.northeast(rectangle), minimumLevel); - var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1); - if (tileCount > 4) { - minimumLevel = 0; - } + rectangle = confineRectangleToTilingScheme(rectangle, tilingScheme); + // clamp our minimum detail level to something that isn't going to request a ridiculous number of tiles + minimumLevel = calculateSafeMinimumDetailLevel(tilingScheme, rectangle, minimumLevel); var templateResource = this._tmsResource.getDerivedResource({ url : '{z}/{x}/{reverseY}.' + fileExtension @@ -262,10 +277,36 @@ import UrlTemplateImageryProvider from './UrlTemplateImageryProvider.js'; }; TileMapServiceImageryProvider.prototype._metadataFailure = function(error) { - // Can't load XML, reject the ready promise. - var message = 'An error occurred while accessing ' + this._xmlResource.url + '. Error: ' + error; - TileProviderError.handleError(error, this, this._errorEvent, message, undefined, undefined, undefined, null); - this._deferred.reject(new RuntimeError(message)); + // Can't load XML, still allow options and defaults + var options = this._options; + var fileExtension = defaultValue(options.fileExtension, 'png'); + var tileWidth = defaultValue(options.tileWidth, 256); + var tileHeight = defaultValue(options.tileHeight, 256); + var maximumLevel = options.maximumLevel; + var tilingScheme = defined(options.tilingScheme) ? options.tilingScheme : new WebMercatorTilingScheme({ellipsoid : options.ellipsoid}); + + var rectangle = defaultValue(options.rectangle, tilingScheme.rectangle); + // The rectangle must not be outside the bounds allowed by the tiling scheme. + rectangle = confineRectangleToTilingScheme(rectangle, tilingScheme); + + // make sure we use a safe minimum detail level, so we don't request a ridiculous number of tiles + var minimumLevel = calculateSafeMinimumDetailLevel(tilingScheme, rectangle, options.maximumLevel); + + var templateResource = this._tmsResource.getDerivedResource({ + url : '{z}/{x}/{reverseY}.' + fileExtension + }); + + this._deferred.resolve({ + url : templateResource, + tilingScheme : tilingScheme, + rectangle : rectangle, + tileWidth : tileWidth, + tileHeight : tileHeight, + minimumLevel : minimumLevel, + maximumLevel : maximumLevel, + tileDiscardPolicy : options.tileDiscardPolicy, + credit : options.credit + }); }; export default TileMapServiceImageryProvider; diff --git a/Specs/Scene/TileMapServiceImageryProviderSpec.js b/Specs/Scene/TileMapServiceImageryProviderSpec.js index a1d5c393162c..9f5a6691732f 100644 --- a/Specs/Scene/TileMapServiceImageryProviderSpec.js +++ b/Specs/Scene/TileMapServiceImageryProviderSpec.js @@ -738,21 +738,44 @@ describe('Scene/TileMapServiceImageryProvider', function() { }); }); - it('rejects readyPromise if the tilemapresource.xml request fails', function (done) { + it('forces minimum detail level to zero if the tilemapresource.xml request fails and the constructor minimum level is too high', function () { patchRequestSchedulerToRejectRequest(); var provider = new TileMapServiceImageryProvider({ url : 'made/up/tms/server/', maximumLevel : 10 }); + // we expect that our minimum detail level was forced to 0, even though we requested 10. + // this is because, our rectangle has been set to the entire world (the default), so a minimum + // detail level of 10 would hang the browser with too many tile requests. + // Forcing detail level to zero to is safe. return provider.readyPromise.then(function() { - // we don't want to resolve this promise because the tilemapresource.xml data is required! - // if we start assuming default values for the xml and combined those defaults with passed in options from the constructor. - // we can end up with unintended and unexpected configurations that can hang the browser. - done.fail('Should not resolve.'); - }).otherwise(function(error) { - expect(error.message).toContain('An error occurred while accessing'); - expect(error.message).toContain('/made/up/tms/server/tilemapresource.xml.'); + expect(provider.minimumLevel).toBe(0); + }); + }); + + it('allows the constructor minimum detail level if the tilemapresource.xml request fails but the constructor rectangle is small enough', function () { + patchRequestSchedulerToRejectRequest(); + var provider = new TileMapServiceImageryProvider({ + url: 'made/up/tms/server/', + // a high minimum detail level + maximumLevel: 12, + // and a very small rectangle + rectangle: new Rectangle( + CesiumMath.toRadians(131.020889), + CesiumMath.toRadians(-25.354730), + CesiumMath.toRadians(131.054363), + CesiumMath.toRadians(-25.335803) + ) + }); + + // we expect that our minimum detail level remains at 12, which is quite high, but that's okay + // because our rectangle is still small enough that it's not too many tiles. + return provider.readyPromise.then(function() { + expect(provider.minimumLevel).toBe(12); + // just make sure we're actually still using a small rectangle + expect(provider.rectangle.width).toBeLessThan(0.001); + expect(provider.rectangle.height).toBeLessThan(0.001); }); }); }); From 5b0a4e049ff5868109ae463e5b8b54c0a27631cc Mon Sep 17 00:00:00 2001 From: Daniel Leone <mail.dleone@gmail.com> Date: Thu, 13 Feb 2020 13:48:34 +0800 Subject: [PATCH 4/4] whoops, who left those sneaky logs in there --- Source/Scene/TileMapServiceImageryProvider.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/Source/Scene/TileMapServiceImageryProvider.js b/Source/Scene/TileMapServiceImageryProvider.js index 75db1292daf5..b284d6ba3466 100644 --- a/Source/Scene/TileMapServiceImageryProvider.js +++ b/Source/Scene/TileMapServiceImageryProvider.js @@ -133,14 +133,12 @@ import UrlTemplateImageryProvider from './UrlTemplateImageryProvider.js'; } function calculateSafeMinimumDetailLevel(tilingScheme, rectangle, minimumLevel) { - console.log(`checking safe minimum detail for ${minimumLevel}`, rectangle); // Check the number of tiles at the minimum level. If it's more than four, // try requesting the lower levels anyway, because starting at the higher minimum // level will cause too many tiles to be downloaded and rendered. var swTile = tilingScheme.positionToTileXY(Rectangle.southwest(rectangle), minimumLevel); var neTile = tilingScheme.positionToTileXY(Rectangle.northeast(rectangle), minimumLevel); var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1); - console.log(`tile count was ${tileCount}`, tileCount < 4); if (tileCount > 4) { return 0; }