Skip to content

Commit

Permalink
Merge pull request #455 from xBimTeam/feature/data-visualization-plugins
Browse files Browse the repository at this point in the history
Added 'Data Visualization' plugins to create heat maps and icons
  • Loading branch information
andyward authored Jul 30, 2024
2 parents 199def5 + edf9962 commit 028e5b4
Show file tree
Hide file tree
Showing 17 changed files with 1,543 additions and 4 deletions.
6 changes: 6 additions & 0 deletions examples/data-visualization/icons.ts

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions examples/data-visualization/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>

<head>
<title>Data Visualization example</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<style>
html,
body {
height: 100%;
padding: 0;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}

canvas {
height: 100%;
width: 100%;
display: block;
border: none;
}

#viewer-container {
flex: 6;
width: 100%;
overflow: hidden;
}

.flex-container {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}

.left-panel {
position: absolute;
top: 40px;
z-index: 1;
padding: 2em;
background-color: white;
box-shadow: 0 0 10px darkgray;
border-radius: 10px;
left: 20px;
width: 400px;
max-width: 400px;
user-select: none;
pointer-events: fill;
}
.left-panel div {
font-weight: 600;
padding-top: 1em;
}

#gradient{
margin-top: 10px;
width: 100%;
}

#gradien-parent{
display: flex;
flex-direction: row;
}
#channels{
width: 100%;
height: 20px;
}
</style>

</head>
<body>
<div class="flex-container">
<div id="viewer-container">
<canvas id="viewer"></canvas>
</div>
<div class="left-panel">
<select id="channels"></select>
<div id="gradien-parent">

<div style="width: 50px; margin-right: 0px;" id="start-grad"></div>
<div id="gradient"></div>
<div style="width: 65px; margin-left: 10px;" id="end-grad"></div>
</div>
</div>
</div>
</body>

</html>
151 changes: 151 additions & 0 deletions examples/data-visualization/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { Viewer, Heatmap, ContinuousHeatmapChannel, HeatmapSource, Icons, CameraType, ViewType, ClippingPlane, ProductType, } from '../..';
import { Icon } from '../../src/plugins/DataVisualization/Icons/icon';
import { IconsData } from './icons';

const viewer = new Viewer("viewer");
const heatmap = new Heatmap();
const icons = new Icons();
viewer.addPlugin(heatmap);
viewer.addPlugin(icons);

const tempChannelId = "room_temp";
const humidityChannelId = "room_humidity";

const temperatureSource = new HeatmapSource("Temp sensor", 1, 152, tempChannelId, 1);
const humiditySource = new HeatmapSource("Humidity sensor", 1, 152, humidityChannelId, 10);
const sourceIcon = new Icon("Room 1 Sensor", "Temperature sensor", 1, 152, IconsData.errorIcon, null, null, null, () => { viewer.zoomTo(152, 1) });

let selectedChannel: ContinuousHeatmapChannel;
const tempChannel = new ContinuousHeatmapChannel
(tempChannelId, "double", "Temperature", "Temperature of Rooms", "temperature", "°C", 0, 40, ["#142ce2","#fff200","#FF0000"]);

const humidityChannel = new ContinuousHeatmapChannel
(humidityChannelId, "double", "Humidity", "Humidity of Rooms", "humidity", "%", 0, 100, ["#1ac603", "#f96c00"]);
selectedChannel = tempChannel;

heatmap.addChannel(tempChannel);
heatmap.addChannel(humidityChannel);

viewer.on('loaded', args => {
try {

viewer.camera = CameraType.PERSPECTIVE;
clipBox();
viewer.resetState(ProductType.IFCSPACE)
viewer.show(ViewType.DEFAULT);

heatmap.addSource(temperatureSource);
heatmap.addSource(humiditySource);

icons.addIcon(sourceIcon);
icons.addIcon(new Icon("Temperature Sensor 2", "Temperature sensor", 1, 617, IconsData.successIcon));
icons.addIcon(new Icon("Temperature Sensor 3", "Temperature sensor", 1, 447, IconsData.successIcon));

heatmap.renderChannel(selectedChannel.channelId);

setInterval(function(){
if(selectedChannel.channelId === tempChannelId){
temperatureSource.value = getRandomInt(40);
heatmap.renderSource(temperatureSource.id);
sourceIcon.description = `Room ${selectedChannel.name}: ${temperatureSource.value}${selectedChannel.unit}`;
}
else{
humiditySource.value = getRandomInt(100);
heatmap.renderSource(humiditySource.id);
sourceIcon.description = `Room ${selectedChannel.name}: ${humiditySource.value}${selectedChannel.unit}`;
}
}, 2000);

} catch (e) {

}
});

const channelsDropdown = document.getElementById('channels') as HTMLSelectElement;
channelsDropdown.addEventListener('change', handleDropdownChange);

heatmap.channels.forEach(obj => {
const option = document.createElement('option');
option.value = obj.name;
option.textContent = obj.description;
channelsDropdown.appendChild(option);
});

setSelectedChannel();

viewer.loadAsync('/tests/data/SampleHouse.wexbim')
viewer.hoverPickEnabled = true;
viewer.adaptivePerformanceOn = false;
viewer.highlightingColour = [0, 0, 255, 255];
viewer.start();
window['viewer'] = viewer;


function setSelectedChannel() {
var colors = selectedChannel.colorGradient;
const gradientElement = document.getElementById('gradient')!;
const gradientStartElement = document.getElementById('start-grad')!;
const gradientEndElement = document.getElementById('end-grad')!;
gradientStartElement.textContent = `${selectedChannel.min}${selectedChannel.unit}`;
gradientEndElement.textContent = `${selectedChannel.max}${selectedChannel.unit}`;

const numColors = colors.length;
const stops = colors.map((color, index) => {
const position = (index / (numColors - 1)) * 100;
return { color, position: `${position}%` };
});
const gradientString = stops.map(stop => `${stop.color} ${stop.position}`).join(', ');
gradientElement.style.background = `linear-gradient(90deg, ${gradientString})`;
}

function handleDropdownChange() {
const selectedChannelName = channelsDropdown.value;
switch(selectedChannelName){
case 'Humidity':{
selectedChannel = humidityChannel;
setSelectedChannel();
return;
}
case 'Temperature':{
selectedChannel = tempChannel;
setSelectedChannel();
return;
}
}
}

function getRandomInt(max: number) {
return Math.floor(Math.random() * max);
}


let clipBox = () => {
var planes: ClippingPlane[] = [
{
direction: [1, 0, 0],
location: [10000, 0, 0]
},
{
direction: [0, 1, 0],
location: [0, 10000, 0]
},
{
direction: [0, 0, 1],
location: [0, 0, 2000]
},
{
direction: [-1, 0, 0],
location: [-10000, 0, 0]
},
{
direction: [0, -1, 0],
location: [0, -10000, 0]
},
{
direction: [0, 0, -1],
location: [0, 0, -10000]
}
];

viewer.sectionBox.setToPlanes(planes);
}
129 changes: 129 additions & 0 deletions examples/data-visualization/pin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions examples/pins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ viewer.on('loaded', args => {
viewer.camera = CameraType.PERSPECTIVE;
clipBox();
viewer.show(ViewType.DEFAULT);
//viewer.zoomTo(763012);

// kick off annotation rendering each frame
window.requestAnimationFrame(() => renderAnnotationLayer());
} catch (e) {
Expand Down
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ <h1>
<li>
<a href="dist/pins/index.html">3D annotations</a>
</li>
<li>
<a href="dist/data-visualization/index.html">Data Visualization</a>
</li>
</ul>
<p>
You can also read <a href="docs" target="_blanc">documentation</a> if you are that kind of developer.
Expand Down
Loading

0 comments on commit 028e5b4

Please sign in to comment.