Mercurial > personal > weather-server
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 11:52ef21607b31 | 12:9e6289598d8c |
|---|---|
| 1 'use strict'; | |
| 2 | |
| 3 /** | |
| 4 * Converts Celsius to Fahrenheit. | |
| 5 * @param {number} tempC | |
| 6 * @return {number} The temperature in Fahrenheit. | |
| 7 */ | |
| 8 function cToF(tempC) { | |
| 9 return tempC * 9 / 5 + 32; | |
| 10 } | |
| 11 | |
| 12 const MAGNUS_B = 17.62; | |
| 13 const MAGNUS_C = 243.12; | |
| 14 | |
| 15 /** | |
| 16 * The gamma function to calculate dew point. | |
| 17 * | |
| 18 * @param {number} tempC The temperature, in degrees Celsius. | |
| 19 * @param {number} rhPct The relative humidity, in percent. | |
| 20 * @return {number} The value of the gamma function. | |
| 21 */ | |
| 22 function gammaFn(tempC, rhPct) { | |
| 23 return Math.log(rhPct / 100) + MAGNUS_B * tempC / (MAGNUS_C + tempC); | |
| 24 } | |
| 25 | |
| 26 /** | |
| 27 * Calculates the dew point. | |
| 28 * | |
| 29 * @param {number} tempC The temperature, in degrees Celsius. | |
| 30 * @param {number} rhPct The relative humidity, in percent. | |
| 31 * @return {number} The dew point, in degrees Celsius. | |
| 32 */ | |
| 33 function dewPointC(tempC, rhPct) { | |
| 34 const gamma = gammaFn(tempC, rhPct); | |
| 35 return MAGNUS_C * gamma / (MAGNUS_B - gamma); | |
| 36 } | |
| 37 | |
| 38 /** The height of the chart in degrees. */ | |
| 39 const CHART_HEIGHT = 15; | |
| 40 | |
| 41 /** | |
| 42 * Charts some data. | |
| 43 * | |
| 44 * @param {CanvasRenderingContext2D} ctx The context to draw in. | |
| 45 * @param {[number, number][]} data The data to chart, as `[x, y]` pairs. | |
| 46 * @param {[number, number]} xRange The bounds of the X axis to draw. | |
| 47 * @param {[number, number]} size The `[width, height]` of the context. | |
| 48 */ | |
| 49 function drawChart(ctx, data, xRange, size) { | |
| 50 const yRange = calculateYRange(data.map(d => d[1])); | |
| 51 | |
| 52 ctx.lineWidth = 1.5; | |
| 53 | |
| 54 ctx.beginPath(); | |
| 55 const first = project(data[0], size, xRange, yRange); | |
| 56 ctx.moveTo(...first); | |
| 57 for (const pt of data) { | |
| 58 const projected = project(pt, size, xRange, yRange); | |
| 59 ctx.lineTo(...projected); | |
| 60 } | |
| 61 ctx.stroke(); | |
| 62 } | |
| 63 | |
| 64 /** | |
| 65 * Determines what the Y range of the chart should be. | |
| 66 * @param {number[]} ys The Y values of the chart. | |
| 67 * @return {[number, number]} The lowest and highest values of the range. | |
| 68 */ | |
| 69 function calculateYRange(ys) { | |
| 70 const yMax = Math.max(...ys); | |
| 71 const yMin = Math.min(...ys); | |
| 72 const yMid = (yMin + yMax) / 2; | |
| 73 const lastY = ys[ys.length - 1]; | |
| 74 | |
| 75 // We want the last value to be, at most, at the top or bottom 1/4 line | |
| 76 // of the chart. | |
| 77 | |
| 78 // If the middle of the range is already close enough, just use that. | |
| 79 if (CHART_HEIGHT / 4 <= Math.abs(yMid - lastY)) { | |
| 80 return [yMid - CHART_HEIGHT / 2, yMid + CHART_HEIGHT / 2]; | |
| 81 } | |
| 82 // Otherwise, clamp the chart range. | |
| 83 if (lastY < yMid) { | |
| 84 return [lastY - CHART_HEIGHT / 4, lastY + 3 * CHART_HEIGHT / 4]; | |
| 85 } | |
| 86 return [lastY - 3 * CHART_HEIGHT / 4, lastY + CHART_HEIGHT / 4]; | |
| 87 } | |
| 88 | |
| 89 /** | |
| 90 * Projects a Cartesian coordinate into Canvas space. | |
| 91 * | |
| 92 * @param {[number, number]} coord The `[x, y]` coordinate to project. | |
| 93 * @param {[number, number]} size The `[width, height]` of the context. | |
| 94 * @param {[number, number]} xRange The range of X values in the context. | |
| 95 * @param {[number, number]} yRange The range of Y values in the context. | |
| 96 * @return {[number, number]} The `[x, y]` coordinate in Canvas space. | |
| 97 */ | |
| 98 function project(coord, size, xRange, yRange) { | |
| 99 const [x, y] = coord; | |
| 100 const [xMin, xMax] = xRange; | |
| 101 const xSpan = xMax - xMin; | |
| 102 const [yMin, yMax] = yRange; | |
| 103 const ySpan = yMax - yMin; | |
| 104 const [xSize, ySize] = size; | |
| 105 return [ | |
| 106 (x - xMin) / xSpan * xSize, | |
| 107 (yMax- y) / ySpan * ySize, | |
| 108 ] | |
| 109 } |
