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.
269 lines
8.1 KiB
JavaScript
269 lines
8.1 KiB
JavaScript
import Uri from "../ThirdParty/Uri.js";
|
|
import when from "../ThirdParty/when.js";
|
|
import Check from "./Check.js";
|
|
import Credit from "./Credit.js";
|
|
import defaultValue from "./defaultValue.js";
|
|
import defined from "./defined.js";
|
|
import Ion from "./Ion.js";
|
|
import Resource from "./Resource.js";
|
|
import RuntimeError from "./RuntimeError.js";
|
|
|
|
/**
|
|
* A {@link Resource} instance that encapsulates Cesium ion asset access.
|
|
* This object is normally not instantiated directly, use {@link IonResource.fromAssetId}.
|
|
*
|
|
* @alias IonResource
|
|
* @constructor
|
|
* @augments Resource
|
|
*
|
|
* @param {Object} endpoint The result of the Cesium ion asset endpoint service.
|
|
* @param {Resource} endpointResource The resource used to retreive the endpoint.
|
|
*
|
|
* @see Ion
|
|
* @see IonImageryProvider
|
|
* @see createWorldTerrain
|
|
* @see https://cesium.com
|
|
*/
|
|
function IonResource(endpoint, endpointResource) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.defined("endpoint", endpoint);
|
|
Check.defined("endpointResource", endpointResource);
|
|
//>>includeEnd('debug');
|
|
|
|
var options;
|
|
var externalType = endpoint.externalType;
|
|
var isExternal = defined(externalType);
|
|
|
|
if (!isExternal) {
|
|
options = {
|
|
url: endpoint.url,
|
|
retryAttempts: 1,
|
|
retryCallback: retryCallback,
|
|
};
|
|
} else if (
|
|
externalType === "3DTILES" ||
|
|
externalType === "STK_TERRAIN_SERVER"
|
|
) {
|
|
// 3D Tiles and STK Terrain Server external assets can still be represented as an IonResource
|
|
options = { url: endpoint.options.url };
|
|
} else {
|
|
//External imagery assets have additional configuration that can't be represented as a Resource
|
|
throw new RuntimeError(
|
|
"Ion.createResource does not support external imagery assets; use IonImageryProvider instead."
|
|
);
|
|
}
|
|
|
|
Resource.call(this, options);
|
|
|
|
// The asset endpoint data returned from ion.
|
|
this._ionEndpoint = endpoint;
|
|
this._ionEndpointDomain = isExternal
|
|
? undefined
|
|
: new Uri(endpoint.url).authority;
|
|
|
|
// The endpoint resource to fetch when a new token is needed
|
|
this._ionEndpointResource = endpointResource;
|
|
|
|
// The primary IonResource from which an instance is derived
|
|
this._ionRoot = undefined;
|
|
|
|
// Shared promise for endpooint requests amd credits (only ever set on the root request)
|
|
this._pendingPromise = undefined;
|
|
this._credits = undefined;
|
|
this._isExternal = isExternal;
|
|
}
|
|
|
|
if (defined(Object.create)) {
|
|
IonResource.prototype = Object.create(Resource.prototype);
|
|
IonResource.prototype.constructor = IonResource;
|
|
}
|
|
|
|
/**
|
|
* Asynchronously creates an instance.
|
|
*
|
|
* @param {Number} assetId The Cesium ion asset id.
|
|
* @param {Object} [options] An object with the following properties:
|
|
* @param {String} [options.accessToken=Ion.defaultAccessToken] The access token to use.
|
|
* @param {String|Resource} [options.server=Ion.defaultServer] The resource to the Cesium ion API server.
|
|
* @returns {Promise.<IonResource>} A Promise to am instance representing the Cesium ion Asset.
|
|
*
|
|
* @example
|
|
* //Load a Cesium3DTileset with asset ID of 124624234
|
|
* viewer.scene.primitives.add(new Cesium.Cesium3DTileset({ url: Cesium.IonResource.fromAssetId(124624234) }));
|
|
*
|
|
* @example
|
|
* //Load a CZML file with asset ID of 10890
|
|
* Cesium.IonResource.fromAssetId(10890)
|
|
* .then(function (resource) {
|
|
* viewer.dataSources.add(Cesium.CzmlDataSource.load(resource));
|
|
* });
|
|
*/
|
|
IonResource.fromAssetId = function (assetId, options) {
|
|
var endpointResource = IonResource._createEndpointResource(assetId, options);
|
|
|
|
return endpointResource.fetchJson().then(function (endpoint) {
|
|
return new IonResource(endpoint, endpointResource);
|
|
});
|
|
};
|
|
|
|
Object.defineProperties(IonResource.prototype, {
|
|
/**
|
|
* Gets the credits required for attribution of the asset.
|
|
*
|
|
* @memberof IonResource.prototype
|
|
* @type {Credit[]}
|
|
* @readonly
|
|
*/
|
|
credits: {
|
|
get: function () {
|
|
// Only we're not the root, return its credits;
|
|
if (defined(this._ionRoot)) {
|
|
return this._ionRoot.credits;
|
|
}
|
|
|
|
// We are the root
|
|
if (defined(this._credits)) {
|
|
return this._credits;
|
|
}
|
|
|
|
this._credits = IonResource.getCreditsFromEndpoint(
|
|
this._ionEndpoint,
|
|
this._ionEndpointResource
|
|
);
|
|
|
|
return this._credits;
|
|
},
|
|
},
|
|
});
|
|
|
|
/** @private */
|
|
IonResource.getCreditsFromEndpoint = function (endpoint, endpointResource) {
|
|
var credits = endpoint.attributions.map(Credit.getIonCredit);
|
|
var defaultTokenCredit = Ion.getDefaultTokenCredit(
|
|
endpointResource.queryParameters.access_token
|
|
);
|
|
if (defined(defaultTokenCredit)) {
|
|
credits.push(Credit.clone(defaultTokenCredit));
|
|
}
|
|
return credits;
|
|
};
|
|
|
|
/** @inheritdoc */
|
|
IonResource.prototype.clone = function (result) {
|
|
// We always want to use the root's information because it's the most up-to-date
|
|
var ionRoot = defaultValue(this._ionRoot, this);
|
|
|
|
if (!defined(result)) {
|
|
result = new IonResource(
|
|
ionRoot._ionEndpoint,
|
|
ionRoot._ionEndpointResource
|
|
);
|
|
}
|
|
|
|
result = Resource.prototype.clone.call(this, result);
|
|
result._ionRoot = ionRoot;
|
|
result._isExternal = this._isExternal;
|
|
|
|
return result;
|
|
};
|
|
|
|
IonResource.prototype.fetchImage = function (options) {
|
|
if (!this._isExternal) {
|
|
var userOptions = options;
|
|
options = {
|
|
preferBlob: true,
|
|
};
|
|
if (defined(userOptions)) {
|
|
options.flipY = userOptions.flipY;
|
|
options.preferImageBitmap = userOptions.preferImageBitmap;
|
|
}
|
|
}
|
|
|
|
return Resource.prototype.fetchImage.call(this, options);
|
|
};
|
|
|
|
IonResource.prototype._makeRequest = function (options) {
|
|
// Don't send ion access token to non-ion servers.
|
|
if (
|
|
this._isExternal ||
|
|
new Uri(this.url).authority !== this._ionEndpointDomain
|
|
) {
|
|
return Resource.prototype._makeRequest.call(this, options);
|
|
}
|
|
|
|
if (!defined(options.headers)) {
|
|
options.headers = {};
|
|
}
|
|
options.headers.Authorization = "Bearer " + this._ionEndpoint.accessToken;
|
|
|
|
return Resource.prototype._makeRequest.call(this, options);
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
IonResource._createEndpointResource = function (assetId, options) {
|
|
//>>includeStart('debug', pragmas.debug);
|
|
Check.defined("assetId", assetId);
|
|
//>>includeEnd('debug');
|
|
|
|
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
|
|
var server = defaultValue(options.server, Ion.defaultServer);
|
|
var accessToken = defaultValue(options.accessToken, Ion.defaultAccessToken);
|
|
server = Resource.createIfNeeded(server);
|
|
|
|
var resourceOptions = {
|
|
url: "v1/assets/" + assetId + "/endpoint",
|
|
};
|
|
|
|
if (defined(accessToken)) {
|
|
resourceOptions.queryParameters = { access_token: accessToken };
|
|
}
|
|
|
|
return server.getDerivedResource(resourceOptions);
|
|
};
|
|
|
|
function retryCallback(that, error) {
|
|
var ionRoot = defaultValue(that._ionRoot, that);
|
|
var endpointResource = ionRoot._ionEndpointResource;
|
|
|
|
// Image is not available in worker threads, so this avoids
|
|
// a ReferenceError
|
|
var imageDefined = typeof Image !== "undefined";
|
|
|
|
// We only want to retry in the case of invalid credentials (401) or image
|
|
// requests(since Image failures can not provide a status code)
|
|
if (
|
|
!defined(error) ||
|
|
(error.statusCode !== 401 &&
|
|
!(imageDefined && error.target instanceof Image))
|
|
) {
|
|
return when.resolve(false);
|
|
}
|
|
|
|
// We use a shared pending promise for all derived assets, since they share
|
|
// a common access_token. If we're already requesting a new token for this
|
|
// asset, we wait on the same promise.
|
|
if (!defined(ionRoot._pendingPromise)) {
|
|
ionRoot._pendingPromise = endpointResource
|
|
.fetchJson()
|
|
.then(function (newEndpoint) {
|
|
//Set the token for root resource so new derived resources automatically pick it up
|
|
ionRoot._ionEndpoint = newEndpoint;
|
|
return newEndpoint;
|
|
})
|
|
.always(function (newEndpoint) {
|
|
// Pass or fail, we're done with this promise, the next failure should use a new one.
|
|
ionRoot._pendingPromise = undefined;
|
|
return newEndpoint;
|
|
});
|
|
}
|
|
|
|
return ionRoot._pendingPromise.then(function (newEndpoint) {
|
|
// Set the new token and endpoint for this resource
|
|
that._ionEndpoint = newEndpoint;
|
|
return true;
|
|
});
|
|
}
|
|
export default IonResource;
|