Mercurial > personal > weather-server
diff weather_server/static/script.js @ 12:9e6289598d8c
Add script to draw charts with initial functions.
author | Paul Fisher <paul@pfish.zone> |
---|---|
date | Sun, 06 Oct 2019 15:21:03 -0400 |
parents | |
children | 4eaa9d69c4e2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/weather_server/static/script.js Sun Oct 06 15:21:03 2019 -0400 @@ -0,0 +1,109 @@ +'use strict'; + +/** + * Converts Celsius to Fahrenheit. + * @param {number} tempC + * @return {number} The temperature in Fahrenheit. + */ +function cToF(tempC) { + return tempC * 9 / 5 + 32; +} + +const MAGNUS_B = 17.62; +const MAGNUS_C = 243.12; + +/** + * The gamma function to calculate dew point. + * + * @param {number} tempC The temperature, in degrees Celsius. + * @param {number} rhPct The relative humidity, in percent. + * @return {number} The value of the gamma function. + */ +function gammaFn(tempC, rhPct) { + return Math.log(rhPct / 100) + MAGNUS_B * tempC / (MAGNUS_C + tempC); +} + +/** + * Calculates the dew point. + * + * @param {number} tempC The temperature, in degrees Celsius. + * @param {number} rhPct The relative humidity, in percent. + * @return {number} The dew point, in degrees Celsius. + */ +function dewPointC(tempC, rhPct) { + const gamma = gammaFn(tempC, rhPct); + return MAGNUS_C * gamma / (MAGNUS_B - gamma); +} + +/** The height of the chart in degrees. */ +const CHART_HEIGHT = 15; + +/** + * Charts some data. + * + * @param {CanvasRenderingContext2D} ctx The context to draw in. + * @param {[number, number][]} data The data to chart, as `[x, y]` pairs. + * @param {[number, number]} xRange The bounds of the X axis to draw. + * @param {[number, number]} size The `[width, height]` of the context. + */ +function drawChart(ctx, data, xRange, size) { + const yRange = calculateYRange(data.map(d => d[1])); + + ctx.lineWidth = 1.5; + + ctx.beginPath(); + const first = project(data[0], size, xRange, yRange); + ctx.moveTo(...first); + for (const pt of data) { + const projected = project(pt, size, xRange, yRange); + ctx.lineTo(...projected); + } + ctx.stroke(); +} + +/** + * Determines what the Y range of the chart should be. + * @param {number[]} ys The Y values of the chart. + * @return {[number, number]} The lowest and highest values of the range. + */ +function calculateYRange(ys) { + const yMax = Math.max(...ys); + const yMin = Math.min(...ys); + const yMid = (yMin + yMax) / 2; + const lastY = ys[ys.length - 1]; + + // We want the last value to be, at most, at the top or bottom 1/4 line + // of the chart. + + // If the middle of the range is already close enough, just use that. + if (CHART_HEIGHT / 4 <= Math.abs(yMid - lastY)) { + return [yMid - CHART_HEIGHT / 2, yMid + CHART_HEIGHT / 2]; + } + // Otherwise, clamp the chart range. + if (lastY < yMid) { + return [lastY - CHART_HEIGHT / 4, lastY + 3 * CHART_HEIGHT / 4]; + } + return [lastY - 3 * CHART_HEIGHT / 4, lastY + CHART_HEIGHT / 4]; +} + +/** + * Projects a Cartesian coordinate into Canvas space. + * + * @param {[number, number]} coord The `[x, y]` coordinate to project. + * @param {[number, number]} size The `[width, height]` of the context. + * @param {[number, number]} xRange The range of X values in the context. + * @param {[number, number]} yRange The range of Y values in the context. + * @return {[number, number]} The `[x, y]` coordinate in Canvas space. + */ +function project(coord, size, xRange, yRange) { + const [x, y] = coord; + const [xMin, xMax] = xRange; + const xSpan = xMax - xMin; + const [yMin, yMax] = yRange; + const ySpan = yMax - yMin; + const [xSize, ySize] = size; + return [ + (x - xMin) / xSpan * xSize, + (yMax- y) / ySpan * ySize, + ] +}