const jsonData = {
"title": "Dashboard",
"updated_on": "2025-02-21T14:08:28.741617",
"panels": [
{
"type": "timeseries",
"table": "air_temp",
"title": "Air Temperature 2 m above Surface",
"unit": "u00b0C",
"data": {
"plot66": [
{
"time": "2025-01-01T00:00:00",
"value": 7.468658447265625
},
{
"time": "2025-01-01T03:00:00",
"value": 7.459381103515625
},
{
"time": "2025-01-01T06:00:00",
"value": 7.389312744140625
},
{
"time": "2025-01-01T09:00:00",
"value": 9.727935791015625
},
{
"time": "2025-01-01T12:00:00",
"value": 10.906646728515625
},
{
"time": "2025-01-01T15:00:00",
"value": 11.067535400390625
},
{
"time": "2025-01-01T18:00:00",
"value": 8.872955322265625
},
{
"time": "2025-01-01T21:00:00",
"value": 8.459625244140625
},
{
"time": "2025-01-02T00:00:00",
"value": 8.005035400390625
},
{
"time": "2025-01-02T03:00:00",
"value": 7.952545166015625
},
{
"time": "2025-01-02T06:00:00",
"value": 7.978424072265625
},
{
"time": "2025-01-02T09:00:00",
"value": 10.216461181640625
},
{
"time": "2025-01-02T12:00:00",
"value": 10.678863525390625
},
{
"time": "2025-01-02T15:00:00",
"value": 10.543609619140625
},
{
"time": "2025-01-02T18:00:00",
"value": 9.369049072265625
},
{
"time": "2025-01-02T21:00:00",
"value": 9.340240478515625
},
{
"time": "2025-01-03T00:00:00",
"value": 8.868316650390625
},
{
"time": "2025-01-03T03:00:00",
"value": 8.211090087890625
},
{
"time": "2025-01-03T06:00:00",
"value": 7.642486572265625
},
{
"time": "2025-01-03T09:00:00",
"value": 9.831451416015625
},
{
"time": "2025-01-03T12:00:00",
"value": 10.954010009765625
},
{
"time": "2025-01-03T15:00:00",
"value": 11.588531494140625
},
{
"time": "2025-01-03T18:00:00",
"value": 10.276763916015625
},
{
"time": "2025-01-03T21:00:00",
"value": 10.511627197265625
},
{
"time": "2025-01-04T00:00:00",
"value": 10.682769775390625
},
{
"time": "2025-01-04T03:00:00",
"value": 10.562896728515625
},
{
"time": "2025-01-04T06:00:00",
"value": 10.225006103515625
},
{
"time": "2025-01-04T09:00:00",
"value": 10.914947509765625
},
{
"time": "2025-01-04T12:00:00",
"value": 11.296051025390625
},
{
"time": "2025-01-04T15:00:00",
"value": 10.217437744140625
},
{
"time": "2025-01-04T18:00:00",
"value": 8.537506103515625
},
{
"time": "2025-01-04T21:00:00",
"value": 7.460845947265625
},
{
"time": "2025-01-05T00:00:00",
"value": 6.343170166015625
},
{
"time": "2025-01-05T03:00:00",
"value": 5.995269775390625
},
{
"time": "2025-01-05T06:00:00",
"value": 6.114410400390625
},
{
"time": "2025-01-05T09:00:00",
"value": 6.575592041015625
},
{
"time": "2025-01-05T12:00:00",
"value": 7.698150634765625
},
{
"time": "2025-01-05T15:00:00",
"value": 7.824859619140625
},
{
"time": "2025-01-05T18:00:00",
"value": 7.553375244140625
},
{
"time": "2025-01-05T21:00:00",
"value": 7.653472900390625
}
],
"plot87": [
{
"time": "2025-01-01T00:00:00",
"value": 7.423492431640625
},
{
"time": "2025-01-01T03:00:00",
"value": 7.418609619140625
},
{
"time": "2025-01-01T06:00:00",
"value": 7.349761962890625
},
{
"time": "2025-01-01T09:00:00",
"value": 9.715728759765625
},
{
"time": "2025-01-01T12:00:00",
"value": 10.905670166015625
},
{
"time": "2025-01-01T15:00:00",
"value": 11.054107666015625
},
{
"time": "2025-01-01T18:00:00",
"value": 8.802398681640625
},
{
"time": "2025-01-01T21:00:00",
"value": 8.401763916015625
},
{
"time": "2025-01-02T00:00:00",
"value": 7.939361572265625
},
{
"time": "2025-01-02T03:00:00",
"value": 7.887359619140625
},
{
"time": "2025-01-02T06:00:00",
"value": 7.899566650390625
},
{
"time": "2025-01-02T09:00:00",
"value": 10.189361572265625
},
{
"time": "2025-01-02T12:00:00",
"value": 10.664215087890625
},
{
"time": "2025-01-02T15:00:00",
"value": 10.540191650390625
},
{
"time": "2025-01-02T18:00:00",
"value": 9.300933837890625
},
{
"time": "2025-01-02T21:00:00",
"value": 9.263092041015625
},
{
"time": "2025-01-03T00:00:00",
"value": 8.797027587890625
},
{
"time": "2025-01-03T03:00:00",
"value": 8.135162353515625
},
{
"time": "2025-01-03T06:00:00",
"value": 7.564117431640625
},
{
"time": "2025-01-03T09:00:00",
"value": 9.803619384765625
},
{
"time": "2025-01-03T12:00:00",
"value": 10.940582275390625
},
{
"time": "2025-01-03T15:00:00",
"value": 11.579498291015625
},
{
"time": "2025-01-03T18:00:00",
"value": 10.209869384765625
},
{
"time": "2025-01-03T21:00:00",
"value": 10.452056884765625
},
{
"time": "2025-01-04T00:00:00",
"value": 10.628082275390625
},
{
"time": "2025-01-04T03:00:00",
"value": 10.505523681640625
},
{
"time": "2025-01-04T06:00:00",
"value": 10.157867431640625
},
{
"time": "2025-01-04T09:00:00",
"value": 10.914459228515625
},
{
"time": "2025-01-04T12:00:00",
"value": 11.318023681640625
},
{
"time": "2025-01-04T15:00:00",
"value": 10.220611572265625
},
{
"time": "2025-01-04T18:00:00",
"value": 8.527984619140625
},
{
"time": "2025-01-04T21:00:00",
"value": 7.444732666015625
},
{
"time": "2025-01-05T00:00:00",
"value": 6.330230712890625
},
{
"time": "2025-01-05T03:00:00",
"value": 5.982574462890625
},
{
"time": "2025-01-05T06:00:00",
"value": 6.104888916015625
},
{
"time": "2025-01-05T09:00:00",
"value": 6.563629150390625
},
{
"time": "2025-01-05T12:00:00",
"value": 7.687896728515625
},
{
"time": "2025-01-05T15:00:00",
"value": 7.812896728515625
},
{
"time": "2025-01-05T18:00:00",
"value": 7.523345947265625
},
{
"time": "2025-01-05T21:00:00",
"value": 7.621490478515625
}
]
}
}
]
}
// Load Google Charts
google.charts.load('current', { packages: ['corechart', 'line'] });
google.charts.setOnLoadCallback(() => drawTimeSeriesChart(jsonData.panels));
// This part is not need for this fiddle, but in original script this is used for loading JSON data
/* async function fetchAndDrawCharts() {
try {
const response = await fetch('../db_data/google_dashboard/ketipis/dashboard.json');
const text = await response.text(); // Read as text first
// console.log("Raw JSON Response:", text); // Debug log
const data = JSON.parse(text); // Parse manually to catch errors
console.log("Parsed JSON:", data);
drawTimeSeriesChart(data.panels);
} catch (error) {
console.error("Error loading JSON:", error);
}
}
*/
function createChartContainer(id) {
// Select the container where you want to add the chart div
const container = document.querySelector(".dashboard-container");
if (!container) {
console.error("Dashboard container not found.");
return;
}
// Create a new div element
const chartDiv = document.createElement("div");
chartDiv.id = id; // Set ID
chartDiv.className = "chart"; // Set class
// Append the new div inside the container
container.appendChild(chartDiv);
}
function drawTimeSeriesChart(panels) {
if (!panels || panels.length === 0) {
console.error("No panels found in JSON data");
return;
}
panels.forEach(panel => {
let timeSeriesData = panel.data;
let chartData = new google.visualization.DataTable(); // Unique DataTable per chart
// Define columns (Time, Value per sensor, Tooltip)
chartData.addColumn({ type: 'date', label: 'Time' });
const plots = Object.keys(timeSeriesData);
plots.forEach(plot => chartData.addColumn({ type: 'number', label: plot }));
chartData.addColumn({ type: 'string', role: 'tooltip' });
const rows = [];
let originalData = [];
// Store the original data for reference
plots.forEach(plot => {
timeSeriesData[plot].forEach(entry => {
const dateObj = new Date(entry.time);
const date = entry.time.split("T")[0];
const time = entry.time.split("T")[1].slice(0,5);
const tooltipText = `${date} ${time} n${plot}: ${entry.value.toFixed(2)} ${panel.unit}`;
const row = [dateObj];
let rowValues = {};
plots.forEach(p => {
const value = (p === plot ? entry.value : null);
row.push(value);
rowValues[p] = value;
});
row.push(tooltipText); // Ensure tooltip is attached
rows.push(row);
originalData.push({ dateObj, rowValues, tooltipText });
});
});
chartData.addRows(rows);
// Track visibility of each series
let seriesVisibility = plots.reduce((obj, plot) => {
obj[plot] = true;
return obj;
}, {});
// Chart options
let options = {
title: panel.title,
curveType: 'function',
backgroundColor: '#1e1e1e',
titleTextStyle: { color: '#fff' },
legendTextStyle: { color: '#fff' },
hAxis: { textStyle: { color: '#fff' }, titleTextStyle: { color: '#fff' } },
vAxis: { textStyle: { color: '#fff' }, titleTextStyle: { color: '#fff' } },
legend: { position: 'top' },
tooltip: { isHtml: true },
explorer: { axis: 'horizontal', keepInBounds: true, maxZoomIn: 0.05 },
};
// Create separate chart container
createChartContainer(panel.table);
const chart = new google.visualization.LineChart(document.getElementById(panel.table));
// Function to toggle series visibility
function updateChart() {
let newData = new google.visualization.DataTable(); // Create fresh DataTable
newData.addColumn({ type: 'date', label: 'Time' });
plots.forEach(plot => newData.addColumn({ type: 'number', label: plot }));
newData.addColumn({ type: 'string', role: 'tooltip' });
let newRows = originalData.map(entry => {
let row = [entry.dateObj];
plots.forEach(plot => {
row.push(seriesVisibility[plot] ? entry.rowValues[plot] : null);
});
row.push(entry.tooltipText);
return row;
});
newData.addRows(newRows);
chart.draw(newData, options);
}
// Click event for legend toggling
google.visualization.events.addListener(chart, 'select', () => {
const selection = chart.getSelection();
if (selection.length > 0 && selection[0].column > 0) {
const seriesIndex = selection[0].column - 1;
const plotKey = plots[seriesIndex];
// Toggle visibility
seriesVisibility[plotKey] = !seriesVisibility[plotKey];
updateChart();
}
});
updateChart(); // Initial chart render
});
}
body {
background-color: #1e1e1e;
color: #fff;
font-family: Arial, sans-serif;
padding: 20px;
}
.dashboard-container {
display: flex;
flex-direction: column;
align-items: center;
}
.chart {
width: 900px;
height: 450px;
margin: 20px 0;
background-color: #2e2e2e;
padding: 10px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.2);
}
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<h1>Sensor Dashboard</h1>
<div class="dashboard-container"></div>
</body>
</html>