You can plug properties from external data sources into a TileSet3DLayer. This means that you can populate the model in your layer with properties that aren’t available inside the OGC 3D Tiles data. You can then use those plugged-in properties in expressions for styling. This leads to the restriction that properties can only be numerical.

For a demonstration of the styling of plugged-in feature properties in a 3D Tiles layer, see the "Selection on OGC 3D Tiles data" sample on the Samples page.

To link the plugged-in properties to features of your model, you need to know the name of a unique numerical feature identifier:

  • For models generated with the Binz converter, this unique identifier is FeatureID.

  • For models generated via the 3D Tiles Processing Engine, this unique identifier is ObjectId.

Typically, you use that same identifier as idProperty for selection purposes too. See Selecting mesh data for more information.

Defining properties with a PropertiesDescriptor

If you want to add properties to your layer, you must describe them at layer construction time.

Also see the TileSet3DLayer reference documentation.

Program: Adding properties to your layer.
const ogc3dTilesLayer = new TileSet3DLayer(model, {
  selectable: true,
  idProperty: "ObjectId",
  properties: {
    "Temperature": {
      default: -100,
    },
    "Maintenance": {
      default: 0,
    }
  }
});

As you can see in Program: Adding properties to your layer, you must provide a numerical default value for each property you are describing. You can only start pushing real property values to features after you’ve finished creating your layer. The default value serves for all feature properties that you didn’t update with actual values yet.

Because the FeatureID and ObjectId identifiers are generated in the process of converting your dataset to 3D Tiles, you may not be able to link properties from external data sources directly through these identifiers. Linking the Binz FeatureID to external properties and Linking the 3D Tiles Processing Engine ObjectId to external properties explain how you can link these identifiers to more usable (possibly non-numerical) identifiers.

Updating properties with PropertiesUpdate

You can add properties from external sources of both static and dynamic data. Static data typically offers information about materials, floor levels, feature types. Dynamic data typically offers changing information about temperature, pressure, fluid levels, or even about the maintenance status of some parts of the structure. You can handle both types of data in exactly the same way.

Pushing property updates to the layer

To get the correct values in the layer, you can use the updateProperties function of the layer. For static data, you may have to do this only once. For dynamic data, you can repeat the update each time your data changes.

For an update, you must give the name of the property you are updating, the IDs for which you are sending an update, and the new values. You add a single value if you want to tell the layer that a part of your structure is in maintenance, for example. You add more than one value, one for each ID, when you are pushing temperature values for parts of the structure fitted with a temperature sensor, for example.

Program: Updating property values for properties on your layer
ogc3dTilesLayer.updateProperties({
  "Temperature": {
    ids: [1, 2, 3],
    //for each id we give a new TemperatureValues, ids that are not updated will remain as is
    values: [25, 27, 28],
  },
  "Maintenance": {
    ids: [9, 10, 12],
    //for each id we provide the same update-value, ids that are not updated are reset to the default value
    value: 1,
  }
})

Waiting for the layer to request updates

To ensure performance when you are dealing with large models with a lot of features, it’s useful to push data for features in view only. This approach is similar to what LuciadRIA does with the geometry inside the tiles. Whenever your view changes, it loads new tiles, and discards tiles that are going out of view.

Once you have configured a layer with properties, it emits a FeaturesInViewChange event whenever features come into view or go out of view. This event comes with two attributes: a list of IDs of features that came into view, and one with IDs of features that went out of view. Using that event, you can always track which features are in the view, and thus, for which features you may need to update the property values. You can also erase all property values of the features that went out of view.

Program: Subscribing to the FeaturesInViewChange event and taking action
ogc3dTilesLayer.on("FeaturesInViewChange", (idsEnteredView, idsLeftView) => {
  // Possible things to do:
  // - subscribe to the idsEnteredView
  // - remove subscription for idsLeftView
  // - keep track of all idsInView (for updates without this callback)
  // - erase values for idsLeftView (note: all values for all properties are erased at once)
  ogc3dTilesLayer.eraseProperties(idsLeftView);
  // - update values for idsEnteredView
});

When you convert Binz data to OGC 3D Tiles, each feature gets a unique numerical FeatureID, counting up from 0 to the total number of features in the dataset. Next to generating that unique identifier, the conversion process also integrates the original metadata from the Binz file in the OGC 3D Tiles.

The original metadata always contains a Linkage identifier, a unique identifier composed of 4 large numbers. This identifier is generated by Smart Interop Publisher while it creates the Binz file from other input file types. It uses this same identifier to populate a *.drv file with all possible attributes from the original dataset. This Linkage identifier is a good entry point to link your features with other, external properties. To get the Linkage for each FeatureID, you must generate a GeoJSON file when you are converting Binz to 3D Tiles. To generate a GeoJSON file, run the Binz converter script with the -g command parameter. See Converting Binz data to OGC 3D Tiles for more information.

In this concrete example, we start from a .binz file and an .rdv file. They originated from an IFC file.

Original files:

  • someSample.binz: contains the data geometry

  • someSample.drv: contains the data attributes

Example of one object or feature with GlobalId 3KnTnKB5TBjup7D4Kz$zFO inside the .drv file. The generated Linkage identifier is 19868 16312 -30765 -23112:
lbl{ 19868  16312  -30765 -23112
    text {
        GlobalId: 3KnTnKB5TBjup7D4Kz$zFO
        Name: SomeName
        ObjectType: SomeType
        Tag: 20504600
        Layer Name: A-FLOR-HRAL-OTLN
        ... other properties
    }
}

After converting the Binz file to OGC 3D Tiles with the GeoJSON file option, you get these files:

  • tileset.json

  • features.geojson

Output: the part of the GeoJSON file describing the feature with the linkage 19868 16312 -30765 -23112. It received FeatureID 1893 during conversion.
"features" : [ {
"type" : "Feature",
"geometry" : {
"type" : "Polygon",
"coordinates" : [  ]
},
"properties" : {
"FeatureID" : 1893,
"Linkage" : "19868  16312  -30765 -23112",
"aabb" : "{ \"max\": [ -19.166865, -21.212387, 1.074825 ],\"min\": [ -20.572663, -22.189715, 1.032661 ]}",
"minZ" : 21.032732863910496,
"maxZ" : 21.074896863910496
}
} ]

When you convert OBJ data to OGC 3D Tiles with the 3D Tiles Processing Engine, each feature gets a unique numerical ObjectId, counting up from 0 to the total number of features in the dataset. Next to generating that unique identifier, the conversion process also integrates some metadata from the OBJ file in the OGC 3D Tiles.

Most of the time, you will find an ObjectName, also a unique identifier, but a string. This ObjectName is a good link to your external properties. To get the ObjectName for each generated ObjectId, you must use a custom ILcd3DTilesProcessorMetadataMapper in the TLcd3DTilesProcessorBuilder. This MetadataMapper is meant to be used for integrating external properties in the 3D Tiles, but you can also use it to generate a JSON file linking ObjectId to ObjectName.

For more information about the 3D Tiles Processing Engine, see Pre-processing 3D Meshes to OGC 3D Tiles.

In this concrete example, we start from an IFC file.

Original files:

  • someSample.ifc: containing geometry

  • someSample.xml: containing attributes

Example for one object or feature with ID 0IVRNVgp1F_PzGPAeVm4N_ inside the XML file:
<IfcWallStandardCase id="0IVRNVgp1F_PzGPAeVm4N_" Name="Basic Wall:ANTO_21_WA_iso hard 93:1496853" ObjectType="Basic Wall:ANTO_21_WA_iso hard 93" ObjectPlacement="0.92448 0.381232 0 0 -0.381232 0.92448 0 0 0 0 1 0 34651.9 35501.8 29079 1" Tag="1496853">
  <IfcElementQuantity xlink:href="#2BAVFqwMb5hfPB51ojzXo8"/>
  <IfcPropertySet xlink:href="#1QJS37L09A9OowaWA5$B7n"/>
  <IfcPropertySet xlink:href="#3RFcCTwWz14xNkgaH0Qn_4"/>
  <IfcPropertySet xlink:href="#2sbE7MDLT3XgapNq8dhlDn"/>
  <IfcPropertySet xlink:href="#3VXZJNloP6fQB4mMoi84fT"/>
  <IfcPropertySet xlink:href="#0IVRNVgp1F_PzGRrSVm4N_"/>
  <IfcPropertySet xlink:href="#1gdLWQFYHCEQiI6WlFGeHj"/>
  <IfcWallType xlink:href="#11y0yv0ujCBeC5IKFuwinh"/>
  <IfcPresentationLayerAssignment xlink:href="#A-WALL-____-OTLN"/>
  <IfcMaterialLayerSetUsage xlink:href="#IfcMaterialLayerSetUsage_5179"/>
</IfcWallStandardCase>

After converting the IFC file to OBJ, using any conversion tool, you get this file:

  • someSample.obj: containing data geometry and the objectName

Output: a small part for the same object inside the .obj file:
g 0IVRNVgp1F_PzGPAeVm4N_
s 1
v 51.798 42.5220633920099 29.079
... a number of vertices, triangles, ...

The custom MetadataMapper you must use with the 3D Tiles Processing Engine could look like this:

Program: using the 3D Tiles Processing Engine in LuciadLightspeed with a custom MetadataMapper
public class CustomMeshup {

  public static void main(String[] args){
      try {
        CustomMapper mapper = new CustomMapper();
        TLcd3DTilesProcessorBuilder.newBuilder()
                                   .addInputFiles("someFile.obj")
                                   .outputPath("someOutputPath")
                                   .metadataMapper(mapper)
                                   .process()
                                   .get();
        mapper.writeJsonFile("somePath", "someSample.json");
      } catch (InterruptedException | ExecutionException | IOException aE) {
        aE.printStackTrace();
      }
    }
  }

  private static class CustomMapper implements ILcd3DTilesProcessorMetadataMapper {

    private static String OBJ_ID =  TLcd3DTilesProcessorDataTypes.OBJECT_ID_PROPERTY;
    private static String OBJ_NAME = TLcd3DTilesProcessorDataTypes.OBJECT_NAME_PROPERTY;

    private List<FeatureData> fFeatures;

    CustomMapper(){
      fFeatures = new ArrayList<>();
    }

    @Override
    public TLcdDataType getDataType(TLcdDataType aDataType) {
      return aDataType;
    }

    @Override
    public ILcdDataObject getDataObject(ILcdDataObject aDataObject) {
      fFeatures.add(new FeatureData((String) aDataObject.getValue(OBJ_ID),
                                  (String) aDataObject.getValue(OBJ_NAME)));
      return aDataObject;
    }

    public void writeJsonFile(String path, String fileName) throws IOException {
      new ObjectMapper().writeValue(new File(path+fileName), fFeatures);
    }
  }

  private static class FeatureData {
    private String fObjectId;
    private String fObjectName;

    FeatureData(String aObjectId, String aObjectName){
      this.fObjectId = aObjectId;
      this.fObjectName = aObjectName;
    }

    public String getObjectId() {
      return fObjectId;
    }
    public void setObjectId(String aObjectId) {
      this.fObjectId = aObjectId;
    }
    public String getObjectName() {
      return fObjectName;
    }
    public void setObjectName(String aObjectName) {
      this.fObjectName = aObjectName;
    }
  }
}

The 3D Tiles Processing Engine generates these files with the custom MetadataMapper:

  • tileset.json: containing data geometry, generated objectId, and objectName

  • someSample.json: a file linking objectId to objectName

Output: the relevant part of the someSample.json file for the feature under investigation
[{"ObjectId":"1492","ObjectName":"0IVRNVgp1F_PzGPAeVm4N_"}]

You can use this objectId 1492 to add properties for the feature with ID 0IVRNVgp1F_PzGPAeVm4N_ — originating from the someSample.xml file — to the layer showing this model.

You can add numerical properties only, so you can add a layer property by number, not by name. Afterward, you can link the number back to the original name.