@cesium/engine
Version:
CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.
284 lines (255 loc) • 9.91 kB
JavaScript
import Cesium3DTileset from "./Cesium3DTileset.js";
import defined from "../Core/defined.js";
import Resource from "../Core/Resource.js";
import ITwinPlatform from "../Core/ITwinPlatform.js";
import RuntimeError from "../Core/RuntimeError.js";
import Check from "../Core/Check.js";
import KmlDataSource from "../DataSources/KmlDataSource.js";
import GeoJsonDataSource from "../DataSources/GeoJsonDataSource.js";
import DeveloperError from "../Core/DeveloperError.js";
/**
* Methods for loading iTwin platform data into CesiumJS
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @see ITwinPlatform
* @namespace ITwinData
*/
const ITwinData = {};
/**
* Create a {@link Cesium3DTileset} for the given iModel id using iTwin's Mesh Export API.
*
* If there is not a completed export available for the given iModel id, the returned promise will resolve to <code>undefined</code>.
* We recommend waiting 10-20 seconds and trying to load the tileset again.
* If all exports are Invalid this will throw an error.
*
* See the {@link https://developer.bentley.com/apis/mesh-export/overview/|iTwin Platform Mesh Export API documentation} for more information on request parameters
*
* @example
* const tileset = await Cesium.ITwinData.createTilesetFromIModelId({ iModelId });
* if (Cesium.defined(tileset)) {
* viewer.scene.primitives.add(tileset);
* }
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @param {object} options
* @param {string} options.iModelId The id of the iModel to load
* @param {Cesium3DTileset.ConstructorOptions} [options.tilesetOptions] Object containing options to pass to the internally created {@link Cesium3DTileset}.
* @param {string} [options.changesetId] The id of the changeset to load, if not provided the latest changesets will be used
* @returns {Promise<Cesium3DTileset | undefined>} A promise that will resolve to the created 3D tileset or <code>undefined</code> if there is no completed export for the given iModel id
*
* @throws {RuntimeError} If all exports for the given iModel are Invalid
* @throws {RuntimeError} If the iTwin API request is not successful
*/
ITwinData.createTilesetFromIModelId = async function ({
iModelId,
changesetId,
tilesetOptions,
}) {
const { exports } = await ITwinPlatform.getExports(iModelId, changesetId);
if (
exports.length > 0 &&
exports.every((exportObj) => {
return exportObj.status === ITwinPlatform.ExportStatus.Invalid;
})
) {
throw new RuntimeError(
`All exports for this iModel are Invalid: ${iModelId}`,
);
}
const completeExport = exports.find((exportObj) => {
return exportObj.status === ITwinPlatform.ExportStatus.Complete;
});
if (!defined(completeExport)) {
return;
}
// Convert the link to the tileset url while preserving the search paramaters
// This link is only valid 1 hour
const baseUrl = new URL(completeExport._links.mesh.href);
baseUrl.pathname = `${baseUrl.pathname}/tileset.json`;
const tilesetUrl = baseUrl.toString();
const resource = new Resource({
url: tilesetUrl,
});
return Cesium3DTileset.fromUrl(resource, tilesetOptions);
};
/**
* Create a tileset for the specified reality data id. This function only works
* with 3D Tiles meshes and point clouds.
*
* If the <code>type</code> or <code>rootDocument</code> are not provided this function
* will first request the full metadata for the specified reality data to fill these values.
*
* The <code>maximumScreenSpaceError</code> of the resulting tileset will default to 4,
* unless it is explicitly overridden with the given tileset options.
*
* @experimental This feature is not final and is subject to change without Cesium's standard deprecation policy.
*
* @param {object} options
* @param {string} options.iTwinId The id of the iTwin to load data from
* @param {string} options.realityDataId The id of the reality data to load
* @param {ITwinPlatform.RealityDataType} [options.type] The type of this reality data
* @param {string} [options.rootDocument] The path of the root document for this reality data
* @param {Cesium3DTileset.ConstructorOptions} [options.tilesetOptions] Object containing
* options to pass to the internally created {@link Cesium3DTileset}.
* @returns {Promise<Cesium3DTileset>}
*
* @throws {RuntimeError} if the type of reality data is not supported by this function
*/
ITwinData.createTilesetForRealityDataId = async function ({
iTwinId,
realityDataId,
type,
rootDocument,
tilesetOptions,
}) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iTwinId", iTwinId);
Check.typeOf.string("realityDataId", realityDataId);
if (defined(type)) {
Check.typeOf.string("type", type);
}
if (defined(rootDocument)) {
Check.typeOf.string("rootDocument", rootDocument);
}
//>>includeEnd('debug');
if (!defined(type) || !defined(rootDocument)) {
const metadata = await ITwinPlatform.getRealityDataMetadata(
iTwinId,
realityDataId,
);
rootDocument = metadata.rootDocument;
type = metadata.type;
}
const supportedRealityDataTypes = [
ITwinPlatform.RealityDataType.Cesium3DTiles,
ITwinPlatform.RealityDataType.PNTS,
ITwinPlatform.RealityDataType.RealityMesh3DTiles,
ITwinPlatform.RealityDataType.Terrain3DTiles,
];
if (!supportedRealityDataTypes.includes(type)) {
throw new RuntimeError(`Reality data type is not a mesh type: ${type}`);
}
const tilesetAccessUrl = await ITwinPlatform.getRealityDataURL(
iTwinId,
realityDataId,
rootDocument,
);
// The maximum screen space error was defined to default to 4 for
// reality data tilesets, because they did not show the expected
// amount of detail with the default value of 16. Values that are
// given in the tilesetOptions should still override that default.
const internalTilesetOptions = {
maximumScreenSpaceError: 4,
...tilesetOptions,
};
return Cesium3DTileset.fromUrl(tilesetAccessUrl, internalTilesetOptions);
};
/**
* Create a data source of the correct type for the specified reality data id.
* This function only works for KML and GeoJSON type data.
*
* If the <code>type</code> or <code>rootDocument</code> are not provided this function
* will first request the full metadata for the specified reality data to fill these values.
*
* @param {object} options
* @param {string} options.iTwinId The id of the iTwin to load data from
* @param {string} options.realityDataId The id of the reality data to load
* @param {ITwinPlatform.RealityDataType} [options.type] The type of this reality data
* @param {string} [options.rootDocument] The path of the root document for this reality data
* @returns {Promise<GeoJsonDataSource | KmlDataSource>}
*
* @throws {RuntimeError} if the type of reality data is not supported by this function
*/
ITwinData.createDataSourceForRealityDataId = async function ({
iTwinId,
realityDataId,
type,
rootDocument,
}) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iTwinId", iTwinId);
Check.typeOf.string("realityDataId", realityDataId);
if (defined(type)) {
Check.typeOf.string("type", type);
}
if (defined(rootDocument)) {
Check.typeOf.string("rootDocument", rootDocument);
}
//>>includeEnd('debug');
if (!defined(type) || !defined(rootDocument)) {
const metadata = await ITwinPlatform.getRealityDataMetadata(
iTwinId,
realityDataId,
);
rootDocument = metadata.rootDocument;
type = metadata.type;
}
const supportedRealityDataTypes = [
ITwinPlatform.RealityDataType.KML,
ITwinPlatform.RealityDataType.GeoJSON,
];
if (!supportedRealityDataTypes.includes(type)) {
throw new RuntimeError(
`Reality data type is not a data source type: ${type}`,
);
}
const tilesetAccessUrl = await ITwinPlatform.getRealityDataURL(
iTwinId,
realityDataId,
rootDocument,
);
if (type === ITwinPlatform.RealityDataType.GeoJSON) {
return GeoJsonDataSource.load(tilesetAccessUrl);
}
// If we get here it's guaranteed to be a KML type
return KmlDataSource.load(tilesetAccessUrl);
};
/**
* Load data from the Geospatial Features API as GeoJSON.
*
* @param {object} options
* @param {string} options.iTwinId The id of the iTwin to load data from
* @param {string} options.collectionId The id of the data collection to load
* @param {number} [options.limit=10000] number of items per page, must be between 1 and 10,000 inclusive
* @returns {Promise<GeoJsonDataSource>}
*/
ITwinData.loadGeospatialFeatures = async function ({
iTwinId,
collectionId,
limit,
}) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.string("iTwinId", iTwinId);
Check.typeOf.string("collectionId", collectionId);
if (defined(limit)) {
Check.typeOf.number("limit", limit);
Check.typeOf.number.lessThanOrEquals("limit", limit, 10000);
Check.typeOf.number.greaterThanOrEquals("limit", limit, 1);
}
if (
!defined(ITwinPlatform.defaultAccessToken) &&
!defined(ITwinPlatform.defaultShareKey)
) {
throw new DeveloperError(
"Must set ITwinPlatform.defaultAccessToken or ITwinPlatform.defaultShareKey first",
);
}
//>>includeEnd('debug');
const pageLimit = limit ?? 10000;
const tilesetUrl = `${ITwinPlatform.apiEndpoint}geospatial-features/itwins/${iTwinId}/ogc/collections/${collectionId}/items`;
const resource = new Resource({
url: tilesetUrl,
headers: {
Authorization: ITwinPlatform._getAuthorizationHeader(),
Accept: "application/vnd.bentley.itwin-platform.v1+json",
},
queryParameters: {
limit: pageLimit,
client: "CesiumJS",
},
});
return GeoJsonDataSource.load(resource);
};
export default ITwinData;