Skip to content

Instantly share code, notes, and snippets.

@yiskang
Created March 5, 2024 01:42
Show Gist options
  • Save yiskang/3016e59cf1be499ae30e287a483571aa to your computer and use it in GitHub Desktop.
Save yiskang/3016e59cf1be499ae30e287a483571aa to your computer and use it in GitHub Desktop.
APS DataViz Extensions Demo - Another way of creating heatmaps by using Revit Rooms, replacing public/extensions/SensorHeatmapsExtension.js
/// import * as Autodesk from "@types/forge-viewer";
import { UIBaseExtension } from './BaseExtension.js';
import { findNearestTimestampIndex } from './HistoricalDataView.js';
import { SensorHeatmapsPanel } from './SensorHeatmapsPanel.js';
export const SensorHeatmapsExtensionID = 'IoT.SensorHeatmaps';
export class SensorHeatmapsExtension extends UIBaseExtension {
constructor(viewer, options) {
super(viewer, options);
this.panel = undefined;
this._surfaceShadingData = undefined;
this.onChannelChanged = undefined;
this.getSensorValue = this.getSensorValue.bind(this);
}
onDataViewChanged(oldDataView, newDataView) {
this.updateChannels();
this.createHeatmaps();
}
onCurrentTimeChanged(oldTime, newTime) { this.updateHeatmaps(); }
onCurrentChannelChanged(oldChannelID, newChannelID) { this.updateHeatmaps(); }
getSensorValue(surfaceShadingPoint, sensorType) {
if (!this.dataView || !this.currentTime || !this.currentChannelID) {
return 0.0;
}
const sensor = this.dataView.getSensors().get(surfaceShadingPoint.id);
if (!sensor) {
return 0.0;
}
const channel = this.dataView.getChannels().get(this.currentChannelID);
if (!channel) {
return 0.0;
}
const samples = this.dataView.getSamples(surfaceShadingPoint.id, this.currentChannelID);
if (!samples) {
return 0.0;
}
const fractionalIndex = findNearestTimestampIndex(samples.timestamps, this.currentTime, true);
const index1 = Math.floor(fractionalIndex);
const index2 = Math.ceil(fractionalIndex);
if (index1 !== index2) {
const value = samples.values[index1] + (samples.values[index2] - samples.values[index1]) * (fractionalIndex - index1);
return (value - channel.min) / (channel.max - channel.min);
}
else {
const value = samples.values[index1];
return (value - channel.min) / (channel.max - channel.min);
}
}
async createHeatmaps() {
if (this.isActive()) { // TODO: update @types/forge-viewer
const channelID = this.currentChannelID;
await this._setupSurfaceShading(this.viewer.model);
let levels = this._surfaceShadingData.children.map(data => data.id);
this._dataVizExt.renderSurfaceShading(levels, channelID, this.getSensorValue);
}
}
async updateHeatmaps() {
if (this.isActive()) { // TODO: update @types/forge-viewer
const channelID = this.currentChannelID;
if (!this._surfaceShadingData) {
await this._setupSurfaceShading(this.viewer.model);
let levels = this._surfaceShadingData.children.map(data => data.id);
this._dataVizExt.renderSurfaceShading(levels, channelID, this.getSensorValue);
}
else {
this._dataVizExt.updateSurfaceShading(this.getSensorValue);
}
}
}
updateChannels() {
if (this.dataView && this.panel) {
this.panel.updateChannels(this.dataView);
}
}
async load() {
await super.load();
this.panel = new SensorHeatmapsPanel(this.viewer, 'heatmaps', 'Heatmaps', {});
this.panel.onChannelChanged = (channelId) => {
if (this.onChannelChanged) {
this.onChannelChanged(channelId);
}
};
console.log(`${SensorHeatmapsExtensionID} extension loaded.`);
return true;
}
unload() {
var _a;
super.unload();
(_a = this.panel) === null || _a === void 0 ? void 0 : _a.uninitialize();
this.panel = undefined;
console.log(`${SensorHeatmapsExtensionID} extension unloaded.`);
return true;
}
activate() {
var _a;
super.activate();
(_a = this.panel) === null || _a === void 0 ? void 0 : _a.setVisible(true);
this.onDataViewChanged(undefined, undefined);
return true;
}
deactivate() {
var _a;
super.deactivate();
(_a = this.panel) === null || _a === void 0 ? void 0 : _a.setVisible(false);
this._dataVizExt.removeSurfaceShading();
return true;
}
onToolbarCreated() {
this.createToolbarButton('iot-heatmaps-btn', 'IoT Heatmaps', 'https://img.icons8.com/ios-filled/50/000000/heat-map.png'); // <a href="https://icons8.com/icon/8315/heat-map">Heat Map icon by Icons8</a>
}
async _setupSurfaceShading(model) {
if (!this.dataView) {
return;
}
const devices = [];
const structureInfo = new Autodesk.DataVisualization.Core.ModelStructureInfo(model);
const types = Array.from(this.dataView.getChannels().keys());
for (const [sensorId, sensor] of this.dataView.getSensors().entries()) {
if (!sensor.objectId) {
continue;
}
devices.push({
id: sensorId,
dbId: sensor.objectId,
position: sensor.location,
name: sensor.name,
sensorTypes: types.concat()
});
}
// Generates `SurfaceShadingData` after assigning each device to a room.
const shadingData = await structureInfo.generateSurfaceShadingData(devices);
this._surfaceShadingData = shadingData;
// Use the resulting shading data to generate heatmap from.
await this._dataVizExt.setupSurfaceShading(model, shadingData);
// Register color stops for the heatmap. Along with the normalized sensor value
// in the range of [0.0, 1.0], `renderSurfaceShading` will interpolate the final
// heatmap color based on these specified colors.
// const sensorColors = [0x0000ff, 0x00ff00, 0xffff00, 0xff0000];
// types.map(sensorType => this._dataVizExt.registerSurfaceShadingColors(sensorType, sensorColors));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment