24#include "wx/tokenzr.h"
25#include "wx/datetime.h"
27#include <wx/wfstream.h>
29#include <wx/filename.h>
31#include <wx/graphics.h>
34#include <wx/stdpaths.h>
47#include <wx/arrimpl.cpp>
50#include "android_jvm.h"
54double m_cursor_lat, m_cursor_lon;
90 return ((x - i) >= 0.5) ? (i + 1) : (i);
92 return (-x + i >= 0.5) ? (i - 1) : (i);
102#if wxCHECK_VERSION(2, 9, 4)
103#define SetBitmapLabelLabel SetBitmap
106#define DEFAULT_STYLE \
107 = wxCAPTION | wxCLOSE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL
109WX_DEFINE_OBJARRAY(ArrayOfGribRecordSets);
112static int CompareFileStringTime(
const wxString &first,
113 const wxString &second) {
115 wxFileName s(second);
116 wxTimeSpan sp = s.GetModificationTime() - f.GetModificationTime();
117 return sp.GetMinutes();
123wxWindow *GetGRIBCanvas() {
146GribTimelineRecordSet::~GribTimelineRecordSet() {
151void GribTimelineRecordSet::ClearCachedData() {
155 for (
unsigned int j = 0; j <
m_IsobarArray[i]->GetCount(); j++) {
169GRIBUICtrlBar::GRIBUICtrlBar(wxWindow *parent, wxWindowID
id,
170 const wxString &title,
const wxPoint &pos,
171 const wxSize &size,
long style,
grib_pi *ppi,
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;
189 m_fgCtrlGrabberSize->Add(m_gGrabber, 0, wxALL, 0);
191 this->SetSizer(m_fgCtrlBarSizer);
193 m_fgCtrlBarSizer->Fit(
this);
196 m_tFormatRefresh.Connect(
197 wxEVT_TIMER, wxTimerEventHandler(GRIBUICtrlBar::OnFormatRefreshTimer),
202 wxDateTime referenceDate(1, wxDateTime::Jan, 2021, 12, 0, 0);
203 m_sLastTimeFormat = toUsrDateTimeFormat_Plugin(referenceDate);
206 m_tFormatRefresh.Start(5000);
209 pConf->SetPath(_T (
"/Settings/GRIB" ));
210 pConf->Read(_T (
"WindPlot" ), &m_bDataPlot[GribOverlaySettings::WIND],
212 pConf->Read(_T (
"WindGustPlot" ),
213 &m_bDataPlot[GribOverlaySettings::WIND_GUST],
false);
214 pConf->Read(_T (
"PressurePlot" ),
215 &m_bDataPlot[GribOverlaySettings::PRESSURE],
false);
216 pConf->Read(_T (
"WavePlot" ), &m_bDataPlot[GribOverlaySettings::WAVE],
218 pConf->Read(_T (
"CurrentPlot" ),
219 &m_bDataPlot[GribOverlaySettings::CURRENT],
false);
220 pConf->Read(_T (
"PrecipitationPlot" ),
221 &m_bDataPlot[GribOverlaySettings::PRECIPITATION],
false);
222 pConf->Read(_T (
"CloudPlot" ), &m_bDataPlot[GribOverlaySettings::CLOUD],
224 pConf->Read(_T (
"AirTemperaturePlot" ),
225 &m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE],
false);
226 pConf->Read(_T (
"SeaTemperaturePlot" ),
227 &m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE],
false);
228 pConf->Read(_T (
"CAPEPlot" ), &m_bDataPlot[GribOverlaySettings::CAPE],
230 pConf->Read(_T (
"CompReflectivityPlot" ),
231 &m_bDataPlot[GribOverlaySettings::COMP_REFL],
false);
233 pConf->Read(_T (
"CursorDataShown" ), &m_CDataIsShown,
true);
235 pConf->Read(_T (
"lastdatatype" ), &m_lastdatatype, 0);
237 pConf->SetPath(_T (
"/Settings/GRIB/FileNames" ));
238 m_file_names.Clear();
239 if (pConf->GetNumberOfEntries()) {
242 bool bCont = pConf->GetFirstEntry(str, dummy);
244 pConf->Read(str, &val);
245 m_file_names.Add(val);
246 bCont = pConf->GetNextEntry(str, dummy);
250 wxStandardPathsBase &spath = wxStandardPaths::Get();
252 pConf->SetPath(_T (
"/Directories" ));
253 pConf->Read(_T (
"GRIBDirectory" ), &m_grib_dir);
255 pConf->SetPath(_T(
"/PlugIns/GRIB" ));
259 pConf->SetPath(_T (
"/Settings/GRIB/XyGrib" ));
260 pConf->Read(_T(
"AtmModelIndex" ), &xyGribConfig.atmModelIndex, 0);
261 pConf->Read(_T(
"WaveModelIndex" ), &xyGribConfig.waveModelIndex, 0);
262 pConf->Read(_T(
"ResolutionIndex" ), &xyGribConfig.resolutionIndex, 0);
263 pConf->Read(_T(
"DurationIndex" ), &xyGribConfig.durationIndex, 0);
264 pConf->Read(_T(
"RunIndex" ), &xyGribConfig.runIndex, 0);
265 pConf->Read(_T(
"IntervalIndex" ), &xyGribConfig.intervalIndex, 0);
266 pConf->Read(_T(
"Wind" ), &xyGribConfig.wind,
true);
267 pConf->Read(_T(
"Gust" ), &xyGribConfig.gust,
true);
268 pConf->Read(_T(
"Pressure" ), &xyGribConfig.pressure,
false);
269 pConf->Read(_T(
"Temperature" ), &xyGribConfig.temperature,
true);
270 pConf->Read(_T(
"Cape" ), &xyGribConfig.cape,
false);
271 pConf->Read(_T(
"Reflectivity" ), &xyGribConfig.reflectivity,
false);
272 pConf->Read(_T(
"CloudCover" ), &xyGribConfig.cloudCover,
true);
273 pConf->Read(_T(
"Precipitation" ), &xyGribConfig.precipitation,
true);
274 pConf->Read(_T(
"WaveHeight" ), &xyGribConfig.waveHeight,
true);
275 pConf->Read(_T(
"WindWaves" ), &xyGribConfig.windWaves,
true);
281 m_tPlayStop.Connect(wxEVT_TIMER,
282 wxTimerEventHandler(GRIBUICtrlBar::OnPlayStopTimer),
285 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICtrlBar::OnMove));
287 m_OverlaySettings.Read();
292 SetMinSize(GetBestSize());
293 if (m_ProjectBoatPanel) {
294 m_ProjectBoatPanel->SetSpeed(pPlugIn->m_boat_sog);
295 m_ProjectBoatPanel->SetCourse(pPlugIn->m_boat_cog);
297 m_highlight_latmax = 0;
298 m_highlight_lonmax = 0;
299 m_highlight_latmin = 0;
300 m_highlight_lonmin = 0;
305 createRequestDialog();
308GRIBUICtrlBar::~GRIBUICtrlBar() {
313 pConf->SetPath(_T (
"/Settings/GRIB" ));
314 pConf->Write(_T (
"WindPlot" ), m_bDataPlot[GribOverlaySettings::WIND]);
315 pConf->Write(_T (
"WindGustPlot" ),
316 m_bDataPlot[GribOverlaySettings::WIND_GUST]);
317 pConf->Write(_T (
"PressurePlot" ),
318 m_bDataPlot[GribOverlaySettings::PRESSURE]);
319 pConf->Write(_T (
"WavePlot" ), m_bDataPlot[GribOverlaySettings::WAVE]);
320 pConf->Write(_T (
"CurrentPlot" ),
321 m_bDataPlot[GribOverlaySettings::CURRENT]);
322 pConf->Write(_T (
"PrecipitationPlot" ),
323 m_bDataPlot[GribOverlaySettings::PRECIPITATION]);
324 pConf->Write(_T (
"CloudPlot" ), m_bDataPlot[GribOverlaySettings::CLOUD]);
325 pConf->Write(_T (
"AirTemperaturePlot" ),
326 m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE]);
327 pConf->Write(_T (
"SeaTemperaturePlot" ),
328 m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE]);
329 pConf->Write(_T (
"CAPEPlot" ), m_bDataPlot[GribOverlaySettings::CAPE]);
330 pConf->Write(_T (
"CompReflectivityPlot" ),
331 m_bDataPlot[GribOverlaySettings::COMP_REFL]);
333 pConf->Write(_T (
"CursorDataShown" ), m_CDataIsShown);
335 pConf->Write(_T (
"lastdatatype" ), m_lastdatatype);
337 pConf->SetPath(_T (
"/Settings/GRIB/FileNames" ));
338 int iFileMax = pConf->GetNumberOfEntries();
342 for (
int i = 0; i < iFileMax; i++) {
343 if (pConf->GetFirstEntry(key, dummy)) pConf->DeleteEntry(key,
false);
347 for (
unsigned int i = 0; i <
m_file_names.GetCount(); i++) {
349 key.Printf(_T(
"Filename%d"), i);
353 pConf->SetPath(_T (
"/Directories" ));
354 pConf->Write(_T (
"GRIBDirectory" ),
m_grib_dir);
357 pConf->SetPath(_T (
"/Settings/GRIB/XyGrib" ));
358 pConf->Write(_T(
"AtmModelIndex" ), xyGribConfig.atmModelIndex);
359 pConf->Write(_T(
"WaveModelIndex" ), xyGribConfig.waveModelIndex);
360 pConf->Write(_T(
"ResolutionIndex" ), xyGribConfig.resolutionIndex);
361 pConf->Write(_T(
"DurationIndex" ), xyGribConfig.durationIndex);
362 pConf->Write(_T(
"RunIndex" ), xyGribConfig.runIndex);
363 pConf->Write(_T(
"IntervalIndex" ), xyGribConfig.intervalIndex);
364 pConf->Write(_T(
"Wind" ), xyGribConfig.wind);
365 pConf->Write(_T(
"Gust" ), xyGribConfig.gust);
366 pConf->Write(_T(
"Pressure" ), xyGribConfig.pressure);
367 pConf->Write(_T(
"Temperature" ), xyGribConfig.temperature);
368 pConf->Write(_T(
"Cape" ), xyGribConfig.cape);
369 pConf->Write(_T(
"Reflectivity" ), xyGribConfig.reflectivity);
370 pConf->Write(_T(
"CloudCover" ), xyGribConfig.cloudCover);
371 pConf->Write(_T(
"Precipitation" ), xyGribConfig.precipitation);
372 pConf->Write(_T(
"WaveHeight" ), xyGribConfig.waveHeight);
373 pConf->Write(_T(
"WindWaves" ), xyGribConfig.windWaves);
379void GRIBUICtrlBar::SetScaledBitmap(
double factor) {
381 m_ScaledFactor = wxRound(factor * 4.0) / 4.0;
383 m_bpPrev->SetBitmapLabel(
384 GetScaledBitmap(wxBitmap(prev), _T(
"prev"), m_ScaledFactor));
385 m_bpNext->SetBitmapLabel(
386 GetScaledBitmap(wxBitmap(next), _T(
"next"), m_ScaledFactor));
387 m_bpAltitude->SetBitmapLabel(
388 GetScaledBitmap(wxBitmap(altitude), _T(
"altitude"), m_ScaledFactor));
389 m_bpNow->SetBitmapLabel(
390 GetScaledBitmap(wxBitmap(now), _T(
"now"), m_ScaledFactor));
391 m_bpZoomToCenter->SetBitmapLabel(
392 GetScaledBitmap(wxBitmap(zoomto), _T(
"zoomto"), m_ScaledFactor));
393 m_bpPlay->SetBitmapLabel(
394 GetScaledBitmap(wxBitmap(play), _T(
"play"), m_ScaledFactor));
395 m_bpShowCursorData->SetBitmapLabel(GetScaledBitmap(
396 wxBitmap(m_CDataIsShown ? curdata : ncurdata),
397 m_CDataIsShown ? _T(
"curdata") : _T(
"ncurdata"), m_ScaledFactor));
399 m_bpOpenFile->SetBitmapLabel(
400 GetScaledBitmap(wxBitmap(openfile), _T(
"openfile"), m_ScaledFactor));
401 m_bpSettings->SetBitmapLabel(
402 GetScaledBitmap(wxBitmap(setting), _T(
"setting"), m_ScaledFactor));
408#ifdef __OCPN__ANDROID__
409 m_sTimeline->SetSize(wxSize(20 * m_ScaledFactor, -1));
410 m_sTimeline->SetMinSize(wxSize(20 * m_ScaledFactor, -1));
412 m_sTimeline->SetSize(wxSize(90 * m_ScaledFactor, -1));
413 m_sTimeline->SetMinSize(wxSize(90 * m_ScaledFactor, -1));
418 if (
nullptr == m_bpRequest)
return;
419 m_bpRequest->SetBitmapLabel(
420 GetScaledBitmap(wxBitmap(request), _T(
"request"), m_ScaledFactor));
421 m_bpRequest->SetToolTip(_(
"Start a download request"));
424void GRIBUICtrlBar::OpenFile(
bool newestFile) {
425 m_bpPlay->SetBitmapLabel(
426 GetScaledBitmap(wxBitmap(play), _T(
"play"), m_ScaledFactor));
427 m_cRecordForecast->Clear();
428 pPlugIn->GetGRIBOverlayFactory()->ClearParticles();
434 m_sTimeline->SetValue(0);
436 m_InterpolateMode =
false;
438 m_SelectionIsSaved =
false;
439 m_HasAltitude =
false;
460 title = (_(
"File: "));
461 title.Append(fn.GetFullName());
462 if (rsa->GetCount() == 0) {
465 title.Prepend(_(
"Error! ")).Append(_(
" contains no valid data!"));
467 PopulateComboDataList();
469 toUsrDateTimeFormat_Plugin(
473 if (rsa->GetCount() > 1) {
475 &last = rsa->Item(rsa->GetCount() - 1);
478 wxTimeSpan span = wxDateTime(last.m_Reference_Time) -
480 m_TimeLineHours = span.GetHours();
483 int halfintermin(wxTimeSpan(wxDateTime(second.m_Reference_Time) -
487 for (m_FileIntervalIndex = 0;; m_FileIntervalIndex++) {
492 if (m_FileIntervalIndex > 0) m_FileIntervalIndex--;
500 title = _(
"No valid GRIB file");
502 pPlugIn->GetGRIBOverlayFactory()->SetMessage(title);
504 SetTimeLineMax(
false);
506 if (
pPlugIn->GetStartOptions() &&
507 m_TimeLineHours != 0)
508 ComputeBestForecastForNow();
514 for (
int i = 1; i < 5; i++) {
519 m_HasAltitude =
true;
524#ifdef __OCPN__ANDROID__
525 m_bpSettings->Enable(
true);
531 m_sTimeline->Enable(
m_pTimelineSet !=
nullptr && m_TimeLineHours);
550 bool bconfigOK =
false;
551 if (m_bDataPlot[GribOverlaySettings::WIND] &&
554 if (m_bDataPlot[GribOverlaySettings::WIND_GUST] &&
557 if (m_bDataPlot[GribOverlaySettings::PRESSURE] &&
560 if (m_bDataPlot[GribOverlaySettings::WAVE] &&
563 if (m_bDataPlot[GribOverlaySettings::WAVE] &&
566 if (m_bDataPlot[GribOverlaySettings::CURRENT] &&
570 if (m_bDataPlot[GribOverlaySettings::PRECIPITATION] &&
573 if (m_bDataPlot[GribOverlaySettings::CLOUD] &&
576 if (m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE] &&
579 if (m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE] &&
582 if (m_bDataPlot[GribOverlaySettings::CAPE] &&
585 if (m_bDataPlot[GribOverlaySettings::COMP_REFL] &&
592 for (
int i = 0; i < (int)GribOverlaySettings::GEO_ALTITUDE; i++) {
594 m_bDataPlot[i] =
true;
601 double *latmin,
double *latmax,
602 double *lonmin,
double *lonmax) {
605 double ltmi = -GRIB_NOTDEF, ltma = GRIB_NOTDEF, lnmi = -GRIB_NOTDEF,
607 for (
unsigned int i = 0; i <
Idx_COUNT; i++) {
610 if (pGRA->getLatMin() < ltmi) ltmi = pGRA->getLatMin();
611 if (pGRA->getLatMax() > ltma) ltma = pGRA->getLatMax();
612 if (pGRA->getLonMin() < lnmi) lnmi = pGRA->getLonMin();
613 if (pGRA->getLonMax() > lnma) lnma = pGRA->getLonMax();
615 if (ltmi == -GRIB_NOTDEF || lnmi == -GRIB_NOTDEF || ltma == GRIB_NOTDEF ||
619 if (latmin) *latmin = ltmi;
620 if (latmax) *latmax = ltma;
621 if (lonmin) *lonmin = lnmi;
622 if (lonmax) *lonmax = lnma;
629 : m_files(files), m_pattern(pattern) {}
630 virtual wxDirTraverseResult OnFile(
const wxString &filename) {
631 if (m_pattern.Matches(filename)) m_files.Add(filename);
632 return wxDIR_CONTINUE;
634 virtual wxDirTraverseResult OnDir(
const wxString &WXUNUSED(dirname)) {
639 wxArrayString &m_files;
640 const wxRegEx &m_pattern;
643wxArrayString GRIBUICtrlBar::GetFilesInDirectory() {
644 wxArrayString file_array;
645 if (!wxDir::Exists(
m_grib_dir))
return file_array;
649 wxRegEx pattern(_T(
".+\\.gri?b2?(\\.(bz2|gz))?$"),
650 wxRE_EXTENDED | wxRE_ICASE | wxRE_NOSUB);
653 dir.Traverse(collector);
655 CompareFileStringTime);
659void GRIBUICtrlBar::SetCursorLatLon(
double lat,
double lon) {
663 if (m_vpMouse && ((lat > m_vpMouse->
lat_min) && (lat < m_vpMouse->lat_max)) &&
664 ((lon > m_vpMouse->
lon_min) && (lon < m_vpMouse->lon_max)))
669 if (!m_CDataIsShown)
return;
671 if (m_DialogStyle >> 1 == SEPARATED) {
672 if (m_gGRIBUICData) {
673 if (!m_gGRIBUICData->m_gCursorData->m_tCursorTrackTimer.IsRunning())
674 m_gGRIBUICData->m_gCursorData->m_tCursorTrackTimer.Start(
675 50, wxTIMER_ONE_SHOT);
679 if (!m_gCursorData->m_tCursorTrackTimer.IsRunning())
680 m_gCursorData->m_tCursorTrackTimer.Start(50, wxTIMER_ONE_SHOT);
685void GRIBUICtrlBar::OnShowCursorData(wxCommandEvent &event) {
686 m_CDataIsShown = !m_CDataIsShown;
687 m_bpShowCursorData->SetBitmapLabel(GetScaledBitmap(
688 wxBitmap(m_CDataIsShown ? curdata : ncurdata),
689 m_CDataIsShown ? _T(
"curdata") : _T(
"ncurdata"), m_ScaledFactor));
690 SetDialogsStyleSizePosition(
true);
693void GRIBUICtrlBar::SetDialogsStyleSizePosition(
bool force_recompute) {
698 if (!force_recompute &&
699 (m_old_DialogStyle == m_DialogStyle
701 (m_old_DialogStyle >> 1 == ATTACHED && m_DialogStyle >> 1 == ATTACHED)))
704 bool m_HasCaption = GetWindowStyleFlag() == (wxCAPTION | wxCLOSE_BOX |
705 wxSYSTEM_MENU | wxTAB_TRAVERSAL);
710 int state = (m_DialogStyle >> 1 == ATTACHED && m_CDataIsShown) ? 0 : 1;
713 bool vis = i > 0 ? true : m_HasAltitude ? true :
false;
714 if (FindWindow(i + ID_CTRLALTITUDE))
715 FindWindow(i + ID_CTRLALTITUDE)
721 m_bpShowCursorData->SetToolTip(m_CDataIsShown ? _(
"Hide data at cursor")
722 : _(
"Show data at cursor"));
723 m_bpPlay->SetToolTip(_(
"Start play back"));
728 m_gCursorData->Hide();
729 m_fgCDataSizer->Detach(m_gCursorData);
732 SetMinSize(wxSize(0, 0));
736 if (m_gGRIBUICData) {
737 m_gGRIBUICData->Destroy();
738 m_gGRIBUICData =
nullptr;
741 if ((m_DialogStyle >> 1 == SEPARATED || !m_CDataIsShown) &&
748 if (m_CDataIsShown) {
749 if (m_DialogStyle >> 1 == ATTACHED) {
751 if (!m_gCursorData) m_gCursorData =
new CursorData(
this, *
this);
752 pPlugIn->SetDialogFont(m_gCursorData);
753 m_gCursorData->PopulateTrackingControls(
false);
755 if (m_fgCDataSizer->GetItem(m_gCursorData) ==
nullptr)
756 m_fgCDataSizer->Add(m_gCursorData, 0);
757 m_gCursorData->Show();
759 }
else if (m_DialogStyle >> 1 == SEPARATED) {
762 m_gGRIBUICData->m_gCursorData->PopulateTrackingControls(
763 m_DialogStyle == SEPARATED_VERTICAL);
764 pPlugIn->SetDialogFont(m_gGRIBUICData->m_gCursorData);
765 m_gGRIBUICData->Fit();
766 m_gGRIBUICData->Update();
767 m_gGRIBUICData->Show();
769 m_gGRIBUICData->Layout();
770 m_gGRIBUICData->Fit();
775 wxSize sd = GetSize();
777 if (!m_gtk_started && m_HasCaption ) {
779 m_gtk_started =
true;
782 SetSize(wxSize(sd.x, sd.y));
783 SetMinSize(wxSize(sd.x, sd.y));
784#ifdef __OCPN__ANDROID__
790 wxPoint pNew =
pPlugIn->GetCtrlBarXY();
791 pNew.x = tbRect.x + tbRect.width + 4;
799 if (sd.x > widthAvail) {
802 int target_char_width = (float)widthAvail / 28;
811 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
813 dc.GetTextExtent(_T(
"W"), &width, &height,
nullptr,
nullptr, sFont);
814 if (width <= target_char_width) bOK =
true;
816 if (pointSize <= 10) bOK =
true;
819 m_cRecordForecast->SetFont(*sFont);
824 SetSize(wxSize(widthAvail, sd.y));
825 SetMinSize(wxSize(widthAvail, sd.y));
829 wxPoint pNow =
pPlugIn->GetCtrlBarXY();
836 m_old_DialogStyle = m_DialogStyle;
839void GRIBUICtrlBar::OnAltitude(wxCommandEvent &event) {
840 if (!m_HasAltitude)
return;
842 wxMenu *amenu =
new wxMenu();
843 amenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
844 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent),
nullptr,
this);
846 for (
int i = 0; i < 5; i++) {
854 amenu, ID_CTRLALTITUDE + 1000 + i,
863 amenu->Check(ID_CTRLALTITUDE + 1000 + m_Altitude,
true);
870void GRIBUICtrlBar::OnMove(wxMoveEvent &event) {
872 GetScreenPosition(&w, &h);
873 pPlugIn->SetCtrlBarXY(wxPoint(w, h));
876void GRIBUICtrlBar::OnMenuEvent(wxMenuEvent &event) {
877 int id =
event.GetId();
880 int alt = m_Altitude;
883 case ID_CTRLALTITUDE + 1000:
886 case ID_CTRLALTITUDE + 1001:
889 case ID_CTRLALTITUDE + 1002:
892 case ID_CTRLALTITUDE + 1003:
895 case ID_CTRLALTITUDE + 1004:
903 OnZoomToCenterClick(evt);
905 case ID_BTNSHOWCDATA:
906 OnShowCursorData(evt);
918 OnRequestForecastData(evt);
920 if (alt != m_Altitude) {
921 SetDialogsStyleSizePosition(
true);
926void GRIBUICtrlBar::MenuAppend(wxMenu *menu,
int id, wxString label,
927 wxItemKind kind, wxBitmap bitmap,
929 wxMenuItem *item =
new wxMenuItem(menu,
id, label, _T(
""), kind);
932 item->SetSubMenu(submenu);
942#if defined(__WXMSW__) || defined(__WXGTK__)
943 if (!bitmap.IsSameAs(wxNullBitmap)) item->SetBitmap(bitmap);
949void GRIBUICtrlBar::OnMouseEvent(wxMouseEvent &event) {
950 if (event.RightDown()) {
952 wxMenu *xmenu =
new wxMenu();
953 xmenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
954 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent),
nullptr,
958 wxMenu *smenu =
new wxMenu();
959 smenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
960 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent),
nullptr,
963 for (
int i = 0; i < 5; i++) {
971 smenu, ID_CTRLALTITUDE + 1000 + i,
978 smenu->Check(ID_CTRLALTITUDE + 1000 + m_Altitude,
true);
980 xmenu, wxID_ANY, _(
"Select geopotential altitude"), wxITEM_NORMAL,
981 GetScaledBitmap(wxBitmap(altitude), _T(
"altitude"), m_ScaledFactor),
984 MenuAppend(xmenu, ID_BTNNOW, _(
"Now"), wxITEM_NORMAL,
985 GetScaledBitmap(wxBitmap(now), _T(
"now"), m_ScaledFactor));
986 MenuAppend(xmenu, ID_BTNZOOMTC, _(
"Zoom To Center"), wxITEM_NORMAL,
987 GetScaledBitmap(wxBitmap(zoomto), _T(
"zoomto"), m_ScaledFactor));
989 xmenu, ID_BTNSHOWCDATA,
990 m_CDataIsShown ? _(
"Hide data at cursor") : _(
"Show data at cursor"),
992 GetScaledBitmap(wxBitmap(m_CDataIsShown ? curdata : ncurdata),
993 m_CDataIsShown ? _T(
"curdata") : _T(
"ncurdata"),
997 m_tPlayStop.IsRunning() ? _(
"Stop play back") : _(
"Start play back"),
999 GetScaledBitmap(wxBitmap(
m_tPlayStop.IsRunning() ? stop : play),
1000 m_tPlayStop.IsRunning() ? _T(
"stop") : _T(
"play"),
1003 xmenu, ID_BTNOPENFILE, _(
"Open a new file"), wxITEM_NORMAL,
1004 GetScaledBitmap(wxBitmap(openfile), _T(
"openfile"), m_ScaledFactor));
1006 xmenu, ID_BTNSETTING, _(
"Settings"), wxITEM_NORMAL,
1007 GetScaledBitmap(wxBitmap(setting), _T(
"setting"), m_ScaledFactor));
1013 requeststate1 ? _(
"Request forecast data")
1015 ? _(
"Draw requested Area or Click here to stop request")
1016 : _(
"Valid Area and Continue"),
1018 GetScaledBitmap(wxBitmap(requeststate1 ? request
1019 : requeststate3 ? selzone
1021 requeststate1 ? _T(
"request")
1022 : requeststate3 ? _T(
"selzone")
1023 : _T(
"request_end"),
1033 if (m_DialogStyle >> 1 == SEPARATED)
return;
1034 wxMouseEvent evt(event);
1037#ifndef __OCPN__ANDROID__
1038 if (m_gCursorData && m_CDataIsShown) {
1039 m_gCursorData->OnMouseEvent(evt);
1044void GRIBUICtrlBar::ContextMenuItemCallback(
int id) {
1046 bool dataisshown = m_CDataIsShown;
1047 m_CDataIsShown =
false;
1060 m_CDataIsShown = dataisshown;
1065 if (m_vpMouse == vp)
return;
1077void GRIBUICtrlBar::OnClose(wxCloseEvent &event) {
1079 if (m_gGRIBUICData) m_gGRIBUICData->Hide();
1082 pReq_Dialog->StopGraphicalZoneSelection();
1086 pPlugIn->SendTimelineMessage(wxInvalidDateTime);
1088 pPlugIn->OnGribCtrlBarClose();
1091void GRIBUICtrlBar::OnSize(wxSizeEvent &event) {
1093 wxSize p =
event.GetSize();
1099void GRIBUICtrlBar::OnPaint(wxPaintEvent &event) {
1100 wxWindowListNode *node = this->GetChildren().GetFirst();
1103 wxWindow *win = node->GetData();
1104 if (
dynamic_cast<wxBitmapButton *
>(win))
1105 dc.DrawBitmap(
dynamic_cast<wxBitmapButton *
>(win)->GetBitmap(), 5, 5,
1107 node = node->GetNext();
1111void GRIBUICtrlBar::createRequestDialog() {
1112 ::wxBeginBusyCursor();
1117 pPlugIn->SetDialogFont(pReq_Dialog);
1118 pPlugIn->SetDialogFont(pReq_Dialog->m_sScrolledDialog);
1120 pReq_Dialog->SetRequestDialogSize();
1121 if (::wxIsBusy()) ::wxEndBusyCursor();
1124void GRIBUICtrlBar::OnRequestForecastData(wxCommandEvent &event) {
1129 if (pReq_Dialog && pReq_Dialog->IsShown())
return;
1132 createRequestDialog();
1135 ::wxDisplaySize(&w,
nullptr);
1136 pReq_Dialog->Move((w - pReq_Dialog->GetSize().GetX()) / 2, 30);
1137 pReq_Dialog->Show();
1142void GRIBUICtrlBar::OnSettings(wxCommandEvent &event) {
1146 ::wxBeginBusyCursor();
1152 pPlugIn->SetDialogFont(dialog);
1153 for (
size_t i = 0; i < dialog->m_nSettingsBook->GetPageCount(); i++) {
1154 wxScrolledWindow *sc =
1155 ((wxScrolledWindow *)dialog->m_nSettingsBook->GetPage(i));
1159 dialog->m_nSettingsBook->ChangeSelection(dialog->GetPageIndex());
1160 dialog->SetSettingsDialogSize();
1163 ::wxDisplaySize(&w,
nullptr);
1164 dialog->Move((w - dialog->GetSize().GetX()) / 2, 30);
1167 ::wxEndBusyCursor();
1169 if (dialog->ShowModal() == wxID_OK) {
1170 dialog->WriteSettings();
1173 initSettings.Settings[GribOverlaySettings::WIND].m_Units &&
1175 GribOverlaySettings::BFS ||
1176 initSettings.Settings[GribOverlaySettings::WIND].m_Units ==
1177 GribOverlaySettings::BFS))
1179 STARTING_STATE_STYLE;
1183 m_DialogStyle = initSettings.m_iCtrlandDataStyle;
1185 ::wxBeginBusyCursor();
1187 dialog->SaveLastPage();
1189 m_InterpolateMode =
false;
1190 SetTimeLineMax(
true);
1191 SetFactoryOptions();
1193 SetDialogsStyleSizePosition(
true);
1199#ifdef __OCPN__ANDROID__
1200wxString callActivityMethod_ss(
const char *method, wxString parm);
1203void GRIBUICtrlBar::OnCompositeDialog(wxCommandEvent &event) {
1206 initSettings.Read();
1209 wxString json_begin = initSettings.SettingsToJSON(json);
1210 wxLogMessage(json_begin);
1219 double lon_min = wxRound(current_vp.
lon_min) - 1;
1220 double lon_max = wxRound(current_vp.
lon_max) + 1;
1221 double lat_min = wxRound(current_vp.
lat_min) - 1;
1222 double lat_max = wxRound(current_vp.
lat_max) + 1;
1226 int numErrors = reader.
Parse(json_begin, &v);
1227 if (numErrors > 0) {
1231 v[_T(
"latMin")] = lat_min;
1232 v[_T(
"latMax")] = lat_max;
1233 v[_T(
"lonMin")] = lon_min;
1234 v[_T(
"lonMax")] = lon_max;
1238 v[_T(
"grib_file")] = _T(
"");
1241 wxString json_final;
1242 w.
Write(v, json_final);
1243 wxLogMessage(json_final);
1245#ifdef __OCPN__ANDROID__
1246 wxString ret = callActivityMethod_ss(
"doGRIBActivity", json_final);
1253void GRIBUICtrlBar::OpenFileFromJSON(wxString json) {
1259 int numErrors = reader.
Parse(json, &root);
1260 if (numErrors > 0) {
1264 wxString file = root[(_T(
"grib_file"))].AsString();
1266 if (file.Length() && wxFileExists(file)) {
1267 wxFileName fn(file);
1275void GRIBUICtrlBar::OnPlayStop(wxCommandEvent &event) {
1279 m_bpPlay->SetBitmapLabel(
1280 GetScaledBitmap(wxBitmap(stop), _T(
"stop"), m_ScaledFactor));
1281 m_bpPlay->SetToolTip(_(
"Stop play back"));
1283 wxTIMER_CONTINUOUS);
1288void GRIBUICtrlBar::OnPlayStopTimer(wxTimerEvent &event) {
1289 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax()) {
1292 ComputeBestForecastForNow();
1293 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax())
1297 m_sTimeline->SetValue(0);
1304 ? GetNearestValue(GetNow(), 1)
1305 : GetNearestIndex(GetNow(), 2)
1306 : m_sTimeline->GetValue();
1307 m_sTimeline->SetValue(value + 1);
1311 if (!m_InterpolateMode)
1312 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1316void GRIBUICtrlBar::StopPlayBack() {
1319 m_bpPlay->SetBitmapLabel(
1320 GetScaledBitmap(wxBitmap(play), _T(
"play"), m_ScaledFactor));
1321 m_bpPlay->SetToolTip(_(
"Start play back"));
1325void GRIBUICtrlBar::TimelineChanged() {
1327 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(
nullptr);
1331 RestaureSelectionString();
1337 if (!m_InterpolateMode) {
1340 GribRecordSet &sel = rsa->Item(m_cRecordForecast->GetCurrentSelection());
1343 ? wxTimeSpan(t - MinTime()).GetMinutes() /
1346 : m_cRecordForecast->GetCurrentSelection());
1348 m_cRecordForecast->SetSelection(GetNearestIndex(time, 2));
1349 SaveSelectionString();
1350 wxString formattedTime = toUsrDateTimeFormat_Plugin(time);
1351 m_cRecordForecast->SetString(m_Selection_index,
1354 m_cRecordForecast->SetStringSelection(
1360 pPlugIn->SendTimelineMessage(time);
1364void GRIBUICtrlBar::RestaureSelectionString() {
1365 if (!m_SelectionIsSaved)
return;
1367 int sel = m_cRecordForecast->GetSelection();
1368 m_cRecordForecast->SetString(m_Selection_index, m_Selection_label);
1369 m_cRecordForecast->SetSelection(sel);
1370 m_SelectionIsSaved =
false;
1373int GRIBUICtrlBar::GetNearestIndex(wxDateTime time,
int model) {
1378 wxDateTime itime, ip1time;
1379 for (i = 0; i < rsa->GetCount() - 1; i++) {
1380 itime = rsa->Item(i).m_Reference_Time;
1381 ip1time = rsa->Item(i + 1).m_Reference_Time;
1382 if (ip1time >= time)
break;
1384 if (!model)
return (time - itime > (ip1time - time) * 3) ? i + 1 : i;
1386 return model == 1 ? time == ip1time ? i : i + 1 : time == ip1time ? i + 1 : i;
1389int GRIBUICtrlBar::GetNearestValue(wxDateTime time,
int model) {
1391 if (m_TimeLineHours == 0)
return 0;
1392 wxDateTime itime, ip1time;
1395 wxTimeSpan span = time - MinTime();
1396 int t = span.GetMinutes() / stepmin;
1398 wxTimeSpan(t * stepmin / 60, (t * stepmin) % 60);
1399 ip1time = itime + wxTimeSpan(stepmin / 60, stepmin % 60);
1401 if (model == 1)
return time == ip1time ? t + 1 : t;
1403 return (time - itime > (ip1time - time) * 3) ? t + 1 : t;
1406wxDateTime GRIBUICtrlBar::GetNow() {
1407 wxDateTime now = wxDateTime::Now();
1413 now = (now > rsa->Item(rsa->GetCount() - 1).m_Reference_Time)
1414 ? rsa->Item(rsa->GetCount() - 1).m_Reference_Time
1415 : (now < rsa->Item(0).m_Reference_Time) ? rsa->Item(0).m_Reference_Time
1421 if (m_InterpolateMode) {
1422 int tl = (m_TimeLineHours == 0) ? 0 : m_sTimeline->GetValue();
1425 return MinTime() + wxTimeSpan(tl * stepmin / 60, (tl * stepmin) % 60);
1429 unsigned int index = m_cRecordForecast->GetCurrentSelection() < 1
1431 : m_cRecordForecast->GetCurrentSelection();
1432 if (rsa && index < rsa->GetCount())
return rsa->Item(index).m_Reference_Time;
1434 return wxDateTime::Now();
1437wxDateTime GRIBUICtrlBar::MinTime() {
1439 if (rsa && rsa->GetCount()) {
1443 return wxDateTime::Now();
1450 if (rsa->GetCount() == 0)
return nullptr;
1457 wxDateTime GR1time, GR2time;
1463 for (j = 0; j < rsa->GetCount(); j++) {
1469 if (curtime <= time) GR1time = curtime, GRS1 = GRS, GR1 = GR;
1471 if (curtime >= time) {
1472 GR2time = curtime, GRS2 = GRS, GR2 = GR;
1477 if (!GR1 || !GR2)
continue;
1479 wxDateTime mintime = MinTime();
1480 double minute2 = (GR2time - mintime).GetMinutes();
1481 double minute1 = (GR1time - mintime).GetMinutes();
1482 double nminute = (time - mintime).GetMinutes();
1484 if (minute2 < minute1 || nminute < minute1 || nminute > minute2)
continue;
1486 double interp_const;
1487 if (minute1 == minute2) {
1492 interp_const = (nminute - minute1) / (minute2 - minute1);
1523 *GR1, *GR2, interp_const, i ==
Idx_WVDIR));
1534 auto sog = m_ProjectBoatPanel->GetSpeed();
1535 auto cog = m_ProjectBoatPanel->GetCourse();
1537 static_cast<double>(now.GetTicks() -
pPlugIn->m_boat_time) * sog / 3600.0;
1539 pPlugIn->m_boat_lon, cog, dist,
1540 &m_projected_lat, &m_projected_lon);
1548double GRIBUICtrlBar::getTimeInterpolatedValue(
int idx,
double lon,
double lat,
1553 if (rsa->GetCount() == 0)
return GRIB_NOTDEF;
1555 GribRecord *before =
nullptr, *after =
nullptr;
1558 time_t t = time.GetTicks();
1559 for (j = 0; j < rsa->GetCount(); j++) {
1564 time_t curtime = GR->getRecordCurrentDate();
1567 if (curtime < t) before = GR;
1575 if (!before || !after)
return GRIB_NOTDEF;
1577 time_t t1 = before->getRecordCurrentDate();
1578 time_t t2 = after->getRecordCurrentDate();
1582 double v2 = after->getInterpolatedValue(lon, lat);
1583 if (v1 != GRIB_NOTDEF && v2 != GRIB_NOTDEF) {
1584 double k = fabs((
double)(t - t1) / (t2 - t1));
1585 return (1.0 - k) * v1 + k * v2;
1591bool GRIBUICtrlBar::getTimeInterpolatedValues(
double &M,
double &A,
int idx1,
1592 int idx2,
double lon,
double lat,
1600 if (rsa->GetCount() == 0)
return false;
1602 GribRecord *beforeX =
nullptr, *afterX =
nullptr;
1603 GribRecord *beforeY =
nullptr, *afterY =
nullptr;
1606 time_t t = time.GetTicks();
1607 for (j = 0; j < rsa->GetCount(); j++) {
1611 if (!GX || !GY)
continue;
1613 time_t curtime = GX->getRecordCurrentDate();
1628 if (!beforeX || !afterX)
return false;
1630 time_t t1 = beforeX->getRecordCurrentDate();
1631 time_t t2 = afterX->getRecordCurrentDate();
1636 double v1m, v2m, v1a, v2a;
1645 if (v1m == GRIB_NOTDEF || v2m == GRIB_NOTDEF || v1a == GRIB_NOTDEF ||
1649 double k = fabs((
double)(t - t1) / (t2 - t1));
1650 M = (1.0 - k) * v1m + k * v2m;
1651 A = (1.0 - k) * v1a + k * v2a;
1655void GRIBUICtrlBar::OnTimeline(wxScrollEvent &event) {
1658 if (!m_InterpolateMode)
1659 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1664void GRIBUICtrlBar::OnOpenFile(wxCommandEvent &event) {
1668#ifndef __OCPN__ANDROID__
1670 wxStandardPathsBase &path = wxStandardPaths::Get();
1671 wxString l_grib_dir = path.GetDocumentsDir();
1675 wxFileDialog *dialog =
1676 new wxFileDialog(
nullptr, _(
"Select a GRIB file"), l_grib_dir, _T(
""),
1678 "(*.grb;*.bz2;*.gz;*.grib2;*.grb2)|*.grb;*.bz2;*.gz;"
1679 "*.grib2;*.grb2|All files (*)|*.*"),
1680 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE,
1681 wxDefaultPosition, wxDefaultSize, _T(
"File Dialog"));
1683 if (dialog->ShowModal() == wxID_OK) {
1684 ::wxBeginBusyCursor();
1690 if (g_pi->m_bZoomToCenterAtInit) DoZoomToCenter();
1692 SetDialogsStyleSizePosition(
true);
1697 wxStandardPathsBase &path = wxStandardPaths::Get();
1703 nullptr, &file, _(
"Select a GRIB file"),
m_grib_dir, _T(
""), _T(
"*.*"));
1705 if (response == wxID_OK) {
1706 wxFileName fn(file);
1711 SetDialogsStyleSizePosition(
true);
1716void GRIBUICtrlBar::CreateActiveFileFromNames(
const wxArrayString &filenames) {
1717 if (filenames.GetCount() != 0) {
1724void GRIBUICtrlBar::PopulateComboDataList() {
1726 if (m_cRecordForecast->GetCount()) {
1727 index = m_cRecordForecast->GetCurrentSelection();
1728 m_cRecordForecast->Clear();
1732 for (
size_t i = 0; i < rsa->GetCount(); i++) {
1733 wxDateTime t(rsa->Item(i).m_Reference_Time);
1734 m_cRecordForecast->Append(toUsrDateTimeFormat_Plugin(t));
1736 m_cRecordForecast->SetSelection(index);
1739void GRIBUICtrlBar::OnZoomToCenterClick(wxCommandEvent &event) {
1744 double latmin,latmax,lonmin,lonmax;
1745 if(!GetGribZoneLimits(
m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax ))
1751 double width = lonmax - lonmin;
1752 double height = latmax - latmin;
1755 double clat = latmin + height / 2;
1756 double clon = lonmin + width / 2;
1760 lonmin = clon - 60.;
1761 lonmax = clon + 60.;
1764 latmin = clat - 60.;
1765 latmax = clat + 60.;
1775 int w =
pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetWidth();
1776 int h =
pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetHeight();
1780 ppm = wxMin(w/(ow*1852), h/(oh*1852)) * ( 100 - fabs( clat ) ) / 90;
1782 ppm = wxMin(ppm, 1.0);
1790void GRIBUICtrlBar::DoZoomToCenter() {
1793 double latmin, latmax, lonmin, lonmax;
1794 if (!GetGribZoneLimits(
m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax))
1800 double width = lonmax - lonmin;
1801 double height = latmax - latmin;
1804 double clat = latmin + height / 2;
1805 double clon = lonmin + width / 2;
1809 lonmin = clon - 60.;
1810 lonmax = clon + 60.;
1812 if (height > 120.) {
1813 latmin = clat - 60.;
1814 latmax = clat + 60.;
1822 wxWindow *wx = GetGRIBCanvas();
1824 int w = wx->GetSize().x;
1825 int h = wx->GetSize().y;
1829 ppm = wxMin(w / (ow * 1852), h / (oh * 1852)) * (100 - fabs(clat)) / 90;
1831 ppm = wxMin(ppm, 1.0);
1836void GRIBUICtrlBar::OnPrev(wxCommandEvent &event) {
1840 RestaureSelectionString();
1844 selection = GetNearestIndex(GetNow(), 1);
1845 else if (m_InterpolateMode)
1849 selection = m_cRecordForecast->GetCurrentSelection();
1852 m_InterpolateMode =
false;
1854 m_cRecordForecast->SetSelection(selection < 1 ? 0 : selection - 1);
1859void GRIBUICtrlBar::OnNext(wxCommandEvent &event) {
1863 RestaureSelectionString();
1867 selection = GetNearestIndex(GetNow(), 2);
1868 else if (m_InterpolateMode)
1872 selection = m_cRecordForecast->GetCurrentSelection();
1874 m_cRecordForecast->SetSelection(selection);
1877 m_InterpolateMode =
false;
1879 if (selection == (
int)m_cRecordForecast->GetCount() - 1)
1882 m_cRecordForecast->SetSelection(selection + 1);
1887void GRIBUICtrlBar::ComputeBestForecastForNow() {
1889 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(
nullptr);
1893 wxDateTime now = GetNow();
1896 m_sTimeline->SetValue(GetNearestValue(now, 0));
1898 m_cRecordForecast->SetSelection(GetNearestIndex(now, 0));
1899 m_sTimeline->SetValue(m_cRecordForecast->GetCurrentSelection());
1902 if (
pPlugIn->GetStartOptions() !=
1909 m_InterpolateMode =
true;
1911 SetGribTimelineRecordSet(
1914 RestaureSelectionString();
1916 m_cRecordForecast->SetSelection(GetNearestIndex(now, 2));
1917 SaveSelectionString();
1918 wxString nowTime = toUsrDateTimeFormat_Plugin(now);
1919 m_cRecordForecast->SetString(m_Selection_index,
1922 m_cRecordForecast->SetStringSelection(nowTime);
1926 pPlugIn->SendTimelineMessage(now);
1930void GRIBUICtrlBar::SetGribTimelineRecordSet(
1935 if (!
pPlugIn->GetGRIBOverlayFactory())
return;
1940void GRIBUICtrlBar::SetTimeLineMax(
bool SetValue) {
1941 int oldmax = wxMax(m_sTimeline->GetMax(), 1),
1942 oldval = m_sTimeline->GetValue();
1947 m_sTimeline->SetMax(m_TimeLineHours * 60 / stepmin);
1951 m_sTimeline->SetMax(rsa->GetCount() - 1);
1956 if (SetValue && m_sTimeline->GetMax() != 0) {
1958 ComputeBestForecastForNow();
1960 m_sTimeline->SetValue(m_sTimeline->GetMax() * oldval / oldmax);
1964void GRIBUICtrlBar::SetFactoryOptions() {
1967 pPlugIn->GetGRIBOverlayFactory()->ClearCachedData();
1973void GRIBUICtrlBar::OnFormatRefreshTimer(wxTimerEvent &event) {
1976 wxDateTime referenceDate(1, wxDateTime::Jan, 2021, 12, 0, 0);
1977 wxString currentFormat = toUsrDateTimeFormat_Plugin(referenceDate);
1979 if (currentFormat != m_sLastTimeFormat) {
1981 m_sLastTimeFormat = currentFormat;
1985 PopulateComboDataList();
1999unsigned int GRIBFile::ID = 0;
2005 m_pGribReader =
nullptr;
2006 m_last_message = wxEmptyString;
2007 for (
unsigned int i = 0; i < file_names.GetCount(); i++) {
2008 wxString file_name = file_names[i];
2009 if (::wxFileExists(file_name)) m_bOK =
true;
2012 if (m_bOK ==
false) {
2013 m_last_message = _(
" files don't exist!");
2022 for (
unsigned int i = 0; i < file_names.GetCount(); i++) {
2023 file_name = file_names[i];
2024 m_pGribReader->openFile(file_name);
2026 if (m_pGribReader->isOk()) {
2033 if (m_bOK ==
false) {
2034 m_last_message = _(
" can't be read!");
2039 m_FileNames.Clear();
2040 m_FileNames.Add(file_name);
2042 m_FileNames = file_names;
2046 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_TOT, LV_GND_SURF, 0);
2047 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_RATE, LV_GND_SURF, 0);
2048 m_pGribReader->computeAccumulationRecords(GRB_CLOUD_TOT, LV_ATMOS_ALL, 0);
2056 m_nGribRecords = m_pGribReader->getTotalNumberOfGribRecords();
2060 std::set<time_t>::iterator iter;
2061 std::set<time_t> date_list = m_pGribReader->getListDates();
2062 for (iter = date_list.begin(); iter != date_list.end(); iter++) {
2064 time_t reftime = *iter;
2066 m_GribRecordSetArray.Add(t);
2074 bool polarWind(
false);
2075 bool polarCurrent(
false);
2076 bool sigWave(
false);
2079 std::map<std::string, std::vector<GribRecord *> *> *p_map =
2080 m_pGribReader->getGribMap();
2083 std::map<std::string, std::vector<GribRecord *> *>::iterator it;
2084 for (it = p_map->begin(); it != p_map->end(); it++) {
2085 std::vector<GribRecord *> *ls = (*it).second;
2086 for (zuint i = 0; i < ls->size(); i++) {
2089 time_t thistime = pRec->getRecordCurrentDate();
2092 for (
unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2093 if (m_GribRecordSetArray.Item(j).m_Reference_Time == thistime) {
2094 int idx = -1, mdx = -1;
2118 case GRB_WIND_SPEED:
2141 polarCurrent =
true;
2147 polarCurrent =
true;
2179 case GRB_PRECIP_RATE:
2180 case GRB_PRECIP_TOT:
2205 mdx = 1000 + NORWAY_METNO;
2235 case GRB_GEOPOT_HGT:
2261 if (m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx]) {
2264 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2286 if (oRec->
getDataType() == GRB_HTSGW) skip =
true;
2296 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx] = pRec;
2297 if (m_GribIdxArray.Index(idx) == wxNOT_FOUND)
2298 m_GribIdxArray.Add(idx, 1);
2299 if (mdx != -1 && m_GribIdxArray.Index(mdx) == wxNOT_FOUND)
2300 m_GribIdxArray.Add(mdx, 1);
2308 if (polarWind || polarCurrent) {
2309 for (
unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2310 for (
unsigned int i = 0; i <
Idx_COUNT; i++) {
2314 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2316 if (pRec !=
nullptr && pRec->
getDataType() == GRB_WIND_DIR) {
2338 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2339 if (pRec1 !=
nullptr && pRec1->
getDataType() == GRB_WIND_SPEED)
2347 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2349 if (pRec !=
nullptr && pRec->
getDataType() == GRB_CUR_DIR) {
2359 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2360 if (pRec1 !=
nullptr && pRec1->
getDataType() == GRB_CUR_SPEED)
2371 pRec->getRecordRefDate();
2374GRIBFile::~GRIBFile() {
delete m_pGribReader; }
2381 :
GRIBUICDataBase(parent.pParent, CURSOR_DATA, _(
"GRIB Display Control"),
2382 wxDefaultPosition, wxDefaultSize,
2383 wxSYSTEM_MENU | wxNO_BORDER | wxSTAY_ON_TOP)
2386 wxDefaultPosition, wxDefaultSize,
2387 wxSYSTEM_MENU | wxNO_BORDER)
2390 m_gpparent(parent) {
2394 m_gCursorData =
new CursorData(
this, m_gpparent);
2395 m_fgCdataSizer->Add(m_gCursorData, 0, wxALL, 0);
2397 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICData::OnMove));
2400void GRIBUICData::OnMove(wxMoveEvent &event) {
2402 GetScreenPosition(&w, &h);
2403 m_gpparent.
pPlugIn->SetCursorDataXY(wxPoint(w, h));
2409#ifdef __OCPN__ANDROID__
2411#include <QtAndroidExtras/QAndroidJniObject>
2413bool CheckPendingJNIException() {
2420 if (java_vm->GetEnv((
void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2425 if ((jenv)->ExceptionCheck() == JNI_TRUE) {
2433wxString callActivityMethod_ss(
const char *method, wxString parm) {
2439 if (CheckPendingJNIException())
return _T(
"NOK");
2441 wxString return_string;
2442 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod(
2443 "org/qtproject/qt5/android/QtNative",
"activity",
2444 "()Landroid/app/Activity;");
2445 if (CheckPendingJNIException())
return _T(
"NOK");
2447 if (!activity.isValid()) {
2449 return return_string;
2454 if (java_vm->GetEnv((
void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2456 return "jenv Error";
2459 jstring p = (jenv)->NewStringUTF(parm.c_str());
2465 QAndroidJniObject data = activity.callObjectMethod(
2466 method,
"(Ljava/lang/String;)Ljava/lang/String;", p);
2467 if (CheckPendingJNIException())
return _T(
"NOK");
2471 jstring s = data.object<jstring>();
2473 if ((jenv)->GetStringLength(s)) {
2474 const char *ret_string = (jenv)->GetStringUTFChars(s,
nullptr);
2475 return_string = wxString(ret_string, wxConvUTF8);
2478 return return_string;
@ 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_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.
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.
void SetTableSizePosition(int vpWidth, int vpHeight)
Set the table size and position relative to viewport.
void InitGribTable(ArrayOfGribRecordSets *rsa)
Initialize the GRIB data table.
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.
zuchar getTimeRange() const
Returns the time range indicator that defines how P1 and P2 should be interpreted.
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.
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.
zuchar getLevelType() const
Returns the type of vertical level for this grid's data.
zuchar getDataType() const
Returns the type of meteorological parameter stored in this grid.
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...
bool GetCopyMissWaveRec()
Returns true if wave data should be propagated across time periods where wave records are missing.
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
The JSON document writer.
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.