Skip to content
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
62 changes: 35 additions & 27 deletions packages/engine/Source/Core/IonResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ import Ion from "./Ion.js";
import Resource from "./Resource.js";
import RuntimeError from "./RuntimeError.js";

/**
* A function that will be invoked when the access token is refreshed.
* @callback IonResourceRefreshCallback
* @param {IonResource} ionResource The root IonResource being refreshed.
* @param {object} endpoint The result of the Cesium ion asset endpoint service. This may be modified in place by the callback.
* @private
*/

/**
* A {@link Resource} instance that encapsulates Cesium ion asset access.
* This object is normally not instantiated directly, use {@link IonResource.fromAssetId}.
Expand All @@ -16,7 +24,7 @@ import RuntimeError from "./RuntimeError.js";
* @augments Resource
*
* @param {object} endpoint The result of the Cesium ion asset endpoint service.
* @param {Resource} endpointResource The resource used to retrieve the endpoint.
* @param {Resource} endpointResource The original resource used to retrieve the endpoint.
*
* @see Ion
* @see IonImageryProvider
Expand All @@ -39,12 +47,6 @@ function IonResource(endpoint, endpointResource) {
retryAttempts: 1,
retryCallback: retryCallback,
};
} else if (["GOOGLE_2D_MAPS", "AZURE_MAPS"].includes(externalType)) {
options = {
url: endpoint.options.url,
retryAttempts: 1,
retryCallback: retryCallback,
};
} else if (
externalType === "3DTILES" ||
externalType === "STK_TERRAIN_SERVER"
Expand Down Expand Up @@ -76,6 +78,13 @@ function IonResource(endpoint, endpointResource) {
this._pendingPromise = undefined;
this._credits = undefined;
this._isExternal = isExternal;

/**
* A function that, if defined, will be invoked when the access token is refreshed.
* @private
* @type {IonResourceRefreshCallback|undefined}
*/
this.refreshCallback = undefined;
}

if (defined(Object.create)) {
Expand Down Expand Up @@ -205,7 +214,7 @@ IonResource.prototype._makeRequest = function (options) {
return Resource.prototype._makeRequest.call(this, options);
}

addClientHeaders(options);
options.headers = addClientHeaders(options.headers);
options.headers.Authorization = `Bearer ${this._ionEndpoint.accessToken}`;

return Resource.prototype._makeRequest.call(this, options);
Expand Down Expand Up @@ -239,20 +248,26 @@ IonResource._createEndpointResource = function (assetId, options) {
};
}

addClientHeaders(resourceOptions);
resourceOptions.headers = addClientHeaders(resourceOptions.headers);

return server.getDerivedResource(resourceOptions);
};

function addClientHeaders(options) {
if (!defined(options.headers)) {
options.headers = {};
}
options.headers["X-Cesium-Client"] = "CesiumJS";
/**
* Adds CesiumJS client headers to the provided headers object.
* @private
* @param {object} [headers={}] The headers to modify.
* @returns {object} The modified headers.
*/
function addClientHeaders(headers = {}) {
headers["X-Cesium-Client"] = "CesiumJS";

/* global CESIUM_VERSION */
if (typeof CESIUM_VERSION !== "undefined") {
options.headers["X-Cesium-Client-Version"] = CESIUM_VERSION;
headers["X-Cesium-Client-Version"] = CESIUM_VERSION;
}

return headers;
}

function retryCallback(that, error) {
Expand Down Expand Up @@ -280,20 +295,13 @@ function retryCallback(that, error) {
ionRoot._pendingPromise = endpointResource
.fetchJson()
.then(function (newEndpoint) {
const refreshCallback = that.refreshCallback ?? ionRoot.refreshCallback;
if (defined(refreshCallback)) {
refreshCallback(ionRoot, newEndpoint);
}

// Set the token for root resource so new derived resources automatically pick it up
ionRoot._ionEndpoint = newEndpoint;
// Reset the session token for Google 2D imagery
if (newEndpoint.externalType === "GOOGLE_2D_MAPS") {
ionRoot.setQueryParameters({
session: newEndpoint.options.session,
key: newEndpoint.options.key,
});
}
if (newEndpoint.externalType === "AZURE_MAPS") {
ionRoot.setQueryParameters({
"subscription-key": newEndpoint.options["subscription-key"],
});
}
return ionRoot._ionEndpoint;
})
.finally(function (newEndpoint) {
Expand Down
20 changes: 9 additions & 11 deletions packages/engine/Source/Core/Resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ Resource.prototype.fetchImage = function (options) {
this.isBlobUri ||
(!this.hasHeaders && !preferBlob)
) {
return fetchImage({
return this._fetchImage({
resource: this,
flipY: flipY,
skipColorSpaceConversion: skipColorSpaceConversion,
Expand Down Expand Up @@ -957,8 +957,7 @@ Resource.prototype.fetchImage = function (options) {
url: blobUrl,
});

return fetchImage({
resource: generatedBlobResource,
return generatedBlobResource._fetchImage({
flipY: flipY,
skipColorSpaceConversion: skipColorSpaceConversion,
preferImageBitmap: false,
Expand Down Expand Up @@ -997,16 +996,15 @@ Resource.prototype.fetchImage = function (options) {

/**
* Fetches an image and returns a promise to it.
*
* @param {object} [options] An object with the following properties.
* @param {Resource} [options.resource] Resource object that points to an image to fetch.
* @param {boolean} [options.preferImageBitmap] If true, image will be decoded during fetch and an <code>ImageBitmap</code> is returned.
* @param {boolean} [options.flipY] If true, image will be vertically flipped during decode. Only applies if the browser supports <code>createImageBitmap</code>.
* @param {boolean} [options.skipColorSpaceConversion=false] If true, any custom gamma or color profiles in the image will be ignored. Only applies if the browser supports <code>createImageBitmap</code>.
* @returns {Promise<ImageBitmap|HTMLImageElement>|undefined} a promise that will resolve to the requested data when loaded. Returns undefined if the request has been throttled and cannot be made at this time.
* @private
*/
function fetchImage(options) {
const resource = options.resource;
Resource.prototype._fetchImage = function (options) {
const resource = this;
const flipY = options.flipY;
const skipColorSpaceConversion = options.skipColorSpaceConversion;
const preferImageBitmap = options.preferImageBitmap;
Expand Down Expand Up @@ -1050,8 +1048,7 @@ function fetchImage(options) {
request.state = RequestState.UNISSUED;
request.deferred = undefined;

return fetchImage({
resource: resource,
return resource._fetchImage({
flipY: flipY,
skipColorSpaceConversion: skipColorSpaceConversion,
preferImageBitmap: preferImageBitmap,
Expand All @@ -1060,7 +1057,7 @@ function fetchImage(options) {
return Promise.reject(e);
});
});
}
};

/**
* Creates a Resource and calls fetchImage() on it.
Expand Down Expand Up @@ -1962,6 +1959,7 @@ Resource._Implementations.createImage = function (
flipY,
skipColorSpaceConversion,
preferImageBitmap,
headers,
) {
const url = request.url;
// Passing an Image to createImageBitmap will force it to run on the main thread
Expand All @@ -1985,7 +1983,7 @@ Resource._Implementations.createImage = function (
responseType,
method,
undefined,
undefined,
headers,
xhrDeferred,
undefined,
undefined,
Expand Down
12 changes: 7 additions & 5 deletions packages/engine/Source/Scene/Azure2DImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const trailingSlashRegex = /\/$/;
*
* @property {object} options Object with the following properties:
* @property {string} [options.url="https://atlas.microsoft.com/"] The Azure server url.
* @property {string} [options.tilesetId="microsoft.imagery"] The Azure tileset ID. Valid options are {@link microsoft.imagery}, {@link microsoft.base.road}, and {@link microsoft.base.labels.road}
* @property {string} options.tilesetId="microsoft.imagery" The Azure tileset ID. Valid options are {@link microsoft.imagery}, {@link microsoft.base.road}, and {@link microsoft.base.labels.road}
* @property {string} options.subscriptionKey The public subscription key for the imagery.
* @property {Ellipsoid} [options.ellipsoid=Ellipsoid.default] The ellipsoid. If not specified, the default ellipsoid is used.
* @property {number} [options.minimumLevel=0] The minimum level-of-detail supported by the imagery provider. Take care when specifying
Expand All @@ -41,13 +41,13 @@ const trailingSlashRegex = /\/$/;
*/
function Azure2DImageryProvider(options) {
options = options ?? {};
options.maximumLevel = options.maximumLevel ?? 22;
options.minimumLevel = options.minimumLevel ?? 0;
const maximumLevel = options.maximumLevel ?? 22;
const minimumLevel = options.minimumLevel ?? 0;
const tilesetId = options.tilesetId ?? "microsoft.imagery";

const subscriptionKey =
options.subscriptionKey ?? options["subscription-key"];
//>>includeStart('debug', pragmas.debug);
Check.defined("options.tilesetId", options.tilesetId);
Check.defined("options.subscriptionKey", subscriptionKey);
//>>includeEnd('debug');

Expand All @@ -66,7 +66,7 @@ function Azure2DImageryProvider(options) {

resource.setQueryParameters({
"api-version": "2024-04-01",
tilesetId: options.tilesetId,
tilesetId: tilesetId,
zoom: `{z}`,
x: `{x}`,
y: `{y}`,
Expand All @@ -83,6 +83,8 @@ function Azure2DImageryProvider(options) {

const provider = new UrlTemplateImageryProvider({
...options,
maximumLevel,
minimumLevel,
url: resource,
credit: credit,
});
Expand Down
37 changes: 25 additions & 12 deletions packages/engine/Source/Scene/Google2DImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import DeveloperError from "../Core/DeveloperError.js";
import Resource from "../Core/Resource.js";
import IonResource from "../Core/IonResource.js";
import Check from "../Core/Check.js";
import UrlTemplateImageryProvider from "./UrlTemplateImageryProvider.js";
import GoogleMaps from "../Core/GoogleMaps.js";
import { GOOGLE_2D_MAPS as createFromIonEndpoint } from "./IonImageryProviderFactory.js";
import UrlTemplateImageryProvider from "./UrlTemplateImageryProvider.js";

const trailingSlashRegex = /\/$/;

Expand Down Expand Up @@ -124,8 +125,6 @@ function Google2DImageryProvider(options) {
// This will be defined for ion resources
this._tileCredits = resource.credits;
this._attributionsByLevel = undefined;
// Asynchronously request and populate _attributionsByLevel
this.getViewportCredits();
}

Object.defineProperties(Google2DImageryProvider.prototype, {
Expand Down Expand Up @@ -353,10 +352,6 @@ Google2DImageryProvider.fromIonAssetId = async function (options) {
},
);

const endpoint = await endpointResource.fetchJson();
const endpointOptions = { ...endpoint.options };
delete endpointOptions.url;

const providerOptions = {
language: options.language,
region: options.region,
Expand All @@ -367,11 +362,15 @@ Google2DImageryProvider.fromIonAssetId = async function (options) {
credit: options.credit,
};

return new Google2DImageryProvider({
...endpointOptions,
const endpoint = await endpointResource.fetchJson();
const url = endpoint.options.url;
delete endpoint.options.url;
endpoint.options = {
...endpoint.options,
...providerOptions,
url: new IonResource(endpoint, endpointResource),
});
};

return createFromIonEndpoint(url, endpoint, endpointResource);
};

/**
Expand Down Expand Up @@ -485,7 +484,21 @@ Google2DImageryProvider.prototype.requestImage = function (
level,
request,
) {
return this._imageryProvider.requestImage(x, y, level, request);
const promise = this._imageryProvider.requestImage(x, y, level, request);

// If the requestImage call returns undefined, it couldn't be scheduled this frame. Make sure to return undefined so this can be handled upstream.
if (!defined(promise)) {
return undefined;
}

// Asynchronously request and populate _attributionsByLevel if it hasn't been already. We do this here so that the promise can be properly awaited.
if (promise && !defined(this._attributionsByLevel)) {
return Promise.all([promise, this.getViewportCredits()]).then(
(results) => results[0],
);
}

return promise;
};

/**
Expand Down
Loading