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