OpenCPN Partial API docs
Loading...
Searching...
No Matches
cursor_data.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
24#include "pi_gl.h"
25#include "grib_pi.h"
26
27extern int m_Altitude;
28extern int m_DialogStyle;
29
30//---------------------------------------------------------------------------------------
31// GRIB Cursor Data implementation
32//---------------------------------------------------------------------------------------
33CursorData::CursorData(wxWindow *window, GRIBUICtrlBar &parent)
34 : CursorDataBase(window), m_gparent(parent) {
35 // transform checkboxes ID to have a formal link to data type and set the
36 // initial value
37 wxWindowListNode *node = this->GetChildren().GetFirst();
38 while (node) {
39 wxWindow *win = node->GetData();
40 if (dynamic_cast<wxCheckBox *>(win)) {
41 int winId = dynamic_cast<wxCheckBox *>(win)->GetId() - ID_CB_WIND;
42 if (m_gparent.InDataPlot(winId)) {
43 dynamic_cast<wxCheckBox *>(win)->SetId(winId);
44 dynamic_cast<wxCheckBox *>(win)->SetValue(m_gparent.m_bDataPlot[winId]);
45 }
46 }
47 node = node->GetNext();
48 }
49
50 m_bLeftDown = false;
51
52 m_tCursorTrackTimer.Connect(
53 wxEVT_TIMER, wxTimerEventHandler(CursorData::OnCursorTrackTimer), nullptr,
54 this);
55
56 DimeWindow(this);
57}
58
59void CursorData::OnCBAny(wxCommandEvent &event) {
60 int id = event.GetId();
61 wxWindow *win = this->FindWindow(id);
62 if (m_gparent.InDataPlot(id))
63 m_gparent.m_bDataPlot[id] = ((wxCheckBox *)win)->IsChecked();
64 ResolveDisplayConflicts(id);
65}
66
67void CursorData::ResolveDisplayConflicts(int Id) {
68 // allow multi selection only if there is no display type superposition
69 for (int i = 0; i < GribOverlaySettings::GEO_ALTITUDE; i++) {
70 if (i != Id && m_gparent.m_bDataPlot[i]) {
71 if ((m_gparent.m_OverlaySettings.Settings[Id].m_bBarbedArrows &&
72 m_gparent.m_OverlaySettings.Settings[i].m_bBarbedArrows) ||
73 (m_gparent.m_OverlaySettings.Settings[Id].m_bDirectionArrows &&
74 m_gparent.m_OverlaySettings.Settings[i].m_bDirectionArrows) ||
75 (m_gparent.m_OverlaySettings.Settings[Id].m_bIsoBars &&
76 m_gparent.m_OverlaySettings.Settings[i].m_bIsoBars) ||
77 (m_gparent.m_OverlaySettings.Settings[Id].m_bNumbers &&
78 m_gparent.m_OverlaySettings.Settings[i].m_bNumbers) ||
79 (m_gparent.m_OverlaySettings.Settings[Id].m_bOverlayMap &&
80 m_gparent.m_OverlaySettings.Settings[i].m_bOverlayMap) ||
81 (m_gparent.m_OverlaySettings.Settings[Id].m_bParticles &&
82 m_gparent.m_OverlaySettings.Settings[i].m_bParticles)) {
83 m_gparent.m_bDataPlot[i] = false;
84 wxWindow *win = FindWindow(i);
85 ((wxCheckBox *)win)->SetValue(false);
86 }
87 }
88 }
89 m_gparent.SetFactoryOptions(); // Reload the visibility options
90}
91
92void CursorData::AddTrackingControl(wxControl *ctrl1, wxControl *ctrl2,
93 wxControl *ctrl3, wxControl *ctrl4,
94 bool show, bool vertical, int wctrl2,
95 int wctrl3_4) {
96 if (show) {
97 m_fgTrackingControls->Add(ctrl1, 0, wxALL, 1);
98 ctrl1->Show();
99 if (ctrl2) {
100 m_fgTrackingControls->Add(ctrl2, 0, wxALL, 0);
101 ctrl2->SetMinSize(wxSize(wctrl2, -1));
102 ctrl2->Show();
103 } else
104 m_fgTrackingControls->Add(0, 0, 1, wxALL, 1); /* spacer */
105
106 if (ctrl3) {
107 long flag1 = wxALIGN_CENTER;
108 long flag = vertical ? flag1 : wxALL;
109 m_fgTrackingControls->Add(ctrl3, 0, flag, 0);
110 ctrl3->SetMinSize(wxSize(wctrl3_4, -1));
111 ctrl3->Show();
112 } else if (!vertical)
113 m_fgTrackingControls->Add(0, 0, 1, wxALL, 1); /* spacer */
114
115 if (ctrl4) {
116 m_fgTrackingControls->Add(ctrl4, 0, wxALL, 0);
117 ctrl4->SetMinSize(wxSize(vertical ? wctrl2 : wctrl3_4, -1));
118 ctrl4->Show();
119 } else if (!vertical)
120 m_fgTrackingControls->Add(0, 0, 1, wxALL, 1); /* spacer */
121
122 } else {
123 if (ctrl1) ctrl1->Hide();
124 if (ctrl2) ctrl2->Hide();
125 if (ctrl3) ctrl3->Hide();
126 if (ctrl4) ctrl4->Hide();
127 }
128}
129
130void CursorData::PopulateTrackingControls(bool vertical) {
131 m_fgTrackingControls->Clear();
132 if (!vertical) {
133 auto *ps = (wxFlexGridSizer *)(m_gparent.GetSizer());
134 if (ps && (ps->GetCols() == 1))
135 m_fgTrackingControls->SetCols(4); // compact mode
136 else
137 m_fgTrackingControls->SetCols(12);
138 } else
139 m_fgTrackingControls->SetCols(2);
140
141 this->Fit();
142 // Get text controls sizing data
143 wxFont *font = OCPNGetFont(_("Dialog"));
144 int wn, wd, ws, wl;
145 GetTextExtent("abcdefghihjk", &wn, nullptr, nullptr, nullptr,
146 font); // normal width text control size
147 GetTextExtent("abcdef", &ws, nullptr, nullptr, nullptr,
148 font); // short width text control size for direction only
149 GetTextExtent(
150 "abcdefghijklmopq", &wd, nullptr, nullptr, nullptr,
151 font); // long width text control size for double unit wind display
152 GetTextExtent(
153 "abcdefghijklm", &wl, nullptr, nullptr, nullptr,
154 font); // long width text control size for double unit wave display
155 //
156 // create a dummy textCtrl to be used as a "space" in vertical display
157 auto *dummy = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
158 wxDefaultSize, wxTE_READONLY | wxNO_BORDER);
159 //
160 bool bf =
161 m_gparent.m_OverlaySettings.Settings[GribOverlaySettings::WIND].m_Units ==
162 GribOverlaySettings::BFS;
163 wd = vertical ? wn : bf ? wn : wd;
164 wl = vertical ? wn : wl;
165
166 AddTrackingControl(m_cbWind, m_tcWindSpeed, m_tcWindSpeedBf,
167 m_tcWindDirection, false, vertical, 0,
168 0); // hide all wind's parameters
169 AddTrackingControl(
170 m_cbWind, m_tcWindSpeed,
171 vertical ? (bf ? dummy : m_tcWindSpeedBf) : m_tcWindDirection,
172 vertical ? m_tcWindDirection : nullptr,
173 m_gparent.m_pTimelineSet &&
174 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VX) !=
175 wxNOT_FOUND &&
176 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VY) !=
177 wxNOT_FOUND,
178 vertical, wd, ws);
179 AddTrackingControl(m_cbWindGust, m_tcWindGust, nullptr, nullptr,
180 m_gparent.m_pTimelineSet &&
181 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
182 Idx_WIND_GUST) != wxNOT_FOUND &&
183 m_Altitude == 0,
184 vertical, wn);
185 AddTrackingControl(m_cbPressure, m_tcPressure, nullptr, nullptr,
186 m_gparent.m_pTimelineSet &&
187 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
188 Idx_PRESSURE) != wxNOT_FOUND &&
189 m_Altitude == 0,
190 vertical, wn);
191
192 /* tracking for wave is funky */
193 AddTrackingControl(m_cbWave, m_tcWaveHeight, m_tcWavePeriode,
194 m_tcWaveDirection, false, vertical, 0,
195 0); // hide all waves's parameters
196 if (m_gparent.m_pTimelineSet &&
197 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_HTSIGW) !=
198 wxNOT_FOUND) {
199 if (m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WVDIR) !=
200 wxNOT_FOUND)
201 AddTrackingControl(m_cbWave, m_tcWaveHeight,
202 vertical ? m_tcWavePeriode : m_tcWaveDirection,
203 vertical ? m_tcWaveDirection : nullptr,
204 m_Altitude == 0, vertical, wl, ws);
205 else
206 AddTrackingControl(m_cbWave, m_tcWaveHeight, nullptr, nullptr,
207 m_Altitude == 0, vertical, wn);
208 } else {
209 if (m_gparent.m_pTimelineSet &&
210 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WVDIR) !=
211 wxNOT_FOUND)
212 AddTrackingControl(m_cbWave, m_tcWaveDirection, nullptr, nullptr,
213 m_Altitude == 0, vertical, wn);
214 }
215
216 AddTrackingControl(m_cbCurrent, m_tcCurrentVelocity, m_tcCurrentDirection,
217 nullptr, false, vertical, 0,
218 0); // hide all current's parameters
219 AddTrackingControl(m_cbCurrent, m_tcCurrentVelocity,
220 vertical ? dummy : m_tcCurrentDirection,
221 vertical ? m_tcCurrentDirection : nullptr,
222 m_gparent.m_pTimelineSet &&
223 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
224 Idx_SEACURRENT_VX) != wxNOT_FOUND &&
225 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
226 Idx_SEACURRENT_VY) != wxNOT_FOUND &&
227 m_Altitude == 0,
228 vertical, wn, ws);
229 AddTrackingControl(m_cbPrecipitation, m_tcPrecipitation, nullptr, nullptr,
230 m_gparent.m_pTimelineSet &&
231 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
232 Idx_PRECIP_TOT) != wxNOT_FOUND &&
233 m_Altitude == 0,
234 vertical, wn);
235 AddTrackingControl(m_cbCloud, m_tcCloud, nullptr, nullptr,
236 m_gparent.m_pTimelineSet &&
237 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
238 Idx_CLOUD_TOT) != wxNOT_FOUND &&
239 m_Altitude == 0,
240 vertical, wn);
241 AddTrackingControl(m_cbAirTemperature, m_tcAirTemperature, nullptr, nullptr,
242 m_gparent.m_pTimelineSet &&
243 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
244 Idx_AIR_TEMP) != wxNOT_FOUND &&
245 m_Altitude == 0,
246 vertical, wn);
247 AddTrackingControl(m_cbSeaTemperature, m_tcSeaTemperature, nullptr, nullptr,
248 m_gparent.m_pTimelineSet &&
249 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
250 Idx_SEA_TEMP) != wxNOT_FOUND &&
251 m_Altitude == 0,
252 vertical, wn);
253 AddTrackingControl(m_cbCAPE, m_tcCAPE, nullptr, nullptr,
254 m_gparent.m_pTimelineSet &&
255 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
256 Idx_CAPE) != wxNOT_FOUND &&
257 m_Altitude == 0,
258 vertical, wn);
259 AddTrackingControl(m_cbReflC, m_tcReflC, nullptr, nullptr,
260 m_gparent.m_pTimelineSet &&
261 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
262 Idx_COMP_REFL) != wxNOT_FOUND &&
263 m_Altitude == 0,
264 vertical, wn);
265 //
266 // init and show extra parameters for altitude tracking if necessary
267 AddTrackingControl(m_cbAltitude, m_tcAltitude, nullptr, nullptr,
268 m_gparent.m_pTimelineSet &&
269 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
270 Idx_GEOP_HGT + m_Altitude) != wxNOT_FOUND &&
271 m_Altitude != 0,
272 vertical, wn);
273 AddTrackingControl(m_cbTemp, m_tcTemp, nullptr, nullptr,
274 m_gparent.m_pTimelineSet &&
275 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
276 Idx_AIR_TEMP + m_Altitude) != wxNOT_FOUND &&
277 m_Altitude != 0,
278 vertical, wn);
279 AddTrackingControl(m_cbRelHumid, m_tcRelHumid, nullptr, nullptr,
280 m_gparent.m_pTimelineSet &&
281 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
282 Idx_HUMID_RE + m_Altitude) != wxNOT_FOUND &&
283 m_Altitude != 0,
284 vertical, wn);
285 //
286 m_stTrackingText->SetLabel(_("Data at cursor position"));
287 // add tooltips
288 wxString t;
289 double lev;
290
291 lev = m_gparent.m_OverlaySettings.CalibrateValue(
292 GribOverlaySettings::GEO_ALTITUDE,
293 10); // convert 10m in current altitude unit
294 t.Printf(m_Altitude ? m_gparent.m_OverlaySettings
295 .GetAltitudeFromIndex(
296 m_Altitude,
297 m_gparent.m_OverlaySettings
298 .Settings[GribOverlaySettings::PRESSURE]
299 .m_Units)
300 .Append(" ")
301 .Append(m_gparent.m_OverlaySettings.GetUnitSymbol(
302 GribOverlaySettings::PRESSURE))
303 : wxString::Format("%1.*f ", lev == (int)lev ? 0 : 1, lev)
304 .Append(m_gparent.m_OverlaySettings.GetUnitSymbol(
305 GribOverlaySettings::GEO_ALTITUDE)));
306 wxString pre = " ";
307 if (m_Altitude) {
308 pre.Append(_("at Geopotential Height"));
309 pre.Append(" ");
310 m_tcAltitude->SetToolTip(_("Altitude") + t);
311 m_tcTemp->SetToolTip(_("Temperature") + t);
312 m_tcRelHumid->SetToolTip(_("Relative Humidity") + t);
313 } else {
314 pre.Append(_("at"));
315 pre.Append(" ");
316 }
317 t.Prepend(pre);
318
319 m_tcWindSpeed->SetToolTip(_("Wind Speed") + t);
320 m_tcWindSpeedBf->SetToolTip(_("Wind Speed in Bf") + t);
321 m_tcWindDirection->SetToolTip(_("Wind Direction") + t);
322
323 t.Printf(" %1.*f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
324 GribOverlaySettings::GEO_ALTITUDE),
325 lev == (int)lev ? 0 : 1, lev);
326 m_tcWindGust->SetToolTip(_("Wind Gust at") + t);
327
328 if (m_gparent.m_pTimelineSet) {
329 wxString s[] = {" ", _("Air Temperature at"), _("surface level"),
330 _("Sea Surface Temperature")};
331
332 lev = m_gparent.m_OverlaySettings.CalibrateValue(
333 GribOverlaySettings::GEO_ALTITUDE,
334 2); // convert 2m in current altitude unit
335 t.Printf(m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(
336 1000 + NORWAY_METNO) != wxNOT_FOUND
337 ? s[0] + s[2]
338 : " %1.*f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
339 GribOverlaySettings::GEO_ALTITUDE),
340 lev == (int)lev ? 0 : 1, lev);
341 m_tcAirTemperature->SetToolTip(s[1] + t);
342
343 m_tcSeaTemperature->SetToolTip(
344 m_gparent.m_bGRIBActiveFile->m_GribIdxArray.Index(1000 + NOAA_GFS) !=
345 wxNOT_FOUND
346 ? s[1] + s[0] + s[2]
347 : s[3]);
348 }
349 dummy->Show(false);
350}
351
352void CursorData::UpdateTrackingControls() {
353 if (!m_gparent.m_pTimelineSet) return;
354
355 GribRecord **RecordArray = m_gparent.m_pTimelineSet->m_GribRecordPtrArray;
356 // Update the wind control
357 double vkn, ang;
359 vkn, ang, RecordArray[Idx_WIND_VX + m_Altitude],
360 RecordArray[Idx_WIND_VY + m_Altitude], m_cursor_lon, m_cursor_lat)) {
361 double vk = m_gparent.m_OverlaySettings.CalibrateValue(
362 GribOverlaySettings::WIND, vkn);
363
364 m_tcWindSpeed->SetValue(
365 wxString::Format("%3d " + m_gparent.m_OverlaySettings.GetUnitSymbol(
366 GribOverlaySettings::WIND),
367 (int)round(vk)));
368
369 // wind is a special case: if current unit is not bf ==> double speed
370 // display (current unit + bf)
371 if (m_gparent.m_OverlaySettings.Settings[GribOverlaySettings::WIND]
372 .m_Units != GribOverlaySettings::BFS) {
373 vk = m_gparent.m_OverlaySettings.GetmstobfFactor(vkn) * vkn;
374 if (m_DialogStyle == SEPARATED_VERTICAL)
375 m_tcWindSpeedBf->SetValue(wxString::Format("%2d bf", (int)round(vk)));
376 else
377 m_tcWindSpeed->SetValue(m_tcWindSpeed->GetValue().Append(" - ").Append(
378 wxString::Format("%2d bf", (int)round(vk))));
379 }
380
381 m_tcWindDirection->SetValue(wxString::Format("%03d%c", (int)(ang), 0x00B0));
382 } else {
383 m_tcWindSpeed->SetValue(_("N/A"));
384 m_tcWindSpeedBf->SetValue(_("N/A"));
385 m_tcWindDirection->SetValue(_("N/A"));
386 }
387
388 // Update the Wind gusts control
389 if (RecordArray[Idx_WIND_GUST]) {
390 double vkn_ = RecordArray[Idx_WIND_GUST]->GetInterpolatedValue(
391 m_cursor_lon, m_cursor_lat, true);
392
393 if (vkn_ != GRIB_NOTDEF) {
394 vkn_ = m_gparent.m_OverlaySettings.CalibrateValue(
395 GribOverlaySettings::WIND_GUST, vkn_);
396 m_tcWindGust->SetValue(
397 wxString::Format("%2d " + m_gparent.m_OverlaySettings.GetUnitSymbol(
398 GribOverlaySettings::WIND_GUST),
399 (int)round(vkn_)));
400 } else
401 m_tcWindGust->SetValue(_("N/A"));
402 }
403
404 // Update the Pressure control
405 if (RecordArray[Idx_PRESSURE]) {
406 double press = RecordArray[Idx_PRESSURE]->GetInterpolatedValue(
407 m_cursor_lon, m_cursor_lat, true);
408
409 if (press != GRIB_NOTDEF) {
410 press = m_gparent.m_OverlaySettings.CalibrateValue(
411 GribOverlaySettings::PRESSURE, press);
412 int p =
413 (m_gparent.m_OverlaySettings.Settings[GribOverlaySettings::PRESSURE]
414 .m_Units == 2)
415 ? 2
416 : 1; // if PRESSURE & inHG = two decimals
417 m_tcPressure->SetValue(
418 wxString::Format("%2.*f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
419 GribOverlaySettings::PRESSURE),
420 p, (press)));
421 } else
422 m_tcPressure->SetValue(_("N/A"));
423 }
424
425 // Update the Sig Wave Height
426 if (RecordArray[Idx_HTSIGW]) {
427 double height = RecordArray[Idx_HTSIGW]->GetInterpolatedValue(
428 m_cursor_lon, m_cursor_lat, true);
429
430 if (height != GRIB_NOTDEF) {
431 height = m_gparent.m_OverlaySettings.CalibrateValue(
432 GribOverlaySettings::WAVE, height);
433 wxString w(
434 wxString::Format("%4.1f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
435 GribOverlaySettings::WAVE),
436 height));
437 if (RecordArray[Idx_WVPER]) {
438 double period = RecordArray[Idx_WVPER]->GetInterpolatedValue(
439 m_cursor_lon, m_cursor_lat, true);
440 if (period != GRIB_NOTDEF) {
441 if (m_DialogStyle == SEPARATED_VERTICAL)
442 m_tcWavePeriode->SetValue(
443 wxString::Format("%01ds", (int)round(period)));
444 else
445 w.Append(wxString::Format(" - %01ds", (int)round(period)));
446 } else
447 m_tcWavePeriode->SetValue(_("N/A"));
448 } else
449 m_tcWavePeriode->SetValue(_("N/A"));
450
451 m_tcWaveHeight->SetValue(w);
452 } else
453 m_tcWaveHeight->SetValue(_("N/A"));
454 }
455
456 // Update the Wave direction
457 if (RecordArray[Idx_WVDIR]) {
458 double direction = RecordArray[Idx_WVDIR]->GetInterpolatedValue(
459 m_cursor_lon, m_cursor_lat, true, true);
460 if (direction != GRIB_NOTDEF)
461 m_tcWaveDirection->SetValue(
462 wxString::Format("%03d%c", (int)direction, 0x00B0));
463 else
464 m_tcWaveDirection->SetValue(_("N/A"));
465 }
466
467 // Update the Current control
469 vkn, ang, RecordArray[Idx_SEACURRENT_VX],
470 RecordArray[Idx_SEACURRENT_VY], m_cursor_lon, m_cursor_lat)) {
471 // Current direction is generally reported as the "flow" direction,
472 // which is opposite from wind convention.
473 // So, adjust.
474 ang += 180;
475 if (ang >= 360) ang -= 360;
476 if (ang < 0) ang += 360;
477
478 vkn = m_gparent.m_OverlaySettings.CalibrateValue(
479 GribOverlaySettings::CURRENT, vkn);
480
481 m_tcCurrentVelocity->SetValue(
482 wxString::Format("%4.1f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
483 GribOverlaySettings::CURRENT),
484 vkn));
485
486 m_tcCurrentDirection->SetValue(
487 wxString::Format("%03d%c", (int)(ang), 0x00B0));
488 } else {
489 m_tcCurrentVelocity->SetValue(_("N/A"));
490 m_tcCurrentDirection->SetValue(_("N/A"));
491 }
492
493 // Update total rainfall control
494 if (RecordArray[Idx_PRECIP_TOT]) {
495 double precip = RecordArray[Idx_PRECIP_TOT]->GetInterpolatedValue(
496 m_cursor_lon, m_cursor_lat, true);
497
498 if (precip != GRIB_NOTDEF) {
499 precip = m_gparent.m_OverlaySettings.CalibrateValue(
500 GribOverlaySettings::PRECIPITATION, precip);
501 int p = precip < 10. ? 2 : precip < 100. ? 1 : 0;
502 p += m_gparent.m_OverlaySettings
503 .Settings[GribOverlaySettings::PRECIPITATION]
504 .m_Units == 1
505 ? 1
506 : 0; // if PRESSURE & in = one decimal more
507 m_tcPrecipitation->SetValue(
508 wxString::Format("%4.*f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
509 GribOverlaySettings::PRECIPITATION),
510 p, precip));
511 } else
512 m_tcPrecipitation->SetValue(_("N/A"));
513 }
514
515 // Update total cloud control
516 if (RecordArray[Idx_CLOUD_TOT]) {
517 double cloud = RecordArray[Idx_CLOUD_TOT]->GetInterpolatedValue(
518 m_cursor_lon, m_cursor_lat, true);
519
520 if (cloud != GRIB_NOTDEF) {
521 cloud = m_gparent.m_OverlaySettings.CalibrateValue(
522 GribOverlaySettings::CLOUD, cloud);
523 wxString val(wxString::Format("%5.0f ", cloud));
524 m_tcCloud->SetValue(val + m_gparent.m_OverlaySettings.GetUnitSymbol(
525 GribOverlaySettings::CLOUD));
526 } else
527 m_tcCloud->SetValue(_("N/A"));
528 }
529
530 // Update the Air Temperature
531 if (RecordArray[Idx_AIR_TEMP]) {
532 double temp = RecordArray[Idx_AIR_TEMP]->GetInterpolatedValue(
533 m_cursor_lon, m_cursor_lat, true);
534
535 if (temp != GRIB_NOTDEF) {
536 temp = m_gparent.m_OverlaySettings.CalibrateValue(
537 GribOverlaySettings::AIR_TEMPERATURE, temp);
538 m_tcAirTemperature->SetValue(
539 wxString::Format("%5.1f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
540 GribOverlaySettings::AIR_TEMPERATURE),
541 temp));
542 } else
543 m_tcAirTemperature->SetValue(_("N/A"));
544 }
545
546 // Update the Sea Surface Temperature
547 if (RecordArray[Idx_SEA_TEMP]) {
548 double temp = RecordArray[Idx_SEA_TEMP]->GetInterpolatedValue(
549 m_cursor_lon, m_cursor_lat, true);
550
551 if (temp != GRIB_NOTDEF) {
552 temp = m_gparent.m_OverlaySettings.CalibrateValue(
553 GribOverlaySettings::SEA_TEMPERATURE, temp);
554 m_tcSeaTemperature->SetValue(
555 wxString::Format("%5.1f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
556 GribOverlaySettings::SEA_TEMPERATURE),
557 temp));
558 } else
559 m_tcSeaTemperature->SetValue(_("N/A"));
560 }
561
562 // Update the Convective Available Potential Energy (CAPE)
563 if (RecordArray[Idx_CAPE]) {
564 double cape = RecordArray[Idx_CAPE]->GetInterpolatedValue(
565 m_cursor_lon, m_cursor_lat, true);
566
567 if (cape != GRIB_NOTDEF) {
568 cape = m_gparent.m_OverlaySettings.CalibrateValue(
569 GribOverlaySettings::CAPE, cape);
570 m_tcCAPE->SetValue(
571 wxString::Format("%5.0f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
572 GribOverlaySettings::CAPE),
573 cape));
574 } else
575 m_tcCAPE->SetValue(_("N/A"));
576 }
577 if (RecordArray[Idx_COMP_REFL]) {
578 double c_refl = RecordArray[Idx_COMP_REFL]->GetInterpolatedValue(
579 m_cursor_lon, m_cursor_lat, true);
580
581 if (c_refl != GRIB_NOTDEF) {
582 c_refl = m_gparent.m_OverlaySettings.CalibrateValue(
583 GribOverlaySettings::COMP_REFL, c_refl);
584 m_tcReflC->SetValue(
585 wxString::Format("%5.0f " + m_gparent.m_OverlaySettings.GetUnitSymbol(
586 GribOverlaySettings::COMP_REFL),
587 c_refl));
588 } else
589 m_tcReflC->SetValue(_("N/A"));
590 }
591 // Update extra data for altitude
592 // geopotential altitude
593 if (RecordArray[Idx_GEOP_HGT + m_Altitude]) {
594 double geop = RecordArray[Idx_GEOP_HGT + m_Altitude]->GetInterpolatedValue(
595 m_cursor_lon, m_cursor_lat, true);
596
597 if (geop != GRIB_NOTDEF) {
598 geop = m_gparent.m_OverlaySettings.CalibrateValue(
599 GribOverlaySettings::GEO_ALTITUDE, geop);
600 m_tcAltitude->SetValue(wxString::Format("%5.0f ", geop) +
601 m_gparent.m_OverlaySettings.GetUnitSymbol(
602 GribOverlaySettings::GEO_ALTITUDE));
603 } else
604 m_tcAltitude->SetValue(_("N/A"));
605 }
606
607 // temperature
608 if (RecordArray[Idx_AIR_TEMP + m_Altitude]) {
609 double temp = RecordArray[Idx_AIR_TEMP + m_Altitude]->GetInterpolatedValue(
610 m_cursor_lon, m_cursor_lat, true);
611
612 if (temp != GRIB_NOTDEF) {
613 temp = m_gparent.m_OverlaySettings.CalibrateValue(
614 GribOverlaySettings::AIR_TEMPERATURE, temp);
615 m_tcTemp->SetValue(wxString::Format("%5.1f ", temp) +
616 m_gparent.m_OverlaySettings.GetUnitSymbol(
617 GribOverlaySettings::AIR_TEMPERATURE));
618 } else
619 m_tcTemp->SetValue(_("N/A"));
620 }
621 // relative humidity
622 if (RecordArray[Idx_HUMID_RE + m_Altitude]) {
623 double humi = RecordArray[Idx_HUMID_RE + m_Altitude]->GetInterpolatedValue(
624 m_cursor_lon, m_cursor_lat, true);
625
626 if (humi != GRIB_NOTDEF) {
627 humi = m_gparent.m_OverlaySettings.CalibrateValue(
628 GribOverlaySettings::REL_HUMIDITY, humi);
629 m_tcRelHumid->SetValue(wxString::Format("%5.0f ", humi) +
630 m_gparent.m_OverlaySettings.GetUnitSymbol(
631 GribOverlaySettings::REL_HUMIDITY));
632 } else
633 m_tcRelHumid->SetValue(_("N/A"));
634 }
635}
636
637void CursorData::OnMenuCallBack(wxMouseEvent &event) {
638 // populate menu
639 auto *menu = new wxMenu();
640 int id = event.GetId();
641 switch (id) {
642 case GribOverlaySettings::WIND:
643 MenuAppend(menu, B_ARROWS, _("Barbed Arrows"), id);
644 MenuAppend(menu, ISO_LINE, _("Display Isotachs"), id);
645 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
646 MenuAppend(menu, NUMBERS, _("Numbers"), id);
647 MenuAppend(menu, PARTICLES, _("Particle Map"), id);
648 break;
649 case GribOverlaySettings::WIND_GUST:
650 MenuAppend(menu, ISO_LINE, _("Display Isotachs"), id);
651 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
652 MenuAppend(menu, NUMBERS, _("Numbers"), id);
653 break;
654 case GribOverlaySettings::PRESSURE:
655 MenuAppend(menu, ISO_LINE, _("Display Isobars"), id);
656 MenuAppend(menu, ISO_ABBR, _("Abbreviated Isobars Numbers"), id);
657 MenuAppend(menu, NUMBERS, _("Numbers"), id);
658 break;
659 case GribOverlaySettings::AIR_TEMPERATURE:
660 case GribOverlaySettings::SEA_TEMPERATURE:
661 MenuAppend(menu, ISO_LINE, _("Display Isotherms"), id);
662 // fall through
663 case GribOverlaySettings::CLOUD:
664 case GribOverlaySettings::PRECIPITATION:
665 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
666 MenuAppend(menu, NUMBERS, _("Numbers"), id);
667 break;
668 case GribOverlaySettings::CAPE:
669 MenuAppend(menu, ISO_LINE, _("Display Iso CAPE"), id);
670 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
671 MenuAppend(menu, NUMBERS, _("Numbers"), id);
672 break;
673 case GribOverlaySettings::COMP_REFL:
674 MenuAppend(menu, ISO_LINE, _("Display Iso Reflectivity"), id);
675 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
676 MenuAppend(menu, NUMBERS, _("Numbers"), id);
677 break;
678 case GribOverlaySettings::WAVE:
679 MenuAppend(menu, D_ARROWS, _("Direction Arrows"), id);
680 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
681 MenuAppend(menu, NUMBERS, _("Numbers"), id);
682 break;
683 case GribOverlaySettings::CURRENT:
684 MenuAppend(menu, D_ARROWS, _("Direction Arrows"), id);
685 MenuAppend(menu, OVERLAY, _("OverlayMap"), id);
686 MenuAppend(menu, NUMBERS, _("Numbers"), id);
687 MenuAppend(menu, PARTICLES, _("Particle Map"), id);
688 }
689
690 PopupMenu(menu);
691
692 // apply new config
693 wxwxMenuItemListNode *node = menu->GetMenuItems().GetFirst();
694 while (node) {
695 wxMenuItem *it = node->GetData();
696 switch (it->GetId()) {
697 case B_ARROWS:
698 m_gparent.m_OverlaySettings.Settings[id].m_bBarbedArrows =
699 it->IsChecked();
700 break;
701 case ISO_LINE:
702 m_gparent.m_OverlaySettings.Settings[id].m_bIsoBars = it->IsChecked();
703 break;
704 case ISO_ABBR:
705 m_gparent.m_OverlaySettings.Settings[id].m_bAbbrIsoBarsNumbers =
706 it->IsChecked();
707 break;
708 case D_ARROWS:
709 m_gparent.m_OverlaySettings.Settings[id].m_bDirectionArrows =
710 it->IsChecked();
711 break;
712 case OVERLAY:
713 m_gparent.m_OverlaySettings.Settings[id].m_bOverlayMap =
714 it->IsChecked();
715 break;
716 case NUMBERS:
717 m_gparent.m_OverlaySettings.Settings[id].m_bNumbers = it->IsChecked();
718 break;
719 case PARTICLES:
720 m_gparent.m_OverlaySettings.Settings[id].m_bParticles = it->IsChecked();
721 default:
722 assert(false && "Illegal menu item");
723 }
724 node = node->GetNext();
725 }
726
727 // if the current parameter type is selected then resolve display conflicts
728 if (m_gparent.InDataPlot(id) && m_gparent.m_bDataPlot[id])
729 ResolveDisplayConflicts(id);
730
731 // save new config
732 m_gparent.m_OverlaySettings.Write();
733
734 delete menu;
735}
736
737void CursorData::MenuAppend(wxMenu *menu, int id, const wxString &label,
738 int setting_) {
739 auto *item = new wxMenuItem(menu, id, label, "", wxITEM_CHECK);
740
741#ifdef __WXMSW__
742 wxFont *qFont = OCPNGetFont(_("Menu"));
743 item->SetFont(*qFont);
744#endif
745
746 menu->Append(item);
747
748 bool check;
749 if (id == B_ARROWS)
750 check = m_gparent.m_OverlaySettings.Settings[setting_].m_bBarbedArrows;
751 else if (id == ISO_LINE)
752 check = m_gparent.m_OverlaySettings.Settings[setting_].m_bIsoBars;
753 else if (id == ISO_ABBR)
754 check =
755 m_gparent.m_OverlaySettings.Settings[setting_].m_bAbbrIsoBarsNumbers;
756 else if (id == D_ARROWS)
757 check = m_gparent.m_OverlaySettings.Settings[setting_].m_bDirectionArrows;
758 else if (id == OVERLAY)
759 check = m_gparent.m_OverlaySettings.Settings[setting_].m_bOverlayMap;
760 else if (id == NUMBERS)
761 check = m_gparent.m_OverlaySettings.Settings[setting_].m_bNumbers;
762 else if (id == PARTICLES)
763 check = m_gparent.m_OverlaySettings.Settings[setting_].m_bParticles;
764 else
765 check = false;
766 item->Check(check);
767}
768
769void CursorData::OnMouseEvent(wxMouseEvent &event) {
770 if (event.RightDown()) {
771 if (m_DialogStyle >> 1 == ATTACHED) {
772 wxMouseEvent evt(event);
773 m_gparent.DoOnMouseEvent(evt);
774 }
775 return;
776 }
777
778 static wxPoint s_gspt;
779 int x, y;
780
781 event.GetPosition(&x, &y);
782 wxPoint spt = wxPoint(x, y);
783 if (event.GetId() != 1000)
784 spt = ClientToScreen(spt);
785 else
786 spt = GetParent()->ClientToScreen(spt);
787
788#ifdef __WXOSX__
789 if (!m_bLeftDown && event.LeftIsDown()) {
790 m_bLeftDown = true;
791 s_gspt = spt;
792 if (!HasCapture()) CaptureMouse();
793 } else if (m_bLeftDown && !event.LeftIsDown()) {
794 // GetParent()->Move( GetParent()->GetPosition() );
795 m_bLeftDown = false;
796 if (HasCapture()) ReleaseMouse();
797 }
798#else
799
800 if (event.LeftDown()) {
801 s_gspt = spt;
802 if (!HasCapture()) CaptureMouse();
803 }
804
805 if (event.LeftUp()) {
806 // GetParent()->Move( GetParent()->GetPosition() );
807 if (HasCapture()) ReleaseMouse();
808 }
809#endif
810
811 if (event.Dragging()) {
812 wxPoint par_pos_old = GetParent()->GetPosition();
813
814 wxPoint par_pos = par_pos_old;
815 par_pos.x += spt.x - s_gspt.x;
816 par_pos.y += spt.y - s_gspt.y;
817
818 wxPoint pos_in_parent = GetOCPNCanvasWindow()->ScreenToClient(par_pos);
819 wxPoint pos_in_parent_old =
820 GetOCPNCanvasWindow()->ScreenToClient(par_pos_old);
821
822 // X
823 if (pos_in_parent.x < pos_in_parent_old.x) { // moving left
824 if (pos_in_parent.x < 10) {
825 pos_in_parent.x = 0;
826 }
827 } else if (pos_in_parent.x > pos_in_parent_old.x) { // moving right
828 int max_right =
829 GetOCPNCanvasWindow()->GetClientSize().x - GetParent()->GetSize().x;
830 if (pos_in_parent.x > (max_right - 10)) {
831 pos_in_parent.x = max_right;
832 }
833 }
834
835 // Y
836 if (pos_in_parent.y < pos_in_parent_old.y) { // moving up
837 if (pos_in_parent.y < 10) {
838 pos_in_parent.y = 0;
839 }
840 } else if (pos_in_parent.y > pos_in_parent_old.y) { // moving dow
841 int max_down =
842 GetOCPNCanvasWindow()->GetClientSize().y - GetParent()->GetSize().y;
843 if (pos_in_parent.y > (max_down - 10)) {
844 pos_in_parent.y = max_down;
845 }
846 }
847
848 wxPoint final_pos = GetOCPNCanvasWindow()->ClientToScreen(pos_in_parent);
849
850 GetParent()->Move(final_pos);
851
852 s_gspt = spt;
853 }
854}
Class CursorDataBase.
GribOverlaySettings m_OverlaySettings
Settings that control how GRIB data is displayed and overlaid.
GRIBFile * m_bGRIBActiveFile
Currently active GRIB file being displayed.
GribTimelineRecordSet * m_pTimelineSet
Current set of GRIB records for timeline playback.
GribRecord * m_GribRecordPtrArray[Idx_COUNT]
Array of pointers to GRIB records representing different meteorological parameters.
A meteorological data grid from a GRIB (Gridded Binary) file.
static bool GetInterpolatedValues(double &M, double &A, const GribRecord *GRX, const GribRecord *GRY, double px, double py, bool numericalInterpolation=true)
Gets spatially interpolated wind or current vector values at a specific latitude/longitude point.
double GetInterpolatedValue(double px, double py, bool numericalInterpolation=true, bool dir=false) const
Get spatially interpolated value at exact lat/lon position.
GRIB Weather Data Plugin for OpenCPN.
@ Idx_COMP_REFL
Composite radar reflectivity in dBZ (decibel relative to Z)
@ Idx_PRECIP_TOT
Precipitation data in millimeters per hour.
@ Idx_AIR_TEMP
Air temperature at 2m in Kelvin (K)
@ Idx_PRESSURE
Surface pressure in Pascal (Pa)
@ Idx_WVDIR
Wave direction.
@ Idx_CLOUD_TOT
Total cloud cover in % (percent, range 0-100%)
@ Idx_WIND_GUST
Wind gust speed at surface in m/s.
@ Idx_WIND_VX
Surface wind velocity X component in m/s.
@ Idx_WVPER
Wave period.
@ Idx_HTSIGW
Significant wave height in meters.
@ Idx_HUMID_RE
Surface relative humidity in % (percent, range 0-100%)
@ Idx_SEACURRENT_VY
Sea current velocity Y component in m/s.
@ Idx_GEOP_HGT
Surface geopotential height in gpm (geopotential meters)
@ Idx_SEA_TEMP
Sea surface temperature in Kelvin (K)
@ Idx_WIND_VY
Surface wind velocity Y component in m/s.
@ Idx_SEACURRENT_VX
Sea current velocity X component in m/s.
@ Idx_CAPE
Convective Available Potential Energy in J/kg (Joules per kilogram)
wxWindow * GetOCPNCanvasWindow()
Gets OpenCPN's main canvas window.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
void DimeWindow(wxWindow *win)
Applies system color scheme to window.
OpenGL Platform Abstraction Layer.