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