|
@ -3,29 +3,23 @@ var SunCalc = require('suncalc') |
|
|
|
|
|
|
|
|
const lat = 39.9243509 |
|
|
const lat = 39.9243509 |
|
|
const lon = -75.1696126 |
|
|
const lon = -75.1696126 |
|
|
|
|
|
const leftMargin = 75 |
|
|
|
|
|
|
|
|
class Chart { |
|
|
class Chart { |
|
|
constructor(canvas, height, width, margin, offset, data, yrange) { |
|
|
|
|
|
|
|
|
constructor(canvas, width, height, offset, margin, data, yrange) { |
|
|
this.canvas = canvas |
|
|
this.canvas = canvas |
|
|
this.height = height |
|
|
|
|
|
this.width = width |
|
|
this.width = width |
|
|
this.margin = margin |
|
|
|
|
|
|
|
|
this.height = height |
|
|
this.offset = offset |
|
|
this.offset = offset |
|
|
|
|
|
this.margin = margin |
|
|
this.data = data |
|
|
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() |
|
|
this.yRange = d3.scaleLinear() |
|
|
.domain(yrange) |
|
|
.domain(yrange) |
|
|
.range([this.height - this.margin.bottom, offset]) |
|
|
|
|
|
|
|
|
.range([height - margin.bottom, offset]) |
|
|
this.xRange = d3.scaleTime() |
|
|
this.xRange = d3.scaleTime() |
|
|
.domain([this.beginTime, this.endTime]) |
|
|
|
|
|
.range([margin.left, width]) |
|
|
|
|
|
|
|
|
.domain([data.beginTime, data.endTime]) |
|
|
|
|
|
.range([margin.left, width - margin.right]) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
draw_yAxisLine() { |
|
|
draw_yAxisLine() { |
|
@ -66,13 +60,6 @@ class Chart { |
|
|
.text(label) |
|
|
.text(label) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
set_weekday(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) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
set_xAxisTic(value, label, anchor) { |
|
|
set_xAxisTic(value, label, anchor) { |
|
|
const x = this.xRange(value) |
|
|
const x = this.xRange(value) |
|
|
this.canvas.append('line').attr('class', 'axis') |
|
|
this.canvas.append('line').attr('class', 'axis') |
|
@ -107,42 +94,133 @@ class Chart { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const drawNights = function (chart) { |
|
|
|
|
|
const year = chart.beginTime.getFullYear() |
|
|
|
|
|
const month = chart.beginTime.getMonth() |
|
|
|
|
|
for (var day = chart.beginTime.getDate(); day <= chart.endTime.getDate(); day++) { |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Draw days to show time |
|
|
|
|
|
*/ |
|
|
|
|
|
export function render_calendar(canvas, width, height, offset, data) { |
|
|
|
|
|
const margin = { |
|
|
|
|
|
top: 0, |
|
|
|
|
|
left: leftMargin, |
|
|
|
|
|
bottom: 0, |
|
|
|
|
|
right: 0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const xRange = d3.scaleTime() |
|
|
|
|
|
.domain([data.beginTime, data.endTime]) |
|
|
|
|
|
.range([margin.left, width]) |
|
|
|
|
|
|
|
|
|
|
|
const calendar = canvas.append('g').attr('id', 'calendar') |
|
|
|
|
|
const weekdays = calendar.append('g').attr('id', 'weekdays') |
|
|
|
|
|
const dates = calendar.append('g').attr('id', 'dates') |
|
|
|
|
|
|
|
|
|
|
|
let noons = [] |
|
|
|
|
|
let days = [] |
|
|
|
|
|
const year = data.beginTime.getFullYear() |
|
|
|
|
|
const month = data.beginTime.getMonth() |
|
|
|
|
|
for (var day = data.beginTime.getDate(); day <= data.endTime.getDate(); day++) { |
|
|
|
|
|
const dayBegin = new Date(year, month, day, 0, 1, 0) |
|
|
|
|
|
const noon = new Date(year, month, day, 12, 0, 0) |
|
|
|
|
|
noons.push(noon) |
|
|
|
|
|
days.push(dayBegin) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
weekdays.selectAll('text').data(noons).enter() |
|
|
|
|
|
.append('text') |
|
|
|
|
|
.attr('text-anchor', 'middle').attr('dominant-baseline', 'alphabetic') |
|
|
|
|
|
.attr('x', function(d) {return xRange(d)}) |
|
|
|
|
|
.attr('y', height - 5) |
|
|
|
|
|
.text(function (d) { |
|
|
|
|
|
return d.toLocaleDateString('en-US', {weekday: 'long'}) |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
dates.selectAll('text').data(days).enter() |
|
|
|
|
|
.append('text') |
|
|
|
|
|
.attr('text-anchor', 'begin').attr('dominant-baseline', 'hanging') |
|
|
|
|
|
.attr('x', function(d) {return xRange(d) + 3}) |
|
|
|
|
|
.attr('y', 0) |
|
|
|
|
|
.text(function (d) { |
|
|
|
|
|
return d.toLocaleDateString('en-US', {day: 'numeric'}) |
|
|
|
|
|
}) |
|
|
|
|
|
dates.selectAll('line').data(days).enter() |
|
|
|
|
|
.append('line').attr('class', 'axis') |
|
|
|
|
|
.attr('x1', function(d) {return xRange(d)}).attr('y1', 0) |
|
|
|
|
|
.attr('x2', function(d) {return xRange(d)}).attr('y2', height) |
|
|
|
|
|
|
|
|
|
|
|
calendar.append('line').attr('class', 'axis') |
|
|
|
|
|
.attr('x1', margin.left).attr('y1', height) |
|
|
|
|
|
.attr('x2', width - margin.right).attr('y2', height) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Draw sky brightness bars |
|
|
|
|
|
*/ |
|
|
|
|
|
export function render_nights(canvas, width, height, offset, data) { |
|
|
|
|
|
const margin = { |
|
|
|
|
|
top: 0, |
|
|
|
|
|
left: leftMargin, |
|
|
|
|
|
bottom: 0, |
|
|
|
|
|
right: 0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const xRange = d3.scaleTime() |
|
|
|
|
|
.domain([data.beginTime, data.endTime]) |
|
|
|
|
|
.range([margin.left, width]) |
|
|
|
|
|
|
|
|
|
|
|
const nights = canvas.append('g').attr('id', 'nights') |
|
|
|
|
|
|
|
|
|
|
|
const draw_box = function (x1, x2, style) { |
|
|
|
|
|
const x = xRange(x1) |
|
|
|
|
|
const width = xRange(x2) - x |
|
|
|
|
|
nights.append('rect').attr('class', style) |
|
|
|
|
|
.attr('x', x).attr('y', offset) |
|
|
|
|
|
.attr('width', width) |
|
|
|
|
|
.attr('height', height - margin.bottom) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const year = data.beginTime.getFullYear() |
|
|
|
|
|
const month = data.beginTime.getMonth() |
|
|
|
|
|
for (var day = data.beginTime.getDate(); day <= data.endTime.getDate(); day++) { |
|
|
const midnight = new Date(year, month, day, 0, 0, 0) |
|
|
const midnight = new Date(year, month, day, 0, 0, 0) |
|
|
const sunEphem = SunCalc.getTimes(new Date(year, month, day, 12, 0, 0), lat, lon) |
|
|
const sunEphem = SunCalc.getTimes(new Date(year, month, day, 12, 0, 0), lat, lon) |
|
|
chart.draw_box(midnight, sunEphem.nauticalDawn, 'night') |
|
|
|
|
|
chart.draw_box(sunEphem.nauticalDawn, sunEphem.sunrise, 'twilight') |
|
|
|
|
|
chart.draw_box(sunEphem.sunset, sunEphem.nauticalDusk, 'twilight') |
|
|
|
|
|
chart.draw_box(sunEphem.nauticalDusk, new Date(year, month, day, 24, 0, 0), 'night') |
|
|
|
|
|
|
|
|
draw_box(midnight, sunEphem.nauticalDawn, 'night') |
|
|
|
|
|
draw_box(sunEphem.nauticalDawn, sunEphem.sunrise, 'twilight') |
|
|
|
|
|
draw_box(sunEphem.sunset, sunEphem.nauticalDusk, 'twilight') |
|
|
|
|
|
draw_box(sunEphem.nauticalDusk, new Date(year, month, day, 24, 0, 0), 'night') |
|
|
|
|
|
nights.append('line').attr('class', 'midnight') |
|
|
|
|
|
.attr('x1', xRange(midnight)).attr('y1', offset) |
|
|
|
|
|
.attr('x2', xRange(midnight)).attr('y2', height) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Sun and moon chart |
|
|
* Sun and moon chart |
|
|
*/ |
|
|
*/ |
|
|
export function render_astronomy(canvas, height, offset, data) { |
|
|
|
|
|
const fullWidth = canvas.node().getBoundingClientRect().width |
|
|
|
|
|
const margins = { |
|
|
|
|
|
'left': 75, |
|
|
|
|
|
'bottom': 15 |
|
|
|
|
|
|
|
|
export function render_astronomy(canvas, width, height, offset, data) { |
|
|
|
|
|
const margin = { |
|
|
|
|
|
top: 0, |
|
|
|
|
|
left: leftMargin, |
|
|
|
|
|
bottom: 12, |
|
|
|
|
|
right: 0 |
|
|
} |
|
|
} |
|
|
const yrange = [0, 75 * (Math.PI/180)] |
|
|
|
|
|
const chart = new Chart(canvas, height, fullWidth, margins, offset, data, yrange) |
|
|
|
|
|
|
|
|
const yrange = [0, 80 * (Math.PI/180)] |
|
|
|
|
|
const chart = new Chart(canvas, width, height, offset, margin, data, yrange) |
|
|
|
|
|
|
|
|
|
|
|
const x = chart.xRange(data.beginTime) |
|
|
|
|
|
const box_width = chart.xRange(data.endTime) - x |
|
|
|
|
|
canvas.append('rect').attr('class', 'blank') |
|
|
|
|
|
.attr('x', x).attr('y', height - margin.bottom) |
|
|
|
|
|
.attr('width', box_width) |
|
|
|
|
|
.attr('height', margin.bottom) |
|
|
|
|
|
|
|
|
chart.set_yAxisTic( 0, '0°') |
|
|
chart.set_yAxisTic( 0, '0°') |
|
|
chart.set_yAxisTic(26.6 * (Math.PI/180), '27°') |
|
|
chart.set_yAxisTic(26.6 * (Math.PI/180), '27°') |
|
|
chart.set_yAxisTic(75 * (Math.PI/180), '75°') |
|
|
chart.set_yAxisTic(75 * (Math.PI/180), '75°') |
|
|
|
|
|
|
|
|
drawNights(chart) |
|
|
|
|
|
|
|
|
|
|
|
const year = chart.beginTime.getFullYear() |
|
|
|
|
|
const month = chart.beginTime.getMonth() |
|
|
|
|
|
|
|
|
|
|
|
let sunData = [] |
|
|
let sunData = [] |
|
|
for (var day = chart.beginTime.getDate(); day <= chart.endTime.getDate(); day++) { |
|
|
|
|
|
|
|
|
const year = data.beginTime.getFullYear() |
|
|
|
|
|
const month = data.beginTime.getMonth() |
|
|
|
|
|
for (var day = data.beginTime.getDate(); day <= data.endTime.getDate(); day++) { |
|
|
const midnight = new Date(year, month, day, 0, 0, 0) |
|
|
const midnight = new Date(year, month, day, 0, 0, 0) |
|
|
|
|
|
|
|
|
// Solar Ephemeris
|
|
|
// Solar Ephemeris
|
|
@ -191,20 +269,8 @@ export function render_astronomy(canvas, height, offset, data) { |
|
|
.attr('class', 'sun') |
|
|
.attr('class', 'sun') |
|
|
.attr('d', line) |
|
|
.attr('d', line) |
|
|
|
|
|
|
|
|
for (var day = chart.beginTime.getDate(); day <= chart.endTime.getDate(); day++) { |
|
|
|
|
|
const noon = new Date(year, month, day, 12, 0, 0) |
|
|
|
|
|
const midnight = new Date(year, month, day, 0, 0, 0) |
|
|
|
|
|
chart.set_weekday(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()) { |
|
|
|
|
|
chart.draw_yGrid(midnight, 'midnight') |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
chart.draw_yAxisLine() |
|
|
|
|
|
chart.draw_xAxisLineTop() |
|
|
|
|
|
|
|
|
// chart.draw_yAxisLine()
|
|
|
|
|
|
// chart.draw_xAxisLineTop()
|
|
|
chart.draw_xAxisLineBottom() |
|
|
chart.draw_xAxisLineBottom() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -212,22 +278,21 @@ export function render_astronomy(canvas, height, offset, data) { |
|
|
/** |
|
|
/** |
|
|
* Temperature chart |
|
|
* Temperature chart |
|
|
*/ |
|
|
*/ |
|
|
export function render_temperature(canvas, height, offset, data) { |
|
|
|
|
|
const fullWidth = canvas.node().getBoundingClientRect().width |
|
|
|
|
|
const margins = { |
|
|
|
|
|
'left': 75, |
|
|
|
|
|
'bottom': 10 |
|
|
|
|
|
|
|
|
export function render_temperature(canvas, width, height, offset, data) { |
|
|
|
|
|
const margin = { |
|
|
|
|
|
top: 0, |
|
|
|
|
|
left: leftMargin, |
|
|
|
|
|
bottom: 12, |
|
|
|
|
|
right: 0 |
|
|
} |
|
|
} |
|
|
const yrange = [-15, 45] |
|
|
const yrange = [-15, 45] |
|
|
const chart = new Chart(canvas, height, fullWidth, margins, offset, data, yrange) |
|
|
|
|
|
|
|
|
const chart = new Chart(canvas, width, height, offset, margin, data, yrange) |
|
|
|
|
|
|
|
|
const line = d3.line() |
|
|
const line = d3.line() |
|
|
.x(function(d) { return chart.xRange(d.date) }) |
|
|
.x(function(d) { return chart.xRange(d.date) }) |
|
|
.y(function(d) { return chart.yRange(d.temp) }) |
|
|
.y(function(d) { return chart.yRange(d.temp) }) |
|
|
.curve(d3.curveBasis) |
|
|
.curve(d3.curveBasis) |
|
|
|
|
|
|
|
|
drawNights(chart) |
|
|
|
|
|
|
|
|
|
|
|
chart.draw_yAxisTitle('Temperature') |
|
|
chart.draw_yAxisTitle('Temperature') |
|
|
chart.set_yAxisTic(-10, '-10 °C') |
|
|
chart.set_yAxisTic(-10, '-10 °C') |
|
|
chart.draw_xGrid( -10, 'guide') |
|
|
chart.draw_xGrid( -10, 'guide') |
|
@ -242,24 +307,12 @@ export function render_temperature(canvas, height, offset, data) { |
|
|
chart.set_yAxisTic( 40, '40 °C') |
|
|
chart.set_yAxisTic( 40, '40 °C') |
|
|
chart.draw_xGrid( 40, 'guide') |
|
|
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') |
|
|
canvas.append('path') |
|
|
.data([data.temperature]) |
|
|
.data([data.temperature]) |
|
|
.attr('class', 'data') |
|
|
.attr('class', 'data') |
|
|
.attr('d', line) |
|
|
.attr('d', line) |
|
|
|
|
|
|
|
|
chart.draw_yAxisLine() |
|
|
|
|
|
chart.draw_xAxisLineTop() |
|
|
|
|
|
|
|
|
//chart.draw_yAxisLine()
|
|
|
|
|
|
//chart.draw_xAxisLineTop()
|
|
|
chart.draw_xAxisLineBottom() |
|
|
chart.draw_xAxisLineBottom() |
|
|
} |
|
|
} |