Files
light_sensor_capture/test/Sensor_datas/visualization.html
2024-12-06 15:30:07 +08:00

234 lines
8.7 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3.js Multiple Line Chart</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.line { fill: none; stroke-width: 2px; }
.axis { font-size: 12px; }
.legend { font-size: 12px; }
.tooltip {
position: absolute;
text-align: center;
width: 120px;
height: 40px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
</head>
<body>
<input type="file" id="fileInput" />
<div id="chart"></div>
<script>
// Sample multiple dataset
const data = [
{"lux":10.0,"timestamp":442470841810000},
{"lux":20.0,"timestamp":442471010509000},
{"lux":30.0,"timestamp":442471182576000},
{"lux":40.0,"timestamp":442471360117000},
{"lux":100.0,"timestamp":442471522264000}
];
const colorMap = new Map();
colorMap.set('original', 'steelblue');
colorMap.set('fastLuxData', 'green');
colorMap.set('slowLuxData', 'red');
function weightIntegral(x) {
return x * (x * 0.5 + 4000000000);
}
function calculateLux(datas, now) {
var sum = 0, totalWeight = 0, endDelta = 100000000;
for (var i = datas.length - 1; i >= 0; i--) {
var eventTime = datas[i].timestamp;
var startDelta = eventTime - now;
var weight = weightIntegral(endDelta) - weightIntegral(startDelta);
var lux = datas[i].lux;
totalWeight += weight;
sum += lux * weight;
endDelta = startDelta;
}
return sum / totalWeight;
}
// Render multiple line chart
function renderMultiLineChart(data) {
var timestampNow;
var fastLuxData = [];
var slowLuxData = [];
var datasets = [];
for (timestampNow=data[0].timestamp; timestampNow<=data[data.length-1].timestamp; timestampNow+=200000000) {
var fastRingBuffer = data.filter((value) => value.timestamp >= timestampNow-2000000000 && value.timestamp <= timestampNow);
var slowRingBuffer = data.filter((value) => value.timestamp >= timestampNow-4000000000 && value.timestamp <= timestampNow);
if (fastRingBuffer.length <= 1) {
fastLuxData.push(data[0]);
} else {
fastLuxData.push({timestamp:timestampNow, lux:calculateLux(fastRingBuffer, timestampNow)});
}
if (slowRingBuffer.length <= 1) {
slowLuxData.push(data[0]);
} else {
slowLuxData.push({timestamp:timestampNow, lux:calculateLux(slowRingBuffer, timestampNow)});
}
}
var dataObj = new Object();
dataObj.name = "original";
dataObj.values = data;
datasets.push(dataObj);
var fastLuxDataObj = new Object();
fastLuxDataObj.name = "fastLuxData";
fastLuxDataObj.values = fastLuxData;
datasets.push(fastLuxDataObj);
var slowLuxDataObj = new Object();
slowLuxDataObj.name = "slowLuxData";
slowLuxDataObj.values = slowLuxData;
datasets.push(slowLuxDataObj);
console.log(datasets.length);
console.log(datasets[0]);
// Clear previous chart
d3.select("#chart").html("");
// Chart dimensions
const margin = {top: 50, right: 50, bottom: 50, left: 50};
const width = 800 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom;
// Tooltip
const tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Create SVG
const svg = d3.select("#chart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
// Parse dates
const parseTime = d3.timeParse("%Y-%m-%d");
const formatTime = d3.timeFormat("%Y-%m-%d");
/*datasets.forEach(series => {
series.values.forEach(d => {
d.timestamp = parseTime(d.timestamp);
});
});*/
// Combine all values for scale
const allValues = datasets.flatMap(series => series.values.map(d => d.lux));
// Scales
const x = d3.scaleLinear()
.domain(d3.extent(datasets[0].values, d => d.timestamp))
.range([0, width]);
const y = d3.scaleLinear()
.domain([
d3.min(allValues) * 0.9,
d3.max(allValues) * 1.1
])
.range([height, 0]);
// Line generator
const line = d3.line()
.x(d => x(d.timestamp))
.y(d => y(d.lux));
// X-axis
svg.append("g")
.attr("class", "axis")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
// Y-axis
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y));
// Lines and data points for each dataset
datasets.forEach(series => {
// Line
svg.append("path")
.datum(series.values)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", colorMap.get(series.name))
.attr("stroke-width", 2)
.attr("d", line);
// Data points
svg.selectAll(`.dot-${series.name}`)
.data(series.values)
.enter().append("circle")
.attr("class", `dot dot-${series.name}`)
.attr("cx", d => x(d.timestamp))
.attr("cy", d => y(d.lux))
.attr("r", 5)
.attr("fill", colorMap.get(series.name))
.on("mouseover", function(event, d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(`${series.name}<br/>timestamp: ${d.timestamp}<br/>lux: ${d.lux}`)
.style("left", (event.pageX + 5) + "px")
.style("top", (event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
});
// Legend
const legend = svg.selectAll(".legend")
.data(datasets)
.enter().append("g")
.attr("class", "legend")
.attr("transform", (d, i) => `translate(${width + 10},${i * 20})`);
legend.append("rect")
.attr("x", 0)
.attr("width", 10)
.attr("height", 10)
.style("fill", d => colorMap.get(d.name));
legend.append("text")
.attr("x", 20)
.attr("y", 9)
.text(d => d.name)
.style("text-anchor", "start");
}
// Render chart
renderMultiLineChart(data);
// File input handler
document.getElementById('fileInput').addEventListener('change', function(e) {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = function(event) {
try {
const jsonData = JSON.parse(event.target.result);
renderMultiLineChart(jsonData);
} catch (error) {
alert('Error parsing JSON file: ' + error);
}
};
reader.readAsText(file);
});
</script>
</body>
</html>