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