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