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