Skip to content

Commit

Permalink
initial rough api
Browse files Browse the repository at this point in the history
  • Loading branch information
jjspace committed Nov 5, 2024
1 parent f21a07f commit 96c4d61
Show file tree
Hide file tree
Showing 3 changed files with 734 additions and 0 deletions.
297 changes: 297 additions & 0 deletions Apps/Sandcastle/gallery/iTwin Demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
<!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>Cesium Demo</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="checkbox"></div>
<output id="status">Initializing</output>
</div>
<script id="cesium_sandcastle_script">
window.startup = async function (Cesium) {
"use strict";
//Sandcastle_Begin
// Gabby's token or one created using the Share API?
// const accessToken =
// "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkJlbnRsZXlJTVNfMjAyNCIsInBpLmF0bSI6ImE4bWUifQ.eyJzY29wZSI6WyJtZXNoLWV4cG9ydDpyZWFkIiwibWVzaC1leHBvcnQ6bW9kaWZ5Il0sImNsaWVudF9pZCI6Iml0d2luLWRldmVsb3Blci1jb25zb2xlIiwiYXVkIjpbImh0dHBzOi8vaW1zLmJlbnRsZXkuY29tL2FzL3Rva2VuLm9hdXRoMiIsImh0dHBzOi8vaW1zb2lkYy5iZW50bGV5LmNvbS9hcy90b2tlbi5vYXV0aDIiLCJodHRwczovL2ltc29pZGMuYmVudGxleS5jb20vcmVzb3VyY2VzIiwiYmVudGxleS1hcGktbWFuYWdlbWVudCJdLCJzdWIiOiJiNmFmMjk1ZC00NGNiLTRjYjMtYThlMy0yMTM3ODM3MDU2ZGUiLCJyb2xlIjoiQkVOVExFWV9FTVBMT1lFRSIsIm9yZyI6ImZhYjk3NzRiLWIzMzgtNGNjMi1hNmM5LTQ1OGJkZjdmOTY2YSIsInN1YmplY3QiOiJiNmFmMjk1ZC00NGNiLTRjYjMtYThlMy0yMTM3ODM3MDU2ZGUiLCJpc3MiOiJodHRwczovL2ltcy5iZW50bGV5LmNvbSIsImVudGl0bGVtZW50IjpbIkJFTlRMRVlfTEVBUk4iLCJJTlRFUk5BTCIsIlNFTEVDVF8yMDA2IiwiQkVOIiwiQkROIl0sInByZWZlcnJlZF91c2VybmFtZSI6IkdhYmJ5LkdldHpAYmVudGxleS5jb20iLCJnaXZlbl9uYW1lIjoiR2FiYnkiLCJzaWQiOiIzZWNtdVUzNWJXV3hyQ1lfVWVnR0IxVkFxcmMuU1UxVExVSmxiblJzWlhrdFZWTS5FbzV4LnVYMEFyS1Ixcm5udHpQNDJsNG9uOGJpWTQiLCJuYmYiOjE3MjkyNzAwNTMsInVsdGltYXRlX3NpdGUiOiIxMDAxMzg5MTE3IiwidXNhZ2VfY291bnRyeV9pc28iOiJVUyIsImF1dGhfdGltZSI6MTcyOTI3MDM1MywibmFtZSI6IkdhYmJ5LkdldHpAYmVudGxleS5jb20iLCJvcmdfbmFtZSI6IkJlbnRsZXkgU3lzdGVtcyBJbmMiLCJmYW1pbHlfbmFtZSI6IkdldHoiLCJlbWFpbCI6IkdhYmJ5LkdldHpAYmVudGxleS5jb20iLCJleHAiOjE3MjkyNzM5NTN9.DTm5w_ARIHL0AuNaGFtgQ7-yhcaVcL8mawKu1wy-9RKVGn-gA-yZx9wU79XDwTI1yGef6ZrJpuwkiGSSkp45dKdwonWEZA0Mb9HYtlFqk519jY3sDxeFb_E2eIMIblA0P6lSyrkP3CNnlLhFctr8jR6LOSSgvYMWvBQhBpA2LgMFDRShxl0-V95qCAL7iP7E-m9WwXghllOMHfmviOU7nmG73fSPcboI9QCPpAINO9bzvPHkJnumvQsukHAbdn4CPEl0cRukZpJTxvTJllQ_YimDeWPtgQOwfidRLoYZviu3PYDYbRVs5X1WEsLd1SeJzfj8WVUMFkfsrlEdpvmbNQ";
// My access token, must be created for the Start export route, probably an oauth scope thing?
const accessToken =
"Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkJlbnRsZXlJTVNfMjAyNCIsInBpLmF0bSI6ImE4bWUifQ.eyJzY29wZSI6WyJtZXNoLWV4cG9ydDpyZWFkIiwibWVzaC1leHBvcnQ6bW9kaWZ5Il0sImNsaWVudF9pZCI6Iml0d2luLWRldmVsb3Blci1jb25zb2xlIiwiYXVkIjpbImh0dHBzOi8vaW1zLmJlbnRsZXkuY29tL2FzL3Rva2VuLm9hdXRoMiIsImh0dHBzOi8vaW1zb2lkYy5iZW50bGV5LmNvbS9hcy90b2tlbi5vYXV0aDIiLCJodHRwczovL2ltc29pZGMuYmVudGxleS5jb20vcmVzb3VyY2VzIiwiYmVudGxleS1hcGktbWFuYWdlbWVudCJdLCJzdWIiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJyb2xlIjoiQkVOVExFWV9FTVBMT1lFRSIsIm9yZyI6ImZhYjk3NzRiLWIzMzgtNGNjMi1hNmM5LTQ1OGJkZjdmOTY2YSIsInN1YmplY3QiOiJjMWM1MzRhNy0zZDk2LTQ2MzMtYjY5ZC1jMGEzNzE5OWQwZGUiLCJpc3MiOiJodHRwczovL2ltcy5iZW50bGV5LmNvbSIsImVudGl0bGVtZW50IjpbIkJFTlRMRVlfTEVBUk4iLCJJTlRFUk5BTCIsIlNFTEVDVF8yMDA2IiwiQkVOIiwiQkROIl0sInByZWZlcnJlZF91c2VybmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZ2l2ZW5fbmFtZSI6Ikpvc2giLCJzaWQiOiJHMGZTamlOXzBMRjE1Vy1IYnRTaWtoeDNuSXMuU1UxVExVSmxiblJzWlhrdFZWTS5LUDVRLjZCakNoYXptMldBMjBIdXVWMUxwNFB6RVAiLCJuYmYiOjE3MzA3NDc3NjUsInVsdGltYXRlX3NpdGUiOiIxMDAxMzg5MTE3IiwidXNhZ2VfY291bnRyeV9pc28iOiJVUyIsImF1dGhfdGltZSI6MTczMDc0ODA2NSwibmFtZSI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwib3JnX25hbWUiOiJCZW50bGV5IFN5c3RlbXMgSW5jIiwiZmFtaWx5X25hbWUiOiJSb3V6ZXIiLCJlbWFpbCI6Ikpvc2guUm91emVyQGJlbnRsZXkuY29tIiwiZXhwIjoxNzMwNzUxNjY2fQ.0yEQZAyKKAdwNDiwHSD6f_Qzq0M8cbHJcMfT6JidBldw9qiyU4jx6ZdqILddrL-seWCkf9sRtWuoHm7Fw-j_wtaLASaOpHMMwC7IVdh25pbRB-D3mN8_rmQiDbXUadJ1MwH8-pNCubrER1lZLYEPrQ4zJcRtAblbJGNjFdoOi3FXB-y3JLleH4qYykLceDkbW3l2lZRfdIW2pytCIuZs7XZ9Hr6F_cLsYIrs9iRfBFVxxHxxfwgRLQoRuPmDuzKl9-ylLZUFN6CQZUfv7vzL9feoXJXGdZeGgqlyFuMOCiVbYU_elx7P7fFm8G13HvTti5hz98mV1r2bXYvfNpuklg";
Cesium.ITwin.defaultAccessToken = accessToken;
// this is the iModel in the "Hello iTwinCesium" iTwin that we should all have access to
// https://developer.bentley.com/my-itwins/b4a30036-0456-49ea-a439-3fcd9365e24e/home/
const imodelId = "2852c3d7-00c3-4b5d-a0ce-82bbde4f061e";

// const knownExportId = undefined;
const knownExportId = "ab9953b2-bc8e-48ac-a5b0-5d43d68593e8";

// async function startExport(iModelId, changesetId, accessToken) {
// console.log("Start Export");

// const requestOptions = {
// method: "POST",
// headers: {
// Authorization: accessToken,
// Accept: "application/vnd.bentley.itwin-platform.v1+json",
// "Content-Type": "application/json",
// },
// body: JSON.stringify({
// iModelId,
// changesetId,
// exportType: "CESIUM",
// }),
// };

// // initiate mesh export
// const response = await fetch(
// `https://api.bentley.com/mesh-export/`,
// requestOptions,
// );
// if (!response.ok) {
// if (response.status === 401) {
// console.error("Unauthorized, bad token, wrong scopes or headers bad");
// } else if (response.status === 403) {
// console.error("Not allowed, forbidden");
// } else if (response.status === 422) {
// console.error("Unprocessable: Cannot create export job");
// } else if (response.status === 429) {
// console.log("Too many requests");
// } else {
// console.error("Bad request, unknown error", response);
// }
// return undefined;
// }
// const result = JSON.parse(JSON.stringify(await response.json()));
// return result?.export?.id;
// }

// async function getExport(exportId, accessToken) {
// const headers = {
// Authorization: accessToken,
// Accept: "application/vnd.bentley.itwin-platform.v1+json",
// };

// // obtain export for specified export id
// const url = `https://api.bentley.com/mesh-export/${exportId}`;
// try {
// // TODO: this request is _really_ slow, like 7 whole second alone for me
// const response = await fetch(url, { headers });
// if (!response.ok) {
// if (response.status === 401) {
// console.error("Unauthorized, bad token, wrong scopes or headers bad");
// } else if (response.status === 404) {
// console.error("Requested export is not available", exportId);
// } else if (response.status === 429) {
// console.error("Too many requests");
// } else {
// console.log("Unknown request failure", response);
// }
// return undefined;
// }
// const result = JSON.parse(JSON.stringify(await response.json()));
// return result;
// } catch (err) {
// return undefined;
// }
// }

const delay = (ms) => new Promise((res) => setTimeout(res, ms));

// Grabbed mapping from the iTwin Viewer
const classes = {
2199023255632: "Building Roof",
2199023255694: "Building Wall",
2199023255696: "Building Window",
};

let selectedFeature;
let picking = false;

Sandcastle.addToggleButton(
"Per-feature selection",
false,
function (checked) {
picking = checked;
if (!picking) {
unselectFeature(selectedFeature);
}
},
"checkbox",
);

// Set up viewer
const viewer = new Cesium.Viewer("cesiumContainer", {
terrain: Cesium.Terrain.fromWorldTerrain(),
});
const scene = viewer.scene;
scene.globe.show = true;
scene.debugShowFramesPerSecond = true;

// HTML overlay for showing feature name on mouseover
const nameOverlay = document.createElement("div");
viewer.container.appendChild(nameOverlay);
nameOverlay.className = "backdrop";
nameOverlay.style.display = "none";
nameOverlay.style.position = "absolute";
nameOverlay.style.bottom = "0";
nameOverlay.style.left = "0";
nameOverlay.style["pointer-events"] = "none";
nameOverlay.style.padding = "4px";
nameOverlay.style.backgroundColor = "black";
nameOverlay.style.whiteSpace = "pre-line";
nameOverlay.style.fontSize = "12px";

function selectFeature(feature, movement) {
feature.color = Cesium.Color.clone(
Cesium.Color.fromCssColorString("#eeff41"),
feature.color,
);
selectedFeature = feature;

nameOverlay.style.display = "block";
nameOverlay.style.bottom = `${
viewer.canvas.clientHeight - movement.endPosition.y
}px`;
nameOverlay.style.left = `${movement.endPosition.x}px`;
const element = feature.getProperty("element");
const subcategory = feature.getProperty("subcategory");
const message = `
Element ID: ${element}
Subcategory: ${classes[subcategory] ?? subcategory}
Feature ID: ${feature.featureId}`;
nameOverlay.textContent = message;
}

function unselectFeature(feature) {
if (!Cesium.defined(feature)) {
return;
}

feature.color = Cesium.Color.clone(Cesium.Color.WHITE, feature.color);
selectedFeature = undefined;
nameOverlay.style.display = "none";
}

const statusOutput = document.querySelector("#status");
async function init() {
// TODO: just testing
// await Cesium.createIModel3DTileset.deleteExport(
// "3a627319-cf6e-4535-9499-da5320a69791",
// );
// console.log(await Cesium.createIModel3DTileset.checkForCesiumExport(imodelId));
// console.log(await Cesium.createIModel3DTileset.getExports(imodelId));

const changesetId = "";
const cesiumExport = await Cesium.createIModel3DTileset.checkForCesiumExport(
imodelId,
changesetId,
);
let exportId = cesiumExport?.id;
if (!Cesium.defined(cesiumExport)) {
exportId = await Cesium.createIModel3DTileset.createExportForModelId(
imodelId,
changesetId,
accessToken,
);
}

statusOutput.innerText = "Starting export";
// const exportId =
// knownExportId ??
// (await Cesium.createIModel3DTileset.createExportForModelId(
// imodelId,
// "",
// accessToken,
// ));

if (!exportId) {
console.error("No export id returned");
return;
}
console.log("Using export id", exportId);

const start = Date.now();
// let result = await getExport(exportId, accessToken);
// let status = result.export.status;
// while (status !== "Complete") {
// await delay(5000);
// result = await getExport(exportId, accessToken);
// status = result.export.status;
// console.log(`Export is ${status}`);

// if (Date.now() - start > 300000) {
// throw new Error("Export did not complete in time.");
// }
// }

// if (result.export.request.exportType !== "CESIUM") {
// console.error("Wrong export type", result.export.request.exportType);
// throw new Error(`Wrong export type ${result.export.request.exportType}`);
// }

// // This link is only valid 1 hour
// let tilesetUrl = result.export._links.mesh.href;
// const splitStr = tilesetUrl.split("?");
// // is there a cleaner way to do this?
// tilesetUrl = `${splitStr[0]}/tileset.json?${splitStr[1]}`;

// const tileset = await Cesium.Cesium3DTileset.fromUrl(tilesetUrl);
statusOutput.innerText = "Creating Tileset";
const tileset = await Cesium.createIModel3DTileset(exportId);
scene.primitives.add(tileset);
tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE;

statusOutput.innerText = "Loaded";

viewer.zoomTo(tileset);
console.log(`Finished in ${((Date.now() - start) / 1000).toString()} seconds`);

const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
if (!picking) {
return;
}
unselectFeature(selectedFeature);

const feature = scene.pick(movement.endPosition);

if (feature instanceof Cesium.Cesium3DTileFeature) {
selectFeature(feature, movement);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}

init();

//Sandcastle_End
Sandcastle.finishedLoading();
};
if (typeof Cesium !== "undefined") {
window.startupCalled = true;
window.startup(Cesium).catch((error) => {
"use strict";
console.error(error);
});
}
</script>
</body>
</html>
66 changes: 66 additions & 0 deletions packages/engine/Source/Core/ITwin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import Resource from "./Resource.js";

/**
* Default settings for accessing the iTwin platform.
*
* Keys can be created using the iModels share routes {@link https://developer.bentley.com/apis/imodels-v2/operations/create-imodel-share/}
*
* An ion access token is only required if you are using any ion related APIs.
* A default access token is provided for evaluation purposes only.
* Sign up for a free ion account and get your own access token at {@link https://cesium.com}
*
* @see IonResource
* @see IonImageryProvider
* @see IonGeocoderService
* @see createWorldImagery
* @see createWorldTerrain
* @namespace ITwin
*/
const ITwin = {};

/**
* Gets or sets the default iTwin access token.
*
* TODO: I'm not sure we can even do this kind of access token. Each route seems to need it's own scopes
* and we may not be able to guarantee this "top level token" has them all
* So far we use
* `mesh-export:read` for loading meshes GET /mesh-export(s)
* `mesh-export:modify` if we want to include a function to create an export
* `itwin-platform` if we want to use the iModel shares ourselves GET /imodels/{id}/shares
*
*
* @type {string|undefined}
*/
ITwin.defaultAccessToken = undefined;

/**
* Gets or sets the default Google Map Tiles API endpoint.
*
* @type {string|Resource}
* @default https://api.bentley.com
*/
ITwin.apiEndpoint = new Resource({
url: "https://api.bentley.com",
});

// TODO: this should only be needed if we have a way to generate really long term access tokens
// to sample data that is accessible to everyone
// ITwin.getDefaultTokenCredit = function (providedKey) {
// if (providedKey !== defaultAccessToken) {
// return undefined;
// }

// if (!defined(defaultTokenCredit)) {
// const defaultTokenMessage =
// '<b> \
// This application is using Cesium\'s default ion access token. Please assign <i>Cesium.Ion.defaultAccessToken</i> \
// with an access token from your ion account before making any Cesium API calls. \
// You can sign up for a free ion account at <a href="https://cesium.com">https://cesium.com</a>.</b>';

// defaultTokenCredit = new Credit(defaultTokenMessage, true);
// }

// return defaultTokenCredit;
// };

export default ITwin;
Loading

0 comments on commit 96c4d61

Please sign in to comment.