|
<!doctype html> |
|
|
|
<html lang="en"> |
|
<head> |
|
<meta charset="utf-8"> |
|
|
|
<title>Arduino sensor.meteo D3.js data vis chart</title> |
|
|
|
<meta name="description" content=""> |
|
<meta name="author" content=""> |
|
|
|
<style> /* set the CSS */ |
|
|
|
body { font: 12px Arial;} |
|
|
|
path { |
|
/* stroke: steelblue;*/ |
|
fill: none; |
|
} |
|
|
|
.chart-container { |
|
float: left; |
|
width: 100%; |
|
margin-bottom: 20px; |
|
} |
|
.line { |
|
fill: none; |
|
stroke-width: 2; |
|
} |
|
.axis path, |
|
.axis line { |
|
fill: none; |
|
stroke: grey; |
|
stroke-width: 1; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.dot { |
|
stroke: none; |
|
} |
|
.tooltip { |
|
position: absolute; |
|
font-size: 12px; |
|
width: auto; |
|
height: auto; |
|
pointer-events: none; |
|
background-color: white; |
|
} |
|
|
|
/* basic positioning */ |
|
.legend { list-style: none; } |
|
.legend li { float: left; margin-right: 10px; } |
|
.legend span { border: 1px solid #ccc; float: left; width: 12px; height: 12px; margin: 2px;} |
|
/* your colors */ |
|
.legend .LDR { background-color: #FFFF00; } |
|
.legend .LUX { background-color: #FF0000; } |
|
.legend .TEMP { background-color: #FF7F0E; } |
|
.legend .HUMIDITY { background-color: #1f77b4; } |
|
|
|
</style> |
|
|
|
<!-- Load d3.js --> |
|
<script src="https://d3js.org/d3.v4.js"></script> |
|
<!--=<script src="https://d3js.org/d3.v5.min.js"></script>--> |
|
|
|
</head> |
|
|
|
<body> |
|
|
|
<div class="container-fluid"> |
|
|
|
<div class="row"> |
|
<div class="col-lg-12"> |
|
|
|
<div class="chart-container"> |
|
<div id="my_dataviz_LDR"></div> |
|
<ul class="legend"> |
|
<li><span class="LUX"></span> Lux</li> |
|
<li><span class="LDR"></span> LDR (Vout)</li> |
|
</ul> |
|
</div> |
|
|
|
<div class="chart-container"> |
|
<div id="my_dataviz_TEMP"></div> |
|
<ul class="legend"> |
|
<li><span class="TEMP"></span> Temp (ºC)</li> |
|
</ul> |
|
</div> |
|
|
|
<div class="chart-container"> |
|
<div id="my_dataviz_HUMIDITY"></div> |
|
<ul class="legend"> |
|
<li><span class="HUMIDITY"></span> Humidity (%)</li> |
|
</ul> |
|
</div> |
|
</div> |
|
|
|
<div class="row"> |
|
<div class="col-lg-12"> |
|
|
|
|
|
</div> |
|
</div> |
|
|
|
</div> |
|
|
|
|
|
<div class="row" style="float: left; width: 100%;"> |
|
<div class="col-lg-12"> |
|
<h3>Arduino - Sensor Data: Temp, Humidity, LDR</h3> |
|
<p>Captured via Micro SD data logger on 25/02/2020</p> |
|
<p>Generated by Arduino Mega2560 w/ DHT11, LDR ( https://learn.adafruit.com/photocells ) & DH1307 RTC in indoor window sill environment </p> |
|
<p>Served from localhost (Ubuntu) using python -m SimpleHTTPServer 3001</p> |
|
</div> |
|
</div> |
|
|
|
</div> |
|
|
|
<script> |
|
|
|
// set the dimensions and margins of the graph |
|
var margin = {top: 10, right: 30, bottom: 50, left: 60}, |
|
width = 800 - margin.left - margin.right, |
|
height = 400 - margin.top - margin.bottom; |
|
|
|
/* |
|
var margin = {top: 50, right: 50, bottom: 50, left: 50} |
|
, width = window.innerWidth - margin.left - margin.right // Use the window's width |
|
, height = window.innerHeight - margin.top - margin.bottom; // Use the window's height |
|
*/ |
|
|
|
// append the svg object to the body of the page |
|
var svg_LDR = d3.select("#my_dataviz_LDR") |
|
.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 + ")"); |
|
|
|
var svg_TEMP = d3.select("#my_dataviz_TEMP") |
|
.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 + ")"); |
|
|
|
var svg_HUMIDITY = d3.select("#my_dataviz_HUMIDITY") |
|
.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 + ")"); |
|
|
|
//Read the data |
|
//d3.json("http://127.0.0.1:3001/sensor.meteo.json", function(data) { |
|
d3.csv("SENSOR.csv", function(data) { |
|
|
|
var tempC = []; |
|
var lux = []; |
|
var humidity = []; |
|
|
|
data.forEach(function(d){ |
|
d.ts = new Date(d.ts * 1000) |
|
|
|
// Calculate LDR vOut as LUX |
|
var res=10.0; // Resistance in the circuit of sensor 0 (KOhms) |
|
var vOut = d.LDR * 0.0048828125; // calculate the voltage |
|
//d.lux = 500 / (res * ((5-vOut) / vOut) ); // calculate the Lux |
|
d.lux=(2500/vOut-500)/res; |
|
|
|
tempC.push(d.tempC) |
|
lux.push(d.lux) |
|
humidity.push(d.humidity) |
|
}); |
|
|
|
data.stats = {} |
|
data.stats.tempC = calculateMeanSD(tempC); |
|
data.stats.lux = calculateMeanSD(lux); |
|
data.stats.humidity = calculateMeanSD(humidity); |
|
|
|
console.log(data.stats); |
|
|
|
// for each x tick append mean & sd (seems like data duplication) |
|
data.forEach(function(d){ |
|
d.tempC_mean = data.stats.tempC.mean; |
|
d.tempC_sd = data.stats.tempC.sd; |
|
d.lux_mean = data.stats.lux.mean; |
|
d.lux_sd = data.stats.lux.sd; |
|
d.humidity_mean = data.stats.humidity.mean; |
|
d.humidity_sd = data.stats.humidity.sd; |
|
}); |
|
|
|
renderVis(data); |
|
}); |
|
|
|
|
|
var calculateMeanSD = function(data) { |
|
|
|
var n = data.length; |
|
var m = data.reduce(function (a,b) { return Number(a) + Number(b) }) / n; |
|
var sd = Math.sqrt(data.reduce(function (sq, n) { |
|
return sq + Math.pow(n - m, 2); |
|
}, 0) / (data.length - 1)); |
|
|
|
var o = {}; |
|
o.items = n; |
|
o.mean = m; |
|
o.sd = sd; |
|
return o; |
|
} |
|
|
|
var renderVis = function(data) { |
|
|
|
// Add X axis --> it is a date format |
|
var x = d3.scaleTime() |
|
.domain(d3.extent(data, function(d) { return d.ts; })) |
|
.range([ 0, width ]); |
|
svg_LDR.append("g") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(d3.axisBottom(x)) |
|
.call(d3.axisBottom(x) |
|
.tickFormat(d3.timeFormat("%H:%M %p")) |
|
.ticks(15)) |
|
.selectAll("text") |
|
.style("text-anchor", "end") |
|
.attr("dx", "-.8em") |
|
.attr("dy", ".15em") |
|
.attr("transform", "rotate(-65)") |
|
; |
|
|
|
svg_TEMP.append("g") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(d3.axisBottom(x)) |
|
.call(d3.axisBottom(x) |
|
.tickFormat(d3.timeFormat("%H:%M %p")) |
|
.ticks(15)) |
|
.selectAll("text") |
|
.style("text-anchor", "end") |
|
.attr("dx", "-.8em") |
|
.attr("dy", ".15em") |
|
.attr("transform", "rotate(-65)") |
|
|
|
svg_HUMIDITY.append("g") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(d3.axisBottom(x)) |
|
.call(d3.axisBottom(x) |
|
.tickFormat(d3.timeFormat("%H:%M %p")) |
|
.ticks(15)) |
|
.selectAll("text") |
|
.style("text-anchor", "end") |
|
.attr("dx", "-.8em") |
|
.attr("dy", ".15em") |
|
.attr("transform", "rotate(-65)") |
|
|
|
// Add Y axis (vOut) |
|
var y = d3.scaleLinear() |
|
.domain([0, d3.max(data, function(d) { return +d.lux; })]) |
|
.range([ height, 0 ]); |
|
|
|
// Add y2 axis (Lux) |
|
var y1 = d3.scaleLinear() |
|
.domain([0, d3.max(data, function(d) { return +d.LDR; })]) |
|
.range([ height, 0 ]); |
|
|
|
svg_LDR.append("g") |
|
.call(d3.axisLeft(y1)); |
|
|
|
svg_LDR.append("g") |
|
.call(d3.axisRight(y)) |
|
.attr('transform', 'translate(' + (700) + ', 0)'); |
|
|
|
svg_LDR.append("text") |
|
.attr("text-anchor", "end") |
|
.attr("transform", "rotate(-90)") |
|
.attr("y", -margin.right+720) |
|
.attr("x", -margin.top) |
|
.text("LDR (vOut)") |
|
|
|
svg_LDR.append("text") |
|
.attr("text-anchor", "end") |
|
.attr("transform", "rotate(-90)") |
|
.attr("y", -margin.left+20) |
|
.attr("x", -margin.top) |
|
.text("Light (Lux)") |
|
|
|
// Add the line |
|
svg_LDR.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#FFFF00") |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.LDR) }) |
|
.curve(d3.curveMonotoneX) // apply smoothing to the line |
|
) |
|
|
|
// Add the line |
|
svg_LDR.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#FF0000") |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.lux) }) |
|
.curve(d3.curveMonotoneX) // apply smoothing to the line |
|
) |
|
|
|
// Plot Mean & Standard Deviation |
|
svg_LDR.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#808080") |
|
.style("stroke-dasharray", ("3, 3")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.lux_mean) }) |
|
) |
|
|
|
svg_LDR.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#C0C0C0") |
|
.style("stroke-dasharray", ("5, 5")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.lux_mean + d.lux_sd) }) |
|
) |
|
|
|
var tooltip_LDR = d3.select("#my_dataviz_LDR").append("div") |
|
.attr("class", "tooltip") |
|
.style("opacity", 0); |
|
|
|
|
|
// tooltip mouseover event handler |
|
var tipMouseover_LDR = function(d) { |
|
var color = "#FFFF00"; |
|
var html = "<b>" + d.LDR + "</b> LDR </b> (vOut)" |
|
tooltip_LDR.html(html) |
|
.style("left", (d3.event.pageX + 15) + "px") |
|
.style("top", (d3.event.pageY - 28) + "px") |
|
.transition() |
|
.duration(200) // ms |
|
.style("opacity", .9) // started as 0! |
|
; |
|
|
|
}; |
|
|
|
// tooltip mouseout event handler |
|
var tipMouseout_LDR = function(d) { |
|
tooltip_LDR.transition() |
|
.duration(300) // ms |
|
.style("opacity", 0); // don't care about position! |
|
}; |
|
|
|
// Add the scatterplot |
|
svg_LDR.selectAll("dot") |
|
.data(data) |
|
.enter().append("circle") |
|
.attr("r", 3.5) |
|
.attr("cx", function(d) { return x(d.ts); }) |
|
.attr("cy", function(d) { return y(d.LDR) + 7; }) |
|
.style("fill", function(d) { return "#FFFF00" }) |
|
.on("mouseover", tipMouseover_LDR) |
|
.on("mouseout", tipMouseout_LDR); |
|
|
|
|
|
// Add Y axis |
|
var y = d3.scaleLinear() |
|
.domain([0, d3.max(data, function(d) { return +d.tempC; })]) |
|
.range([ height, 0 ]); |
|
|
|
svg_TEMP.append("g") |
|
.call(d3.axisLeft(y)); |
|
|
|
svg_TEMP.append("text") |
|
.attr("text-anchor", "end") |
|
.attr("transform", "rotate(-90)") |
|
.attr("y", -margin.left+20) |
|
.attr("x", -margin.top) |
|
.text("Temperature (ºC)") |
|
|
|
// Add the line |
|
svg_TEMP.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#FF7F0E") |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.tempC) }) |
|
) |
|
|
|
// Plot Mean & Standard Deviation |
|
svg_TEMP.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#808080") |
|
.style("stroke-dasharray", ("3, 3")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.tempC_mean) }) |
|
) |
|
|
|
svg_TEMP.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#C0C0C0") |
|
.style("stroke-dasharray", ("5, 5")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.tempC_mean + d.tempC_sd) }) |
|
) |
|
|
|
svg_TEMP.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#C0C0C0") |
|
.style("stroke-dasharray", ("5, 5")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.tempC_mean - d.tempC_sd) }) |
|
) |
|
|
|
var tooltip_TEMP = d3.select("#my_dataviz_TEMP").append("div") |
|
.attr("class", "tooltip") |
|
.style("opacity", 0); |
|
|
|
|
|
// tooltip mouseover event handler |
|
var tipMouseover_TEMP = function(d) { |
|
var color = "#FF7F0E"; |
|
//var color = colorScale(d.LDR); |
|
var html = "<b>" + d.tempC + "</b> TEMP (Celcius)" |
|
tooltip_TEMP.html(html) |
|
.style("left", (d3.event.pageX + 15) + "px") |
|
.style("top", (d3.event.pageY - 28) + "px") |
|
.transition() |
|
.duration(200) // ms |
|
.style("opacity", .9) // started as 0! |
|
; |
|
|
|
}; |
|
|
|
// tooltip mouseout event handler |
|
var tipMouseout_TEMP = function(d) { |
|
tooltip_TEMP.transition() |
|
.duration(300) // ms |
|
.style("opacity", 0); // don't care about position! |
|
}; |
|
|
|
// Add the scatterplot |
|
svg_TEMP.selectAll("dot") |
|
.data(data) |
|
.enter().append("circle") |
|
.attr("r", 3.5) |
|
.attr("cx", function(d) { return x(d.ts); }) |
|
.attr("cy", function(d) { return y(d.tempC) + 7; }) |
|
.style("fill", function(d) { return "#FF7F0E" }) |
|
.on("mouseover", tipMouseover_TEMP) |
|
.on("mouseout", tipMouseout_TEMP); |
|
|
|
|
|
|
|
// Add Y axis |
|
var y = d3.scaleLinear() |
|
.domain([0, d3.max(data, function(d) { return +d.humidity; })]) |
|
.range([ height, 0 ]); |
|
|
|
svg_HUMIDITY.append("g") |
|
.call(d3.axisLeft(y)); |
|
|
|
svg_HUMIDITY.append("text") |
|
.attr("text-anchor", "end") |
|
.attr("transform", "rotate(-90)") |
|
.attr("y", -margin.left+20) |
|
.attr("x", -margin.top) |
|
.text("Humidity (%)") |
|
|
|
// Add the line |
|
svg_HUMIDITY.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#1f77b4") |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.humidity) }) |
|
) |
|
|
|
// Plot Mean & Standard Deviation |
|
svg_HUMIDITY.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#808080") |
|
.style("stroke-dasharray", ("3, 3")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.humidity_mean) }) |
|
) |
|
|
|
svg_HUMIDITY.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#C0C0C0") |
|
.style("stroke-dasharray", ("5, 5")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.humidity_mean + d.humidity_sd) }) |
|
) |
|
|
|
svg_HUMIDITY.append("path") |
|
.datum(data) |
|
.attr("class", "line") |
|
.attr("fill", "none") |
|
.attr("stroke", "#C0C0C0") |
|
.style("stroke-dasharray", ("5, 5")) |
|
.attr("d", d3.line() |
|
.x(function(d) { return x(d.ts) }) |
|
.y(function(d) { return y(d.humidity_mean - d.humidity_sd) }) |
|
) |
|
|
|
var tooltip = d3.select("#my_dataviz_HUMIDITY").append("div") |
|
.attr("class", "tooltip") |
|
.style("opacity", 0); |
|
|
|
|
|
// tooltip mouseover event handler |
|
var tipMouseover = function(d) { |
|
var color = "#1f77b4"; |
|
var html = "<b>" + d.humidity + "</b> Humidity (%)" |
|
tooltip.html(html) |
|
.style("left", (d3.event.pageX + 15) + "px") |
|
.style("top", (d3.event.pageY - 28) + "px") |
|
.transition() |
|
.duration(200) // ms |
|
.style("opacity", .9) // started as 0! |
|
; |
|
|
|
}; |
|
|
|
// tooltip mouseout event handler |
|
var tipMouseout = function(d) { |
|
tooltip.transition() |
|
.duration(300) // ms |
|
.style("opacity", 0); // don't care about position! |
|
}; |
|
|
|
// Add the scatterplot |
|
svg_HUMIDITY.selectAll("dot") |
|
.data(data) |
|
.enter().append("circle") |
|
.attr("r", 3.5) |
|
.attr("cx", function(d) { return x(d.ts); }) |
|
.attr("cy", function(d) { return y(d.humidity) + 7; }) |
|
.style("fill", function(d) { return "#1f77b4" }) |
|
.on("mouseover", tipMouseover) |
|
.on("mouseout", tipMouseout); |
|
|
|
|
|
|
|
}; |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |
D3.js (v3) Line Charts Displaying rduino - Sensor Data: Temp, Humidity, LDR
D3.js Chart Features: