@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
389 lines (361 loc) • 13.5 kB
JavaScript
import Check from "./Check.js";
import defined from "./defined.js";
import DeveloperError from "./DeveloperError.js";
import Resource from "./Resource.js";
import RuntimeError from "./RuntimeError.js";
/**
* Default settings for accessing the iTwin platform.
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @see ITwinData
* @namespace ITwinPlatform
*/
const ITwinPlatform = {};
/**
* Status states for a mesh-export export.
* Valid values are: <code>NotStarted</code>, <code>InProgress</code>, <code>Complete</code>, <code>Invalid</code>
* @enum {string}
*/
ITwinPlatform.ExportStatus = Object.freeze({
NotStarted: "NotStarted",
InProgress: "InProgress",
Complete: "Complete",
Invalid: "Invalid",
});
/**
* Types of mesh-export exports. CesiumJS only supports loading <code>3DTILES</code> type exports.
* Valid values are: <code>IMODEL</code>, <code>CESIUM</code>, <code>3DTILES</code>
* @enum {string}
*/
ITwinPlatform.ExportType = Object.freeze({
IMODEL: "IMODEL",
CESIUM: "CESIUM",
"3DTILES": "3DTILES",
});
/**
* Types of Reality data. This is a partial list of types we know we can support
*
* @see https://developer.bentley.com/apis/reality-management/rm-rd-details/#types
* @enum {string}
*/
ITwinPlatform.RealityDataType = Object.freeze({
Cesium3DTiles: "Cesium3DTiles",
PNTS: "PNTS",
RealityMesh3DTiles: "RealityMesh3DTiles",
Terrain3DTiles: "Terrain3DTiles",
KML: "KML",
GeoJSON: "GeoJSON",
Unstructured: "Unstructured",
});
/**
* Gets or sets the default iTwin access token. This token should have the <code>itwin-platform</code> scope.
*
* This value will be ignored if {@link ITwinPlatform.defaultShareKey} is defined.
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @type {string|undefined}
*/
ITwinPlatform.defaultAccessToken = undefined;
/**
* Gets or sets the default iTwin share key. If this value is provided it will override {@link ITwinPlatform.defaultAccessToken} in all requests.
*
* Share keys can be generated using the iTwin Shares api
* https://developer.bentley.com/apis/access-control-v2/operations/create-itwin-share/
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @type {string|undefined}
*/
ITwinPlatform.defaultShareKey = undefined;
/**
* Create the necessary Authorization header based on which key/token is set.
* If the {@link ITwinPlatform.defaultShareKey} is set it takes precedence and
* will be used regardless if the {@link ITwinPlatform.defaultAccessToken} is set
* @private
* @returns {string} full auth header with basic/bearer method
*/
ITwinPlatform._getAuthorizationHeader = function () {
//>>includeStart('debug', pragmas.debug);
if (
!defined(ITwinPlatform.defaultAccessToken) &&
!defined(ITwinPlatform.defaultShareKey)
) {
throw new DeveloperError(
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
);
}
//>>includeEnd('debug');
if (defined(ITwinPlatform.defaultShareKey)) {
return `Basic ${ITwinPlatform.defaultShareKey}`;
}
return `Bearer ${ITwinPlatform.defaultAccessToken}`;
};
/**
* Gets or sets the default iTwin API endpoint.
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @type {string|Resource}
* @default "https://api.bentley.com"
*/
ITwinPlatform.apiEndpoint = new Resource({
url: "https://api.bentley.com",
});
/**
* @typedef {Object} ExportRequest
* @private
* @property {string} iModelId
* @property {string} changesetId
* @property {ITwinPlatform.ExportType} exportType Type of the export. CesiumJS only supports the 3DTILES type
*/
/**
* @typedef {Object} Link
* @private
* @property {string} href
*/
/**
* @typedef {Object} ExportRepresentation
* The export objects from get-exports when using return=representation
* @private
* @property {string} id Export id
* @property {string} displayName Name of the iModel
* @property {ITwinPlatform.ExportStatus} status Status of this export
* @property {string} lastModified
* @property {ExportRequest} request Object containing info about the export itself
* @property {{mesh: Link}} _links Object containing relevant links. For Exports this includes the access url for the mesh itself
*/
/**
* @typedef {Object} GetExportsResponse
* @private
* @property {ExportRepresentation[]} exports The list of exports for the current page
* @property {{self: Link, next: Link | undefined, prev: Link | undefined}} _links Pagination links
*/
/**
* Get the list of exports for the specified iModel at it's most current version.
* This will only return the top 5 exports with {@link ITwinPlatform.ExportType} of <code>3DTILES</code>.
*
* @private
*
* @param {string} iModelId iModel id
* @param {string} [changesetId] The id of the changeset to filter results by. If not provided, exports from the latest available changesets will be returned.
* @returns {Promise<GetExportsResponse>}
*
* @throws {RuntimeError} If the iTwin API request is not successful
*/
ITwinPlatform.getExports = async function (iModelId, changesetId) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iModelId", iModelId);
if (defined(changesetId)) {
Check.typeOf.string("changesetId", changesetId);
}
if (
!defined(ITwinPlatform.defaultAccessToken) &&
!defined(ITwinPlatform.defaultShareKey)
) {
throw new DeveloperError(
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
);
}
//>>includeEnd('debug');
const resource = new Resource({
url: `${ITwinPlatform.apiEndpoint}mesh-export`,
headers: {
Authorization: ITwinPlatform._getAuthorizationHeader(),
Accept: "application/vnd.bentley.itwin-platform.v1+json",
Prefer: "return=representation",
},
queryParameters: {
iModelId: iModelId,
exportType: ITwinPlatform.ExportType["3DTILES"],
// With the export auto-generation it will auto-delete the 6th export so
// there should never be more than 5 results. Just request them all and parse
// for ones that are COMPLETE
$top: "5",
client: "CesiumJS",
},
});
/* global CESIUM_VERSION */
if (typeof CESIUM_VERSION !== "undefined") {
resource.appendQueryParameters({ clientVersion: CESIUM_VERSION });
}
if (defined(changesetId) && changesetId !== "") {
resource.appendQueryParameters({ changesetId: changesetId });
}
try {
const response = await resource.fetchJson();
return response;
} catch (error) {
const result = JSON.parse(error.response);
if (error.statusCode === 401) {
const code = result.error.details?.[0].code ?? "";
throw new RuntimeError(
`Unauthorized, bad token, wrong scopes or headers bad. ${code}`,
);
} else if (error.statusCode === 403) {
console.error(result.error.code, result.error.message);
throw new RuntimeError("Not allowed, forbidden");
} else if (error.statusCode === 422) {
throw new RuntimeError(
`Unprocessable Entity:${result.error.code} ${result.error.message}`,
);
} else if (error.statusCode === 429) {
throw new RuntimeError("Too many requests");
}
throw new RuntimeError(`Unknown request failure ${error.statusCode}`);
}
};
/**
* @typedef {Object} RealityDataExtent
* @private
* @property {{latitude: number, longitude: number}} southWest
* @property {{latitude: number, longitude: number}} northEast
*/
/**
* @typedef {Object} RealityDataRepresentation
* @private
* @property {string} id "95d8dccd-d89e-4287-bb5f-3219acbc71ae",
* @property {string} displayName "Name of reality data",
* @property {string} dataset "Dataset",
* @property {string} group "73d09423-28c3-4fdb-ab4a-03a47a5b04f8",
* @property {string} description "Description of reality data",
* @property {string} rootDocument "Directory/SubDirectory/realityData.3mx",
* @property {number} size 6521212,
* @property {string} classification "Model",
* @property {ITwinPlatform.RealityDataType} type "3MX",
* @property {{startDateTime: string, endDateTime: string, acquirer: string}} acquisition
* @property {RealityDataExtent} extent
* @property {boolean} authoring false,
* @property {string} dataCenterLocation "North Europe",
* @property {string} modifiedDateTime "2021-04-09T19:03:12Z",
* @property {string} lastAccessedDateTime "2021-04-09T00:00:00Z",
* @property {string} createdDateTime "2021-02-22T20:03:40Z",
* @property {string} ownerId "f1d49cc7-f9b3-494f-9c67-563ea5597063",
*/
/**
* Load the full metadata for the given iTwin id and reality data id.
*
* @private
*
* @param {string} iTwinId The id of the iTwin to load data from
* @param {string} realityDataId The id of the reality data to load
* @returns {Promise<RealityDataRepresentation>}
*/
ITwinPlatform.getRealityDataMetadata = async function (iTwinId, realityDataId) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iTwinId", iTwinId);
Check.typeOf.string("realityDataId", realityDataId);
if (
!defined(ITwinPlatform.defaultAccessToken) &&
!defined(ITwinPlatform.defaultShareKey)
) {
throw new DeveloperError(
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
);
}
//>>includeEnd('debug');
const resource = new Resource({
url: `${ITwinPlatform.apiEndpoint}reality-management/reality-data/${realityDataId}`,
headers: {
Authorization: ITwinPlatform._getAuthorizationHeader(),
Accept: "application/vnd.bentley.itwin-platform.v1+json",
},
queryParameters: { iTwinId: iTwinId },
});
try {
const response = await resource.fetchJson();
return response.realityData;
} catch (error) {
const result = JSON.parse(error.response);
if (error.statusCode === 401) {
const code = result.error.details?.[0].code ?? "";
throw new RuntimeError(
`Unauthorized, bad token, wrong scopes or headers bad. ${code}`,
);
} else if (error.statusCode === 403) {
console.error(result.error.code, result.error.message);
throw new RuntimeError("Not allowed, forbidden");
} else if (error.statusCode === 404) {
throw new RuntimeError(
`Reality data not found: ${iTwinId}, ${realityDataId}`,
);
} else if (error.statusCode === 422) {
throw new RuntimeError(
`Unprocessable Entity:${result.error.code} ${result.error.message}`,
);
} else if (error.statusCode === 429) {
throw new RuntimeError("Too many requests");
}
throw new RuntimeError(`Unknown request failure ${error.statusCode}`);
}
};
/**
* Request the access url for the given iTwin id, reality data id and root document.
* The root document can be requested from the list using <code>return=representation</code>
* or the metadata route from {@link ITwinPlatform.getRealityDataMetadata}
*
* @private
*
* @param {string} iTwinId The id of the iTwin to load data from
* @param {string} realityDataId The id of the reality data to load
* @param {string} rootDocument The path of the root document for this reality data
* @returns {Promise<string>}
*/
ITwinPlatform.getRealityDataURL = async function (
iTwinId,
realityDataId,
rootDocument,
) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iTwinId", iTwinId);
Check.typeOf.string("realityDataId", realityDataId);
Check.typeOf.string("rootDocument", rootDocument);
if (
!defined(ITwinPlatform.defaultAccessToken) &&
!defined(ITwinPlatform.defaultShareKey)
) {
throw new DeveloperError(
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
);
}
//>>includeEnd('debug');
const resource = new Resource({
url: `${ITwinPlatform.apiEndpoint}reality-management/reality-data/${realityDataId}/readaccess`,
headers: {
Authorization: ITwinPlatform._getAuthorizationHeader(),
Accept: "application/vnd.bentley.itwin-platform.v1+json",
},
queryParameters: { iTwinId: iTwinId },
});
try {
const result = await resource.fetchJson();
const containerUrl = result._links.containerUrl.href;
const tilesetUrl = new URL(containerUrl);
tilesetUrl.pathname = `${tilesetUrl.pathname}/${rootDocument}`;
return tilesetUrl.toString();
} catch (error) {
const result = JSON.parse(error.response);
if (error.statusCode === 401) {
const code = result.error.details?.[0].code ?? "";
throw new RuntimeError(
`Unauthorized, bad token, wrong scopes or headers bad. ${code}`,
);
} else if (error.statusCode === 403) {
console.error(result.error.code, result.error.message);
throw new RuntimeError("Not allowed, forbidden");
} else if (error.statusCode === 404) {
throw new RuntimeError(
`Reality data not found: ${iTwinId}, ${realityDataId}`,
);
} else if (error.statusCode === 422) {
throw new RuntimeError(
`Unprocessable Entity:${result.error.code} ${result.error.message}`,
);
} else if (error.statusCode === 429) {
throw new RuntimeError("Too many requests");
}
throw new RuntimeError(`Unknown request failure ${error.statusCode}`);
}
};
export default ITwinPlatform;