Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] add support for iTP curated-content/cesium endpoint #7436

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 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
14 changes: 5 additions & 9 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"source.fixAll.markdownlint": "explicit"
},
"editor.insertSpaces": true,
"editor.formatOnSave": true,
// "editor.formatOnSave": true,
"editor.tabSize": 2,
"eslint.enable": true,
"eslint.format.enable": true,
Expand Down Expand Up @@ -74,9 +74,7 @@
"importSorter.sortConfiguration.removeUnusedImports": true,
"json.schemas": [
{
"fileMatch": [
"certa.json"
],
"fileMatch": ["certa.json"],
"url": "./tools/certa/certa.schema.json"
}
],
Expand All @@ -87,9 +85,7 @@
"MD033": false,
"MD041": false
},
"markdownlint.ignore": [
"**/CHANGELOG.md"
],
"markdownlint.ignore": ["**/CHANGELOG.md"],
"search.exclude": {
"**/.nyc_output": true,
"**/.rush": true,
Expand All @@ -106,5 +102,5 @@
"NODE_ENV": "development"
},
"typescript.tsdk": "common/temp/node_modules/typescript/lib",
"vitest.maximumConfigs": 20,
}
"vitest.maximumConfigs": 20
}
2 changes: 2 additions & 0 deletions core/common/src/TerrainSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export type TerrainProviderName = string;
export enum CesiumTerrainAssetId {
aruniverse marked this conversation as resolved.
Show resolved Hide resolved
/** Default [global 3d terrain](https://cesium.com/platform/cesium-ion/content/cesium-world-terrain/). */
Default = "1",
/** A [3D Building](https://cesium.com/platform/cesium-ion/content/cesium-osm-buildings/) layer derived from OpenStreetMap covering the entire world. */
OSMBuildings = "96188",
/** Global 3d terrain that includes [bathymetry](https://cesium.com/platform/cesium-ion/content/cesium-world-bathymetry/) (seafloor) terrain. */
Bathymetry = "2426648",
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,7 @@ describe("ArcGISImageryProvider", () => {

const provider = new TestArcGISProvider(settings, true);

const fetchStub = vi.spyOn(globalThis, "fetch").mockImplementation(async function (_input: NodeJS.fetch.RequestInfo, _init?: RequestInit) {
return Promise.resolve({
status: 200,
headers: new Headers({ "content-type": "application/json" }),
json: async () => ({}),
} as Response);
});
const fetchStub = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response());

const testUrl = `${settings.url}?testParam=test`;
await provider.fetch(new URL(testUrl), { method: "GET" });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,7 @@ describe("ArcGISMapLayerImageryProvider", () => {
await provider.initialize();
const resolveChildren = (_childIds: QuadId[]) => {};

const fetchStub = vi.spyOn(globalThis, "fetch").mockImplementation(async function (_input: NodeJS.fetch.RequestInfo, _init?: RequestInit) {
return Promise.resolve({
status: 200,
headers: new Headers({ "content-type": "application/json" }),
json: async () => {},
} as unknown as Response);
});

const fetchStub = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response());
await (provider as any)._generateChildIds(QuadId.createFromContentId("1_0_0"), resolveChildren);
expect(fetchStub).toHaveBeenCalledOnce();
// eslint-disable-next-line @typescript-eslint/no-base-to-string
Expand Down
7 changes: 1 addition & 6 deletions core/frontend/src/test/tile/map/ArcGisUtilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,7 @@ describe("ArcGisUtilities", () => {

it("should validate by invoking getServiceJson with proper parameters ", async () => {
const source = getSampleSourceWithQueryParamsAndCreds();
const fetchStub = vi.spyOn(globalThis, "fetch").mockImplementation(async function (_input: NodeJS.fetch.RequestInfo, _init?: RequestInit) {
return Promise.resolve({
status: 200,
json: async () => {return {};},
} as unknown as Response);
});
const fetchStub = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response());
await ArcGisUtilities.getServiceJson({ url: source.url, formatId: source.formatId, userName: source.userName, password: source.password, queryParams: source.collectQueryParams() });

expect(fetchStub).toHaveBeenCalledOnce();
Expand Down
81 changes: 63 additions & 18 deletions core/frontend/src/tile/map/CesiumTerrainProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@ enum QuantizedMeshExtensionIds {
Metadata = 4,
}

type ContentType =
| "3DTiles"
| "GLTF"
| "IMAGERY"
| "TERRAIN"
| "KML"
| "CZML"
| "GEOJSON";

interface ContentTileAttribution {
html: string;
collapsible: boolean;
}

interface CesiumContentAccessTileProps {
type: ContentType;
url: string;
accessToken: string;
attributions: ContentTileAttribution[];
}

/** Return the URL for a Cesium ION asset from its asset ID and request Key.
* @public
*/
Expand All @@ -38,21 +59,20 @@ export function getCesiumOSMBuildingsUrl(): string | undefined {
if (undefined === key)
return undefined;

const osmBuildingAssetId = 96188;
return getCesiumAssetUrl(osmBuildingAssetId, key);
return getCesiumAssetUrl(+CesiumTerrainAssetId.OSMBuildings, key);
}

/** @internal */
export async function getCesiumAccessTokenAndEndpointUrl(assetId: string, requestKey?: string): Promise<{ token?: string, url?: string }> {
if (undefined === requestKey) {
requestKey = IModelApp.tileAdmin.cesiumIonKey;
if (undefined === requestKey)
if (undefined === requestKey) {
notifyTerrainError(IModelApp.localization.getLocalizedString(`iModelJs:BackgroundMap.MissingCesiumToken`));
return {};
}
}

const requestTemplate = `https://api.cesium.com/v1/assets/${assetId}/endpoint?access_token={CesiumRequestToken}`;
const apiUrl: string = requestTemplate.replace("{CesiumRequestToken}", requestKey);

const apiUrl = `https://api.cesium.com/v1/assets/${assetId}/endpoint?access_token=${requestKey}`;
try {
const apiResponse = await request(apiUrl, "json");
if (undefined === apiResponse || undefined === apiResponse.url) {
Expand All @@ -77,18 +97,17 @@ function notifyTerrainError(detailedDescription?: string): void {
IModelApp.notifications.displayMessage(MessageSeverity.Information, IModelApp.localization.getLocalizedString(`iModelJs:BackgroundMap.CannotObtainTerrain`), detailedDescription);
}

/** @internal */
export async function getCesiumTerrainProvider(opts: TerrainMeshProviderOptions): Promise<TerrainMeshProvider | undefined> {
const accessTokenAndEndpointUrl = await getCesiumAccessTokenAndEndpointUrl(opts.dataSource || CesiumTerrainAssetId.Default);
if (!accessTokenAndEndpointUrl.token || !accessTokenAndEndpointUrl.url) {
notifyTerrainError(IModelApp.localization.getLocalizedString(`iModelJs:BackgroundMap.MissingCesiumToken`));
/** @beta */
export async function createCesiumTerrainProvider(opts: TerrainMeshProviderOptions & Partial<CesiumContentAccessTileProps>): Promise<TerrainMeshProvider | undefined> {
Copy link
Member Author

Choose a reason for hiding this comment

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

hoping to get the displays team input here on what theyre preference is
should we expose a helper fn like this or just change the tag on CesiumTerrainProvider?

Copy link
Member Author

Choose a reason for hiding this comment

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

@pmconne let me know if you have thoughts

Copy link
Member

Choose a reason for hiding this comment

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

Why is this beta and how is somebody expected to use it? Not every curated Cesium asset represents "terrain".

if (opts.url === undefined || opts.accessToken === undefined) {
notifyTerrainError();
return undefined;
}

let layers;
try {
const layerRequestOptions: RequestOptions = { headers: { authorization: `Bearer ${accessTokenAndEndpointUrl.token}` } };
const layerUrl = `${accessTokenAndEndpointUrl.url}layer.json`;
const layerRequestOptions: RequestOptions = { headers: { authorization: `Bearer ${opts.accessToken}` } };
const layerUrl = `${opts.url}layer.json`;
layers = await request(layerUrl, "json", layerRequestOptions);
} catch {
notifyTerrainError();
Expand Down Expand Up @@ -119,14 +138,31 @@ export async function getCesiumTerrainProvider(opts: TerrainMeshProviderOptions)
}
}

let tileUrlTemplate = accessTokenAndEndpointUrl.url + layers.tiles[0].replace("{version}", layers.version);
let tileUrlTemplate = opts.url + layers.tiles[0].replace("{version}", layers.version);
if (opts.wantNormals)
tileUrlTemplate = tileUrlTemplate.replace("?", "?extensions=octvertexnormals-watermask-metadata&");

const maxDepth = JsonUtils.asInt(layers.maxzoom, 19);

// TBD -- When we have an API extract the heights for the project from the terrain tiles - for use temporary Bing elevation.
return new CesiumTerrainProvider(opts, accessTokenAndEndpointUrl.token, tileUrlTemplate, maxDepth, tilingScheme, tileAvailability, layers.metadataAvailability);
// TBD -- When we have an API extract the heights for the project from the terrain tiles - for use temporary Bing elevation.
return new CesiumTerrainProvider({
opts,
accessToken: opts.accessToken,
tileUrlTemplate,
maxDepth,
tilingScheme,
tileAvailability,
metaDataAvailableLevel: layers.metadataAvailability,
});
}

/** @internal */
export async function getCesiumTerrainProvider(opts: TerrainMeshProviderOptions): Promise<TerrainMeshProvider | undefined> {
const accessTokenAndEndpointUrl = await getCesiumAccessTokenAndEndpointUrl(opts.dataSource || CesiumTerrainAssetId.Default);
if (!accessTokenAndEndpointUrl.token || !accessTokenAndEndpointUrl.url) {
return undefined;
}
return createCesiumTerrainProvider({ ...opts, ...accessTokenAndEndpointUrl });
}

function zigZagDecode(value: number) {
Expand Down Expand Up @@ -156,6 +192,16 @@ function zigZagDeltaDecode(uBuffer: Uint16Array, vBuffer: Uint16Array, heightBuf
}
}

interface CesiumTerrainProviderOptions {
opts: TerrainMeshProviderOptions;
accessToken: string;
tileUrlTemplate: string;
maxDepth: number;
tilingScheme: MapTilingScheme;
tileAvailability: TileAvailability | undefined;
metaDataAvailableLevel: number | undefined;
}

/** @internal */
class CesiumTerrainProvider extends TerrainMeshProvider {
private _accessToken: string;
Expand All @@ -182,8 +228,7 @@ class CesiumTerrainProvider extends TerrainMeshProvider {
return undefined !== this._metaDataAvailableLevel && mapTile.quadId.level === this._metaDataAvailableLevel && !mapTile.everLoaded;
}

constructor(opts: TerrainMeshProviderOptions, accessToken: string, tileUrlTemplate: string, maxDepth: number, tilingScheme: MapTilingScheme,
tileAvailability: TileAvailability | undefined, metaDataAvailableLevel: number | undefined) {
constructor({ opts, accessToken, tileUrlTemplate, maxDepth, tilingScheme, tileAvailability, metaDataAvailableLevel }: CesiumTerrainProviderOptions) {
super();
this._wantSkirts = opts.wantSkirts;
this._exaggeration = opts.exaggeration;
Expand Down
8 changes: 7 additions & 1 deletion test-apps/display-test-app/src/frontend/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { ElectronRendererAuthorization } from "@itwin/electron-authorization/Ren
import { ITwinLocalization } from "@itwin/core-i18n";
import { getConfigurationString } from "./DisplayTestApp";
import { AddSeequentRealityModel } from "./RealityDataModel";
import { registerCesiumCuratedContentProvider } from "./CuratedCesiumContentProvider";

class DisplayTestAppAccuSnap extends AccuSnap {
private readonly _activeSnaps: SnapMode[] = [SnapMode.NearestKeypoint];
Expand Down Expand Up @@ -233,12 +234,13 @@ export class DisplayTestApp {

public static async startup(configuration: DtaConfiguration, renderSys: RenderSystem.Options, tileAdmin: TileAdmin.Props): Promise<void> {
let socketUrl = new URL(configuration.customOrchestratorUri || "http://localhost:3001");
const iTwinPlatformUrl = `https://${process.env.IMJS_URL_PREFIX ?? ""}api.bentley.com`
socketUrl = LocalhostIpcApp.buildUrlForSocket(socketUrl);
const realityDataClientOptions: RealityDataClientOptions = {
/** API Version. v1 by default */
// version?: ApiVersion;
/** API Url. Used to select environment. Defaults to "https://api.bentley.com/reality-management/reality-data" */
baseUrl: `https://${process.env.IMJS_URL_PREFIX ?? ""}api.bentley.com`,
baseUrl: iTwinPlatformUrl,
};
const opts: ElectronAppOpts | LocalHostIpcAppOpts = {
iModelApp: {
Expand Down Expand Up @@ -376,6 +378,10 @@ export class DisplayTestApp {
IModelApp.toolAdmin.defaultToolId = SVTSelectionTool.toolId;

BingTerrainMeshProvider.register();
await registerCesiumCuratedContentProvider({
iTwinId: configuration.iTwinId,
baseUrl: iTwinPlatformUrl,
});

const realityApiKey = process.env.IMJS_REALITY_DATA_KEY;
if (realityApiKey)
Expand Down
Loading
Loading