speedandcurrent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,186 @@
1
+ class TableRenderer {
2
+ constructor() {
3
+ this.formatColumnHeader = this.defaultDisplay;
4
+ this.formatRowHeader = this.defaultDisplay;
5
+ this.formatCell = this.defaultDisplay;
6
+ this.formatCellX = this.defaultDisplay;
7
+ this.formatCellY = this.defaultDisplay;
8
+ }
9
+
10
+ setColumnHeaderFormat(columnHeader) {
11
+ columnHeader != null ? this.formatColumnHeader = columnHeader : this.formatColumnHeader = this.defaultDisplay;
12
+ }
13
+
14
+ setRowHeaderFormat(rowHeader) {
15
+ rowHeader != null ? this.formatRowHeader = rowHeader : this.formatRowHeader = this.defaultDisplay;
16
+ }
17
+
18
+ setCellFormat(cell) {
19
+ cell != null ? this.formatCell = cell : this.formatCell = this.defaultDisplay;
20
+ }
21
+
22
+ setCellFormatX(cellX) {
23
+ cellX != null ? this.formatCellX = cellX : this.formatCellX = this.defaultDisplay;
24
+ }
25
+
26
+ setCellFormatY(cellY) {
27
+ cellY != null ? this.formatCellY = cellY : this.formatCellY = this.defaultDisplay;
28
+ }
29
+
30
+ render(data) {
31
+ const { row, col, table, selected, id, label } = data;
32
+ this.data = data;
33
+
34
+ // Create a table element
35
+ const tableElement = document.createElement('table');
36
+ tableElement.id = id;
37
+ tableElement.classList.add('Table2D');
38
+ // create header row
39
+ tableElement.appendChild(this.headerRow(col, label));
40
+
41
+ // Create other rows
42
+ var rIndex = 0;
43
+ for (let r = row.min; r <= row.max + 0.01; r += row.step) {
44
+ tableElement.appendChild(this.tableRow(r, rIndex, col, table, selected));
45
+ rIndex++;
46
+ }
47
+ return tableElement;
48
+ }
49
+
50
+ headerRow(col, label) {
51
+ // Create label cell
52
+ const headerRow = document.createElement('tr');
53
+ const firstHeaderCell = document.createElement('th');
54
+ firstHeaderCell.innerText = label ? label : '';
55
+ firstHeaderCell.classList.add('TableRowHeader');
56
+ headerRow.appendChild(firstHeaderCell);
57
+
58
+ // Create header row
59
+ for (let c = col.min; c <= col.max + 0.01; c += col.step) {
60
+ const headerCell = document.createElement('th');
61
+ headerCell.innerHTML = this.formatColumnHeader(c);
62
+ headerCell.classList.add('TablecolumnHeader');
63
+ headerRow.appendChild(headerCell);
64
+ }
65
+ return headerRow;
66
+ }
67
+
68
+ rowLabel(r) {
69
+ const headerCell = document.createElement('th');
70
+ headerCell.innerHTML = this.formatRowHeader(r);
71
+ //headerCell.innerText = `${cSpeed(r)} `;
72
+ headerCell.classList.add('TableRowHeader');
73
+ return headerCell;
74
+ }
75
+
76
+ tableRow(r, rIndex, col, table, selected) {
77
+ var cIndex = 0;
78
+ const rowElement = document.createElement('tr');
79
+ rowElement.appendChild(this.rowLabel(r));
80
+ for (let c = col.min; c <= col.max + 0.01; c += col.step) {
81
+ const cell = document.createElement('td');
82
+ cell.classList.add('TableCell');
83
+ const value = table[rIndex][cIndex];
84
+ const text = this.formatCell(value, r, c);
85
+ if (text != null) {
86
+ cell.innerHTML = text;
87
+ }
88
+ else {
89
+ cell.classList.add('emptyCell');
90
+ }
91
+ if (selected[0] == rIndex && selected[1] == cIndex) cell.classList.add('selectedCell');
92
+
93
+ rowElement.appendChild(cell);
94
+ cIndex++;
95
+ }
96
+ return rowElement;
97
+ }
98
+
99
+ defaultDisplay(value) {
100
+ return value.toFixed(2);
101
+ }
102
+
103
+ createSurfacePlot() {
104
+ const { row, col, table, id } = this.data;
105
+
106
+ // Format the axes values
107
+ const x = [];
108
+ for (let c = col.min; c <= col.max + 0.01; c += col.step) {
109
+ x.push(this.formatColumnHeader(c));
110
+ }
111
+
112
+ const y = [];
113
+ for (let r = row.min; r <= row.max + 0.01; r += row.step) {
114
+ y.push(this.formatRowHeader(r));
115
+ }
116
+
117
+
118
+ const zX = table.map(row => row.map(cell => cell.N > 0 ? cell.x : null)); // Assuming 'x' values for the surface plot
119
+ const zY = table.map(row => row.map(cell => cell.N > 0 ? cell.y : null)); // Assuming 'y' values for the surface plot
120
+
121
+ // Open a new window
122
+ const newWindow = window.open('', 'Correction Plot', 'width=1024,height=800');
123
+ newWindow.document.title = 'Correction Plot';
124
+
125
+ // Create div elements for the plots
126
+ const plotDivX = newWindow.document.createElement('div');
127
+ plotDivX.id = 'surfacePlotX';
128
+ plotDivX.style.width = '100%';
129
+ plotDivX.style.height = '400px';
130
+
131
+ const plotDivY = newWindow.document.createElement('div');
132
+ plotDivY.id = 'surfacePlotY';
133
+ plotDivY.style.width = '100%';
134
+ plotDivY.style.height = '400px';
135
+
136
+ newWindow.document.body.appendChild(plotDivX);
137
+ newWindow.document.body.appendChild(plotDivY);
138
+
139
+ // Data for the surface plots
140
+ const plotDataX = [{
141
+ type: 'surface',
142
+ x: x,
143
+ y: y,
144
+ z: zX
145
+ }];
146
+
147
+ const plotDataY = [{
148
+ type: 'surface',
149
+ x: x,
150
+ y: y,
151
+ z: zY
152
+ }];
153
+
154
+ // Layout for the surface plots
155
+ const layoutX = {
156
+ title: 'X-value',
157
+ autosize: true,
158
+ scene: {
159
+ xaxis: { title: 'Speed' },
160
+ yaxis: { title: 'Heel' },
161
+ zaxis: { title: 'Correction' }
162
+ }
163
+ };
164
+
165
+ const layoutY = {
166
+ title: 'Y-value',
167
+ autosize: true,
168
+ scene: {
169
+ xaxis: { title: 'Speed' },
170
+ yaxis: { title: 'Heel' },
171
+ zaxis: { title: 'Correction' }
172
+ }
173
+ };
174
+
175
+ const config = {
176
+ displayModeBar: false,
177
+ responsive: true // Ensure the plot is responsive
178
+ };
179
+
180
+ // Render the plots using Plotly
181
+ Plotly.newPlot(plotDivX, plotDataX, layoutX, config);
182
+ Plotly.newPlot(plotDivY, plotDataY, layoutY, config);
183
+ }
184
+ }
185
+
186
+ export default TableRenderer;
package/public/app.js ADDED
@@ -0,0 +1,278 @@
1
+ const API_BASE_URL = "/plugins/speedandcurrent"; // Adjust based on your server configuration
2
+ import TableRenderer from './tableRenderer.js';
3
+
4
+
5
+ let updateInterval = 1000;
6
+ let updateTimer;
7
+ let updatesPaused = false;
8
+
9
+ let vAngle = 1;
10
+ let dAngle = 2;
11
+ let vSpeed = 1;
12
+ let dSpeed = 1;
13
+
14
+
15
+ export function handleSpeedUnitChange(value) {
16
+ if (value == "knots") {
17
+ vSpeed = 1.943844;
18
+ dSpeed = 1;
19
+ return;
20
+ }
21
+ if (value == "kmh") {
22
+ vSpeed = 3.6;
23
+ dSpeed = 1;
24
+ return;
25
+ }
26
+ vSpeed = 1;
27
+ dSpeed = 1;
28
+ }
29
+
30
+ export function handleAngleUnitChange(value) {
31
+ if (value == "degrees") {
32
+ vAngle = 180 / Math.PI;
33
+ dAngle = 0;
34
+ return;
35
+ }
36
+ vAngle = 1;
37
+ dAngle = 2;
38
+ }
39
+
40
+
41
+
42
+ export function handleTableStyleChange(value) {
43
+ console.log(value);
44
+ if (value == "cartesian") {
45
+ tableRenderer.setCellFormat(cartesian);
46
+ }
47
+ else {
48
+ tableRenderer.setCellFormat(polar);
49
+ }
50
+ }
51
+
52
+ function cAngle(value) {
53
+ value *= vAngle;
54
+ return value.toFixed(dAngle);
55
+ }
56
+
57
+ function cSpeed(value) {
58
+ value *= vSpeed;
59
+ return value.toFixed(dSpeed);
60
+ }
61
+
62
+ function cartesian(correction, speed, heel) {
63
+ if (correction.N == 0) return null;
64
+ return ` <div><strong>X:</strong> ${cSpeed(correction.x)}</div>
65
+ <div><strong>Y:</strong> ${cSpeed(correction.y)}</div>
66
+ <div><strong>N:</strong> ${correction.N}</div>
67
+ `;
68
+ }
69
+
70
+ function polar(correction, speed, heel) {
71
+ if (speed == 0 || correction.N == 0) return null;
72
+ const factor = (correction.x + speed) / speed;
73
+ return ` <div><strong>factor:</strong> ${factor.toFixed(2)}</div>
74
+ <div><strong>leeway:</strong> ${cAngle(Math.atan2(correction.y, speed))}</div>
75
+ <div><strong>N:</strong> ${correction.N}</div>
76
+ `;
77
+ }
78
+
79
+ function formatCellX(value, speed, heel) {
80
+ if (value.N == 0) return null;
81
+ return cSpeed(value.x);
82
+ }
83
+
84
+ function formatCellY(value, speed, heel) {
85
+ if (value.N == 0) return null;
86
+ return Math.abs(cSpeed(value.y));
87
+ }
88
+
89
+ function formatCellXPolar(value, speed, heel) {
90
+ if (value.N == 0) return null;
91
+ const factor = (correction.x + speed) / speed;
92
+ return factor.toFixed(2);
93
+ }
94
+
95
+ function formatCellYPolar(value, speed, heel) {
96
+ if (value.N == 0) return null;
97
+ return Math.abs(cAngle(Math.atan2(value.y, speed)));
98
+ }
99
+
100
+
101
+
102
+ async function getFromServer(endpoint) {
103
+ try {
104
+ const response = await fetch(`${API_BASE_URL}/${endpoint}`, {
105
+ method: 'GET',
106
+ headers: {
107
+ 'Content-Type': 'application/json'
108
+ }
109
+ });
110
+
111
+ if (!response.ok) {
112
+ if (response.status === 503) {
113
+ document.getElementById("message").innerHTML = "Plugin is not running";
114
+ } else {
115
+ document.getElementById("message").innerHTML = "Failed to fetch data";
116
+ }
117
+ }
118
+ else {
119
+ document.getElementById("message").innerHTML = "";
120
+ }
121
+
122
+ const data = await response.json();
123
+ return data;
124
+ } catch (error) {
125
+ console.error("Failed to fetch data from server:", error);
126
+ document.getElementById("message").innerHTML = error;
127
+ return null;
128
+ }
129
+ }
130
+
131
+
132
+
133
+ function updateOptions(data) {
134
+ const optionsContent = document.getElementById('options-content');
135
+ optionsContent.innerHTML = ''; // Clear previous content
136
+
137
+ const table = document.createElement('table');
138
+ const headerRow = document.createElement('tr');
139
+ headerRow.innerHTML = '<th>Option Name</th><th>Value</th>';
140
+ table.appendChild(headerRow);
141
+
142
+ Object.entries(data.options).forEach(([key, value]) => {
143
+ const type = typeof value;
144
+ if (type === 'string' || type === 'number' || type === 'boolean') {
145
+
146
+ const row = document.createElement('tr');
147
+ row.innerHTML = `<td>${key}</td><td>${value}</td>`;
148
+ table.appendChild(row);
149
+ };
150
+ });
151
+
152
+ optionsContent.appendChild(table);
153
+ }
154
+
155
+
156
+ function updatePolar(data) {
157
+ const stepsList = document.getElementById('speeds-container');
158
+ stepsList.innerHTML = ''; // Clear previous steps
159
+
160
+ const table = document.createElement('table');
161
+ const headerRow = document.createElement('tr');
162
+ headerRow.innerHTML = '<th>Label</th><th>Speed</th><th>Angle</th>';
163
+ table.appendChild(headerRow);
164
+
165
+ data.polarSteps.forEach(step => {
166
+ const row = document.createElement('tr');
167
+ if (step.id != undefined)
168
+ row.classList.add(step.id);
169
+ row.innerHTML = `<td>${step.label}</td><td>${cSpeed(step.speed)}</td><td>${cAngle(step.angle)}</td>`;
170
+ table.appendChild(row);
171
+ });
172
+
173
+ stepsList.appendChild(table);
174
+ }
175
+
176
+ function updateDelta(data) {
177
+ const stepsList = document.getElementById('delta-container');
178
+ stepsList.innerHTML = ''; // Clear previous steps
179
+
180
+ const table = document.createElement('table');
181
+ const headerRow = document.createElement('tr');
182
+ headerRow.innerHTML = '<th>Label</th><th>Delta</th>';
183
+ table.appendChild(headerRow);
184
+
185
+ data.deltas.forEach(step => {
186
+ const row = document.createElement('tr');
187
+ if (step.id != undefined)
188
+ row.classList.add(step.id);
189
+ row.innerHTML = `<td>${step.label}</td><td>${cAngle(step.value)}</td>`;
190
+ table.appendChild(row);
191
+ });
192
+
193
+ stepsList.appendChild(table);
194
+ }
195
+
196
+ function updateAttitude(data) {
197
+ const attitudeContainer = document.getElementById('attitude-container');
198
+ attitudeContainer.innerHTML = '';
199
+ const table = document.createElement('table');
200
+ const headerRow = document.createElement('tr');
201
+ headerRow.innerHTML = '<th>Label</th><th>roll</th><th>pitch</th>';
202
+ table.appendChild(headerRow);
203
+
204
+
205
+ data.attitudeSteps.forEach(step => {
206
+ const row = document.createElement('tr');
207
+ if (step.id != undefined)
208
+ row.classList.add(step.id);
209
+ row.innerHTML = `<td>${step.label}</td><td>${cAngle(step.roll)}</td><td>${cAngle(step.pitch)}</td>`;
210
+ table.appendChild(row);
211
+ });
212
+ attitudeContainer.appendChild(table);
213
+ }
214
+
215
+ function updateTable(data) {
216
+ const tableContainer = document.getElementById('table-container');
217
+ tableContainer.innerHTML = '';
218
+ const tableElement = tableRenderer.render(data.tables[0]);
219
+ tableContainer.appendChild(tableElement);
220
+ }
221
+
222
+
223
+
224
+
225
+ async function fetchAndUpdateData() {
226
+ const data = await getFromServer('getResults'); // Updated endpoint
227
+ if (data) {
228
+ //console.log(data);
229
+ updateOptions(data);
230
+ updatePolar(data);
231
+ updateAttitude(data);
232
+ updateDelta(data);
233
+ updateTable(data);
234
+ }
235
+ }
236
+
237
+ function startUpdates() {
238
+ if (updateTimer) clearInterval(updateTimer);
239
+ updateTimer = setInterval(fetchAndUpdateData, updateInterval);
240
+ }
241
+
242
+ export function toggleUpdates() {
243
+ updatesPaused = !updatesPaused;
244
+ const toggleButton = document.getElementById('toggle-updates');
245
+
246
+ if (updatesPaused) {
247
+ clearInterval(updateTimer);
248
+ toggleButton.textContent = "Resume";
249
+ } else {
250
+ toggleButton.textContent = "Pause";
251
+ startUpdates();
252
+ }
253
+ }
254
+
255
+
256
+
257
+
258
+
259
+ //document.getElementById('toggle-updates').addEventListener('click', toggleUpdates);
260
+
261
+ handleSpeedUnitChange("knots");
262
+ handleAngleUnitChange("degrees");
263
+
264
+ const tableRenderer = new TableRenderer();
265
+ handleTableStyleChange("cartesian");
266
+ tableRenderer.setColumnHeaderFormat(cAngle);
267
+ tableRenderer.setRowHeaderFormat(cSpeed);
268
+
269
+
270
+ // Attach functions to the window object to make them globally accessible
271
+ window.handleSpeedUnitChange = handleSpeedUnitChange;
272
+ window.handleAngleUnitChange = handleAngleUnitChange;
273
+ window.handleTableStyleChange = handleTableStyleChange;
274
+ window.toggleUpdates = toggleUpdates;
275
+
276
+
277
+ // Initial fetch and start updates
278
+ startUpdates();
Binary file
@@ -0,0 +1,107 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Speed and current</title>
8
+ <link rel="stylesheet" href="style.css">
9
+ </head>
10
+
11
+ <body>
12
+
13
+ <header class="menu-bar">
14
+ <!-- Speed Unit Selection -->
15
+ <div class="menu-item dropdown">
16
+ <label for="speed-unit">Speed:</label>
17
+ <select id="speed-unit" onchange="handleSpeedUnitChange(this.value)">
18
+ <option value="ms">m/s</option>
19
+ <option value="knots" selected>knots</option>
20
+ <option value="kmh">km/h</option>
21
+ </select>
22
+ </div>
23
+
24
+ <!-- Angle Unit Selection -->
25
+ <div class="menu-item dropdown">
26
+ <label for="angle-unit">Angle:</label>
27
+ <select id="angle-unit" onchange="handleAngleUnitChange(this.value)">
28
+ <option value="radians">radians</option>
29
+ <option value="degrees" selected>degrees</option>
30
+ </select>
31
+ </div>
32
+
33
+ <!-- Table style Selection -->
34
+ <div class="menu-item dropdown">
35
+ <label for="table-style">Table style:</label>
36
+ <select id="table-style" onchange="handleTableStyleChange(this.value)">
37
+ <option value="cartesian" selected>cartesian</option>
38
+ <option value="polar" >polar</option>
39
+ </select>
40
+ </div>
41
+
42
+
43
+ <!-- Pause Button -->
44
+ <button id="toggle-updates" class="menu-item" onclick="toggleUpdates()">Pause</button>
45
+
46
+ <!-- messages -->
47
+
48
+ <div id="message">
49
+ </div>
50
+
51
+ </header>
52
+
53
+ <!-- Content below the fixed menu -->
54
+ <main class="content">
55
+
56
+ <section class="svg-section">
57
+ <object data="vectors.html" width="100%" height="500px" type="text/html"></object>
58
+ </section>
59
+
60
+
61
+ <section class="delta-section">
62
+ <h2>Deltas</h2>
63
+ <div id="delta-container">
64
+ <!-- Speeds table will be populated here -->
65
+ </div>
66
+ </section>
67
+
68
+ <section class="speeds-section">
69
+ <h2>Vectors</h2>
70
+ <div id="speeds-container">
71
+ <!-- Speeds table will be populated here -->
72
+ </div>
73
+ </section>
74
+
75
+ <section class="attitude-section">
76
+ <h2>Attitude</h2>
77
+ <div id="attitude-container">
78
+ <!-- attitude table will be populated here -->
79
+ </div>
80
+ </section>
81
+
82
+
83
+ <section class="table-section">
84
+ <h2>Correction table</h2>
85
+ <div id="table-container">
86
+ <!-- Steps table will be populated here -->
87
+ </div>
88
+
89
+ </section>
90
+
91
+ <section class="options-section">
92
+ <h2>Plugin options</h2>
93
+ <div id="options-content">
94
+ <!-- Options table will be populated here -->
95
+ </div>
96
+ </section>
97
+ </main>
98
+
99
+
100
+
101
+
102
+
103
+
104
+ <script type="module" src="app.js"></script>
105
+ </body>
106
+
107
+ </html>