diff --git a/Source/Scene/ArcGisMapServerImageryProvider.js b/Source/Scene/ArcGisMapServerImageryProvider.js index 8575ef77babd..cbe2ea3a4589 100644 --- a/Source/Scene/ArcGisMapServerImageryProvider.js +++ b/Source/Scene/ArcGisMapServerImageryProvider.js @@ -256,6 +256,23 @@ define([ return this._maximumLevel; }; + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link ArcGisMapServerImageryProvider#isReady} returns true. + * + * @memberof ArcGisMapServerImageryProvider + * + * @returns {Number} The minimum level. Unlike the maximum level, the minimum level must not be undefined. + * + * @exception {DeveloperError} getMinimumLevel must not be called before the imagery provider is ready. + */ + ArcGisMapServerImageryProvider.prototype.getMinimumLevel = function() { + if (!this._ready) { + throw new DeveloperError('getMinimumLevel must not be called before the imagery provider is ready.'); + } + return 0; + }; + /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link ArcGisMapServerImageryProvider#isReady} returns true. diff --git a/Source/Scene/BingMapsImageryProvider.js b/Source/Scene/BingMapsImageryProvider.js index fe819618679c..f5b71316a255 100644 --- a/Source/Scene/BingMapsImageryProvider.js +++ b/Source/Scene/BingMapsImageryProvider.js @@ -307,6 +307,23 @@ define([ return this._maximumLevel; }; + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link BingMapsImageryProvider#isReady} returns true. + * + * @memberof BingMapsImageryProvider + * + * @returns {Number} The minimum level. Unlike the maximum level, the minimum level must not be undefined. + * + * @exception {DeveloperError} getMinimumLevel must not be called before the imagery provider is ready. + */ + BingMapsImageryProvider.prototype.getMinimumLevel = function() { + if (!this._ready) { + throw new DeveloperError('getMinimumLevel must not be called before the imagery provider is ready.'); + } + return 0; + }; + /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link BingMapsImageryProvider#isReady} returns true. diff --git a/Source/Scene/CentralBodySurface.js b/Source/Scene/CentralBodySurface.js index 022b099ef4f0..13b91d87e52c 100644 --- a/Source/Scene/CentralBodySurface.js +++ b/Source/Scene/CentralBodySurface.js @@ -670,7 +670,7 @@ define([ if (imageryLayer.getImageryProvider().isReady()) { // Remove the placeholder and add the actual skeletons (if any) // at the same position. Then continue the loop at the same index. - imagery.releaseReference(); + tileImagery.freeResources(); tileImageryCollection.splice(i, 1); imageryLayer._createTileImagerySkeletons(tile, terrainProvider, i); --i; @@ -704,6 +704,15 @@ define([ parent = parent.parent; } + // If there's no valid parent, remove this TileImagery from the tile. + if (typeof parent === 'undefined') { + tileImagery.freeResources(); + tileImageryCollection.splice(i, 1); + --i; + len = tileImageryCollection.length; + continue; + } + // use that parent imagery instead, storing the original imagery // in originalImagery to keep it alive tileImagery.originalImagery = imagery; diff --git a/Source/Scene/ImageryLayer.js b/Source/Scene/ImageryLayer.js index c4fcaa5076f4..5d37d6323f73 100755 --- a/Source/Scene/ImageryLayer.js +++ b/Source/Scene/ImageryLayer.js @@ -260,9 +260,13 @@ define([ var imageryLevel = getLevelWithMaximumTexelSpacing(this, targetGeometricError, latitudeClosestToEquator); imageryLevel = Math.max(0, imageryLevel); var maximumLevel = imageryProvider.getMaximumLevel(); + var minimumLevel = imageryProvider.getMinimumLevel(); if (imageryLevel > maximumLevel) { imageryLevel = maximumLevel; } + if (imageryLevel < minimumLevel) { + imageryLevel = minimumLevel; + } var imageryTilingScheme = imageryProvider.getTilingScheme(); var northwestTileCoordinates = imageryTilingScheme.positionToTileXY(extent.getNorthwest(), imageryLevel); diff --git a/Source/Scene/ImageryProvider.js b/Source/Scene/ImageryProvider.js index 026ab5976a3e..bd1a43ad0fd4 100755 --- a/Source/Scene/ImageryProvider.js +++ b/Source/Scene/ImageryProvider.js @@ -83,6 +83,18 @@ define([ throw new DeveloperError('This type should not be instantiated directly.'); }; + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link ImageryProvider#isReady} returns true. + * + * @returns {Number} The minimum level. Unlike the maximum level, the minimum level must not be undefined. + * + * @exception {DeveloperError} getMinimumLevel must not be called before the imagery provider is ready. + */ + ImageryProvider.prototype.getMinimumLevel = function() { + throw new DeveloperError('This type should not be instantiated directly.'); + }; + /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link ImageryProvider#isReady} returns true. diff --git a/Source/Scene/OpenStreetMapImageryProvider.js b/Source/Scene/OpenStreetMapImageryProvider.js index 0cda620a7835..f1925bbfcbe3 100644 --- a/Source/Scene/OpenStreetMapImageryProvider.js +++ b/Source/Scene/OpenStreetMapImageryProvider.js @@ -153,6 +153,23 @@ define([ return this._maximumLevel; }; + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link OpenStreetMapImageryProvider#isReady} returns true. + * + * @memberof OpenStreetMapImageryProvider + * + * @returns {Number} The minimum level. Unlike the maximum level, the minimum level must not be undefined. + * + * @exception {DeveloperError} getMinimumLevel must not be called before the imagery provider is ready. + */ + OpenStreetMapImageryProvider.prototype.getMinimumLevel = function() { + if (!this._ready) { + throw new DeveloperError('getMinimumLevel must not be called before the imagery provider is ready.'); + } + return 0; + }; + /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link OpenStreetMapImageryProvider#isReady} returns true. diff --git a/Source/Scene/SingleTileImageryProvider.js b/Source/Scene/SingleTileImageryProvider.js index c2853887eeaf..a2936e12e3e2 100644 --- a/Source/Scene/SingleTileImageryProvider.js +++ b/Source/Scene/SingleTileImageryProvider.js @@ -174,6 +174,23 @@ define([ return 0; }; + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link SingleTileImageryProvider#isReady} returns true. + * + * @memberof SingleTileImageryProvider + * + * @returns {Number} The minimum level. Unlike the maximum level, the minimum level must not be undefined. + * + * @exception {DeveloperError} getMinimumLevel must not be called before the imagery provider is ready. + */ + SingleTileImageryProvider.prototype.getMinimumLevel = function() { + if (!this._ready) { + throw new DeveloperError('getMinimumLevel must not be called before the imagery provider is ready.'); + } + return 0; + }; + /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link SingleTileImageryProvider#isReady} returns true. diff --git a/Source/Scene/WebMapServiceImageryProvider.js b/Source/Scene/WebMapServiceImageryProvider.js index 339a9ce642ce..fc0e0e52d6d4 100644 --- a/Source/Scene/WebMapServiceImageryProvider.js +++ b/Source/Scene/WebMapServiceImageryProvider.js @@ -229,6 +229,23 @@ define([ return this._maximumLevel; }; + /** + * Gets the minimum level-of-detail that can be requested. This function should + * not be called before {@link WebMapServiceImageryProvider#isReady} returns true. + * + * @memberof WebMapServiceImageryProvider + * + * @returns {Number} The minimum level. Unlike the maximum level, the minimum level must not be undefined. + * + * @exception {DeveloperError} getMinimumLevel must not be called before the imagery provider is ready. + */ + WebMapServiceImageryProvider.prototype.getMinimumLevel = function() { + if (!this._ready) { + throw new DeveloperError('getMinimumLevel must not be called before the imagery provider is ready.'); + } + return 0; + }; + /** * Gets the tiling scheme used by this provider. This function should * not be called before {@link WebMapServiceImageryProvider#isReady} returns true. diff --git a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js index 9ac2f60ff3a3..cc7867d9bc58 100644 --- a/Specs/Scene/ArcGisMapServerImageryProviderSpec.js +++ b/Specs/Scene/ArcGisMapServerImageryProviderSpec.js @@ -88,6 +88,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(128); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(2); expect(provider.getTilingScheme()).toBeInstanceOf(WebMercatorTilingScheme); expect(provider.getLogo()).not.toBeUndefined(); @@ -161,6 +162,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(128); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(2); expect(provider.getTilingScheme()).toBeInstanceOf(GeographicTilingScheme); expect(provider.getLogo()).not.toBeUndefined(); @@ -218,6 +220,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(256); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toBeUndefined(); expect(provider.getTilingScheme()).toBeInstanceOf(GeographicTilingScheme); expect(provider.getLogo()).not.toBeUndefined(); @@ -303,6 +306,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(128); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(2); expect(provider.getTilingScheme()).toBeInstanceOf(GeographicTilingScheme); expect(provider.getLogo()).not.toBeUndefined(); diff --git a/Specs/Scene/BingMapsImageryProviderSpec.js b/Specs/Scene/BingMapsImageryProviderSpec.js index d844691996b5..2173e9311158 100644 --- a/Specs/Scene/BingMapsImageryProviderSpec.js +++ b/Specs/Scene/BingMapsImageryProviderSpec.js @@ -180,6 +180,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(256); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(20); expect(provider.getTilingScheme()).toBeInstanceOf(WebMercatorTilingScheme); expect(provider.getTileDiscardPolicy()).toBeInstanceOf(DiscardMissingTileImagePolicy); diff --git a/Specs/Scene/CentralBodySurfaceSpec.js b/Specs/Scene/CentralBodySurfaceSpec.js index ec35301d6dda..eb00a223d9ec 100644 --- a/Specs/Scene/CentralBodySurfaceSpec.js +++ b/Specs/Scene/CentralBodySurfaceSpec.js @@ -17,7 +17,8 @@ defineSuite([ 'Scene/ImageryLayerCollection', 'Scene/OrthographicFrustum', 'Scene/SceneMode', - 'Scene/SingleTileImageryProvider' + 'Scene/SingleTileImageryProvider', + 'Scene/WebMapServiceImageryProvider' ], function( CentralBodySurface, createContext, @@ -36,7 +37,8 @@ defineSuite([ ImageryLayerCollection, OrthographicFrustum, SceneMode, - SingleTileImageryProvider) { + SingleTileImageryProvider, + WebMapServiceImageryProvider) { "use strict"; /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ @@ -346,5 +348,25 @@ defineSuite([ expect(render(context, frameState, cb)).toBeGreaterThan(0); }); }); + + it('renders even if imagery root tiles fail to load', function() { + var layerCollection = cb.getImageryLayers(); + layerCollection.removeAll(); + + var providerWithInvalidRootTiles = new WebMapServiceImageryProvider({ + url : '/invalid', + layers : 'invalid' + }); + + layerCollection.addImageryProvider(providerWithInvalidRootTiles); + + frameState.camera.viewExtent(new Extent(0.0001, 0.0001, 0.0025, 0.0025), Ellipsoid.WGS84); + + updateUntilDone(cb); + + runs(function() { + expect(render(context, frameState, cb)).toBeGreaterThan(0); + }); + }); }); }); diff --git a/Specs/Scene/ImageryLayerSpec.js b/Specs/Scene/ImageryLayerSpec.js index 4130754a0b83..6d35aed07959 100644 --- a/Specs/Scene/ImageryLayerSpec.js +++ b/Specs/Scene/ImageryLayerSpec.js @@ -7,10 +7,13 @@ defineSuite([ 'Core/jsonp', 'Core/loadImage', 'Scene/BingMapsImageryProvider', + 'Scene/EllipsoidTerrainProvider', + 'Scene/GeographicTilingScheme', 'Scene/Imagery', 'Scene/ImageryState', 'Scene/NeverTileDiscardPolicy', 'Scene/SingleTileImageryProvider', + 'Scene/Tile', 'Scene/WebMapServiceImageryProvider' ], function( ImageryLayer, @@ -20,10 +23,13 @@ defineSuite([ jsonp, loadImage, BingMapsImageryProvider, + EllipsoidTerrainProvider, + GeographicTilingScheme, Imagery, ImageryState, NeverTileDiscardPolicy, SingleTileImageryProvider, + Tile, WebMapServiceImageryProvider) { "use strict"; /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/ @@ -182,4 +188,51 @@ defineSuite([ expect(layer.getExtent()).toEqual(extent); expect(layer.isDestroyed()).toEqual(false); }); + + it('honors the imagery provider\'s minimum level', function() { + var tilingScheme = new GeographicTilingScheme(); + var imageryProviderWithMinimumLevel = { + isReady : function() { return true; }, + getExtent : function() { return Extent.MAX_VALUE; }, + getTileWidth : function() { return 256; }, + getTileHeight : function() { return 256; }, + getMaximumLevel : function() { return 10; }, + getMinimumLevel : function() { return 5; }, + getTilingScheme : function() { return tilingScheme; }, + getTileDiscardPolicy : function() { return undefined; }, + getErrorEvent : function() { return undefined; }, + getLogo : function() { return undefined; }, + requestImage : function(hostnames, hostnameIndex, x, y, level) { + expect(level).toBeLessThanOrEqualTo(this.getMaximumLevel()); + expect(level).toBeGreaterThanOrEqualTo(this.getMinimumLevel()); + return undefined; + } + }; + + var layer = new ImageryLayer(imageryProviderWithMinimumLevel); + + waitsFor(function() { + return imageryProviderWithMinimumLevel.isReady(); + }, 'imagery provider to become ready'); + + runs(function() { + var tile = new Tile({ + x : 0, + y: 0, + level : 0, + tilingScheme : tilingScheme + }); + var terrainProvider = new EllipsoidTerrainProvider(); + layer._createTileImagerySkeletons(tile, terrainProvider); + + expect(tile.imagery.length).toBeGreaterThan(0); + + for (var i = 0, len = tile.imagery.length; i < len; ++i) { + var tileImagery = tile.imagery[i]; + var imagery = tileImagery.imagery; + expect(imagery.level).toBeLessThanOrEqualTo(imageryProviderWithMinimumLevel.getMaximumLevel()); + expect(imagery.level).toBeGreaterThanOrEqualTo(imageryProviderWithMinimumLevel.getMinimumLevel()); + } + }); + }); }); diff --git a/Specs/Scene/OpenStreetMapImageryProviderSpec.js b/Specs/Scene/OpenStreetMapImageryProviderSpec.js index 46f2f7d60327..93cd68701b3c 100644 --- a/Specs/Scene/OpenStreetMapImageryProviderSpec.js +++ b/Specs/Scene/OpenStreetMapImageryProviderSpec.js @@ -110,6 +110,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(256); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(18); expect(provider.getTilingScheme()).toBeInstanceOf(WebMercatorTilingScheme); expect(provider.getExtent()).toEqual(new WebMercatorTilingScheme().getExtent()); @@ -200,6 +201,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(256); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(18); expect(provider.getTilingScheme()).toBeInstanceOf(WebMercatorTilingScheme); expect(provider.getExtent()).toEqual(extent); diff --git a/Specs/Scene/SingleTileImageryProviderSpec.js b/Specs/Scene/SingleTileImageryProviderSpec.js index 57ffce441ae4..ae045801e410 100644 --- a/Specs/Scene/SingleTileImageryProviderSpec.js +++ b/Specs/Scene/SingleTileImageryProviderSpec.js @@ -59,6 +59,7 @@ defineSuite([ expect(provider.getTilingScheme().getExtent()).toEqual(extent); expect(provider.getTileWidth()).toEqual(16); expect(provider.getTileHeight()).toEqual(16); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toEqual(0); expect(provider.getTileDiscardPolicy()).toBeUndefined(); }); diff --git a/Specs/Scene/WebMapServiceImageryProviderSpec.js b/Specs/Scene/WebMapServiceImageryProviderSpec.js index ad419d7819df..0cce2793e276 100644 --- a/Specs/Scene/WebMapServiceImageryProviderSpec.js +++ b/Specs/Scene/WebMapServiceImageryProviderSpec.js @@ -195,6 +195,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(256); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toBeUndefined(); expect(provider.getTilingScheme()).toBeInstanceOf(GeographicTilingScheme); expect(provider.getExtent()).toEqual(new GeographicTilingScheme().getExtent()); @@ -315,6 +316,7 @@ defineSuite([ runs(function() { expect(provider.getTileWidth()).toEqual(256); expect(provider.getTileHeight()).toEqual(256); + expect(provider.getMinimumLevel()).toEqual(0); expect(provider.getMaximumLevel()).toBeUndefined(); expect(provider.getTilingScheme()).toBeInstanceOf(GeographicTilingScheme); expect(provider.getExtent()).toEqual(extent);