Skip to content

Commit

Permalink
Hodograph
Browse files Browse the repository at this point in the history
  • Loading branch information
kocatepedogu committed Sep 20, 2023
1 parent ff4ba12 commit 0673618
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 5 deletions.
134 changes: 134 additions & 0 deletions src/renderer/hodograph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// 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 <https://www.gnu.org/licenses/>.
*/

import * as sounding from './sounding';

export class Hodograph {
private snd: sounding.Sounding;
private canvas: HTMLCanvasElement;
private ctx: CanvasRenderingContext2D;
private width: number;
private height: number;

private plotWidth: number;
private plotHeight: number;
private plotRadius: number;
private originX: number;
private originY: number;

private maxWindSpeed: number = 0;
private rR: number = NaN;

constructor(snd: sounding.Sounding) {
this.snd = snd;
this.snd.addObserver(() => {this.update()});

this.canvas = document.getElementById('hodograph-canvas') as HTMLCanvasElement;
this.ctx = this.canvas.getContext('2d')!;
this.width = this.canvas.width;
this.height = this.canvas.height;

this.plotWidth = this.width * 0.9;
this.plotHeight = this.height * 0.9;
this.plotRadius = Math.min(this.plotWidth, this.plotHeight) / 2;
this.originX = (this.width - this.plotWidth) / 2 + this.plotRadius - 1;
this.originY = (this.height - this.plotHeight) / 2 + this.plotRadius - 1;
}

private findLimits() {
for (const level of this.snd) {
if (level.windspd > this.maxWindSpeed) {
this.maxWindSpeed = level.windspd;
}
}

this.rR = this.plotRadius / this.maxWindSpeed;
}

computeCoordinates(windspd: number, winddir: number): [number, number] {
const x = this.originX + windspd * this.rR * Math.cos(Math.PI * (90 + winddir) / 180);
const y = this.originY + windspd * this.rR * Math.sin(Math.PI * (90 + winddir) / 180);

return [x, y];
}

plotBackground() {
// Clear canvas
this.ctx.beginPath();
this.ctx.fillStyle = "white";
this.ctx.fillRect(0, 0, this.width, this.height);

// Plot axes
this.ctx.beginPath();
this.ctx.moveTo(this.originX, this.originY - this.plotRadius);
this.ctx.lineTo(this.originX, this.originY + this.plotRadius);
this.ctx.stroke();
this.ctx.beginPath();
this.ctx.moveTo(this.originX - this.plotRadius, this.originY);
this.ctx.lineTo(this.originX + this.plotRadius, this.originY);
this.ctx.stroke();

// Print degree labels on the axes
for (let dir = 0; dir < 360; dir += 90) {
this.ctx.fillStyle = "black";
this.ctx.fillText(dir.toString(), ...this.computeCoordinates(this.maxWindSpeed, dir));
}

// Plot circles
for (let wspd = 0; wspd <= this.maxWindSpeed; wspd += this.maxWindSpeed / 5) {
const r = wspd * this.rR;

this.ctx.arc(this.originX, this.originY, r, 0, 2 * Math.PI);
this.ctx.strokeStyle = 'black';
this.ctx.lineWidth = 0.7;
this.ctx.stroke();

this.ctx.fillStyle = "red";
this.ctx.fillText(wspd.toFixed(1), this.originX, this.originY + r);
}
}

plotWinds() {
let [ox, oy] = this.computeCoordinates(this.snd.first().windspd, this.snd.first().winddir);
for (const level of this.snd) {
const [x, y] = this.computeCoordinates(level.windspd, level.winddir);

this.ctx.beginPath();
this.ctx.moveTo(ox, oy);
this.ctx.lineTo(x, y);
this.ctx.lineWidth = 1;
this.ctx.strokeStyle = "blue";
this.ctx.stroke();

ox = x;
oy = y;
}
}

plot() {
this.findLimits();
this.plotBackground();
this.plotWinds();
}

update() {
this.plot();
}
}
11 changes: 11 additions & 0 deletions src/renderer/viewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,17 @@ html, body {
text-align: left;
}

#hodograph {
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);
Expand Down
8 changes: 8 additions & 0 deletions src/renderer/viewer.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<button type="submit" id="load-new-btn" onclick="window.location.href='./index.html';">New</button>
<button type="button" id="settings-btn">Settings</button>
<button type="button" id="level-details-btn">Level details</button>
<button type="button" id="hodograph-btn">Hodograph</button>
<button type="button" id="export-btn">Export</button>
</div>
<canvas id="sounding-diagram"></canvas>
Expand Down Expand Up @@ -68,6 +69,13 @@
<textarea id="sounding-level-details-text" readonly></textarea>
</div>

<div id="hodograph" hidden>
<div id="hodograph-title" class="dialog-title">Hodograph
<div id="hodograph-close" class="dialog-close">X</div>
</div>
<canvas id="hodograph-canvas" width="360px" height="360px"></canvas>
</div>

<div id="sounding-diagram-settings" hidden>
<div id="sounding-diagram-settings-title" class="dialog-title">Diagram Settings
<div id="sounding-diagram-settings-close" class="dialog-close">X</div>
Expand Down
20 changes: 15 additions & 5 deletions src/renderer/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as diagram from "./diagram"
import * as indices from "./indices"
import * as levelDetails from "./leveldetails"
import { Dialog } from "./dialog"
import { Hodograph } from './hodograph'

function getData(){
const url = new URL(window.location.href);
Expand Down Expand Up @@ -101,8 +102,10 @@ window.initializeSounding = async function() {
})();

setTitle();

const snd = new sounding.Sounding(src);
const plt = new diagram.SoundingPlot(snd);
const hod = new Hodograph(snd);
const ind = new indices.IndexTable(snd);
const tbl = new table.SoundingTable(snd);
const det = new levelDetails.LevelDetails(snd);
Expand All @@ -111,19 +114,26 @@ window.initializeSounding = async function() {
tbl.addObserver((lev) => det.setLevel(lev));
plt.update();
ind.update();
hod.update();

new Dialog("sounding-diagram-settings");
new Dialog("sounding-level-details");
new Dialog("hodograph");

document.getElementById('export-btn')?.addEventListener('click', () => {
exportData(snd);
});

document.getElementById('level-details-btn')?.addEventListener('click', () => {
const levelDetailsDialog = document.getElementById('sounding-level-details');
levelDetailsDialog!.hidden = false;
const levelDetailsDialog = document.getElementById('sounding-level-details')!;
levelDetailsDialog.hidden = false;
})

document.getElementById('hodograph-btn')?.addEventListener('click', () => {
const hodographDialog = document.getElementById('hodograph')!;
hodographDialog.hidden = false;
});

document.getElementById('main-div')!.style.visibility = "visible";
document.getElementById('loading-div')!.style.visibility = "hidden";

new Dialog("sounding-diagram-settings");
new Dialog("sounding-level-details");
}

0 comments on commit 0673618

Please sign in to comment.