how to add tooltip to autodesk forge sprite viewable?

308 Views Asked by At

i am trying to add tooltip and data over hover of a sprite inside using the data visualization api. I have achieved adding the sprite viewable, but it is not mentioned as to how we add a tooltip on hover. Docs just mention there is a DATAVID_OBJECT_HOVERING event that is triggered to the viewer object, and we can add a callback on it. Also the code is not clear. Attaching hyperion demo's code below which only adds a callback to the onhover event on sprite and no mention of tooltip and data passed to it. Where in the code that is present as in this website i am seeing the tooltip with some data. this is the link https://hyperion.autodesk.io/ and code is this https://github.dev/Autodesk-Forge/forge-dataviz-iot-reference-app

async function onModelLoaded(viewer) {
        const dataVizExt = viewer.getExtension("Autodesk.DataVisualization");
        const DATAVIZEXTN = Autodesk.DataVisualization.Core;
        var styleMap = {};

        // Create model-to-style map from style definitions.
        Object.entries(SensorStyleDefinitions).forEach(([type, styleDef]) => {
            styleMap[type] = new DATAVIZEXTN.ViewableStyle(
                DATAVIZEXTN.ViewableType.SPRITE,
                new THREE.Color(styleDef.color),
                styleDef.url
            );
        });

        const viewableData = new DATAVIZEXTN.ViewableData();
        viewableData.spriteSize = 16;
        let startId = 1;

        devices.forEach((device) => {
            let style = styleMap[device.type] || styleMap["default"];
            const viewable = new DATAVIZEXTN.SpriteViewable(device.position, style, startId);
            viewableData.addViewable(viewable);
            startId++;
        });
        await viewableData.finish();
        dataVizExt.addViewables(viewableData);

        /**
         * Called when a user clicks on a Sprite Viewable
         * @param {Event} event
         */
        function onItemClick(/* event */) {}

        /**
         *  Called when a user hovers over a Sprite Viewable
         * @param {Event} event
         */
        function onItemHovering(event) {
            console.log("Show tooltip here", event.dbId);
        }

        const DataVizCore = Autodesk.DataVisualization.Core;
        viewer.addEventListener(DataVizCore.MOUSE_CLICK, onItemClick);
        viewer.addEventListener(DataVizCore.MOUSE_HOVERING, onItemHovering);
    }
1

There are 1 best solutions below

4
Eason Kang On

May check out my BIM360SensorTooltip at forge-bim360-assets.viewer/BIM360IotConnectedExtension.js#L300. The core concept is like the following

  1. Create your custom tooltip by JavaScript DOM API.
  2. To add a tooltip for a sprite, just add some codes in your onItemHovering event to create/show your custom tooltip.

Here is an example of my BIM360SensorTooltip (removed BIM360 prefix):

class SensorTooltip extends THREE.EventDispatcher {
    constructor(parent) {
        super();

        this.parent = parent;
        this.init();
    }

    get viewer() {
        return this.parent.viewer;
    }

    get dataVizTool() {
        return this.parent.dataVizTool;
    }

    init() {
        const container = document.createElement('div');
        container.classList.add('bim360-sensor-tooltip');
        this.container = container;

        const bodyContainer = document.createElement('div');
        bodyContainer.classList.add('bim360-sensor-tooltip-body');
        container.appendChild(bodyContainer);
        this.bodyContainer = bodyContainer;

        bodyContainer.innerHTML = 'No Data';

        this.viewer.container.appendChild(container);
    }

    setVisible(visible) {
        if (visible) {
            this.bodyContainer.classList.add('visible');
        } else {
            this.bodyContainer.classList.remove('visible');
        }
    }

    setPosition(point) {
        const contentRect = this.bodyContainer.getBoundingClientRect();
        const offsetX = contentRect.width / 2;
        const spriteSize = this.dataVizTool.viewableData.spriteSize;
        const offsetY = contentRect.height + 0.7 * spriteSize / this.parent.viewer.getWindow().devicePixelRatio;

        const pos = new THREE.Vector3(
            point.x - offsetX,
            point.y - offsetY,
            0
        );

        this.container.style.transform = `translate3d(${pos.x}px, ${pos.y}px, ${pos.z}px)`;
    }

    setPositionByWordPoint(point) {
        this.setPosition(this.viewer.worldToClient(point));
    }

    async show(sensor) {
        if (!sensor) return;

        this.bodyContainer.innerHTML = '';

        const nameSpan = document.createElement('span');
        nameSpan.classList.add('bim360-sensor-tooltip-name');
        this.bodyContainer.appendChild(nameSpan);

        const assetInfo = this.parent.dataProvider.assetInfoCache[sensor.externalId];
        let nameString = 'Unknown asset';
        if (assetInfo) {
            nameString = `Asset [${assetInfo.assetId}]`;
        }
        nameSpan.innerHTML = `${nameString} ${sensor.name}`;

        const valueSpan = document.createElement('span');
        valueSpan.classList.add('bim360-sensor-tooltip-value');
        this.bodyContainer.appendChild(valueSpan);

        let cachedData = this.parent.dataHelper.getDataFromCache(sensor.id, sensor.name);
        if (cachedData) {
            let value = Utility.getClosestValue(cachedData, Utility.getTimeInEpochSeconds(this.parent.currentTime));
            let valueString = `${value.toFixed(2)}`;
            if (sensor.dataUnit)
                valueString += ` ${sensor.dataUnit}`;

            valueSpan.innerHTML = valueString;
        }

        this.setVisible(true);
        this.setPosition(this.viewer.worldToClient(sensor.position));
    }

    hide() {
        this.bodyContainer.innerHTML = 'No Data';
        this.setVisible(false);
    }
}

const tooltip = new SensorTooltip(yourExtesionLoadedTheDataVisulationExtesion);

const onSensorHovered => (event) {
    if (event.hovering && dbId2DeviceIdMap) {
        const deviceId = dbId2DeviceIdMap[event.dbId];

        const { sensors } = dataProvider;
        if (!sensors || sensors.length <= 0) return;

        const sensor = sensors.find(s => s.externalId == deviceId);
        if (!sensor) return;

        tooltip.show(sensor);
    } else {
        tooltip.hide();
    }
};

viewer.addEventListener(
    Autodesk.DataVisualization.Core.MOUSE_HOVERING,
    onSensorHovered
);

and its CSS style see https://github.com/yiskang/forge-bim360-assets.viewer/blob/bim360-iot-connected/bim360assets/wwwroot/css/main.css#L182

/** DataViz Sensor Tooltip **/
/*https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_tooltip*/
.bim360-sensor-tooltip {
  visibility: hidden;
  position: absolute;
  display: inline-block;
  border-bottom: 1px dotted black;
  top: 0;
  left: 0;
  width: 170px;
}

.bim360-sensor-tooltip .bim360-sensor-tooltip-body {
  width: 170px;
  background-color: #555;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px 0;
  position: absolute;
  z-index: 1;
  left: 50%;
  margin-left: -85px;
  opacity: 0;
  transition: opacity 0.3s;
  visibility: hidden;
  width: 170px;
  background-color: #555;
  color: #fff;
  text-align: center;
  border-radius: 6px;
  padding: 5px 0;
  position: absolute;
  z-index: 1;
  opacity: 0;
  transition: opacity 0.3s;
  font-size: 12px;
}

.bim360-sensor-tooltip .bim360-sensor-tooltip-body::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: #555 transparent transparent transparent;
}

.bim360-sensor-tooltip .bim360-sensor-tooltip-body.visible {
  visibility: visible;
  opacity: 1;
}

.bim360-sensor-tooltip-name {
  display: flex;
  justify-content: center;
  font-weight: bold;
  font-size: 12px;
  padding-top: 1px;
  padding-bottom: 3px;
}

.bim360-sensor-tooltip-value {
  font-size: 13px;
  display: flex;
  justify-content: center;
}