Skip to content

Commit

Permalink
Merge pull request #12344 from CesiumGS/itwin-more-reality-data-types
Browse files Browse the repository at this point in the history
iTwin Reality Data - add GeoJSON/KML support
  • Loading branch information
jjhembd authored Dec 4, 2024
2 parents 382c517 + 16496df commit 7d33049
Show file tree
Hide file tree
Showing 7 changed files with 385 additions and 39 deletions.
1 change: 1 addition & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"iife",
"lerp",
"Lilli",
"maki",
"MAXAR",
"minifiers",
"mipmapped",
Expand Down
184 changes: 184 additions & 0 deletions Apps/Sandcastle/gallery/iTwin Feature Service.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<meta
name="description"
content="Use Viewer to start building new applications or easily embed Cesium into existing applications."
/>
<meta name="cesium-sandcastle-labels" content="Beginner, Showcases" />
<title>iTwin Feature Service</title>
<script type="text/javascript" src="../Sandcastle-header.js"></script>
<script type="module" src="../load-cesium-es6.js"></script>
</head>
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
<style>
@import url(../templates/bucket.css);
</style>
<div id="cesiumContainer" class="fullSize"></div>
<div id="loadingOverlay"><h1>Loading...</h1></div>
<div id="toolbar">
<div id="layers"></div>
</div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
const serviceResponse = await fetch("https://api.cesium.com/itwin/token");
const { access_token: token } = await serviceResponse.json();

Cesium.ITwinPlatform.defaultAccessToken = token;

const iTwinId = "04ba725f-f3c0-4f30-8014-a4488cbd612d";

const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
sceneModePicker: false,
homeButton: false,
timeline: false,
animation: false,
});
viewer.baseLayerPicker.viewModel.selectedImagery =
viewer.baseLayerPicker.viewModel.imageryProviderViewModels[2];

const birdsEyeView = {
destination: new Cesium.Cartesian3(
-1525359.4318772827,
6191643.528984093,
148851.5321709012,
),
orientation: new Cesium.HeadingPitchRoll(
0.16657338935967037,
-0.7943050121851765,
6.283180723449992,
),
duration: 0,
easingFunction: Cesium.EasingFunction.LINEAR_NONE,
};
viewer.scene.camera.flyTo(birdsEyeView);

// Load feature service geojson files
const points = await Cesium.ITwinData.createDataSourceForRealityDataId(
iTwinId,
"57b975f6-fd92-42ba-8014-79911ed606d1",
);
const lines = await Cesium.ITwinData.createDataSourceForRealityDataId(
iTwinId,
"1099c53f-c568-48a3-a57c-0230a6f37229",
);
const areas = await Cesium.ITwinData.createDataSourceForRealityDataId(
iTwinId,
"21eaf0d0-ab90-400f-97cf-adc455b29a78",
);

// Add some styling to the lines and points to differentiate types
const pinBuilder = new Cesium.PinBuilder();
points.entities.values.forEach(async (entity) => {
const styleByType = {
Tree: { color: Cesium.Color.GREEN, icon: "park2" },
Lamp_post: { color: Cesium.Color.WHITE, icon: "lighthouse" },
Traffic_light: { color: Cesium.Color.CRIMSON, icon: "circle-stroked" },
Arrow_Marking: { color: Cesium.Color.YELLOW, icon: "car" },
Road_Sign: { color: Cesium.Color.ORANGE, icon: "triangle" },
};
const type = entity.properties.Type?.getValue();
if (Cesium.defined(type) && Cesium.defined(styleByType[type])) {
const { color, icon } = styleByType[type];
const canvas = await pinBuilder.fromMakiIconId(icon, color, 48);
entity.billboard.image = canvas.toDataURL();
entity.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
}
});
lines.entities.values.forEach((entity) => {
const lineColorsByType = {
Contours: Cesium.Color.CRIMSON,
Lane_Marking: Cesium.Color.CYAN,
Kerb: Cesium.Color.BLUEVIOLET,
Chevron_marking: Cesium.Color.DARKORANGE,
Turning_pocket: Cesium.Color.DEEPPINK,
Yellow_Box: Cesium.Color.GOLD,
};
const type = entity.properties.Type?.getValue();
if (Cesium.defined(type) && Cesium.defined(lineColorsByType[type])) {
entity.polyline.material = lineColorsByType[type];
}
});

// add the geojsons to the viewer
viewer.dataSources.add(points);
viewer.dataSources.add(lines);
viewer.dataSources.add(areas);

// Create tileset of the reality data mesh and pointcloud
const realityMeshId = "62e4432d-621d-489a-87ff-1fc56a2b5369";
const realityMesh = await Cesium.ITwinData.createTilesetForRealityDataId(
iTwinId,
realityMeshId,
);
viewer.scene.primitives.add(realityMesh);
const pointcloudId = "ebf2ee74-f0de-4cd6-a311-19a169c55fdc";
const pointcloud = await Cesium.ITwinData.createTilesetForRealityDataId(
iTwinId,
pointcloudId,
);
// increase the size of the pointcloud points and turn on attenuation to
// make them more visible in the viewer
pointcloud.maximumScreenSpaceError = 1;
pointcloud.pointCloudShading.attenuation = true;
pointcloud.style = new Cesium.Cesium3DTileStyle({
pointSize: 5.0,
});
pointcloud.show = false;
viewer.scene.primitives.add(pointcloud);

Sandcastle.addToolbarButton(
"Toggle Points",
() => (points.show = !points.show),
"layers",
);
Sandcastle.addToolbarButton(
"Toggle Lines",
() => (lines.show = !lines.show),
"layers",
);
Sandcastle.addToolbarButton(
"Toggle Areas",
() => (areas.show = !areas.show),
"layers",
);
Sandcastle.addToolbarButton(
"Toggle Reality Mesh",
() => (realityMesh.show = !realityMesh.show),
"layers",
);
Sandcastle.addToolbarButton(
"Toggle Pointcloud",
() => (pointcloud.show = !pointcloud.show),
"layers",
);

Sandcastle.addToolbarButton("Birdseye View", () => {
viewer.scene.camera.flyTo(birdsEyeView);
});
Sandcastle.addToolbarButton("Zoom to Pointcloud", () => {
pointcloud.show = true;
viewer.zoomTo(pointcloud);
});
//Sandcastle_End
Sandcastle.finishedLoading();
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
window.startup(Cesium).catch((error) => {
"use strict";
console.error(error);
});
}
</script>
</body>
</html>
Binary file added Apps/Sandcastle/gallery/iTwin Feature Service.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
##### Additions :tada:

- Added an integration with the [iTwin Platform](https://developer.bentley.com/) to load iModels as 3D Tiles. Use `ITwinPlatform.defaultAccessToken` to set the access token. Use `ITwinData.createTilesetFromIModelId(iModelId)` to load the iModel as a `Cesium3DTileset`. [#12289](https://github.com/CesiumGS/cesium/pull/12289)
- Added an integration with the [iTwin Platform](https://developer.bentley.com/) to load Reality Data terrain meshes and GeoJSON. Use `ITwinPlatform.defaultAccessToken` to set the access token. Then use `ITwinData.createTilesetForRealityDataId(iTwinId, dataId)` to load terrain meshes as a `Cesium3DTileset` or `ITwinData.createDataSourceForRealityDataId(iTwinId, dataId)` to load GeoJSON or KML files as data sources. [#12344](https://github.com/CesiumGS/cesium/pull/12344)
- Added `getSample` to `SampledProperty` to get the time of samples. [#12253](https://github.com/CesiumGS/cesium/pull/12253)
- Added `Entity.trackingReferenceFrame` property to allow tracking entities in various reference frames. [#12194](https://github.com/CesiumGS/cesium/pull/12194), [#12314](https://github.com/CesiumGS/cesium/pull/12314)
- `TrackingReferenceFrame.AUTODETECT` (default): uses either VVLH or ENU depending on entity's dynamic. Use `TrackingReferenceFrame.ENU` if your camera orientation flips abruptly from time to time.
Expand Down
41 changes: 3 additions & 38 deletions packages/engine/Source/Core/ITwinPlatform.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,54 +38,19 @@ ITwinPlatform.ExportType = Object.freeze({
});

/**
* Types of Reality data
* 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",
OPC: "OPC",
RealityMesh3DTiles: "RealityMesh3DTiles",
Terrain3DTiles: "Terrain3DTiles",
"3MX": "3MX",
"3SM": "3SM",
CCCloudProject: "CCCloudProject",
CCImageCollection: "CCImageCollection",
CCOrientations: "CCOrientations",
ContextCaptureInputs: "ContextCaptureInputs",
ContextDetector: "ContextDetector",
ContextScene: "ContextScene",
DAE: "DAE",
DGN: "DGN",
DSM: "DSM",
FBX: "FBX",
GLB: "GLB",
GLTF: "GLTF",
KML: "KML",
LAS: "LAS",
LAZ: "LAZ",
LOD: "LOD",
LodTree: "LodTree",
OBJ: "OBJ",
OMI: "OMI",
OMR: "OMR",
Orthophoto: "Orthophoto",
OrthophotoDSM: "OrthophotoDSM",
OSGB: "OSGB",
OVF: "OVF",
OBT: "OBT",
PLY: "PLY",
PointCloud: "PointCloud",
S3C: "S3C",
ScanCollection: "ScanCollection",
SHP: "SHP",
SLPK: "SLPK",
SpaceEyes3D: "SpaceEyes3D",
STL: "STL",
TSM: "TSM",
GeoJSON: "GeoJSON",
Unstructured: "Unstructured",
Other: "Other",
});

/**
Expand Down
70 changes: 70 additions & 0 deletions packages/engine/Source/Scene/ITwinData.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ 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";

/**
* Methods for loading iTwin platform data into CesiumJS
Expand Down Expand Up @@ -86,6 +88,8 @@ ITwinData.createTilesetFromIModelId = async function (iModelId, options) {
* @param {ITwinPlatform.RealityDataType} [type] The type of this reality data
* @param {string} [rootDocument] The path of the root document for this reality data
* @returns {Promise<Cesium3DTileset>}
*
* @throws {RuntimeError} if the type of reality data is not supported by this function
*/
ITwinData.createTilesetForRealityDataId = async function (
iTwinId,
Expand Down Expand Up @@ -135,4 +139,70 @@ ITwinData.createTilesetForRealityDataId = async function (
});
};

/**
* 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 {string} iTwinId The id of the iTwin to load data from
* @param {string} realityDataId The id of the reality data to load
* @param {ITwinPlatform.RealityDataType} [type] The type of this reality data
* @param {string} [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 loadRealityData(
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);
};

export default ITwinData;
Loading

0 comments on commit 7d33049

Please sign in to comment.