diff --git a/src/index.js b/src/index.js index 7bdab8a..47bee58 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ var d3 = require('d3') import {readxml} from './data.js' -import {render} from './render.js' +import {render_astronomy, render_temperature} from './render.js' let canvas = d3.select('svg') @@ -8,5 +8,6 @@ let canvas = d3.select('svg') const url = '/forcast.xml' d3.xml(url).then(function(xmldoc) { let data = readxml(xmldoc) - render(canvas, data) + render_astronomy(canvas, 100, 25, data) + render_temperature(canvas, 600, 100, data) }) diff --git a/src/render.js b/src/render.js index 2c5679f..541c99c 100644 --- a/src/render.js +++ b/src/render.js @@ -1,110 +1,173 @@ var d3 = require('d3') -export function render(canvas, data) { - const xWidth = canvas.node().getBoundingClientRect().width - const yHeight = canvas.node().getBoundingClientRect().height - const xMargin = 50 - const yMargin = 25 - const nElements = data.startTimes.length - - const beginTime = new Date( - data.startTimes[0].getFullYear(), - data.startTimes[0].getMonth(), - data.startTimes[0].getDate(), - 0,0,0 - ) - - const xRange = d3.scaleTime() - .domain([beginTime, data.endTimes[nElements - 1]]) - .range([xMargin, xWidth]) - const yRange = d3.scaleLinear() - .domain([-15, 45]) - .range([yHeight - yMargin, 0]) - - const xLine = function (location, style) { - canvas.append('line') - .attr('class', style) - .attr('x1', xMargin) - .attr('x2', xWidth) - .attr('y1', yRange(location)) - .attr('y2', yRange(location)) +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) } - const yLine = function (date, style) { - canvas.append('line') - .attr('class', style) - .attr('x1', xRange(date)) - .attr('x2', xRange(date)) - .attr('y1', 0) - .attr('y2', yHeight - yMargin) + + 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) } - const xAxisLabel = function (date) { - canvas.append('text') - .attr('class', 'axis-label') - .attr('text-anchor', 'middle') - .attr('dominant-baseline', 'alphabetic') - .attr('x', xRange(date)) - .attr('y', yHeight - yMargin + 12) - .text(date.toLocaleDateString('en-US', {weekday: 'long'})) + + 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) } - const yAxisLabel = function (value) { - canvas.append('text') - .attr('class', 'axis-label') - .attr('text-anchor', 'end') - .attr('dominant-baseline', 'middle') - .attr('x', xMargin - 5) - .attr('y', yRange(value)) - .text(value + ' °C') + + 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 xRange(d.date) }) - .y(function(d) { return yRange(d.temp) }) + .x(function(d) { return chart.xRange(d.date) }) + .y(function(d) { return chart.yRange(d.temp) }) .curve(d3.curveBasis) - //////////////////////////////////////////////////////////////////////////////////////////////// - // Paint + 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') - yLine(Date.now(), 'now') - for (let time of data.startTimes) { - if (time.getHours() === 0) { - yLine(time, 'midnight') - } - if (time.getHours() === 12) { - yLine(time, 'noon') - xAxisLabel(time) + 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') } } - xLine(-10, 'guide') - xLine( 0, 'zero') - xLine( 12, 'guide') - xLine( 22, 'guide') - xLine( 30, 'hot') - xLine( 40, 'guide') - yAxisLabel(-10) - yAxisLabel(0) - yAxisLabel(12) - yAxisLabel(22) - yAxisLabel(30) - yAxisLabel(40) - canvas.append('path') .data([data.temperature]) .attr('class', 'data') .attr('d', line) - // Axis Lines - canvas.append('line') - .attr('class', 'axis') - .attr('x1', xMargin) - .attr('x2', xMargin) - .attr('y1', 0) - .attr('y2', yHeight - yMargin) - canvas.append('line') - .attr('class', 'axis') - .attr('x1', xMargin) - .attr('x2', xWidth) - .attr('y1', yHeight - yMargin) - .attr('y2', yHeight - yMargin) + chart.draw_yAxisLine() + chart.draw_xAxisLineTop() + chart.draw_xAxisLineBottom() }