OpenCPN Partial API docs
Loading...
Searching...
No Matches
GribUIDialog.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, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
23#include "wx/wx.h"
24#include "wx/tokenzr.h"
25#include "wx/datetime.h"
26#include "wx/sound.h"
27#include <wx/wfstream.h>
28#include <wx/dir.h>
29#include <wx/filename.h>
30#include <wx/debug.h>
31#include <wx/graphics.h>
32#include <wx/regex.h>
33
34#include <wx/stdpaths.h>
35
36#include <stdlib.h>
37#include <math.h>
38#include <time.h>
39
40#include "pi_gl.h"
41#include "grib_pi.h"
42#include "GribTable.h"
43#include "email.h"
44#include "folder.xpm"
45#include "GribUIDialog.h"
46#include <wx/arrimpl.cpp>
47
48#ifdef __ANDROID__
49#include "android_jvm.h"
50#endif
51
52// general variables
53double m_cursor_lat, m_cursor_lon;
54int m_Altitude;
55int m_DialogStyle;
56int m_SavedZoneSelMode;
57int m_ZoneSelMode;
58
59extern grib_pi *g_pi;
60
61#ifdef __MSVC__
62#if _MSC_VER < 1700
63int round(double x) {
64 int i = (int)x;
65 if (x >= 0.0) {
66 return ((x - i) >= 0.5) ? (i + 1) : (i);
67 } else {
68 return (-x + i >= 0.5) ? (i - 1) : (i);
69 }
70}
71#endif
72#endif
73
74#ifdef __WXQT__
75#include "qdebug.h"
76#endif
77
78#if wxCHECK_VERSION(2, 9, 4) /* to work with wx 2.8 */
79#define SetBitmapLabelLabel SetBitmap
80#endif
81
82#define DEFAULT_STYLE \
83 = wxCAPTION | wxCLOSE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL
84
85WX_DEFINE_OBJARRAY(ArrayOfGribRecordSets);
86
87// Sort compare function for File Modification Time
88static int CompareFileStringTime(const wxString &first,
89 const wxString &second) {
90 wxFileName f(first);
91 wxFileName s(second);
92 wxTimeSpan sp = s.GetModificationTime() - f.GetModificationTime();
93 return sp.GetMinutes();
94
95 // return ::wxFileModificationTime(first) -
96 // ::wxFileModificationTime(second);
97}
98
99// date/time in the desired time zone format
100static wxString TToString(const wxDateTime date_time, const int time_zone) {
101 wxDateTime t(date_time);
102 switch (time_zone) {
103 case 0:
104 if ((wxDateTime::Now() == (wxDateTime::Now().ToGMT())) &&
105 t.IsDST()) // bug in wxWingets 3.0 for UTC meridien ?
106 t.Add(wxTimeSpan(1, 0, 0, 0));
107 return t.Format(_T(" %a %d-%b-%Y %H:%M "), wxDateTime::Local) +
108 _T("LOC");
109 case 1:
110 default:
111 return t.Format(_T(" %a %d-%b-%Y %H:%M "), wxDateTime::UTC) + _T("UTC");
112 }
113}
114
115wxWindow *GetGRIBCanvas() {
116 wxWindow *wx;
117 // If multicanvas are active, render the overlay on the right canvas only
118 if (GetCanvasCount() > 1) // multi?
119 wx = GetCanvasByIndex(1);
120 else
121 wx = GetOCPNCanvasWindow();
122 return wx;
123}
124
125//---------------------------------------------------------------------------------------
126// GRIB Control Implementation
127//---------------------------------------------------------------------------------------
128/* interpolating constructor
129 as a possible optimization, write this function to also
130 take latitude longitude boundaries so the resulting record can be
131 a subset of the input, but also would need to be recomputed when panning the
132 screen */
134 : GribRecordSet(cnt) {
135 for (int i = 0; i < Idx_COUNT; i++) m_IsobarArray[i] = nullptr;
136}
137
138GribTimelineRecordSet::~GribTimelineRecordSet() {
139 // RemoveGribRecords();
140 ClearCachedData();
141}
142
143void GribTimelineRecordSet::ClearCachedData() {
144 for (int i = 0; i < Idx_COUNT; i++) {
145 if (m_IsobarArray[i]) {
146 // Clear out the cached isobars
147 for (unsigned int j = 0; j < m_IsobarArray[i]->GetCount(); j++) {
148 IsoLine *piso = (IsoLine *)m_IsobarArray[i]->Item(j);
149 delete piso;
150 }
151 delete m_IsobarArray[i];
152 m_IsobarArray[i] = nullptr;
153 }
154 }
155}
156
157//---------------------------------------------------------------------------------------
158// GRIB CtrlBar Implementation
159//---------------------------------------------------------------------------------------
160
161GRIBUICtrlBar::GRIBUICtrlBar(wxWindow *parent, wxWindowID id,
162 const wxString &title, const wxPoint &pos,
163 const wxSize &size, long style, grib_pi *ppi)
164 : GRIBUICtrlBarBase(parent, id, title, pos, size, style) {
165 pParent = parent;
166 pPlugIn = ppi;
167 // Preinitialize the vierwport with an existing value, see
168 // https://github.com/OpenCPN/OpenCPN/pull/4002/files
169 m_vp = new PlugIn_ViewPort(pPlugIn->GetCurrentViewPort());
170 pReq_Dialog = nullptr;
171 m_bGRIBActiveFile = nullptr;
172 m_pTimelineSet = nullptr;
173 m_gCursorData = nullptr;
174 m_gGRIBUICData = nullptr;
175 m_gtk_started = false;
176
177 wxFileConfig *pConf = GetOCPNConfigObject();
178
179 m_gGrabber = new GribGrabberWin(this); // add the grabber to the dialog
180 m_fgCtrlGrabberSize->Add(m_gGrabber, 0, wxALL, 0);
181
182 this->SetSizer(m_fgCtrlBarSizer);
183 this->Layout();
184 m_fgCtrlBarSizer->Fit(this);
185
186 if (pConf) {
187 pConf->SetPath(_T ( "/Settings/GRIB" ));
188 pConf->Read(_T ( "WindPlot" ), &m_bDataPlot[GribOverlaySettings::WIND],
189 true);
190 pConf->Read(_T ( "WindGustPlot" ),
191 &m_bDataPlot[GribOverlaySettings::WIND_GUST], false);
192 pConf->Read(_T ( "PressurePlot" ),
193 &m_bDataPlot[GribOverlaySettings::PRESSURE], false);
194 pConf->Read(_T ( "WavePlot" ), &m_bDataPlot[GribOverlaySettings::WAVE],
195 false);
196 pConf->Read(_T ( "CurrentPlot" ),
197 &m_bDataPlot[GribOverlaySettings::CURRENT], false);
198 pConf->Read(_T ( "PrecipitationPlot" ),
199 &m_bDataPlot[GribOverlaySettings::PRECIPITATION], false);
200 pConf->Read(_T ( "CloudPlot" ), &m_bDataPlot[GribOverlaySettings::CLOUD],
201 false);
202 pConf->Read(_T ( "AirTemperaturePlot" ),
203 &m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE], false);
204 pConf->Read(_T ( "SeaTemperaturePlot" ),
205 &m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE], false);
206 pConf->Read(_T ( "CAPEPlot" ), &m_bDataPlot[GribOverlaySettings::CAPE],
207 false);
208 pConf->Read(_T ( "CompReflectivityPlot" ),
209 &m_bDataPlot[GribOverlaySettings::COMP_REFL], false);
210
211 pConf->Read(_T ( "CursorDataShown" ), &m_CDataIsShown, true);
212
213 pConf->Read(_T ( "lastdatatype" ), &m_lastdatatype, 0);
214
215 pConf->SetPath(_T ( "/Settings/GRIB/FileNames" ));
216 m_file_names.Clear();
217 if (pConf->GetNumberOfEntries()) {
218 wxString str, val;
219 long dummy;
220 bool bCont = pConf->GetFirstEntry(str, dummy);
221 while (bCont) {
222 pConf->Read(str, &val); // Get a file name
223 m_file_names.Add(val);
224 bCont = pConf->GetNextEntry(str, dummy);
225 }
226 }
227
228 wxStandardPathsBase &spath = wxStandardPaths::Get();
229
230 pConf->SetPath(_T ( "/Directories" ));
231 pConf->Read(_T ( "GRIBDirectory" ), &m_grib_dir);
232
233 pConf->SetPath(_T( "/PlugIns/GRIB" ));
234 pConf->Read(_T( "ManualRequestZoneSizing" ), &m_SavedZoneSelMode, 0);
235
236 // Read XyGrib related configuration
237 pConf->SetPath(_T ( "/Settings/GRIB/XyGrib" ));
238 pConf->Read(_T( "AtmModelIndex" ), &xyGribConfig.atmModelIndex, 0);
239 pConf->Read(_T( "WaveModelIndex" ), &xyGribConfig.waveModelIndex, 0);
240 pConf->Read(_T( "ResolutionIndex" ), &xyGribConfig.resolutionIndex, 0);
241 pConf->Read(_T( "DurationIndex" ), &xyGribConfig.durationIndex, 0);
242 pConf->Read(_T( "RunIndex" ), &xyGribConfig.runIndex, 0);
243 pConf->Read(_T( "IntervalIndex" ), &xyGribConfig.intervalIndex, 0);
244 pConf->Read(_T( "Wind" ), &xyGribConfig.wind, true);
245 pConf->Read(_T( "Gust" ), &xyGribConfig.gust, true);
246 pConf->Read(_T( "Pressure" ), &xyGribConfig.pressure, false);
247 pConf->Read(_T( "Temperature" ), &xyGribConfig.temperature, true);
248 pConf->Read(_T( "Cape" ), &xyGribConfig.cape, false);
249 pConf->Read(_T( "Reflectivity" ), &xyGribConfig.reflectivity, false);
250 pConf->Read(_T( "CloudCover" ), &xyGribConfig.cloudCover, true);
251 pConf->Read(_T( "Precipitation" ), &xyGribConfig.precipitation, true);
252 pConf->Read(_T( "WaveHeight" ), &xyGribConfig.waveHeight, true);
253 pConf->Read(_T( "WindWaves" ), &xyGribConfig.windWaves, true);
254 }
255 // init zone selection parameters
256 m_ZoneSelMode = m_SavedZoneSelMode;
257
258 // connect Timer
259 m_tPlayStop.Connect(wxEVT_TIMER,
260 wxTimerEventHandler(GRIBUICtrlBar::OnPlayStopTimer),
261 nullptr, this);
262 // connect functions
263 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICtrlBar::OnMove));
264
265 m_OverlaySettings.Read();
266
267 DimeWindow(this);
268
269 Fit();
270 SetMinSize(GetBestSize());
271 if (m_ProjectBoatPanel) {
272 m_ProjectBoatPanel->SetSpeed(pPlugIn->m_boat_sog);
273 m_ProjectBoatPanel->SetCourse(pPlugIn->m_boat_cog);
274 }
275 m_highlight_latmax = 0;
276 m_highlight_lonmax = 0;
277 m_highlight_latmin = 0;
278 m_highlight_lonmin = 0;
279}
280
281GRIBUICtrlBar::~GRIBUICtrlBar() {
282 wxFileConfig *pConf = GetOCPNConfigObject();
283 ;
284
285 if (pConf) {
286 pConf->SetPath(_T ( "/Settings/GRIB" ));
287 pConf->Write(_T ( "WindPlot" ), m_bDataPlot[GribOverlaySettings::WIND]);
288 pConf->Write(_T ( "WindGustPlot" ),
289 m_bDataPlot[GribOverlaySettings::WIND_GUST]);
290 pConf->Write(_T ( "PressurePlot" ),
291 m_bDataPlot[GribOverlaySettings::PRESSURE]);
292 pConf->Write(_T ( "WavePlot" ), m_bDataPlot[GribOverlaySettings::WAVE]);
293 pConf->Write(_T ( "CurrentPlot" ),
294 m_bDataPlot[GribOverlaySettings::CURRENT]);
295 pConf->Write(_T ( "PrecipitationPlot" ),
296 m_bDataPlot[GribOverlaySettings::PRECIPITATION]);
297 pConf->Write(_T ( "CloudPlot" ), m_bDataPlot[GribOverlaySettings::CLOUD]);
298 pConf->Write(_T ( "AirTemperaturePlot" ),
299 m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE]);
300 pConf->Write(_T ( "SeaTemperaturePlot" ),
301 m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE]);
302 pConf->Write(_T ( "CAPEPlot" ), m_bDataPlot[GribOverlaySettings::CAPE]);
303 pConf->Write(_T ( "CompReflectivityPlot" ),
304 m_bDataPlot[GribOverlaySettings::COMP_REFL]);
305
306 pConf->Write(_T ( "CursorDataShown" ), m_CDataIsShown);
307
308 pConf->Write(_T ( "lastdatatype" ), m_lastdatatype);
309
310 pConf->SetPath(_T ( "/Settings/GRIB/FileNames" ));
311 int iFileMax = pConf->GetNumberOfEntries();
312 if (iFileMax) {
313 wxString key;
314 long dummy;
315 for (int i = 0; i < iFileMax; i++) {
316 if (pConf->GetFirstEntry(key, dummy)) pConf->DeleteEntry(key, false);
317 }
318 }
319
320 for (unsigned int i = 0; i < m_file_names.GetCount(); i++) {
321 wxString key;
322 key.Printf(_T("Filename%d"), i);
323 pConf->Write(key, m_file_names[i]);
324 }
325
326 pConf->SetPath(_T ( "/Directories" ));
327 pConf->Write(_T ( "GRIBDirectory" ), m_grib_dir);
328
329 // Write current XyGrib panel configuration to configuration file
330 pConf->SetPath(_T ( "/Settings/GRIB/XyGrib" ));
331 pConf->Write(_T( "AtmModelIndex" ), xyGribConfig.atmModelIndex);
332 pConf->Write(_T( "WaveModelIndex" ), xyGribConfig.waveModelIndex);
333 pConf->Write(_T( "ResolutionIndex" ), xyGribConfig.resolutionIndex);
334 pConf->Write(_T( "DurationIndex" ), xyGribConfig.durationIndex);
335 pConf->Write(_T( "RunIndex" ), xyGribConfig.runIndex);
336 pConf->Write(_T( "IntervalIndex" ), xyGribConfig.intervalIndex);
337 pConf->Write(_T( "Wind" ), xyGribConfig.wind);
338 pConf->Write(_T( "Gust" ), xyGribConfig.gust);
339 pConf->Write(_T( "Pressure" ), xyGribConfig.pressure);
340 pConf->Write(_T( "Temperature" ), xyGribConfig.temperature);
341 pConf->Write(_T( "Cape" ), xyGribConfig.cape);
342 pConf->Write(_T( "Reflectivity" ), xyGribConfig.reflectivity);
343 pConf->Write(_T( "CloudCover" ), xyGribConfig.cloudCover);
344 pConf->Write(_T( "Precipitation" ), xyGribConfig.precipitation);
345 pConf->Write(_T( "WaveHeight" ), xyGribConfig.waveHeight);
346 pConf->Write(_T( "WindWaves" ), xyGribConfig.windWaves);
347 }
348 delete m_vp;
349 delete m_pTimelineSet;
350}
351
352wxBitmap GRIBUICtrlBar::GetScaledBitmap(wxBitmap bitmap,
353 const wxString svgFileName,
354 double scale_factor) {
355 int margin = 4; // there is a small margin around the bitmap drawn by the
356 // wxBitmapButton
357 int w = bitmap.GetWidth() - margin;
358 int h = bitmap.GetHeight() - margin;
359 w *= scale_factor;
360 h *= scale_factor;
361
362#ifdef ocpnUSE_SVG
363 wxString shareLocn = *GetpSharedDataLocation() + _T("plugins") +
364 wxFileName::GetPathSeparator() + _T("grib_pi") +
365 wxFileName::GetPathSeparator() + _T("data") +
366 wxFileName::GetPathSeparator();
367 wxString filename = shareLocn + svgFileName + _T(".svg");
368
369 wxBitmap svgbm = GetBitmapFromSVGFile(filename, w, h);
370 if (svgbm.GetWidth() > 0 && svgbm.GetHeight() > 0)
371 return svgbm;
372 else
373#endif // ocpnUSE_SVG
374 {
375 wxImage a = bitmap.ConvertToImage();
376 return wxBitmap(a.Scale(w, h), wxIMAGE_QUALITY_HIGH);
377 }
378}
379
380void GRIBUICtrlBar::SetScaledBitmap(double factor) {
381 // Round to the nearest "quarter", to avoid rendering artifacts
382 m_ScaledFactor = wxRound(factor * 4.0) / 4.0;
383 // set buttons bitmap
384 m_bpPrev->SetBitmapLabel(
385 GetScaledBitmap(wxBitmap(prev), _T("prev"), m_ScaledFactor));
386 m_bpNext->SetBitmapLabel(
387 GetScaledBitmap(wxBitmap(next), _T("next"), m_ScaledFactor));
388 m_bpAltitude->SetBitmapLabel(
389 GetScaledBitmap(wxBitmap(altitude), _T("altitude"), m_ScaledFactor));
390 m_bpNow->SetBitmapLabel(
391 GetScaledBitmap(wxBitmap(now), _T("now"), m_ScaledFactor));
392 m_bpZoomToCenter->SetBitmapLabel(
393 GetScaledBitmap(wxBitmap(zoomto), _T("zoomto"), m_ScaledFactor));
394 m_bpPlay->SetBitmapLabel(
395 GetScaledBitmap(wxBitmap(play), _T("play"), m_ScaledFactor));
396 m_bpShowCursorData->SetBitmapLabel(GetScaledBitmap(
397 wxBitmap(m_CDataIsShown ? curdata : ncurdata),
398 m_CDataIsShown ? _T("curdata") : _T("ncurdata"), m_ScaledFactor));
399 if (m_bpOpenFile)
400 m_bpOpenFile->SetBitmapLabel(
401 GetScaledBitmap(wxBitmap(openfile), _T("openfile"), m_ScaledFactor));
402 m_bpSettings->SetBitmapLabel(
403 GetScaledBitmap(wxBitmap(setting), _T("setting"), m_ScaledFactor));
404
405 SetRequestBitmap(m_ZoneSelMode);
406
407 // Careful here, this MinSize() sets the final width of the control bar,
408 // overriding the width of the wxChoice above it.
409#ifdef __OCPN__ANDROID__
410 m_sTimeline->SetSize(wxSize(20 * m_ScaledFactor, -1));
411 m_sTimeline->SetMinSize(wxSize(20 * m_ScaledFactor, -1));
412#else
413 m_sTimeline->SetSize(wxSize(90 * m_ScaledFactor, -1));
414 m_sTimeline->SetMinSize(wxSize(90 * m_ScaledFactor, -1));
415#endif
416}
417
418void GRIBUICtrlBar::SetRequestBitmap(int type) {
419 if (nullptr == m_bpRequest) return;
420
421 switch (type) {
422 case AUTO_SELECTION:
423 case SAVED_SELECTION:
424 case START_SELECTION:
425 m_bpRequest->SetBitmapLabel(
426 GetScaledBitmap(wxBitmap(request), _T("request"), m_ScaledFactor));
427 m_bpRequest->SetToolTip(_("Start a request"));
428 break;
429 case DRAW_SELECTION:
430 m_bpRequest->SetBitmapLabel(
431 GetScaledBitmap(wxBitmap(selzone), _T("selzone"), m_ScaledFactor));
432 m_bpRequest->SetToolTip(
433 _("Draw requested Area\nor Click here to stop request"));
434 break;
435 case COMPLETE_SELECTION:
436 m_bpRequest->SetBitmapLabel(GetScaledBitmap(
437 wxBitmap(request_end), _T("request_end"), m_ScaledFactor));
438 m_bpRequest->SetToolTip(_("Valid Area and Continue"));
439 break;
440 }
441}
442
443void GRIBUICtrlBar::OpenFile(bool newestFile) {
444 m_bpPlay->SetBitmapLabel(
445 GetScaledBitmap(wxBitmap(play), _T("play"), m_ScaledFactor));
446 m_cRecordForecast->Clear();
447 pPlugIn->GetGRIBOverlayFactory()->ClearParticles();
448 m_Altitude = 0;
449 m_FileIntervalIndex = m_OverlaySettings.m_SlicesPerUpdate;
450 delete m_bGRIBActiveFile;
451 delete m_pTimelineSet;
452 m_pTimelineSet = nullptr;
453 m_sTimeline->SetValue(0);
454 m_TimeLineHours = 0;
455 m_InterpolateMode = false;
456 m_pNowMode = false;
457 m_SelectionIsSaved = false;
458 m_HasAltitude = false;
459
460 // get more recent file in default directory if necessary
461 wxFileName f;
462 if (newestFile)
463 m_file_names.Clear(); // file names list must be cleared if we expect only
464 // the newest file! otherwise newest file is added to
465 // the previously recorded, what we don't want
466 if (m_file_names.IsEmpty()) { // in any case were there is no filename
467 // previously recorded, we must take the newest
468 m_file_names = GetFilesInDirectory();
469 newestFile = true;
470 }
471
473 pPlugIn->GetCopyMissWaveRec(), newestFile);
474
475 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
476 wxString title;
477 if (m_bGRIBActiveFile->IsOK()) {
478 wxFileName fn(m_bGRIBActiveFile->GetFileNames()[0]);
479 title = (_("File: "));
480 title.Append(fn.GetFullName());
481 if (rsa->GetCount() == 0) { // valid but empty file
482 delete m_bGRIBActiveFile;
483 m_bGRIBActiveFile = nullptr;
484 title.Prepend(_("Error! ")).Append(_(" contains no valid data!"));
485 } else {
486 PopulateComboDataList();
487 title.append(_T(" (") +
489 pPlugIn->GetTimeZone()) +
490 _T(" )"));
491
492 if (rsa->GetCount() > 1) {
493 GribRecordSet &first = rsa->Item(0), &second = rsa->Item(1),
494 &last = rsa->Item(rsa->GetCount() - 1);
495
496 // compute ntotal time span
497 wxTimeSpan span = wxDateTime(last.m_Reference_Time) -
498 wxDateTime(first.m_Reference_Time);
499 m_TimeLineHours = span.GetHours();
500
501 // get file interval index and update intervale choice if necessary
502 int halfintermin(wxTimeSpan(wxDateTime(second.m_Reference_Time) -
503 wxDateTime(first.m_Reference_Time))
504 .GetMinutes() /
505 2);
506 for (m_FileIntervalIndex = 0;; m_FileIntervalIndex++) {
507 if (m_OverlaySettings.GetMinFromIndex(m_FileIntervalIndex) >
508 halfintermin)
509 break;
510 }
511 if (m_FileIntervalIndex > 0) m_FileIntervalIndex--;
512 if (m_OverlaySettings.m_SlicesPerUpdate > m_FileIntervalIndex)
513 m_OverlaySettings.m_SlicesPerUpdate = m_FileIntervalIndex;
514 }
515 }
516 } else {
517 delete m_bGRIBActiveFile;
518 m_bGRIBActiveFile = nullptr;
519 title = _("No valid GRIB file");
520 }
521 pPlugIn->GetGRIBOverlayFactory()->SetMessage(title);
522 SetTitle(title);
523 SetTimeLineMax(false);
524 SetFactoryOptions();
525 if (pPlugIn->GetStartOptions() &&
526 m_TimeLineHours != 0) // fix a crash for one date files
527 ComputeBestForecastForNow();
528 else
529 TimelineChanged();
530
531 // populate altitude choice and show if necessary
533 for (int i = 1; i < 5; i++) {
534 if (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VX + i) !=
535 wxNOT_FOUND &&
536 m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VY + i) !=
537 wxNOT_FOUND)
538 m_HasAltitude = true;
539 }
540 m_Altitude = 0; // set altitude at std
541
542 // enable buttons according with file contents to ovoid crashes
543#ifdef __OCPN__ANDROID__
544 m_bpSettings->Enable(true);
545#else
546 m_bpSettings->Enable(m_pTimelineSet != nullptr);
547#endif
548 m_bpZoomToCenter->Enable(m_pTimelineSet != nullptr);
549
550 m_sTimeline->Enable(m_pTimelineSet != nullptr && m_TimeLineHours);
551 m_bpPlay->Enable(m_pTimelineSet != nullptr && m_TimeLineHours);
552
553 m_bpPrev->Enable(m_pTimelineSet != nullptr && m_TimeLineHours);
554 m_bpNext->Enable(m_pTimelineSet != nullptr && m_TimeLineHours);
555 m_bpNow->Enable(m_pTimelineSet != nullptr && m_TimeLineHours);
556
557 SetCanvasContextMenuItemViz(pPlugIn->m_MenuItem, m_TimeLineHours != 0);
558
559 //
560 if (m_bGRIBActiveFile == nullptr) {
561 // there's no data we can use in this file
562 return;
563 }
564 // Try to verify that there will be at least one parameter in the GRIB file
565 // that is enabled for display This will ensure that at least "some" data is
566 // displayed on file change, and so avoid user confusion of no data shown.
567 // This is especially important if cursor tracking of data is disabled.
568
569 bool bconfigOK = false;
570 if (m_bDataPlot[GribOverlaySettings::WIND] &&
571 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VX) != wxNOT_FOUND))
572 bconfigOK = true;
573 if (m_bDataPlot[GribOverlaySettings::WIND_GUST] &&
574 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_GUST) != wxNOT_FOUND))
575 bconfigOK = true;
576 if (m_bDataPlot[GribOverlaySettings::PRESSURE] &&
577 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_PRESSURE) != wxNOT_FOUND))
578 bconfigOK = true;
579 if (m_bDataPlot[GribOverlaySettings::WAVE] &&
580 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WVDIR) != wxNOT_FOUND))
581 bconfigOK = true;
582 if (m_bDataPlot[GribOverlaySettings::WAVE] &&
583 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_HTSIGW) != wxNOT_FOUND))
584 bconfigOK = true;
585 if (m_bDataPlot[GribOverlaySettings::CURRENT] &&
586 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_SEACURRENT_VX) !=
587 wxNOT_FOUND))
588 bconfigOK = true;
589 if (m_bDataPlot[GribOverlaySettings::PRECIPITATION] &&
590 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_PRECIP_TOT) != wxNOT_FOUND))
591 bconfigOK = true;
592 if (m_bDataPlot[GribOverlaySettings::CLOUD] &&
593 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_CLOUD_TOT) != wxNOT_FOUND))
594 bconfigOK = true;
595 if (m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE] &&
596 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_AIR_TEMP) != wxNOT_FOUND))
597 bconfigOK = true;
598 if (m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE] &&
599 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_SEA_TEMP) != wxNOT_FOUND))
600 bconfigOK = true;
601 if (m_bDataPlot[GribOverlaySettings::CAPE] &&
602 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_CAPE) != wxNOT_FOUND))
603 bconfigOK = true;
604 if (m_bDataPlot[GribOverlaySettings::COMP_REFL] &&
605 (m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_COMP_REFL) != wxNOT_FOUND))
606 bconfigOK = true;
607
608 // If no parameter seems to be enabled by config, enable them all just to be
609 // sure something shows.
610 if (!bconfigOK) {
611 for (int i = 0; i < (int)GribOverlaySettings::GEO_ALTITUDE; i++) {
612 if (InDataPlot(i)) {
613 m_bDataPlot[i] = true;
614 }
615 }
616 }
617}
618
619bool GRIBUICtrlBar::GetGribZoneLimits(GribTimelineRecordSet *timelineSet,
620 double *latmin, double *latmax,
621 double *lonmin, double *lonmax) {
622 // calculate the largest overlay size
623 GribRecord **pGR = timelineSet->m_GribRecordPtrArray;
624 double ltmi = -GRIB_NOTDEF, ltma = GRIB_NOTDEF, lnmi = -GRIB_NOTDEF,
625 lnma = GRIB_NOTDEF;
626 for (unsigned int i = 0; i < Idx_COUNT; i++) {
627 GribRecord *pGRA = pGR[i];
628 if (!pGRA) continue;
629 if (pGRA->getLatMin() < ltmi) ltmi = pGRA->getLatMin();
630 if (pGRA->getLatMax() > ltma) ltma = pGRA->getLatMax();
631 if (pGRA->getLonMin() < lnmi) lnmi = pGRA->getLonMin();
632 if (pGRA->getLonMax() > lnma) lnma = pGRA->getLonMax();
633 }
634 if (ltmi == -GRIB_NOTDEF || lnmi == -GRIB_NOTDEF || ltma == GRIB_NOTDEF ||
635 lnma == GRIB_NOTDEF)
636 return false;
637
638 if (latmin) *latmin = ltmi;
639 if (latmax) *latmax = ltma;
640 if (lonmin) *lonmin = lnmi;
641 if (lonmax) *lonmax = lnma;
642 return true;
643}
644
645class FileCollector : public wxDirTraverser {
646public:
647 FileCollector(wxArrayString &files, const wxRegEx &pattern)
648 : m_files(files), m_pattern(pattern) {}
649 virtual wxDirTraverseResult OnFile(const wxString &filename) {
650 if (m_pattern.Matches(filename)) m_files.Add(filename);
651 return wxDIR_CONTINUE;
652 }
653 virtual wxDirTraverseResult OnDir(const wxString &WXUNUSED(dirname)) {
654 return wxDIR_IGNORE;
655 }
656
657private:
658 wxArrayString &m_files;
659 const wxRegEx &m_pattern;
660};
661
662wxArrayString GRIBUICtrlBar::GetFilesInDirectory() {
663 wxArrayString file_array;
664 if (!wxDir::Exists(m_grib_dir)) return file_array;
665
666 // Get an array of GRIB file names in the target directory, not descending
667 // into subdirs
668 wxRegEx pattern(_T(".+\\.gri?b2?(\\.(bz2|gz))?$"),
669 wxRE_EXTENDED | wxRE_ICASE | wxRE_NOSUB);
670 FileCollector collector(file_array, pattern);
671 wxDir dir(m_grib_dir);
672 dir.Traverse(collector);
673 file_array.Sort(
674 CompareFileStringTime); // sort the files by File Modification Date
675 return file_array;
676}
677
678void GRIBUICtrlBar::SetCursorLatLon(double lat, double lon) {
679 m_cursor_lon = lon;
680 m_cursor_lat = lat;
681
682 if (m_vp && ((lat > m_vp->lat_min) && (lat < m_vp->lat_max)) &&
683 ((lon > m_vp->lon_min) && (lon < m_vp->lon_max)))
684 UpdateTrackingControl();
685}
686
687void GRIBUICtrlBar::UpdateTrackingControl() {
688 if (!m_CDataIsShown) return;
689
690 if (m_DialogStyle >> 1 == SEPARATED) {
691 if (m_gGRIBUICData) {
692 if (!m_gGRIBUICData->m_gCursorData->m_tCursorTrackTimer.IsRunning())
693 m_gGRIBUICData->m_gCursorData->m_tCursorTrackTimer.Start(
694 50, wxTIMER_ONE_SHOT);
695 }
696 } else {
697 if (m_gCursorData) {
698 if (!m_gCursorData->m_tCursorTrackTimer.IsRunning())
699 m_gCursorData->m_tCursorTrackTimer.Start(50, wxTIMER_ONE_SHOT);
700 }
701 }
702}
703
704void GRIBUICtrlBar::OnShowCursorData(wxCommandEvent &event) {
705 m_CDataIsShown = !m_CDataIsShown;
706 m_bpShowCursorData->SetBitmapLabel(GetScaledBitmap(
707 wxBitmap(m_CDataIsShown ? curdata : ncurdata),
708 m_CDataIsShown ? _T("curdata") : _T("ncurdata"), m_ScaledFactor));
709 SetDialogsStyleSizePosition(true);
710}
711
712void GRIBUICtrlBar::SetDialogsStyleSizePosition(bool force_recompute) {
713 /*Not all plateforms accept the dynamic window style changes.
714 So these changes are applied only after exit from the plugin and re-opening
715 it*/
716
717 if (!force_recompute &&
718 (m_old_DialogStyle == m_DialogStyle // recompute only if necessary
719 ||
720 (m_old_DialogStyle >> 1 == ATTACHED && m_DialogStyle >> 1 == ATTACHED)))
721 return;
722
723 bool m_HasCaption = GetWindowStyleFlag() == (wxCAPTION | wxCLOSE_BOX |
724 wxSYSTEM_MENU | wxTAB_TRAVERSAL);
725
726 /* first hide grabber, detach cursordata and set ctrl/buttons visibility to
727 have CtrlBar in his "alone" version altitude button visibility is a special
728 case ( i == 0 ) */
729 int state = (m_DialogStyle >> 1 == ATTACHED && m_CDataIsShown) ? 0 : 1;
730 for (unsigned i = 0; i < m_OverlaySettings.m_iCtrlBarCtrlVisible[state].Len();
731 i++) {
732 bool vis = i > 0 ? true : m_HasAltitude ? true : false;
733 if (FindWindow(i + ID_CTRLALTITUDE))
734 FindWindow(i + ID_CTRLALTITUDE)
735 ->Show(m_OverlaySettings.m_iCtrlBarCtrlVisible[state].GetChar(i) ==
736 _T('X') &&
737 vis);
738 }
739 // initiate tooltips
740 m_bpShowCursorData->SetToolTip(m_CDataIsShown ? _("Hide data at cursor")
741 : _("Show data at cursor"));
742 m_bpPlay->SetToolTip(_("Start play back"));
743
744 m_gGrabber->Hide();
745 // then hide and detach cursor data window
746 if (m_gCursorData) {
747 m_gCursorData->Hide();
748 m_fgCDataSizer->Detach(m_gCursorData);
749 }
750
751 SetMinSize(wxSize(0, 0));
752
753 // then cancel eventually Cursor data dialog (to be re-created later if
754 // necessary )
755 if (m_gGRIBUICData) {
756 m_gGRIBUICData->Destroy();
757 m_gGRIBUICData = nullptr;
758 }
759
760 if ((m_DialogStyle >> 1 == SEPARATED || !m_CDataIsShown) &&
761 !m_HasCaption) { // Size and show grabber if necessary
762 Fit(); // each time CtrlData dialog will be alone
763 m_gGrabber->Size(); // or separated
764 m_gGrabber->Show();
765 }
766
767 if (m_CDataIsShown) {
768 if (m_DialogStyle >> 1 == ATTACHED) { // dialogs attached
769 // generate CursorData
770 if (!m_gCursorData) m_gCursorData = new CursorData(this, *this);
771 pPlugIn->SetDialogFont(m_gCursorData);
772 m_gCursorData->PopulateTrackingControls(false);
773 // attach CursorData to CtrlBar if necessary
774 if (m_fgCDataSizer->GetItem(m_gCursorData) == nullptr)
775 m_fgCDataSizer->Add(m_gCursorData, 0);
776 m_gCursorData->Show();
777
778 } else if (m_DialogStyle >> 1 == SEPARATED) { // dialogs isolated
779 // create cursor data dialog
780 m_gGRIBUICData = new GRIBUICData(*this);
781 m_gGRIBUICData->m_gCursorData->PopulateTrackingControls(
782 m_DialogStyle == SEPARATED_VERTICAL);
783 pPlugIn->SetDialogFont(m_gGRIBUICData->m_gCursorData);
784 m_gGRIBUICData->Fit();
785 m_gGRIBUICData->Update();
786 m_gGRIBUICData->Show();
787 pPlugIn->MoveDialog(m_gGRIBUICData, pPlugIn->GetCursorDataXY());
788 m_gGRIBUICData->Layout();
789 m_gGRIBUICData->Fit();
790 }
791 }
792 Layout();
793 Fit();
794 wxSize sd = GetSize();
795#ifdef __WXGTK__
796 if (!m_gtk_started && m_HasCaption /*&& sd.y == GetSize().y*/) {
797 sd.y += 30;
798 m_gtk_started = true;
799 }
800#endif
801 SetSize(wxSize(sd.x, sd.y));
802 SetMinSize(wxSize(sd.x, sd.y));
803#ifdef __OCPN__ANDROID__
804 wxRect tbRect = GetMasterToolbarRect();
805 // qDebug() << "TBR" << tbRect.x << tbRect.y << tbRect.width << tbRect.height
806 // << pPlugIn->GetCtrlBarXY().x << pPlugIn->GetCtrlBarXY().y;
807
808 if (1) {
809 wxPoint pNew = pPlugIn->GetCtrlBarXY();
810 pNew.x = tbRect.x + tbRect.width + 4;
811 pNew.y = 0; // tbRect.y;
812 pPlugIn->SetCtrlBarXY(pNew);
813 // qDebug() << "pNew" << pNew.x;
814
815 int widthAvail =
816 GetCanvasByIndex(0)->GetClientSize().x - (tbRect.x + tbRect.width);
817
818 if (sd.x > widthAvail) {
819 // qDebug() << "Too big" << widthAvail << sd.x;
820
821 int target_char_width = (float)widthAvail / 28;
822 wxScreenDC dc;
823 bool bOK = false;
824 int pointSize = 20;
825 int width, height;
826 wxFont *sFont;
827 while (!bOK) {
828 // qDebug() << "PointSize" << pointSize;
829 sFont = FindOrCreateFont_PlugIn(pointSize, wxFONTFAMILY_DEFAULT,
830 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
831 FALSE);
832 dc.GetTextExtent(_T("W"), &width, &height, nullptr, nullptr, sFont);
833 if (width <= target_char_width) bOK = true;
834 pointSize--;
835 if (pointSize <= 10) bOK = true;
836 }
837
838 m_cRecordForecast->SetFont(*sFont);
839
840 Layout();
841 Fit();
842 Hide();
843 SetSize(wxSize(widthAvail, sd.y));
844 SetMinSize(wxSize(widthAvail, sd.y));
845 Show();
846 }
847 }
848 wxPoint pNow = pPlugIn->GetCtrlBarXY();
849 pNow.y = 0;
850 pPlugIn->SetCtrlBarXY(pNow);
851
852#endif
853
854 pPlugIn->MoveDialog(this, pPlugIn->GetCtrlBarXY());
855 m_old_DialogStyle = m_DialogStyle;
856}
857
858void GRIBUICtrlBar::OnAltitude(wxCommandEvent &event) {
859 if (!m_HasAltitude) return;
860
861 wxMenu *amenu = new wxMenu();
862 amenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
863 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent), nullptr, this);
864
865 for (int i = 0; i < 5; i++) {
866 if (((m_pTimelineSet &&
867 m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VX + i) !=
868 wxNOT_FOUND &&
869 m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VY + i) !=
870 wxNOT_FOUND)) ||
871 i == 0) {
872 MenuAppend(
873 amenu, ID_CTRLALTITUDE + 1000 + i,
874
875 m_OverlaySettings.GetAltitudeFromIndex(
876 i, m_OverlaySettings.Settings[GribOverlaySettings::PRESSURE]
877 .m_Units),
878 wxITEM_RADIO);
879 }
880 }
881
882 amenu->Check(ID_CTRLALTITUDE + 1000 + m_Altitude, true);
883
884 PopupMenu(amenu);
885
886 delete amenu;
887}
888
889void GRIBUICtrlBar::OnMove(wxMoveEvent &event) {
890 int w, h;
891 GetScreenPosition(&w, &h);
892 pPlugIn->SetCtrlBarXY(wxPoint(w, h));
893}
894
895void GRIBUICtrlBar::OnMenuEvent(wxMenuEvent &event) {
896 int id = event.GetId();
897 wxCommandEvent evt;
898 evt.SetId(id);
899 int alt = m_Altitude;
900 switch (id) {
901 // sub menu altitude data
902 case ID_CTRLALTITUDE + 1000:
903 m_Altitude = 0;
904 break;
905 case ID_CTRLALTITUDE + 1001:
906 m_Altitude = 1;
907 break;
908 case ID_CTRLALTITUDE + 1002:
909 m_Altitude = 2;
910 break;
911 case ID_CTRLALTITUDE + 1003:
912 m_Altitude = 3;
913 break;
914 case ID_CTRLALTITUDE + 1004:
915 m_Altitude = 4;
916 break;
917 // end sub menu
918 case ID_BTNNOW:
919 OnNow(evt);
920 break;
921 case ID_BTNZOOMTC:
922 OnZoomToCenterClick(evt);
923 break;
924 case ID_BTNSHOWCDATA:
925 OnShowCursorData(evt);
926 break;
927 case ID_BTNPLAY:
928 OnPlayStop(evt);
929 break;
930 case ID_BTNOPENFILE:
931 OnOpenFile(evt);
932 break;
933 case ID_BTNSETTING:
934 OnSettings(evt);
935 break;
936 case ID_BTNREQUEST:
937 OnRequest(evt);
938 }
939 if (alt != m_Altitude) {
940 SetDialogsStyleSizePosition(true);
941 SetFactoryOptions(); // Reload the visibility options
942 }
943}
944
945void GRIBUICtrlBar::MenuAppend(wxMenu *menu, int id, wxString label,
946 wxItemKind kind, wxBitmap bitmap,
947 wxMenu *submenu) {
948 wxMenuItem *item = new wxMenuItem(menu, id, label, _T(""), kind);
949 // add a submenu to this item if necessary
950 if (submenu) item->SetSubMenu(submenu);
951
952 /* Menu font do not work properly for MSW (wxWidgets 3.2.1)
953 #ifdef __WXMSW__
954 wxFont *qFont = OCPNGetFont(_("Menu"), 0);
955 item->SetFont(*qFont);
956 #endif
957 */
958
959#if defined(__WXMSW__) || defined(__WXGTK__)
960 if (!bitmap.IsSameAs(wxNullBitmap)) item->SetBitmap(bitmap);
961#endif
962
963 menu->Append(item);
964}
965
966void GRIBUICtrlBar::OnMouseEvent(wxMouseEvent &event) {
967 if (event.RightDown()) {
968 // populate menu
969 wxMenu *xmenu = new wxMenu();
970 xmenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
971 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent), nullptr,
972 this);
973
974 if (m_HasAltitude) { // eventually populate altitude choice
975 wxMenu *smenu = new wxMenu();
976 smenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
977 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent), nullptr,
978 this);
979
980 for (int i = 0; i < 5; i++) {
981 if (((m_pTimelineSet &&
982 m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VX + i) !=
983 wxNOT_FOUND &&
984 m_bGRIBActiveFile->m_GribIdxArray.Index(Idx_WIND_VY + i) !=
985 wxNOT_FOUND)) ||
986 i == 0) {
987 MenuAppend(
988 smenu, ID_CTRLALTITUDE + 1000 + i,
989 m_OverlaySettings.GetAltitudeFromIndex(
990 i, m_OverlaySettings.Settings[GribOverlaySettings::PRESSURE]
991 .m_Units),
992 wxITEM_RADIO);
993 }
994 }
995 smenu->Check(ID_CTRLALTITUDE + 1000 + m_Altitude, true);
996 MenuAppend(
997 xmenu, wxID_ANY, _("Select geopotential altitude"), wxITEM_NORMAL,
998 GetScaledBitmap(wxBitmap(altitude), _T("altitude"), m_ScaledFactor),
999 smenu);
1000 }
1001 MenuAppend(xmenu, ID_BTNNOW, _("Now"), wxITEM_NORMAL,
1002 GetScaledBitmap(wxBitmap(now), _T("now"), m_ScaledFactor));
1003 MenuAppend(xmenu, ID_BTNZOOMTC, _("Zoom To Center"), wxITEM_NORMAL,
1004 GetScaledBitmap(wxBitmap(zoomto), _T("zoomto"), m_ScaledFactor));
1005 MenuAppend(
1006 xmenu, ID_BTNSHOWCDATA,
1007 m_CDataIsShown ? _("Hide data at cursor") : _("Show data at cursor"),
1008 wxITEM_NORMAL,
1009 GetScaledBitmap(wxBitmap(m_CDataIsShown ? curdata : ncurdata),
1010 m_CDataIsShown ? _T("curdata") : _T("ncurdata"),
1011 m_ScaledFactor));
1012 MenuAppend(
1013 xmenu, ID_BTNPLAY,
1014 m_tPlayStop.IsRunning() ? _("Stop play back") : _("Start play back"),
1015 wxITEM_NORMAL,
1016 GetScaledBitmap(wxBitmap(m_tPlayStop.IsRunning() ? stop : play),
1017 m_tPlayStop.IsRunning() ? _T("stop") : _T("play"),
1018 m_ScaledFactor));
1019 MenuAppend(
1020 xmenu, ID_BTNOPENFILE, _("Open a new file"), wxITEM_NORMAL,
1021 GetScaledBitmap(wxBitmap(openfile), _T("openfile"), m_ScaledFactor));
1022 MenuAppend(
1023 xmenu, ID_BTNSETTING, _("Settings"), wxITEM_NORMAL,
1024 GetScaledBitmap(wxBitmap(setting), _T("setting"), m_ScaledFactor));
1025 bool requeststate1 = m_ZoneSelMode == AUTO_SELECTION ||
1026 m_ZoneSelMode == SAVED_SELECTION ||
1027 m_ZoneSelMode == START_SELECTION;
1028 bool requeststate3 = m_ZoneSelMode == DRAW_SELECTION;
1029 MenuAppend(xmenu, ID_BTNREQUEST,
1030 requeststate1 ? _("Start a request")
1031 : requeststate3
1032 ? _("Draw requested Area or Click here to stop request")
1033 : _("Valid Area and Continue"),
1034 wxITEM_NORMAL,
1035 GetScaledBitmap(wxBitmap(requeststate1 ? request
1036 : requeststate3 ? selzone
1037 : request_end),
1038 requeststate1 ? _T("request")
1039 : requeststate3 ? _T("selzone")
1040 : _T("request_end"),
1041 m_ScaledFactor));
1042
1043 PopupMenu(xmenu);
1044
1045 delete xmenu;
1046
1047 return;
1048 }
1049
1050 if (m_DialogStyle >> 1 == SEPARATED) return;
1051 wxMouseEvent evt(event);
1052 evt.SetId(1000);
1053
1054#ifndef __OCPN__ANDROID__
1055 if (m_gCursorData && m_CDataIsShown) {
1056 m_gCursorData->OnMouseEvent(evt);
1057 }
1058#endif
1059}
1060
1061void GRIBUICtrlBar::ContextMenuItemCallback(int id) {
1062 // deactivate cursor data update during menu callback
1063 bool dataisshown = m_CDataIsShown;
1064 m_CDataIsShown = false;
1065 //
1066 wxFileConfig *pConf = GetOCPNConfigObject();
1067
1068 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1069 GRIBTable *table = new GRIBTable(*this);
1070
1071 table->InitGribTable(pPlugIn->GetTimeZone(), rsa,
1072 GetNearestIndex(GetNow(), 0));
1073 table->SetTableSizePosition(m_vp->pix_width, m_vp->pix_height);
1074
1075 table->ShowModal();
1076
1077 // re-activate cursor data
1078 m_CDataIsShown = dataisshown;
1079 delete table;
1080}
1081
1082void GRIBUICtrlBar::SetViewPort(PlugIn_ViewPort *vp) {
1083 if (m_vp == vp) return;
1084
1085 delete m_vp;
1086 m_vp = new PlugIn_ViewPort(*vp);
1087
1088 if (pReq_Dialog) pReq_Dialog->OnVpChange(vp);
1089}
1090
1091void GRIBUICtrlBar::OnClose(wxCloseEvent &event) {
1092 StopPlayBack();
1093 if (m_gGRIBUICData) m_gGRIBUICData->Hide();
1094 if (pReq_Dialog)
1095 if (m_ZoneSelMode > START_SELECTION) {
1096 pReq_Dialog->StopGraphicalZoneSelection();
1097 m_ZoneSelMode = START_SELECTION;
1098 // SetRequestBitmap( m_ZoneSelMode );
1099 }
1100 pPlugIn->SendTimelineMessage(wxInvalidDateTime);
1101
1102 pPlugIn->OnGribCtrlBarClose();
1103}
1104
1105void GRIBUICtrlBar::OnSize(wxSizeEvent &event) {
1106 // Record the dialog size
1107 wxSize p = event.GetSize();
1108 pPlugIn->SetCtrlBarSizeXY(p);
1109
1110 event.Skip();
1111}
1112
1113void GRIBUICtrlBar::OnPaint(wxPaintEvent &event) {
1114 wxWindowListNode *node = this->GetChildren().GetFirst();
1115 wxPaintDC dc(this);
1116 while (node) {
1117 wxWindow *win = node->GetData();
1118 if (dynamic_cast<wxBitmapButton *>(win))
1119 dc.DrawBitmap(dynamic_cast<wxBitmapButton *>(win)->GetBitmap(), 5, 5,
1120 false);
1121 node = node->GetNext();
1122 }
1123}
1124
1125void GRIBUICtrlBar::OnRequest(wxCommandEvent &event) {
1126 if (m_tPlayStop.IsRunning())
1127 return; // do nothing when play back is running !
1128
1129 /*if there is one instance of the dialog already visible, do nothing*/
1130 if (pReq_Dialog && pReq_Dialog->IsShown()) return;
1131
1132 /*a second click without selection cancel the process*/
1133 if (m_ZoneSelMode == DRAW_SELECTION) {
1134 assert(pReq_Dialog);
1135 m_ZoneSelMode = START_SELECTION;
1136 pReq_Dialog->StopGraphicalZoneSelection();
1137 SetRequestBitmap(m_ZoneSelMode);
1138 return;
1139 }
1140
1141 /*create new request dialog*/
1142 if (m_ZoneSelMode == AUTO_SELECTION || m_ZoneSelMode == SAVED_SELECTION ||
1143 m_ZoneSelMode == START_SELECTION) {
1144 ::wxBeginBusyCursor();
1145
1146 delete pReq_Dialog; // delete to be re-created
1147
1148 pReq_Dialog = new GribRequestSetting(*this);
1149 pPlugIn->SetDialogFont(pReq_Dialog);
1150 pPlugIn->SetDialogFont(pReq_Dialog->m_sScrolledDialog);
1151 pReq_Dialog->OnVpChange(m_vp);
1152 pReq_Dialog->SetRequestDialogSize();
1153 // need to set a position at start
1154 int w;
1155 ::wxDisplaySize(&w, nullptr);
1156 pReq_Dialog->Move((w - pReq_Dialog->GetSize().GetX()) / 2, 30);
1157
1158 } // end create new request dialog
1159
1160 pReq_Dialog->Show(m_ZoneSelMode == AUTO_SELECTION ||
1161 m_ZoneSelMode == SAVED_SELECTION ||
1162 m_ZoneSelMode == COMPLETE_SELECTION);
1163 m_ZoneSelMode = m_ZoneSelMode == START_SELECTION ? DRAW_SELECTION
1164 : m_ZoneSelMode == COMPLETE_SELECTION ? START_SELECTION
1165 : m_ZoneSelMode;
1166 if (m_ZoneSelMode == START_SELECTION)
1167 pReq_Dialog->StopGraphicalZoneSelection();
1168 SetRequestBitmap(m_ZoneSelMode); // set appopriate bitmap
1169
1170 if (::wxIsBusy()) ::wxEndBusyCursor();
1171}
1172
1173void GRIBUICtrlBar::OnSettings(wxCommandEvent &event) {
1174 if (m_tPlayStop.IsRunning())
1175 return; // do nothing when play back is running !
1176
1177 ::wxBeginBusyCursor();
1178
1181 *this, m_OverlaySettings, m_lastdatatype, m_FileIntervalIndex);
1182 // set font
1183 pPlugIn->SetDialogFont(dialog);
1184 for (size_t i = 0; i < dialog->m_nSettingsBook->GetPageCount(); i++) {
1185 wxScrolledWindow *sc =
1186 ((wxScrolledWindow *)dialog->m_nSettingsBook->GetPage(i));
1187 pPlugIn->SetDialogFont(sc);
1188 } // end set font
1189
1190 dialog->m_nSettingsBook->ChangeSelection(dialog->GetPageIndex());
1191 dialog->SetSettingsDialogSize();
1192 // need to set a position at start
1193 int w;
1194 ::wxDisplaySize(&w, nullptr);
1195 dialog->Move((w - dialog->GetSize().GetX()) / 2, 30);
1196 // end set position
1197
1198 ::wxEndBusyCursor();
1199
1200 if (dialog->ShowModal() == wxID_OK) {
1201 dialog->WriteSettings();
1202 m_OverlaySettings.Write();
1203 if (m_OverlaySettings.Settings[GribOverlaySettings::WIND].m_Units !=
1204 initSettings.Settings[GribOverlaySettings::WIND].m_Units &&
1205 (m_OverlaySettings.Settings[GribOverlaySettings::WIND].m_Units ==
1206 GribOverlaySettings::BFS ||
1207 initSettings.Settings[GribOverlaySettings::WIND].m_Units ==
1208 GribOverlaySettings::BFS))
1209 m_old_DialogStyle =
1210 STARTING_STATE_STYLE; // must recompute dialogs size if wind unit
1211 // have been changed
1212 } else {
1213 m_OverlaySettings = initSettings;
1214 m_DialogStyle = initSettings.m_iCtrlandDataStyle;
1215 }
1216 ::wxBeginBusyCursor();
1217
1218 dialog->SaveLastPage();
1219 if (!m_OverlaySettings.m_bInterpolate)
1220 m_InterpolateMode = false; // Interpolate could have been unchecked
1221 SetTimeLineMax(true);
1222 SetFactoryOptions();
1223
1224 SetDialogsStyleSizePosition(true);
1225 delete dialog;
1226
1227 event.Skip();
1228}
1229
1230#ifdef __OCPN__ANDROID__
1231wxString callActivityMethod_ss(const char *method, wxString parm);
1232#endif
1233
1234void GRIBUICtrlBar::OnCompositeDialog(wxCommandEvent &event) {
1235 // Grab the current settings values
1237 initSettings.Read();
1238
1239 wxString json;
1240 wxString json_begin = initSettings.SettingsToJSON(json);
1241 wxLogMessage(json_begin);
1242
1243 // Pick up the required options from the Request dialog
1244 // and add them to the JSON object
1245 // Really, this just means the current viewport coordinates.
1246 // Everything else is stored in Android app preferences bundle.
1247
1248 PlugIn_ViewPort current_vp = pPlugIn->GetCurrentViewPort();
1249
1250 double lon_min = wxRound(current_vp.lon_min) - 1;
1251 double lon_max = wxRound(current_vp.lon_max) + 1;
1252 double lat_min = wxRound(current_vp.lat_min) - 1;
1253 double lat_max = wxRound(current_vp.lat_max) + 1;
1254
1255 wxJSONValue v;
1256 wxJSONReader reader;
1257 int numErrors = reader.Parse(json_begin, &v);
1258 if (numErrors > 0) {
1259 return;
1260 }
1261
1262 v[_T("latMin")] = lat_min;
1263 v[_T("latMax")] = lat_max;
1264 v[_T("lonMin")] = lon_min;
1265 v[_T("lonMax")] = lon_max;
1266
1267 // Clear the file name field, so that a retrieved or selected file name can
1268 // be returned
1269 v[_T("grib_file")] = _T("");
1270
1271 wxJSONWriter w;
1272 wxString json_final;
1273 w.Write(v, json_final);
1274 wxLogMessage(json_final);
1275
1276#ifdef __OCPN__ANDROID__
1277 wxString ret = callActivityMethod_ss("doGRIBActivity", json_final);
1278 wxLogMessage(ret);
1279#endif
1280
1281 event.Skip();
1282}
1283
1284void GRIBUICtrlBar::OpenFileFromJSON(wxString json) {
1285 // construct the JSON root object
1286 wxJSONValue root;
1287 // construct a JSON parser
1288 wxJSONReader reader;
1289
1290 int numErrors = reader.Parse(json, &root);
1291 if (numErrors > 0) {
1292 return;
1293 }
1294
1295 wxString file = root[(_T("grib_file"))].AsString();
1296
1297 if (file.Length() && wxFileExists(file)) {
1298 wxFileName fn(file);
1299 m_grib_dir = fn.GetPath();
1300 m_file_names.Clear();
1301 m_file_names.Add(file);
1302 OpenFile();
1303 }
1304}
1305
1306void GRIBUICtrlBar::OnPlayStop(wxCommandEvent &event) {
1307 if (m_tPlayStop.IsRunning()) {
1308 StopPlayBack();
1309 } else {
1310 m_bpPlay->SetBitmapLabel(
1311 GetScaledBitmap(wxBitmap(stop), _T("stop"), m_ScaledFactor));
1312 m_bpPlay->SetToolTip(_("Stop play back"));
1313 m_tPlayStop.Start(3000 / m_OverlaySettings.m_UpdatesPerSecond,
1314 wxTIMER_CONTINUOUS);
1315 m_InterpolateMode = m_OverlaySettings.m_bInterpolate;
1316 }
1317}
1318
1319void GRIBUICtrlBar::OnPlayStopTimer(wxTimerEvent &event) {
1320 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax()) {
1321 if (m_OverlaySettings.m_bLoopMode) {
1322 if (m_OverlaySettings.m_LoopStartPoint) {
1323 ComputeBestForecastForNow();
1324 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax())
1325 StopPlayBack(); // will stop playback
1326 return;
1327 } else
1328 m_sTimeline->SetValue(0);
1329 } else {
1330 StopPlayBack(); // will stop playback
1331 return;
1332 }
1333 } else {
1334 int value = m_pNowMode ? m_OverlaySettings.m_bInterpolate
1335 ? GetNearestValue(GetNow(), 1)
1336 : GetNearestIndex(GetNow(), 2)
1337 : m_sTimeline->GetValue();
1338 m_sTimeline->SetValue(value + 1);
1339 }
1340
1341 m_pNowMode = false;
1342 if (!m_InterpolateMode)
1343 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1344 TimelineChanged();
1345}
1346
1347void GRIBUICtrlBar::StopPlayBack() {
1348 if (m_tPlayStop.IsRunning()) {
1349 m_tPlayStop.Stop();
1350 m_bpPlay->SetBitmapLabel(
1351 GetScaledBitmap(wxBitmap(play), _T("play"), m_ScaledFactor));
1352 m_bpPlay->SetToolTip(_("Start play back"));
1353 }
1354}
1355
1356void GRIBUICtrlBar::TimelineChanged() {
1358 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(nullptr);
1359 return;
1360 }
1361
1362 RestaureSelectionString(); // eventually restaure the previousely saved time
1363 // label
1364
1365 wxDateTime time = TimelineTime();
1366 SetGribTimelineRecordSet(GetTimeLineRecordSet(time));
1367
1368 if (!m_InterpolateMode) {
1369 /* get closest value to update timeline */
1370 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1371 GribRecordSet &sel = rsa->Item(m_cRecordForecast->GetCurrentSelection());
1372 wxDateTime t = sel.m_Reference_Time;
1373 m_sTimeline->SetValue(m_OverlaySettings.m_bInterpolate
1374 ? wxTimeSpan(t - MinTime()).GetMinutes() /
1375 m_OverlaySettings.GetMinFromIndex(
1376 m_OverlaySettings.m_SlicesPerUpdate)
1377 : m_cRecordForecast->GetCurrentSelection());
1378 } else {
1379 m_cRecordForecast->SetSelection(GetNearestIndex(time, 2));
1380 SaveSelectionString(); // memorize index and label
1381 m_cRecordForecast->SetString(
1382 m_Selection_index,
1383 TToString(time, pPlugIn->GetTimeZone())); // replace it by the
1384 // interpolated time label
1385 m_cRecordForecast->SetStringSelection(TToString(
1386 time, pPlugIn->GetTimeZone())); // ensure it's visible in the box
1387 }
1388
1389 UpdateTrackingControl();
1390
1391 pPlugIn->SendTimelineMessage(time);
1392 RequestRefresh(GetGRIBCanvas());
1393}
1394
1395void GRIBUICtrlBar::RestaureSelectionString() {
1396 if (!m_SelectionIsSaved) return;
1397
1398 int sel = m_cRecordForecast->GetSelection();
1399 m_cRecordForecast->SetString(m_Selection_index, m_Selection_label);
1400 m_cRecordForecast->SetSelection(sel);
1401 m_SelectionIsSaved = false;
1402}
1403
1404int GRIBUICtrlBar::GetNearestIndex(wxDateTime time, int model) {
1405 /* get closest index to update combo box */
1406 size_t i;
1407 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1408
1409 wxDateTime itime, ip1time;
1410 for (i = 0; i < rsa->GetCount() - 1; i++) {
1411 itime = rsa->Item(i).m_Reference_Time;
1412 ip1time = rsa->Item(i + 1).m_Reference_Time;
1413 if (ip1time >= time) break;
1414 }
1415 if (!model) return (time - itime > (ip1time - time) * 3) ? i + 1 : i;
1416
1417 return model == 1 ? time == ip1time ? i : i + 1 : time == ip1time ? i + 1 : i;
1418}
1419
1420int GRIBUICtrlBar::GetNearestValue(wxDateTime time, int model) {
1421 /* get closest value to update Time line */
1422 if (m_TimeLineHours == 0) return 0;
1423 wxDateTime itime, ip1time;
1424 int stepmin =
1425 m_OverlaySettings.GetMinFromIndex(m_OverlaySettings.m_SlicesPerUpdate);
1426 wxTimeSpan span = time - MinTime();
1427 int t = span.GetMinutes() / stepmin;
1428 itime = MinTime() +
1429 wxTimeSpan(t * stepmin / 60, (t * stepmin) % 60); // time at t
1430 ip1time = itime + wxTimeSpan(stepmin / 60, stepmin % 60); // time at t+1
1431
1432 if (model == 1) return time == ip1time ? t + 1 : t;
1433
1434 return (time - itime > (ip1time - time) * 3) ? t + 1 : t;
1435}
1436
1437wxDateTime GRIBUICtrlBar::GetNow() {
1438 wxDateTime now = wxDateTime::Now();
1439 now.GetSecond(0);
1440
1441 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1442
1443 // verifie if we are outside of the file time range
1444 now = (now > rsa->Item(rsa->GetCount() - 1).m_Reference_Time)
1445 ? rsa->Item(rsa->GetCount() - 1).m_Reference_Time
1446 : (now < rsa->Item(0).m_Reference_Time) ? rsa->Item(0).m_Reference_Time
1447 : now;
1448 return now;
1449}
1450
1451wxDateTime GRIBUICtrlBar::TimelineTime() {
1452 if (m_InterpolateMode) {
1453 int tl = (m_TimeLineHours == 0) ? 0 : m_sTimeline->GetValue();
1454 int stepmin =
1455 m_OverlaySettings.GetMinFromIndex(m_OverlaySettings.m_SlicesPerUpdate);
1456 return MinTime() + wxTimeSpan(tl * stepmin / 60, (tl * stepmin) % 60);
1457 }
1458
1459 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1460 unsigned int index = m_cRecordForecast->GetCurrentSelection() < 1
1461 ? 0
1462 : m_cRecordForecast->GetCurrentSelection();
1463 if (rsa && index < rsa->GetCount()) return rsa->Item(index).m_Reference_Time;
1464
1465 return wxDateTime::Now();
1466}
1467
1468wxDateTime GRIBUICtrlBar::MinTime() {
1469 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1470 if (rsa && rsa->GetCount()) {
1471 GribRecordSet &first = rsa->Item(0);
1472 return first.m_Reference_Time;
1473 }
1474 return wxDateTime::Now();
1475}
1476
1478 if (m_bGRIBActiveFile == nullptr) return nullptr;
1479 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1480
1481 if (rsa->GetCount() == 0) return nullptr;
1482
1484 new GribTimelineRecordSet(m_bGRIBActiveFile->GetCounter());
1485 for (int i = 0; i < Idx_COUNT; i++) {
1486 GribRecordSet *GRS1 = nullptr, *GRS2 = nullptr;
1487 GribRecord *GR1 = nullptr, *GR2 = nullptr;
1488 wxDateTime GR1time, GR2time;
1489
1490 // already computed using polar interpolation from first axis
1491 if (set->m_GribRecordPtrArray[i]) continue;
1492
1493 unsigned int j;
1494 for (j = 0; j < rsa->GetCount(); j++) {
1495 GribRecordSet *GRS = &rsa->Item(j);
1496 GribRecord *GR = GRS->m_GribRecordPtrArray[i];
1497 if (!GR) continue;
1498
1499 wxDateTime curtime = GRS->m_Reference_Time;
1500 if (curtime <= time) GR1time = curtime, GRS1 = GRS, GR1 = GR;
1501
1502 if (curtime >= time) {
1503 GR2time = curtime, GRS2 = GRS, GR2 = GR;
1504 break;
1505 }
1506 }
1507
1508 if (!GR1 || !GR2) continue;
1509
1510 wxDateTime mintime = MinTime();
1511 double minute2 = (GR2time - mintime).GetMinutes();
1512 double minute1 = (GR1time - mintime).GetMinutes();
1513 double nminute = (time - mintime).GetMinutes();
1514
1515 if (minute2 < minute1 || nminute < minute1 || nminute > minute2) continue;
1516
1517 double interp_const;
1518 if (minute1 == minute2) {
1519 // with big grib a copy is slow use a reference.
1520 set->m_GribRecordPtrArray[i] = GR1;
1521 continue;
1522 } else
1523 interp_const = (nminute - minute1) / (minute2 - minute1);
1524
1525 /* if this is a vector interpolation use the 2d method */
1526 if (i < Idx_WIND_VY) {
1527 GribRecord *GR1y = GRS1->m_GribRecordPtrArray[i + Idx_WIND_VY];
1528 GribRecord *GR2y = GRS2->m_GribRecordPtrArray[i + Idx_WIND_VY];
1529 if (GR1y && GR2y) {
1530 GribRecord *Ry;
1531 set->SetUnRefGribRecord(
1532 i, GribRecord::Interpolated2DRecord(Ry, *GR1, *GR1y, *GR2, *GR2y,
1533 interp_const));
1534 set->SetUnRefGribRecord(i + Idx_WIND_VY, Ry);
1535 continue;
1536 }
1537 } else if (i <= Idx_WIND_VY300)
1538 continue;
1539 else if (i == Idx_SEACURRENT_VX) {
1541 GribRecord *GR2y = GRS2->m_GribRecordPtrArray[Idx_SEACURRENT_VY];
1542 if (GR1y && GR2y) {
1543 GribRecord *Ry;
1544 set->SetUnRefGribRecord(
1545 i, GribRecord::Interpolated2DRecord(Ry, *GR1, *GR1y, *GR2, *GR2y,
1546 interp_const));
1548 continue;
1549 }
1550 } else if (i == Idx_SEACURRENT_VY)
1551 continue;
1552
1554 *GR1, *GR2, interp_const, i == Idx_WVDIR));
1555 }
1556
1557 set->m_Reference_Time = time.GetTicks();
1558 //(1-interp_const)*GRS1.m_Reference_Time + interp_const*GRS2.m_Reference_Time;
1559 return set;
1560}
1561
1562void GRIBUICtrlBar::GetProjectedLatLon(int &x, int &y) {
1563 wxPoint p(0, 0);
1564 auto now = TimelineTime();
1565 auto sog = m_ProjectBoatPanel->GetSpeed();
1566 auto cog = m_ProjectBoatPanel->GetCourse();
1567 double dist =
1568 static_cast<double>(now.GetTicks() - pPlugIn->m_boat_time) * sog / 3600.0;
1569 PositionBearingDistanceMercator_Plugin(pPlugIn->m_boat_lat,
1570 pPlugIn->m_boat_lon, cog, dist,
1571 &m_projected_lat, &m_projected_lon);
1572 if (m_vp) {
1573 GetCanvasPixLL(m_vp, &p, m_projected_lat, m_projected_lon);
1574 }
1575 x = p.x;
1576 y = p.y;
1577}
1578
1579double GRIBUICtrlBar::getTimeInterpolatedValue(int idx, double lon, double lat,
1580 wxDateTime time) {
1581 if (m_bGRIBActiveFile == nullptr) return GRIB_NOTDEF;
1582 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1583
1584 if (rsa->GetCount() == 0) return GRIB_NOTDEF;
1585
1586 GribRecord *before = nullptr, *after = nullptr;
1587
1588 unsigned int j;
1589 time_t t = time.GetTicks();
1590 for (j = 0; j < rsa->GetCount(); j++) {
1591 GribRecordSet *GRS = &rsa->Item(j);
1592 GribRecord *GR = GRS->m_GribRecordPtrArray[idx];
1593 if (!GR) continue;
1594
1595 time_t curtime = GR->getRecordCurrentDate();
1596 if (curtime == t) return GR->getInterpolatedValue(lon, lat);
1597
1598 if (curtime < t) before = GR;
1599
1600 if (curtime > t) {
1601 after = GR;
1602 break;
1603 }
1604 }
1605 // time_t wxDateTime::GetTicks();
1606 if (!before || !after) return GRIB_NOTDEF;
1607
1608 time_t t1 = before->getRecordCurrentDate();
1609 time_t t2 = after->getRecordCurrentDate();
1610 if (t1 == t2) return before->getInterpolatedValue(lon, lat);
1611
1612 double v1 = before->getInterpolatedValue(lon, lat);
1613 double v2 = after->getInterpolatedValue(lon, lat);
1614 if (v1 != GRIB_NOTDEF && v2 != GRIB_NOTDEF) {
1615 double k = fabs((double)(t - t1) / (t2 - t1));
1616 return (1.0 - k) * v1 + k * v2;
1617 }
1618
1619 return GRIB_NOTDEF;
1620}
1621
1622bool GRIBUICtrlBar::getTimeInterpolatedValues(double &M, double &A, int idx1,
1623 int idx2, double lon, double lat,
1624 wxDateTime time) {
1625 M = GRIB_NOTDEF;
1626 A = GRIB_NOTDEF;
1627
1628 if (m_bGRIBActiveFile == nullptr) return false;
1629 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1630
1631 if (rsa->GetCount() == 0) return false;
1632
1633 GribRecord *beforeX = nullptr, *afterX = nullptr;
1634 GribRecord *beforeY = nullptr, *afterY = nullptr;
1635
1636 unsigned int j;
1637 time_t t = time.GetTicks();
1638 for (j = 0; j < rsa->GetCount(); j++) {
1639 GribRecordSet *GRS = &rsa->Item(j);
1640 GribRecord *GX = GRS->m_GribRecordPtrArray[idx1];
1641 GribRecord *GY = GRS->m_GribRecordPtrArray[idx2];
1642 if (!GX || !GY) continue;
1643
1644 time_t curtime = GX->getRecordCurrentDate();
1645 if (curtime == t) {
1646 return GribRecord::getInterpolatedValues(M, A, GX, GY, lon, lat, true);
1647 }
1648 if (curtime < t) {
1649 beforeX = GX;
1650 beforeY = GY;
1651 }
1652 if (curtime > t) {
1653 afterX = GX;
1654 afterY = GY;
1655 break;
1656 }
1657 }
1658 // time_t wxDateTime::GetTicks();
1659 if (!beforeX || !afterX) return false;
1660
1661 time_t t1 = beforeX->getRecordCurrentDate();
1662 time_t t2 = afterX->getRecordCurrentDate();
1663 if (t1 == t2) {
1664 return GribRecord::getInterpolatedValues(M, A, beforeX, beforeY, lon, lat,
1665 true);
1666 }
1667 double v1m, v2m, v1a, v2a;
1668 if (!GribRecord::getInterpolatedValues(v1m, v1a, beforeX, beforeY, lon, lat,
1669 true))
1670 return false;
1671
1672 if (!GribRecord::getInterpolatedValues(v2m, v2a, afterX, afterY, lon, lat,
1673 true))
1674 return false;
1675
1676 if (v1m == GRIB_NOTDEF || v2m == GRIB_NOTDEF || v1a == GRIB_NOTDEF ||
1677 v2a == GRIB_NOTDEF)
1678 return false;
1679
1680 double k = fabs((double)(t - t1) / (t2 - t1));
1681 M = (1.0 - k) * v1m + k * v2m;
1682 A = (1.0 - k) * v1a + k * v2a;
1683 return true;
1684}
1685
1686void GRIBUICtrlBar::OnTimeline(wxScrollEvent &event) {
1687 StopPlayBack();
1688 m_InterpolateMode = m_OverlaySettings.m_bInterpolate;
1689 if (!m_InterpolateMode)
1690 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1691 m_pNowMode = false;
1692 TimelineChanged();
1693}
1694
1695void GRIBUICtrlBar::OnOpenFile(wxCommandEvent &event) {
1696 if (m_tPlayStop.IsRunning())
1697 return; // do nothing when play back is running !
1698
1699#ifndef __OCPN__ANDROID__
1700
1701 wxStandardPathsBase &path = wxStandardPaths::Get();
1702 wxString l_grib_dir = path.GetDocumentsDir();
1703
1704 if (wxDir::Exists(m_grib_dir)) l_grib_dir = m_grib_dir;
1705
1706 wxFileDialog *dialog =
1707 new wxFileDialog(nullptr, _("Select a GRIB file"), l_grib_dir, _T(""),
1708 wxT("Grib files "
1709 "(*.grb;*.bz2;*.gz;*.grib2;*.grb2)|*.grb;*.bz2;*.gz;"
1710 "*.grib2;*.grb2|All files (*)|*.*"),
1711 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE,
1712 wxDefaultPosition, wxDefaultSize, _T("File Dialog"));
1713
1714 if (dialog->ShowModal() == wxID_OK) {
1715 ::wxBeginBusyCursor();
1716
1717 m_grib_dir = dialog->GetDirectory();
1718 dialog->GetPaths(m_file_names);
1719 OpenFile();
1720 if (g_pi) {
1721 if (g_pi->m_bZoomToCenterAtInit) DoZoomToCenter();
1722 }
1723 SetDialogsStyleSizePosition(true);
1724 }
1725 delete dialog;
1726#else
1727 if (!wxDir::Exists(m_grib_dir)) {
1728 wxStandardPathsBase &path = wxStandardPaths::Get();
1729 m_grib_dir = path.GetDocumentsDir();
1730 }
1731
1732 wxString file;
1733 int response = PlatformFileSelectorDialog(
1734 nullptr, &file, _("Select a GRIB file"), m_grib_dir, _T(""), _T("*.*"));
1735
1736 if (response == wxID_OK) {
1737 wxFileName fn(file);
1738 m_grib_dir = fn.GetPath();
1739 m_file_names.Clear();
1740 m_file_names.Add(file);
1741 OpenFile();
1742 SetDialogsStyleSizePosition(true);
1743 }
1744#endif
1745}
1746
1747void GRIBUICtrlBar::CreateActiveFileFromNames(const wxArrayString &filenames) {
1748 if (filenames.GetCount() != 0) {
1749 m_bGRIBActiveFile = nullptr;
1752 }
1753}
1754
1755void GRIBUICtrlBar::PopulateComboDataList() {
1756 int index = 0;
1757 if (m_cRecordForecast->GetCount()) {
1758 index = m_cRecordForecast->GetCurrentSelection();
1759 m_cRecordForecast->Clear();
1760 }
1761
1762 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1763 for (size_t i = 0; i < rsa->GetCount(); i++) {
1764 wxDateTime t(rsa->Item(i).m_Reference_Time);
1765
1766 m_cRecordForecast->Append(TToString(t, pPlugIn->GetTimeZone()));
1767 }
1768 m_cRecordForecast->SetSelection(index);
1769}
1770
1771void GRIBUICtrlBar::OnZoomToCenterClick(wxCommandEvent &event) {
1772 DoZoomToCenter();
1773#if 0
1774 if(!m_pTimelineSet) return;
1775
1776 double latmin,latmax,lonmin,lonmax;
1777 if(!GetGribZoneLimits(m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax ))
1778 return;
1779
1780 //::wxBeginBusyCursor();
1781
1782 //calculate overlay size
1783 double width = lonmax - lonmin;
1784 double height = latmax - latmin;
1785
1786 // Calculate overlay center
1787 double clat = latmin + height / 2;
1788 double clon = lonmin + width / 2;
1789
1790 //try to limit the ppm at a reasonable value
1791 if(width > 120.){
1792 lonmin = clon - 60.;
1793 lonmax = clon + 60.;
1794 }
1795 if(height > 120.){
1796 latmin = clat - 60.;
1797 latmax = clat + 60.;
1798 }
1799
1800
1801 //Calculate overlay width & height in nm (around the center)
1802 double ow, oh;
1803 DistanceBearingMercator_Plugin(clat, lonmin, clat, lonmax, nullptr, &ow );
1804 DistanceBearingMercator_Plugin( latmin, clon, latmax, clon, nullptr, &oh );
1805
1806 //calculate screen size
1807 int w = pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetWidth();
1808 int h = pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetHeight();
1809
1810 //calculate final ppm scale to use
1811 double ppm;
1812 ppm = wxMin(w/(ow*1852), h/(oh*1852)) * ( 100 - fabs( clat ) ) / 90;
1813
1814 ppm = wxMin(ppm, 1.0);
1815
1816 JumpToPosition(clat, clon, ppm);
1817
1818 RequestRefresh( pParent );
1819#endif
1820}
1821
1822void GRIBUICtrlBar::DoZoomToCenter() {
1823 if (!m_pTimelineSet) return;
1824
1825 double latmin, latmax, lonmin, lonmax;
1826 if (!GetGribZoneLimits(m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax))
1827 return;
1828
1829 //::wxBeginBusyCursor();
1830
1831 // calculate overlay size
1832 double width = lonmax - lonmin;
1833 double height = latmax - latmin;
1834
1835 // Calculate overlay center
1836 double clat = latmin + height / 2;
1837 double clon = lonmin + width / 2;
1838
1839 // try to limit the ppm at a reasonable value
1840 if (width > 120.) {
1841 lonmin = clon - 60.;
1842 lonmax = clon + 60.;
1843 }
1844 if (height > 120.) {
1845 latmin = clat - 60.;
1846 latmax = clat + 60.;
1847 }
1848
1849 // Calculate overlay width & height in nm (around the center)
1850 double ow, oh;
1851 DistanceBearingMercator_Plugin(clat, lonmin, clat, lonmax, nullptr, &ow);
1852 DistanceBearingMercator_Plugin(latmin, clon, latmax, clon, nullptr, &oh);
1853
1854 wxWindow *wx = GetGRIBCanvas();
1855 // calculate screen size
1856 int w = wx->GetSize().x;
1857 int h = wx->GetSize().y;
1858
1859 // calculate final ppm scale to use
1860 double ppm;
1861 ppm = wxMin(w / (ow * 1852), h / (oh * 1852)) * (100 - fabs(clat)) / 90;
1862
1863 ppm = wxMin(ppm, 1.0);
1864
1865 CanvasJumpToPosition(wx, clat, clon, ppm);
1866}
1867
1868void GRIBUICtrlBar::OnPrev(wxCommandEvent &event) {
1869 if (m_tPlayStop.IsRunning())
1870 return; // do nothing when play back is running !
1871
1872 RestaureSelectionString();
1873
1874 int selection;
1875 if (m_pNowMode)
1876 selection = GetNearestIndex(GetNow(), 1);
1877 else if (m_InterpolateMode)
1878 selection =
1879 GetNearestIndex(TimelineTime(), 1); /* set to interpolated entry */
1880 else
1881 selection = m_cRecordForecast->GetCurrentSelection();
1882
1883 m_pNowMode = false;
1884 m_InterpolateMode = false;
1885
1886 m_cRecordForecast->SetSelection(selection < 1 ? 0 : selection - 1);
1887
1888 TimelineChanged();
1889}
1890
1891void GRIBUICtrlBar::OnNext(wxCommandEvent &event) {
1892 if (m_tPlayStop.IsRunning())
1893 return; // do nothing when play back is running !
1894
1895 RestaureSelectionString();
1896
1897 int selection;
1898 if (m_pNowMode)
1899 selection = GetNearestIndex(GetNow(), 2);
1900 else if (m_InterpolateMode)
1901 selection =
1902 GetNearestIndex(TimelineTime(), 2); /* set to interpolated entry */
1903 else
1904 selection = m_cRecordForecast->GetCurrentSelection();
1905
1906 m_cRecordForecast->SetSelection(selection);
1907
1908 m_pNowMode = false;
1909 m_InterpolateMode = false;
1910
1911 if (selection == (int)m_cRecordForecast->GetCount() - 1)
1912 return; // end of list
1913
1914 m_cRecordForecast->SetSelection(selection + 1);
1915
1916 TimelineChanged();
1917}
1918
1919void GRIBUICtrlBar::ComputeBestForecastForNow() {
1921 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(nullptr);
1922 return;
1923 }
1924
1925 wxDateTime now = GetNow();
1926
1927 if (m_OverlaySettings.m_bInterpolate)
1928 m_sTimeline->SetValue(GetNearestValue(now, 0));
1929 else {
1930 m_cRecordForecast->SetSelection(GetNearestIndex(now, 0));
1931 m_sTimeline->SetValue(m_cRecordForecast->GetCurrentSelection());
1932 }
1933
1934 if (pPlugIn->GetStartOptions() !=
1935 2) { // no interpolation at start : take the nearest forecast
1936 m_InterpolateMode = m_OverlaySettings.m_bInterpolate;
1937 TimelineChanged();
1938 return;
1939 }
1940 // interpolation on 'now' at start
1941 m_InterpolateMode = true;
1942 m_pNowMode = true;
1943 SetGribTimelineRecordSet(
1944 GetTimeLineRecordSet(now)); // take current time & interpolate forecast
1945
1946 RestaureSelectionString(); // eventually restaure the previousely saved
1947 // wxChoice date time label
1948 m_cRecordForecast->SetSelection(GetNearestIndex(now, 2));
1949 SaveSelectionString(); // memorize the new selected wxChoice date time label
1950 m_cRecordForecast->SetString(
1951 m_Selection_index,
1952 TToString(now,
1953 pPlugIn->GetTimeZone())); // write the now date time label
1954 // in the right place in wxChoice
1955 m_cRecordForecast->SetStringSelection(
1956 TToString(now, pPlugIn->GetTimeZone())); // put it in the box
1957
1958 UpdateTrackingControl();
1959
1960 pPlugIn->SendTimelineMessage(now);
1961 RequestRefresh(GetGRIBCanvas());
1962}
1963
1964void GRIBUICtrlBar::SetGribTimelineRecordSet(
1965 GribTimelineRecordSet *pTimelineSet) {
1966 delete m_pTimelineSet;
1967 m_pTimelineSet = pTimelineSet;
1968
1969 if (!pPlugIn->GetGRIBOverlayFactory()) return;
1970
1971 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(m_pTimelineSet);
1972}
1973
1974void GRIBUICtrlBar::SetTimeLineMax(bool SetValue) {
1975 int oldmax = wxMax(m_sTimeline->GetMax(), 1),
1976 oldval = m_sTimeline->GetValue(); // memorize the old range and value
1977
1978 if (m_OverlaySettings.m_bInterpolate) {
1979 int stepmin =
1980 m_OverlaySettings.GetMinFromIndex(m_OverlaySettings.m_SlicesPerUpdate);
1981 m_sTimeline->SetMax(m_TimeLineHours * 60 / stepmin);
1982 } else {
1984 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1985 m_sTimeline->SetMax(rsa->GetCount() - 1);
1986 }
1987 }
1988 // try to retrieve a coherent timeline value with the new timeline range if it
1989 // has changed
1990 if (SetValue && m_sTimeline->GetMax() != 0) {
1991 if (m_pNowMode)
1992 ComputeBestForecastForNow();
1993 else
1994 m_sTimeline->SetValue(m_sTimeline->GetMax() * oldval / oldmax);
1995 }
1996}
1997
1998void GRIBUICtrlBar::SetFactoryOptions() {
1999 if (m_pTimelineSet) m_pTimelineSet->ClearCachedData();
2000
2001 pPlugIn->GetGRIBOverlayFactory()->ClearCachedData();
2002
2003 UpdateTrackingControl();
2004 RequestRefresh(GetGRIBCanvas());
2005}
2006
2007//----------------------------------------------------------------------------------------------------------
2008// GRIBFile Object Implementation
2009//----------------------------------------------------------------------------------------------------------
2010unsigned int GRIBFile::ID = 0;
2011
2012GRIBFile::GRIBFile(const wxArrayString &file_names, bool CumRec, bool WaveRec,
2013 bool newestFile)
2014 : m_counter(++ID) {
2015 m_bOK = false; // Assume ok until proven otherwise
2016 m_pGribReader = nullptr;
2017 m_last_message = wxEmptyString;
2018 for (unsigned int i = 0; i < file_names.GetCount(); i++) {
2019 wxString file_name = file_names[i];
2020 if (::wxFileExists(file_name)) m_bOK = true;
2021 }
2022
2023 if (m_bOK == false) {
2024 m_last_message = _(" files don't exist!");
2025 return;
2026 }
2027 // Use the zyGrib support classes, as (slightly) modified locally....
2028 m_pGribReader = new GribReader();
2029
2030 // Read and ingest the entire GRIB file.......
2031 m_bOK = false;
2032 wxString file_name;
2033 for (unsigned int i = 0; i < file_names.GetCount(); i++) {
2034 file_name = file_names[i];
2035 m_pGribReader->openFile(file_name);
2036
2037 if (m_pGribReader->isOk()) {
2038 m_bOK = true;
2039 if (newestFile) {
2040 break;
2041 }
2042 }
2043 }
2044 if (m_bOK == false) {
2045 m_last_message = _(" can't be read!");
2046 return;
2047 }
2048
2049 if (newestFile) {
2050 m_FileNames.Clear();
2051 m_FileNames.Add(file_name);
2052 } else {
2053 m_FileNames = file_names;
2054 }
2055
2056 // fixup Accumulation records
2057 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_TOT, LV_GND_SURF, 0);
2058 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_RATE, LV_GND_SURF, 0);
2059 m_pGribReader->computeAccumulationRecords(GRB_CLOUD_TOT, LV_ATMOS_ALL, 0);
2060
2061 if (CumRec)
2062 m_pGribReader->copyFirstCumulativeRecord(); // add missing records if
2063 // option selected
2064 if (WaveRec)
2065 m_pGribReader->copyMissingWaveRecords(); // "" ""
2066
2067 m_nGribRecords = m_pGribReader->getTotalNumberOfGribRecords();
2068
2069 // Walk the GribReader date list to populate our array of GribRecordSets
2070
2071 std::set<time_t>::iterator iter;
2072 std::set<time_t> date_list = m_pGribReader->getListDates();
2073 for (iter = date_list.begin(); iter != date_list.end(); iter++) {
2074 GribRecordSet *t = new GribRecordSet(m_counter);
2075 time_t reftime = *iter;
2076 t->m_Reference_Time = reftime;
2077 m_GribRecordSetArray.Add(t);
2078 }
2079
2080 // Convert from zyGrib organization by data type/level to our organization
2081 // by time.
2082
2083 GribRecord *pRec;
2084 bool isOK(false);
2085 bool polarWind(false);
2086 bool polarCurrent(false);
2087 bool sigWave(false);
2088 bool sigH(false);
2089 // Get the map of GribRecord vectors
2090 std::map<std::string, std::vector<GribRecord *> *> *p_map =
2091 m_pGribReader->getGribMap();
2092
2093 // Iterate over the map to get vectors of related GribRecords
2094 std::map<std::string, std::vector<GribRecord *> *>::iterator it;
2095 for (it = p_map->begin(); it != p_map->end(); it++) {
2096 std::vector<GribRecord *> *ls = (*it).second;
2097 for (zuint i = 0; i < ls->size(); i++) {
2098 pRec = ls->at(i);
2099 isOK = true;
2100 time_t thistime = pRec->getRecordCurrentDate();
2101
2102 // Search the GribRecordSet array for a GribRecordSet with matching time
2103 for (unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2104 if (m_GribRecordSetArray.Item(j).m_Reference_Time == thistime) {
2105 int idx = -1, mdx = -1;
2106 switch (pRec->getDataType()) {
2107 case GRB_WIND_DIR:
2108 polarWind = true;
2109 // fall through
2110 case GRB_WIND_VX:
2111 if (pRec->getLevelType() == LV_ISOBARIC) {
2112 switch (pRec->getLevelValue()) {
2113 case 300:
2114 idx = Idx_WIND_VX300;
2115 break;
2116 case 500:
2117 idx = Idx_WIND_VX500;
2118 break;
2119 case 700:
2120 idx = Idx_WIND_VX700;
2121 break;
2122 case 850:
2123 idx = Idx_WIND_VX850;
2124 break;
2125 }
2126 } else
2127 idx = Idx_WIND_VX;
2128 break;
2129 case GRB_WIND_SPEED:
2130 polarWind = true;
2131 // fall through
2132 case GRB_WIND_VY:
2133 if (pRec->getLevelType() == LV_ISOBARIC) {
2134 switch (pRec->getLevelValue()) {
2135 case 300:
2136 idx = Idx_WIND_VY300;
2137 break;
2138 case 500:
2139 idx = Idx_WIND_VY500;
2140 break;
2141 case 700:
2142 idx = Idx_WIND_VY700;
2143 break;
2144 case 850:
2145 idx = Idx_WIND_VY850;
2146 break;
2147 }
2148 } else
2149 idx = Idx_WIND_VY;
2150 break;
2151 case GRB_CUR_DIR:
2152 polarCurrent = true;
2153 // fall through
2154 case GRB_UOGRD:
2155 idx = Idx_SEACURRENT_VX;
2156 break;
2157 case GRB_CUR_SPEED:
2158 polarCurrent = true;
2159 // fall through
2160 case GRB_VOGRD:
2161 idx = Idx_SEACURRENT_VY;
2162 break;
2163 case GRB_WIND_GUST:
2164 idx = Idx_WIND_GUST;
2165 break;
2166 case GRB_PRESSURE:
2167 idx = Idx_PRESSURE;
2168 break;
2169 case GRB_HTSGW:
2170 sigH = true;
2171 idx = Idx_HTSIGW;
2172 break;
2173 case GRB_PER:
2174 sigWave = true;
2175 idx = Idx_WVPER;
2176 break;
2177 case GRB_DIR:
2178 sigWave = true;
2179 idx = Idx_WVDIR;
2180 break;
2181 case GRB_WVHGT:
2182 idx = Idx_HTSIGW;
2183 break; // Translation from NOAA WW3
2184 case GRB_WVPER:
2185 idx = Idx_WVPER;
2186 break;
2187 case GRB_WVDIR:
2188 idx = Idx_WVDIR;
2189 break;
2190 case GRB_PRECIP_RATE:
2191 case GRB_PRECIP_TOT:
2192 idx = Idx_PRECIP_TOT;
2193 break;
2194 case GRB_CLOUD_TOT:
2195 idx = Idx_CLOUD_TOT;
2196 break;
2197 case GRB_TEMP:
2198 if (pRec->getLevelType() == LV_ISOBARIC) {
2199 switch (pRec->getLevelValue()) {
2200 case 300:
2201 idx = Idx_AIR_TEMP300;
2202 break;
2203 case 500:
2204 idx = Idx_AIR_TEMP500;
2205 break;
2206 case 700:
2207 idx = Idx_AIR_TEMP700;
2208 break;
2209 case 850:
2210 idx = Idx_AIR_TEMP850;
2211 break;
2212 }
2213 } else
2214 idx = Idx_AIR_TEMP;
2215 if (pRec->getDataCenterModel() == NORWAY_METNO)
2216 mdx = 1000 + NORWAY_METNO;
2217 break;
2218 case GRB_WTMP:
2219 idx = Idx_SEA_TEMP;
2220 if (pRec->getDataCenterModel() == NOAA_GFS) mdx = 1000 + NOAA_GFS;
2221 break;
2222 case GRB_CAPE:
2223 idx = Idx_CAPE;
2224 break;
2225 case GRB_COMP_REFL:
2226 idx = Idx_COMP_REFL;
2227 break;
2228 case GRB_HUMID_REL:
2229 if (pRec->getLevelType() == LV_ISOBARIC) {
2230 switch (pRec->getLevelValue()) {
2231 case 300:
2232 idx = Idx_HUMID_RE300;
2233 break;
2234 case 500:
2235 idx = Idx_HUMID_RE500;
2236 break;
2237 case 700:
2238 idx = Idx_HUMID_RE700;
2239 break;
2240 case 850:
2241 idx = Idx_HUMID_RE850;
2242 break;
2243 }
2244 }
2245 break;
2246 case GRB_GEOPOT_HGT:
2247 if (pRec->getLevelType() == LV_ISOBARIC) {
2248 switch (pRec->getLevelValue()) {
2249 case 300:
2250 idx = Idx_GEOP_HGT300;
2251 break;
2252 case 500:
2253 idx = Idx_GEOP_HGT500;
2254 break;
2255 case 700:
2256 idx = Idx_GEOP_HGT700;
2257 break;
2258 case 850:
2259 idx = Idx_GEOP_HGT850;
2260 break;
2261 }
2262 }
2263 break;
2264 }
2265 if (idx == -1) {
2266 // XXX bug ?
2267 break;
2268 }
2269
2270 bool skip = false;
2271
2272 if (m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx]) {
2273 // already one
2274 GribRecord *oRec =
2275 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2276 if (idx == Idx_PRESSURE) {
2277 skip = (oRec->getLevelType() == LV_MSL);
2278 } else {
2279 // we favor UV over DIR/SPEED
2280 if (polarWind) {
2281 if (oRec->getDataType() == GRB_WIND_VY ||
2282 oRec->getDataType() == GRB_WIND_VX)
2283 skip = true;
2284 }
2285 if (polarCurrent) {
2286 if (oRec->getDataType() == GRB_UOGRD ||
2287 oRec->getDataType() == GRB_VOGRD)
2288 skip = true;
2289 }
2290 // favor average aka timeRange == 3 (HRRR subhourly subsets have
2291 // both 3 and 0 records for winds)
2292 if (!skip && (oRec->getTimeRange() == 3)) {
2293 skip = true;
2294 }
2295 // we favor significant Wave other wind wave.
2296 if (sigH) {
2297 if (oRec->getDataType() == GRB_HTSGW) skip = true;
2298 }
2299 if (sigWave) {
2300 if (oRec->getDataType() == GRB_DIR ||
2301 oRec->getDataType() == GRB_PER)
2302 skip = true;
2303 }
2304 }
2305 }
2306 if (!skip) {
2307 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx] = pRec;
2308 if (m_GribIdxArray.Index(idx) == wxNOT_FOUND)
2309 m_GribIdxArray.Add(idx, 1);
2310 if (mdx != -1 && m_GribIdxArray.Index(mdx) == wxNOT_FOUND)
2311 m_GribIdxArray.Add(mdx, 1);
2312 }
2313 break;
2314 }
2315 }
2316 }
2317 }
2318
2319 if (polarWind || polarCurrent) {
2320 for (unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2321 for (unsigned int i = 0; i < Idx_COUNT; i++) {
2322 int idx = -1;
2323 if (polarWind) {
2324 GribRecord *pRec =
2325 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2326
2327 if (pRec != nullptr && pRec->getDataType() == GRB_WIND_DIR) {
2328 switch (i) {
2329 case Idx_WIND_VX300:
2330 idx = Idx_WIND_VY300;
2331 break;
2332 case Idx_WIND_VX500:
2333 idx = Idx_WIND_VY500;
2334 break;
2335 case Idx_WIND_VX700:
2336 idx = Idx_WIND_VY700;
2337 break;
2338 case Idx_WIND_VX850:
2339 idx = Idx_WIND_VY850;
2340 break;
2341 case Idx_WIND_VX:
2342 idx = Idx_WIND_VY;
2343 break;
2344 default:
2345 break;
2346 }
2347 if (idx != -1) {
2348 GribRecord *pRec1 =
2349 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2350 if (pRec1 != nullptr && pRec1->getDataType() == GRB_WIND_SPEED)
2351 GribRecord::Polar2UV(pRec, pRec1);
2352 }
2353 }
2354 }
2355 if (polarCurrent) {
2356 idx = -1;
2357 GribRecord *pRec =
2358 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2359
2360 if (pRec != nullptr && pRec->getDataType() == GRB_CUR_DIR) {
2361 switch (i) {
2362 case Idx_SEACURRENT_VX:
2363 idx = Idx_SEACURRENT_VY;
2364 break;
2365 default:
2366 break;
2367 }
2368 if (idx != -1) {
2369 GribRecord *pRec1 =
2370 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2371 if (pRec1 != nullptr && pRec1->getDataType() == GRB_CUR_SPEED)
2372 GribRecord::Polar2UV(pRec, pRec1);
2373 }
2374 }
2375 }
2376 }
2377 }
2378 }
2379
2380 if (isOK)
2381 m_pRefDateTime =
2382 pRec->getRecordRefDate(); // to ovoid crash with some bad files
2383}
2384
2385GRIBFile::~GRIBFile() { delete m_pGribReader; }
2386
2387//---------------------------------------------------------------------------------------
2388// GRIB Cursor Data Ctrl & Display implementation
2389//---------------------------------------------------------------------------------------
2390GRIBUICData::GRIBUICData(GRIBUICtrlBar &parent)
2391#ifdef __WXOSX__
2392 : GRIBUICDataBase(parent.pParent, CURSOR_DATA, _("GRIB Display Control"),
2393 wxDefaultPosition, wxDefaultSize,
2394 wxSYSTEM_MENU | wxNO_BORDER | wxSTAY_ON_TOP)
2395#else
2396 : GRIBUICDataBase(&parent, CURSOR_DATA, _("GRIB Display Control"),
2397 wxDefaultPosition, wxDefaultSize,
2398 wxSYSTEM_MENU | wxNO_BORDER)
2399#endif
2400 ,
2401 m_gpparent(parent) {
2402 // m_gGrabber = new GribGrabberWin( this );
2403 // fgSizer58->Add( m_gGrabber, 0, wxALL, 0 );
2404
2405 m_gCursorData = new CursorData(this, m_gpparent);
2406 m_fgCdataSizer->Add(m_gCursorData, 0, wxALL, 0);
2407
2408 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICData::OnMove));
2409}
2410
2411void GRIBUICData::OnMove(wxMoveEvent &event) {
2412 int w, h;
2413 GetScreenPosition(&w, &h);
2414 m_gpparent.pPlugIn->SetCursorDataXY(wxPoint(w, h));
2415}
2416
2417//---------------------------------------------------------------------------------------
2418// Android Utility Methods
2419//---------------------------------------------------------------------------------------
2420#ifdef __OCPN__ANDROID__
2421
2422#include <QtAndroidExtras/QAndroidJniObject>
2423
2424bool CheckPendingJNIException() {
2425 if (!java_vm) {
2426 // qDebug() << "java_vm is nullptr.";
2427 return true;
2428 }
2429
2430 JNIEnv *jenv;
2431 if (java_vm->GetEnv((void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2432 // qDebug() << "GetEnv failed.";
2433 return true;
2434 }
2435
2436 if ((jenv)->ExceptionCheck() == JNI_TRUE) {
2437 // qDebug() << "Found JNI Exception Pending.";
2438 return true;
2439 }
2440
2441 return false;
2442}
2443
2444wxString callActivityMethod_ss(const char *method, wxString parm) {
2445 if (!java_vm) {
2446 // qDebug() << "java_vm is nullptr.";
2447 return _T("NOK");
2448 }
2449
2450 if (CheckPendingJNIException()) return _T("NOK");
2451
2452 wxString return_string;
2453 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod(
2454 "org/qtproject/qt5/android/QtNative", "activity",
2455 "()Landroid/app/Activity;");
2456 if (CheckPendingJNIException()) return _T("NOK");
2457
2458 if (!activity.isValid()) {
2459 // qDebug() << "Activity is not valid";
2460 return return_string;
2461 }
2462
2463 // Need a Java environment to decode the resulting string
2464 JNIEnv *jenv;
2465 if (java_vm->GetEnv((void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2466 // qDebug() << "GetEnv failed.";
2467 return "jenv Error";
2468 }
2469
2470 jstring p = (jenv)->NewStringUTF(parm.c_str());
2471
2472 // Call the desired method
2473 // qDebug() << "Calling method_ss";
2474 // qDebug() << method;
2475
2476 QAndroidJniObject data = activity.callObjectMethod(
2477 method, "(Ljava/lang/String;)Ljava/lang/String;", p);
2478 if (CheckPendingJNIException()) return _T("NOK");
2479
2480 // qDebug() << "Back from method_ss";
2481
2482 jstring s = data.object<jstring>();
2483
2484 if ((jenv)->GetStringLength(s)) {
2485 const char *ret_string = (jenv)->GetStringUTFChars(s, nullptr);
2486 return_string = wxString(ret_string, wxConvUTF8);
2487 }
2488
2489 return return_string;
2490}
2491
2492#endif
@ Idx_AIR_TEMP850
Air temperature at 850 hPa.
@ Idx_COMP_REFL
Composite radar reflectivity.
@ Idx_PRECIP_TOT
Total precipitation.
@ Idx_AIR_TEMP
Air temperature at 2m.
@ Idx_PRESSURE
Surface pressure.
@ Idx_WVDIR
Wave direction.
@ Idx_HUMID_RE850
Relative humidity at 850 hPa.
@ Idx_CLOUD_TOT
Total cloud cover.
@ Idx_WIND_GUST
Wind gust speed at surface.
@ Idx_WIND_VX
Surface wind velocity X component.
@ Idx_AIR_TEMP300
Air temperature at 300 hPa.
@ Idx_COUNT
Number of supported GRIB record types.
@ Idx_WIND_VY850
Wind velocity Y component at 850 hPa.
@ Idx_HUMID_RE500
Relative humidity at 500 hPa.
@ Idx_WIND_VX300
Wind velocity X component at 300 hPa.
@ Idx_HUMID_RE300
Relative humidity at 300 hPa.
@ Idx_WIND_VX850
Wind velocity X component at 850 hPa.
@ Idx_WIND_VY300
Wind velocity Y component at 300 hPa.
@ Idx_WVPER
Wave period.
@ Idx_WIND_VX700
Wind velocity X component at 700 hPa.
@ Idx_HTSIGW
Significant wave height.
@ Idx_AIR_TEMP700
Air temperature at 700 hPa.
@ Idx_WIND_VY500
Wind velocity Y component at 500 hPa.
@ Idx_GEOP_HGT500
Geopotential height at 500 hPa.
@ Idx_SEACURRENT_VY
Sea current velocity Y component.
@ Idx_WIND_VX500
Wind velocity X component at 500 hPa.
@ Idx_GEOP_HGT300
Geopotential height at 300 hPa.
@ Idx_GEOP_HGT700
Geopotential height at 700 hPa.
@ Idx_WIND_VY700
Wind velocity Y component at 700 hPa.
@ Idx_SEA_TEMP
Sea surface temperature.
@ Idx_WIND_VY
Surface wind velocity Y component.
@ Idx_GEOP_HGT850
Geopotential height at 850 hPa.
@ Idx_SEACURRENT_VX
Sea current velocity X component.
@ Idx_AIR_TEMP500
Air temperature at 500 hPa.
@ Idx_HUMID_RE700
Relative humidity at 700 hPa.
@ Idx_CAPE
Convective Available Potential Energy.
GRIB Data Table View and Export Interface.
GRIB Weather Data Control Interface.
Tracks and displays GRIB meteorological data at cursor position.
Definition CursorData.h:52
Manages multiple GRIB record sets from one or more GRIB files.
GRIBFile(const wxArrayString &file_names, bool CumRec, bool WaveRec, bool newestFile=false)
Creates a new GRIBFile by parsing one or more GRIB files.
time_t GetRefDateTime(void)
Gets reference datetime of the GRIB data.
ArrayOfGribRecordSets * GetRecordSetArrayPtr(void)
Gets pointer to array of record sets organized by timestamp.
wxArrayString & GetFileNames(void)
Gets the list of source filenames being used.
bool IsOK(void)
Checks if file loading and parsing was successful.
Dialog showing GRIB data in a table format.
Definition GribTable.h:71
void SetTableSizePosition(int vpWidth, int vpHeight)
Set the table size and position relative to viewport.
void InitGribTable(int zone, ArrayOfGribRecordSets *rsa)
Initialize the GRIB data table.
Class GRIBUICDataBase.
Class GRIBUICtrlBarBase.
GribOverlaySettings m_OverlaySettings
Settings that control how GRIB data is displayed and overlaid.
wxTimer m_tPlayStop
Timer for controlling GRIB animation playback.
wxString m_grib_dir
Directory containing GRIB files.
grib_pi * pPlugIn
Plugin instance that owns this control bar.
GRIBFile * m_bGRIBActiveFile
Currently active GRIB file being displayed.
GribTimelineRecordSet * m_pTimelineSet
Current set of GRIB records for timeline playback.
wxArrayString m_file_names
List of GRIB filenames being displayed.
GribTimelineRecordSet * GetTimeLineRecordSet(wxDateTime time)
Retrieves or creates a temporally interpolated GRIB record set for a specific timestamp.
void copyMissingWaveRecords()
Fills gaps in wave-related data fields by propagating known values across missing time periods.
void copyFirstCumulativeRecord()
Initializes cumulative meteorological parameters by copying their first record values.
Manages a collection of GribRecord objects representing multiple meteorological parameters at a singl...
void SetUnRefGribRecord(int i, GribRecord *pGR)
Sets a GRIB record that this set owns and will be responsible for deleting.
GribRecord * m_GribRecordPtrArray[Idx_COUNT]
Array of pointers to GRIB records representing different meteorological parameters.
time_t m_Reference_Time
Reference time for this set of records.
Represents a meteorological data grid from a GRIB (Gridded Binary) file.
Definition GribRecord.h:182
zuchar getTimeRange() const
Returns the time range indicator that defines how P1 and P2 should be interpreted.
Definition GribRecord.h:440
static void Polar2UV(GribRecord *pDIR, GribRecord *pSPEED)
Converts wind or current values from polar (direction/speed) to cartesian (U/V) components.
static GribRecord * Interpolated2DRecord(GribRecord *&rety, const GribRecord &rec1x, const GribRecord &rec1y, const GribRecord &rec2x, const GribRecord &rec2y, double d)
Creates temporally interpolated records for vector fields (wind, currents).
zuint getDataCenterModel() const
Returns the numerical weather prediction model/center that produced this data.
Definition GribRecord.h:348
double getInterpolatedValue(double px, double py, bool numericalInterpolation=true, bool dir=false) const
Get spatially interpolated value at exact lat/lon position.
static GribRecord * InterpolatedRecord(const GribRecord &rec1, const GribRecord &rec2, double d, bool dir=false)
Creates a new GribRecord by temporally interpolating between two time points.
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.
zuint getLevelValue() const
Returns the numeric value associated with the level type.
Definition GribRecord.h:328
zuchar getLevelType() const
Returns the type of vertical level for this grid's data.
Definition GribRecord.h:315
zuchar getDataType() const
Returns the type of meteorological parameter stored in this grid.
Definition GribRecord.h:298
Manages GRIB file request configuration and downloads.
A specialized GribRecordSet that represents temporally interpolated weather data with isobar renderin...
wxArrayPtrVoid * m_IsobarArray[Idx_COUNT]
Array of cached isobar calculations for each data type (wind, pressure, etc).
GribTimelineRecordSet(unsigned int cnt)
Creates a timeline record set containing temporally interpolated GRIB records.
bool GetCopyFirstCumRec()
Returns true if cumulative parameters like precipitation and cloud cover should initialize their star...
Definition grib_pi.h:148
bool GetCopyMissWaveRec()
Returns true if wave data should be propagated across time periods where wave records are missing.
Definition grib_pi.h:156
The JSON parser.
Definition jsonreader.h:50
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
The JSON document writer.
Definition jsonwriter.h:50
void Write(const wxJSONValue &value, wxString &str)
Write the JSONvalue object to a JSON text.
Email Request System for GRIB Data.
GRIB Weather Data Plugin for OpenCPN.
wxFont * FindOrCreateFont_PlugIn(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline, const wxString &facename, wxFontEncoding encoding)
Creates or finds a font in the font cache.
OpenGL Platform Abstraction Layer.