Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for a minimum level for imagery providers, especially TileMapServiceImageryProvider #913

Merged
merged 5 commits into from
Jun 30, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Beta Releases
* Added `CesiumWidget.onRenderLoopError` which is an `Event` that is raised if an exception is generated inside of the default render loop.
* `ImageryProviderViewModel.creationCommand` can now return an array of ImageryProvider instances, which allows adding multiple layers when a single item is selected in the `BaseLayerPicker` widget.
* Changed static `clone` functions in all objects such that if the object being cloned is undefined, the function will return undefined instead of throwing an exception
* `TileMapServiceImageryProvider` now supports imagery with a minimum level. This improves compatibility with tile sets generated by MapTiler or gdal2tiles.py using their default settings.

### b17 - 2013-06-03

Expand Down
17 changes: 17 additions & 0 deletions Source/Scene/ArcGisMapServerImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,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, or undefined if there is no minimum level.
*
* @exception {DeveloperError} <code>getMinimumLevel</code> 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.
Expand Down
17 changes: 17 additions & 0 deletions Source/Scene/BingMapsImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,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.
*
* @exception {DeveloperError} <code>getMinimumLevel</code> 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.
Expand Down
14 changes: 13 additions & 1 deletion Source/Scene/GridImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ define([

/**
* Gets the maximum level-of-detail that can be requested. This function should
* not be called before {@link BingMapsImageryProvider#isReady} returns true.
* not be called before {@link GridImageryProvider#isReady} returns true.
*
* @memberof GridImageryProvider
*
Expand All @@ -99,6 +99,18 @@ define([
return undefined;
};

/**
* Gets the minimum level-of-detail that can be requested. This function should
* not be called before {@link GridImageryProvider#isReady} returns true.
*
* @memberof GridImageryProvider
*
* @returns {Number} The minimum level.
*/
GridImageryProvider.prototype.getMinimumLevel = function() {
return undefined;
};

/**
* Gets the tiling scheme used by this provider. This function should
* not be called before {@link BingMapsImageryProvider#isReady} returns true.
Expand Down
7 changes: 7 additions & 0 deletions Source/Scene/ImageryLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,13 @@ define([
imageryLevel = maximumLevel;
}

if (typeof imageryProvider.getMinimumLevel !== 'undefined') {
var minimumLevel = imageryProvider.getMinimumLevel();
if (imageryLevel < minimumLevel) {
imageryLevel = minimumLevel;
}
}

var imageryTilingScheme = imageryProvider.getTilingScheme();
var northwestTileCoordinates = imageryTilingScheme.positionToTileXY(extent.getNorthwest(), imageryLevel);
var southeastTileCoordinates = imageryTilingScheme.positionToTileXY(extent.getSoutheast(), imageryLevel);
Expand Down
16 changes: 16 additions & 0 deletions Source/Scene/ImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,22 @@ 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. Generally,
* a minimum level should only be used when the extent of the imagery is small
* enough that the number of tiles at the minimum level is small. An imagery
* provider with more than a few tiles at the minimum level will lead to
* rendering problems.
*
* @returns {Number} The minimum level, or undefined if there is no minimum level.
*
* @exception {DeveloperError} <code>getMinimumLevel</code> 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.
Expand Down
17 changes: 17 additions & 0 deletions Source/Scene/OpenStreetMapImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,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.
*
* @exception {DeveloperError} <code>getMinimumLevel</code> 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.
Expand Down
17 changes: 17 additions & 0 deletions Source/Scene/SingleTileImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,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.
*
* @exception {DeveloperError} <code>getMinimumLevel</code> 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.
Expand Down
14 changes: 13 additions & 1 deletion Source/Scene/TileCoordinatesImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ define([

/**
* Gets the maximum level-of-detail that can be requested. This function should
* not be called before {@link BingMapsImageryProvider#isReady} returns true.
* not be called before {@link TileCoordinatesImageryProvider#isReady} returns true.
*
* @memberof TileCoordinatesImageryProvider
*
Expand All @@ -83,6 +83,18 @@ define([
return undefined;
};

/**
* Gets the minimum level-of-detail that can be requested. This function should
* not be called before {@link TileCoordinatesImageryProvider#isReady} returns true.
*
* @memberof TileCoordinatesImageryProvider
*
* @returns {Number} The minimum level.
*/
TileCoordinatesImageryProvider.prototype.getMinimumLevel = function() {
return undefined;
};

/**
* Gets the tiling scheme used by this provider. This function should
* not be called before {@link BingMapsImageryProvider#isReady} returns true.
Expand Down
29 changes: 29 additions & 0 deletions Source/Scene/TileMapServiceImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ define([
* @param {String} [description.fileExtension='png'] The file extension for images on the server.
* @param {Object} [description.proxy] A proxy to use for requests. This object is expected to have a getURL function which returns the proxied URL.
* @param {String} [description.credit=''] A string crediting the data source, which is displayed on the canvas.
* @param {Number} [description.minimumLevel=0] The minimum level-of-detail supported by the imagery provider. Take care when specifying
* this that the number of tiles at the minimum level is small, such as four or less. A larger number is likely
* to result in rendering problems.
* @param {Number} [description.maximumLevel=18] The maximum level-of-detail supported by the imagery provider.
* @param {Extent} [description.extent=Extent.MAX_VALUE] The extent, in radians, covered by the image.
* @param {TilingScheme} [description.tilingScheme] The tiling scheme specifying how the ellipsoidal
Expand Down Expand Up @@ -104,6 +107,7 @@ define([
that._tileWidth = defaultValue(description.tileWidth, parseInt(format.getAttribute('width'), 10));
that._tileHeight = defaultValue(description.tileHeight, parseInt(format.getAttribute('height'), 10));
var tilesets = xml.getElementsByTagName('TileSet');
that._minimumLevel = defaultValue(description.minimumLevel, parseInt(tilesets[0].getAttribute('order'), 10));
that._maximumLevel = defaultValue(description.maximumLevel, parseInt(tilesets[tilesets.length - 1].getAttribute('order'), 10));

// extent handling
Expand Down Expand Up @@ -138,6 +142,16 @@ define([
that._extent.north = tilingScheme.getExtent().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(that._extent.getSouthwest(), that._minimumLevel);
var neTile = tilingScheme.positionToTileXY(that._extent.getNortheast(), that._minimumLevel);
var tileCount = (Math.abs(neTile.x - swTile.x) + 1) * (Math.abs(neTile.y - swTile.y) + 1);
if (tileCount > 4) {
that._minimumLevel = 0;
}

that._tilingScheme = tilingScheme;
that._ready = true;
}, function(error) {
Expand Down Expand Up @@ -220,6 +234,21 @@ define([
return this._tileHeight;
};

/**
* Gets the minimum level-of-detail that can be requested. This function should
* not be called before {@link TileMapServiceImageryProvider#isReady} returns true.
*
* @memberof TileMapServiceImageryProvider
*
* @returns {Number} The minimum level.
*/
TileMapServiceImageryProvider.prototype.getMinimumLevel = function() {
if (!this._ready) {
throw new DeveloperError('getMinimumLevel must not be called before the imagery provider is ready.');
}
return this._minimumLevel;
};

/**
* Gets the maximum level-of-detail that can be requested. This function should
* not be called before {@link TileMapServiceImageryProvider#isReady} returns true.
Expand Down
17 changes: 17 additions & 0 deletions Source/Scene/WebMapServiceImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,23 @@ define([
return this._tileHeight;
};

/**
* 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.
*
* @exception {DeveloperError} <code>getMinimumLevel</code> 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 maximum level-of-detail that can be requested. This function should
* not be called before {@link WebMapServiceImageryProvider#isReady} returns true.
Expand Down
68 changes: 68 additions & 0 deletions Specs/Scene/TileMapServiceImageryProviderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,4 +323,72 @@ defineSuite([
expect(provider.getExtent().north).toBeLessThanOrEqualTo(provider.getTilingScheme().getExtent().north);
});
});

it('uses a minimum level if the tilemapresource.xml specifies one and it is reasonable', function() {
loadXML.loadXML = function(url, headers, deferred) {
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</Title>" +
" <Abstract/>" +
" <SRS>EPSG:900913</SRS>" +
" <BoundingBox minx='-10.0' miny='5.0' maxx='-9.0' maxy='6.0'/>" +
" <Origin x='-88.0' y='-180.00000000000000'/>" +
" <TileFormat width='256' height='256' mime-type='image/png' extension='png'/>" +
" <TileSets profile='mercator'>" +
" <TileSet href='7' units-per-pixel='1222.99245234375008' order='7'/>" +
" <TileSet href='8' units-per-pixel='611.49622617187504' order='8'/>" +
" </TileSets>" +
"</TileMap>";
var xml = parser.parseFromString(xmlString, "text/xml");
deferred.resolve(xml);
};

var provider = new TileMapServiceImageryProvider({
url : 'made/up/tms/server'
});

waitsFor(function() {
return provider.isReady();
}, 'imagery provider to become ready');

runs(function() {
expect(provider.getMaximumLevel()).toBe(8);
expect(provider.getMinimumLevel()).toBe(7);
});
});

it('ignores the minimum level in the tilemapresource.xml if it is unreasonable', function() {
loadXML.loadXML = function(url, headers, deferred) {
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</Title>" +
" <Abstract/>" +
" <SRS>EPSG:900913</SRS>" +
" <BoundingBox minx='-170.0' miny='-85.0' maxx='170.0' maxy='85.0'/>" +
" <Origin x='-88.0' y='-180.00000000000000'/>" +
" <TileFormat width='256' height='256' mime-type='image/png' extension='png'/>" +
" <TileSets profile='mercator'>" +
" <TileSet href='7' units-per-pixel='1222.99245234375008' order='7'/>" +
" <TileSet href='8' units-per-pixel='611.49622617187504' order='8'/>" +
" </TileSets>" +
"</TileMap>";
var xml = parser.parseFromString(xmlString, "text/xml");
deferred.resolve(xml);
};

var provider = new TileMapServiceImageryProvider({
url : 'made/up/tms/server'
});

waitsFor(function() {
return provider.isReady();
}, 'imagery provider to become ready');

runs(function() {
expect(provider.getMaximumLevel()).toBe(8);
expect(provider.getMinimumLevel()).toBe(0);
});
});
});