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->OnVpWithFocusChange(m_vpMouse);
1127 pReq_Dialog->SetRequestDialogSize();
1128 if (::wxIsBusy()) ::wxEndBusyCursor();
1129}
1130
1131void GRIBUICtrlBar::OnRequestForecastData(wxCommandEvent &event) {
1132 if (m_tPlayStop.IsRunning())
1133 return; // do nothing when play back is running !
1134
1135 /*if there is one instance of the dialog already visible, do nothing*/
1136 if (pReq_Dialog && pReq_Dialog->IsShown()) return;
1137
1138 /*create new request dialog*/
1139 createRequestDialog();
1140 // need to set a position at start
1141 int w;
1142 ::wxDisplaySize(&w, nullptr);
1143 pReq_Dialog->Move((w - pReq_Dialog->GetSize().GetX()) / 2, 30);
1144 pReq_Dialog->Show();
1145
1146 SetRequestButtonBitmap(m_ZoneSelMode); // set appopriate bitmap
1147}
1148
1149void GRIBUICtrlBar::OnSettings(wxCommandEvent &event) {
1150 if (m_tPlayStop.IsRunning())
1151 return; // do nothing when play back is running !
1152
1153 ::wxBeginBusyCursor();
1154
1157 *this, m_OverlaySettings, m_lastdatatype, m_FileIntervalIndex);
1158 // set font
1159 pPlugIn->SetDialogFont(dialog);
1160 for (size_t i = 0; i < dialog->m_nSettingsBook->GetPageCount(); i++) {
1161 wxScrolledWindow *sc =
1162 ((wxScrolledWindow *)dialog->m_nSettingsBook->GetPage(i));
1163 pPlugIn->SetDialogFont(sc);
1164 } // end set font
1165
1166 dialog->m_nSettingsBook->ChangeSelection(dialog->GetPageIndex());
1167 dialog->SetSettingsDialogSize();
1168 // need to set a position at start
1169 int w;
1170 ::wxDisplaySize(&w, nullptr);
1171 dialog->Move((w - dialog->GetSize().GetX()) / 2, 30);
1172 // end set position
1173
1174 ::wxEndBusyCursor();
1175
1176 if (dialog->ShowModal() == wxID_OK) {
1177 dialog->WriteSettings();
1178 m_OverlaySettings.Write();
1179 if (m_OverlaySettings.Settings[GribOverlaySettings::WIND].m_Units !=
1180 initSettings.Settings[GribOverlaySettings::WIND].m_Units &&
1181 (m_OverlaySettings.Settings[GribOverlaySettings::WIND].m_Units ==
1182 GribOverlaySettings::BFS ||
1183 initSettings.Settings[GribOverlaySettings::WIND].m_Units ==
1184 GribOverlaySettings::BFS))
1185 m_old_DialogStyle =
1186 STARTING_STATE_STYLE; // must recompute dialogs size if wind unit
1187 // have been changed
1188 } else {
1189 m_OverlaySettings = initSettings;
1190 m_DialogStyle = initSettings.m_iCtrlandDataStyle;
1191 }
1192 ::wxBeginBusyCursor();
1193
1194 dialog->SaveLastPage();
1195 if (!m_OverlaySettings.m_bInterpolate)
1196 m_InterpolateMode = false; // Interpolate could have been unchecked
1197 SetTimeLineMax(true);
1198 SetFactoryOptions();
1199
1200 SetDialogsStyleSizePosition(true);
1201 delete dialog;
1202
1203 event.Skip();
1204}
1205
1206#ifdef __OCPN__ANDROID__
1207wxString callActivityMethod_ss(const char *method, wxString parm);
1208#endif
1209
1210void GRIBUICtrlBar::OnCompositeDialog(wxCommandEvent &event) {
1211 // Grab the current settings values
1213 initSettings.Read();
1214
1215 wxString json;
1216 wxString json_begin = initSettings.SettingsToJSON(json);
1217 wxLogMessage(json_begin);
1218
1219 // Pick up the required options from the Request dialog
1220 // and add them to the JSON object
1221 // Really, this just means the current viewport coordinates.
1222 // Everything else is stored in Android app preferences bundle.
1223
1224 PlugIn_ViewPort current_vp = pPlugIn->GetCurrentViewPort();
1225
1226 double lon_min = wxRound(current_vp.lon_min) - 1;
1227 double lon_max = wxRound(current_vp.lon_max) + 1;
1228 double lat_min = wxRound(current_vp.lat_min) - 1;
1229 double lat_max = wxRound(current_vp.lat_max) + 1;
1230
1231 wxJSONValue v;
1232 wxJSONReader reader;
1233 int numErrors = reader.Parse(json_begin, &v);
1234 if (numErrors > 0) {
1235 return;
1236 }
1237
1238 v[_T("latMin")] = lat_min;
1239 v[_T("latMax")] = lat_max;
1240 v[_T("lonMin")] = lon_min;
1241 v[_T("lonMax")] = lon_max;
1242
1243 // Clear the file name field, so that a retrieved or selected file name can
1244 // be returned
1245 v[_T("grib_file")] = _T("");
1246
1247 wxJSONWriter w;
1248 wxString json_final;
1249 w.Write(v, json_final);
1250 wxLogMessage(json_final);
1251
1252#ifdef __OCPN__ANDROID__
1253 wxString ret = callActivityMethod_ss("doGRIBActivity", json_final);
1254 wxLogMessage(ret);
1255#endif
1256
1257 event.Skip();
1258}
1259
1260void GRIBUICtrlBar::OpenFileFromJSON(wxString json) {
1261 // construct the JSON root object
1262 wxJSONValue root;
1263 // construct a JSON parser
1264 wxJSONReader reader;
1265
1266 int numErrors = reader.Parse(json, &root);
1267 if (numErrors > 0) {
1268 return;
1269 }
1270
1271 wxString file = root[(_T("grib_file"))].AsString();
1272
1273 if (file.Length() && wxFileExists(file)) {
1274 wxFileName fn(file);
1275 m_grib_dir = fn.GetPath();
1276 m_file_names.Clear();
1277 m_file_names.Add(file);
1278 OpenFile();
1279 }
1280}
1281
1282void GRIBUICtrlBar::OnPlayStop(wxCommandEvent &event) {
1283 if (m_tPlayStop.IsRunning()) {
1284 StopPlayBack();
1285 } else {
1286 m_bpPlay->SetBitmapLabel(
1287 GetScaledBitmap(wxBitmap(stop), _T("stop"), m_ScaledFactor));
1288 m_bpPlay->SetToolTip(_("Stop play back"));
1289 m_tPlayStop.Start(3000 / m_OverlaySettings.m_UpdatesPerSecond,
1290 wxTIMER_CONTINUOUS);
1291 m_InterpolateMode = m_OverlaySettings.m_bInterpolate;
1292 }
1293}
1294
1295void GRIBUICtrlBar::OnPlayStopTimer(wxTimerEvent &event) {
1296 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax()) {
1297 if (m_OverlaySettings.m_bLoopMode) {
1298 if (m_OverlaySettings.m_LoopStartPoint) {
1299 ComputeBestForecastForNow();
1300 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax())
1301 StopPlayBack(); // will stop playback
1302 return;
1303 } else
1304 m_sTimeline->SetValue(0);
1305 } else {
1306 StopPlayBack(); // will stop playback
1307 return;
1308 }
1309 } else {
1310 int value = m_pNowMode ? m_OverlaySettings.m_bInterpolate
1311 ? GetNearestValue(GetNow(), 1)
1312 : GetNearestIndex(GetNow(), 2)
1313 : m_sTimeline->GetValue();
1314 m_sTimeline->SetValue(value + 1);
1315 }
1316
1317 m_pNowMode = false;
1318 if (!m_InterpolateMode)
1319 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1320 TimelineChanged();
1321}
1322
1323void GRIBUICtrlBar::StopPlayBack() {
1324 if (m_tPlayStop.IsRunning()) {
1325 m_tPlayStop.Stop();
1326 m_bpPlay->SetBitmapLabel(
1327 GetScaledBitmap(wxBitmap(play), _T("play"), m_ScaledFactor));
1328 m_bpPlay->SetToolTip(_("Start play back"));
1329 }
1330}
1331
1332void GRIBUICtrlBar::TimelineChanged() {
1334 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(nullptr);
1335 return;
1336 }
1337
1338 RestaureSelectionString(); // eventually restaure the previousely saved time
1339 // label
1340
1341 wxDateTime time = TimelineTime();
1342 SetGribTimelineRecordSet(GetTimeLineRecordSet(time));
1343
1344 if (!m_InterpolateMode) {
1345 /* get closest value to update timeline */
1346 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1347 GribRecordSet &sel = rsa->Item(m_cRecordForecast->GetCurrentSelection());
1348 wxDateTime t = sel.m_Reference_Time;
1349 m_sTimeline->SetValue(m_OverlaySettings.m_bInterpolate
1350 ? wxTimeSpan(t - MinTime()).GetMinutes() /
1351 m_OverlaySettings.GetMinFromIndex(
1352 m_OverlaySettings.m_SlicesPerUpdate)
1353 : m_cRecordForecast->GetCurrentSelection());
1354 } else {
1355 m_cRecordForecast->SetSelection(GetNearestIndex(time, 2));
1356 SaveSelectionString(); // memorize index and label
1357 wxString formattedTime = toUsrDateTimeFormat_Plugin(time);
1358 m_cRecordForecast->SetString(m_Selection_index,
1359 formattedTime); // replace it by the
1360 // interpolated time label
1361 m_cRecordForecast->SetStringSelection(
1362 formattedTime); // ensure it's visible in the box
1363 }
1364
1366
1367 pPlugIn->SendTimelineMessage(time);
1368 RequestRefresh(GetGRIBCanvas());
1369}
1370
1371void GRIBUICtrlBar::RestaureSelectionString() {
1372 if (!m_SelectionIsSaved) return;
1373
1374 int sel = m_cRecordForecast->GetSelection();
1375 m_cRecordForecast->SetString(m_Selection_index, m_Selection_label);
1376 m_cRecordForecast->SetSelection(sel);
1377 m_SelectionIsSaved = false;
1378}
1379
1380int GRIBUICtrlBar::GetNearestIndex(wxDateTime time, int model) {
1381 /* get closest index to update combo box */
1382 size_t i;
1383 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1384
1385 wxDateTime itime, ip1time;
1386 for (i = 0; i < rsa->GetCount() - 1; i++) {
1387 itime = rsa->Item(i).m_Reference_Time;
1388 ip1time = rsa->Item(i + 1).m_Reference_Time;
1389 if (ip1time >= time) break;
1390 }
1391 if (!model) return (time - itime > (ip1time - time) * 3) ? i + 1 : i;
1392
1393 return model == 1 ? time == ip1time ? i : i + 1 : time == ip1time ? i + 1 : i;
1394}
1395
1396int GRIBUICtrlBar::GetNearestValue(wxDateTime time, int model) {
1397 /* get closest value to update Time line */
1398 if (m_TimeLineHours == 0) return 0;
1399 wxDateTime itime, ip1time;
1400 int stepmin =
1401 m_OverlaySettings.GetMinFromIndex(m_OverlaySettings.m_SlicesPerUpdate);
1402 wxTimeSpan span = time - MinTime();
1403 int t = span.GetMinutes() / stepmin;
1404 itime = MinTime() +
1405 wxTimeSpan(t * stepmin / 60, (t * stepmin) % 60); // time at t
1406 ip1time = itime + wxTimeSpan(stepmin / 60, stepmin % 60); // time at t+1
1407
1408 if (model == 1) return time == ip1time ? t + 1 : t;
1409
1410 return (time - itime > (ip1time - time) * 3) ? t + 1 : t;
1411}
1412
1413wxDateTime GRIBUICtrlBar::GetNow() {
1414 wxDateTime now = wxDateTime::Now();
1415 now.GetSecond(0);
1416
1417 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1418
1419 // Verify if we are outside of the file time range
1420 now = (now > rsa->Item(rsa->GetCount() - 1).m_Reference_Time)
1421 ? rsa->Item(rsa->GetCount() - 1).m_Reference_Time
1422 : (now < rsa->Item(0).m_Reference_Time) ? rsa->Item(0).m_Reference_Time
1423 : now;
1424 return now;
1425}
1426
1428 if (m_InterpolateMode) {
1429 int tl = (m_TimeLineHours == 0) ? 0 : m_sTimeline->GetValue();
1430 int stepmin =
1431 m_OverlaySettings.GetMinFromIndex(m_OverlaySettings.m_SlicesPerUpdate);
1432 return MinTime() + wxTimeSpan(tl * stepmin / 60, (tl * stepmin) % 60);
1433 }
1434
1435 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1436 unsigned int index = m_cRecordForecast->GetCurrentSelection() < 1
1437 ? 0
1438 : m_cRecordForecast->GetCurrentSelection();
1439 if (rsa && index < rsa->GetCount()) return rsa->Item(index).m_Reference_Time;
1440
1441 return wxDateTime::Now();
1442}
1443
1444wxDateTime GRIBUICtrlBar::MinTime() {
1445 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1446 if (rsa && rsa->GetCount()) {
1447 GribRecordSet &first = rsa->Item(0);
1448 return first.m_Reference_Time;
1449 }
1450 return wxDateTime::Now();
1451}
1452
1454 if (m_bGRIBActiveFile == nullptr) return nullptr;
1455 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1456
1457 if (rsa->GetCount() == 0) return nullptr;
1458
1460 new GribTimelineRecordSet(m_bGRIBActiveFile->GetCounter());
1461 for (int i = 0; i < Idx_COUNT; i++) {
1462 GribRecordSet *GRS1 = nullptr, *GRS2 = nullptr;
1463 GribRecord *GR1 = nullptr, *GR2 = nullptr;
1464 wxDateTime GR1time, GR2time;
1465
1466 // already computed using polar interpolation from first axis
1467 if (set->m_GribRecordPtrArray[i]) continue;
1468
1469 unsigned int j;
1470 for (j = 0; j < rsa->GetCount(); j++) {
1471 GribRecordSet *GRS = &rsa->Item(j);
1472 GribRecord *GR = GRS->m_GribRecordPtrArray[i];
1473 if (!GR) continue;
1474
1475 wxDateTime curtime = GRS->m_Reference_Time;
1476 if (curtime <= time) GR1time = curtime, GRS1 = GRS, GR1 = GR;
1477
1478 if (curtime >= time) {
1479 GR2time = curtime, GRS2 = GRS, GR2 = GR;
1480 break;
1481 }
1482 }
1483
1484 if (!GR1 || !GR2) continue;
1485
1486 wxDateTime mintime = MinTime();
1487 double minute2 = (GR2time - mintime).GetMinutes();
1488 double minute1 = (GR1time - mintime).GetMinutes();
1489 double nminute = (time - mintime).GetMinutes();
1490
1491 if (minute2 < minute1 || nminute < minute1 || nminute > minute2) continue;
1492
1493 double interp_const;
1494 if (minute1 == minute2) {
1495 // with big grib a copy is slow use a reference.
1496 set->m_GribRecordPtrArray[i] = GR1;
1497 continue;
1498 } else
1499 interp_const = (nminute - minute1) / (minute2 - minute1);
1500
1501 /* if this is a vector interpolation use the 2d method */
1502 if (i < Idx_WIND_VY) {
1503 GribRecord *GR1y = GRS1->m_GribRecordPtrArray[i + Idx_WIND_VY];
1504 GribRecord *GR2y = GRS2->m_GribRecordPtrArray[i + Idx_WIND_VY];
1505 if (GR1y && GR2y) {
1506 GribRecord *Ry;
1507 set->SetUnRefGribRecord(
1508 i, GribRecord::Interpolated2DRecord(Ry, *GR1, *GR1y, *GR2, *GR2y,
1509 interp_const));
1510 set->SetUnRefGribRecord(i + Idx_WIND_VY, Ry);
1511 continue;
1512 }
1513 } else if (i <= Idx_WIND_VY300)
1514 continue;
1515 else if (i == Idx_SEACURRENT_VX) {
1517 GribRecord *GR2y = GRS2->m_GribRecordPtrArray[Idx_SEACURRENT_VY];
1518 if (GR1y && GR2y) {
1519 GribRecord *Ry;
1520 set->SetUnRefGribRecord(
1521 i, GribRecord::Interpolated2DRecord(Ry, *GR1, *GR1y, *GR2, *GR2y,
1522 interp_const));
1524 continue;
1525 }
1526 } else if (i == Idx_SEACURRENT_VY)
1527 continue;
1528
1530 *GR1, *GR2, interp_const, i == Idx_WVDIR));
1531 }
1532
1533 set->m_Reference_Time = time.GetTicks();
1534 //(1-interp_const)*GRS1.m_Reference_Time + interp_const*GRS2.m_Reference_Time;
1535 return set;
1536}
1537
1539 wxPoint p(0, 0);
1540 auto now = TimelineTime();
1541 auto sog = m_ProjectBoatPanel->GetSpeed();
1542 auto cog = m_ProjectBoatPanel->GetCourse();
1543 double dist =
1544 static_cast<double>(now.GetTicks() - pPlugIn->m_boat_time) * sog / 3600.0;
1546 pPlugIn->m_boat_lon, cog, dist,
1547 &m_projected_lat, &m_projected_lon);
1548 if (vp) {
1549 GetCanvasPixLL(vp, &p, m_projected_lat, m_projected_lon);
1550 }
1551 x = p.x;
1552 y = p.y;
1553}
1554
1555double GRIBUICtrlBar::getTimeInterpolatedValue(int idx, double lon, double lat,
1556 wxDateTime time) {
1557 if (m_bGRIBActiveFile == nullptr) return GRIB_NOTDEF;
1558 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1559
1560 if (rsa->GetCount() == 0) return GRIB_NOTDEF;
1561
1562 GribRecord *before = nullptr, *after = nullptr;
1563
1564 unsigned int j;
1565 time_t t = time.GetTicks();
1566 for (j = 0; j < rsa->GetCount(); j++) {
1567 GribRecordSet *GRS = &rsa->Item(j);
1568 GribRecord *GR = GRS->m_GribRecordPtrArray[idx];
1569 if (!GR) continue;
1570
1571 time_t curtime = GR->getRecordCurrentDate();
1572 if (curtime == t) return GR->getInterpolatedValue(lon, lat);
1573
1574 if (curtime < t) before = GR;
1575
1576 if (curtime > t) {
1577 after = GR;
1578 break;
1579 }
1580 }
1581 // time_t wxDateTime::GetTicks();
1582 if (!before || !after) return GRIB_NOTDEF;
1583
1584 time_t t1 = before->getRecordCurrentDate();
1585 time_t t2 = after->getRecordCurrentDate();
1586 if (t1 == t2) return before->getInterpolatedValue(lon, lat);
1587
1588 double v1 = before->getInterpolatedValue(lon, lat);
1589 double v2 = after->getInterpolatedValue(lon, lat);
1590 if (v1 != GRIB_NOTDEF && v2 != GRIB_NOTDEF) {
1591 double k = fabs((double)(t - t1) / (t2 - t1));
1592 return (1.0 - k) * v1 + k * v2;
1593 }
1594
1595 return GRIB_NOTDEF;
1596}
1597
1598bool GRIBUICtrlBar::getTimeInterpolatedValues(double &M, double &A, int idx1,
1599 int idx2, double lon, double lat,
1600 wxDateTime time) {
1601 M = GRIB_NOTDEF;
1602 A = GRIB_NOTDEF;
1603
1604 if (m_bGRIBActiveFile == nullptr) return false;
1605 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1606
1607 if (rsa->GetCount() == 0) return false;
1608
1609 GribRecord *beforeX = nullptr, *afterX = nullptr;
1610 GribRecord *beforeY = nullptr, *afterY = nullptr;
1611
1612 unsigned int j;
1613 time_t t = time.GetTicks();
1614 for (j = 0; j < rsa->GetCount(); j++) {
1615 GribRecordSet *GRS = &rsa->Item(j);
1616 GribRecord *GX = GRS->m_GribRecordPtrArray[idx1];
1617 GribRecord *GY = GRS->m_GribRecordPtrArray[idx2];
1618 if (!GX || !GY) continue;
1619
1620 time_t curtime = GX->getRecordCurrentDate();
1621 if (curtime == t) {
1622 return GribRecord::getInterpolatedValues(M, A, GX, GY, lon, lat, true);
1623 }
1624 if (curtime < t) {
1625 beforeX = GX;
1626 beforeY = GY;
1627 }
1628 if (curtime > t) {
1629 afterX = GX;
1630 afterY = GY;
1631 break;
1632 }
1633 }
1634 // time_t wxDateTime::GetTicks();
1635 if (!beforeX || !afterX) return false;
1636
1637 time_t t1 = beforeX->getRecordCurrentDate();
1638 time_t t2 = afterX->getRecordCurrentDate();
1639 if (t1 == t2) {
1640 return GribRecord::getInterpolatedValues(M, A, beforeX, beforeY, lon, lat,
1641 true);
1642 }
1643 double v1m, v2m, v1a, v2a;
1644 if (!GribRecord::getInterpolatedValues(v1m, v1a, beforeX, beforeY, lon, lat,
1645 true))
1646 return false;
1647
1648 if (!GribRecord::getInterpolatedValues(v2m, v2a, afterX, afterY, lon, lat,
1649 true))
1650 return false;
1651
1652 if (v1m == GRIB_NOTDEF || v2m == GRIB_NOTDEF || v1a == GRIB_NOTDEF ||
1653 v2a == GRIB_NOTDEF)
1654 return false;
1655
1656 double k = fabs((double)(t - t1) / (t2 - t1));
1657 M = (1.0 - k) * v1m + k * v2m;
1658 A = (1.0 - k) * v1a + k * v2a;
1659 return true;
1660}
1661
1662void GRIBUICtrlBar::OnTimeline(wxScrollEvent &event) {
1663 StopPlayBack();
1664 m_InterpolateMode = m_OverlaySettings.m_bInterpolate;
1665 if (!m_InterpolateMode)
1666 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1667 m_pNowMode = false;
1668 TimelineChanged();
1669}
1670
1671void GRIBUICtrlBar::OnOpenFile(wxCommandEvent &event) {
1672 if (m_tPlayStop.IsRunning())
1673 return; // do nothing when play back is running !
1674
1675#ifndef __OCPN__ANDROID__
1676
1677 wxStandardPathsBase &path = wxStandardPaths::Get();
1678 wxString l_grib_dir = path.GetDocumentsDir();
1679
1680 if (wxDir::Exists(m_grib_dir)) l_grib_dir = m_grib_dir;
1681
1682 wxFileDialog *dialog =
1683 new wxFileDialog(nullptr, _("Select a GRIB file"), l_grib_dir, _T(""),
1684 wxT("Grib files "
1685 "(*.grb;*.bz2;*.gz;*.grib2;*.grb2)|*.grb;*.bz2;*.gz;"
1686 "*.grib2;*.grb2|All files (*)|*.*"),
1687 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE,
1688 wxDefaultPosition, wxDefaultSize, _T("File Dialog"));
1689
1690 if (dialog->ShowModal() == wxID_OK) {
1691 ::wxBeginBusyCursor();
1692
1693 m_grib_dir = dialog->GetDirectory();
1694 dialog->GetPaths(m_file_names);
1695 OpenFile();
1696 if (g_pi) {
1697 if (g_pi->m_bZoomToCenterAtInit) DoZoomToCenter();
1698 }
1699 SetDialogsStyleSizePosition(true);
1700 }
1701 delete dialog;
1702#else
1703 if (!wxDir::Exists(m_grib_dir)) {
1704 wxStandardPathsBase &path = wxStandardPaths::Get();
1705 m_grib_dir = path.GetDocumentsDir();
1706 }
1707
1708 wxString file;
1709 int response = PlatformFileSelectorDialog(
1710 nullptr, &file, _("Select a GRIB file"), m_grib_dir, _T(""), _T("*.*"));
1711
1712 if (response == wxID_OK) {
1713 wxFileName fn(file);
1714 m_grib_dir = fn.GetPath();
1715 m_file_names.Clear();
1716 m_file_names.Add(file);
1717 OpenFile();
1718 SetDialogsStyleSizePosition(true);
1719 }
1720#endif
1721}
1722
1723void GRIBUICtrlBar::CreateActiveFileFromNames(const wxArrayString &filenames) {
1724 if (filenames.GetCount() != 0) {
1725 m_bGRIBActiveFile = nullptr;
1728 }
1729}
1730
1731void GRIBUICtrlBar::PopulateComboDataList() {
1732 int index = 0;
1733 if (m_cRecordForecast->GetCount()) {
1734 index = m_cRecordForecast->GetCurrentSelection();
1735 m_cRecordForecast->Clear();
1736 }
1737
1738 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1739 for (size_t i = 0; i < rsa->GetCount(); i++) {
1740 wxDateTime t(rsa->Item(i).m_Reference_Time);
1741 m_cRecordForecast->Append(toUsrDateTimeFormat_Plugin(t));
1742 }
1743 m_cRecordForecast->SetSelection(index);
1744}
1745
1746void GRIBUICtrlBar::OnZoomToCenterClick(wxCommandEvent &event) {
1747 DoZoomToCenter();
1748#if 0
1749 if(!m_pTimelineSet) return;
1750
1751 double latmin,latmax,lonmin,lonmax;
1752 if(!GetGribZoneLimits(m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax ))
1753 return;
1754
1755 //::wxBeginBusyCursor();
1756
1757 //calculate overlay size
1758 double width = lonmax - lonmin;
1759 double height = latmax - latmin;
1760
1761 // Calculate overlay center
1762 double clat = latmin + height / 2;
1763 double clon = lonmin + width / 2;
1764
1765 //try to limit the ppm at a reasonable value
1766 if(width > 120.){
1767 lonmin = clon - 60.;
1768 lonmax = clon + 60.;
1769 }
1770 if(height > 120.){
1771 latmin = clat - 60.;
1772 latmax = clat + 60.;
1773 }
1774
1775
1776 //Calculate overlay width & height in nm (around the center)
1777 double ow, oh;
1778 DistanceBearingMercator_Plugin(clat, lonmin, clat, lonmax, nullptr, &ow );
1779 DistanceBearingMercator_Plugin( latmin, clon, latmax, clon, nullptr, &oh );
1780
1781 //calculate screen size
1782 int w = pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetWidth();
1783 int h = pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetHeight();
1784
1785 //calculate final ppm scale to use
1786 double ppm;
1787 ppm = wxMin(w/(ow*1852), h/(oh*1852)) * ( 100 - fabs( clat ) ) / 90;
1788
1789 ppm = wxMin(ppm, 1.0);
1790
1791 JumpToPosition(clat, clon, ppm);
1792
1793 RequestRefresh( pParent );
1794#endif
1795}
1796
1797void GRIBUICtrlBar::DoZoomToCenter() {
1798 if (!m_pTimelineSet) return;
1799
1800 double latmin, latmax, lonmin, lonmax;
1801 if (!GetGribZoneLimits(m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax))
1802 return;
1803
1804 //::wxBeginBusyCursor();
1805
1806 // calculate overlay size
1807 double width = lonmax - lonmin;
1808 double height = latmax - latmin;
1809
1810 // Calculate overlay center
1811 double clat = latmin + height / 2;
1812 double clon = lonmin + width / 2;
1813
1814 // try to limit the ppm at a reasonable value
1815 if (width > 120.) {
1816 lonmin = clon - 60.;
1817 lonmax = clon + 60.;
1818 }
1819 if (height > 120.) {
1820 latmin = clat - 60.;
1821 latmax = clat + 60.;
1822 }
1823
1824 // Calculate overlay width & height in nm (around the center)
1825 double ow, oh;
1826 DistanceBearingMercator_Plugin(clat, lonmin, clat, lonmax, nullptr, &ow);
1827 DistanceBearingMercator_Plugin(latmin, clon, latmax, clon, nullptr, &oh);
1828
1829 wxWindow *wx = GetGRIBCanvas();
1830 // calculate screen size
1831 int w = wx->GetSize().x;
1832 int h = wx->GetSize().y;
1833
1834 // calculate final ppm scale to use
1835 double ppm;
1836 ppm = wxMin(w / (ow * 1852), h / (oh * 1852)) * (100 - fabs(clat)) / 90;
1837
1838 ppm = wxMin(ppm, 1.0);
1839
1840 CanvasJumpToPosition(wx, clat, clon, ppm);
1841}
1842
1843void GRIBUICtrlBar::OnPrev(wxCommandEvent &event) {
1844 if (m_tPlayStop.IsRunning())
1845 return; // do nothing when play back is running !
1846
1847 RestaureSelectionString();
1848
1849 int selection;
1850 if (m_pNowMode)
1851 selection = GetNearestIndex(GetNow(), 1);
1852 else if (m_InterpolateMode)
1853 selection =
1854 GetNearestIndex(TimelineTime(), 1); /* set to interpolated entry */
1855 else
1856 selection = m_cRecordForecast->GetCurrentSelection();
1857
1858 m_pNowMode = false;
1859 m_InterpolateMode = false;
1860
1861 m_cRecordForecast->SetSelection(selection < 1 ? 0 : selection - 1);
1862
1863 TimelineChanged();
1864}
1865
1866void GRIBUICtrlBar::OnNext(wxCommandEvent &event) {
1867 if (m_tPlayStop.IsRunning())
1868 return; // do nothing when play back is running !
1869
1870 RestaureSelectionString();
1871
1872 int selection;
1873 if (m_pNowMode)
1874 selection = GetNearestIndex(GetNow(), 2);
1875 else if (m_InterpolateMode)
1876 selection =
1877 GetNearestIndex(TimelineTime(), 2); /* set to interpolated entry */
1878 else
1879 selection = m_cRecordForecast->GetCurrentSelection();
1880
1881 m_cRecordForecast->SetSelection(selection);
1882
1883 m_pNowMode = false;
1884 m_InterpolateMode = false;
1885
1886 if (selection == (int)m_cRecordForecast->GetCount() - 1)
1887 return; // end of list
1888
1889 m_cRecordForecast->SetSelection(selection + 1);
1890
1891 TimelineChanged();
1892}
1893
1894void GRIBUICtrlBar::ComputeBestForecastForNow() {
1896 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(nullptr);
1897 return;
1898 }
1899
1900 wxDateTime now = GetNow();
1901
1902 if (m_OverlaySettings.m_bInterpolate)
1903 m_sTimeline->SetValue(GetNearestValue(now, 0));
1904 else {
1905 m_cRecordForecast->SetSelection(GetNearestIndex(now, 0));
1906 m_sTimeline->SetValue(m_cRecordForecast->GetCurrentSelection());
1907 }
1908
1909 if (pPlugIn->GetStartOptions() !=
1910 2) { // no interpolation at start : take the nearest forecast
1911 m_InterpolateMode = m_OverlaySettings.m_bInterpolate;
1912 TimelineChanged();
1913 return;
1914 }
1915 // interpolation on 'now' at start
1916 m_InterpolateMode = true;
1917 m_pNowMode = true;
1918 SetGribTimelineRecordSet(
1919 GetTimeLineRecordSet(now)); // take current time & interpolate forecast
1920
1921 RestaureSelectionString(); // eventually restaure the previousely saved
1922 // wxChoice date time label
1923 m_cRecordForecast->SetSelection(GetNearestIndex(now, 2));
1924 SaveSelectionString(); // memorize the new selected wxChoice date time label
1925 wxString nowTime = toUsrDateTimeFormat_Plugin(now);
1926 m_cRecordForecast->SetString(m_Selection_index,
1927 nowTime); // write the now date time label
1928 // in the right place in wxChoice
1929 m_cRecordForecast->SetStringSelection(nowTime); // put it in the box
1930
1932
1933 pPlugIn->SendTimelineMessage(now);
1934 RequestRefresh(GetGRIBCanvas());
1935}
1936
1937void GRIBUICtrlBar::SetGribTimelineRecordSet(
1938 GribTimelineRecordSet *pTimelineSet) {
1939 delete m_pTimelineSet;
1940 m_pTimelineSet = pTimelineSet;
1941
1942 if (!pPlugIn->GetGRIBOverlayFactory()) return;
1943
1944 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(m_pTimelineSet);
1945}
1946
1947void GRIBUICtrlBar::SetTimeLineMax(bool SetValue) {
1948 int oldmax = wxMax(m_sTimeline->GetMax(), 1),
1949 oldval = m_sTimeline->GetValue(); // memorize the old range and value
1950
1951 if (m_OverlaySettings.m_bInterpolate) {
1952 int stepmin =
1953 m_OverlaySettings.GetMinFromIndex(m_OverlaySettings.m_SlicesPerUpdate);
1954 m_sTimeline->SetMax(m_TimeLineHours * 60 / stepmin);
1955 } else {
1957 ArrayOfGribRecordSets *rsa = m_bGRIBActiveFile->GetRecordSetArrayPtr();
1958 m_sTimeline->SetMax(rsa->GetCount() - 1);
1959 }
1960 }
1961 // try to retrieve a coherent timeline value with the new timeline range if it
1962 // has changed
1963 if (SetValue && m_sTimeline->GetMax() != 0) {
1964 if (m_pNowMode)
1965 ComputeBestForecastForNow();
1966 else
1967 m_sTimeline->SetValue(m_sTimeline->GetMax() * oldval / oldmax);
1968 }
1969}
1970
1971void GRIBUICtrlBar::SetFactoryOptions() {
1972 if (m_pTimelineSet) m_pTimelineSet->ClearCachedData();
1973
1974 pPlugIn->GetGRIBOverlayFactory()->ClearCachedData();
1975
1977 RequestRefresh(GetGRIBCanvas());
1978}
1979
1980void GRIBUICtrlBar::OnFormatRefreshTimer(wxTimerEvent &event) {
1981 // Check if time format has changed by comparing current format with saved
1982 // format
1983 wxDateTime referenceDate(1, wxDateTime::Jan, 2021, 12, 0, 0);
1984 wxString currentFormat = toUsrDateTimeFormat_Plugin(referenceDate);
1985
1986 if (currentFormat != m_sLastTimeFormat) {
1987 // Time format has changed, update all time displays
1988 m_sLastTimeFormat = currentFormat;
1989
1991 // Refresh the time format in the dropdown list
1992 PopulateComboDataList();
1993
1994 // Update the timeline display
1995 TimelineChanged();
1996
1997 // Request a refresh to update any on-screen displays
1998 RequestRefresh(GetGRIBCanvas());
1999 }
2000 }
2001}
2002
2003//----------------------------------------------------------------------------------------------------------
2004// GRIBFile Object Implementation
2005//----------------------------------------------------------------------------------------------------------
2006unsigned int GRIBFile::ID = 0;
2007
2008GRIBFile::GRIBFile(const wxArrayString &file_names, bool CumRec, bool WaveRec,
2009 bool newestFile)
2010 : m_counter(++ID) {
2011 m_bOK = false; // Assume ok until proven otherwise
2012 m_pGribReader = nullptr;
2013 m_last_message = wxEmptyString;
2014 for (unsigned int i = 0; i < file_names.GetCount(); i++) {
2015 wxString file_name = file_names[i];
2016 if (::wxFileExists(file_name)) m_bOK = true;
2017 }
2018
2019 if (m_bOK == false) {
2020 m_last_message = _(" files don't exist!");
2021 return;
2022 }
2023 // Use the zyGrib support classes, as (slightly) modified locally....
2024 m_pGribReader = new GribReader();
2025
2026 // Read and ingest the entire GRIB file.......
2027 m_bOK = false;
2028 wxString file_name;
2029 for (unsigned int i = 0; i < file_names.GetCount(); i++) {
2030 file_name = file_names[i];
2031 m_pGribReader->openFile(file_name);
2032
2033 if (m_pGribReader->isOk()) {
2034 m_bOK = true;
2035 if (newestFile) {
2036 break;
2037 }
2038 }
2039 }
2040 if (m_bOK == false) {
2041 m_last_message = _(" can't be read!");
2042 return;
2043 }
2044
2045 if (newestFile) {
2046 m_FileNames.Clear();
2047 m_FileNames.Add(file_name);
2048 } else {
2049 m_FileNames = file_names;
2050 }
2051
2052 // fixup Accumulation records
2053 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_TOT, LV_GND_SURF, 0);
2054 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_RATE, LV_GND_SURF, 0);
2055 m_pGribReader->computeAccumulationRecords(GRB_CLOUD_TOT, LV_ATMOS_ALL, 0);
2056
2057 if (CumRec)
2058 m_pGribReader->copyFirstCumulativeRecord(); // add missing records if
2059 // option selected
2060 if (WaveRec)
2061 m_pGribReader->copyMissingWaveRecords(); // "" ""
2062
2063 m_nGribRecords = m_pGribReader->getTotalNumberOfGribRecords();
2064
2065 // Walk the GribReader date list to populate our array of GribRecordSets
2066
2067 std::set<time_t>::iterator iter;
2068 std::set<time_t> date_list = m_pGribReader->getListDates();
2069 for (iter = date_list.begin(); iter != date_list.end(); iter++) {
2070 GribRecordSet *t = new GribRecordSet(m_counter);
2071 time_t reftime = *iter;
2072 t->m_Reference_Time = reftime;
2073 m_GribRecordSetArray.Add(t);
2074 }
2075
2076 // Convert from zyGrib organization by data type/level to our organization
2077 // by time.
2078
2079 GribRecord *pRec;
2080 bool isOK(false);
2081 bool polarWind(false);
2082 bool polarCurrent(false);
2083 bool sigWave(false);
2084 bool sigH(false);
2085 // Get the map of GribRecord vectors
2086 std::map<std::string, std::vector<GribRecord *> *> *p_map =
2087 m_pGribReader->getGribMap();
2088
2089 // Iterate over the map to get vectors of related GribRecords
2090 std::map<std::string, std::vector<GribRecord *> *>::iterator it;
2091 for (it = p_map->begin(); it != p_map->end(); it++) {
2092 std::vector<GribRecord *> *ls = (*it).second;
2093 for (zuint i = 0; i < ls->size(); i++) {
2094 pRec = ls->at(i);
2095 isOK = true;
2096 time_t thistime = pRec->getRecordCurrentDate();
2097
2098 // Search the GribRecordSet array for a GribRecordSet with matching time
2099 for (unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2100 if (m_GribRecordSetArray.Item(j).m_Reference_Time == thistime) {
2101 int idx = -1, mdx = -1;
2102 switch (pRec->getDataType()) {
2103 case GRB_WIND_DIR:
2104 polarWind = true;
2105 // fall through
2106 case GRB_WIND_VX:
2107 if (pRec->getLevelType() == LV_ISOBARIC) {
2108 switch (pRec->getLevelValue()) {
2109 case 300:
2110 idx = Idx_WIND_VX300;
2111 break;
2112 case 500:
2113 idx = Idx_WIND_VX500;
2114 break;
2115 case 700:
2116 idx = Idx_WIND_VX700;
2117 break;
2118 case 850:
2119 idx = Idx_WIND_VX850;
2120 break;
2121 }
2122 } else
2123 idx = Idx_WIND_VX;
2124 break;
2125 case GRB_WIND_SPEED:
2126 polarWind = true;
2127 // fall through
2128 case GRB_WIND_VY:
2129 if (pRec->getLevelType() == LV_ISOBARIC) {
2130 switch (pRec->getLevelValue()) {
2131 case 300:
2132 idx = Idx_WIND_VY300;
2133 break;
2134 case 500:
2135 idx = Idx_WIND_VY500;
2136 break;
2137 case 700:
2138 idx = Idx_WIND_VY700;
2139 break;
2140 case 850:
2141 idx = Idx_WIND_VY850;
2142 break;
2143 }
2144 } else
2145 idx = Idx_WIND_VY;
2146 break;
2147 case GRB_CUR_DIR:
2148 polarCurrent = true;
2149 // fall through
2150 case GRB_UOGRD:
2151 idx = Idx_SEACURRENT_VX;
2152 break;
2153 case GRB_CUR_SPEED:
2154 polarCurrent = true;
2155 // fall through
2156 case GRB_VOGRD:
2157 idx = Idx_SEACURRENT_VY;
2158 break;
2159 case GRB_WIND_GUST:
2160 idx = Idx_WIND_GUST;
2161 break;
2162 case GRB_PRESSURE:
2163 idx = Idx_PRESSURE;
2164 break;
2165 case GRB_HTSGW:
2166 sigH = true;
2167 idx = Idx_HTSIGW;
2168 break;
2169 case GRB_PER:
2170 sigWave = true;
2171 idx = Idx_WVPER;
2172 break;
2173 case GRB_DIR:
2174 sigWave = true;
2175 idx = Idx_WVDIR;
2176 break;
2177 case GRB_WVHGT:
2178 idx = Idx_HTSIGW;
2179 break; // Translation from NOAA WW3
2180 case GRB_WVPER:
2181 idx = Idx_WVPER;
2182 break;
2183 case GRB_WVDIR:
2184 idx = Idx_WVDIR;
2185 break;
2186 case GRB_PRECIP_RATE:
2187 case GRB_PRECIP_TOT:
2188 idx = Idx_PRECIP_TOT;
2189 break;
2190 case GRB_CLOUD_TOT:
2191 idx = Idx_CLOUD_TOT;
2192 break;
2193 case GRB_TEMP:
2194 if (pRec->getLevelType() == LV_ISOBARIC) {
2195 switch (pRec->getLevelValue()) {
2196 case 300:
2197 idx = Idx_AIR_TEMP300;
2198 break;
2199 case 500:
2200 idx = Idx_AIR_TEMP500;
2201 break;
2202 case 700:
2203 idx = Idx_AIR_TEMP700;
2204 break;
2205 case 850:
2206 idx = Idx_AIR_TEMP850;
2207 break;
2208 }
2209 } else
2210 idx = Idx_AIR_TEMP;
2211 if (pRec->getDataCenterModel() == NORWAY_METNO)
2212 mdx = 1000 + NORWAY_METNO;
2213 break;
2214 case GRB_WTMP:
2215 idx = Idx_SEA_TEMP;
2216 if (pRec->getDataCenterModel() == NOAA_GFS) mdx = 1000 + NOAA_GFS;
2217 break;
2218 case GRB_CAPE:
2219 idx = Idx_CAPE;
2220 break;
2221 case GRB_COMP_REFL:
2222 idx = Idx_COMP_REFL;
2223 break;
2224 case GRB_HUMID_REL:
2225 if (pRec->getLevelType() == LV_ISOBARIC) {
2226 switch (pRec->getLevelValue()) {
2227 case 300:
2228 idx = Idx_HUMID_RE300;
2229 break;
2230 case 500:
2231 idx = Idx_HUMID_RE500;
2232 break;
2233 case 700:
2234 idx = Idx_HUMID_RE700;
2235 break;
2236 case 850:
2237 idx = Idx_HUMID_RE850;
2238 break;
2239 }
2240 }
2241 break;
2242 case GRB_GEOPOT_HGT:
2243 if (pRec->getLevelType() == LV_ISOBARIC) {
2244 switch (pRec->getLevelValue()) {
2245 case 300:
2246 idx = Idx_GEOP_HGT300;
2247 break;
2248 case 500:
2249 idx = Idx_GEOP_HGT500;
2250 break;
2251 case 700:
2252 idx = Idx_GEOP_HGT700;
2253 break;
2254 case 850:
2255 idx = Idx_GEOP_HGT850;
2256 break;
2257 }
2258 }
2259 break;
2260 }
2261 if (idx == -1) {
2262 // XXX bug ?
2263 break;
2264 }
2265
2266 bool skip = false;
2267
2268 if (m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx]) {
2269 // already one
2270 GribRecord *oRec =
2271 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2272 if (idx == Idx_PRESSURE) {
2273 skip = (oRec->getLevelType() == LV_MSL);
2274 } else {
2275 // we favor UV over DIR/SPEED
2276 if (polarWind) {
2277 if (oRec->getDataType() == GRB_WIND_VY ||
2278 oRec->getDataType() == GRB_WIND_VX)
2279 skip = true;
2280 }
2281 if (polarCurrent) {
2282 if (oRec->getDataType() == GRB_UOGRD ||
2283 oRec->getDataType() == GRB_VOGRD)
2284 skip = true;
2285 }
2286 // favor average aka timeRange == 3 (HRRR subhourly subsets have
2287 // both 3 and 0 records for winds)
2288 if (!skip && (oRec->getTimeRange() == 3)) {
2289 skip = true;
2290 }
2291 // we favor significant Wave other wind wave.
2292 if (sigH) {
2293 if (oRec->getDataType() == GRB_HTSGW) skip = true;
2294 }
2295 if (sigWave) {
2296 if (oRec->getDataType() == GRB_DIR ||
2297 oRec->getDataType() == GRB_PER)
2298 skip = true;
2299 }
2300 }
2301 }
2302 if (!skip) {
2303 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx] = pRec;
2304 if (m_GribIdxArray.Index(idx) == wxNOT_FOUND)
2305 m_GribIdxArray.Add(idx, 1);
2306 if (mdx != -1 && m_GribIdxArray.Index(mdx) == wxNOT_FOUND)
2307 m_GribIdxArray.Add(mdx, 1);
2308 }
2309 break;
2310 }
2311 }
2312 }
2313 }
2314
2315 if (polarWind || polarCurrent) {
2316 for (unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2317 for (unsigned int i = 0; i < Idx_COUNT; i++) {
2318 int idx = -1;
2319 if (polarWind) {
2320 GribRecord *pRec =
2321 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2322
2323 if (pRec != nullptr && pRec->getDataType() == GRB_WIND_DIR) {
2324 switch (i) {
2325 case Idx_WIND_VX300:
2326 idx = Idx_WIND_VY300;
2327 break;
2328 case Idx_WIND_VX500:
2329 idx = Idx_WIND_VY500;
2330 break;
2331 case Idx_WIND_VX700:
2332 idx = Idx_WIND_VY700;
2333 break;
2334 case Idx_WIND_VX850:
2335 idx = Idx_WIND_VY850;
2336 break;
2337 case Idx_WIND_VX:
2338 idx = Idx_WIND_VY;
2339 break;
2340 default:
2341 break;
2342 }
2343 if (idx != -1) {
2344 GribRecord *pRec1 =
2345 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2346 if (pRec1 != nullptr && pRec1->getDataType() == GRB_WIND_SPEED)
2347 GribRecord::Polar2UV(pRec, pRec1);
2348 }
2349 }
2350 }
2351 if (polarCurrent) {
2352 idx = -1;
2353 GribRecord *pRec =
2354 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2355
2356 if (pRec != nullptr && pRec->getDataType() == GRB_CUR_DIR) {
2357 switch (i) {
2358 case Idx_SEACURRENT_VX:
2359 idx = Idx_SEACURRENT_VY;
2360 break;
2361 default:
2362 break;
2363 }
2364 if (idx != -1) {
2365 GribRecord *pRec1 =
2366 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2367 if (pRec1 != nullptr && pRec1->getDataType() == GRB_CUR_SPEED)
2368 GribRecord::Polar2UV(pRec, pRec1);
2369 }
2370 }
2371 }
2372 }
2373 }
2374 }
2375
2376 if (isOK)
2377 m_pRefDateTime =
2378 pRec->getRecordRefDate(); // to ovoid crash with some bad files
2379}
2380
2381GRIBFile::~GRIBFile() { delete m_pGribReader; }
2382
2383//---------------------------------------------------------------------------------------
2384// GRIB Cursor Data Ctrl & Display implementation
2385//---------------------------------------------------------------------------------------
2386GRIBUICData::GRIBUICData(GRIBUICtrlBar &parent)
2387#ifdef __WXOSX__
2388 : GRIBUICDataBase(parent.pParent, CURSOR_DATA, _("GRIB Display Control"),
2389 wxDefaultPosition, wxDefaultSize,
2390 wxSYSTEM_MENU | wxNO_BORDER | wxSTAY_ON_TOP)
2391#else
2392 : GRIBUICDataBase(&parent, CURSOR_DATA, _("GRIB Display Control"),
2393 wxDefaultPosition, wxDefaultSize,
2394 wxSYSTEM_MENU | wxNO_BORDER)
2395#endif
2396 ,
2397 m_gpparent(parent) {
2398 // m_gGrabber = new GribGrabberWin( this );
2399 // fgSizer58->Add( m_gGrabber, 0, wxALL, 0 );
2400
2401 m_gCursorData = new CursorData(this, m_gpparent);
2402 m_fgCdataSizer->Add(m_gCursorData, 0, wxALL, 0);
2403
2404 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICData::OnMove));
2405}
2406
2407void GRIBUICData::OnMove(wxMoveEvent &event) {
2408 int w, h;
2409 GetScreenPosition(&w, &h);
2410 m_gpparent.pPlugIn->SetCursorDataXY(wxPoint(w, h));
2411}
2412
2413//---------------------------------------------------------------------------------------
2414// Android Utility Methods
2415//---------------------------------------------------------------------------------------
2416#ifdef __OCPN__ANDROID__
2417
2418#include <QtAndroidExtras/QAndroidJniObject>
2419
2420bool CheckPendingJNIException() {
2421 if (!java_vm) {
2422 // qDebug() << "java_vm is nullptr.";
2423 return true;
2424 }
2425
2426 JNIEnv *jenv;
2427 if (java_vm->GetEnv((void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2428 // qDebug() << "GetEnv failed.";
2429 return true;
2430 }
2431
2432 if ((jenv)->ExceptionCheck() == JNI_TRUE) {
2433 // qDebug() << "Found JNI Exception Pending.";
2434 return true;
2435 }
2436
2437 return false;
2438}
2439
2440wxString callActivityMethod_ss(const char *method, wxString parm) {
2441 if (!java_vm) {
2442 // qDebug() << "java_vm is nullptr.";
2443 return _T("NOK");
2444 }
2445
2446 if (CheckPendingJNIException()) return _T("NOK");
2447
2448 wxString return_string;
2449 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod(
2450 "org/qtproject/qt5/android/QtNative", "activity",
2451 "()Landroid/app/Activity;");
2452 if (CheckPendingJNIException()) return _T("NOK");
2453
2454 if (!activity.isValid()) {
2455 // qDebug() << "Activity is not valid";
2456 return return_string;
2457 }
2458
2459 // Need a Java environment to decode the resulting string
2460 JNIEnv *jenv;
2461 if (java_vm->GetEnv((void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2462 // qDebug() << "GetEnv failed.";
2463 return "jenv Error";
2464 }
2465
2466 jstring p = (jenv)->NewStringUTF(parm.c_str());
2467
2468 // Call the desired method
2469 // qDebug() << "Calling method_ss";
2470 // qDebug() << method;
2471
2472 QAndroidJniObject data = activity.callObjectMethod(
2473 method, "(Ljava/lang/String;)Ljava/lang/String;", p);
2474 if (CheckPendingJNIException()) return _T("NOK");
2475
2476 // qDebug() << "Back from method_ss";
2477
2478 jstring s = data.object<jstring>();
2479
2480 if ((jenv)->GetStringLength(s)) {
2481 const char *ret_string = (jenv)->GetStringUTFChars(s, nullptr);
2482 return_string = wxString(ret_string, wxConvUTF8);
2483 }
2484
2485 return return_string;
2486}
2487
2488#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.
#define wxCLOSE_BOX
Definition mark_info.h:133
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...