view weather_server/static/graph.js.map @ 28:f817fa785c93

Make graph only consider visible points when setting range.
author Paul Fisher <paul@pfish.zone>
date Sun, 19 Jan 2020 17:05:11 -0500
parents 47987502bf4c
children beedfa8eaa3f
line wrap: on
line source

{"version":3,"file":"graph.js","sourceRoot":"","sources":["../typescript/app/math.ts","../typescript/app/graph.ts"],"names":[],"mappings":"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","sourcesContent":["/** Converts Celsius to Fahrenheit. */\nexport function cToF(tempC: number): number {\n    return tempC * 9 / 5 + 32;\n}\n\nconst MAGNUS_B = 17.62;\nconst MAGNUS_C = 243.12;\n\n/** The gamma function to calculate dew point. */\nfunction gammaFn(tempC: number, rhPct: number): number {\n    return Math.log(rhPct / 100) + MAGNUS_B * tempC / (MAGNUS_C + tempC);\n}\n\n/** Calculates the dew point. */\nexport function dewPointC(tempC: any, rhPct: any): number {\n    const gamma = gammaFn(tempC, rhPct);\n    return MAGNUS_C * gamma / (MAGNUS_B - gamma);\n}\n","import { WeatherResponse } from './apis';\nimport { cToF, dewPointC } from './math';\n\n/** The amount of time we will draw on the graph. */\nconst HISTORY_SECONDS = 60 * 60 * 4;\n/** The amount of buffer room we will request before HISTORY_SECONDS. */\nconst BUFFER_SECONDS = 300;\n\n/**\n * Sets up everything.\n * @param tempElement The element where temperature data is.\n * @param dewPointElement The element where the dew point is.\n */\nexport async function setUp(\n    root: HTMLElement,\n    tempElement: HTMLElement,\n    dewPointElement: HTMLElement) {\n    const nowTS = new Date().getTime() / 1000;\n    const startTS = nowTS - HISTORY_SECONDS;\n    const query = new URL('?', location.href);\n    query.pathname = query.pathname + '/recent';\n    query.searchParams.set('seconds', String(HISTORY_SECONDS + BUFFER_SECONDS));\n    const results = await fetch(query.href);\n    if (!results.ok) return;\n    const data: WeatherResponse = await results.json();\n    const readings = data.readings;\n    if (readings.length === 0) return;\n    root.classList.remove('plain');\n    root.classList.add('fancy');\n\n    const tempsF =\n        readings.map(s => [s.sample_time, cToF(s.temp_c)] as Point);\n    const dewPointsF = readings.map(\n        s => [s.sample_time, cToF(dewPointC(s.temp_c, s.rh_pct))] as Point);\n    setUpElement(tempElement, [startTS, nowTS], tempsF);\n    setUpElement(dewPointElement, [startTS, nowTS], dewPointsF);\n}\n\ntype Point = [number, number];\n\n/**\n * Sets up charting for this element.\n * @param element The element to put a graph in.\n * @param timeRange The `[start, end]` of the time range.\n * @param data The data to chart.\n */\nfunction setUpElement(\n    element: HTMLElement, timeRange: [number, number], data: Point[]) {\n    if (data.length === 0) return;\n    const chart = new Chart(element, timeRange, data);\n    chart.resize();\n    addEventListener('resize', () => chart.resize());\n}\n\n/** The number of degrees that the graph shows vertically. */\nconst Y_DEGREE_RANGE = 10;\n\nconst LINE_WIDTH_PX = 2;\n\nconst FONT_SIZE = '40px';\n\nclass Chart {\n    private readonly canvas: HTMLCanvasElement;\n    private readonly unit: string;\n    /**\n     * Creates a new chart.\n     * @param element The parent element to create `<canvas>`-based chart in.\n     * @param timeRange `[start, end]` of the range to chart as Unix timestamps.\n     * @param data The data to chart.\n     */\n    constructor(\n        private readonly element: HTMLElement,\n        private readonly timeRange: [number, number],\n        private readonly data: Point[]) {\n        this.canvas = document.createElement('canvas');\n        this.element.insertBefore(this.canvas, element.firstChild);\n        const unit = element.getElementsByClassName('unit')[0];\n        this.unit = unit && unit.textContent || '';\n    }\n\n    resize() {\n        const dpr = self.devicePixelRatio || 1;\n        const [w, h] = this.size();\n        const pxSize: Point = [w * dpr, h * dpr];\n        this.canvas.width = pxSize[0];\n        this.canvas.height = pxSize[1];\n        const ctx = this.canvas.getContext('2d')!;\n        ctx.clearRect(0, 0, pxSize[0], pxSize[1]);\n        ctx.scale(dpr, dpr);\n        this.redraw(ctx);\n    }\n\n    private redraw(ctx: CanvasRenderingContext2D) {\n        const stroke = getComputedStyle(this.element).color!;\n        const family = getComputedStyle(this.element).fontFamily!;\n        ctx.strokeStyle = stroke;\n        ctx.lineJoin = 'round';\n        ctx.lineWidth = LINE_WIDTH_PX;\n        ctx.font = `bold ${FONT_SIZE} ${family}`;\n\n        const onScreenData = this.data.filter(\n            ([time, _]) => this.timeRange[0] <= time);\n\n        const yRange = calculateYRange(onScreenData.map(d => d[1]));\n\n        const [fullW, fullH] = this.size();\n        const [xPad, yMargin] = this.measureMargin(ctx);\n        const graphSize: Point = [fullW - xPad, fullH];\n\n        ctx.beginPath();\n        for (const pt of this.data) {\n            const projected = project(pt, graphSize, this.timeRange, yRange);\n            ctx.lineTo(...projected);\n        }\n        ctx.stroke();\n        ctx.beginPath();\n        const lastPt = this.data[this.data.length - 1];\n        const center = project(lastPt, graphSize, this.timeRange, yRange);\n        ctx.ellipse(\n            center[0],\n            center[1],\n            1.5 * LINE_WIDTH_PX,\n            1.5 * LINE_WIDTH_PX,\n            0,\n            0,\n            2 * Math.PI);\n        ctx.fillStyle = getComputedStyle(this.element).backgroundColor!;\n        ctx.fill();\n        ctx.stroke();\n\n        ctx.fillStyle = stroke;\n        ctx.textAlign = 'left';\n        ctx.textBaseline = 'top';\n        ctx.fillText(\n            `${niceNumber(lastPt[1])} ${this.unit}`,\n            center[0] + 5 * LINE_WIDTH_PX,\n            center[1] + yMargin);\n    }\n\n    private measureMargin(ctx: CanvasRenderingContext2D): [number, number] {\n        const bbox = ctx.measureText(`−99 ${this.unit}`);\n        const margin =\n            5 * LINE_WIDTH_PX +  // margin to text\n            bbox.width +  // max (?) width of text\n            16;  // Pixel margin to wall.\n        return [margin, -31.5 / 2];\n    }\n\n    private size(): Point {\n        const cssSize = this.element.getBoundingClientRect();\n        return [cssSize.width, cssSize.height];\n    }\n}\n\nfunction niceNumber(n: number) {\n    return Math.round(n).toLocaleString('en-us').replace('-', '−');\n}\n\n/** The closest that the last point will be allowed to get to the edge. */\nconst EDGE_FRACTION = 0.125;\n\n/**\n * Determines what the Y range of the chart should be.\n * @param ys The Y values of the chart.\n * @return The lowest and highest values of the range.\n */\nfunction calculateYRange(ys: number[]): [number, number] {\n    const yMax = Math.max(...ys);\n    const yMin = Math.min(...ys);\n    const yMid = (yMin + yMax) / 2;\n    const lastY = ys[ys.length - 1];\n    const yLo = yMid - Y_DEGREE_RANGE / 2;\n    const yProportion = Math.max(\n        Math.min((lastY - yLo) / Y_DEGREE_RANGE, 1 - EDGE_FRACTION),\n        EDGE_FRACTION);\n    const rangeLo = lastY - yProportion * Y_DEGREE_RANGE;\n    return [rangeLo, rangeLo + Y_DEGREE_RANGE];\n}\n\n/**\n * Projects a Cartesian coordinate into Canvas space.\n *\n * @param coord The `[x, y]` coordinate to project.\n * @param size The `[width, height]` of the context.\n * @param xRange The range of X values in the context.\n * @param yRange The range of Y values in the context.\n * @return The `[x, y]` coordinate in Canvas space.\n */\nfunction project(\n    coord: Point,\n    size: Point,\n    xRange: [number, number],\n    yRange: [number, number]): Point {\n    const [x, y] = coord;\n    const [xMin, xMax] = xRange;\n    const xSpan = xMax - xMin;\n    const [yMin, yMax] = yRange;\n    const ySpan = yMax - yMin;\n    const [xSize, ySize] = size;\n    return [\n        (x - xMin) / xSpan * xSize,\n        (yMax - y) / ySpan * ySize,\n    ]\n}\n"]}