Skip to content
Open
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
6 changes: 3 additions & 3 deletions Apps/Sandcastle/gallery/Imagery Layers.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
"use strict";
//Sandcastle_Begin
const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayer: Cesium.ImageryLayer.fromWorldImagery({
style: Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS,
}),
baseLayer: Cesium.ImageryLayer.fromProviderAsync(
Cesium.IonImageryProvider.fromAssetId(3830183),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the motivation for changing this away from Bing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was missed in the last release for Google imagery. This sandcastle corresponds to and is linked from the imagery tutorial https://cesium.com/learn/cesiumjs-learn/cesiumjs-imagery/ We updated the tutorial and sample code to use Google but forgot to update the sandcastle.

),
baseLayerPicker: false,
});
const layers = viewer.scene.imageryLayers;
Expand Down
108 changes: 96 additions & 12 deletions packages/engine/Source/Scene/Azure2DImageryProvider.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Check from "../Core/Check.js";
import Frozen from "../Core/Frozen.js";
import Credit from "../Core/Credit.js";
import defined from "../Core/defined.js";
import Resource from "../Core/Resource.js";
Expand Down Expand Up @@ -29,7 +30,6 @@ const trailingSlashRegex = /\/$/;
*
* @alias Azure2DImageryProvider
* @constructor
* @private
* @param {Azure2DImageryProvider.ConstructorOptions} options Object describing initialization options
*
* @example
Expand All @@ -41,16 +41,18 @@ const trailingSlashRegex = /\/$/;
*/
function Azure2DImageryProvider(options) {
options = options ?? {};
const maximumLevel = options.maximumLevel ?? 22;
const minimumLevel = options.minimumLevel ?? 0;
const tilesetId = options.tilesetId ?? "microsoft.imagery";
this._maximumLevel = options.maximumLevel ?? 22;
this._minimumLevel = options.minimumLevel ?? 0;

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

this._tilesetId = options.tilesetId;

const resource =
options.url instanceof IonResource
? options.url
Expand All @@ -60,19 +62,23 @@ function Azure2DImageryProvider(options) {
if (!trailingSlashRegex.test(templateUrl)) {
templateUrl += "/";
}
templateUrl += `map/tile`;

resource.url = templateUrl;
const tilesUrl = `${templateUrl}map/tile`;
this._viewportUrl = `${templateUrl}map/attribution`;

resource.url = tilesUrl;

resource.setQueryParameters({
"api-version": "2024-04-01",
tilesetId: tilesetId,
"subscription-key": this._subscriptionKey,
zoom: `{z}`,
x: `{x}`,
y: `{y}`,
"subscription-key": subscriptionKey,
});

this._resource = resource;

let credit;
if (defined(options.credit)) {
credit = options.credit;
Expand All @@ -83,8 +89,8 @@ function Azure2DImageryProvider(options) {

const provider = new UrlTemplateImageryProvider({
...options,
maximumLevel,
minimumLevel,
maximumLevel: this._maximumLevel,
minimumLevel: this._minimumLevel,
url: resource,
credit: credit,
});
Expand All @@ -93,6 +99,7 @@ function Azure2DImageryProvider(options) {

// This will be defined for ion resources
this._tileCredits = resource.credits;
this._attributionsByLevel = undefined;
}

Object.defineProperties(Azure2DImageryProvider.prototype, {
Expand Down Expand Up @@ -263,7 +270,18 @@ Object.defineProperties(Azure2DImageryProvider.prototype, {
* @returns {Credit[]|undefined} The credits to be displayed when the tile is displayed.
*/
Azure2DImageryProvider.prototype.getTileCredits = function (x, y, level) {
return this._imageryProvider.getTileCredits(x, y, level);
const hasAttributions = defined(this._attributionsByLevel);

if (!hasAttributions || !defined(this._tileCredits)) {
return undefined;
}

const innerCredits = this._attributionsByLevel.get(level);
if (!defined(this._tileCredits)) {
return innerCredits;
}

return this._tileCredits.concat(innerCredits);
};

/**
Expand All @@ -282,7 +300,21 @@ Azure2DImageryProvider.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 All @@ -306,5 +338,57 @@ Azure2DImageryProvider.prototype.pickFeatures = function (
return undefined;
};

/**
* Get attribution for imagery from Azure Maps to display in the credits
* @private
* @return {Promise<Map<Credit[]>>} The list of attribution sources to display in the credits.
*/
Azure2DImageryProvider.prototype.getViewportCredits = async function () {
const maximumLevel = this._maximumLevel;

const promises = [];
for (let level = 0; level < maximumLevel + 1; level++) {
promises.push(
fetchViewportAttribution(
this._resource,
this._viewportUrl,
this._subscriptionKey,
this._tilesetId,
level,
),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly required to merge this PR, but I wonder if we can more lazily request tile credits as needed based on the current zoom level, and potentially extents, alongside the image requests in requestImage.

This would also potentially apply to Google2DImageryProvider.

const results = await Promise.all(promises);

const attributionsByLevel = new Map();
for (let level = 0; level < maximumLevel + 1; level++) {
const credits = [];
const attributions = results[level].join(",");
if (attributions) {
const levelCredits = new Credit(attributions);
credits.push(levelCredits);
}
attributionsByLevel.set(level, credits);
}

this._attributionsByLevel = attributionsByLevel;

return attributionsByLevel;
};

async function fetchViewportAttribution(resource, url, key, tilesetId, level) {
const viewportResource = resource.getDerivedResource({
url,
queryParameters: {
zoom: level,
bounds: "-180,-90,180,90",
},
data: JSON.stringify(Frozen.EMPTY_OBJECT),
});

const viewportJson = await viewportResource.fetchJson();
return viewportJson.copyrights;
}

// Exposed for tests
export default Azure2DImageryProvider;
23 changes: 9 additions & 14 deletions packages/engine/Source/Scene/Google2DImageryProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ function Google2DImageryProvider(options) {
key: encodeURIComponent(options.key),
});

this._resource = resource.clone();

let credit;
if (defined(options.credit)) {
credit = options.credit;
Expand Down Expand Up @@ -312,7 +314,7 @@ Object.defineProperties(Google2DImageryProvider.prototype, {
* });
* @example
* // Google 2D roadmap overlay with custom styles
* const googleTileProvider = Cesium.Google2DImageryProvider.fromIonAssetId({
* const googleTilesProvider = Cesium.Google2DImageryProvider.fromIonAssetId({
* assetId: 3830184,
* overlayLayerType: "layerRoadmap",
* styles: [
Expand Down Expand Up @@ -403,7 +405,7 @@ Google2DImageryProvider.fromIonAssetId = async function (options) {
* // Google 2D roadmap overlay with custom styles
* Cesium.GoogleMaps.defaultApiKey = "your-api-key";
*
* const googleTileProvider = Cesium.Google2DImageryProvider.fromUrl({
* const googleTilesProvider = Cesium.Google2DImageryProvider.fromUrl({
* overlayLayerType: "layerRoadmap",
* styles: [
* {
Expand Down Expand Up @@ -533,12 +535,7 @@ Google2DImageryProvider.prototype.getViewportCredits = async function () {
const promises = [];
for (let level = 0; level < maximumLevel + 1; level++) {
promises.push(
fetchViewportAttribution(
this._viewportUrl,
this._key,
this._session,
level,
),
fetchViewportAttribution(this._resource, this._viewportUrl, level),
);
}
const results = await Promise.all(promises);
Expand All @@ -559,12 +556,10 @@ Google2DImageryProvider.prototype.getViewportCredits = async function () {
return attributionsByLevel;
};

async function fetchViewportAttribution(url, key, session, level) {
const viewport = await Resource.fetch({
url: url,
async function fetchViewportAttribution(resource, url, level) {
const viewportResource = resource.getDerivedResource({
url,
queryParameters: {
key,
session,
zoom: level,
north: 90,
south: -90,
Expand All @@ -573,7 +568,7 @@ async function fetchViewportAttribution(url, key, session, level) {
},
data: JSON.stringify(Frozen.EMPTY_OBJECT),
});
const viewportJson = JSON.parse(viewport);
const viewportJson = await viewportResource.fetchJson();
return viewportJson.copyright;
}

Expand Down
7 changes: 6 additions & 1 deletion packages/engine/Specs/Scene/Azure2DImageryProviderSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@ describe("Scene/Azure2DImageryProvider", function () {
tilesetId: "a-tileset-id",
});

provider._attributionsByLevel = {};

expect(provider.url).toEqual(
"https://atlas.microsoft.com/map/tile?api-version=2024-04-01&tilesetId=a-tileset-id&zoom={z}&x={x}&y={y}&subscription-key=test-subscriptionKey",
"https://atlas.microsoft.com/map/tile?api-version=2024-04-01&tilesetId=a-tileset-id&subscription-key=test-subscriptionKey&zoom={z}&x={x}&y={y}",
);
expect(provider.tileWidth).toEqual(256);
expect(provider.tileHeight).toEqual(256);
Expand Down Expand Up @@ -74,6 +76,8 @@ describe("Scene/Azure2DImageryProvider", function () {
rectangle: rectangle,
});

provider._attributionsByLevel = {};

expect(provider.tileWidth).toEqual(256);
expect(provider.tileHeight).toEqual(256);
expect(provider.maximumLevel).toBe(22);
Expand Down Expand Up @@ -133,6 +137,7 @@ describe("Scene/Azure2DImageryProvider", function () {
subscriptionKey: "test-subscriptionKey",
tilesetId: "a-tileset-id",
});
provider._attributionsByLevel = {};

const layer = new ImageryLayer(provider);

Expand Down
6 changes: 3 additions & 3 deletions packages/sandcastle/gallery/imagery-layers/main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as Cesium from "cesium";

const viewer = new Cesium.Viewer("cesiumContainer", {
baseLayer: Cesium.ImageryLayer.fromWorldImagery({
style: Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS,
}),
baseLayer: Cesium.ImageryLayer.fromProviderAsync(
Cesium.IonImageryProvider.fromAssetId(3830183),
),
baseLayerPicker: false,
});
const layers = viewer.scene.imageryLayers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,30 +347,42 @@ of the world.\nhttp://www.openstreetmap.org",

providerViewModels.push(
new ProviderViewModel({
name: "Google Maps Labels Only",
name: "Google Maps Contour",
iconUrl: buildModuleUrl(
"Widgets/Images/ImageryProviders/googleLabels.png",
"Widgets/Images/ImageryProviders/googleContour.png",
),
tooltip:
"Place labels from Google Maps to combine with other imagery such as Sentinel-2",
"Hillshade mapping, contour lines, natural features (roadmap features hidden) from Google Maps",
category: "Cesium ion",
creationFunction: function () {
return IonImageryProvider.fromAssetId(3830185);
return IonImageryProvider.fromAssetId(3830186);
},
}),
);

providerViewModels.push(
new ProviderViewModel({
name: "Google Maps Contour",
name: "Azure Maps Aerial",
iconUrl: buildModuleUrl(
"Widgets/Images/ImageryProviders/googleContour.png",
"Widgets/Images/ImageryProviders/azureAerial.png",
),
tooltip: "Imagery from Azure Maps",
category: "Cesium ion",
creationFunction: function () {
return IonImageryProvider.fromAssetId(3891168);
},
}),
);

providerViewModels.push(
new ProviderViewModel({
name: "Azure Maps Roads",
iconUrl: buildModuleUrl("Widgets/Images/ImageryProviders/azureRoads.png"),
tooltip:
"Hillshade mapping, contour lines, natural features (roadmap features hidden) from Google Maps",
"Labeled roads and other features on a base landscape from Azure Maps",
category: "Cesium ion",
creationFunction: function () {
return IonImageryProvider.fromAssetId(3830186);
return IonImageryProvider.fromAssetId(3891169);
},
}),
);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.