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>
46#include <wx/arrimpl.cpp>
49#include "android_jvm.h"
53double m_cursor_lat, m_cursor_lon;
56int m_SavedZoneSelMode;
66 return ((x - i) >= 0.5) ? (i + 1) : (i);
68 return (-x + i >= 0.5) ? (i - 1) : (i);
78#if wxCHECK_VERSION(2, 9, 4)
79#define SetBitmapLabelLabel SetBitmap
82#define DEFAULT_STYLE \
83 = wxCAPTION | wxCLOSE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL
85WX_DEFINE_OBJARRAY(ArrayOfGribRecordSets);
88static int CompareFileStringTime(
const wxString &first,
89 const wxString &second) {
92 wxTimeSpan sp = s.GetModificationTime() - f.GetModificationTime();
93 return sp.GetMinutes();
100static wxString TToString(
const wxDateTime date_time,
const int time_zone) {
101 wxDateTime t(date_time);
104 if ((wxDateTime::Now() == (wxDateTime::Now().ToGMT())) &&
106 t.Add(wxTimeSpan(1, 0, 0, 0));
107 return t.Format(_T(
" %a %d-%b-%Y %H:%M "), wxDateTime::Local) +
111 return t.Format(_T(
" %a %d-%b-%Y %H:%M "), wxDateTime::UTC) + _T(
"UTC");
115wxWindow *GetGRIBCanvas() {
118 if (GetCanvasCount() > 1)
119 wx = GetCanvasByIndex(1);
121 wx = GetOCPNCanvasWindow();
138GribTimelineRecordSet::~GribTimelineRecordSet() {
143void GribTimelineRecordSet::ClearCachedData() {
147 for (
unsigned int j = 0; j <
m_IsobarArray[i]->GetCount(); j++) {
161GRIBUICtrlBar::GRIBUICtrlBar(wxWindow *parent, wxWindowID
id,
162 const wxString &title,
const wxPoint &pos,
163 const wxSize &size,
long style,
grib_pi *ppi)
170 pReq_Dialog =
nullptr;
171 m_bGRIBActiveFile =
nullptr;
172 m_pTimelineSet =
nullptr;
173 m_gCursorData =
nullptr;
174 m_gGRIBUICData =
nullptr;
175 m_gtk_started =
false;
177 wxFileConfig *pConf = GetOCPNConfigObject();
180 m_fgCtrlGrabberSize->Add(m_gGrabber, 0, wxALL, 0);
182 this->SetSizer(m_fgCtrlBarSizer);
184 m_fgCtrlBarSizer->Fit(
this);
187 pConf->SetPath(_T (
"/Settings/GRIB" ));
188 pConf->Read(_T (
"WindPlot" ), &m_bDataPlot[GribOverlaySettings::WIND],
190 pConf->Read(_T (
"WindGustPlot" ),
191 &m_bDataPlot[GribOverlaySettings::WIND_GUST],
false);
192 pConf->Read(_T (
"PressurePlot" ),
193 &m_bDataPlot[GribOverlaySettings::PRESSURE],
false);
194 pConf->Read(_T (
"WavePlot" ), &m_bDataPlot[GribOverlaySettings::WAVE],
196 pConf->Read(_T (
"CurrentPlot" ),
197 &m_bDataPlot[GribOverlaySettings::CURRENT],
false);
198 pConf->Read(_T (
"PrecipitationPlot" ),
199 &m_bDataPlot[GribOverlaySettings::PRECIPITATION],
false);
200 pConf->Read(_T (
"CloudPlot" ), &m_bDataPlot[GribOverlaySettings::CLOUD],
202 pConf->Read(_T (
"AirTemperaturePlot" ),
203 &m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE],
false);
204 pConf->Read(_T (
"SeaTemperaturePlot" ),
205 &m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE],
false);
206 pConf->Read(_T (
"CAPEPlot" ), &m_bDataPlot[GribOverlaySettings::CAPE],
208 pConf->Read(_T (
"CompReflectivityPlot" ),
209 &m_bDataPlot[GribOverlaySettings::COMP_REFL],
false);
211 pConf->Read(_T (
"CursorDataShown" ), &m_CDataIsShown,
true);
213 pConf->Read(_T (
"lastdatatype" ), &m_lastdatatype, 0);
215 pConf->SetPath(_T (
"/Settings/GRIB/FileNames" ));
216 m_file_names.Clear();
217 if (pConf->GetNumberOfEntries()) {
220 bool bCont = pConf->GetFirstEntry(str, dummy);
222 pConf->Read(str, &val);
223 m_file_names.Add(val);
224 bCont = pConf->GetNextEntry(str, dummy);
228 wxStandardPathsBase &spath = wxStandardPaths::Get();
230 pConf->SetPath(_T (
"/Directories" ));
231 pConf->Read(_T (
"GRIBDirectory" ), &m_grib_dir);
233 pConf->SetPath(_T(
"/PlugIns/GRIB" ));
234 pConf->Read(_T(
"ManualRequestZoneSizing" ), &m_SavedZoneSelMode, 0);
237 pConf->SetPath(_T (
"/Settings/GRIB/XyGrib" ));
238 pConf->Read(_T(
"AtmModelIndex" ), &xyGribConfig.atmModelIndex, 0);
239 pConf->Read(_T(
"WaveModelIndex" ), &xyGribConfig.waveModelIndex, 0);
240 pConf->Read(_T(
"ResolutionIndex" ), &xyGribConfig.resolutionIndex, 0);
241 pConf->Read(_T(
"DurationIndex" ), &xyGribConfig.durationIndex, 0);
242 pConf->Read(_T(
"RunIndex" ), &xyGribConfig.runIndex, 0);
243 pConf->Read(_T(
"IntervalIndex" ), &xyGribConfig.intervalIndex, 0);
244 pConf->Read(_T(
"Wind" ), &xyGribConfig.wind,
true);
245 pConf->Read(_T(
"Gust" ), &xyGribConfig.gust,
true);
246 pConf->Read(_T(
"Pressure" ), &xyGribConfig.pressure,
false);
247 pConf->Read(_T(
"Temperature" ), &xyGribConfig.temperature,
true);
248 pConf->Read(_T(
"Cape" ), &xyGribConfig.cape,
false);
249 pConf->Read(_T(
"Reflectivity" ), &xyGribConfig.reflectivity,
false);
250 pConf->Read(_T(
"CloudCover" ), &xyGribConfig.cloudCover,
true);
251 pConf->Read(_T(
"Precipitation" ), &xyGribConfig.precipitation,
true);
252 pConf->Read(_T(
"WaveHeight" ), &xyGribConfig.waveHeight,
true);
253 pConf->Read(_T(
"WindWaves" ), &xyGribConfig.windWaves,
true);
256 m_ZoneSelMode = m_SavedZoneSelMode;
259 m_tPlayStop.Connect(wxEVT_TIMER,
260 wxTimerEventHandler(GRIBUICtrlBar::OnPlayStopTimer),
263 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICtrlBar::OnMove));
265 m_OverlaySettings.Read();
270 SetMinSize(GetBestSize());
271 if (m_ProjectBoatPanel) {
272 m_ProjectBoatPanel->SetSpeed(pPlugIn->m_boat_sog);
273 m_ProjectBoatPanel->SetCourse(pPlugIn->m_boat_cog);
275 m_highlight_latmax = 0;
276 m_highlight_lonmax = 0;
277 m_highlight_latmin = 0;
278 m_highlight_lonmin = 0;
281GRIBUICtrlBar::~GRIBUICtrlBar() {
282 wxFileConfig *pConf = GetOCPNConfigObject();
286 pConf->SetPath(_T (
"/Settings/GRIB" ));
287 pConf->Write(_T (
"WindPlot" ), m_bDataPlot[GribOverlaySettings::WIND]);
288 pConf->Write(_T (
"WindGustPlot" ),
289 m_bDataPlot[GribOverlaySettings::WIND_GUST]);
290 pConf->Write(_T (
"PressurePlot" ),
291 m_bDataPlot[GribOverlaySettings::PRESSURE]);
292 pConf->Write(_T (
"WavePlot" ), m_bDataPlot[GribOverlaySettings::WAVE]);
293 pConf->Write(_T (
"CurrentPlot" ),
294 m_bDataPlot[GribOverlaySettings::CURRENT]);
295 pConf->Write(_T (
"PrecipitationPlot" ),
296 m_bDataPlot[GribOverlaySettings::PRECIPITATION]);
297 pConf->Write(_T (
"CloudPlot" ), m_bDataPlot[GribOverlaySettings::CLOUD]);
298 pConf->Write(_T (
"AirTemperaturePlot" ),
299 m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE]);
300 pConf->Write(_T (
"SeaTemperaturePlot" ),
301 m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE]);
302 pConf->Write(_T (
"CAPEPlot" ), m_bDataPlot[GribOverlaySettings::CAPE]);
303 pConf->Write(_T (
"CompReflectivityPlot" ),
304 m_bDataPlot[GribOverlaySettings::COMP_REFL]);
306 pConf->Write(_T (
"CursorDataShown" ), m_CDataIsShown);
308 pConf->Write(_T (
"lastdatatype" ), m_lastdatatype);
310 pConf->SetPath(_T (
"/Settings/GRIB/FileNames" ));
311 int iFileMax = pConf->GetNumberOfEntries();
315 for (
int i = 0; i < iFileMax; i++) {
316 if (pConf->GetFirstEntry(key, dummy)) pConf->DeleteEntry(key,
false);
320 for (
unsigned int i = 0; i <
m_file_names.GetCount(); i++) {
322 key.Printf(_T(
"Filename%d"), i);
326 pConf->SetPath(_T (
"/Directories" ));
327 pConf->Write(_T (
"GRIBDirectory" ),
m_grib_dir);
330 pConf->SetPath(_T (
"/Settings/GRIB/XyGrib" ));
331 pConf->Write(_T(
"AtmModelIndex" ), xyGribConfig.atmModelIndex);
332 pConf->Write(_T(
"WaveModelIndex" ), xyGribConfig.waveModelIndex);
333 pConf->Write(_T(
"ResolutionIndex" ), xyGribConfig.resolutionIndex);
334 pConf->Write(_T(
"DurationIndex" ), xyGribConfig.durationIndex);
335 pConf->Write(_T(
"RunIndex" ), xyGribConfig.runIndex);
336 pConf->Write(_T(
"IntervalIndex" ), xyGribConfig.intervalIndex);
337 pConf->Write(_T(
"Wind" ), xyGribConfig.wind);
338 pConf->Write(_T(
"Gust" ), xyGribConfig.gust);
339 pConf->Write(_T(
"Pressure" ), xyGribConfig.pressure);
340 pConf->Write(_T(
"Temperature" ), xyGribConfig.temperature);
341 pConf->Write(_T(
"Cape" ), xyGribConfig.cape);
342 pConf->Write(_T(
"Reflectivity" ), xyGribConfig.reflectivity);
343 pConf->Write(_T(
"CloudCover" ), xyGribConfig.cloudCover);
344 pConf->Write(_T(
"Precipitation" ), xyGribConfig.precipitation);
345 pConf->Write(_T(
"WaveHeight" ), xyGribConfig.waveHeight);
346 pConf->Write(_T(
"WindWaves" ), xyGribConfig.windWaves);
352wxBitmap GRIBUICtrlBar::GetScaledBitmap(wxBitmap bitmap,
353 const wxString svgFileName,
354 double scale_factor) {
357 int w = bitmap.GetWidth() - margin;
358 int h = bitmap.GetHeight() - margin;
363 wxString shareLocn = *GetpSharedDataLocation() + _T(
"plugins") +
364 wxFileName::GetPathSeparator() + _T(
"grib_pi") +
365 wxFileName::GetPathSeparator() + _T(
"data") +
366 wxFileName::GetPathSeparator();
367 wxString filename = shareLocn + svgFileName + _T(
".svg");
369 wxBitmap svgbm = GetBitmapFromSVGFile(filename, w, h);
370 if (svgbm.GetWidth() > 0 && svgbm.GetHeight() > 0)
375 wxImage a = bitmap.ConvertToImage();
376 return wxBitmap(a.Scale(w, h), wxIMAGE_QUALITY_HIGH);
380void GRIBUICtrlBar::SetScaledBitmap(
double factor) {
382 m_ScaledFactor = wxRound(factor * 4.0) / 4.0;
384 m_bpPrev->SetBitmapLabel(
385 GetScaledBitmap(wxBitmap(prev), _T(
"prev"), m_ScaledFactor));
386 m_bpNext->SetBitmapLabel(
387 GetScaledBitmap(wxBitmap(next), _T(
"next"), m_ScaledFactor));
388 m_bpAltitude->SetBitmapLabel(
389 GetScaledBitmap(wxBitmap(altitude), _T(
"altitude"), m_ScaledFactor));
390 m_bpNow->SetBitmapLabel(
391 GetScaledBitmap(wxBitmap(now), _T(
"now"), m_ScaledFactor));
392 m_bpZoomToCenter->SetBitmapLabel(
393 GetScaledBitmap(wxBitmap(zoomto), _T(
"zoomto"), m_ScaledFactor));
394 m_bpPlay->SetBitmapLabel(
395 GetScaledBitmap(wxBitmap(play), _T(
"play"), m_ScaledFactor));
396 m_bpShowCursorData->SetBitmapLabel(GetScaledBitmap(
397 wxBitmap(m_CDataIsShown ? curdata : ncurdata),
398 m_CDataIsShown ? _T(
"curdata") : _T(
"ncurdata"), m_ScaledFactor));
400 m_bpOpenFile->SetBitmapLabel(
401 GetScaledBitmap(wxBitmap(openfile), _T(
"openfile"), m_ScaledFactor));
402 m_bpSettings->SetBitmapLabel(
403 GetScaledBitmap(wxBitmap(setting), _T(
"setting"), m_ScaledFactor));
405 SetRequestBitmap(m_ZoneSelMode);
409#ifdef __OCPN__ANDROID__
410 m_sTimeline->SetSize(wxSize(20 * m_ScaledFactor, -1));
411 m_sTimeline->SetMinSize(wxSize(20 * m_ScaledFactor, -1));
413 m_sTimeline->SetSize(wxSize(90 * m_ScaledFactor, -1));
414 m_sTimeline->SetMinSize(wxSize(90 * m_ScaledFactor, -1));
418void GRIBUICtrlBar::SetRequestBitmap(
int type) {
419 if (
nullptr == m_bpRequest)
return;
423 case SAVED_SELECTION:
424 case START_SELECTION:
425 m_bpRequest->SetBitmapLabel(
426 GetScaledBitmap(wxBitmap(request), _T(
"request"), m_ScaledFactor));
427 m_bpRequest->SetToolTip(_(
"Start a request"));
430 m_bpRequest->SetBitmapLabel(
431 GetScaledBitmap(wxBitmap(selzone), _T(
"selzone"), m_ScaledFactor));
432 m_bpRequest->SetToolTip(
433 _(
"Draw requested Area\nor Click here to stop request"));
435 case COMPLETE_SELECTION:
436 m_bpRequest->SetBitmapLabel(GetScaledBitmap(
437 wxBitmap(request_end), _T(
"request_end"), m_ScaledFactor));
438 m_bpRequest->SetToolTip(_(
"Valid Area and Continue"));
443void GRIBUICtrlBar::OpenFile(
bool newestFile) {
444 m_bpPlay->SetBitmapLabel(
445 GetScaledBitmap(wxBitmap(play), _T(
"play"), m_ScaledFactor));
446 m_cRecordForecast->Clear();
447 pPlugIn->GetGRIBOverlayFactory()->ClearParticles();
453 m_sTimeline->SetValue(0);
455 m_InterpolateMode =
false;
457 m_SelectionIsSaved =
false;
458 m_HasAltitude =
false;
479 title = (_(
"File: "));
480 title.Append(fn.GetFullName());
481 if (rsa->GetCount() == 0) {
484 title.Prepend(_(
"Error! ")).Append(_(
" contains no valid data!"));
486 PopulateComboDataList();
487 title.append(_T(
" (") +
492 if (rsa->GetCount() > 1) {
494 &last = rsa->Item(rsa->GetCount() - 1);
497 wxTimeSpan span = wxDateTime(last.m_Reference_Time) -
499 m_TimeLineHours = span.GetHours();
502 int halfintermin(wxTimeSpan(wxDateTime(second.m_Reference_Time) -
506 for (m_FileIntervalIndex = 0;; m_FileIntervalIndex++) {
511 if (m_FileIntervalIndex > 0) m_FileIntervalIndex--;
519 title = _(
"No valid GRIB file");
521 pPlugIn->GetGRIBOverlayFactory()->SetMessage(title);
523 SetTimeLineMax(
false);
525 if (
pPlugIn->GetStartOptions() &&
526 m_TimeLineHours != 0)
527 ComputeBestForecastForNow();
533 for (
int i = 1; i < 5; i++) {
538 m_HasAltitude =
true;
543#ifdef __OCPN__ANDROID__
544 m_bpSettings->Enable(
true);
550 m_sTimeline->Enable(
m_pTimelineSet !=
nullptr && m_TimeLineHours);
557 SetCanvasContextMenuItemViz(
pPlugIn->m_MenuItem, m_TimeLineHours != 0);
569 bool bconfigOK =
false;
570 if (m_bDataPlot[GribOverlaySettings::WIND] &&
573 if (m_bDataPlot[GribOverlaySettings::WIND_GUST] &&
576 if (m_bDataPlot[GribOverlaySettings::PRESSURE] &&
579 if (m_bDataPlot[GribOverlaySettings::WAVE] &&
582 if (m_bDataPlot[GribOverlaySettings::WAVE] &&
585 if (m_bDataPlot[GribOverlaySettings::CURRENT] &&
589 if (m_bDataPlot[GribOverlaySettings::PRECIPITATION] &&
592 if (m_bDataPlot[GribOverlaySettings::CLOUD] &&
595 if (m_bDataPlot[GribOverlaySettings::AIR_TEMPERATURE] &&
598 if (m_bDataPlot[GribOverlaySettings::SEA_TEMPERATURE] &&
601 if (m_bDataPlot[GribOverlaySettings::CAPE] &&
604 if (m_bDataPlot[GribOverlaySettings::COMP_REFL] &&
611 for (
int i = 0; i < (int)GribOverlaySettings::GEO_ALTITUDE; i++) {
613 m_bDataPlot[i] =
true;
620 double *latmin,
double *latmax,
621 double *lonmin,
double *lonmax) {
624 double ltmi = -GRIB_NOTDEF, ltma = GRIB_NOTDEF, lnmi = -GRIB_NOTDEF,
626 for (
unsigned int i = 0; i <
Idx_COUNT; i++) {
629 if (pGRA->getLatMin() < ltmi) ltmi = pGRA->getLatMin();
630 if (pGRA->getLatMax() > ltma) ltma = pGRA->getLatMax();
631 if (pGRA->getLonMin() < lnmi) lnmi = pGRA->getLonMin();
632 if (pGRA->getLonMax() > lnma) lnma = pGRA->getLonMax();
634 if (ltmi == -GRIB_NOTDEF || lnmi == -GRIB_NOTDEF || ltma == GRIB_NOTDEF ||
638 if (latmin) *latmin = ltmi;
639 if (latmax) *latmax = ltma;
640 if (lonmin) *lonmin = lnmi;
641 if (lonmax) *lonmax = lnma;
648 : m_files(files), m_pattern(pattern) {}
649 virtual wxDirTraverseResult OnFile(
const wxString &filename) {
650 if (m_pattern.Matches(filename)) m_files.Add(filename);
651 return wxDIR_CONTINUE;
653 virtual wxDirTraverseResult OnDir(
const wxString &WXUNUSED(dirname)) {
658 wxArrayString &m_files;
659 const wxRegEx &m_pattern;
662wxArrayString GRIBUICtrlBar::GetFilesInDirectory() {
663 wxArrayString file_array;
664 if (!wxDir::Exists(
m_grib_dir))
return file_array;
668 wxRegEx pattern(_T(
".+\\.gri?b2?(\\.(bz2|gz))?$"),
669 wxRE_EXTENDED | wxRE_ICASE | wxRE_NOSUB);
672 dir.Traverse(collector);
674 CompareFileStringTime);
678void GRIBUICtrlBar::SetCursorLatLon(
double lat,
double lon) {
682 if (m_vp && ((lat > m_vp->lat_min) && (lat < m_vp->lat_max)) &&
683 ((lon > m_vp->lon_min) && (lon < m_vp->lon_max)))
684 UpdateTrackingControl();
687void GRIBUICtrlBar::UpdateTrackingControl() {
688 if (!m_CDataIsShown)
return;
690 if (m_DialogStyle >> 1 == SEPARATED) {
691 if (m_gGRIBUICData) {
692 if (!m_gGRIBUICData->m_gCursorData->m_tCursorTrackTimer.IsRunning())
693 m_gGRIBUICData->m_gCursorData->m_tCursorTrackTimer.Start(
694 50, wxTIMER_ONE_SHOT);
698 if (!m_gCursorData->m_tCursorTrackTimer.IsRunning())
699 m_gCursorData->m_tCursorTrackTimer.Start(50, wxTIMER_ONE_SHOT);
704void GRIBUICtrlBar::OnShowCursorData(wxCommandEvent &event) {
705 m_CDataIsShown = !m_CDataIsShown;
706 m_bpShowCursorData->SetBitmapLabel(GetScaledBitmap(
707 wxBitmap(m_CDataIsShown ? curdata : ncurdata),
708 m_CDataIsShown ? _T(
"curdata") : _T(
"ncurdata"), m_ScaledFactor));
709 SetDialogsStyleSizePosition(
true);
712void GRIBUICtrlBar::SetDialogsStyleSizePosition(
bool force_recompute) {
717 if (!force_recompute &&
718 (m_old_DialogStyle == m_DialogStyle
720 (m_old_DialogStyle >> 1 == ATTACHED && m_DialogStyle >> 1 == ATTACHED)))
723 bool m_HasCaption = GetWindowStyleFlag() == (wxCAPTION | wxCLOSE_BOX |
724 wxSYSTEM_MENU | wxTAB_TRAVERSAL);
729 int state = (m_DialogStyle >> 1 == ATTACHED && m_CDataIsShown) ? 0 : 1;
732 bool vis = i > 0 ? true : m_HasAltitude ? true :
false;
733 if (FindWindow(i + ID_CTRLALTITUDE))
734 FindWindow(i + ID_CTRLALTITUDE)
740 m_bpShowCursorData->SetToolTip(m_CDataIsShown ? _(
"Hide data at cursor")
741 : _(
"Show data at cursor"));
742 m_bpPlay->SetToolTip(_(
"Start play back"));
747 m_gCursorData->Hide();
748 m_fgCDataSizer->Detach(m_gCursorData);
751 SetMinSize(wxSize(0, 0));
755 if (m_gGRIBUICData) {
756 m_gGRIBUICData->Destroy();
757 m_gGRIBUICData =
nullptr;
760 if ((m_DialogStyle >> 1 == SEPARATED || !m_CDataIsShown) &&
767 if (m_CDataIsShown) {
768 if (m_DialogStyle >> 1 == ATTACHED) {
770 if (!m_gCursorData) m_gCursorData =
new CursorData(
this, *
this);
771 pPlugIn->SetDialogFont(m_gCursorData);
772 m_gCursorData->PopulateTrackingControls(
false);
774 if (m_fgCDataSizer->GetItem(m_gCursorData) ==
nullptr)
775 m_fgCDataSizer->Add(m_gCursorData, 0);
776 m_gCursorData->Show();
778 }
else if (m_DialogStyle >> 1 == SEPARATED) {
781 m_gGRIBUICData->m_gCursorData->PopulateTrackingControls(
782 m_DialogStyle == SEPARATED_VERTICAL);
783 pPlugIn->SetDialogFont(m_gGRIBUICData->m_gCursorData);
784 m_gGRIBUICData->Fit();
785 m_gGRIBUICData->Update();
786 m_gGRIBUICData->Show();
788 m_gGRIBUICData->Layout();
789 m_gGRIBUICData->Fit();
794 wxSize sd = GetSize();
796 if (!m_gtk_started && m_HasCaption ) {
798 m_gtk_started =
true;
801 SetSize(wxSize(sd.x, sd.y));
802 SetMinSize(wxSize(sd.x, sd.y));
803#ifdef __OCPN__ANDROID__
804 wxRect tbRect = GetMasterToolbarRect();
809 wxPoint pNew =
pPlugIn->GetCtrlBarXY();
810 pNew.x = tbRect.x + tbRect.width + 4;
816 GetCanvasByIndex(0)->GetClientSize().x - (tbRect.x + tbRect.width);
818 if (sd.x > widthAvail) {
821 int target_char_width = (float)widthAvail / 28;
830 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL,
832 dc.GetTextExtent(_T(
"W"), &width, &height,
nullptr,
nullptr, sFont);
833 if (width <= target_char_width) bOK =
true;
835 if (pointSize <= 10) bOK =
true;
838 m_cRecordForecast->SetFont(*sFont);
843 SetSize(wxSize(widthAvail, sd.y));
844 SetMinSize(wxSize(widthAvail, sd.y));
848 wxPoint pNow =
pPlugIn->GetCtrlBarXY();
855 m_old_DialogStyle = m_DialogStyle;
858void GRIBUICtrlBar::OnAltitude(wxCommandEvent &event) {
859 if (!m_HasAltitude)
return;
861 wxMenu *amenu =
new wxMenu();
862 amenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
863 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent),
nullptr,
this);
865 for (
int i = 0; i < 5; i++) {
873 amenu, ID_CTRLALTITUDE + 1000 + i,
882 amenu->Check(ID_CTRLALTITUDE + 1000 + m_Altitude,
true);
889void GRIBUICtrlBar::OnMove(wxMoveEvent &event) {
891 GetScreenPosition(&w, &h);
892 pPlugIn->SetCtrlBarXY(wxPoint(w, h));
895void GRIBUICtrlBar::OnMenuEvent(wxMenuEvent &event) {
896 int id =
event.GetId();
899 int alt = m_Altitude;
902 case ID_CTRLALTITUDE + 1000:
905 case ID_CTRLALTITUDE + 1001:
908 case ID_CTRLALTITUDE + 1002:
911 case ID_CTRLALTITUDE + 1003:
914 case ID_CTRLALTITUDE + 1004:
922 OnZoomToCenterClick(evt);
924 case ID_BTNSHOWCDATA:
925 OnShowCursorData(evt);
939 if (alt != m_Altitude) {
940 SetDialogsStyleSizePosition(
true);
945void GRIBUICtrlBar::MenuAppend(wxMenu *menu,
int id, wxString label,
946 wxItemKind kind, wxBitmap bitmap,
948 wxMenuItem *item =
new wxMenuItem(menu,
id, label, _T(
""), kind);
950 if (submenu) item->SetSubMenu(submenu);
959#if defined(__WXMSW__) || defined(__WXGTK__)
960 if (!bitmap.IsSameAs(wxNullBitmap)) item->SetBitmap(bitmap);
966void GRIBUICtrlBar::OnMouseEvent(wxMouseEvent &event) {
967 if (event.RightDown()) {
969 wxMenu *xmenu =
new wxMenu();
970 xmenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
971 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent),
nullptr,
975 wxMenu *smenu =
new wxMenu();
976 smenu->Connect(wxEVT_COMMAND_MENU_SELECTED,
977 wxMenuEventHandler(GRIBUICtrlBar::OnMenuEvent),
nullptr,
980 for (
int i = 0; i < 5; i++) {
988 smenu, ID_CTRLALTITUDE + 1000 + i,
995 smenu->Check(ID_CTRLALTITUDE + 1000 + m_Altitude,
true);
997 xmenu, wxID_ANY, _(
"Select geopotential altitude"), wxITEM_NORMAL,
998 GetScaledBitmap(wxBitmap(altitude), _T(
"altitude"), m_ScaledFactor),
1001 MenuAppend(xmenu, ID_BTNNOW, _(
"Now"), wxITEM_NORMAL,
1002 GetScaledBitmap(wxBitmap(now), _T(
"now"), m_ScaledFactor));
1003 MenuAppend(xmenu, ID_BTNZOOMTC, _(
"Zoom To Center"), wxITEM_NORMAL,
1004 GetScaledBitmap(wxBitmap(zoomto), _T(
"zoomto"), m_ScaledFactor));
1006 xmenu, ID_BTNSHOWCDATA,
1007 m_CDataIsShown ? _(
"Hide data at cursor") : _(
"Show data at cursor"),
1009 GetScaledBitmap(wxBitmap(m_CDataIsShown ? curdata : ncurdata),
1010 m_CDataIsShown ? _T(
"curdata") : _T(
"ncurdata"),
1014 m_tPlayStop.IsRunning() ? _(
"Stop play back") : _(
"Start play back"),
1016 GetScaledBitmap(wxBitmap(
m_tPlayStop.IsRunning() ? stop : play),
1017 m_tPlayStop.IsRunning() ? _T(
"stop") : _T(
"play"),
1020 xmenu, ID_BTNOPENFILE, _(
"Open a new file"), wxITEM_NORMAL,
1021 GetScaledBitmap(wxBitmap(openfile), _T(
"openfile"), m_ScaledFactor));
1023 xmenu, ID_BTNSETTING, _(
"Settings"), wxITEM_NORMAL,
1024 GetScaledBitmap(wxBitmap(setting), _T(
"setting"), m_ScaledFactor));
1025 bool requeststate1 = m_ZoneSelMode == AUTO_SELECTION ||
1026 m_ZoneSelMode == SAVED_SELECTION ||
1027 m_ZoneSelMode == START_SELECTION;
1028 bool requeststate3 = m_ZoneSelMode == DRAW_SELECTION;
1029 MenuAppend(xmenu, ID_BTNREQUEST,
1030 requeststate1 ? _(
"Start a request")
1032 ? _(
"Draw requested Area or Click here to stop request")
1033 : _(
"Valid Area and Continue"),
1035 GetScaledBitmap(wxBitmap(requeststate1 ? request
1036 : requeststate3 ? selzone
1038 requeststate1 ? _T(
"request")
1039 : requeststate3 ? _T(
"selzone")
1040 : _T(
"request_end"),
1050 if (m_DialogStyle >> 1 == SEPARATED)
return;
1051 wxMouseEvent evt(event);
1054#ifndef __OCPN__ANDROID__
1055 if (m_gCursorData && m_CDataIsShown) {
1056 m_gCursorData->OnMouseEvent(evt);
1061void GRIBUICtrlBar::ContextMenuItemCallback(
int id) {
1063 bool dataisshown = m_CDataIsShown;
1064 m_CDataIsShown =
false;
1066 wxFileConfig *pConf = GetOCPNConfigObject();
1072 GetNearestIndex(GetNow(), 0));
1078 m_CDataIsShown = dataisshown;
1083 if (m_vp == vp)
return;
1088 if (pReq_Dialog) pReq_Dialog->OnVpChange(vp);
1091void GRIBUICtrlBar::OnClose(wxCloseEvent &event) {
1093 if (m_gGRIBUICData) m_gGRIBUICData->Hide();
1095 if (m_ZoneSelMode > START_SELECTION) {
1096 pReq_Dialog->StopGraphicalZoneSelection();
1097 m_ZoneSelMode = START_SELECTION;
1100 pPlugIn->SendTimelineMessage(wxInvalidDateTime);
1102 pPlugIn->OnGribCtrlBarClose();
1105void GRIBUICtrlBar::OnSize(wxSizeEvent &event) {
1107 wxSize p =
event.GetSize();
1113void GRIBUICtrlBar::OnPaint(wxPaintEvent &event) {
1114 wxWindowListNode *node = this->GetChildren().GetFirst();
1117 wxWindow *win = node->GetData();
1118 if (
dynamic_cast<wxBitmapButton *
>(win))
1119 dc.DrawBitmap(
dynamic_cast<wxBitmapButton *
>(win)->GetBitmap(), 5, 5,
1121 node = node->GetNext();
1125void GRIBUICtrlBar::OnRequest(wxCommandEvent &event) {
1130 if (pReq_Dialog && pReq_Dialog->IsShown())
return;
1133 if (m_ZoneSelMode == DRAW_SELECTION) {
1134 assert(pReq_Dialog);
1135 m_ZoneSelMode = START_SELECTION;
1136 pReq_Dialog->StopGraphicalZoneSelection();
1137 SetRequestBitmap(m_ZoneSelMode);
1142 if (m_ZoneSelMode == AUTO_SELECTION || m_ZoneSelMode == SAVED_SELECTION ||
1143 m_ZoneSelMode == START_SELECTION) {
1144 ::wxBeginBusyCursor();
1149 pPlugIn->SetDialogFont(pReq_Dialog);
1150 pPlugIn->SetDialogFont(pReq_Dialog->m_sScrolledDialog);
1151 pReq_Dialog->OnVpChange(m_vp);
1152 pReq_Dialog->SetRequestDialogSize();
1155 ::wxDisplaySize(&w,
nullptr);
1156 pReq_Dialog->Move((w - pReq_Dialog->GetSize().GetX()) / 2, 30);
1160 pReq_Dialog->Show(m_ZoneSelMode == AUTO_SELECTION ||
1161 m_ZoneSelMode == SAVED_SELECTION ||
1162 m_ZoneSelMode == COMPLETE_SELECTION);
1163 m_ZoneSelMode = m_ZoneSelMode == START_SELECTION ? DRAW_SELECTION
1164 : m_ZoneSelMode == COMPLETE_SELECTION ? START_SELECTION
1166 if (m_ZoneSelMode == START_SELECTION)
1167 pReq_Dialog->StopGraphicalZoneSelection();
1168 SetRequestBitmap(m_ZoneSelMode);
1170 if (::wxIsBusy()) ::wxEndBusyCursor();
1173void GRIBUICtrlBar::OnSettings(wxCommandEvent &event) {
1177 ::wxBeginBusyCursor();
1183 pPlugIn->SetDialogFont(dialog);
1184 for (
size_t i = 0; i < dialog->m_nSettingsBook->GetPageCount(); i++) {
1185 wxScrolledWindow *sc =
1186 ((wxScrolledWindow *)dialog->m_nSettingsBook->GetPage(i));
1190 dialog->m_nSettingsBook->ChangeSelection(dialog->GetPageIndex());
1191 dialog->SetSettingsDialogSize();
1194 ::wxDisplaySize(&w,
nullptr);
1195 dialog->Move((w - dialog->GetSize().GetX()) / 2, 30);
1198 ::wxEndBusyCursor();
1200 if (dialog->ShowModal() == wxID_OK) {
1201 dialog->WriteSettings();
1204 initSettings.Settings[GribOverlaySettings::WIND].m_Units &&
1206 GribOverlaySettings::BFS ||
1207 initSettings.Settings[GribOverlaySettings::WIND].m_Units ==
1208 GribOverlaySettings::BFS))
1210 STARTING_STATE_STYLE;
1214 m_DialogStyle = initSettings.m_iCtrlandDataStyle;
1216 ::wxBeginBusyCursor();
1218 dialog->SaveLastPage();
1220 m_InterpolateMode =
false;
1221 SetTimeLineMax(
true);
1222 SetFactoryOptions();
1224 SetDialogsStyleSizePosition(
true);
1230#ifdef __OCPN__ANDROID__
1231wxString callActivityMethod_ss(
const char *method, wxString parm);
1234void GRIBUICtrlBar::OnCompositeDialog(wxCommandEvent &event) {
1237 initSettings.Read();
1240 wxString json_begin = initSettings.SettingsToJSON(json);
1241 wxLogMessage(json_begin);
1250 double lon_min = wxRound(current_vp.lon_min) - 1;
1251 double lon_max = wxRound(current_vp.lon_max) + 1;
1252 double lat_min = wxRound(current_vp.lat_min) - 1;
1253 double lat_max = wxRound(current_vp.lat_max) + 1;
1257 int numErrors = reader.
Parse(json_begin, &v);
1258 if (numErrors > 0) {
1262 v[_T(
"latMin")] = lat_min;
1263 v[_T(
"latMax")] = lat_max;
1264 v[_T(
"lonMin")] = lon_min;
1265 v[_T(
"lonMax")] = lon_max;
1269 v[_T(
"grib_file")] = _T(
"");
1272 wxString json_final;
1273 w.
Write(v, json_final);
1274 wxLogMessage(json_final);
1276#ifdef __OCPN__ANDROID__
1277 wxString ret = callActivityMethod_ss(
"doGRIBActivity", json_final);
1284void GRIBUICtrlBar::OpenFileFromJSON(wxString json) {
1290 int numErrors = reader.
Parse(json, &root);
1291 if (numErrors > 0) {
1295 wxString file = root[(_T(
"grib_file"))].AsString();
1297 if (file.Length() && wxFileExists(file)) {
1298 wxFileName fn(file);
1306void GRIBUICtrlBar::OnPlayStop(wxCommandEvent &event) {
1310 m_bpPlay->SetBitmapLabel(
1311 GetScaledBitmap(wxBitmap(stop), _T(
"stop"), m_ScaledFactor));
1312 m_bpPlay->SetToolTip(_(
"Stop play back"));
1314 wxTIMER_CONTINUOUS);
1319void GRIBUICtrlBar::OnPlayStopTimer(wxTimerEvent &event) {
1320 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax()) {
1323 ComputeBestForecastForNow();
1324 if (m_sTimeline->GetValue() >= m_sTimeline->GetMax())
1328 m_sTimeline->SetValue(0);
1335 ? GetNearestValue(GetNow(), 1)
1336 : GetNearestIndex(GetNow(), 2)
1337 : m_sTimeline->GetValue();
1338 m_sTimeline->SetValue(value + 1);
1342 if (!m_InterpolateMode)
1343 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1347void GRIBUICtrlBar::StopPlayBack() {
1350 m_bpPlay->SetBitmapLabel(
1351 GetScaledBitmap(wxBitmap(play), _T(
"play"), m_ScaledFactor));
1352 m_bpPlay->SetToolTip(_(
"Start play back"));
1356void GRIBUICtrlBar::TimelineChanged() {
1358 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(
nullptr);
1362 RestaureSelectionString();
1365 wxDateTime time = TimelineTime();
1368 if (!m_InterpolateMode) {
1371 GribRecordSet &sel = rsa->Item(m_cRecordForecast->GetCurrentSelection());
1374 ? wxTimeSpan(t - MinTime()).GetMinutes() /
1377 : m_cRecordForecast->GetCurrentSelection());
1379 m_cRecordForecast->SetSelection(GetNearestIndex(time, 2));
1380 SaveSelectionString();
1381 m_cRecordForecast->SetString(
1383 TToString(time,
pPlugIn->GetTimeZone()));
1385 m_cRecordForecast->SetStringSelection(TToString(
1386 time,
pPlugIn->GetTimeZone()));
1389 UpdateTrackingControl();
1391 pPlugIn->SendTimelineMessage(time);
1392 RequestRefresh(GetGRIBCanvas());
1395void GRIBUICtrlBar::RestaureSelectionString() {
1396 if (!m_SelectionIsSaved)
return;
1398 int sel = m_cRecordForecast->GetSelection();
1399 m_cRecordForecast->SetString(m_Selection_index, m_Selection_label);
1400 m_cRecordForecast->SetSelection(sel);
1401 m_SelectionIsSaved =
false;
1404int GRIBUICtrlBar::GetNearestIndex(wxDateTime time,
int model) {
1409 wxDateTime itime, ip1time;
1410 for (i = 0; i < rsa->GetCount() - 1; i++) {
1411 itime = rsa->Item(i).m_Reference_Time;
1412 ip1time = rsa->Item(i + 1).m_Reference_Time;
1413 if (ip1time >= time)
break;
1415 if (!model)
return (time - itime > (ip1time - time) * 3) ? i + 1 : i;
1417 return model == 1 ? time == ip1time ? i : i + 1 : time == ip1time ? i + 1 : i;
1420int GRIBUICtrlBar::GetNearestValue(wxDateTime time,
int model) {
1422 if (m_TimeLineHours == 0)
return 0;
1423 wxDateTime itime, ip1time;
1426 wxTimeSpan span = time - MinTime();
1427 int t = span.GetMinutes() / stepmin;
1429 wxTimeSpan(t * stepmin / 60, (t * stepmin) % 60);
1430 ip1time = itime + wxTimeSpan(stepmin / 60, stepmin % 60);
1432 if (model == 1)
return time == ip1time ? t + 1 : t;
1434 return (time - itime > (ip1time - time) * 3) ? t + 1 : t;
1437wxDateTime GRIBUICtrlBar::GetNow() {
1438 wxDateTime now = wxDateTime::Now();
1444 now = (now > rsa->Item(rsa->GetCount() - 1).m_Reference_Time)
1445 ? rsa->Item(rsa->GetCount() - 1).m_Reference_Time
1446 : (now < rsa->Item(0).m_Reference_Time) ? rsa->Item(0).m_Reference_Time
1451wxDateTime GRIBUICtrlBar::TimelineTime() {
1452 if (m_InterpolateMode) {
1453 int tl = (m_TimeLineHours == 0) ? 0 : m_sTimeline->GetValue();
1456 return MinTime() + wxTimeSpan(tl * stepmin / 60, (tl * stepmin) % 60);
1460 unsigned int index = m_cRecordForecast->GetCurrentSelection() < 1
1462 : m_cRecordForecast->GetCurrentSelection();
1463 if (rsa && index < rsa->GetCount())
return rsa->Item(index).m_Reference_Time;
1465 return wxDateTime::Now();
1468wxDateTime GRIBUICtrlBar::MinTime() {
1470 if (rsa && rsa->GetCount()) {
1474 return wxDateTime::Now();
1481 if (rsa->GetCount() == 0)
return nullptr;
1488 wxDateTime GR1time, GR2time;
1494 for (j = 0; j < rsa->GetCount(); j++) {
1500 if (curtime <= time) GR1time = curtime, GRS1 = GRS, GR1 = GR;
1502 if (curtime >= time) {
1503 GR2time = curtime, GRS2 = GRS, GR2 = GR;
1508 if (!GR1 || !GR2)
continue;
1510 wxDateTime mintime = MinTime();
1511 double minute2 = (GR2time - mintime).GetMinutes();
1512 double minute1 = (GR1time - mintime).GetMinutes();
1513 double nminute = (time - mintime).GetMinutes();
1515 if (minute2 < minute1 || nminute < minute1 || nminute > minute2)
continue;
1517 double interp_const;
1518 if (minute1 == minute2) {
1523 interp_const = (nminute - minute1) / (minute2 - minute1);
1554 *GR1, *GR2, interp_const, i ==
Idx_WVDIR));
1562void GRIBUICtrlBar::GetProjectedLatLon(
int &x,
int &y) {
1564 auto now = TimelineTime();
1565 auto sog = m_ProjectBoatPanel->GetSpeed();
1566 auto cog = m_ProjectBoatPanel->GetCourse();
1568 static_cast<double>(now.GetTicks() -
pPlugIn->m_boat_time) * sog / 3600.0;
1569 PositionBearingDistanceMercator_Plugin(
pPlugIn->m_boat_lat,
1570 pPlugIn->m_boat_lon, cog, dist,
1571 &m_projected_lat, &m_projected_lon);
1573 GetCanvasPixLL(m_vp, &p, m_projected_lat, m_projected_lon);
1579double GRIBUICtrlBar::getTimeInterpolatedValue(
int idx,
double lon,
double lat,
1584 if (rsa->GetCount() == 0)
return GRIB_NOTDEF;
1586 GribRecord *before =
nullptr, *after =
nullptr;
1589 time_t t = time.GetTicks();
1590 for (j = 0; j < rsa->GetCount(); j++) {
1595 time_t curtime = GR->getRecordCurrentDate();
1598 if (curtime < t) before = GR;
1606 if (!before || !after)
return GRIB_NOTDEF;
1608 time_t t1 = before->getRecordCurrentDate();
1609 time_t t2 = after->getRecordCurrentDate();
1613 double v2 = after->getInterpolatedValue(lon, lat);
1614 if (v1 != GRIB_NOTDEF && v2 != GRIB_NOTDEF) {
1615 double k = fabs((
double)(t - t1) / (t2 - t1));
1616 return (1.0 - k) * v1 + k * v2;
1622bool GRIBUICtrlBar::getTimeInterpolatedValues(
double &M,
double &A,
int idx1,
1623 int idx2,
double lon,
double lat,
1631 if (rsa->GetCount() == 0)
return false;
1633 GribRecord *beforeX =
nullptr, *afterX =
nullptr;
1634 GribRecord *beforeY =
nullptr, *afterY =
nullptr;
1637 time_t t = time.GetTicks();
1638 for (j = 0; j < rsa->GetCount(); j++) {
1642 if (!GX || !GY)
continue;
1644 time_t curtime = GX->getRecordCurrentDate();
1659 if (!beforeX || !afterX)
return false;
1661 time_t t1 = beforeX->getRecordCurrentDate();
1662 time_t t2 = afterX->getRecordCurrentDate();
1667 double v1m, v2m, v1a, v2a;
1676 if (v1m == GRIB_NOTDEF || v2m == GRIB_NOTDEF || v1a == GRIB_NOTDEF ||
1680 double k = fabs((
double)(t - t1) / (t2 - t1));
1681 M = (1.0 - k) * v1m + k * v2m;
1682 A = (1.0 - k) * v1a + k * v2a;
1686void GRIBUICtrlBar::OnTimeline(wxScrollEvent &event) {
1689 if (!m_InterpolateMode)
1690 m_cRecordForecast->SetSelection(m_sTimeline->GetValue());
1695void GRIBUICtrlBar::OnOpenFile(wxCommandEvent &event) {
1699#ifndef __OCPN__ANDROID__
1701 wxStandardPathsBase &path = wxStandardPaths::Get();
1702 wxString l_grib_dir = path.GetDocumentsDir();
1706 wxFileDialog *dialog =
1707 new wxFileDialog(
nullptr, _(
"Select a GRIB file"), l_grib_dir, _T(
""),
1709 "(*.grb;*.bz2;*.gz;*.grib2;*.grb2)|*.grb;*.bz2;*.gz;"
1710 "*.grib2;*.grb2|All files (*)|*.*"),
1711 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE,
1712 wxDefaultPosition, wxDefaultSize, _T(
"File Dialog"));
1714 if (dialog->ShowModal() == wxID_OK) {
1715 ::wxBeginBusyCursor();
1721 if (g_pi->m_bZoomToCenterAtInit) DoZoomToCenter();
1723 SetDialogsStyleSizePosition(
true);
1728 wxStandardPathsBase &path = wxStandardPaths::Get();
1733 int response = PlatformFileSelectorDialog(
1734 nullptr, &file, _(
"Select a GRIB file"),
m_grib_dir, _T(
""), _T(
"*.*"));
1736 if (response == wxID_OK) {
1737 wxFileName fn(file);
1742 SetDialogsStyleSizePosition(
true);
1747void GRIBUICtrlBar::CreateActiveFileFromNames(
const wxArrayString &filenames) {
1748 if (filenames.GetCount() != 0) {
1755void GRIBUICtrlBar::PopulateComboDataList() {
1757 if (m_cRecordForecast->GetCount()) {
1758 index = m_cRecordForecast->GetCurrentSelection();
1759 m_cRecordForecast->Clear();
1763 for (
size_t i = 0; i < rsa->GetCount(); i++) {
1764 wxDateTime t(rsa->Item(i).m_Reference_Time);
1766 m_cRecordForecast->Append(TToString(t,
pPlugIn->GetTimeZone()));
1768 m_cRecordForecast->SetSelection(index);
1771void GRIBUICtrlBar::OnZoomToCenterClick(wxCommandEvent &event) {
1776 double latmin,latmax,lonmin,lonmax;
1777 if(!GetGribZoneLimits(
m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax ))
1783 double width = lonmax - lonmin;
1784 double height = latmax - latmin;
1787 double clat = latmin + height / 2;
1788 double clon = lonmin + width / 2;
1792 lonmin = clon - 60.;
1793 lonmax = clon + 60.;
1796 latmin = clat - 60.;
1797 latmax = clat + 60.;
1803 DistanceBearingMercator_Plugin(clat, lonmin, clat, lonmax,
nullptr, &ow );
1804 DistanceBearingMercator_Plugin( latmin, clon, latmax, clon,
nullptr, &oh );
1807 int w =
pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetWidth();
1808 int h =
pPlugIn->GetGRIBOverlayFactory()->m_ParentSize.GetHeight();
1812 ppm = wxMin(w/(ow*1852), h/(oh*1852)) * ( 100 - fabs( clat ) ) / 90;
1814 ppm = wxMin(ppm, 1.0);
1816 JumpToPosition(clat, clon, ppm);
1818 RequestRefresh( pParent );
1822void GRIBUICtrlBar::DoZoomToCenter() {
1825 double latmin, latmax, lonmin, lonmax;
1826 if (!GetGribZoneLimits(
m_pTimelineSet, &latmin, &latmax, &lonmin, &lonmax))
1832 double width = lonmax - lonmin;
1833 double height = latmax - latmin;
1836 double clat = latmin + height / 2;
1837 double clon = lonmin + width / 2;
1841 lonmin = clon - 60.;
1842 lonmax = clon + 60.;
1844 if (height > 120.) {
1845 latmin = clat - 60.;
1846 latmax = clat + 60.;
1851 DistanceBearingMercator_Plugin(clat, lonmin, clat, lonmax,
nullptr, &ow);
1852 DistanceBearingMercator_Plugin(latmin, clon, latmax, clon,
nullptr, &oh);
1854 wxWindow *wx = GetGRIBCanvas();
1856 int w = wx->GetSize().x;
1857 int h = wx->GetSize().y;
1861 ppm = wxMin(w / (ow * 1852), h / (oh * 1852)) * (100 - fabs(clat)) / 90;
1863 ppm = wxMin(ppm, 1.0);
1865 CanvasJumpToPosition(wx, clat, clon, ppm);
1868void GRIBUICtrlBar::OnPrev(wxCommandEvent &event) {
1872 RestaureSelectionString();
1876 selection = GetNearestIndex(GetNow(), 1);
1877 else if (m_InterpolateMode)
1879 GetNearestIndex(TimelineTime(), 1);
1881 selection = m_cRecordForecast->GetCurrentSelection();
1884 m_InterpolateMode =
false;
1886 m_cRecordForecast->SetSelection(selection < 1 ? 0 : selection - 1);
1891void GRIBUICtrlBar::OnNext(wxCommandEvent &event) {
1895 RestaureSelectionString();
1899 selection = GetNearestIndex(GetNow(), 2);
1900 else if (m_InterpolateMode)
1902 GetNearestIndex(TimelineTime(), 2);
1904 selection = m_cRecordForecast->GetCurrentSelection();
1906 m_cRecordForecast->SetSelection(selection);
1909 m_InterpolateMode =
false;
1911 if (selection == (
int)m_cRecordForecast->GetCount() - 1)
1914 m_cRecordForecast->SetSelection(selection + 1);
1919void GRIBUICtrlBar::ComputeBestForecastForNow() {
1921 pPlugIn->GetGRIBOverlayFactory()->SetGribTimelineRecordSet(
nullptr);
1925 wxDateTime now = GetNow();
1928 m_sTimeline->SetValue(GetNearestValue(now, 0));
1930 m_cRecordForecast->SetSelection(GetNearestIndex(now, 0));
1931 m_sTimeline->SetValue(m_cRecordForecast->GetCurrentSelection());
1934 if (
pPlugIn->GetStartOptions() !=
1941 m_InterpolateMode =
true;
1943 SetGribTimelineRecordSet(
1946 RestaureSelectionString();
1948 m_cRecordForecast->SetSelection(GetNearestIndex(now, 2));
1949 SaveSelectionString();
1950 m_cRecordForecast->SetString(
1955 m_cRecordForecast->SetStringSelection(
1956 TToString(now,
pPlugIn->GetTimeZone()));
1958 UpdateTrackingControl();
1960 pPlugIn->SendTimelineMessage(now);
1961 RequestRefresh(GetGRIBCanvas());
1964void GRIBUICtrlBar::SetGribTimelineRecordSet(
1969 if (!
pPlugIn->GetGRIBOverlayFactory())
return;
1974void GRIBUICtrlBar::SetTimeLineMax(
bool SetValue) {
1975 int oldmax = wxMax(m_sTimeline->GetMax(), 1),
1976 oldval = m_sTimeline->GetValue();
1981 m_sTimeline->SetMax(m_TimeLineHours * 60 / stepmin);
1985 m_sTimeline->SetMax(rsa->GetCount() - 1);
1990 if (SetValue && m_sTimeline->GetMax() != 0) {
1992 ComputeBestForecastForNow();
1994 m_sTimeline->SetValue(m_sTimeline->GetMax() * oldval / oldmax);
1998void GRIBUICtrlBar::SetFactoryOptions() {
2001 pPlugIn->GetGRIBOverlayFactory()->ClearCachedData();
2003 UpdateTrackingControl();
2004 RequestRefresh(GetGRIBCanvas());
2010unsigned int GRIBFile::ID = 0;
2016 m_pGribReader =
nullptr;
2017 m_last_message = wxEmptyString;
2018 for (
unsigned int i = 0; i < file_names.GetCount(); i++) {
2019 wxString file_name = file_names[i];
2020 if (::wxFileExists(file_name)) m_bOK =
true;
2023 if (m_bOK ==
false) {
2024 m_last_message = _(
" files don't exist!");
2033 for (
unsigned int i = 0; i < file_names.GetCount(); i++) {
2034 file_name = file_names[i];
2035 m_pGribReader->openFile(file_name);
2037 if (m_pGribReader->isOk()) {
2044 if (m_bOK ==
false) {
2045 m_last_message = _(
" can't be read!");
2050 m_FileNames.Clear();
2051 m_FileNames.Add(file_name);
2053 m_FileNames = file_names;
2057 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_TOT, LV_GND_SURF, 0);
2058 m_pGribReader->computeAccumulationRecords(GRB_PRECIP_RATE, LV_GND_SURF, 0);
2059 m_pGribReader->computeAccumulationRecords(GRB_CLOUD_TOT, LV_ATMOS_ALL, 0);
2067 m_nGribRecords = m_pGribReader->getTotalNumberOfGribRecords();
2071 std::set<time_t>::iterator iter;
2072 std::set<time_t> date_list = m_pGribReader->getListDates();
2073 for (iter = date_list.begin(); iter != date_list.end(); iter++) {
2075 time_t reftime = *iter;
2077 m_GribRecordSetArray.Add(t);
2085 bool polarWind(
false);
2086 bool polarCurrent(
false);
2087 bool sigWave(
false);
2090 std::map<std::string, std::vector<GribRecord *> *> *p_map =
2091 m_pGribReader->getGribMap();
2094 std::map<std::string, std::vector<GribRecord *> *>::iterator it;
2095 for (it = p_map->begin(); it != p_map->end(); it++) {
2096 std::vector<GribRecord *> *ls = (*it).second;
2097 for (zuint i = 0; i < ls->size(); i++) {
2100 time_t thistime = pRec->getRecordCurrentDate();
2103 for (
unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2104 if (m_GribRecordSetArray.Item(j).m_Reference_Time == thistime) {
2105 int idx = -1, mdx = -1;
2129 case GRB_WIND_SPEED:
2152 polarCurrent =
true;
2158 polarCurrent =
true;
2190 case GRB_PRECIP_RATE:
2191 case GRB_PRECIP_TOT:
2216 mdx = 1000 + NORWAY_METNO;
2246 case GRB_GEOPOT_HGT:
2272 if (m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx]) {
2275 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2297 if (oRec->
getDataType() == GRB_HTSGW) skip =
true;
2307 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx] = pRec;
2308 if (m_GribIdxArray.Index(idx) == wxNOT_FOUND)
2309 m_GribIdxArray.Add(idx, 1);
2310 if (mdx != -1 && m_GribIdxArray.Index(mdx) == wxNOT_FOUND)
2311 m_GribIdxArray.Add(mdx, 1);
2319 if (polarWind || polarCurrent) {
2320 for (
unsigned int j = 0; j < m_GribRecordSetArray.GetCount(); j++) {
2321 for (
unsigned int i = 0; i <
Idx_COUNT; i++) {
2325 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2327 if (pRec !=
nullptr && pRec->
getDataType() == GRB_WIND_DIR) {
2349 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2350 if (pRec1 !=
nullptr && pRec1->
getDataType() == GRB_WIND_SPEED)
2358 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[i];
2360 if (pRec !=
nullptr && pRec->
getDataType() == GRB_CUR_DIR) {
2370 m_GribRecordSetArray.Item(j).m_GribRecordPtrArray[idx];
2371 if (pRec1 !=
nullptr && pRec1->
getDataType() == GRB_CUR_SPEED)
2382 pRec->getRecordRefDate();
2385GRIBFile::~GRIBFile() {
delete m_pGribReader; }
2392 :
GRIBUICDataBase(parent.pParent, CURSOR_DATA, _(
"GRIB Display Control"),
2393 wxDefaultPosition, wxDefaultSize,
2394 wxSYSTEM_MENU | wxNO_BORDER | wxSTAY_ON_TOP)
2397 wxDefaultPosition, wxDefaultSize,
2398 wxSYSTEM_MENU | wxNO_BORDER)
2401 m_gpparent(parent) {
2405 m_gCursorData =
new CursorData(
this, m_gpparent);
2406 m_fgCdataSizer->Add(m_gCursorData, 0, wxALL, 0);
2408 Connect(wxEVT_MOVE, wxMoveEventHandler(GRIBUICData::OnMove));
2411void GRIBUICData::OnMove(wxMoveEvent &event) {
2413 GetScreenPosition(&w, &h);
2414 m_gpparent.
pPlugIn->SetCursorDataXY(wxPoint(w, h));
2420#ifdef __OCPN__ANDROID__
2422#include <QtAndroidExtras/QAndroidJniObject>
2424bool CheckPendingJNIException() {
2431 if (java_vm->GetEnv((
void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2436 if ((jenv)->ExceptionCheck() == JNI_TRUE) {
2444wxString callActivityMethod_ss(
const char *method, wxString parm) {
2450 if (CheckPendingJNIException())
return _T(
"NOK");
2452 wxString return_string;
2453 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod(
2454 "org/qtproject/qt5/android/QtNative",
"activity",
2455 "()Landroid/app/Activity;");
2456 if (CheckPendingJNIException())
return _T(
"NOK");
2458 if (!activity.isValid()) {
2460 return return_string;
2465 if (java_vm->GetEnv((
void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
2467 return "jenv Error";
2470 jstring p = (jenv)->NewStringUTF(parm.c_str());
2476 QAndroidJniObject data = activity.callObjectMethod(
2477 method,
"(Ljava/lang/String;)Ljava/lang/String;", p);
2478 if (CheckPendingJNIException())
return _T(
"NOK");
2482 jstring s = data.object<jstring>();
2484 if ((jenv)->GetStringLength(s)) {
2485 const char *ret_string = (jenv)->GetStringUTFChars(s,
nullptr);
2486 return_string = wxString(ret_string, wxConvUTF8);
2489 return return_string;
@ 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_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.
GRIB Data Table View and Export Interface.
GRIB Weather Data Control Interface.
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)
Gets reference datetime of the GRIB data.
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(int zone, ArrayOfGribRecordSets *rsa)
Initialize the GRIB data table.
GribOverlaySettings m_OverlaySettings
Settings that control how GRIB data is displayed and overlaid.
wxTimer m_tPlayStop
Timer for controlling GRIB animation playback.
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.
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.
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.
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.
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.
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.
OpenGL Platform Abstraction Layer.