You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
700 lines
20 KiB
JavaScript
700 lines
20 KiB
JavaScript
import when from "../ThirdParty/when.js";
|
|
import Cartesian2 from "./Cartesian2.js";
|
|
import Credit from "./Credit.js";
|
|
import defaultValue from "./defaultValue.js";
|
|
import defined from "./defined.js";
|
|
import DeveloperError from "./DeveloperError.js";
|
|
import Ellipsoid from "./Ellipsoid.js";
|
|
import Event from "./Event.js";
|
|
import GeographicTilingScheme from "./GeographicTilingScheme.js";
|
|
import HeightmapEncoding from "./HeightmapEncoding.js";
|
|
import HeightmapTerrainData from "./HeightmapTerrainData.js";
|
|
import Rectangle from "./Rectangle.js";
|
|
import Request from "./Request.js";
|
|
import RequestState from "./RequestState.js";
|
|
import RequestType from "./RequestType.js";
|
|
import Resource from "./Resource.js";
|
|
import RuntimeError from "./RuntimeError.js";
|
|
import TerrainProvider from "./TerrainProvider.js";
|
|
import TileAvailability from "./TileAvailability.js";
|
|
import TileProviderError from "./TileProviderError.js";
|
|
import WebMercatorTilingScheme from "./WebMercatorTilingScheme.js";
|
|
|
|
var ALL_CHILDREN = 15;
|
|
|
|
/**
|
|
* A {@link TerrainProvider} that produces terrain geometry by tessellating height maps
|
|
* retrieved from Elevation Tiles of an an ArcGIS ImageService.
|
|
*
|
|
* @alias ArcGISTiledElevationTerrainProvider
|
|
* @constructor
|
|
*
|
|
* @param {Object} options Object with the following properties:
|
|
* @param {Resource|String|Promise<Resource>|Promise<String>} options.url The URL of the ArcGIS ImageServer service.
|
|
* @param {String} [options.token] The authorization token to use to connect to the service.
|
|
* @param {Ellipsoid} [options.ellipsoid] The ellipsoid. If the tilingScheme is specified,
|
|
* this parameter is ignored and the tiling scheme's ellipsoid is used instead.
|
|
* If neither parameter is specified, the WGS84 ellipsoid is used.
|
|
*
|
|
* @example
|
|
* var terrainProvider = new Cesium.ArcGISTiledElevationTerrainProvider({
|
|
* url : 'https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer',
|
|
* token : 'KED1aF_I4UzXOHy3BnhwyBHU4l5oY6rO6walkmHoYqGp4XyIWUd5YZUC1ZrLAzvV40pR6gBXQayh0eFA8m6vPg..'
|
|
* });
|
|
* viewer.terrainProvider = terrainProvider;
|
|
*
|
|
* @see TerrainProvider
|
|
*/
|
|
function ArcGISTiledElevationTerrainProvider(options) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
if (!defined(options) || !defined(options.url)) {
|
|
throw new DeveloperError("options.url is required.");
|
|
}
|
|
//>>includeEnd('debug');
|
|
|
|
this._resource = undefined;
|
|
this._credit = undefined;
|
|
this._tilingScheme = undefined;
|
|
this._levelZeroMaximumGeometricError = undefined;
|
|
this._maxLevel = undefined;
|
|
this._terrainDataStructure = undefined;
|
|
this._ready = false;
|
|
this._width = undefined;
|
|
this._height = undefined;
|
|
this._encoding = undefined;
|
|
var token = options.token;
|
|
|
|
this._hasAvailability = false;
|
|
this._tilesAvailable = undefined;
|
|
this._tilesAvailablityLoaded = undefined;
|
|
this._availableCache = {};
|
|
|
|
var that = this;
|
|
var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84);
|
|
this._readyPromise = when(options.url)
|
|
.then(function (url) {
|
|
var resource = Resource.createIfNeeded(url);
|
|
resource.appendForwardSlash();
|
|
if (defined(token)) {
|
|
resource = resource.getDerivedResource({
|
|
queryParameters: {
|
|
token: token,
|
|
},
|
|
});
|
|
}
|
|
that._resource = resource;
|
|
|
|
var metadataResource = resource.getDerivedResource({
|
|
queryParameters: {
|
|
f: "pjson",
|
|
},
|
|
});
|
|
|
|
return metadataResource.fetchJson();
|
|
})
|
|
.then(function (metadata) {
|
|
var copyrightText = metadata.copyrightText;
|
|
if (defined(copyrightText)) {
|
|
that._credit = new Credit(copyrightText);
|
|
}
|
|
|
|
var spatialReference = metadata.spatialReference;
|
|
var wkid = defaultValue(
|
|
spatialReference.latestWkid,
|
|
spatialReference.wkid
|
|
);
|
|
var extent = metadata.extent;
|
|
var tilingSchemeOptions = {
|
|
ellipsoid: ellipsoid,
|
|
};
|
|
if (wkid === 4326) {
|
|
tilingSchemeOptions.rectangle = Rectangle.fromDegrees(
|
|
extent.xmin,
|
|
extent.ymin,
|
|
extent.xmax,
|
|
extent.ymax
|
|
);
|
|
that._tilingScheme = new GeographicTilingScheme(tilingSchemeOptions);
|
|
} else if (wkid === 3857) {
|
|
tilingSchemeOptions.rectangleSouthwestInMeters = new Cartesian2(
|
|
extent.xmin,
|
|
extent.ymin
|
|
);
|
|
tilingSchemeOptions.rectangleNortheastInMeters = new Cartesian2(
|
|
extent.xmax,
|
|
extent.ymax
|
|
);
|
|
that._tilingScheme = new WebMercatorTilingScheme(tilingSchemeOptions);
|
|
} else {
|
|
return when.reject(new RuntimeError("Invalid spatial reference"));
|
|
}
|
|
|
|
var tileInfo = metadata.tileInfo;
|
|
if (!defined(tileInfo)) {
|
|
return when.reject(new RuntimeError("tileInfo is required"));
|
|
}
|
|
|
|
that._width = tileInfo.rows + 1;
|
|
that._height = tileInfo.cols + 1;
|
|
that._encoding =
|
|
tileInfo.format === "LERC"
|
|
? HeightmapEncoding.LERC
|
|
: HeightmapEncoding.NONE;
|
|
that._lodCount = tileInfo.lods.length - 1;
|
|
|
|
var hasAvailability = (that._hasAvailability =
|
|
metadata.capabilities.indexOf("Tilemap") !== -1);
|
|
if (hasAvailability) {
|
|
that._tilesAvailable = new TileAvailability(
|
|
that._tilingScheme,
|
|
that._lodCount
|
|
);
|
|
that._tilesAvailable.addAvailableTileRange(
|
|
0,
|
|
0,
|
|
0,
|
|
that._tilingScheme.getNumberOfXTilesAtLevel(0),
|
|
that._tilingScheme.getNumberOfYTilesAtLevel(0)
|
|
);
|
|
that._tilesAvailablityLoaded = new TileAvailability(
|
|
that._tilingScheme,
|
|
that._lodCount
|
|
);
|
|
}
|
|
|
|
that._levelZeroMaximumGeometricError = TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(
|
|
that._tilingScheme.ellipsoid,
|
|
that._width,
|
|
that._tilingScheme.getNumberOfXTilesAtLevel(0)
|
|
);
|
|
|
|
if (metadata.bandCount > 1) {
|
|
console.log(
|
|
"ArcGISTiledElevationTerrainProvider: Terrain data has more than 1 band. Using the first one."
|
|
);
|
|
}
|
|
|
|
that._terrainDataStructure = {
|
|
elementMultiplier: 1.0,
|
|
lowestEncodedHeight: metadata.minValues[0],
|
|
highestEncodedHeight: metadata.maxValues[0],
|
|
};
|
|
|
|
that._ready = true;
|
|
|
|
return true;
|
|
})
|
|
.otherwise(function (error) {
|
|
var message =
|
|
"An error occurred while accessing " + that._resource.url + ".";
|
|
TileProviderError.handleError(undefined, that, that._errorEvent, message);
|
|
return when.reject(error);
|
|
});
|
|
|
|
this._errorEvent = new Event();
|
|
}
|
|
|
|
Object.defineProperties(ArcGISTiledElevationTerrainProvider.prototype, {
|
|
/**
|
|
* Gets an event that is raised when the terrain provider encounters an asynchronous error. By subscribing
|
|
* to the event, you will be notified of the error and can potentially recover from it. Event listeners
|
|
* are passed an instance of {@link TileProviderError}.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {Event}
|
|
*/
|
|
errorEvent: {
|
|
get: function () {
|
|
return this._errorEvent;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets the credit to display when this terrain provider is active. Typically this is used to credit
|
|
* the source of the terrain. This function should not be called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {Credit}
|
|
*/
|
|
credit: {
|
|
get: function () {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
if (!this.ready) {
|
|
throw new DeveloperError(
|
|
"credit must not be called before ready returns true."
|
|
);
|
|
}
|
|
//>>includeEnd('debug');
|
|
return this._credit;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets the tiling scheme used by this provider. This function should
|
|
* not be called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {GeographicTilingScheme}
|
|
*/
|
|
tilingScheme: {
|
|
get: function () {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
if (!this.ready) {
|
|
throw new DeveloperError(
|
|
"tilingScheme must not be called before ready returns true."
|
|
);
|
|
}
|
|
//>>includeEnd('debug');
|
|
return this._tilingScheme;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets a value indicating whether or not the provider is ready for use.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {Boolean}
|
|
*/
|
|
ready: {
|
|
get: function () {
|
|
return this._ready;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets a promise that resolves to true when the provider is ready for use.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {Promise.<Boolean>}
|
|
* @readonly
|
|
*/
|
|
readyPromise: {
|
|
get: function () {
|
|
return this._readyPromise;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets a value indicating whether or not the provider includes a water mask. The water mask
|
|
* indicates which areas of the globe are water rather than land, so they can be rendered
|
|
* as a reflective surface with animated waves. This function should not be
|
|
* called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {Boolean}
|
|
*/
|
|
hasWaterMask: {
|
|
get: function () {
|
|
return false;
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Gets a value indicating whether or not the requested tiles include vertex normals.
|
|
* This function should not be called before {@link ArcGISTiledElevationTerrainProvider#ready} returns true.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {Boolean}
|
|
*/
|
|
hasVertexNormals: {
|
|
get: function () {
|
|
return false;
|
|
},
|
|
},
|
|
/**
|
|
* Gets an object that can be used to determine availability of terrain from this provider, such as
|
|
* at points and in rectangles. This function should not be called before
|
|
* {@link TerrainProvider#ready} returns true. This property may be undefined if availability
|
|
* information is not available.
|
|
* @memberof ArcGISTiledElevationTerrainProvider.prototype
|
|
* @type {TileAvailability}
|
|
*/
|
|
availability: {
|
|
get: function () {
|
|
//>>includeStart('debug', pragmas.debug)
|
|
if (!this._ready) {
|
|
throw new DeveloperError(
|
|
"availability must not be called before the terrain provider is ready."
|
|
);
|
|
}
|
|
//>>includeEnd('debug');
|
|
return this._tilesAvailable;
|
|
},
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Requests the geometry for a given tile. This function should not be called before
|
|
* {@link ArcGISTiledElevationTerrainProvider#ready} returns true. The result includes terrain
|
|
* data and indicates that all child tiles are available.
|
|
*
|
|
* @param {Number} x The X coordinate of the tile for which to request geometry.
|
|
* @param {Number} y The Y coordinate of the tile for which to request geometry.
|
|
* @param {Number} level The level of the tile for which to request geometry.
|
|
* @param {Request} [request] The request object. Intended for internal use only.
|
|
* @returns {Promise.<TerrainData>|undefined} A promise for the requested geometry. If this method
|
|
* returns undefined instead of a promise, it is an indication that too many requests are already
|
|
* pending and the request will be retried later.
|
|
*/
|
|
ArcGISTiledElevationTerrainProvider.prototype.requestTileGeometry = function (
|
|
x,
|
|
y,
|
|
level,
|
|
request
|
|
) {
|
|
//>>includeStart('debug', pragmas.debug)
|
|
if (!this._ready) {
|
|
throw new DeveloperError(
|
|
"requestTileGeometry must not be called before the terrain provider is ready."
|
|
);
|
|
}
|
|
//>>includeEnd('debug');
|
|
|
|
var tileResource = this._resource.getDerivedResource({
|
|
url: "tile/" + level + "/" + y + "/" + x,
|
|
request: request,
|
|
});
|
|
|
|
var hasAvailability = this._hasAvailability;
|
|
var availabilityPromise = when.resolve(true);
|
|
var availabilityRequest;
|
|
if (
|
|
hasAvailability &&
|
|
!defined(isTileAvailable(this, level + 1, x * 2, y * 2))
|
|
) {
|
|
// We need to load child availability
|
|
var availabilityResult = requestAvailability(this, level + 1, x * 2, y * 2);
|
|
|
|
availabilityPromise = availabilityResult.promise;
|
|
availabilityRequest = availabilityResult.request;
|
|
}
|
|
|
|
var promise = tileResource.fetchArrayBuffer();
|
|
if (!defined(promise) || !defined(availabilityPromise)) {
|
|
return undefined;
|
|
}
|
|
|
|
var that = this;
|
|
var tilesAvailable = this._tilesAvailable;
|
|
return when
|
|
.join(promise, availabilityPromise)
|
|
.then(function (result) {
|
|
return new HeightmapTerrainData({
|
|
buffer: result[0],
|
|
width: that._width,
|
|
height: that._height,
|
|
childTileMask: hasAvailability
|
|
? tilesAvailable.computeChildMaskForTile(level, x, y)
|
|
: ALL_CHILDREN,
|
|
structure: that._terrainDataStructure,
|
|
encoding: that._encoding,
|
|
});
|
|
})
|
|
.otherwise(function (error) {
|
|
if (
|
|
defined(availabilityRequest) &&
|
|
availabilityRequest.state === RequestState.CANCELLED
|
|
) {
|
|
request.cancel();
|
|
|
|
// Don't reject the promise till the request is actually cancelled
|
|
// Otherwise it will think the request failed, but it didn't.
|
|
return request.deferred.promise.always(function () {
|
|
request.state = RequestState.CANCELLED;
|
|
return when.reject(error);
|
|
});
|
|
}
|
|
return when.reject(error);
|
|
});
|
|
};
|
|
|
|
function isTileAvailable(that, level, x, y) {
|
|
if (!that._hasAvailability) {
|
|
return undefined;
|
|
}
|
|
|
|
var tilesAvailablityLoaded = that._tilesAvailablityLoaded;
|
|
var tilesAvailable = that._tilesAvailable;
|
|
|
|
if (level > that._lodCount) {
|
|
return false;
|
|
}
|
|
|
|
// Check if tiles are known to be available
|
|
if (tilesAvailable.isTileAvailable(level, x, y)) {
|
|
return true;
|
|
}
|
|
|
|
// or to not be available
|
|
if (tilesAvailablityLoaded.isTileAvailable(level, x, y)) {
|
|
return false;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Gets the maximum geometric error allowed in a tile at a given level.
|
|
*
|
|
* @param {Number} level The tile level for which to get the maximum geometric error.
|
|
* @returns {Number} The maximum geometric error.
|
|
*/
|
|
ArcGISTiledElevationTerrainProvider.prototype.getLevelMaximumGeometricError = function (
|
|
level
|
|
) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
if (!this.ready) {
|
|
throw new DeveloperError(
|
|
"getLevelMaximumGeometricError must not be called before ready returns true."
|
|
);
|
|
}
|
|
//>>includeEnd('debug');
|
|
|
|
return this._levelZeroMaximumGeometricError / (1 << level);
|
|
};
|
|
|
|
/**
|
|
* Determines whether data for a tile is available to be loaded.
|
|
*
|
|
* @param {Number} x The X coordinate of the tile for which to request geometry.
|
|
* @param {Number} y The Y coordinate of the tile for which to request geometry.
|
|
* @param {Number} level The level of the tile for which to request geometry.
|
|
* @returns {Boolean} Undefined if not supported, otherwise true or false.
|
|
*/
|
|
ArcGISTiledElevationTerrainProvider.prototype.getTileDataAvailable = function (
|
|
x,
|
|
y,
|
|
level
|
|
) {
|
|
if (!this._hasAvailability) {
|
|
return undefined;
|
|
}
|
|
|
|
var result = isTileAvailable(this, level, x, y);
|
|
if (defined(result)) {
|
|
return result;
|
|
}
|
|
|
|
requestAvailability(this, level, x, y);
|
|
|
|
return undefined;
|
|
};
|
|
|
|
/**
|
|
* Makes sure we load availability data for a tile
|
|
*
|
|
* @param {Number} x The X coordinate of the tile for which to request geometry.
|
|
* @param {Number} y The Y coordinate of the tile for which to request geometry.
|
|
* @param {Number} level The level of the tile for which to request geometry.
|
|
* @returns {undefined|Promise<void>} Undefined if nothing need to be loaded or a Promise that resolves when all required tiles are loaded
|
|
*/
|
|
ArcGISTiledElevationTerrainProvider.prototype.loadTileDataAvailability = function (
|
|
x,
|
|
y,
|
|
level
|
|
) {
|
|
return undefined;
|
|
};
|
|
|
|
function findRange(origin, width, height, data) {
|
|
var endCol = width - 1;
|
|
var endRow = height - 1;
|
|
|
|
var value = data[origin.y * width + origin.x];
|
|
var endingIndices = [];
|
|
var range = {
|
|
startX: origin.x,
|
|
startY: origin.y,
|
|
endX: 0,
|
|
endY: 0,
|
|
};
|
|
|
|
var corner = new Cartesian2(origin.x + 1, origin.y + 1);
|
|
var doneX = false;
|
|
var doneY = false;
|
|
while (!(doneX && doneY)) {
|
|
// We want to use the original value when checking Y,
|
|
// so get it before it possibly gets incremented
|
|
var endX = corner.x;
|
|
|
|
// If we no longer move in the Y direction we need to check the corner tile in X pass
|
|
var endY = doneY ? corner.y + 1 : corner.y;
|
|
|
|
// Check X range
|
|
if (!doneX) {
|
|
for (var y = origin.y; y < endY; ++y) {
|
|
if (data[y * width + corner.x] !== value) {
|
|
doneX = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (doneX) {
|
|
endingIndices.push(new Cartesian2(corner.x, origin.y));
|
|
|
|
// Use the last good column so we can continue with Y
|
|
--corner.x;
|
|
--endX;
|
|
range.endX = corner.x;
|
|
} else if (corner.x === endCol) {
|
|
range.endX = corner.x;
|
|
doneX = true;
|
|
} else {
|
|
++corner.x;
|
|
}
|
|
}
|
|
|
|
// Check Y range - The corner tile is checked here
|
|
if (!doneY) {
|
|
var col = corner.y * width;
|
|
for (var x = origin.x; x <= endX; ++x) {
|
|
if (data[col + x] !== value) {
|
|
doneY = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (doneY) {
|
|
endingIndices.push(new Cartesian2(origin.x, corner.y));
|
|
|
|
// Use the last good row so we can continue with X
|
|
--corner.y;
|
|
range.endY = corner.y;
|
|
} else if (corner.y === endRow) {
|
|
range.endY = corner.y;
|
|
doneY = true;
|
|
} else {
|
|
++corner.y;
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
endingIndices: endingIndices,
|
|
range: range,
|
|
value: value,
|
|
};
|
|
}
|
|
|
|
function computeAvailability(x, y, width, height, data) {
|
|
var ranges = [];
|
|
|
|
var singleValue = data.every(function (val) {
|
|
return val === data[0];
|
|
});
|
|
if (singleValue) {
|
|
if (data[0] === 1) {
|
|
ranges.push({
|
|
startX: x,
|
|
startY: y,
|
|
endX: x + width - 1,
|
|
endY: y + height - 1,
|
|
});
|
|
}
|
|
|
|
return ranges;
|
|
}
|
|
|
|
var positions = [new Cartesian2(0, 0)];
|
|
while (positions.length > 0) {
|
|
var origin = positions.pop();
|
|
var result = findRange(origin, width, height, data);
|
|
|
|
if (result.value === 1) {
|
|
// Convert range into the array into global tile coordinates
|
|
var range = result.range;
|
|
range.startX += x;
|
|
range.endX += x;
|
|
range.startY += y;
|
|
range.endY += y;
|
|
ranges.push(range);
|
|
}
|
|
|
|
var endingIndices = result.endingIndices;
|
|
if (endingIndices.length > 0) {
|
|
positions = positions.concat(endingIndices);
|
|
}
|
|
}
|
|
|
|
return ranges;
|
|
}
|
|
|
|
function requestAvailability(that, level, x, y) {
|
|
if (!that._hasAvailability) {
|
|
return {};
|
|
}
|
|
|
|
// Fetch 128x128 availability list, so we make the minimum amount of requests
|
|
var xOffset = Math.floor(x / 128) * 128;
|
|
var yOffset = Math.floor(y / 128) * 128;
|
|
|
|
var dim = Math.min(1 << level, 128);
|
|
var url =
|
|
"tilemap/" + level + "/" + yOffset + "/" + xOffset + "/" + dim + "/" + dim;
|
|
|
|
var availableCache = that._availableCache;
|
|
if (defined(availableCache[url])) {
|
|
return availableCache[url];
|
|
}
|
|
|
|
var request = new Request({
|
|
throttle: false,
|
|
throttleByServer: true,
|
|
type: RequestType.TERRAIN,
|
|
});
|
|
|
|
var tilemapResource = that._resource.getDerivedResource({
|
|
url: url,
|
|
request: request,
|
|
});
|
|
|
|
var promise = tilemapResource.fetchJson();
|
|
if (!defined(promise)) {
|
|
return {};
|
|
}
|
|
|
|
promise = promise.then(function (result) {
|
|
var available = computeAvailability(
|
|
xOffset,
|
|
yOffset,
|
|
dim,
|
|
dim,
|
|
result.data
|
|
);
|
|
|
|
// Mark whole area as having availability loaded
|
|
that._tilesAvailablityLoaded.addAvailableTileRange(
|
|
level,
|
|
xOffset,
|
|
yOffset,
|
|
xOffset + dim,
|
|
yOffset + dim
|
|
);
|
|
|
|
var tilesAvailable = that._tilesAvailable;
|
|
for (var i = 0; i < available.length; ++i) {
|
|
var range = available[i];
|
|
tilesAvailable.addAvailableTileRange(
|
|
level,
|
|
range.startX,
|
|
range.startY,
|
|
range.endX,
|
|
range.endY
|
|
);
|
|
}
|
|
|
|
// Conveniently return availability of original tile
|
|
return isTileAvailable(that, level, x, y);
|
|
});
|
|
|
|
availableCache[url] = {
|
|
promise: promise,
|
|
request: request,
|
|
};
|
|
|
|
promise = promise.always(function (result) {
|
|
delete availableCache[url];
|
|
|
|
return result;
|
|
});
|
|
|
|
return {
|
|
promise: promise,
|
|
request: request,
|
|
};
|
|
}
|
|
export default ArcGISTiledElevationTerrainProvider;
|