Use Data Visualization Extension multiple times

I have two separate extensions visualizing different "datapoints" from different domains (measurements like temperature, humidity, ... and image locations). These extensions should remain separated (and should not know each other).

There is no problem when using the extensions independent of each other. But problems occur, if both extensions are used at the same time (for the same model) in the Forge Viewer.

Both extensions try to get an existing instance of the DataVis-extension and load the extension if it is not available (call to viewer.loadExtension in each custom extension no difference). The extensions create ViewableData and add viewables to it (sharing a single instance of ViewableData makes no difference).

After adding viewables await viewableData.finish() is called and the addViewables-method of the DataVis-extension is called.

One of the main problems is, that method changeOcclusion changes the occlusion only of the viewables which were added last. The other viewables remain visible at any time. Probably because the pointMaterial in the DataVis-extension is overwritten any time the addViewables extension is called.

Is there a way to instantiate the extension multiple times, so that it is guaranteed that there are no side-effects when using it from within different custom extensions? Or maybe other mechanisms?


After consulting our engineering team, we submitted an issue report, LMV-6574, for tracking this issue. Please take note of the LMV prefixed id for tracking. We're welcome to ask for updates in the future by sending the issue id to forge (DOT) help (AT) autodesk (DOT) com.

However, we don't want to stop your development, so here is a workaround.

As I mentioned in the above comments area, SpriteViewable's constructor accepts an argument, ViewableStyles, that is used to set up the sprite icon. So, you don't need to call DataVisualization.addViewables(data) twice. Before finishing the ViewableData, you can add viewables with different ViewableStyles without a doubt.

Back to your use case, you want to reuse the DataVisualization extension for different data sources. To do so, I would advise you to store your device (sensor) data separately (e.g. device container). When you need to add/remove devices, just modify the device container, clear viewables, and then add new viewalbe data among your device container.

Here are some code snippets demonstrating this idea:

  • Example code for initializing:
let sensorStyleDefinitions = {
    co2: {
        url: "http://localhost:3000/assets-1/images/co2.svg",
        color: 0xffffff,
    },
    temperature: {
        url: "http://localhost:3000/assets-1/images/thermometer.svg",
        color: 0xffffff,
    },
    default: {
        url: "http://localhost:3000/assets-1/images/circle.svg",
        color: 0xffffff,
    },
};

// Create model-to-style map from style definitions.
let styleMap = {};

Object.entries(sensorStyleDefinitions).forEach(([type, styleDef]) => {
    styleMap[type] = new Autodesk.DataVisualization.Core.ViewableStyle(
        Autodesk.DataVisualization.Core.ViewableType.SPRITE,
        new THREE.Color(styleDef.color),
        styleDef.url
    );
});

let devices = [
    {
        id: "Hall I",
        position: {
            x: -14.297511041164398,
            y: -77.6432056427002,
            z: 11.31889820098877,
        },
        type: "temperature",
        sensorTypes: ["temperature"],
    },
    {
        id: "Hall IV",
        position: {
            x: 60.53697395324707,
            y: -74.6432056427002,
            z: 11.31889820098877,
        },
        type: "co2",
        sensorTypes: ["co2"],
    },
];

const viewableData = new Autodesk.DataVisualization.Core.ViewableData();
viewableData.spriteSize = 16;

// Add viewables
devices.forEach((device, index) => {
    const style = styleMap[device.type] || styleMap["default"];
    const viewable = new Autodesk.DataVisualization.Core.SpriteViewable(
        device.position,
        style,
        index + 1
    );

    viewableData.addViewable(viewable);
});

await viewableData.finish();
dataVizExt.addViewables(viewableData);

  • Example code for adding a device(sensor)
devices.push({
    id: "Hall XII",
    position: {
        x: -15,
        y: -70,
        z: 50,
    },
    type: "temperature",
    sensorTypes: ["temperature"],
});

// Remove existing sprites
dataVizExt.removeAllViewables();

const viewableData = new Autodesk.DataVisualization.Core.ViewableData();
viewableData.spriteSize = 16;

// re-add viewables
devices.forEach((device, index) => {
    const style = styleMap[device.type] || styleMap["default"];
    const viewable = new Autodesk.DataVisualization.Core.SpriteViewable(
        device.position,
        style,
        index + 1
    );

    viewableData.addViewable(viewable);
});

await viewableData.finish();
dataVizExt.addViewables(viewableData);
  • Example code for removing a device(sensor)
devices = devices.splice(1, 1);

// Remove existing sprites
dataVizExt.removeAllViewables();

const viewableData = new Autodesk.DataVisualization.Core.ViewableData();
viewableData.spriteSize = 16;

// re-add viewables
devices.forEach((device, index) => {
    const style = styleMap[device.type] || styleMap["default"];
    const viewable = new Autodesk.DataVisualization.Core.SpriteViewable(
        device.position,
        style,
        index + 1
    );

    viewableData.addViewable(viewable);
});

await viewableData.finish();
dataVizExt.addViewables(viewableData);