Compare commits

...

12 Commits

Author SHA1 Message Date
Luke McKinstry c9ca959c06
Merge 7b3b5c25a9 into ee2b3813b2 2025-11-24 11:55:07 -05:00
lukemckinstry 7b3b5c25a9 switch to azure production asset ids
deploy / deploy (push) Has been cancelled Details
2025-11-24 11:54:43 -05:00
lukemckinstry dce3549952 remove labels thumbnails 2025-11-24 11:54:43 -05:00
lukemckinstry 162458dd58 update specs 2025-11-24 11:54:43 -05:00
lukemckinstry 528acebaca remove labels only layers from base layer picker defaults 2025-11-24 11:54:43 -05:00
lukemckinstry fc1da0452e use derived resources for attribution calls 2025-11-24 11:54:43 -05:00
lukemckinstry b423ef6c62 add azure maps to default options in imagery layer picker 2025-11-24 11:54:43 -05:00
lukemckinstry e9ab31a23f move azure sandcastles out of development folder 2025-11-24 11:54:43 -05:00
lukemckinstry 9a0da95b1b make Azure class public 2025-11-24 11:54:43 -05:00
lukemckinstry 1c8b77b31f docs typo 2025-11-24 11:54:43 -05:00
lukemckinstry 2d1e175deb update imagery tutorial sandcastle 2025-11-24 11:54:43 -05:00
lukemckinstry b3a6c52f9c add attribution for azure 2025-11-24 11:54:42 -05:00
14 changed files with 152 additions and 64 deletions

View File

@ -24,10 +24,7 @@
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
Cesium.Ion.defaultServer = "https://api.ion-staging.cesium.com";
Cesium.Ion.defaultAccessToken = "";
const assetId = 1683;
const assetId = 3891169;
const azure = Cesium.ImageryLayer.fromProviderAsync(
Cesium.IonImageryProvider.fromAssetId(assetId),

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -45,10 +45,9 @@
{ label: "Google Maps 2D Roadmap", assetId: 3830184 },
{ label: "Google Maps 2D Satellite", assetId: 3830182 },
{ label: "Google Maps 2D Satellite with Labels", assetId: 3830183 },
{ label: "Bing Maps Aerial", assetId: 2 },
{ label: "Bing Maps Aerial with Labels", assetId: 3 },
{ label: "Bing Maps Road", assetId: 4 },
{ label: "Bing Maps Labels Only", assetId: 2411391 },
{ label: "Azure Maps Aerial", assetId: 3891168 },
{ label: "Azure Maps Roads", assetId: 3891169 },
{ label: "Azure Maps Labels Only", assetId: 3891170 },
{ label: "Sentinel-2", assetId: 3954 },
];

View File

@ -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),
),
baseLayerPicker: false,
});
const layers = viewer.scene.imageryLayers;

View File

@ -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";
@ -29,7 +30,6 @@ const trailingSlashRegex = /\/$/;
*
* @alias Azure2DImageryProvider
* @constructor
* @private
* @param {Azure2DImageryProvider.ConstructorOptions} options Object describing initialization options
*
* @example
@ -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
@ -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;
@ -83,8 +89,8 @@ function Azure2DImageryProvider(options) {
const provider = new UrlTemplateImageryProvider({
...options,
maximumLevel,
minimumLevel,
maximumLevel: this._maximumLevel,
minimumLevel: this._minimumLevel,
url: resource,
credit: credit,
});
@ -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, {
@ -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);
};
/**
@ -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;
};
/**
@ -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,
),
);
}
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;

View File

@ -101,6 +101,8 @@ function Google2DImageryProvider(options) {
key: encodeURIComponent(options.key),
});
this._resource = resource.clone();
let credit;
if (defined(options.credit)) {
credit = options.credit;
@ -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: [
@ -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: [
* {
@ -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);
@ -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,
@ -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;
}

View File

@ -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);
@ -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);
@ -133,6 +137,7 @@ describe("Scene/Azure2DImageryProvider", function () {
subscriptionKey: "test-subscriptionKey",
tilesetId: "a-tileset-id",
});
provider._attributionsByLevel = {};
const layer = new ImageryLayer(provider);

View File

@ -1,9 +1,6 @@
import * as Cesium from "cesium";
Cesium.Ion.defaultServer = "https://api.ion-staging.cesium.com";
Cesium.Ion.defaultAccessToken = "";
const assetId = 1683;
const assetId = 3891169;
const azure = Cesium.ImageryLayer.fromProviderAsync(
Cesium.IonImageryProvider.fromAssetId(assetId),

View File

@ -22,10 +22,9 @@ const dropdownOptions = [
{ label: "Google Maps 2D Roadmap", assetId: 3830184 },
{ label: "Google Maps 2D Satellite", assetId: 3830182 },
{ label: "Google Maps 2D Satellite with Labels", assetId: 3830183 },
{ label: "Bing Maps Aerial", assetId: 2 },
{ label: "Bing Maps Aerial with Labels", assetId: 3 },
{ label: "Bing Maps Road", assetId: 4 },
{ label: "Bing Maps Labels Only", assetId: 2411391 },
{ label: "Azure Maps Aerial", assetId: 3891168 },
{ label: "Azure Maps Roads", assetId: 3891169 },
{ label: "Azure Maps Labels Only", assetId: 3891170 },
{ label: "Sentinel-2", assetId: 3954 },
];

View File

@ -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;

View File

@ -345,21 +345,6 @@ of the world.\nhttp://www.openstreetmap.org",
}),
);
providerViewModels.push(
new ProviderViewModel({
name: "Google Maps Labels Only",
iconUrl: buildModuleUrl(
"Widgets/Images/ImageryProviders/googleLabels.png",
),
tooltip:
"Place labels from Google Maps to combine with other imagery such as Sentinel-2",
category: "Cesium ion",
creationFunction: function () {
return IonImageryProvider.fromAssetId(3830185);
},
}),
);
providerViewModels.push(
new ProviderViewModel({
name: "Google Maps Contour",
@ -375,6 +360,33 @@ of the world.\nhttp://www.openstreetmap.org",
}),
);
providerViewModels.push(
new ProviderViewModel({
name: "Azure Maps Aerial",
iconUrl: buildModuleUrl(
"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:
"Labeled roads and other features on a base landscape from Azure Maps",
category: "Cesium ion",
creationFunction: function () {
return IonImageryProvider.fromAssetId(3891169);
},
}),
);
return providerViewModels;
}
export default createDefaultImageryProviderViewModels;

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB