Skip to content

Commit 6ffaa7e

Browse files
authored
Merge pull request #12449 from CesiumGS/geo-features
Add support for iTwin Geospatial Features API
2 parents 4e6dde5 + ce96459 commit 6ffaa7e

File tree

4 files changed

+122
-9
lines changed

4 files changed

+122
-9
lines changed

Apps/Sandcastle/gallery/iTwin Feature Service.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@
6363
viewer.scene.camera.flyTo(birdsEyeView);
6464

6565
// Load feature service geojson files
66-
const points = await Cesium.ITwinData.createDataSourceForRealityDataId(
66+
const points = await Cesium.ITwinData.loadGeospatialFeatures(
6767
iTwinId,
68-
"57b975f6-fd92-42ba-8014-79911ed606d1",
68+
"2380dc1b-1dac-4709-aa5c-f6cb38c4e9f5",
6969
);
70-
const lines = await Cesium.ITwinData.createDataSourceForRealityDataId(
70+
const lines = await Cesium.ITwinData.loadGeospatialFeatures(
7171
iTwinId,
72-
"1099c53f-c568-48a3-a57c-0230a6f37229",
72+
"613d2310-4d01-43b7-bc92-873a2ca4a4a0",
7373
);
74-
const areas = await Cesium.ITwinData.createDataSourceForRealityDataId(
74+
const areas = await Cesium.ITwinData.loadGeospatialFeatures(
7575
iTwinId,
76-
"21eaf0d0-ab90-400f-97cf-adc455b29a78",
76+
"93e7ef51-5210-49f2-92a3-c7f6685e102f",
7777
);
7878

7979
// Add some styling to the lines and points to differentiate types
@@ -86,7 +86,7 @@
8686
Arrow_Marking: { color: Cesium.Color.YELLOW, icon: "car" },
8787
Road_Sign: { color: Cesium.Color.ORANGE, icon: "triangle" },
8888
};
89-
const type = entity.properties.Type?.getValue();
89+
const type = entity.properties.type?.getValue();
9090
if (Cesium.defined(type) && Cesium.defined(styleByType[type])) {
9191
const { color, icon } = styleByType[type];
9292
const canvas = await pinBuilder.fromMakiIconId(icon, color, 48);
@@ -103,7 +103,7 @@
103103
Turning_pocket: Cesium.Color.DEEPPINK,
104104
Yellow_Box: Cesium.Color.GOLD,
105105
};
106-
const type = entity.properties.Type?.getValue();
106+
const type = entity.properties.type?.getValue();
107107
if (Cesium.defined(type) && Cesium.defined(lineColorsByType[type])) {
108108
entity.polyline.material = lineColorsByType[type];
109109
}

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99
- Changed behavior of `DataSourceDisplay.ready` to always stay `true` once it is initially set to `true`. [#12429](https://github.com/CesiumGS/cesium/pull/12429)
1010

11+
#### Additions :tada:
12+
13+
- Add `ITwinData.loadGeospatialFeatures(iTwinId, collectionId)` function to load data from the [Geospatial Features API](https://developer.bentley.com/apis/geospatial-features/operations/get-features/) [#12449](https://github.com/CesiumGS/cesium/pull/12449)
14+
1115
#### Fixes :wrench:
1216

1317
- Fixed error when resetting `Cesium3DTileset.modelMatrix` to its initial value. [#12409](https://github.com/CesiumGS/cesium/pull/12409)

packages/engine/Source/Scene/ITwinData.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import RuntimeError from "../Core/RuntimeError.js";
66
import Check from "../Core/Check.js";
77
import KmlDataSource from "../DataSources/KmlDataSource.js";
88
import GeoJsonDataSource from "../DataSources/GeoJsonDataSource.js";
9+
import DeveloperError from "../Core/DeveloperError.js";
910

1011
/**
1112
* Methods for loading iTwin platform data into CesiumJS
@@ -154,7 +155,7 @@ ITwinData.createTilesetForRealityDataId = async function (
154155
*
155156
* @throws {RuntimeError} if the type of reality data is not supported by this function
156157
*/
157-
ITwinData.createDataSourceForRealityDataId = async function loadRealityData(
158+
ITwinData.createDataSourceForRealityDataId = async function (
158159
iTwinId,
159160
realityDataId,
160161
type,
@@ -205,4 +206,49 @@ ITwinData.createDataSourceForRealityDataId = async function loadRealityData(
205206
return KmlDataSource.load(tilesetAccessUrl);
206207
};
207208

209+
/**
210+
* Load data from the Geospatial Features API as GeoJSON.
211+
*
212+
* @param {string} iTwinId The id of the iTwin to load data from
213+
* @param {string} collectionId The id of the data collection to load
214+
* @param {number} [limit=10000] number of items per page, must be between 1 and 10,000 inclusive
215+
* @returns {Promise<GeoJsonDataSource>}
216+
*/
217+
ITwinData.loadGeospatialFeatures = async function (
218+
iTwinId,
219+
collectionId,
220+
limit,
221+
) {
222+
//>>includeStart('debug', pragmas.debug);
223+
Check.typeOf.string("iTwinId", iTwinId);
224+
Check.typeOf.string("collectionId", collectionId);
225+
if (defined(limit)) {
226+
Check.typeOf.number("limit", limit);
227+
Check.typeOf.number.lessThanOrEquals("limit", limit, 10000);
228+
Check.typeOf.number.greaterThanOrEquals("limit", limit, 1);
229+
}
230+
if (!defined(ITwinPlatform.defaultAccessToken)) {
231+
throw new DeveloperError("Must set ITwinPlatform.defaultAccessToken first");
232+
}
233+
//>>includeEnd('debug')
234+
235+
const pageLimit = limit ?? 10000;
236+
237+
const tilesetUrl = `${ITwinPlatform.apiEndpoint}geospatial-features/itwins/${iTwinId}/ogc/collections/${collectionId}/items`;
238+
239+
const resource = new Resource({
240+
url: tilesetUrl,
241+
headers: {
242+
Authorization: `Bearer ${ITwinPlatform.defaultAccessToken}`,
243+
Accept: "application/vnd.bentley.itwin-platform.v1+json",
244+
},
245+
queryParameters: {
246+
limit: pageLimit,
247+
client: "CesiumJS",
248+
},
249+
});
250+
251+
return GeoJsonDataSource.load(resource);
252+
};
253+
208254
export default ITwinData;

packages/engine/Specs/Scene/ITwinDataSpec.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,4 +346,67 @@ describe("ITwinData", () => {
346346
expect(geojsonSpy).not.toHaveBeenCalled();
347347
});
348348
});
349+
350+
describe("loadGeospatialFeatures", () => {
351+
let geojsonSpy;
352+
beforeEach(() => {
353+
geojsonSpy = spyOn(GeoJsonDataSource, "load");
354+
});
355+
356+
it("rejects with no iTwinId", async () => {
357+
await expectAsync(
358+
// @ts-expect-error
359+
ITwinData.loadGeospatialFeatures(undefined),
360+
).toBeRejectedWithDeveloperError(
361+
"Expected iTwinId to be typeof string, actual typeof was undefined",
362+
);
363+
});
364+
365+
it("rejects with no collectionId", async () => {
366+
await expectAsync(
367+
// @ts-expect-error
368+
ITwinData.loadGeospatialFeatures("itwin-id-1", undefined),
369+
).toBeRejectedWithDeveloperError(
370+
"Expected collectionId to be typeof string, actual typeof was undefined",
371+
);
372+
});
373+
374+
it("rejects with limit < 1", async () => {
375+
await expectAsync(
376+
ITwinData.loadGeospatialFeatures("itwin-id-1", "collection-id-1", 0),
377+
).toBeRejectedWithDeveloperError(
378+
"Expected limit to be greater than or equal to 1, actual value was 0",
379+
);
380+
});
381+
382+
it("rejects with limit > 10000", async () => {
383+
await expectAsync(
384+
ITwinData.loadGeospatialFeatures(
385+
"itwin-id-1",
386+
"collection-id-1",
387+
20000,
388+
),
389+
).toBeRejectedWithDeveloperError(
390+
"Expected limit to be less than or equal to 10000, actual value was 20000",
391+
);
392+
});
393+
394+
it("rejects with no default access token set", async () => {
395+
ITwinPlatform.defaultAccessToken = undefined;
396+
await expectAsync(
397+
ITwinData.loadGeospatialFeatures("itwin-id-1", "collection-id-1"),
398+
).toBeRejectedWithDeveloperError(
399+
"Must set ITwinPlatform.defaultAccessToken first",
400+
);
401+
});
402+
403+
it("creates a GeoJsonDataSource from the constructed blob url if the type is GeoJSON", async () => {
404+
await ITwinData.loadGeospatialFeatures("itwin-id-1", "collection-id-1");
405+
406+
expect(geojsonSpy).toHaveBeenCalledTimes(1);
407+
expect(geojsonSpy.calls.mostRecent().args[0].url).toEqual(
408+
"https://api.bentley.com/geospatial-features/itwins/itwin-id-1/ogc/collections/collection-id-1/items?limit=10000&client=CesiumJS",
409+
);
410+
});
411+
});
349412
});

0 commit comments

Comments
 (0)