1 /******************************************************************************
2  * $Id: wmm_pi.cpp,v 1.0 2011/02/26 01:54:37 nohal Exp $
3  *
4  * Project:  OpenCPN
5  * Purpose:  WMM Plugin
6  * Author:   Pavel Kalian
7  *
8  ***************************************************************************
9  *   Copyright (C) 2011-2019 by Pavel Kalian   *
10  *   $EMAIL$   *
11  *                                                 *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                         *
16  *                                                 *
17  *   This program is distributed in the hope that it will be useful,     *
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of      *
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       *
20  *   GNU General Public License for more details.                  *
21  *                                                 *
22  *   You should have received a copy of the GNU General Public License     *
23  *   along with this program; if not, write to the                 *
24  *   Free Software Foundation, Inc.,                           *
25  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
26  ***************************************************************************
27  */
28 
29 
30 #include "wx/wxprec.h"
31 
32 #ifndef  WX_PRECOMP
33   #include "wx/wx.h"
34 #endif //precompiled headers
35 
36 #ifndef __OCPN__ANDROID__
37 #include <GL/gl.h>
38 #include <GL/glu.h>
39 #else
40 #include "qopengl.h"                  // this gives us the qt runtime gles2.h
41 #include "GL/gl_private.h"
42 #include "qdebug.h"
43 #endif
44 
45 float g_piGLMinSymbolLineWidth;
46 
WMMLogMessage1(wxString s)47 void WMMLogMessage1(wxString s) { wxLogMessage(_T("WMM: ") + s); }
WMMLogMessage(const char * s)48 extern "C" void WMMLogMessage(const char *s) { WMMLogMessage1(wxString::FromAscii(s)); }
49 
50 #include "wmm_pi.h"
51 
52 // the class factories, used to create and destroy instances of the PlugIn
53 
create_pi(void * ppimgr)54 extern "C" DECL_EXP opencpn_plugin* create_pi(void *ppimgr)
55 {
56     return new wmm_pi(ppimgr);
57 }
58 
destroy_pi(opencpn_plugin * p)59 extern "C" DECL_EXP void destroy_pi(opencpn_plugin* p)
60 {
61     delete p;
62 }
63 
64 wmm_pi *g_pi;
65 
66 bool g_compact;
67 
68 //---------------------------------------------------------------------------------------------------------
69 //
70 //    WMM PlugIn Implementation
71 //
72 //---------------------------------------------------------------------------------------------------------
73 
74 #include "icons.h"
75 
EnablePlotChanged(wxCommandEvent & event)76 void WmmUIDialog::EnablePlotChanged( wxCommandEvent& event )
77 {
78     if(m_cbEnablePlot->GetValue())
79       m_wmm_pi.RecomputePlot();
80     m_wmm_pi.SetShowPlot(m_cbEnablePlot->GetValue());
81     RequestRefresh( m_wmm_pi.m_parent_window );
82 }
83 
PlotSettings(wxCommandEvent & event)84 void WmmUIDialog::PlotSettings( wxCommandEvent& event )
85 {
86     m_wmm_pi.ShowPlotSettings();
87 }
88 
About(wxCommandEvent & event)89 void WmmPlotSettingsDialog::About( wxCommandEvent& event )
90 {
91     wxString msg0(
92         _("\n\
93 World Magnetic Model Plotting allows users to cross reference the\
94  magnetic variation values printed on many raster charts.\n\n\
95 Variation is the angle between true and magnetic north.\n\
96 Inclination or dip, is the vertical angle of the magnetic field.\n\
97 \t(+- 90 at the magnetic poles)\n\
98 Field Strength is the magnetic field in nano tesla from\n\
99 \t20000 to 66000\n\n\
100 The plotted lines are similar to a topographic map.  The \
101 space between them can be adjusted; more space takes \
102 less time to calculate.\n\n\
103 The Step size and Pole accuracy sliders allow a trade off \
104 for speed vs computation time.\n\n\
105 The World Magnetic Model Plugin was written by Pavel Kalian \
106 and extended by Sean D'Epagnier to support plotting."));
107 
108     wxMessageDialog dlg( this, msg0, _("WMM Plugin"), wxOK );
109 
110     dlg.ShowModal();
111 }
112 
113 //---------------------------------------------------------------------------------------------------------
114 //
115 //        PlugIn initialization and de-init
116 //
117 //---------------------------------------------------------------------------------------------------------
118 
wmm_pi(void * ppimgr)119 wmm_pi::wmm_pi(void *ppimgr)
120     : opencpn_plugin_18(ppimgr),
121     m_bShowPlot(false),
122     m_DeclinationMap(DECLINATION_PLOT, MagneticModel, TimedMagneticModel, &Ellip),
123     m_InclinationMap(INCLINATION_PLOT, MagneticModel, TimedMagneticModel, &Ellip),
124     m_FieldStrengthMap(FIELD_STRENGTH_PLOT, MagneticModel, TimedMagneticModel, &Ellip),
125     m_bComputingPlot(false)
126 {
127     // Create the PlugIn icons
128     initialize_images();
129 
130     g_pi = this;
131 }
132 
Init(void)133 int wmm_pi::Init(void)
134 {
135     AddLocaleCatalog( PLUGIN_CATALOG_NAME );
136 
137     // Set some default private member parameters
138     m_wmm_dialog_x = 0;
139     m_wmm_dialog_y = 0;
140 
141     MagneticModel = NULL;
142     TimedMagneticModel = NULL;
143 
144     ::wxDisplaySize(&m_display_width, &m_display_height);
145 
146     //    Get a pointer to the opencpn display canvas, to use as a parent for the POI Manager dialog
147     m_parent_window = GetOCPNCanvasWindow();
148 
149     //    Get a pointer to the opencpn configuration object
150     m_pconfig = GetOCPNConfigObject();
151 
152     //    And load the configuration items
153     LoadConfig();
154 
155 #ifdef __OCPN__ANDROID__
156     g_compact = true;
157     m_bShowPlotOptions = false;
158     m_iViewType = 1;
159 #endif
160 
161 
162     m_buseable = true;
163 
164     m_LastVal = wxEmptyString;
165 
166     //pFontSmall = new wxFont( 10, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD );
167     pFontSmall = OCPNGetFont( _("WMM_Live_Overlay"), 10);
168 
169     m_shareLocn =*GetpSharedDataLocation() +
170     _T("plugins") + wxFileName::GetPathSeparator() +
171     _T("wmm_pi") + wxFileName::GetPathSeparator() +
172     _T("data") + wxFileName::GetPathSeparator();
173 
174     //    WMM initialization
175 
176     /* Memory allocation */
177     int NumTerms, epochs = 1, nMax = 0;
178     wxString cof_filename = m_shareLocn + "WMM.COF";
179 
180     if(!MAG_robustReadMagModels(const_cast<char*>((const char*)cof_filename.mb_str()), &MagneticModels)) {
181         WMMLogMessage1(_T("initialization error"));
182         m_buseable = false;
183     } else {
184         WMMLogMessage1(wxString::Format(_T("WMM model data loaded from file %s."), cof_filename.c_str()));
185         for(int i = 0; i < epochs; i++) {
186             if(MagneticModels[i]->nMax > nMax) {
187                 nMax = MagneticModels[i]->nMax;
188             }
189         }
190         NumTerms = ((nMax + 1) * (nMax + 2) / 2);
191 
192         TimedMagneticModel = MAG_AllocateModelMemory(NumTerms); /* For storing the time modified WMM Model parameters */
193 
194         for(int i = 0; i < epochs; i++) {
195             if(MagneticModels[i] == NULL || TimedMagneticModel == NULL) {
196                 WMMLogMessage1(_T("initialization error MAG_Error(2)"));
197                 m_buseable = false;
198             }
199         }
200 
201         MagneticModel = MagneticModels[0];
202 
203         MAG_SetDefaults(&Ellip, &Geoid); /* Set default values and constants */
204         /* Check for Geographic Poles */
205 
206         /* Set EGM96 Geoid parameters */
207         Geoid.GeoidHeightBuffer = GeoidHeightBuffer;
208         Geoid.Geoid_Initialized = 1;
209         /* Set EGM96 Geoid parameters END */
210     }
211 
212     int ret_flag =  (WANTS_OVERLAY_CALLBACK |
213     WANTS_OPENGL_OVERLAY_CALLBACK |
214     WANTS_CURSOR_LATLON     |
215     WANTS_TOOLBAR_CALLBACK  |
216     WANTS_NMEA_EVENTS       |
217     WANTS_PREFERENCES       |
218     WANTS_CONFIG          |
219     WANTS_PLUGIN_MESSAGING
220     );
221 
222     if(m_bShowIcon){
223     //    This PlugIn needs a toolbar icon, so request its insertion
224         m_leftclick_tool_id  = InsertPlugInTool(_T(""), _img_wmm, _img_wmm, wxITEM_NORMAL,
225                                             _("WMM"), _T(""), NULL, WMM_TOOL_POSITION, 0, this);
226 
227         SetIconType();          // SVGs allowed if not showing live icon
228 
229         ret_flag |= INSTALLS_TOOLBAR_TOOL;
230     }
231 
232     m_pWmmDialog = NULL;
233     m_oDC = NULL;
234 
235 #ifdef ocpnUSE_GL
236         //  Set the minimum line width
237     GLint parms[2];
238 #ifndef USE_ANDROID_GLES2
239     glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
240 #else
241     glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
242 #endif
243     g_piGLMinSymbolLineWidth = wxMax(parms[0], 1);
244 #endif
245 
246     return ret_flag;
247 }
248 
DeInit(void)249 bool wmm_pi::DeInit(void)
250 {
251     //    Record the dialog position
252      if (NULL != m_pWmmDialog)
253      {
254          wxPoint p = m_pWmmDialog->GetPosition();
255          SetWmmDialogX(p.x);
256          SetWmmDialogY(p.y);
257 
258          m_pWmmDialog->Close();
259          delete m_pWmmDialog;
260          m_pWmmDialog = NULL;
261      }
262     SaveConfig();
263     if(MagneticModel) {
264         MAG_FreeMagneticModelMemory(MagneticModel);
265     }
266     if(TimedMagneticModel) {
267         MAG_FreeMagneticModelMemory(TimedMagneticModel);
268     }
269 
270     RemovePlugInTool(m_leftclick_tool_id);
271 
272     /*if (Geoid.GeoidHeightBuffer)
273     {
274         free(Geoid.GeoidHeightBuffer);
275         Geoid.GeoidHeightBuffer = NULL;
276     }*/
277 
278     //delete pFontSmall;
279 
280     if(m_oDC)
281         delete m_oDC;
282 
283     return true;
284 }
285 
GetAPIVersionMajor()286 int wmm_pi::GetAPIVersionMajor()
287 {
288     return MY_API_VERSION_MAJOR;
289 }
290 
GetAPIVersionMinor()291 int wmm_pi::GetAPIVersionMinor()
292 {
293     return MY_API_VERSION_MINOR;
294 }
295 
GetPlugInVersionMajor()296 int wmm_pi::GetPlugInVersionMajor()
297 {
298     return PLUGIN_VERSION_MAJOR;
299 }
300 
GetPlugInVersionMinor()301 int wmm_pi::GetPlugInVersionMinor()
302 {
303     return PLUGIN_VERSION_MINOR;
304 }
305 
GetPlugInBitmap()306 wxBitmap *wmm_pi::GetPlugInBitmap()
307 {
308     return _img_wmm_pi;
309 }
310 
GetCommonName()311 wxString wmm_pi::GetCommonName()
312 {
313     return _("WMM");
314 }
315 
316 
GetShortDescription()317 wxString wmm_pi::GetShortDescription()
318 {
319     return _("World Magnetic Model PlugIn for OpenCPN");
320 }
321 
GetLongDescription()322 wxString wmm_pi::GetLongDescription()
323 {
324     return _("World Magnetic Model PlugIn for OpenCPN\n\
325 Implements the NOAA World Magnetic Model\n\
326 More information:\n\
327 https://www.ngdc.noaa.gov/geomag/WMM/DoDWMM.shtml\n\
328 The bundled WMM2020 model expires on December 31, 2025.\n\
329 After then, if new version of the plugin will not be released\n\
330 in time, get a new WMM.COF from NOAA and place it to the\n\
331 location you can find in the OpenCPN logfile.");
332 }
333 
334 
GetToolbarToolCount(void)335 int wmm_pi::GetToolbarToolCount(void)
336 {
337     return 1;
338 }
339 
SetColorScheme(PI_ColorScheme cs)340 void wmm_pi::SetColorScheme(PI_ColorScheme cs)
341 {
342     if (NULL == m_pWmmDialog)
343         return;
344     DimeWindow(m_pWmmDialog);
345 }
346 
SetIconType()347 void wmm_pi::SetIconType()
348 {
349     if(m_bShowLiveIcon){
350         SetToolbarToolBitmaps(m_leftclick_tool_id, _img_wmm, _img_wmm);
351         SetToolbarToolBitmapsSVG(m_leftclick_tool_id, _T(""), _T(""), _T(""));
352         m_LastVal.Empty();
353     }
354     else{
355         wxString normalIcon = m_shareLocn + _T("wmm_pi.svg");
356         wxString toggledIcon = m_shareLocn + _T("wmm_pi.svg");
357         wxString rolloverIcon = m_shareLocn + _T("wmm_pi.svg");
358 
359         SetToolbarToolBitmapsSVG(m_leftclick_tool_id, normalIcon, rolloverIcon, toggledIcon);
360     }
361 
362 }
363 
364 
RearrangeWindow()365 void wmm_pi::RearrangeWindow()
366 {
367     if (NULL == m_pWmmDialog)
368         return;
369     if (m_iViewType == 1)
370     {
371         m_pWmmDialog->sbScursor->Hide(m_pWmmDialog->gScursor, true);
372         m_pWmmDialog->sbSboat->Hide(m_pWmmDialog->gSboat, true);
373     }
374     else
375     {
376         m_pWmmDialog->sbScursor->Show(m_pWmmDialog->gScursor, true, true);
377         m_pWmmDialog->sbSboat->Show(m_pWmmDialog->gSboat, true, true);
378     }
379 
380     m_pWmmDialog->m_cbEnablePlot->Show(m_bShowPlotOptions);
381     m_pWmmDialog->m_bPlotSettings->Show(m_bShowPlotOptions);
382 
383     if (!m_bShowAtCursor)
384     {
385         m_pWmmDialog->bSframe->Hide(m_pWmmDialog->sbScursor, true);
386     }
387     else
388     {
389         m_pWmmDialog->bSframe->Show(m_pWmmDialog->sbScursor, true, true);
390         if (m_iViewType == 1)
391             m_pWmmDialog->sbScursor->Hide(m_pWmmDialog->gScursor, true);
392     }
393 
394     SetColorScheme(PI_ColorScheme());
395 
396     m_pWmmDialog->Fit();
397 
398 #ifdef __WXMSW__
399     //UGLY!!!!!!! On Windows XP the transparent window is not refreshed properly in OpenGL mode at least on the Atom powered netbooks, so we have to disable transparency.
400     wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
401     bool gl = true;
402     if(pConf)
403     {
404         pConf->SetPath(_T("/Settings"));
405         pConf->Read(_T("OpenGL"), &gl, false);
406         pConf = NULL;
407     }
408     if (!(gl && wxPlatformInfo::Get().GetOSMajorVersion() == 5 && wxPlatformInfo::Get().GetOSMinorVersion() == 1))
409 #endif
410         if (m_pWmmDialog->CanSetTransparent())
411             m_pWmmDialog->SetTransparent(m_iOpacity);
412 }
413 
OnToolbarToolCallback(int id)414 void wmm_pi::OnToolbarToolCallback(int id)
415 {
416     if( !m_buseable )
417         return;
418     if(NULL == m_pWmmDialog)
419     {
420         m_pWmmDialog = new WmmUIDialog(*this, m_parent_window);
421         wxFont *pFont = OCPNGetFont(_T("Dialog"), 0);
422         m_pWmmDialog->SetFont(*pFont);
423 
424         m_pWmmDialog->Move(wxPoint(m_wmm_dialog_x, m_wmm_dialog_y));
425     }
426 
427     RearrangeWindow();
428     /*m_pWmmDialog->SetMaxSize(m_pWmmDialog->GetSize());
429     m_pWmmDialog->SetMinSize(m_pWmmDialog->GetSize());*/
430     m_pWmmDialog->Show(!m_pWmmDialog->IsShown());
431     m_pWmmDialog->Layout();     // Some platforms need a re-Layout at this point (gtk, at least)
432     if (m_pWmmDialog->IsShown())
433         SendPluginMessage(_T("WMM_WINDOW_SHOWN"), wxEmptyString);
434     else
435         SendPluginMessage(_T("WMM_WINDOW_HIDDEN"), wxEmptyString);
436 
437     wxPoint p = m_pWmmDialog->GetPosition();
438     m_pWmmDialog->Move(0,0);      // workaround for gtk autocentre dialog behavior
439     m_pWmmDialog->Move(p);
440 
441 #ifdef __OCPN__ANDROID__
442     m_pWmmDialog->CentreOnScreen();
443     m_pWmmDialog->Move(-1,0);
444 #endif
445 
446 }
447 
RenderOverlayBoth(pi_ocpnDC * dc,PlugIn_ViewPort * vp)448 void wmm_pi::RenderOverlayBoth(pi_ocpnDC *dc, PlugIn_ViewPort *vp)
449 {
450     if(!m_bShowPlot)
451       return;
452 
453     m_DeclinationMap.Plot(dc, vp, wxColour(255, 0, 90, 220));
454     m_InclinationMap.Plot(dc, vp, wxColour(60, 255, 30, 220));
455     m_FieldStrengthMap.Plot(dc, vp, wxColour(0, 60, 255, 220));
456 }
457 
RenderOverlay(wxDC & dc,PlugIn_ViewPort * vp)458 bool wmm_pi::RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp)
459 {
460     if(!m_bShowPlot)
461         return true;
462 
463     if(!m_oDC)
464         m_oDC = new pi_ocpnDC();
465 
466     m_oDC->SetVP(vp);
467     m_oDC->SetDC(&dc);
468 
469     RenderOverlayBoth(m_oDC, vp);
470 
471     return true;
472 }
473 
RenderGLOverlay(wxGLContext * pcontext,PlugIn_ViewPort * vp)474 bool wmm_pi::RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp)
475 {
476     if(!m_bShowPlot)
477         return true;
478 
479     if(!m_oDC)
480         m_oDC = new pi_ocpnDC();
481 
482     m_oDC->SetVP(vp);
483     m_oDC->SetDC(NULL);
484 
485 #ifndef USE_ANDROID_GLES2
486     glPushAttrib(GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT | GL_HINT_BIT );
487 
488     glEnable( GL_LINE_SMOOTH );
489     glEnable( GL_BLEND );
490     glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
491     glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
492 #endif
493 
494     RenderOverlayBoth(m_oDC, vp);
495 
496 #ifndef USE_ANDROID_GLES2
497     glPopAttrib();
498 #endif
499 
500     return true;
501 }
502 
RecomputePlot()503 void wmm_pi::RecomputePlot()
504 {
505     if(m_bCachedPlotOk)
506       return;
507 
508     if(m_bComputingPlot)
509       return;
510     m_bComputingPlot = true;
511 
512     if(!m_DeclinationMap.Recompute(m_MapDate) ||
513      !m_InclinationMap.Recompute(m_MapDate) ||
514      !m_FieldStrengthMap.Recompute(m_MapDate)) {
515       m_bShowPlot = false;
516       if(m_pWmmDialog)
517         m_pWmmDialog->m_cbEnablePlot->SetValue(false);
518     } else
519       m_bCachedPlotOk = true;
520 
521     m_bComputingPlot = false;
522 }
523 
SetCursorLatLon(double lat,double lon)524 void wmm_pi::SetCursorLatLon(double lat, double lon)
525 {
526     if(!m_pWmmDialog)
527         return;
528 
529     if (!m_bShowAtCursor)
530         return; //We don't want to waste CPU cycles that much...
531     if (lat < -90 || lat > 90 || lon < -180 || lon > 180 || NULL == m_pWmmDialog || !m_pWmmDialog->IsShown())
532         return;
533     if (!m_buseable)
534     {
535         m_pWmmDialog->m_tbD->SetValue(_("Error, see log."));
536         return;
537     }
538     CoordGeodetic.lambda = lon;
539     CoordGeodetic.phi = lat;
540     CoordGeodetic.HeightAboveEllipsoid = 0;
541     CoordGeodetic.HeightAboveGeoid = 0;
542     CoordGeodetic.UseGeoid = 0;
543     UserDate.Year = wxDateTime::GetCurrentYear();
544     UserDate.Month = wxDateTime::GetCurrentMonth() + 1; //WHY is it 0 based????????
545     UserDate.Day = wxDateTime::Now().GetDay();
546     char err[255];
547     MAG_DateToYear(&UserDate, err);
548     MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &CoordSpherical);    /*Convert from geodeitic to Spherical Equations: 17-18, WMM Technical report*/
549     MAG_TimelyModifyMagneticModel(UserDate, MagneticModel, TimedMagneticModel); /* Time adjust the coefficients, Equation 19, WMM Technical report */
550     MAG_Geomag(Ellip, CoordSpherical, CoordGeodetic, TimedMagneticModel, &GeoMagneticElements);   /* Computes the geoMagnetic field elements and their time change*/
551     MAG_CalculateGridVariation(CoordGeodetic,&GeoMagneticElements);
552     //WMM_PrintUserData(GeoMagneticElements,CoordGeodetic, UserDate, TimedMagneticModel, &Geoid);     /* Print the results */
553     m_pWmmDialog->m_tcF->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.F));
554     m_pWmmDialog->m_tcH->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.H));
555     m_pWmmDialog->m_tcX->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.X));
556     m_pWmmDialog->m_tcY->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.Y));
557     m_pWmmDialog->m_tcZ->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.Z));
558     m_pWmmDialog->m_tcD->SetValue(wxString::Format(_T("%-5.1lf\u00B0 (%s)"), GeoMagneticElements.Decl, AngleToText(GeoMagneticElements.Decl).c_str()));
559     m_pWmmDialog->m_tcI->SetValue(wxString::Format(_T("%-5.1lf\u00B0"), GeoMagneticElements.Incl));
560 
561     m_cursorVariation = GeoMagneticElements;
562     SendCursorVariation();
563 }
564 
SetPositionFix(PlugIn_Position_Fix & pfix)565 void wmm_pi::SetPositionFix(PlugIn_Position_Fix &pfix)
566 {
567     if(!m_buseable) {
568         return;
569     }
570     CoordGeodetic.lambda = pfix.Lon;
571     CoordGeodetic.phi = pfix.Lat;
572     CoordGeodetic.HeightAboveEllipsoid = 0;
573     CoordGeodetic.UseGeoid = 0;
574     UserDate.Year = wxDateTime::GetCurrentYear();
575     UserDate.Month = wxDateTime::GetCurrentMonth() + 1; //WHY is it 0 based????????
576     UserDate.Day = wxDateTime::Now().GetDay();
577     char err[255];
578     MAG_DateToYear(&UserDate, err);
579     MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &CoordSpherical);    /*Convert from geodeitic to Spherical Equations: 17-18, WMM Technical report*/
580     MAG_TimelyModifyMagneticModel(UserDate, MagneticModel, TimedMagneticModel); /* Time adjust the coefficients, Equation 19, WMM Technical report */
581     MAG_Geomag(Ellip, CoordSpherical, CoordGeodetic, TimedMagneticModel, &GeoMagneticElements);   /* Computes the geoMagnetic field elements and their time change*/
582     MAG_CalculateGridVariation(CoordGeodetic,&GeoMagneticElements);
583     //WMM_PrintUserData(GeoMagneticElements,CoordGeodetic, UserDate, TimedMagneticModel, &Geoid);     /* Print the results */
584 
585     m_boatVariation = GeoMagneticElements;
586     SendBoatVariation();
587 
588     wxString NewVal = wxString::Format(_T("%.1f"), GeoMagneticElements.Decl);
589     double scale = GetOCPNGUIToolScaleFactor_PlugIn();
590     scale = wxRound(scale * 4.0) / 4.0;
591     scale = wxMax(1.0, scale);          // Let the upstream processing handle minification.
592 
593     if( m_bShowIcon && m_bShowLiveIcon && ((m_LastVal != NewVal) || (scale != m_scale)) )
594     {
595         m_scale = scale;
596         m_LastVal = NewVal;
597         int w = _img_wmm_live->GetWidth() * scale;
598         int h = _img_wmm_live->GetHeight() * scale;
599         wxMemoryDC dc;
600         wxBitmap icon;
601 
602         //  Is SVG available?
603         wxBitmap live = GetBitmapFromSVGFile(m_shareLocn + _T("wmm_live.svg"), w, h);
604         if( ! live.IsOk() ){
605             icon = wxBitmap(_img_wmm_live->GetWidth(), _img_wmm_live->GetHeight());
606             dc.SelectObject(icon);
607             dc.DrawBitmap(*_img_wmm_live, 0, 0, true);
608         }
609         else{
610             icon = wxBitmap(w, h);
611             dc.SelectObject(icon);
612             wxColour col;
613             dc.SetBackground( *wxTRANSPARENT_BRUSH );
614             dc.Clear();
615 
616             dc.DrawBitmap(live, 0, 0, true);
617         }
618 
619         wxColour cf;
620         GetGlobalColor(_T("CHWHT"), &cf);
621         dc.SetTextForeground(cf);
622         if(pFontSmall->IsOk()){
623             if(live.IsOk()){
624                 int point_size = wxMax(10, 10 * scale);
625                 pFontSmall->SetPointSize(point_size);
626 
627                 //  Validate and adjust the font size...
628                 //   No smaller than 8 pt.
629                 int w;
630                 wxScreenDC sdc;
631                 sdc.GetTextExtent(NewVal, &w, NULL, NULL, NULL, pFontSmall);
632 
633                 while( (w > (icon.GetWidth() * 8 / 10) ) && (point_size >= 8) ){
634                     point_size--;
635                     pFontSmall->SetPointSize(point_size);
636                     sdc.GetTextExtent(NewVal, &w, NULL, NULL, NULL, pFontSmall);
637                 }
638             }
639             dc.SetFont(*pFontSmall);
640         }
641         wxSize s = dc.GetTextExtent(NewVal);
642         dc.DrawText(NewVal, (icon.GetWidth() - s.GetWidth()) / 2, (icon.GetHeight() - s.GetHeight()) / 2);
643         dc.SelectObject(wxNullBitmap);
644 
645         if(live.IsOk()){
646             //  By using a DC to modify the bitmap, we have lost the original bitmap's alpha channel
647             //  Recover it by copying from the original to the target, bit by bit
648             wxImage imo = live.ConvertToImage();
649             wxImage im = icon.ConvertToImage();
650 
651             if(!imo.HasAlpha())
652                 imo.InitAlpha();
653             if(!im.HasAlpha())
654                 im.InitAlpha();
655 
656             unsigned char *alive = imo.GetAlpha();
657             unsigned char *target = im.GetAlpha();
658 
659             for(int i=0 ; i < h ; i ++){
660                 for(int j=0 ; j < w ; j++){
661                     int index = (i * w) + j;
662                     target[index] = alive[index];
663                 }
664             }
665             icon = wxBitmap(im);
666         }
667 
668         SetToolbarToolBitmaps(m_leftclick_tool_id, &icon, &icon);
669     }
670 
671     if (NULL == m_pWmmDialog || !m_pWmmDialog->IsShown())
672         return;
673     if (!m_buseable)
674     {
675         m_pWmmDialog->m_tbD->SetValue(_("Error, see log."));
676         return;
677     }
678     m_pWmmDialog->m_tbF->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.F));
679     m_pWmmDialog->m_tbH->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.H));
680     m_pWmmDialog->m_tbX->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.X));
681     m_pWmmDialog->m_tbY->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.Y));
682     m_pWmmDialog->m_tbZ->SetValue(wxString::Format(_T("%-9.1lf nT"), GeoMagneticElements.Z));
683     m_pWmmDialog->m_tbD->SetValue(wxString::Format(_T("%-5.1lf\u00B0 (%s)"), GeoMagneticElements.Decl, AngleToText(GeoMagneticElements.Decl).c_str()));
684     m_pWmmDialog->m_tbI->SetValue(wxString::Format(_T("%-5.1lf\u00B0"), GeoMagneticElements.Incl));
685 }
686 
687 //Demo implementation of response mechanism
SetPluginMessage(wxString & message_id,wxString & message_body)688 void wmm_pi::SetPluginMessage(wxString &message_id, wxString &message_body)
689 {
690     if(message_id == _T("WMM_VARIATION_REQUEST"))
691     {
692         wxJSONReader r;
693         wxJSONValue v;
694         r.Parse(message_body, &v);
695         double lat = v[_T("Lat")].AsDouble();
696         double lon = v[_T("Lon")].AsDouble();
697         int year = v[_T("Year")].AsInt();
698         int month = v[_T("Month")].AsInt();
699         int day = v[_T("Day")].AsInt();
700         SendVariationAt(lat, lon, year, month, day);
701     }
702     else if(message_id == _T("WMM_VARIATION_BOAT_REQUEST"))
703     {
704         SendBoatVariation();
705     }
706     else if(message_id == _T("WMM_VARIATION_CURSOR_REQUEST"))
707     {
708         SendCursorVariation();
709     }
710 }
711 
SendVariationAt(double lat,double lon,int year,int month,int day)712 void wmm_pi::SendVariationAt(double lat, double lon, int year, int month, int day)
713 {
714     wxJSONValue v;
715     v[_T("Lat")] = lat;
716     v[_T("Lon")] = lon;
717     v[_T("Year")] = year;
718     v[_T("Month")] = month;
719     v[_T("Day")] = day;
720     CoordGeodetic.lambda = lon;
721     CoordGeodetic.phi = lat;
722     CoordGeodetic.HeightAboveEllipsoid = 0;
723     CoordGeodetic.UseGeoid = 0;
724     UserDate.Year = year;
725     UserDate.Month = month;
726     UserDate.Day = day;
727     char err[255];
728     MAG_DateToYear(&UserDate, err);
729     MAG_GeodeticToSpherical(Ellip, CoordGeodetic, &CoordSpherical);    /*Convert from geodeitic to Spherical Equations: 17-18, WMM Technical report*/
730     MAG_TimelyModifyMagneticModel(UserDate, MagneticModel, TimedMagneticModel); /* Time adjust the coefficients, Equation 19, WMM Technical report */
731     MAG_Geomag(Ellip, CoordSpherical, CoordGeodetic, TimedMagneticModel, &GeoMagneticElements);   /* Computes the geoMagnetic field elements and their time change*/
732     MAG_CalculateGridVariation(CoordGeodetic,&GeoMagneticElements);
733     v[_T("Decl")] = GeoMagneticElements.Decl;
734     v[_T("Decldot")] = GeoMagneticElements.Decldot;
735     v[_T("F")] = GeoMagneticElements.F;
736     v[_T("Fdot")] = GeoMagneticElements.Fdot;
737     v[_T("GV")] = GeoMagneticElements.GV;
738     v[_T("GVdot")] = GeoMagneticElements.GVdot;
739     v[_T("H")] = GeoMagneticElements.H;
740     v[_T("Hdot")] = GeoMagneticElements.Hdot;
741     v[_T("Incl")] = GeoMagneticElements.Incl;
742     v[_T("Incldot")] = GeoMagneticElements.Incldot;
743     v[_T("X")] = GeoMagneticElements.X;
744     v[_T("Xdot")] = GeoMagneticElements.Xdot;
745     v[_T("Y")] = GeoMagneticElements.Y;
746     v[_T("Ydot")] = GeoMagneticElements.Ydot;
747     v[_T("Z")] = GeoMagneticElements.Z;
748     v[_T("Zdot")] = GeoMagneticElements.Zdot;
749     wxJSONWriter w;
750     wxString out;
751     w.Write(v, out);
752     SendPluginMessage(wxString(_T("WMM_VARIATION")), out);
753 }
754 
SendBoatVariation()755 void wmm_pi::SendBoatVariation()
756 {
757     wxJSONValue v;
758     v[_T("Decl")] = m_boatVariation.Decl;
759     v[_T("Decldot")] = m_boatVariation.Decldot;
760     v[_T("F")] = m_boatVariation.F;
761     v[_T("Fdot")] = m_boatVariation.Fdot;
762     v[_T("GV")] = m_boatVariation.GV;
763     v[_T("GVdot")] = m_boatVariation.GVdot;
764     v[_T("H")] = m_boatVariation.H;
765     v[_T("Hdot")] = m_boatVariation.Hdot;
766     v[_T("Incl")] = m_boatVariation.Incl;
767     v[_T("Incldot")] = m_boatVariation.Incldot;
768     v[_T("X")] = m_boatVariation.X;
769     v[_T("Xdot")] = m_boatVariation.Xdot;
770     v[_T("Y")] = m_boatVariation.Y;
771     v[_T("Ydot")] = m_boatVariation.Ydot;
772     v[_T("Z")] = m_boatVariation.Z;
773     v[_T("Zdot")] = m_boatVariation.Zdot;
774     wxJSONWriter w;
775     wxString out;
776     w.Write(v, out);
777     SendPluginMessage(wxString(_T("WMM_VARIATION_BOAT")), out);
778 }
779 
SendCursorVariation()780 void wmm_pi::SendCursorVariation()
781 {
782     wxJSONValue v;
783     v[_T("Decl")] = m_cursorVariation.Decl;
784     v[_T("Decldot")] = m_cursorVariation.Decldot;
785     v[_T("F")] = m_cursorVariation.F;
786     v[_T("Fdot")] = m_cursorVariation.Fdot;
787     v[_T("GV")] = m_cursorVariation.GV;
788     v[_T("GVdot")] = m_cursorVariation.GVdot;
789     v[_T("H")] = m_cursorVariation.H;
790     v[_T("Hdot")] = m_cursorVariation.Hdot;
791     v[_T("Incl")] = m_cursorVariation.Incl;
792     v[_T("Incldot")] = m_cursorVariation.Incldot;
793     v[_T("X")] = m_cursorVariation.X;
794     v[_T("Xdot")] = m_cursorVariation.Xdot;
795     v[_T("Y")] = m_cursorVariation.Y;
796     v[_T("Ydot")] = m_cursorVariation.Ydot;
797     v[_T("Z")] = m_cursorVariation.Z;
798     v[_T("Zdot")] = m_cursorVariation.Zdot;
799     wxJSONWriter w;
800     wxString out;
801     w.Write(v, out);
802     SendPluginMessage(wxString(_T("WMM_VARIATION_CURSOR")), out);
803 }
804 
AngleToText(double angle)805 wxString wmm_pi::AngleToText(double angle)
806 {
807     int deg = (int)fabs(angle);
808     int min = (fabs(angle) - deg) * 60;
809     if (angle < 0)
810         return wxString::Format(_T("%u\u00B0%u' W"), deg, min);
811     else
812         return wxString::Format(_T("%u\u00B0%u' E"), deg, min);
813 }
814 
LoadConfig(void)815 bool wmm_pi::LoadConfig(void)
816 {
817     wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
818 
819     if(pConf)
820     {
821         pConf->SetPath ( _T( "/Settings/WMM" ) );
822         pConf->Read ( _T( "ViewType" ),  &m_iViewType, 1 );
823         pConf->Read ( _T( "ShowPlotOptions" ),  &m_bShowPlotOptions, 1 );
824         pConf->Read ( _T( "ShowAtCursor" ),  &m_bShowAtCursor, 1 );
825         pConf->Read ( _T( "ShowLiveIcon" ),  &m_bShowLiveIcon, 1 );
826         pConf->Read ( _T( "ShowIcon" ),  &m_bShowIcon, 1 );
827         pConf->Read ( _T( "Opacity" ),  &m_iOpacity, 255 );
828 
829         m_wmm_dialog_x =  pConf->Read ( _T ( "DialogPosX" ), 20L );
830         m_wmm_dialog_y =  pConf->Read ( _T ( "DialogPosY" ), 20L );
831 
832         if((m_wmm_dialog_x < 0) || (m_wmm_dialog_x > m_display_width))
833             m_wmm_dialog_x = 5;
834         if((m_wmm_dialog_y < 0) || (m_wmm_dialog_y > m_display_height))
835             m_wmm_dialog_y = 5;
836 
837         pConf->SetPath ( _T( "/Settings/WMM/Plot" ) );
838         pConf->Read ( _T( "Declination" ), &m_DeclinationMap.m_bEnabled, 1);
839         pConf->Read ( _T( "DeclinationSpacing" ), &m_DeclinationMap.m_Spacing, 10);
840         pConf->Read ( _T( "Inclination" ), &m_InclinationMap.m_bEnabled, 0);
841         pConf->Read ( _T( "InclinationSpacing" ), &m_InclinationMap.m_Spacing, 10);
842         pConf->Read ( _T( "FieldStrength" ), &m_FieldStrengthMap.m_bEnabled, 0);
843         pConf->Read ( _T( "FieldStrengthSpacing" ), &m_FieldStrengthMap.m_Spacing, 10000);
844 
845         pConf->Read ( _T( "StepSize" ), &m_MapStep, 6);
846         pConf->Read ( _T( "PoleAccuracy" ), &m_MapPoleAccuracy, 2);
847         m_DeclinationMap.ConfigureAccuracy(m_MapStep, m_MapPoleAccuracy);
848         m_InclinationMap.ConfigureAccuracy(m_MapStep, m_MapPoleAccuracy);
849         m_FieldStrengthMap.ConfigureAccuracy(m_MapStep, m_MapPoleAccuracy);
850 
851         m_MapDate = wxDateTime::Now(); /* always reset to current date */
852 
853         m_bCachedPlotOk = false;
854 
855         pConf->SetPath ( _T ( "/Directories" ) );
856         wxString s =wxFileName::GetPathSeparator();
857         wxString def = *GetpSharedDataLocation() + _T("plugins")
858             + s + _T("wmm_pi") + s + _T("data") + s;
859         //pConf->Read ( _T ( "WMMDataLocation" ), &m_wmm_dir, def);
860         m_wmm_dir = def;
861         return true;
862     }
863     else
864         return false;
865 }
866 
SaveConfig(void)867 bool wmm_pi::SaveConfig(void)
868 {
869     wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
870 
871     if(pConf)
872     {
873         pConf->SetPath ( _T ( "/Settings/WMM" ) );
874         pConf->Write ( _T ( "ViewType" ), m_iViewType );
875         pConf->Write ( _T ( "ShowPlotOptions" ), m_bShowPlotOptions );
876         pConf->Write ( _T ( "ShowAtCursor" ), m_bShowAtCursor );
877         pConf->Write ( _T ( "ShowLiveIcon" ), m_bShowLiveIcon );
878         pConf->Write ( _T ( "ShowIcon" ), m_bShowIcon );
879         pConf->Write ( _T ( "Opacity" ), m_iOpacity );
880 
881         pConf->Write ( _T ( "DialogPosX" ),   m_wmm_dialog_x );
882         pConf->Write ( _T ( "DialogPosY" ),   m_wmm_dialog_y );
883 
884         pConf->SetPath ( _T( "/Settings/WMM/Plot" ) );
885         pConf->Write ( _T( "Declination" ), m_DeclinationMap.m_bEnabled);
886         pConf->Write ( _T( "DeclinationSpacing" ), m_DeclinationMap.m_Spacing);
887         pConf->Write ( _T( "Inclination" ), m_InclinationMap.m_bEnabled);
888         pConf->Write ( _T( "InclinationSpacing" ), m_InclinationMap.m_Spacing);
889         pConf->Write ( _T( "FieldStrength" ), m_FieldStrengthMap.m_bEnabled);
890         pConf->Write ( _T( "FieldStrengthSpacing" ), m_FieldStrengthMap.m_Spacing);
891         pConf->Write ( _T( "StepSize" ), m_MapStep);
892         pConf->Write ( _T( "PoleAccuracy" ), m_MapPoleAccuracy);
893 
894         pConf->SetPath ( _T ( "/Directories" ) );
895         pConf->Write ( _T ( "WMMDataLocation" ), m_wmm_dir );
896 
897         return true;
898     }
899     else
900         return false;
901 }
902 
SetBackColor(wxWindow * ctrl,wxColour col)903 void SetBackColor( wxWindow* ctrl, wxColour col)
904 {
905     static int depth = 0; // recursion count
906     if ( depth == 0 ) {   // only for the window root, not for every child
907 
908         ctrl->SetBackgroundColour( col );
909     }
910 
911     wxWindowList kids = ctrl->GetChildren();
912     for( unsigned int i = 0; i < kids.GetCount(); i++ ) {
913         wxWindowListNode *node = kids.Item( i );
914         wxWindow *win = node->GetData();
915 
916         if( win->IsKindOf( CLASSINFO(wxListBox) ) )
917             ( (wxListBox*) win )->SetBackgroundColour( col );
918 
919         else if( win->IsKindOf( CLASSINFO(wxTextCtrl) ) )
920             ( (wxTextCtrl*) win )->SetBackgroundColour( col );
921 
922         //        else if( win->IsKindOf( CLASSINFO(wxStaticText) ) )
923             //            ( (wxStaticText*) win )->SetForegroundColour( uitext );
924 
925             else if( win->IsKindOf( CLASSINFO(wxChoice) ) )
926                 ( (wxChoice*) win )->SetBackgroundColour( col );
927 
928             else if( win->IsKindOf( CLASSINFO(wxComboBox) ) )
929                 ( (wxComboBox*) win )->SetBackgroundColour( col );
930 
931             else if( win->IsKindOf( CLASSINFO(wxRadioButton) ) )
932                 ( (wxRadioButton*) win )->SetBackgroundColour( col );
933 
934             else if( win->IsKindOf( CLASSINFO(wxScrolledWindow) ) ) {
935                 ( (wxScrolledWindow*) win )->SetBackgroundColour( col );
936             }
937 
938 
939             else if( win->IsKindOf( CLASSINFO(wxButton) ) ) {
940                 ( (wxButton*) win )->SetBackgroundColour( col );
941             }
942 
943             else {
944                 ;
945             }
946 
947             if( win->GetChildren().GetCount() > 0 ) {
948                 depth++;
949                 wxWindow * w = win;
950                 SetBackColor( w, col );
951                 depth--;
952             }
953     }
954 }
955 
ShowPreferencesDialog(wxWindow * parent)956 void wmm_pi::ShowPreferencesDialog( wxWindow* parent )
957 {
958     WmmPrefsDialog *dialog = new WmmPrefsDialog( parent, wxID_ANY, _("WMM Preferences"), wxPoint( m_wmm_dialog_x, m_wmm_dialog_y), wxDefaultSize, wxDEFAULT_DIALOG_STYLE );
959     dialog->Fit();
960     wxColour cl;
961     GetGlobalColor(_T("DILG1"), &cl);
962     dialog->SetBackgroundColour(cl);
963 
964     dialog->m_rbViewType->SetSelection(m_iViewType);
965     dialog->m_cbShowPlotOptions->SetValue(m_bShowPlotOptions);
966     dialog->m_cbShowAtCursor->SetValue(m_bShowAtCursor);
967     dialog->m_cbShowIcon->SetValue(m_bShowIcon);
968     dialog->m_cbLiveIcon->SetValue(m_bShowLiveIcon);
969     dialog->m_sOpacity->SetValue(m_iOpacity);
970 
971     if(dialog->ShowModal() == wxID_OK)
972     {
973         m_iViewType = dialog->m_rbViewType->GetSelection();
974         m_bShowPlotOptions = dialog->m_cbShowPlotOptions->GetValue();
975         m_bShowAtCursor = dialog->m_cbShowAtCursor->GetValue();
976         m_bShowLiveIcon = dialog->m_cbLiveIcon->GetValue();
977         m_bShowIcon = dialog->m_cbShowIcon->GetValue();
978         m_iOpacity = dialog->m_sOpacity->GetValue();
979 
980         RearrangeWindow();
981         SetIconType();
982 
983         SaveConfig();
984     }
985     delete dialog;
986 }
987 
ShowPlotSettings()988 void wmm_pi::ShowPlotSettings()
989 {
990     WmmPlotSettingsDialog *dialog = new WmmPlotSettingsDialog( m_parent_window );
991     wxFont *pFont = OCPNGetFont(_T("Dialog"), 0);
992     dialog->SetFont(*pFont);
993 
994     dialog->Fit();
995     wxColour cl;
996     GetGlobalColor(_T("DILG1"), &cl);
997     dialog->SetBackgroundColour(cl);
998 
999     dialog->m_cbDeclination->SetValue(m_DeclinationMap.m_bEnabled);
1000     dialog->m_scDeclinationSpacing->SetValue(m_DeclinationMap.m_Spacing);
1001     dialog->m_cbInclination->SetValue(m_InclinationMap.m_bEnabled);
1002     dialog->m_scInclinationSpacing->SetValue(m_InclinationMap.m_Spacing);
1003     dialog->m_cbFieldStrength->SetValue(m_FieldStrengthMap.m_bEnabled);
1004     dialog->m_scFieldStrengthSpacing->SetValue(m_FieldStrengthMap.m_Spacing);
1005     ///dialog->m_dpDate->SetValue(m_MapDate);
1006     dialog->m_sStep->SetValue(m_MapStep);
1007     dialog->m_sPoleAccuracy->SetValue(m_MapPoleAccuracy);
1008 
1009     if(dialog->ShowModal() == wxID_OK)
1010     {
1011         m_DeclinationMap.m_bEnabled = dialog->m_cbDeclination->GetValue();
1012         m_DeclinationMap.m_Spacing = dialog->m_scDeclinationSpacing->GetValue();
1013         m_InclinationMap.m_bEnabled = dialog->m_cbInclination->GetValue();
1014         m_InclinationMap.m_Spacing = dialog->m_scInclinationSpacing->GetValue();
1015         m_FieldStrengthMap.m_bEnabled = dialog->m_cbFieldStrength->GetValue();
1016         m_FieldStrengthMap.m_Spacing = dialog->m_scFieldStrengthSpacing->GetValue();
1017         ///m_MapDate = dialog->m_dpDate->GetValue();
1018         m_MapStep = dialog->m_sStep->GetValue();
1019         m_MapPoleAccuracy = dialog->m_sPoleAccuracy->GetValue();
1020         m_DeclinationMap.ConfigureAccuracy(m_MapStep, m_MapPoleAccuracy);
1021         m_InclinationMap.ConfigureAccuracy(m_MapStep, m_MapPoleAccuracy);
1022         m_FieldStrengthMap.ConfigureAccuracy(m_MapStep, m_MapPoleAccuracy);
1023 
1024         m_bCachedPlotOk = false;
1025         if(m_pWmmDialog->m_cbEnablePlot->GetValue())
1026           RecomputePlot();
1027 
1028         RequestRefresh( m_parent_window );
1029         RearrangeWindow();
1030 
1031         SaveConfig();
1032     }
1033     delete dialog;
1034 }
1035