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,
+    ]
+}