OpenCPN Partial API docs
Loading...
Searching...
No Matches
wind_history.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by Thomas Rauch *
3 * Copyright (C) 2010 by David S. Register *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include <wx/wxprec.h>
26
27#ifndef WX_PRECOMP
28#include <wx/wx.h>
29#endif
30
31#include "wind_history.h"
32
33//************************************************************************************************************************
34// History of wind direction
35//************************************************************************************************************************
36
37DashboardInstrument_WindDirHistory::DashboardInstrument_WindDirHistory(
38 wxWindow* parent, wxWindowID id, wxString title,
39 InstrumentProperties* Properties)
40 : DashboardInstrument(parent, id, std::move(title), OCPN_DBP_STC_TWD,
41 Properties) {
42 m_cap_flag.set(OCPN_DBP_STC_TWS);
43 SetDrawSoloInPane(true);
44 m_MaxWindDir = -1;
45 m_WindDir = -1;
46 m_WindDirRange = 90;
47 m_MaxWindSpd = 0;
48 m_WindSpeedUnit = _("-");
49 m_TotalMaxWindSpd = 0;
50 m_WindSpd = 0;
51 // Set top line height to leave space for wind data
52 wxClientDC dc(this);
53 int w, h;
54 wxFont f;
55 if (m_Properties)
56 f = m_Properties->m_DataFont.GetChosenFont();
57 else
58 f = g_pFontData->GetChosenFont();
59 dc.GetTextExtent("TWS----", &w, &h, nullptr, nullptr, &f);
60 m_TopLineHeight = wxMax(30, h);
61 m_SpdRecCnt = 0;
62 m_DirRecCnt = 0;
63 m_SpdStartVal = -1;
64 m_DirStartVal = -1;
65 m_IsRunning = false;
66 m_SetNewData = 0;
67 m_SampleCount = 0;
68 m_LeftLegend = 3;
69 m_RightLegend = 3;
70 for (int idx = 0; idx < WIND_RECORD_COUNT; idx++) {
71 m_ArrayWindDirHistory[idx] = -1;
72 m_ArrayWindSpdHistory[idx] = -1;
73 m_ExpSmoothArrayWindSpd[idx] = -1;
74 m_ExpSmoothArrayWindDir[idx] = -1;
75 m_ArrayRecTime[idx] = wxDateTime::Now().GetTm();
76 m_ArrayRecTime[idx].year = 999;
77 }
78 alpha = 0.01; // smoothing constant
79 m_WindowRect = GetClientRect();
80 m_DrawAreaRect = GetClientRect();
81 m_DrawAreaRect.SetHeight(m_WindowRect.height - m_TopLineHeight -
82 m_TitleHeight);
83}
84
85wxSize DashboardInstrument_WindDirHistory::GetSize(int orient, wxSize hint) {
86 wxClientDC dc(this);
87 int w;
88 wxFont f;
89 if (m_Properties)
90 f = m_Properties->m_TitleFont.GetChosenFont();
91 else
92 f = g_pFontTitle->GetChosenFont();
93 // Use a dummy for default min width instead of m_title
94 wxString widthdummy = "Left Space TWS 25.5 kn TWD 320 right s";
95 dc.GetTextExtent(widthdummy, &w, &m_TitleHeight, nullptr, nullptr, &f);
96 if (orient == wxHORIZONTAL) {
97 return wxSize(DefaultWidth, wxMax(m_TitleHeight + 140, hint.y));
98 } else {
99 return wxSize(wxMax(hint.x, w), wxMax(m_TitleHeight + 140, hint.y));
100 }
101}
102
103void DashboardInstrument_WindDirHistory::SetData(DASH_CAP st, double data,
104 wxString unit) {
105 if (st == OCPN_DBP_STC_TWD || st == OCPN_DBP_STC_TWS) {
106 if (m_SetNewData < 1) {
107 if (st == OCPN_DBP_STC_TWD) {
108 if (std::isnan(data)) {
109 // This NAN is from the one Watchdog used to reset wind history graph
110 ResetData();
111 m_WindSpd = m_WindDir = NAN;
112 } else {
113 m_WindDir = data;
114 if (m_DirRecCnt <= 5) {
115 m_DirStartVal += static_cast<int>(data);
116 m_DirRecCnt++;
117 }
118 }
119 }
120 if (st == OCPN_DBP_STC_TWS && !std::isnan(data) && data < 200.0) {
121 m_WindSpd = data;
122 // if unit changes, reset everything ...
123 if (unit != m_WindSpeedUnit && m_WindSpeedUnit != _("-")) {
124 ResetData();
125 }
126 m_WindSpeedUnit = unit;
127 if (m_SpdRecCnt <= 5) {
128 m_SpdStartVal += static_cast<int>(data);
129 m_SpdRecCnt++;
130 }
131 }
132 if (m_SpdRecCnt == 5 && m_DirRecCnt == 5) {
133 m_WindSpd = static_cast<int>(m_SpdStartVal / 5);
134 m_WindDir = static_cast<int>(m_DirStartVal / 5);
135 m_oldDirVal = m_WindDir; // make sure we don't get a diff > or <180 in
136 // the initial run
137 }
138 // start working after we collected 5 records each, as start values for
139 // the smoothed curves
140 if (m_SpdRecCnt > 5 && m_DirRecCnt > 5) {
141 m_IsRunning = true;
142 m_SampleCount = m_SampleCount < WIND_RECORD_COUNT ? m_SampleCount + 1
143 : WIND_RECORD_COUNT;
144 m_MaxWindDir = 0;
145 m_MinWindDir = 360;
146 m_MaxWindSpd = 0;
147 // data shifting
148 for (int idx = 1; idx < WIND_RECORD_COUNT; idx++) {
149 if (WIND_RECORD_COUNT - m_SampleCount <= idx)
150 m_MinWindDir = wxMin(m_ArrayWindDirHistory[idx], m_MinWindDir);
151 m_MaxWindDir = wxMax(m_ArrayWindDirHistory[idx - 1], m_MaxWindDir);
152 m_MaxWindSpd = wxMax(m_ArrayWindSpdHistory[idx - 1], m_MaxWindSpd);
153 m_ArrayWindDirHistory[idx - 1] = m_ArrayWindDirHistory[idx];
154 m_ArrayWindSpdHistory[idx - 1] = m_ArrayWindSpdHistory[idx];
155 m_ExpSmoothArrayWindSpd[idx - 1] = m_ExpSmoothArrayWindSpd[idx];
156 m_ExpSmoothArrayWindDir[idx - 1] = m_ExpSmoothArrayWindDir[idx];
157 m_ArrayRecTime[idx - 1] = m_ArrayRecTime[idx];
158 }
159 double diff = m_WindDir - m_oldDirVal;
160 if (diff < -270) {
161 m_WindDir += 360;
162 } else if (diff > 270) {
163 m_WindDir -= 360;
164 }
165 m_ArrayWindDirHistory[WIND_RECORD_COUNT - 1] = m_WindDir;
166 m_ArrayWindSpdHistory[WIND_RECORD_COUNT - 1] = m_WindSpd;
167 if (m_SampleCount < 2) {
168 m_ArrayWindSpdHistory[WIND_RECORD_COUNT - 2] = m_WindSpd;
169 m_ExpSmoothArrayWindSpd[WIND_RECORD_COUNT - 2] = m_WindSpd;
170 m_ArrayWindDirHistory[WIND_RECORD_COUNT - 2] = m_WindDir;
171 m_ExpSmoothArrayWindDir[WIND_RECORD_COUNT - 2] = m_WindDir;
172 }
173 m_ExpSmoothArrayWindSpd[WIND_RECORD_COUNT - 1] =
174 alpha * m_ArrayWindSpdHistory[WIND_RECORD_COUNT - 2] +
175 (1 - alpha) * m_ExpSmoothArrayWindSpd[WIND_RECORD_COUNT - 2];
176 m_ExpSmoothArrayWindDir[WIND_RECORD_COUNT - 1] =
177 alpha * m_ArrayWindDirHistory[WIND_RECORD_COUNT - 2] +
178 (1 - alpha) * m_ExpSmoothArrayWindDir[WIND_RECORD_COUNT - 2];
179 m_ArrayRecTime[WIND_RECORD_COUNT - 1] = wxDateTime::Now().GetTm();
180 m_oldDirVal = m_ExpSmoothArrayWindDir[WIND_RECORD_COUNT - 1];
181 // include the new/latest value in the max/min value test too
182 m_MaxWindDir = wxMax(m_WindDir, m_MaxWindDir);
183 m_MinWindDir = wxMin(m_WindDir, m_MinWindDir);
184 m_MaxWindSpd = wxMax(m_WindSpd, m_MaxWindSpd);
185 // get the overall max Wind Speed
186 m_TotalMaxWindSpd = wxMax(m_WindSpd, m_TotalMaxWindSpd);
187
188 // set wind angle scale to full +/- 90 degr depending on the real
189 // max/min value recorded
190 SetMinMaxWindScale();
191 // Wait two times until new data.
192 m_SetNewData = 2;
193 }
194 } else
195 m_SetNewData--;
196 }
197}
198
199void DashboardInstrument_WindDirHistory::ResetData() {
200 m_MaxWindDir = -1;
201 m_WindDir = -1;
202 m_WindDirRange = 90;
203 m_MaxWindSpd = 0;
204 m_TotalMaxWindSpd = 0;
205 m_WindSpd = 0;
206 m_SpdRecCnt = 0;
207 m_DirRecCnt = 0;
208 m_SpdStartVal = -1;
209 m_DirStartVal = -1;
210 m_IsRunning = false;
211 m_SetNewData = 0;
212 m_SampleCount = 0;
213 m_LeftLegend = 3;
214 m_RightLegend = 3;
215 for (int idx = 0; idx < WIND_RECORD_COUNT; idx++) {
216 m_ArrayWindDirHistory[idx] = -1;
217 m_ArrayWindSpdHistory[idx] = -1;
218 m_ExpSmoothArrayWindSpd[idx] = -1;
219 m_ExpSmoothArrayWindDir[idx] = -1;
220 m_ArrayRecTime[idx] = wxDateTime::Now().GetTm();
221 m_ArrayRecTime[idx].year = 999;
222 }
223}
224
225void DashboardInstrument_WindDirHistory::Draw(wxGCDC* dc) {
226 m_WindowRect = GetClientRect();
227 m_DrawAreaRect = GetClientRect();
228 m_DrawAreaRect.SetHeight(m_WindowRect.height - m_TopLineHeight -
229 m_TitleHeight);
230 m_DrawAreaRect.SetX(m_LeftLegend + 3);
231 DrawBackground(dc);
232 DrawForeground(dc);
233}
234
235//*********************************************************************************
236// determine and set min and max values for the direction
237//*********************************************************************************
238void DashboardInstrument_WindDirHistory::SetMinMaxWindScale() {
239 // set wind direction legend to full +/- 90 degr depending on the real max/min
240 // value recorded example : max wind dir. = 45 degr ==> max = 90 degr
241 // min wind dir. = 45 degr ==> min = 0 degr
242 // first calculate the max wind direction
243 // chop off the decimals bytype conversion from double to int !
244 int fulldeg = static_cast<int>(m_MaxWindDir / 90);
245 if (fulldeg == 0)
246 fulldeg = m_MaxWindDir < 0 ? 0 : 1;
247 else if (m_MaxWindDir > 0)
248 fulldeg += 1;
249 m_MaxWindDir = fulldeg * 90;
250 // now calculate the min wind direction
251 fulldeg = static_cast<int>(m_MinWindDir / 90);
252 if (fulldeg == 0)
253 fulldeg = m_MinWindDir < 0 ? -1 : 0;
254 else
255 fulldeg = m_MinWindDir > 0 ? fulldeg : (fulldeg - 1);
256 m_MinWindDir = fulldeg * 90;
257
258 // limit the visible wind dir range to 360 degr remove the extra range on the
259 // opposite side of the current wind dir value
260 m_WindDirRange = m_MaxWindDir - m_MinWindDir;
261 if (m_WindDirRange > 360) {
262 int diff2min = static_cast<int>(m_WindDir - m_MinWindDir);
263 // diff between min value and current value
264 int diff2max = static_cast<int>(m_MaxWindDir - m_WindDir);
265 // diff between max value and current value
266 if (diff2min > diff2max) {
267 while (m_WindDirRange > 360) {
268 m_MinWindDir += 90;
269 m_WindDirRange = m_MaxWindDir - m_MinWindDir;
270 }
271 }
272 if (diff2min < diff2max) {
273 while (m_WindDirRange > 360) {
274 m_MaxWindDir -= 90;
275 m_WindDirRange = m_MaxWindDir - m_MinWindDir;
276 }
277 }
278 }
279}
280//*********************************************************************************
281// wind direction legend
282//*********************************************************************************
283void DashboardInstrument_WindDirHistory::DrawWindDirScale(wxGCDC* dc) {
284 wxString label1, label2, label3, label4, label5;
285 wxPen pen;
286 int width, height;
287 if (m_Properties) {
288 dc->SetTextForeground(
289 GetColourSchemeFont(m_Properties->m_SmallFont.GetColour()));
290 dc->SetFont(m_Properties->m_SmallFont.GetChosenFont());
291 } else {
292 dc->SetTextForeground(GetColourSchemeFont(g_pFontSmall->GetColour()));
293 dc->SetFont(g_pFontSmall->GetChosenFont());
294 }
295 if (!m_IsRunning) {
296 label1 = "---";
297 label2 = "---";
298 label3 = "---";
299 label4 = "---";
300 label5 = "---";
301 } else {
302 // label 1 : legend for bottom line. By definition always w/o decimals
303 double tempdir = m_MinWindDir;
304 while (tempdir < 0) tempdir += 360;
305 while (tempdir >= 360) tempdir -= 360;
306 label1 = GetWindDirStr(wxString::Format("%.1f", tempdir));
307 // label 2 : 1/4
308 tempdir = m_MinWindDir + m_WindDirRange / 4.;
309 while (tempdir < 0) tempdir += 360;
310 while (tempdir >= 360) tempdir -= 360;
311 label2 = GetWindDirStr(wxString::Format("%.1f", tempdir));
312 // label 3 : legend for center line
313 tempdir = m_MinWindDir + m_WindDirRange / 2;
314 while (tempdir < 0) tempdir += 360;
315 while (tempdir >= 360) tempdir -= 360;
316 label3 = GetWindDirStr(wxString::Format("%.1f", tempdir));
317 // label 4 : 3/4
318 tempdir = m_MinWindDir + m_WindDirRange * 0.75;
319 while (tempdir < 0) tempdir += 360;
320 while (tempdir >= 360) tempdir -= 360;
321 label4 = GetWindDirStr(wxString::Format("%.1f", tempdir));
322 // label 5 : legend for top line
323 tempdir = m_MaxWindDir;
324 while (tempdir < 0) tempdir += 360;
325 while (tempdir >= 360) tempdir -= 360;
326 label5 = GetWindDirStr(wxString::Format("%.1f", tempdir));
327 }
328 // draw the legend with the labels; find the widest string and store it in
329 // m_RightLegend.
330 // m_RightLegend is the basis for the horizontal lines !
331 wxFont f;
332 if (m_Properties) {
333 f = m_Properties->m_SmallFont.GetChosenFont();
334 dc->GetTextExtent(label5, &width, &height, nullptr, nullptr, &f);
335 m_RightLegend = width;
336 dc->GetTextExtent(label4, &width, &height, nullptr, nullptr, &f);
337 m_RightLegend = wxMax(width, m_RightLegend);
338 dc->GetTextExtent(label3, &width, &height, nullptr, nullptr, &f);
339 m_RightLegend = wxMax(width, m_RightLegend);
340 dc->GetTextExtent(label2, &width, &height, nullptr, nullptr, &f);
341 m_RightLegend = wxMax(width, m_RightLegend);
342 dc->GetTextExtent(label1, &width, &height, nullptr, nullptr, &f);
343 m_RightLegend = wxMax(width, m_RightLegend);
344 } else {
345 f = g_pFontSmall->GetChosenFont();
346 dc->GetTextExtent(label5, &width, &height, nullptr, nullptr, &f);
347 m_RightLegend = width;
348 dc->GetTextExtent(label4, &width, &height, nullptr, nullptr, &f);
349 m_RightLegend = wxMax(width, m_RightLegend);
350 dc->GetTextExtent(label3, &width, &height, nullptr, nullptr, &f);
351 m_RightLegend = wxMax(width, m_RightLegend);
352 dc->GetTextExtent(label2, &width, &height, nullptr, nullptr, &f);
353 m_RightLegend = wxMax(width, m_RightLegend);
354 dc->GetTextExtent(label1, &width, &height, nullptr, nullptr, &f);
355 m_RightLegend = wxMax(width, m_RightLegend);
356 }
357 m_RightLegend += 4; // leave some space to the edge
358 dc->DrawText(label5, m_WindowRect.width - m_RightLegend,
359 m_TopLineHeight - height / 2);
360 dc->DrawText(label4, m_WindowRect.width - m_RightLegend,
361 (int)(m_TopLineHeight + m_DrawAreaRect.height / 4 - height / 2));
362 dc->DrawText(label3, m_WindowRect.width - m_RightLegend,
363 (int)(m_TopLineHeight + m_DrawAreaRect.height / 2 - height / 2));
364 dc->DrawText(
365 label2, m_WindowRect.width - m_RightLegend,
366 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.75 - height / 2));
367 dc->DrawText(label1, m_WindowRect.width - m_RightLegend,
368 (int)(m_TopLineHeight + m_DrawAreaRect.height - height / 2));
369}
370
371//*********************************************************************************
372// draw wind speed scale
373//*********************************************************************************
374void DashboardInstrument_WindDirHistory::DrawWindSpeedScale(wxGCDC* dc) {
375 wxString label1, label2, label3, label4, label5;
376 wxColour cl;
377 int width, height;
378 double val1;
379 double WindSpdScale;
380
381 // cl = wxColour(61, 61, 204, 255);
382 if (m_Properties) {
383 dc->SetTextForeground(
384 GetColourSchemeFont(m_Properties->m_SmallFont.GetColour()));
385 dc->SetFont(m_Properties->m_SmallFont.GetChosenFont());
386 } else {
387 dc->SetTextForeground(GetColourSchemeFont(g_pFontSmall->GetColour()));
388 dc->SetFont(g_pFontSmall->GetChosenFont());
389 }
390 // round maxWindSpd up to the next full knot; nicer view ...
391 m_MaxWindSpdScale = (int)m_MaxWindSpd + 1;
392 if (!m_IsRunning) {
393 label1.Printf(_("--- %s"), m_WindSpeedUnit.c_str());
394 label2 = label1;
395 label3 = label1;
396 label4 = label1;
397 label5 = label1;
398 } else {
399 /*we round the speed up to the next full knot ==> the top and bottom line
400 have full numbers as legend (e.g. 23 kn -- 0 kn) but the intermediate lines
401 may have decimal values (e.g. center line : 23/2=11.5 or quarter line
402 23/4=5.75), so in worst case we end up with 23 - 17.25 - 11.5 - 5.75 - 0
403 The goal is to draw the legend with decimals only, if we really have them !
404 */
405 // top legend for max wind
406 label1.Printf("%.0f %s", m_MaxWindSpdScale, m_WindSpeedUnit.c_str());
407 // 3/4 legend
408 WindSpdScale = m_MaxWindSpdScale * 3. / 4.;
409 // do we need a decimal ?
410 val1 = (int)((WindSpdScale - (int)WindSpdScale) * 100);
411 if (val1 == 25 || val1 == 75) // it's a .25 or a .75
412 label2.Printf("%.2f %s", WindSpdScale, m_WindSpeedUnit.c_str());
413 else if (val1 == 50)
414 label2.Printf("%.1f %s", WindSpdScale, m_WindSpeedUnit.c_str());
415 else
416 label2.Printf("%.0f %s", WindSpdScale, m_WindSpeedUnit.c_str());
417 // center legend
418 WindSpdScale = m_MaxWindSpdScale / 2.;
419 // center line can either have a .0 or .5 value !
420 if ((int)(WindSpdScale * 10) % 10 == 5)
421 label3.Printf("%.1f %s", WindSpdScale, m_WindSpeedUnit.c_str());
422 else
423 label3.Printf("%.0f %s", WindSpdScale, m_WindSpeedUnit.c_str());
424
425 // 1/4 legend
426 WindSpdScale = m_MaxWindSpdScale / 4.;
427 // do we need a decimal ?
428 val1 = (int)((WindSpdScale - (int)WindSpdScale) * 100);
429 if (val1 == 25 || val1 == 75)
430 label4.Printf("%.2f %s", WindSpdScale, m_WindSpeedUnit.c_str());
431 else if (val1 == 50)
432 label4.Printf("%.1f %s", WindSpdScale, m_WindSpeedUnit.c_str());
433 else
434 label4.Printf("%.0f %s", WindSpdScale, m_WindSpeedUnit.c_str());
435
436 // bottom legend for min wind, always 0
437 label5.Printf("%.0f %s", 0.0, m_WindSpeedUnit.c_str());
438 }
439 wxFont f;
440 if (m_Properties)
441 f = m_Properties->m_SmallFont.GetChosenFont();
442 else
443 f = g_pFontSmall->GetChosenFont();
444 dc->GetTextExtent(label1, &m_LeftLegend, &height, nullptr, nullptr, &f);
445 dc->DrawText(label1, 4, (int)(m_TopLineHeight - height / 2));
446 if (m_Properties)
447 f = m_Properties->m_SmallFont.GetChosenFont();
448 else
449 f = g_pFontSmall->GetChosenFont();
450 dc->GetTextExtent(label2, &width, &height, nullptr, nullptr, &f);
451 dc->DrawText(label2, 4,
452 (int)(m_TopLineHeight + m_DrawAreaRect.height / 4 - height / 2));
453 m_LeftLegend = wxMax(width, m_LeftLegend);
454 if (m_Properties)
455 f = m_Properties->m_SmallFont.GetChosenFont();
456 else
457 f = g_pFontSmall->GetChosenFont();
458 dc->GetTextExtent(label3, &width, &height, nullptr, nullptr, &f);
459 dc->DrawText(label3, 4,
460 (int)(m_TopLineHeight + m_DrawAreaRect.height / 2 - height / 2));
461 m_LeftLegend = wxMax(width, m_LeftLegend);
462 if (m_Properties)
463 f = m_Properties->m_SmallFont.GetChosenFont();
464 else
465 f = g_pFontSmall->GetChosenFont();
466 dc->GetTextExtent(label4, &width, &height, nullptr, nullptr, &f);
467 dc->DrawText(
468 label4, 4,
469 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.75 - height / 2));
470 m_LeftLegend = wxMax(width, m_LeftLegend);
471 if (m_Properties)
472 f = m_Properties->m_SmallFont.GetChosenFont();
473 else
474 f = g_pFontSmall->GetChosenFont();
475 dc->GetTextExtent(label5, &width, &height, nullptr, nullptr, &f);
476 dc->DrawText(label5, 4,
477 (int)(m_TopLineHeight + m_DrawAreaRect.height - height / 2));
478 m_LeftLegend = wxMax(width, m_LeftLegend);
479 m_LeftLegend += 4;
480}
481
482//*********************************************************************************
483// draw background
484//*********************************************************************************
485void DashboardInstrument_WindDirHistory::DrawBackground(wxGCDC* dc) {
486 wxString label, label1, label2, label3, label4, label5;
487 wxColour cl;
488 wxPen pen;
489 //---------------------------------------------------------------------------------
490 // draw legends for speed and direction
491 //---------------------------------------------------------------------------------
492 DrawWindDirScale(dc);
493 DrawWindSpeedScale(dc);
494
495 //---------------------------------------------------------------------------------
496 // horizontal lines
497 //---------------------------------------------------------------------------------
498 GetGlobalColor("UBLCK", &cl);
499 pen.SetColour(cl);
500 dc->SetPen(pen);
501 dc->DrawLine(m_LeftLegend + 3, m_TopLineHeight,
502 m_WindowRect.width - 3 - m_RightLegend,
503 m_TopLineHeight); // the upper line
504 dc->DrawLine(m_LeftLegend + 3, (int)(m_TopLineHeight + m_DrawAreaRect.height),
505 m_WindowRect.width - 3 - m_RightLegend,
506 (int)(m_TopLineHeight + m_DrawAreaRect.height));
507 pen.SetStyle(wxPENSTYLE_DOT);
508 dc->SetPen(pen);
509 dc->DrawLine(m_LeftLegend + 3,
510 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.25),
511 m_WindowRect.width - 3 - m_RightLegend,
512 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.25));
513 dc->DrawLine(m_LeftLegend + 3,
514 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.75),
515 m_WindowRect.width - 3 - m_RightLegend,
516 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.75));
517#ifdef __WXMSW__
518 pen.SetStyle(wxPENSTYLE_SHORT_DASH);
519 dc->SetPen(pen);
520#endif
521 dc->DrawLine(m_LeftLegend + 3,
522 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.5),
523 m_WindowRect.width - 3 - m_RightLegend,
524 (int)(m_TopLineHeight + m_DrawAreaRect.height * 0.5));
525}
526
527//***********************************************************************************
528// convert numerical wind direction values to text, used in the wind direction
529// legend
530//***********************************************************************************
531wxString DashboardInstrument_WindDirHistory::GetWindDirStr(wxString WindDir) {
532 if (WindDir == "0.0" || WindDir == "360.0")
533 return _("N");
534 else if (WindDir == "22.5")
535 return _("NNE");
536 else if (WindDir == "45.0")
537 return _("NE");
538 else if (WindDir == "67.5")
539 return _("ENE");
540 else if (WindDir == "90.0")
541 return _("E");
542 else if (WindDir == "112.5")
543 return _("ESE");
544 else if (WindDir == "135.0")
545 return _("SE");
546 else if (WindDir == "157.5")
547 return _("SSE");
548 else if (WindDir == "180.0")
549 return _("S");
550 else if (WindDir == "202.5")
551 return _("SSW");
552 else if (WindDir == "225.0")
553 return _("SW");
554 else if (WindDir == "247.5")
555 return _("WSW");
556 else if (WindDir == "270.0")
557 return _("W");
558 else if (WindDir == "292.5")
559 return _("WNW");
560 else if (WindDir == "315.0")
561 return _("NW");
562 else if (WindDir == "337.5")
563 return _("NNW");
564 else
565 return WindDir;
566}
567
568//*********************************************************************************
569// draw foreground
570//*********************************************************************************
571void DashboardInstrument_WindDirHistory::DrawForeground(wxGCDC* dc) {
572 wxColour col;
573 double ratioH;
574 int width, height, min, hour;
575 double dir;
576 wxString WindAngle, WindSpeed;
577 wxPen pen;
578 wxString label;
579
580 //---------------------------------------------------------------------------------
581 // wind direction
582 //---------------------------------------------------------------------------------
583 dc->SetFont((g_pFontSmall->GetChosenFont()));
584 col = wxColour(204, 41, 41, 255); // red, opaque Set TWD to DataFonf + Color,
585 // TWS to LabelFOnt + Color
586 if (m_Properties) {
587 dc->SetFont(m_Properties->m_SmallFont.GetChosenFont());
588 dc->SetTextForeground(
589 GetColourSchemeFont(m_Properties->m_SmallFont.GetColour()));
590 } else {
591 dc->SetFont(g_pFontSmall->GetChosenFont());
592 if (GetColourSchemeFont(g_pFontLabel->GetColour()) ==
593 GetColourSchemeFont(g_pFontLabel->GetColour()))
594 dc->SetTextForeground(col);
595 else
596 dc->SetTextForeground(GetColourSchemeFont(g_pFontSmall->GetColour()));
597 }
598 if (!m_IsRunning)
599 WindAngle = "TWD --- ";
600 else {
601 dir = m_WindDir;
602 while (dir > 360) dir -= 360;
603 while (dir < 0) dir += 360;
604 if (!std::isnan(dir))
605 WindAngle = wxString::Format("TWD %3.0f", dir) + DEGREE_SIGN;
606 else
607 WindAngle = wxString::Format("TWD ---") + DEGREE_SIGN;
608 }
609 wxFont f;
610 if (m_Properties)
611 f = m_Properties->m_SmallFont.GetChosenFont();
612 else
613 f = g_pFontLabel->GetChosenFont();
614
615 dc->GetTextExtent(WindAngle, &degw, &degh, nullptr, nullptr, &f);
616 dc->DrawText(WindAngle, m_WindowRect.width - m_RightLegend - degw, 3);
617
618 pen.SetStyle(wxPENSTYLE_SOLID);
619 if (m_Properties) {
620#if wxCHECK_VERSION(3, 1, 6)
621 unsigned int r =
622 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()).GetRed();
623 unsigned int g =
624 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()).GetGreen();
625 unsigned int b =
626 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()).GetBlue();
627#else
628 unsigned int r =
629 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()).Red();
630 unsigned int g =
631 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()).Green();
632 unsigned int b =
633 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()).Blue();
634#endif
635 pen.SetColour(wxColour(r, g, b, 96)); // transparent
636 } else {
637 if (GetColourSchemeFont(g_pFontData->GetColour()) ==
638 GetColourSchemeFont(g_pFontLabel->GetColour()))
639 pen.SetColour(wxColour(204, 41, 41, 96));
640 else {
641#if wxCHECK_VERSION(3, 1, 6)
642 unsigned int r = GetColourSchemeFont(g_pFontData->GetColour()).GetRed();
643 unsigned int g = GetColourSchemeFont(g_pFontData->GetColour()).GetGreen();
644 unsigned int b = GetColourSchemeFont(g_pFontData->GetColour()).GetBlue();
645#else
646 unsigned int r = GetColourSchemeFont(g_pFontData->GetColour()).Red();
647 unsigned int g = GetColourSchemeFont(g_pFontData->GetColour()).Green();
648 unsigned int b = GetColourSchemeFont(g_pFontData->GetColour()).Blue();
649#endif
650 pen.SetColour(wxColour(r, g, b, 96)); // transparent
651 }
652 }
653 // pen.SetColour(wxColour(204, 41, 41, 96)); // red, transparent
654 pen.SetWidth(1);
655 dc->SetPen(pen);
656 ratioH = (double)m_DrawAreaRect.height / m_WindDirRange;
657 m_DrawAreaRect.SetWidth(m_WindowRect.width - 6 - m_LeftLegend -
658 m_RightLegend);
659 m_ratioW = double(m_DrawAreaRect.width) / (WIND_RECORD_COUNT - 1);
660
661 //---------------------------------------------------------------------------------
662 // live direction data
663 //---------------------------------------------------------------------------------
664 wxPoint points[WIND_RECORD_COUNT + 2];
665 wxPoint wdDraw[WIND_RECORD_COUNT + 2];
666 int ld = 0;
667 wdDraw[ld].x = static_cast<int>(m_ratioW + 3 + m_LeftLegend);
668 wdDraw[ld].y =
669 static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
670 (m_ArrayWindDirHistory[1] - m_MinWindDir) * ratioH);
671
672 for (int idx = 1; idx < WIND_RECORD_COUNT; idx++) {
673 points[idx].x = static_cast<int>(idx * m_ratioW + 3 + m_LeftLegend);
674 points[idx].y =
675 static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
676 (m_ArrayWindDirHistory[idx] - m_MinWindDir) * ratioH);
677 if (WIND_RECORD_COUNT - m_SampleCount <= idx &&
678 points[idx].y > m_TopLineHeight &&
679 points[idx].y <= m_TopLineHeight + m_DrawAreaRect.height) {
680 wdDraw[ld] = points[idx];
681 ld++;
682 }
683 }
684 if (ld > 1) dc->DrawLines(ld, wdDraw);
685
686 //---------------------------------------------------------------------------------
687 // exponential smoothing of direction
688 //---------------------------------------------------------------------------------
689 pen.SetStyle(wxPENSTYLE_SOLID);
690 // pen.SetColour(wxColour(204, 41, 41, 255));
691 if (m_Properties)
692 pen.SetColour(GetColourSchemeFont(m_Properties->m_DataFont.GetColour()));
693 else {
694 if (GetColourSchemeFont(g_pFontData->GetColour()) ==
695 GetColourSchemeFont(g_pFontLabel->GetColour()))
696 pen.SetColour(wxColour(204, 41, 41, 255));
697 else
698 pen.SetColour(GetColourSchemeFont(g_pFontData->GetColour()));
699 }
700 pen.SetWidth(2);
701 dc->SetPen(pen);
702
703 ld = 0;
704 wdDraw[ld].x = static_cast<int>(m_ratioW + 3 + m_LeftLegend);
705 wdDraw[ld].y =
706 static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
707 (m_ExpSmoothArrayWindDir[ld] - m_MinWindDir) * ratioH);
708
709 for (int idx = 1; idx < WIND_RECORD_COUNT; idx++) {
710 points[idx].x = static_cast<int>(idx * m_ratioW + 3 + m_LeftLegend);
711 points[idx].y = static_cast<int>(
712 m_TopLineHeight + m_DrawAreaRect.height -
713 (m_ExpSmoothArrayWindDir[idx] - m_MinWindDir) * ratioH);
714 if (WIND_RECORD_COUNT - m_SampleCount <= idx &&
715 points[idx].y > m_TopLineHeight &&
716 points[idx].y <= m_TopLineHeight + m_DrawAreaRect.height) {
717 wdDraw[ld] = points[idx];
718 ld++;
719 }
720 }
721 if (ld > 1) dc->DrawLines(ld, wdDraw);
722
723 //---------------------------------------------------------------------------------
724 // wind speed
725 //---------------------------------------------------------------------------------
726 col = wxColour(61, 61, 204, 255); // blue, opaque
727 if (m_Properties) {
728 dc->SetFont(m_Properties->m_SmallFont.GetChosenFont());
729 dc->SetTextForeground(
730 GetColourSchemeFont(m_Properties->m_SmallFont.GetColour()));
731 } else {
732 dc->SetFont(g_pFontSmall->GetChosenFont());
733 if (GetColourSchemeFont(g_pFontSmall->GetColour()) ==
734 GetColourSchemeFont(g_pFontSmall->GetColour()))
735 dc->SetTextForeground(col);
736 else
737 dc->SetTextForeground(GetColourSchemeFont(g_pFontSmall->GetColour()));
738 }
739 if (!std::isnan(m_WindSpd))
740 WindSpeed =
741 wxString::Format("TWS %3.1f %s ", m_WindSpd, m_WindSpeedUnit.c_str());
742 else
743 WindSpeed = wxString::Format("TWS --- %s ", m_WindSpeedUnit.c_str());
744 if (m_Properties) {
745 f = m_Properties->m_LabelFont.GetChosenFont();
746 dc->GetTextExtent(WindSpeed, &speedw, &degh, nullptr, nullptr, &f);
747 } else {
748 f = g_pFontSmall->GetChosenFont();
749 dc->GetTextExtent(WindSpeed, &speedw, &degh, nullptr, nullptr, &f);
750 }
751 dc->DrawText(WindSpeed, m_LeftLegend, 3);
752
753 dc->SetFont((g_pFontSmall->GetChosenFont()));
754 int labelw, labelh;
755 if (m_Properties) {
756 f = m_Properties->m_SmallFont.GetChosenFont();
757 dc->GetTextExtent(WindSpeed, &labelw, &labelh, nullptr, nullptr, &f);
758 dc->SetFont(m_Properties->m_SmallFont.GetChosenFont());
759 dc->SetTextForeground(
760 GetColourSchemeFont(m_Properties->m_SmallFont.GetColour()));
761 } else {
762 f = g_pFontSmall->GetChosenFont();
763 dc->GetTextExtent(WindSpeed, &labelw, &labelh, nullptr, nullptr, &f);
764 dc->SetFont(g_pFontSmall->GetChosenFont());
765 dc->SetTextForeground(GetColourSchemeFont(g_pFontSmall->GetColour()));
766 }
767 // determine the time range of the available data (=oldest data value)
768 int i = 0;
769 while (m_ArrayRecTime[i].year == 999 && i < WIND_RECORD_COUNT - 1) i++;
770 if (i == WIND_RECORD_COUNT - 1) {
771 min = 0;
772 hour = 0;
773 } else {
774 wxDateTime localTime(m_ArrayRecTime[i]);
775 min = localTime.GetMinute();
776 hour = localTime.GetHour();
777 }
778
779 wxString statistics =
780 wxString::Format(_(" Max %.1f %s since %02d:%02d Overall %.1f"),
781 m_MaxWindSpd, m_WindSpeedUnit.c_str(), hour, min,
782 m_TotalMaxWindSpd, m_WindSpeedUnit.c_str());
783 int statw, stath;
784 dc->GetTextExtent(statistics, &statw, &stath, nullptr, nullptr, &f);
785 int dispw =
786 (m_WindowRect.width - m_LeftLegend - speedw - degw - m_RightLegend);
787 if (statw < dispw) {
788 dc->DrawText(statistics, speedw + m_LeftLegend, 3);
789 } else { // Try a shorter text
790 dc->GetTextExtent(statistics.Left(12), &statw, &stath, nullptr, nullptr,
791 &f);
792 if (statw < dispw)
793 dc->DrawText(statistics.Left(12), speedw + m_LeftLegend, 3);
794 }
795
796 pen.SetStyle(wxPENSTYLE_SOLID);
797 if (m_Properties) {
798#if wxCHECK_VERSION(3, 1, 6)
799 unsigned int r =
800 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()).GetRed();
801 unsigned int g =
802 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()).GetGreen();
803 unsigned int b =
804 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()).GetBlue();
805#else
806 unsigned int r =
807 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()).Red();
808 unsigned int g =
809 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()).Green();
810 unsigned int b =
811 GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()).Blue();
812#endif
813 pen.SetColour(wxColour(r, g, b, 96)); // transparent
814 } else {
815 if (GetColourSchemeFont(g_pFontData->GetColour()) ==
816 GetColourSchemeFont(g_pFontLabel->GetColour()))
817 pen.SetColour(wxColour(61, 61, 204, 96)); // blue, transparent
818 else {
819#if wxCHECK_VERSION(3, 1, 6)
820 unsigned int r = GetColourSchemeFont(g_pFontLabel->GetColour()).GetRed();
821 unsigned int g =
822 GetColourSchemeFont(g_pFontLabel->GetColour()).GetGreen();
823 unsigned int b = GetColourSchemeFont(g_pFontLabel->GetColour()).GetBlue();
824#else
825 unsigned int r = GetColourSchemeFont(g_pFontLabel->GetColour()).Red();
826 unsigned int g = GetColourSchemeFont(g_pFontLabel->GetColour()).Green();
827 unsigned int b = GetColourSchemeFont(g_pFontLabel->GetColour()).Blue();
828#endif
829 pen.SetColour(wxColour(r, g, b, 96)); // transparent
830 }
831 }
832 // pen.SetColour(wxColour(61, 61, 204, 96)); // blue, transparent
833 pen.SetWidth(1);
834 dc->SetPen(pen);
835 ratioH = (double)m_DrawAreaRect.height / m_MaxWindSpdScale;
836 wxPoint pointsSpd[WIND_RECORD_COUNT + 2];
837 wxPoint spdDraw[WIND_RECORD_COUNT + 2];
838
839 //---------------------------------------------------------------------------------
840 // live speed data
841 //---------------------------------------------------------------------------------
842
843 int ls = 0;
844 spdDraw[ls].x = static_cast<int>(1 * m_ratioW + 3 + m_LeftLegend);
845 spdDraw[ls].y = static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
846 m_ArrayWindSpdHistory[1] * ratioH);
847
848 for (int idx = 1; idx < WIND_RECORD_COUNT; idx++) {
849 pointsSpd[idx].x = static_cast<int>(idx * m_ratioW + 3 + m_LeftLegend);
850 pointsSpd[idx].y =
851 static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
852 m_ArrayWindSpdHistory[idx] * ratioH);
853 if (WIND_RECORD_COUNT - m_SampleCount <= idx &&
854 pointsSpd[idx].y > m_TopLineHeight &&
855 pointsSpd[idx].y <= m_TopLineHeight + m_DrawAreaRect.height) {
856 spdDraw[ls] = pointsSpd[idx];
857 ls++;
858 }
859 }
860 if (ls > 1) dc->DrawLines(ls, spdDraw);
861
862 //---------------------------------------------------------------------------------
863 // exponential smoothing of speed
864 //---------------------------------------------------------------------------------
865 pen.SetStyle(wxPENSTYLE_SOLID);
866 if (m_Properties)
867 pen.SetColour(GetColourSchemeFont(m_Properties->m_LabelFont.GetColour()));
868 else {
869 if (GetColourSchemeFont(g_pFontData->GetColour()) ==
870 GetColourSchemeFont(g_pFontLabel->GetColour()))
871 pen.SetColour(wxColour(61, 61, 204, 255)); // blue, opaque
872 else
873 pen.SetColour(GetColourSchemeFont(g_pFontLabel->GetColour()));
874 }
875 // pen.SetColour(wxColour(61, 61, 204, 255)); // blue, opaque
876 pen.SetWidth(2);
877 dc->SetPen(pen);
878 ls = 0;
879 spdDraw[ls].x = static_cast<int>(1 * m_ratioW + 3 + m_LeftLegend);
880 spdDraw[ls].y = static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
881 m_ExpSmoothArrayWindSpd[1] * ratioH);
882
883 for (int idx = 1; idx < WIND_RECORD_COUNT; idx++) {
884 pointsSpd[idx].x = static_cast<int>(idx * m_ratioW + 3 + m_LeftLegend);
885 pointsSpd[idx].y =
886 static_cast<int>(m_TopLineHeight + m_DrawAreaRect.height -
887 m_ExpSmoothArrayWindSpd[idx] * ratioH);
888 if (WIND_RECORD_COUNT - m_SampleCount <= idx &&
889 pointsSpd[idx].y > m_TopLineHeight &&
890 pointsSpd[idx].y <= m_TopLineHeight + m_DrawAreaRect.height) {
891 spdDraw[ls] = pointsSpd[idx];
892 ls++;
893 }
894 }
895 if (ls > 1) dc->DrawLines(ls, spdDraw);
896
897 //---------------------------------------------------------------------------------
898 // draw vertical timelines every 10 minutes
899 //---------------------------------------------------------------------------------
900 GetGlobalColor("DASHL", &col);
901 pen.SetColour(col);
902 pen.SetStyle(wxPENSTYLE_DOT);
903 dc->SetPen(pen);
904 dc->SetTextForeground(col);
905 dc->SetFont((g_pFontSmall->GetChosenFont()));
906 int done = -1;
907 wxPoint pointTime;
908 for (int idx = 0; idx < WIND_RECORD_COUNT; idx++) {
909 if (m_ArrayRecTime[idx].year != 999) {
910 wxDateTime localTime(m_ArrayRecTime[idx]);
911 hour = localTime.GetHour();
912 min = localTime.GetMinute();
913 if ((hour * 100 + min) != done && (min % 15 == 0)) {
914 pointTime.x = static_cast<int>(idx * m_ratioW + 3 + m_LeftLegend);
915 dc->DrawLine(pointTime.x, m_TopLineHeight + 1, pointTime.x,
916 (m_TopLineHeight + m_DrawAreaRect.height + 1));
917 label.Printf("%02d:%02d", hour, min);
918 f = g_pFontSmall->GetChosenFont();
919 dc->GetTextExtent(label, &width, &height, nullptr, nullptr, &f);
920 dc->DrawText(label, pointTime.x - width / 2,
921 m_WindowRect.height - height);
922 done = hour * 100 + min;
923 }
924 }
925 }
926}
DASH_CAP
Definition instrument.h:77
@ OCPN_DBP_STC_TWD
True Wind Direction (from NMEA device if available, otherwise TWA + magnetic variation)
Definition instrument.h:104
@ OCPN_DBP_STC_TWS
True wind speed.
Definition instrument.h:91
Dashboard wind history instrument.