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