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