add sensor datas

This commit is contained in:
2024-12-06 13:59:27 +08:00
committed by YueLiang Chen(陳岳良)
parent 1c3b0bf6c9
commit 15476ae363
7 changed files with 452 additions and 221 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

@@ -0,0 +1,229 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>D3.js Line Chart - Local File with Point Details</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
fill: steelblue;
stroke: #fff;
}
.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>
<h1>D3.js Line Chart from Local JSON File with Point Details</h1>
<input type="file" id="fileInput">
<div id="chart"></div>
<script>
// Set the dimensions and margins of the graph
const margin = {top: 20, right: 20, bottom: 30, left: 50};
const width = 960 - margin.left - margin.right;
const height = 500 - margin.top - margin.bottom;
// Parse the date / time
const parseTime = d3.timeParse("%Y-%m-%d");
const formatTime = d3.timeFormat("%Y-%m-%d");
// Set the ranges
const x = d3.scaleLinear().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);
// Define the line
const valueline = d3.line()
.x(d => x(d.timestamp))
.y(d => y(d.lux));
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;
}
// Function to create the chart
function createChart(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]);
datasets = JSON.parse(JSON.stringify(datasets));
// Clear previous chart if any
d3.select("#chart").html("");
// Append the svg object to the body of the page
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})`);
// Create tooltip
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Format the data
/*fastLuxData.forEach(function(d) {
d.timestamp = d.timestamp;//parseTime(d.timestamp);
d.lux = +d.lux;
});*/
// Scale the range of the data
// Scale the range of the data
x.domain(d3.extent(datasets[0].values.flatMap(d => d.timestamp), d => d.timestamp));
y.domain([0, d3.max(datasets[0].values.flatMap(d => d.lux), d => d.lux)]);
// Add the valueline paths
const dataset = svg.selectAll(".dataset")
.data(datasets)
.enter().append("g")
.attr("class", "dataset");
dataset.append("path")
.attr("class", "line")
.attr("d", d => valueline(d.values))
.style("stroke", d => color(d.name));
// Add the X Axis
svg.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));
// Add the Y Axis
svg.append("g")
.call(d3.axisLeft(y));
// Add the dots
dataset.each(function(dataset) {
d3.select(this).selectAll(".dot")
.data(dataset.values)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 5)
.attr("cx", d => x(d.timestamp))
.attr("cy", d => y(d.lux))
.style("fill", color(dataset.name))
.on("mouseover", function(event, d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(`${dataset.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);
});
});
// Add a legend
const legend = svg.selectAll('.legend')
.data(datasets)
.enter().append('g')
.attr('class', 'legend')
.attr('transform', (d, i) => `translate(0,${i * 20})`);
legend.append('rect')
.attr('x', width + 5)
.attr('width', 18)
.attr('height', 18)
.style('fill', d => color(d.name));
legend.append('text')
.attr('x', width + 25)
.attr('y', 9)
.attr('dy', '.35em')
.style('text-anchor', 'start')
.text(d => d.name);
}
// File input event listener
document.getElementById('fileInput').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
const contents = e.target.result;
try {
const data = JSON.parse(contents);
createChart(data);
} catch (error) {
console.error("Error parsing JSON:", error);
alert("Error parsing JSON file. Please make sure it's a valid JSON.");
}
};
reader.readAsText(file);
}
});
</script>
</body>
</html>

View File

@@ -1,188 +1,188 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>D3.js Multiple Line Chart</title> <title>D3.js Multiple Line Chart</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
<style> <style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.line { fill: none; stroke-width: 2px; } .line { fill: none; stroke-width: 2px; }
.axis { font-size: 12px; } .axis { font-size: 12px; }
.legend { font-size: 12px; } .legend { font-size: 12px; }
</style> </style>
</head> </head>
<body> <body>
<input type="file" id="fileInput" accept=".json" /> <input type="file" id="fileInput" accept=".json" />
<div id="chart"></div> <div id="chart"></div>
<script> <script>
// Sample multiple dataset // Sample multiple dataset
const data = [ const data = [
{ {
name: 'Temperature', name: 'Temperature',
color: 'steelblue', color: 'steelblue',
values: [ values: [
{date: '2023-01-01', value: 32}, {date: '2023-01-01', value: 32},
{date: '2023-02-01', value: 35}, {date: '2023-02-01', value: 35},
{date: '2023-03-01', value: 40}, {date: '2023-03-01', value: 40},
{date: '2023-04-01', value: 45}, {date: '2023-04-01', value: 45},
{date: '2023-05-01', value: 50} {date: '2023-05-01', value: 50}
] ]
}, },
{ {
name: 'Rainfall', name: 'Rainfall',
color: 'green', color: 'green',
values: [ values: [
{date: '2023-01-01', value: 10}, {date: '2023-01-01', value: 10},
{date: '2023-02-01', value: 15}, {date: '2023-02-01', value: 15},
{date: '2023-03-01', value: 20}, {date: '2023-03-01', value: 20},
{date: '2023-04-01', value: 25}, {date: '2023-04-01', value: 25},
{date: '2023-05-01', value: 30} {date: '2023-05-01', value: 30}
] ]
} }
]; ];
// Render multiple line chart // Render multiple line chart
function renderMultiLineChart(datasets) { function renderMultiLineChart(datasets) {
// Clear previous chart // Clear previous chart
d3.select("#chart").html(""); d3.select("#chart").html("");
// Chart dimensions // Chart dimensions
const margin = {top: 50, right: 100, bottom: 50, left: 50}; const margin = {top: 50, right: 100, bottom: 50, left: 50};
const width = 700 - margin.left - margin.right; const width = 700 - margin.left - margin.right;
const height = 400 - margin.top - margin.bottom; const height = 400 - margin.top - margin.bottom;
// Tooltip // Tooltip
const tooltip = d3.select("body") const tooltip = d3.select("body")
.append("div") .append("div")
.attr("class", "tooltip") .attr("class", "tooltip")
.style("opacity", 0); .style("opacity", 0);
// Create SVG // Create SVG
const svg = d3.select("#chart") const svg = d3.select("#chart")
.append("svg") .append("svg")
.attr("width", width + margin.left + margin.right) .attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom) .attr("height", height + margin.top + margin.bottom)
.append("g") .append("g")
.attr("transform", `translate(${margin.left},${margin.top})`); .attr("transform", `translate(${margin.left},${margin.top})`);
// Parse dates // Parse dates
const parseTime = d3.timeParse("%Y-%m-%d"); const parseTime = d3.timeParse("%Y-%m-%d");
const formatTime = d3.timeFormat("%Y-%m-%d"); const formatTime = d3.timeFormat("%Y-%m-%d");
datasets.forEach(series => { datasets.forEach(series => {
series.values.forEach(d => { series.values.forEach(d => {
d.date = parseTime(d.date); d.date = parseTime(d.date);
}); });
}); });
// Combine all values for scale // Combine all values for scale
const allValues = datasets.flatMap(series => series.values.map(d => d.value)); const allValues = datasets.flatMap(series => series.values.map(d => d.value));
// Scales // Scales
const x = d3.scaleTime() const x = d3.scaleTime()
.domain(d3.extent(datasets[0].values, d => d.date)) .domain(d3.extent(datasets[0].values, d => d.date))
.range([0, width]); .range([0, width]);
const y = d3.scaleLinear() const y = d3.scaleLinear()
.domain([ .domain([
d3.min(allValues) * 0.9, d3.min(allValues) * 0.9,
d3.max(allValues) * 1.1 d3.max(allValues) * 1.1
]) ])
.range([height, 0]); .range([height, 0]);
// Line generator // Line generator
const line = d3.line() const line = d3.line()
.x(d => x(d.date)) .x(d => x(d.date))
.y(d => y(d.value)); .y(d => y(d.value));
// X-axis // X-axis
svg.append("g") svg.append("g")
.attr("class", "axis") .attr("class", "axis")
.attr("transform", `translate(0,${height})`) .attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x)); .call(d3.axisBottom(x));
// Y-axis // Y-axis
svg.append("g") svg.append("g")
.attr("class", "axis") .attr("class", "axis")
.call(d3.axisLeft(y)); .call(d3.axisLeft(y));
// Lines and data points for each dataset // Lines and data points for each dataset
datasets.forEach(series => { datasets.forEach(series => {
// Line // Line
svg.append("path") svg.append("path")
.datum(series.values) .datum(series.values)
.attr("class", "line") .attr("class", "line")
.attr("fill", "none") .attr("fill", "none")
.attr("stroke", series.color) .attr("stroke", series.color)
.attr("stroke-width", 2) .attr("stroke-width", 2)
.attr("d", line); .attr("d", line);
// Data points // Data points
svg.selectAll(`.dot-${series.name}`) svg.selectAll(`.dot-${series.name}`)
.data(series.values) .data(series.values)
.enter().append("circle") .enter().append("circle")
.attr("class", `dot dot-${series.name}`) .attr("class", `dot dot-${series.name}`)
.attr("cx", d => x(d.date)) .attr("cx", d => x(d.date))
.attr("cy", d => y(d.value)) .attr("cy", d => y(d.value))
.attr("r", 5) .attr("r", 5)
.attr("fill", series.color) .attr("fill", series.color)
.on("mouseover", (event, d) => { .on("mouseover", (event, d) => {
tooltip.transition() tooltip.transition()
.duration(200) .duration(200)
.style("opacity", .9); .style("opacity", .9);
tooltip.html(` tooltip.html(`
<strong>${series.name}</strong><br> <strong>${series.name}</strong><br>
Date: ${formatTime(d.date)}<br> Date: ${formatTime(d.date)}<br>
Value: ${d.value} Value: ${d.value}
`) `)
.style("left", (event.pageX + 10) + "px") .style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px"); .style("top", (event.pageY - 28) + "px");
}) })
.on("mouseout", () => { .on("mouseout", () => {
tooltip.transition() tooltip.transition()
.duration(500) .duration(500)
.style("opacity", 0); .style("opacity", 0);
}); });
}); });
// Legend // Legend
const legend = svg.selectAll(".legend") const legend = svg.selectAll(".legend")
.data(datasets) .data(datasets)
.enter().append("g") .enter().append("g")
.attr("class", "legend") .attr("class", "legend")
.attr("transform", (d, i) => `translate(${width + 10},${i * 20})`); .attr("transform", (d, i) => `translate(${width + 10},${i * 20})`);
legend.append("rect") legend.append("rect")
.attr("x", 0) .attr("x", 0)
.attr("width", 10) .attr("width", 10)
.attr("height", 10) .attr("height", 10)
.style("fill", d => d.color); .style("fill", d => d.color);
legend.append("text") legend.append("text")
.attr("x", 20) .attr("x", 20)
.attr("y", 9) .attr("y", 9)
.text(d => d.name) .text(d => d.name)
.style("text-anchor", "start"); .style("text-anchor", "start");
} }
// Render chart // Render chart
renderMultiLineChart(data); renderMultiLineChart(data);
// File input handler // File input handler
document.getElementById('fileInput').addEventListener('change', function(e) { document.getElementById('fileInput').addEventListener('change', function(e) {
const file = e.target.files[0]; const file = e.target.files[0];
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function(event) { reader.onload = function(event) {
try { try {
const jsonData = JSON.parse(event.target.result); const jsonData = JSON.parse(event.target.result);
renderMultiLineChart(jsonData); renderMultiLineChart(jsonData);
} catch (error) { } catch (error) {
alert('Error parsing JSON file: ' + error); alert('Error parsing JSON file: ' + error);
} }
}; };
reader.readAsText(file); reader.readAsText(file);
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@@ -1,35 +1,35 @@
[ [
{ {
"name": "Temperature", "name": "Temperature",
"color": "steelblue", "color": "steelblue",
"values": [ "values": [
{"date": "2023-01-01", "value": 32}, {"date": "2023-01-01", "value": 32},
{"date": "2023-02-01", "value": 35}, {"date": "2023-02-01", "value": 35},
{"date": "2023-03-01", "value": 40}, {"date": "2023-03-01", "value": 40},
{"date": "2023-04-01", "value": 45}, {"date": "2023-04-01", "value": 45},
{"date": "2023-05-01", "value": 50} {"date": "2023-05-01", "value": 50}
] ]
}, },
{ {
"name": "Temperature2", "name": "Temperature2",
"color": "red", "color": "red",
"values": [ "values": [
{"date": "2023-01-01", "value": 35}, {"date": "2023-01-01", "value": 35},
{"date": "2023-02-01", "value": 40}, {"date": "2023-02-01", "value": 40},
{"date": "2023-03-01", "value": 45}, {"date": "2023-03-01", "value": 45},
{"date": "2023-04-01", "value": 50}, {"date": "2023-04-01", "value": 50},
{"date": "2023-05-01", "value": 32} {"date": "2023-05-01", "value": 32}
] ]
}, },
{ {
"name": "Temperature3", "name": "Temperature3",
"color": "green", "color": "green",
"values": [ "values": [
{"date": "2023-01-01", "value": 40}, {"date": "2023-01-01", "value": 40},
{"date": "2023-02-01", "value": 45}, {"date": "2023-02-01", "value": 45},
{"date": "2023-03-01", "value": 50}, {"date": "2023-03-01", "value": 50},
{"date": "2023-04-01", "value": 32}, {"date": "2023-04-01", "value": 32},
{"date": "2023-05-01", "value": 35} {"date": "2023-05-01", "value": 35}
] ]
} }
] ]