Browse Source

Big refactor of drawing background

master
Nathan Bergey 6 years ago
parent
commit
bb0c607b33
  1. 31
      src/index.js
  2. 207
      src/render.js
  3. 24
      src/style.sass

31
src/index.js

@ -1,13 +1,36 @@
var d3 = require('d3') var d3 = require('d3')
import {readxml} from './data.js' import {readxml} from './data.js'
import {render_astronomy, render_temperature} from './render.js'
import {render_calendar, render_nights, render_astronomy, render_temperature} from './render.js'
let canvas = d3.select('svg')
const canvas = d3.select('svg')
//const url = 'https://forecast.weather.gov/MapClick.php?lat=39.9243509&lon=-75.1696126&FcstType=digitalDWML' //const url = 'https://forecast.weather.gov/MapClick.php?lat=39.9243509&lon=-75.1696126&FcstType=digitalDWML'
const url = '/forcast.xml' const url = '/forcast.xml'
const render = function (data) {
data.beginTime = new Date(
data.startTimes[0].getFullYear(),
data.startTimes[0].getMonth(),
data.startTimes[0].getDate(),
0,0,0
)
data.endTime = new Date(
data.endTimes[data.endTimes.length - 1].getFullYear(),
data.endTimes[data.endTimes.length - 1].getMonth(),
data.endTimes[data.endTimes.length - 1].getDate(),
23,59,59
)
const width = canvas.node().getBoundingClientRect().width
const height = canvas.node().getBoundingClientRect().height
render_calendar(canvas, width, 20, 0, data)
render_nights(canvas, width, height - 20, 20, data)
render_astronomy(canvas, width, 70, 20, data)
render_temperature(canvas, width, 450, 70, data)
}
d3.xml(url).then(function(xmldoc) { d3.xml(url).then(function(xmldoc) {
let data = readxml(xmldoc) let data = readxml(xmldoc)
render_astronomy(canvas, 80, 18, data)
render_temperature(canvas, 500, 80, data)
render(data)
}) })

207
src/render.js

@ -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()
} }

24
src/style.sass

@ -6,10 +6,13 @@ html, body
height: 100% height: 100%
font-family: sans-serif font-family: sans-serif
$primary-font: sans-serif
.wrapper .wrapper
min-height: 100% min-height: 100%
margin-bottom: -25px margin-bottom: -25px
position: relative position: relative
margin-right: 10px
.wrapper:after .wrapper:after
content: "" content: ""
@ -17,11 +20,9 @@ html, body
svg svg
position: absolute position: absolute
background: #f7f7f7
width: 100% width: 100%
height: 90% height: 90%
footer, .wrapper:after footer, .wrapper:after
height: 25px height: 25px
@ -44,6 +45,22 @@ h1.title
path, line path, line
fill: none fill: none
line
shape-rendering: crispEdges
#calendar .axis
stroke: #bbb
stroke-width: 1px
#weekdays text
font: bold 12px $primary-font
#dates text
font: normal 10px $primary-font
.blank
fill: rgba(255, 255, 255, 0.7)
path.data path.data
stroke: #f00 stroke: #f00
stroke-width: 2px stroke-width: 2px
@ -57,9 +74,6 @@ path.moon
stroke: #999 stroke: #999
stroke-width: 1px stroke-width: 1px
line
shape-rendering: crispEdges
line.now line.now
stroke: #f5f stroke: #f5f
stroke-width: 1px stroke-width: 1px

Loading…
Cancel
Save