From d84606dd1529992a319f813e83fb5f6e2d99b66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Do=C4=9Fu=20Kocatepe?= <93269312+kocatepedogu@users.noreply.github.com> Date: Wed, 20 Sep 2023 17:01:37 +0300 Subject: [PATCH] Level details dialog added --- src/renderer/diagram.ts | 8 ++++ src/renderer/dialog.ts | 24 +++++++++++ src/renderer/leveldetails.ts | 77 ++++++++++++++++++++++++++++++++++++ src/renderer/table.ts | 23 +++++++++++ src/renderer/viewer.css | 15 ++++++- src/renderer/viewer.html | 12 +++++- src/renderer/viewer.ts | 12 +++++- 7 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 src/renderer/leveldetails.ts diff --git a/src/renderer/diagram.ts b/src/renderer/diagram.ts index 5949a9a..4661beb 100644 --- a/src/renderer/diagram.ts +++ b/src/renderer/diagram.ts @@ -45,6 +45,7 @@ interface PlotCurve extends PlotFeature { } export class SoundingPlot { + private observers: Array<(lev: number) => void> = []; private sounding: data.Sounding; private canvas: HTMLCanvasElement; private ctx: CanvasRenderingContext2D; @@ -133,6 +134,11 @@ export class SoundingPlot { }); } + /** Adds a callback function that gets called when the active level is changed. */ + addObserver(fncallback: (lev: number) => void) { + this.observers.push(fncallback); + } + /** * Determines size of canvas based on the size of the div containing it. */ @@ -228,6 +234,8 @@ export class SoundingPlot { const temp = this.tmin + (x - (this.plotHeight - y) * Math.sin(Math.PI * this.skew/180)) / this.rT; informationBox.innerHTML += temp.toFixed(0) + '°C'; + + this.observers.forEach(fn => fn(pres)); } } diff --git a/src/renderer/dialog.ts b/src/renderer/dialog.ts index edd517b..4d00161 100644 --- a/src/renderer/dialog.ts +++ b/src/renderer/dialog.ts @@ -19,6 +19,10 @@ */ export class Dialog { + private static windows: Dialog[] = []; + + private zIndex: number; + private element: HTMLDivElement; private titleElement: HTMLDivElement; private closeBtn: HTMLDivElement; @@ -34,6 +38,10 @@ export class Dialog { this.titleElement = document.getElementById(id + '-title')!; this.closeBtn = document.getElementById(id + '-close'); + this.zIndex = Dialog.windows.length; + this.element.style.zIndex = this.zIndex.toString(); + this.element.addEventListener('mousedown', () => {Dialog.raise(this)}); + this.isBeingMoved = false; this.pos1 = 0; this.pos2 = 0; @@ -45,6 +53,8 @@ export class Dialog { document.addEventListener('mousemove', (e: MouseEvent) => {this.mouseMove(e)}); this.titleElement.addEventListener('mousedown', (e: MouseEvent) => {this.mouseDown(e)}); this.closeBtn.addEventListener('click', () => {this.close()}); + + Dialog.windows.push(this); } private mouseUp() { @@ -123,4 +133,18 @@ export class Dialog { private close() { this.element.hidden = true; } + + private static raise(dialog: Dialog) { + const selfZ = dialog.zIndex; + + for (const win of Dialog.windows) { + if (win.zIndex > selfZ) { + win.zIndex--; + win.element.style.zIndex = win.zIndex.toString(); + } + } + + dialog.zIndex = Dialog.windows.length; + dialog.element.style.zIndex = dialog.zIndex.toString(); + } } \ No newline at end of file diff --git a/src/renderer/leveldetails.ts b/src/renderer/leveldetails.ts new file mode 100644 index 0000000..0f02369 --- /dev/null +++ b/src/renderer/leveldetails.ts @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/* + * Copyright 2023 Doğu Kocatepe + * This file is part of Sounding Viewer. + + * Sounding Viewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * Sounding Viewer is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + + * You should have received a copy of the GNU General Public License along + * with Sounding Viewer. If not, see . + */ + +import { Sounding } from "./sounding"; +import * as numerical from "./numerical"; + +export class LevelDetails { + private snd: Sounding; + private pres: number; + private element: HTMLTextAreaElement; + + constructor(snd: Sounding) { + this.element = document.getElementById('sounding-level-details-text')!; + this.snd = snd; + this.pres = this.snd.first().pressure; + this.snd.addObserver(() => {this.update();}); + this.update(); + } + + setLevel(pres: number) { + this.pres = pres; + this.update(); + } + + update() { + let result = ''; + + const pres = this.pres; + const height = this.snd.getValueAt(pres, 'height'); + const temp = this.snd.getValueAt(pres, 'temp'); + const dewpt = this.snd.getValueAt(pres, 'dewpt'); + const e = numerical.vaporPressure(dewpt); + const es = numerical.saturatedVaporPressure(temp); + const w = numerical.mixingRatio(dewpt, pres); + const ws = numerical.saturatedMixingRatio(temp, pres); + const rh = numerical.relativeHumidity(temp, dewpt, pres); + const spec = numerical.specificHumidityFromMixingRatio(w); + const tv = numerical.virtualTemperature(temp, dewpt, pres); + const theta = numerical.potentialTemperature(temp, pres); + const thetae = numerical.equivalentPotentialTemperature(temp, dewpt, pres); + const twet = numerical.wetBulbTemperature(temp, dewpt, pres); + + result += `Pressure: ${pres.toFixed(1)} mb\n`; + result += `Height: ${height.toFixed(1)} m\n`; + result += `Temperature: ${temp.toFixed(2)} degC\n`; + result += `Dewpoint Temperature: ${dewpt.toFixed(2)} degC\n`; + result += `Virtual Temperature: ${tv.toFixed(2)} degC\n`; + result += `Potential Temperature: ${theta.toFixed(2)} degC\n`; + result += `Equivalent Potential Temperature: ${thetae.toFixed(2)} degC\n`; + result += `Wet bulb Temperature: ${twet.toFixed(2)} degC\n`; + result += `Vapor Pressure: ${e.toFixed(1)} mb\n`; + result += `Saturated Vapor Pressure: ${es.toFixed(1)} mb\n`; + result += `Mixing Ratio: ${w.toFixed(1)} g/kg\n`; + result += `Saturated Mixing Ratio: ${ws.toFixed(1)} g/kg\n`; + result += `Relative Humidity: ${(rh * 100).toFixed(1)}%\n`; + result += `Specific Humidity: ${(spec).toFixed(1)} g/kg\n`; + + this.element.value = result; + } +} \ No newline at end of file diff --git a/src/renderer/table.ts b/src/renderer/table.ts index e21d886..b34334e 100644 --- a/src/renderer/table.ts +++ b/src/renderer/table.ts @@ -21,6 +21,9 @@ import * as data from "./sounding" export class SoundingTable { + private observers: Array<(lev: number) => void> = []; + private previousActiveLevel: number; + /* Level properties that will be displayed together with their titles */ private readonly levelProperties = ["pressure", "height", "temp", "dewpt", "windspd", "winddir"]; private readonly levelPropertyTitles = [ @@ -179,8 +182,22 @@ export class SoundingTable { event.preventDefault(); } + private mouseMoveHandler(event: MouseEvent, sounding: data.Sounding) { + const targetElement = event.target as HTMLElement; + if (targetElement.className == 'sounding-table-data-cells') { + const id = parseInt(targetElement.id.split('-')[0]); + const level = sounding.find(id); + if (this.previousActiveLevel != level.pressure) { + this.observers.forEach(fn => fn(level.pressure)); + this.previousActiveLevel = level.pressure; + } + } + } + /* Fills table with given data and sets event handlers */ constructor(sounding: data.Sounding) { + this.previousActiveLevel = sounding.first().pressure; + /* Get sounding table element */ const soundingTable = document.getElementById('sounding-table'); if (!soundingTable) { @@ -207,5 +224,11 @@ export class SoundingTable { /* Event handlers of table */ soundingTable.addEventListener('click', (event) => {this.tableLeftClickHandler(event, sounding);}); soundingTable.addEventListener('contextmenu', (event) => {this.tableRightClickHandler(event);}); + soundingTable.addEventListener('mousemove', (event) => {this.mouseMoveHandler(event, sounding);}); + } + + /** Adds a callback function that gets called when the active level is changed */ + addObserver(fncallback: (lev: number) => void) { + this.observers.push(fncallback); } } \ No newline at end of file diff --git a/src/renderer/viewer.css b/src/renderer/viewer.css index bf0a693..ff73960 100644 --- a/src/renderer/viewer.css +++ b/src/renderer/viewer.css @@ -184,14 +184,25 @@ html, body { text-align: left; } -#sounding-diagram-settings-title { +#sounding-level-details { + position: absolute; + top: 100px; + left: 100px; + background-color: rgb(233, 233, 233); + border: rgb(121, 121, 121); + border-width: 1px; + border-style:solid; + text-align: left; +} + +.dialog-title { width: 100%; background-color: var(--dark); color: white; cursor: move; } -#sounding-diagram-settings-close { +.dialog-close { float:right; background-color: #888888; cursor: pointer; diff --git a/src/renderer/viewer.html b/src/renderer/viewer.html index aefe7ff..9f2e33a 100644 --- a/src/renderer/viewer.html +++ b/src/renderer/viewer.html @@ -35,6 +35,7 @@
+
@@ -56,8 +57,17 @@ + +