Weather dashboard
https://natronics.org/weather/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
6.0 KiB
173 lines
6.0 KiB
var d3 = require('d3')
|
|
|
|
|
|
class Chart {
|
|
constructor(canvas, height, width, margin, offset, data, yrange) {
|
|
this.canvas = canvas
|
|
this.height = height
|
|
this.width = width
|
|
this.margin = margin
|
|
this.offset = offset
|
|
this.data = data
|
|
|
|
this.beginTime = new Date(
|
|
data.startTimes[0].getFullYear(),
|
|
data.startTimes[0].getMonth(),
|
|
data.startTimes[0].getDate(),
|
|
0,0,0
|
|
)
|
|
this.endTime = data.endTimes[data.endTimes.length - 1]
|
|
this.yRange = d3.scaleLinear()
|
|
.domain(yrange)
|
|
.range([this.height - this.margin.bottom, offset])
|
|
this.xRange = d3.scaleTime()
|
|
.domain([this.beginTime, this.endTime])
|
|
.range([margin.left, width])
|
|
}
|
|
|
|
draw_yAxisLine() {
|
|
this.canvas.append('line').attr('class', 'axis')
|
|
.attr('x1', this.margin.left).attr('y1', this.offset)
|
|
.attr('x2', this.margin.left).attr('y2', this.height - this.margin.bottom)
|
|
}
|
|
|
|
draw_xAxisLineTop() {
|
|
this.canvas.append('line').attr('class', 'axis')
|
|
.attr('x1', this.margin.left).attr('y1', this.offset)
|
|
.attr('x2', this.width).attr('y2', this.offset)
|
|
}
|
|
|
|
draw_xAxisLineBottom() {
|
|
this.canvas.append('line').attr('class', 'axis')
|
|
.attr('x1', this.margin.left).attr('y1', this.height - this.margin.bottom)
|
|
.attr('x2', this.width).attr('y2', this.height - this.margin.bottom)
|
|
}
|
|
|
|
draw_yAxisTitle(title) {
|
|
const middle = (this.offset + this.height - this.margin.bottom) / 2.0
|
|
const x = 15
|
|
this.canvas.append('text').attr('class', 'axis-title')
|
|
.attr('text-anchor', 'middle').attr('dominant-baseline', 'middle')
|
|
.attr('transform', 'rotate(-90,' + x + ',' + middle + ')')
|
|
.attr('x', x).attr('y', middle)
|
|
.text(title)
|
|
}
|
|
|
|
set_yAxisTic(value, label) {
|
|
this.canvas.append('line').attr('class', 'axis')
|
|
.attr('x1', this.margin.left - 4).attr('y1', this.yRange(value))
|
|
.attr('x2', this.margin.left).attr('y2', this.yRange(value))
|
|
this.canvas.append('text').attr('class', 'axis-label')
|
|
.attr('text-anchor', 'end').attr('dominant-baseline', 'middle')
|
|
.attr('x', this.margin.left - 6).attr('y', this.yRange(value))
|
|
.text(label)
|
|
}
|
|
|
|
set_xAxisTic(value, label) {
|
|
this.canvas.append('text').attr('class', 'axis-label')
|
|
.attr('text-anchor', 'middle').attr('dominant-baseline', 'alphabetic')
|
|
.attr('x', this.xRange(value)).attr('y', this.offset - 5)
|
|
.text(label)
|
|
}
|
|
|
|
draw_yGrid(value, style) {
|
|
this.canvas.append('line').attr('class', style)
|
|
.attr('x1', this.xRange(value)).attr('y1', this.offset)
|
|
.attr('x2', this.xRange(value)).attr('y2', this.height - this.margin.bottom)
|
|
}
|
|
|
|
draw_xGrid(value, style) {
|
|
this.canvas.append('line').attr('class', style)
|
|
.attr('x1', this.margin.left).attr('y1', this.yRange(value))
|
|
.attr('x2', this.width).attr('y2', this.yRange(value))
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Sun and moon chart
|
|
*/
|
|
export function render_astronomy(canvas, height, offset, data) {
|
|
const fullWidth = canvas.node().getBoundingClientRect().width
|
|
const margins = {
|
|
'left': 75,
|
|
'bottom': 10
|
|
}
|
|
const yrange = [0, 75]
|
|
const chart = new Chart(canvas, height, fullWidth, margins, offset, data, yrange)
|
|
|
|
chart.draw_yAxisTitle('Altitude')
|
|
chart.set_yAxisTic( 0, '0°')
|
|
chart.set_yAxisTic(45, '45°')
|
|
chart.set_yAxisTic(75, '75°')
|
|
|
|
const year = chart.beginTime.getFullYear()
|
|
const month = chart.beginTime.getMonth()
|
|
|
|
for (var day = chart.beginTime.getDate(); day <= chart.endTime.getDate(); day++) {
|
|
const noon = new Date(year, month, day, 12, 0, 0)
|
|
chart.set_xAxisTic(noon, noon.toLocaleDateString('en-US', {weekday: 'long'}))
|
|
chart.draw_yGrid(noon, 'noon')
|
|
|
|
// Don't draw the first midnight line because that's also our yAxis line
|
|
if (day != chart.beginTime.getDate()) {
|
|
const midnight = new Date(year, month, day, 0, 0, 0)
|
|
chart.draw_yGrid(midnight, 'midnight')
|
|
}
|
|
}
|
|
|
|
chart.draw_yAxisLine()
|
|
chart.draw_xAxisLineTop()
|
|
chart.draw_xAxisLineBottom()
|
|
}
|
|
|
|
|
|
export function render_temperature(canvas, height, offset, data) {
|
|
const fullWidth = canvas.node().getBoundingClientRect().width
|
|
const margins = {
|
|
'left': 75,
|
|
'bottom': 10
|
|
}
|
|
const yrange = [-15, 45]
|
|
const chart = new Chart(canvas, height, fullWidth, margins, offset, data, yrange)
|
|
|
|
const line = d3.line()
|
|
.x(function(d) { return chart.xRange(d.date) })
|
|
.y(function(d) { return chart.yRange(d.temp) })
|
|
.curve(d3.curveBasis)
|
|
|
|
chart.draw_yAxisTitle('Temperature')
|
|
chart.set_yAxisTic(-10, '-10 °C')
|
|
chart.draw_xGrid( -10, 'guide')
|
|
chart.set_yAxisTic( 0, '0 °C')
|
|
chart.draw_xGrid( 0, 'zero')
|
|
chart.set_yAxisTic( 12, '12 °C')
|
|
chart.draw_xGrid( 12, 'guide')
|
|
chart.set_yAxisTic( 22, '22 °C')
|
|
chart.draw_xGrid( 22, 'guide')
|
|
chart.set_yAxisTic( 30, '30 °C')
|
|
chart.draw_xGrid( 30, 'hot')
|
|
chart.set_yAxisTic( 40, '40 °C')
|
|
chart.draw_xGrid( 40, 'guide')
|
|
|
|
const year = chart.beginTime.getFullYear()
|
|
const month = chart.beginTime.getMonth()
|
|
for (var day = chart.beginTime.getDate(); day <= chart.endTime.getDate(); day++) {
|
|
const noon = new Date(year, month, day, 12, 0, 0)
|
|
chart.draw_yGrid(noon, 'noon')
|
|
// Don't draw the first midnight line because that's also our yAxis line
|
|
if (day != chart.beginTime.getDate()) {
|
|
const midnight = new Date(year, month, day, 0, 0, 0)
|
|
chart.draw_yGrid(midnight, 'midnight')
|
|
}
|
|
}
|
|
|
|
canvas.append('path')
|
|
.data([data.temperature])
|
|
.attr('class', 'data')
|
|
.attr('d', line)
|
|
|
|
chart.draw_yAxisLine()
|
|
chart.draw_xAxisLineTop()
|
|
chart.draw_xAxisLineBottom()
|
|
}
|