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

iTwin Reality Data - add GeoJSON/KML support #12344

Merged
merged 6 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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(
jjspace marked this conversation as resolved.
Show resolved Hide resolved
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
jjspace marked this conversation as resolved.
Show resolved Hide resolved
*
* @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
Loading