Namespace data

xeokit Semantic Data Model


The SDK's buildable, queryable, importable and exportable semantic data model


Overview

The xeokit SDK uses a generic entity-relationship data graph to manage model semantics. This graph includes entities, properties, and relationships and is compatible with both the browser and NodeJS. It serves as a versatile tool for generating models, converting between model formats, and navigating content within the model viewer.

In more detail, the xeokit SDK provides a Data container class that holds DataModels consisting of DataObjects, PropertySets, and Relationships, as shown in the diagram below.


Various model file formats can be imported into DataModels using methods such as loadGLTF, loadLAS, loadCityJSON, loadWebIFC, loadDotBIM, and loadXGF, while DataModels can be exported to various formats, using methods such as saveDotBIM.

To programmatically build DataModels, builder methods such as Data.createModel, DataModel.createObject, DataModel.createPropertySet, and DataModel.createRelationship can be employed. DataObjects can be queried using the Data.searchObjects method, and semantic data can be attached to model representations by using it alongside SceneModel.

It's important to note that DataObjects and PropertySets are global, created on their DataModels but stored globally on the Data. Additionally, DataModels automatically reuse DataObjects and PropertySets wherever they're already created by other DataModels. Finally, DataObjects can have Relationships with other DataObjects in different DataModels.


Installation

npm install @xeokit/sdk

Usage

We will start with an example where we create a DataModel using a single parameter object of type DataModelParams. The DataModel we create will define a simple piece of furniture - a table consisting of a tabletop and four legs. We will then query the data model to retrieve all the DataObjects within it.

To achieve this, we will create a DataModel that contains six DataObjects: one for the table, one for the tabletop, and one for each of the four legs. We will also define Relationships to connect the DataObjects into an aggregation hierarchy, and we will assign Properties to the DataObjects to give them attributes such as height and weight.

To give the DataObjects and Relationships semantic meaning, we will assign them types from one of the SDK's bundled data type sets, @xeokit/basictypes. This set of types classifies each DataObject as a BasicEntity and each Relationship as a BasicAggregation.

It's worth noting that in a real-world scenario, we would likely use a more complex set of data types, such as @xeokit/ifctypes. However, we cannot mix different sets of data types within our Data, as traversals of the DataObjects with Data.searchObjects must be guided uniformly by the same set of types across all the DataObjects and Relationships in the graph.

To create our DataModel, we will use the following code, which creates a new Data object and then creates a DataModel from a set of objects, relationships, and property sets. The SDKError class is used to handle errors that may occur during the process:

import { SDKError } from "@xeokit/sdk/core";
import { Data } from "@xeokit/sdk/data";
import * as basicTypes from "@xeokit/sdk/basictypes/basicTypes";

const myData = new Data({});

const myDataModel = myData.createModel({
id: "myTableModel",
objects: [
{
id: "table",
type: basicTypes.BasicEntity,
name: "Table",
propertySetIds: ["tablePropertySet"],
},
{
id: "redLeg",
name: "Red table Leg",
type: basicTypes.BasicEntity,
propertySetIds: ["legPropertySet"],
},
{
id: "greenLeg",
name: "Green table leg",
type: basicTypes.BasicEntity,
propertySetIds: ["legPropertySet"],
},
{
id: "blueLeg",
name: "Blue table leg",
type: basicTypes.BasicEntity,
propertySetIds: ["legPropertySet"],
},
{
id: "yellowLeg",
name: "Yellow table leg",
type: basicTypes.BasicEntity,
propertySetIds: ["legPropertySet"],
},
{
id: "tableTop",
name: "Purple table top",
type: basicTypes.BasicEntity,
propertySetIds: ["tableTopPropertySet"],
},
],
relationships: [
{
type: basicTypes.BasicAggregation,
relatingObjectId: "table",
relatedObjectId: "tableTop",
},
{
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "redLeg",
},
{
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "greenLeg",
},
{
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "blueLeg",
},
{
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "yellowLeg",
},
],
propertySets: [
{
id: "tablePropertySet",
originalSystemId: "tablePropertySet",
name: "Table properties",
type: "",
properties: [
{
name: "Weight",
value: 5,
type: "",
valueType: "",
description: "Weight of the thing",
},
{
name: "Height",
value: 12,
type: "",
valueType: "",
description: "Height of the thing",
},
],
},
{
id: "legPropertySet",
originalSystemId: "legPropertySet",
name: "Table leg properties",
type: "",
properties: [
{
name: "Weight",
value: 5,
type: "",
valueType: "",
description: "Weight of the thing",
},
{
name: "Height",
value: 12,
type: "",
valueType: "",
description: "Height of the thing",
},
],
},
],
});

if (myDataModel instanceof SDKError) {
console.log(myDataModel.message);
} else {
myDataModel.build();
}

In our second example, we'll create our DataModel again, this time instantiating each PropertySet, Property, DataObject and Relationship individually, using the DataModel's builder methods.

import {SDKError} from "@xeokit/sdk/core";
import {Data} from "@xeokit/sdk/data";
import * as basicTypes from "@xeokit/sdk/basictypes/basicTypes";

const data = new Data();

const dataModel = data.createModel({ // DataModel | SDKError
id: "myTableModel"
});

if (dataModel instanceof SDKError) {
console.log(dataModel.message);

} else {

const tablePropertySet = dataModel.createPropertySet({ // PropertySet | SDKError
id: "tablePropertySet",
name: "Table properties",
type: "",
properties: [ // Property[]
{
name: "Weight",
value: 5,
type: "",
valueType: "",
description: "Weight of the thing"
},
{
name: "Height",
value: 12,
type: "",
valueType: "",
description: "Height of the thing"
}
]
});

if (tablePropertySet instanceof SDKError) {
console.log(tablePropertySet.message);
}

const legPropertySet = dataModel.createPropertySet({
id: "legPropertySet",
name: "Table leg properties",
type: "",
properties: [
{
name: "Weight",
value: 5,
type: "",
valueType: "",
description: "Weight of the thing"
},
{
name: "Height",
value: 12,
type: "",
valueType: "",
description: "Height of the thing"
}
]
});

const myTableObject = dataModel.createObject({ // DataObject | SDKError
id: "table",
type: basicTypes.BasicEntity,
name: "Table",
propertySetIds: ["tablePropertySet"]
});

if (myTableObject instanceof SDKError) {
console.log(myTableObject.message);
}

dataModel.createObject({
id: "redLeg",
name: "Red table Leg",
type: basicTypes.BasicEntity,
propertySetIds: ["tableLegPropertySet"]
});

dataModel.createObject({
id: "greenLeg",
name: "Green table leg",
type: basicTypes.BasicEntity,
propertySetIds: ["tableLegPropertySet"]
});

dataModel.createObject({
id: "blueLeg",
name: "Blue table leg",
type: basicTypes.BasicEntity,
propertySetIds: ["tableLegPropertySet"]
});

dataModel.createObject({
id: "yellowLeg",
name: "Yellow table leg",
type: "leg",
propertySetIds: ["tableLegPropertySet"]
});

dataModel.createObject({
id: "tableTop",
name: "Purple table top",
type: basicTypes.BasicEntity,
propertySetIds: ["tableTopPropertySet"]
});

const myRelationship = dataModel.createRelationship({
type: basicTypes.BasicAggregation,
relatingObjectId: "table",
relatedObjectId: "tableTop"
});

if (myRelationship instanceof SDKError) {
console.log(myRelationship.message);
}

dataModel.createRelationship({
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "redLeg"
});

dataModel.createRelationship({
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "greenLeg"
});

dataModel.createRelationship({
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "blueLeg"
});

dataModel.createRelationship({
type: basicTypes.BasicAggregation,
relatingObjectId: "tableTop",
relatedObjectId: "yellowLeg"
});

dataModel.build()
.then(()=>{
// Ready for action
})
.catch((sdkError) => {
console.error(sdkError.message);
});
}

With our DataModel built, we'll now use the Data.searchObjects method to traverse it to fetch the IDs of the DataObjects we find on that path.

One example of where we use this method is to query the aggregation hierarchy of the DataObjects for building a tree view of an IFC element hierarchy.

const resultObjectIds = [];

data.searchObjects({
startObjectId: "table",
includeObjects: [basicTypes.BasicEntity],
includeRelated: [basicTypes.BasicAggregation],
resultObjectIds
});

// resultObjectIds == ["table", "tableTop", "redLeg", "greenLeg", "blueLeg", "yellowLeg"];

In our next example, we'll demonstrate how to traverse the DataObjects along their Relationships. We'll start at the root DataObject and visit all the DataObjects we encounter along the outgoing Relationships.

const table = data.objects["table"];

const relations = table.related[basicTypes.BasicAggregation];

for (let i = 0, len = relations.length; i < len; i++) {

const relation = relations[i];
const dataObject = relation.related;

//..
}

 const dataModelParams = dataModel.toParams();

const dataModel2 = sce.createDataModel({
id: "myDataModel2"
});

dataModel2.fromParams(dataModelParams);

dataModel2.build();

 dataModel2.destroy();

We can also import DataModels from several file formats.

For example, let's use loadDotBIM to load a .BIM file into a new SceneModel and DataModel:

const sceneModel3 = scene.createModel({
id: "myModel3"
});

const dataModel3 = data.createModel({
id: "myModel3"
});

fetch("model.json").then(response => {

response.json().then(fileData => {

loadDotBIM({
fileData,
sceneModel3,
dataModel3
}).then(() => {

sceneModel3.build();
dataModel3.build();

}).catch(err => {

sceneModel3.destroy();
dataModel3.destroy();

console.error(`Error loading CityJSON: ${err}`);
});

}).catch(err => {
console.error(`Error creating JSON from fetch response: ${err}`);
});

}).catch(err => {
console.error(`Error fetching CityJSON file: ${err}`);
});

Classes

Data
DataModel
DataObject
Property
PropertySet
Relationship

Interfaces

DataModelContentParams
DataModelParams
DataObjectParams
PropertyParams
PropertySetParams
RelationshipParams
SearchParams

Functions

loadDataModel