-1

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);
    }
Adarsh Raj
  • 297
  • 4
  • 14

1 Answers1

1

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;
}
Eason Kang
  • 6,155
  • 1
  • 7
  • 24
  • i could use this but it seems a bit complicated to me like u have found the position and created and element and placed this element near to the position does this not overlay the exact position?also do i need to know threejs?.. is there no function inside of the api docs itself? – Adarsh Raj Nov 10 '22 at 04:24
  • Sorry for missing the css style. I added the CSS style of my tooltip. You need to add them to your app. – Eason Kang Nov 11 '22 at 03:51
  • No, it's just JavaScript DOM API, which is not part of Viewer API, and it doesn't require three.js. But we need to get convert the 3d point get from the hovering event to the [browser's viewport space](https://developer.mozilla.org/en-US/docs/Web/CSS/CSSOM_View/Coordinate_systems) by using [Viewer3D#worldToClient](https://forge.autodesk.com/en/docs/viewer/v7/reference/Viewing/GuiViewer3D/#worldtoclient-point-camera) – Eason Kang Nov 11 '22 at 03:54
  • JavaScript DOM API -> https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model – Eason Kang Nov 11 '22 at 03:55