Browse Source

Cloud coverage chart with lots of realignment

master
Nathan Bergey 6 years ago
parent
commit
db718e185f
  1. 58
      src/data.js
  2. 16
      src/index.js
  3. 60
      src/render.js
  4. 8
      src/style.sass

58
src/data.js

@ -1,50 +1,72 @@
var d3 = require('d3') var d3 = require('d3')
export function readxml(xmldoc) { export function readxml(xmldoc) {
let forcast = xmldoc.childNodes[0]
let data = forcast.children[1]
const forcast = xmldoc.childNodes[0]
const data = forcast.children[1]
// Time // Time
let times = data.getElementsByTagName('time-layout')[0]
var startTimes = []
const times = data.getElementsByTagName('time-layout')[0]
let startTimes = []
for (let time of times.getElementsByTagName('start-valid-time')) { for (let time of times.getElementsByTagName('start-valid-time')) {
let date = new Date(time.textContent)
const date = new Date(time.textContent)
startTimes.push(date) startTimes.push(date)
} }
var endTimes = []
let endTimes = []
for (let time of times.getElementsByTagName('end-valid-time')) { for (let time of times.getElementsByTagName('end-valid-time')) {
let date = new Date(time.textContent)
const date = new Date(time.textContent)
endTimes.push(date) endTimes.push(date)
} }
// Temperature // Temperature
var temperatures = []
let temperatures = []
for (let temp_type of data.getElementsByTagName('temperature')) { for (let temp_type of data.getElementsByTagName('temperature')) {
if (temp_type.attributes.type.nodeValue === 'hourly') { if (temp_type.attributes.type.nodeValue === 'hourly') {
for (let temp of temp_type.getElementsByTagName('value')) { for (let temp of temp_type.getElementsByTagName('value')) {
let degC = (parseFloat(temp.textContent) - 32) * (5.0/9.0)
const degC = (parseFloat(temp.textContent) - 32) * (5.0/9.0)
temperatures.push(degC) temperatures.push(degC)
} }
} }
} }
// Cloud Cover
let cloudCover = []
const clouds = data.getElementsByTagName('cloud-amount')[0]
for (let cloud of clouds.getElementsByTagName('value')) {
cloudCover.push(parseFloat(cloud.textContent))
}
// Map // Map
let nElements = startTimes.length
var forcastData = []
const nElements = startTimes.length
let forcastData = []
let cloudData = []
cloudData.push({
date: startTimes[0],
coverage: 0
})
for (var i = 0; i < nElements; i++) { for (var i = 0; i < nElements; i++) {
let midtimeScale = d3.scaleTime()
const midtimeScale = d3.scaleTime()
.domain([0, 1]) .domain([0, 1])
.range([startTimes[i], endTimes[i]]) .range([startTimes[i], endTimes[i]])
let midpointTime = midtimeScale(0.5)
const midpointTime = midtimeScale(0.5)
forcastData.push({ forcastData.push({
'date': midpointTime,
'temp': temperatures[i]
date: midpointTime,
temp: temperatures[i]
})
cloudData.push({
date: midpointTime,
coverage: cloudCover[i]
}) })
} }
cloudData.push({
date: endTimes[endTimes.length - 1],
coverage: 0
})
return { return {
'startTimes': startTimes,
'endTimes': endTimes,
'temperature': forcastData
startTimes: startTimes,
endTimes: endTimes,
temperature: forcastData,
clouds: cloudData
} }
} }

16
src/index.js

@ -1,6 +1,6 @@
var d3 = require('d3') var d3 = require('d3')
import {readxml} from './data.js' import {readxml} from './data.js'
import {render_calendar, render_nights, render_astronomy, render_temperature} from './render.js'
import {render_calendar, render_nights, render_astronomy, render_temperature, render_clouds} from './render.js'
const canvas = d3.select('svg') const canvas = d3.select('svg')
@ -24,10 +24,16 @@ const render = function (data) {
const width = canvas.node().getBoundingClientRect().width const width = canvas.node().getBoundingClientRect().width
const height = canvas.node().getBoundingClientRect().height 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, 350, 70, data)
const calHeight = 20
const astroHeight = 50
const tempHeight = 300
const cloudHeight = height - (calHeight +tempHeight + astroHeight)
render_calendar(canvas, width, calHeight, 0, data)
render_nights(canvas, width, height - calHeight, calHeight, data)
render_astronomy(canvas, width, astroHeight, calHeight, data)
render_temperature(canvas, width, tempHeight, calHeight + astroHeight, data)
render_clouds(canvas, width, cloudHeight, calHeight + astroHeight + tempHeight, data)
} }
d3.xml(url).then(function(xmldoc) { d3.xml(url).then(function(xmldoc) {

60
src/render.js

@ -14,9 +14,12 @@ class Chart {
this.margin = margin this.margin = margin
this.data = data this.data = data
this.top = offset + margin.top
this.bottom = offset + height - margin.bottom
this.yRange = d3.scaleLinear() this.yRange = d3.scaleLinear()
.domain(yrange) .domain(yrange)
.range([height - margin.bottom, offset])
.range([this.bottom, this.top])
this.xRange = d3.scaleTime() this.xRange = d3.scaleTime()
.domain([data.beginTime, data.endTime]) .domain([data.beginTime, data.endTime])
.range([margin.left, width - margin.right]) .range([margin.left, width - margin.right])
@ -24,24 +27,24 @@ class Chart {
draw_yAxisLine() { draw_yAxisLine() {
this.canvas.append('line').attr('class', 'axis') 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)
.attr('x1', this.margin.left).attr('y1', this.top)
.attr('x2', this.margin.left).attr('y2', this.bottom)
} }
draw_xAxisLineTop() { draw_xAxisLineTop() {
this.canvas.append('line').attr('class', 'axis') this.canvas.append('line').attr('class', 'axis')
.attr('x1', this.margin.left).attr('y1', this.offset)
.attr('x2', this.width).attr('y2', this.offset)
.attr('x1', this.margin.left).attr('y1', this.top)
.attr('x2', this.width - this.margin.right).attr('y2', this.top)
} }
draw_xAxisLineBottom() { draw_xAxisLineBottom() {
this.canvas.append('line').attr('class', 'axis') 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)
.attr('x1', this.margin.left).attr('y1', this.bottom)
.attr('x2', this.width - this.margin.right).attr('y2', this.bottom)
} }
draw_yAxisTitle(title) { draw_yAxisTitle(title) {
const middle = (this.offset + this.height - this.margin.bottom) / 2.0
const middle = (this.top - this.bottom) / 2.0
const x = 15 const x = 15
this.canvas.append('text').attr('class', 'axis-title') this.canvas.append('text').attr('class', 'axis-title')
.attr('text-anchor', 'middle').attr('dominant-baseline', 'middle') .attr('text-anchor', 'middle').attr('dominant-baseline', 'middle')
@ -63,11 +66,11 @@ class Chart {
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')
.attr('x1', x).attr('y1', this.height - this.margin.bottom)
.attr('x2', x).attr('y2', this.height - this.margin.bottom + 2)
.attr('x1', x).attr('y1', this.bottom)
.attr('x2', x).attr('y2', this.bottom + 2)
this.canvas.append('text').attr('class', 'axis-label') this.canvas.append('text').attr('class', 'axis-label')
.attr('text-anchor', anchor).attr('dominant-baseline', 'hanging') .attr('text-anchor', anchor).attr('dominant-baseline', 'hanging')
.attr('x', x).attr('y', this.height - this.margin.bottom + 1)
.attr('x', x).attr('y', this.bottom + 1)
.text(label) .text(label)
} }
@ -209,7 +212,7 @@ export function render_astronomy(canvas, width, height, offset, data) {
const x = chart.xRange(data.beginTime) const x = chart.xRange(data.beginTime)
const box_width = chart.xRange(data.endTime) - x const box_width = chart.xRange(data.endTime) - x
canvas.append('rect').attr('class', 'blank') canvas.append('rect').attr('class', 'blank')
.attr('x', x).attr('y', height - margin.bottom)
.attr('x', x).attr('y', offset + height - margin.bottom)
.attr('width', box_width) .attr('width', box_width)
.attr('height', margin.bottom) .attr('height', margin.bottom)
@ -282,7 +285,7 @@ export function render_temperature(canvas, width, height, offset, data) {
const margin = { const margin = {
top: 0, top: 0,
left: leftMargin, left: leftMargin,
bottom: 12,
bottom: 5,
right: 0 right: 0
} }
const yrange = [-15, 45] const yrange = [-15, 45]
@ -316,3 +319,34 @@ export function render_temperature(canvas, width, height, offset, data) {
//chart.draw_xAxisLineTop() //chart.draw_xAxisLineTop()
chart.draw_xAxisLineBottom() chart.draw_xAxisLineBottom()
} }
export function render_clouds(canvas, width, height, offset, data) {
const margin = {
top: 0,
left: leftMargin,
bottom: 10,
right: 0
}
const yrange = [0, 100]
const chart = new Chart(canvas, width, height, offset, margin, data, yrange)
const line = d3.line()
.x(function(d) { return chart.xRange(d.date) })
.y(function(d) { return chart.yRange(d.coverage) })
.curve(d3.curveBasis)
//chart.draw_xAxisLineTop()
chart.set_yAxisTic( 0, '0%')
chart.set_yAxisTic( 50, '50%')
chart.draw_xGrid( 50, 'guide')
chart.set_yAxisTic(100, '100%')
chart.draw_xGrid( 100, 'guide')
canvas.append('path')
.data([data.clouds])
.attr('class', 'clouds')
.attr('d', line)
chart.draw_xAxisLineBottom()
}

8
src/style.sass

@ -45,9 +45,6 @@ h1.title
path, line path, line
fill: none fill: none
line
shape-rendering: crispEdges
#calendar .axis #calendar .axis
stroke: #bbb stroke: #bbb
stroke-width: 1px stroke-width: 1px
@ -74,6 +71,11 @@ path.moon
stroke: #999 stroke: #999
stroke-width: 1px stroke-width: 1px
path.clouds
opacity: 0.7
stroke: #bbb
fill: #ddd
line.now line.now
stroke: #f5f stroke: #f5f
stroke-width: 1px stroke-width: 1px

Loading…
Cancel
Save