1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  *
5  ***************************************************************************
6  *   Copyright (C) 2013 by David S. Register                               *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
22  **************************************************************************/
23 
24 #include "wx/wxprec.h"
25 
26 #include <wx/dcscreen.h>
27 #include <wx/tokenzr.h>
28 
29 #include "routeman.h"
30 #include "chcanv.h"
31 #include "RoutePoint.h"
32 #include "multiplexer.h"
33 #include "navutil.h"
34 #include "FontMgr.h"
35 #include "cutil.h"
36 #include "georef.h"
37 #include "wx28compat.h"
38 #include "OCPNPlatform.h"
39 #include "glChartCanvas.h"
40 #include "Select.h"
41 #include "chart1.h"
42 
43 extern WayPointman  *pWayPointMan;
44 extern bool         g_bIsNewLayer;
45 extern int          g_LayerIdx;
46 extern Routeman     *g_pRouteMan;
47 extern wxRect       g_blink_rect;
48 extern Multiplexer  *g_pMUX;
49 extern MyFrame      *gFrame;
50 extern bool         g_btouch;
51 extern ocpnStyle::StyleManager* g_StyleManager;
52 extern double       g_n_arrival_circle_radius;
53 extern int          g_iWaypointRangeRingsNumber;
54 extern float        g_fWaypointRangeRingsStep;
55 extern int          g_iWaypointRangeRingsStepUnits;
56 extern wxColour     g_colourWaypointRangeRingsColour;
57 extern OCPNPlatform *g_Platform;
58 extern float        g_ChartScaleFactorExp;
59 extern int          g_iWpt_ScaMin;
60 extern bool         g_bUseWptScaMin;
61 extern bool         g_bOverruleScaMin;
62 
63 extern wxImage LoadSVGIcon( wxString filename, int width, int height );
64 
65 #include <wx/listimpl.cpp>
66 WX_DEFINE_LIST ( RoutePointList );
67 
RoutePoint()68 RoutePoint::RoutePoint()
69 {
70     m_pbmIcon = NULL;
71 
72     //  Nice defaults
73     m_seg_len = 0.0;
74     m_seg_vmg = 0.0;
75 
76     m_seg_etd = wxInvalidDateTime;
77     m_manual_etd =  false;
78 
79     m_seg_eta = wxInvalidDateTime;
80     m_bDynamicName = false;
81     m_bPtIsSelected = false;
82     m_bRPIsBeingEdited = false;
83     m_bIsActive = false;
84     m_bBlink = false;
85     m_bIsInRoute = false;
86     m_CreateTimeX = wxDateTime::Now();
87     m_bIsolatedMark = false;
88     m_bShowName = true;
89     m_bKeepXRoute = false;
90     m_bIsVisible = true;
91     m_bIsListed = true;
92     CurrentRect_in_DC = wxRect( 0, 0, 0, 0 );
93     m_NameLocationOffsetX = -10;
94     m_NameLocationOffsetY = 8;
95     m_pMarkFont = NULL;
96     m_btemp = false;
97     m_SelectNode = NULL;
98     m_ManagerNode = NULL;
99 
100     m_iTextTexture = 0;
101 
102     m_HyperlinkList = new HyperlinkList;
103 
104     m_GUID = pWayPointMan->CreateGUID( this );
105 
106     m_IconName = wxEmptyString;
107     ReLoadIcon();
108 
109     m_MarkName = wxEmptyString;
110 
111     m_bIsInLayer = false;
112     m_LayerID = 0;
113 
114     m_WaypointArrivalRadius = g_n_arrival_circle_radius;
115 
116     m_bShowWaypointRangeRings = (bool)g_iWaypointRangeRingsNumber;
117 
118     m_iWaypointRangeRingsNumber = g_iWaypointRangeRingsNumber;
119     m_fWaypointRangeRingsStep = g_fWaypointRangeRingsStep;
120     m_iWaypointRangeRingsStepUnits = g_iWaypointRangeRingsStepUnits;
121     m_wxcWaypointRangeRingsColour = g_colourWaypointRangeRingsColour;
122     m_ScaMin = g_iWpt_ScaMin;
123     m_ScaMax = 0;
124     b_UseScamin = g_bUseWptScaMin;
125 
126 
127 #ifdef ocpnUSE_GL
128     m_pos_on_screen = false;
129 #endif
130     m_bDrawDragHandle = false;
131     m_dragIconTexture = 0;
132     m_draggingOffsetx = m_draggingOffsety = 0;
133 
134     m_PlannedSpeed = 0.;
135 }
136 
137 // Copy Constructor
RoutePoint(RoutePoint * orig)138 RoutePoint::RoutePoint( RoutePoint* orig )
139 {
140     m_MarkName = orig->GetName();
141     m_lat = orig->m_lat;
142     m_lon = orig->m_lon;
143     m_seg_len = orig->m_seg_len;
144     m_seg_vmg = orig->m_seg_vmg;
145 
146     m_seg_etd = orig->m_seg_etd;
147     m_manual_etd = false;
148 
149     m_bDynamicName = orig->m_bDynamicName;
150     m_bPtIsSelected = orig->m_bPtIsSelected;
151     m_bRPIsBeingEdited = orig->m_bRPIsBeingEdited;
152     m_bIsActive = orig->m_bIsActive;
153     m_bBlink = orig->m_bBlink;
154     m_bIsInRoute = orig->m_bIsInRoute;
155     m_CreateTimeX = orig->m_CreateTimeX;
156     m_bIsolatedMark = orig->m_bIsolatedMark;
157     m_bShowName = orig->m_bShowName;
158     m_bKeepXRoute = orig->m_bKeepXRoute;
159     m_bIsVisible = orig->m_bIsVisible;
160     m_bIsListed = orig->m_bIsListed;
161     CurrentRect_in_DC = orig->CurrentRect_in_DC;
162     m_NameLocationOffsetX = orig->m_NameLocationOffsetX;
163     m_NameLocationOffsetY = orig->m_NameLocationOffsetY;
164     m_pMarkFont = orig->m_pMarkFont;
165     m_MarkDescription = orig->m_MarkDescription;
166     m_btemp = orig->m_btemp;
167     m_ScaMin = orig->m_ScaMin;
168     m_ScaMax = orig->m_ScaMax;
169     m_HyperlinkList = new HyperlinkList;
170     m_IconName = orig->m_IconName;
171     m_TideStation = orig->m_TideStation;
172     SetPlannedSpeed(orig->GetPlannedSpeed());
173     ReLoadIcon();
174 
175     m_bIsInLayer = orig->m_bIsInLayer;
176     m_GUID = pWayPointMan->CreateGUID( this );
177 
178     m_SelectNode = NULL;
179     m_ManagerNode = NULL;
180 
181     m_WaypointArrivalRadius = orig->GetWaypointArrivalRadius();
182     m_bShowWaypointRangeRings = orig->m_bShowWaypointRangeRings;
183     m_iWaypointRangeRingsNumber = orig->m_iWaypointRangeRingsNumber;
184     m_fWaypointRangeRingsStep = orig->m_fWaypointRangeRingsStep;
185     m_iWaypointRangeRingsStepUnits = orig->m_iWaypointRangeRingsStepUnits;
186     m_wxcWaypointRangeRingsColour = orig->m_wxcWaypointRangeRingsColour;
187     m_ScaMin = orig->m_ScaMin;
188     m_ScaMax = orig->m_ScaMax;
189     b_UseScamin = orig->b_UseScamin;
190 
191     m_bDrawDragHandle = false;
192     m_dragIconTexture = 0;
193     m_draggingOffsetx = m_draggingOffsety = 0;
194 
195 
196 }
197 
RoutePoint(double lat,double lon,const wxString & icon_ident,const wxString & name,const wxString & pGUID,bool bAddToList)198 RoutePoint::RoutePoint( double lat, double lon, const wxString& icon_ident, const wxString& name,
199         const wxString &pGUID, bool bAddToList )
200 {
201     //  Establish points
202     m_lat = lat;
203     m_lon = lon;
204 
205     //      Normalize the longitude, to fix any old poorly formed points
206     if( m_lon < -180. ) m_lon += 360.;
207     else
208         if( m_lon > 180. ) m_lon -= 360.;
209 
210     //  Nice defaults
211     m_seg_len = 0.0;
212     m_seg_vmg = 0.0;
213 
214     m_seg_etd = wxInvalidDateTime;
215     m_manual_etd =  false;
216 
217     m_bDynamicName = false;
218     m_bPtIsSelected = false;
219     m_bRPIsBeingEdited = false;
220     m_bIsActive = false;
221     m_bBlink = false;
222     m_bIsInRoute = false;
223     m_CreateTimeX = wxDateTime::Now();
224     m_bIsolatedMark = false;
225     m_bShowName = true;
226     m_bKeepXRoute = false;
227     m_bIsVisible = true;
228     m_bIsListed = true;
229     CurrentRect_in_DC = wxRect( 0, 0, 0, 0 );
230     m_NameLocationOffsetX = -10;
231     m_NameLocationOffsetY = 8;
232     m_pMarkFont = NULL;
233     m_btemp = false;
234     m_bPreScaled = false;
235 
236     m_SelectNode = NULL;
237     m_ManagerNode = NULL;
238     m_IconScaleFactor = 1.0;
239     m_ScaMin = MAX_INT_VAL;
240     m_ScaMax = 0;
241     m_HyperlinkList = new HyperlinkList;
242 
243     if( !pGUID.IsEmpty() )
244         m_GUID = pGUID;
245     else
246         m_GUID = pWayPointMan->CreateGUID( this );
247 
248     //      Get Icon bitmap
249     m_IconName = icon_ident;
250     ReLoadIcon();
251 
252     SetName( name );
253 
254     //  Possibly add the waypoint to the global list maintained by the waypoint manager
255 
256     if( bAddToList && NULL != pWayPointMan )
257         pWayPointMan->AddRoutePoint( this );
258 
259     m_bIsInLayer = g_bIsNewLayer;
260     if( m_bIsInLayer ) {
261         m_LayerID = g_LayerIdx;
262         m_bIsListed = false;
263     } else
264         m_LayerID = 0;
265 
266     SetWaypointArrivalRadius( g_n_arrival_circle_radius );
267 
268     m_bShowWaypointRangeRings = (bool)g_iWaypointRangeRingsNumber;
269 
270     m_iWaypointRangeRingsNumber = g_iWaypointRangeRingsNumber;
271     m_fWaypointRangeRingsStep = g_fWaypointRangeRingsStep;
272     m_iWaypointRangeRingsStepUnits = g_iWaypointRangeRingsStepUnits;
273     m_wxcWaypointRangeRingsColour = g_colourWaypointRangeRingsColour;
274     m_ScaMin = g_iWpt_ScaMin;
275     m_ScaMax = 0;
276     b_UseScamin = g_bUseWptScaMin;
277 
278 
279     m_bDrawDragHandle = false;
280     m_dragIconTexture = 0;
281     m_draggingOffsetx = m_draggingOffsety = 0;
282 
283     m_PlannedSpeed = 0.;
284 }
285 
~RoutePoint()286 RoutePoint::~RoutePoint( )
287 {
288 //  Remove this point from the global waypoint list
289     if( NULL != pWayPointMan )
290         pWayPointMan->RemoveRoutePoint( this );
291 
292     if( m_HyperlinkList ) {
293         m_HyperlinkList->DeleteContents( true );
294         delete m_HyperlinkList;
295     }
296 #ifdef ocpnUSE_GL
297     if(m_dragIconTexture > 0)
298         glDeleteTextures(1, &m_dragIconTexture);
299 #endif
300 }
301 
GetDragHandlePoint(ChartCanvas * canvas)302 wxPoint2DDouble RoutePoint::GetDragHandlePoint(ChartCanvas *canvas)
303 {
304     if(!m_bDrawDragHandle)
305        return wxPoint2DDouble(m_lon, m_lat);
306     else{
307        return computeDragHandlePoint(canvas);
308     }
309 }
310 
computeDragHandlePoint(ChartCanvas * canvas)311 wxPoint2DDouble RoutePoint::computeDragHandlePoint(ChartCanvas *canvas)
312 {
313     wxPoint r;
314     canvas->GetCanvasPointPix( m_lat, m_lon, &r );
315     double lat, lon;
316     canvas->GetCanvasPixPoint(r.x + m_drag_icon_offset, r.y + m_drag_icon_offset, lat, lon);
317 
318     // Keep the members updated
319     m_dragHandleLat = lat;
320     m_dragHandleLon = lon;
321 
322     return wxPoint2DDouble(lon, lat);
323 }
324 
SetPointFromDraghandlePoint(ChartCanvas * canvas,double lat,double lon)325 void RoutePoint::SetPointFromDraghandlePoint(ChartCanvas *canvas, double lat, double lon)
326 {
327     wxPoint r;
328     canvas->GetCanvasPointPix( lat, lon, &r );
329     double tlat, tlon;
330     canvas->GetCanvasPixPoint(r.x - m_drag_icon_offset, r.y - m_drag_icon_offset, tlat, tlon);
331     m_lat = tlat;
332     m_lon = tlon;
333 }
334 
SetPointFromDraghandlePoint(ChartCanvas * canvas,int x,int y)335 void RoutePoint::SetPointFromDraghandlePoint(ChartCanvas *canvas, int x, int y)
336 {
337     double tlat, tlon;
338     canvas->GetCanvasPixPoint(x - m_drag_icon_offset - m_draggingOffsetx, y - m_drag_icon_offset - m_draggingOffsety, tlat, tlon);
339     m_lat = tlat;
340     m_lon = tlon;
341 }
342 
PresetDragOffset(ChartCanvas * canvas,int x,int y)343 void RoutePoint::PresetDragOffset( ChartCanvas *canvas, int x, int y)
344 {
345     wxPoint r;
346     canvas->GetCanvasPointPix( m_lat, m_lon, &r );
347 
348     m_draggingOffsetx = x - (r.x + m_drag_icon_offset);
349     m_draggingOffsety = y - (r.y + m_drag_icon_offset);
350 }
351 
EnableDragHandle(bool bEnable)352 void RoutePoint::EnableDragHandle(bool bEnable)
353 {
354     m_bDrawDragHandle = bEnable;
355     if(bEnable){
356         if(!m_dragIcon.IsOk()){
357             // Get the icon
358             // What size?
359             int bm_size = g_Platform->GetDisplayDPmm() * 9;     //9 mm nominal
360 
361             // What icon?
362             wxString UserIconPath = g_Platform->GetSharedDataDir() + _T("uidata") + wxFileName::GetPathSeparator();
363 
364             wxImage iconSVG = LoadSVGIcon( UserIconPath  + _T("DragHandle.svg"), bm_size, bm_size );
365             if(iconSVG.IsOk())
366                 m_dragIcon = wxBitmap(iconSVG);
367             else
368                 m_dragIcon = *m_pbmIcon;                // Drag handle icon not found
369 
370             // build a texture
371 #ifdef ocpnUSE_GL
372         /* make rgba texture */
373             if(m_dragIconTexture == 0){
374                 glGenTextures(1, &m_dragIconTexture);
375                 glBindTexture(GL_TEXTURE_2D, m_dragIconTexture);
376 
377                 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
378                 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
379                 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
380 
381 
382                 wxImage image = iconSVG;
383                 int w = image.GetWidth(), h = image.GetHeight();
384 
385                 m_dragIconTextureWidth = NextPow2(w);
386                 m_dragIconTextureHeight = NextPow2(h);
387 
388                 unsigned char *d = image.GetData();
389                 unsigned char *a = image.GetAlpha();
390 
391                 unsigned char mr, mg, mb;
392                 image.GetOrFindMaskColour( &mr, &mg, &mb );
393 
394                 unsigned char *e = new unsigned char[4 * w * h];
395                 if(d && e){
396                     for( int y = 0; y < h; y++ )
397                         for( int x = 0; x < w; x++ ) {
398                             unsigned char r, g, b;
399                             int off = ( y * image.GetWidth() + x );
400                             r = d[off * 3 + 0];
401                             g = d[off * 3 + 1];
402                             b = d[off * 3 + 2];
403                             e[off * 4 + 0] = r;
404                             e[off * 4 + 1] = g;
405                             e[off * 4 + 2] = b;
406 
407                             e[off * 4 + 3] =  a ? a[off] : ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
408                         }
409                 }
410 
411                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_dragIconTextureWidth, m_dragIconTextureHeight,
412                         0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
413                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
414                             GL_RGBA, GL_UNSIGNED_BYTE, e);
415 
416                 delete [] e;
417             }
418 #endif
419 
420             // set the drawing metrics
421             if(iconSVG.IsOk()){
422                 m_drag_line_length_man = bm_size;
423                 m_drag_icon_offset = bm_size;
424             }
425             else{
426                 m_drag_line_length_man = 64;
427                 m_drag_icon_offset = 64;
428             }
429         }
430     }
431 }
432 
GetCreateTime()433 wxDateTime RoutePoint::GetCreateTime()
434 {
435     if(!m_CreateTimeX.IsValid()) {
436         if(m_timestring.Len())
437             ParseGPXDateTime( m_CreateTimeX, m_timestring );
438     }
439     return m_CreateTimeX;
440 }
441 
SetCreateTime(wxDateTime dt)442 void RoutePoint::SetCreateTime( wxDateTime dt )
443 {
444     m_CreateTimeX = dt;
445 }
446 
SetName(const wxString & name)447 void RoutePoint::SetName(const wxString & name)
448 {
449 #ifdef ocpnUSE_GL
450     if(m_iTextTexture){
451         glDeleteTextures(1,&m_iTextTexture);
452         m_iTextTexture = 0;
453     }
454 #endif
455     m_MarkName = name;
456     CalculateNameExtents();
457 }
458 
CalculateNameExtents(void)459 void RoutePoint::CalculateNameExtents( void )
460 {
461     if( m_pMarkFont ) {
462         wxScreenDC dc;
463 
464 #ifdef __WXQT__                 // avoiding "painter not active" warning
465         int w, h;
466         dc.GetTextExtent(m_MarkName, &w, &h, NULL, NULL, m_pMarkFont);
467         m_NameExtents = wxSize(w,h);
468 #else
469         dc.SetFont( *m_pMarkFont );
470         m_NameExtents = dc.GetMultiLineTextExtent( m_MarkName );
471 #endif
472     } else
473         m_NameExtents = wxSize( 0, 0 );
474 
475 }
476 
ReLoadIcon(void)477 void RoutePoint::ReLoadIcon( void )
478 {
479     if(!pWayPointMan)
480         return;
481     bool icon_exists = pWayPointMan->DoesIconExist(m_IconName);
482 
483     wxString iconUse = m_IconName;
484     if( !icon_exists ){
485 
486         //  Try all lower case as a favor in the case where imported waypoints use mixed case names
487         wxString tentative_icon = m_IconName.Lower();
488         if(pWayPointMan->DoesIconExist(tentative_icon)){
489             // if found, convert point's icon name permanently.
490             m_IconName = tentative_icon;
491             iconUse = m_IconName;
492         }
493         //      Icon name is not in the standard or user lists, so add to the list a generic placeholder
494         else{
495             if(!pWayPointMan->DoesIconExist(_T("tempsub"))){
496 
497                 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
498                 if(style){
499                     wxBitmap bmp = style->GetIcon( _T("circle") );
500                     if(bmp.IsOk())
501                         pWayPointMan->ProcessIcon( bmp, _T("tempsub"), _T("tempsub") );
502                 }
503             }
504             iconUse = _T("tempsub");
505 
506         }
507     }
508 
509     m_pbmIcon = pWayPointMan->GetIconBitmap( iconUse );
510     m_bPreScaled = pWayPointMan->GetIconPrescaled( iconUse );
511 
512 #ifdef ocpnUSE_GL
513     m_wpBBox_view_scale_ppm = -1;
514 
515     m_iTextTexture = 0;
516 #endif
517 
518     m_IconScaleFactor = -1;             // Force scaled icon reload
519 }
520 
IsVisibleSelectable(ChartCanvas * canvas,bool boverrideViz)521 bool RoutePoint::IsVisibleSelectable(ChartCanvas *canvas, bool boverrideViz)
522 {
523     if( m_bIsActive)  //  An active route point must always be visible
524         return true;
525 
526     if(!boverrideViz){
527         if( !m_bIsVisible ) // if not visible nevermind the rest.
528             return false;
529     }
530 
531     if( b_UseScamin ){
532         if (g_bOverruleScaMin)
533             return true;
534         else
535             if (canvas->GetScaleValue() > m_ScaMin)
536                 return false;
537     }
538     return true;
539 }
540 
Draw(ocpnDC & dc,ChartCanvas * canvas,wxPoint * rpn,bool boverride_viz)541 void RoutePoint::Draw( ocpnDC& dc, ChartCanvas *canvas, wxPoint *rpn, bool boverride_viz )
542 {
543     wxPoint r;
544     wxRect hilitebox;
545 
546     canvas->GetCanvasPointPix( m_lat, m_lon, &r );
547 
548     //  return the home point in this dc to allow "connect the dots"
549     if( NULL != rpn ) *rpn = r;
550 
551     /*if( !m_bIsVisible )     // pjotrc 2010.02.13, 2011.02.24
552         return;
553     if( !m_bIsActive)  //  An active route point must always be visible
554         if( !IsScaVisible( canvas) )
555             return;   */
556     if ( !IsVisibleSelectable(canvas, boverride_viz) ) return;
557 
558     //    Optimization, especially apparent on tracks in normal cases
559     if( m_IconName == _T("empty") && !m_bShowName && !m_bPtIsSelected ) return;
560 
561     wxPen *pen;
562     if( m_bBlink )
563         pen = g_pRouteMan->GetActiveRoutePointPen();
564     else
565         pen = g_pRouteMan->GetRoutePointPen();
566 
567 //    Substitue icon?
568     wxBitmap *pbm;
569     if( ( m_bIsActive ) && ( m_IconName != _T("mob") ) )
570         pbm = pWayPointMan->GetIconBitmap( _T ( "activepoint" ) );
571     else
572         pbm = m_pbmIcon;
573 
574     wxBitmap *pbms = NULL;
575     if( (g_ChartScaleFactorExp > 1.0) && !m_bPreScaled ){
576         if(m_IconScaleFactor != g_ChartScaleFactorExp){
577             wxImage scaled_image = pbm->ConvertToImage();
578             int new_width = pbm->GetWidth() * g_ChartScaleFactorExp;
579             int new_height = pbm->GetHeight() * g_ChartScaleFactorExp;
580             m_ScaledBMP = wxBitmap(scaled_image.Scale(new_width, new_height, wxIMAGE_QUALITY_HIGH));
581 
582             m_IconScaleFactor = g_ChartScaleFactorExp;
583         }
584         if( m_ScaledBMP.IsOk() )
585             pbm = &m_ScaledBMP;
586     }
587 
588     int sx2 = pbm->GetWidth() / 2;
589     int sy2 = pbm->GetHeight() / 2;
590 
591 //    Calculate the mark drawing extents
592     wxRect r1( r.x - sx2, r.y - sy2, sx2 * 2, sy2 * 2 );           // the bitmap extents
593 
594     if( m_bShowName ) {
595         if( 0 == m_pMarkFont ) {
596             m_pMarkFont = FontMgr::Get().GetFont( _( "Marks" ) );
597             m_FontColor = FontMgr::Get().GetFontColor( _( "Marks" ) );
598             CalculateNameExtents();
599         }
600 
601         if( m_pMarkFont ) {
602             wxRect r2( r.x + m_NameLocationOffsetX, r.y + m_NameLocationOffsetY, m_NameExtents.x,
603                     m_NameExtents.y );
604             r1.Union( r2 );
605         }
606     }
607 
608     hilitebox = r1;
609     hilitebox.x -= r.x;
610     hilitebox.y -= r.y;
611     float radius;
612     if( g_btouch ){
613         hilitebox.Inflate( 20 );
614         radius = 20.0f;
615     }
616     else{
617         hilitebox.Inflate( 4 );
618         radius = 4.0f;
619     }
620 
621     wxColour hi_colour = pen->GetColour();
622     unsigned char transparency = 100;
623     if( m_bRPIsBeingEdited ){
624         hi_colour = GetGlobalColor( _T ( "YELO1" ) );
625         transparency = 150;
626     }
627 
628 
629     //  Highlite any selected point
630     if( m_bPtIsSelected || m_bRPIsBeingEdited) {
631         AlphaBlending( dc, r.x + hilitebox.x, r.y + hilitebox.y, hilitebox.width, hilitebox.height, radius,
632                 hi_colour, transparency );
633     }
634 
635     bool bDrawHL = false;
636 
637     if( m_bBlink && ( gFrame->nBlinkerTick & 1 ) ) bDrawHL = true;
638 
639     if( ( !bDrawHL ) && ( NULL != m_pbmIcon ) ) {
640         dc.DrawBitmap( *pbm, r.x - sx2, r.y - sy2, true );
641         // on MSW, the dc Bounding box is not updated on DrawBitmap() method.
642         // Do it explicitely here for all platforms.
643         dc.CalcBoundingBox( r.x - sx2, r.y - sy2 );
644         dc.CalcBoundingBox( r.x + sx2, r.y + sy2 );
645     }
646 
647     if( m_bShowName ) {
648         if( m_pMarkFont ) {
649             dc.SetFont( *m_pMarkFont );
650             dc.SetTextForeground( m_FontColor );
651 
652             dc.DrawText( m_MarkName, r.x + m_NameLocationOffsetX, r.y + m_NameLocationOffsetY );
653         }
654     }
655 
656     // Draw waypoint radar rings if activated
657     if( m_iWaypointRangeRingsNumber && m_bShowWaypointRangeRings ) {
658         double factor = 1.00;
659         if( m_iWaypointRangeRingsStepUnits == 1 )          // nautical miles
660             factor = 1 / 1.852;
661 
662         factor *= m_fWaypointRangeRingsStep;
663 
664         double tlat, tlon;
665         wxPoint r1;
666         ll_gc_ll( m_lat, m_lon, 0, factor, &tlat, &tlon );
667         canvas->GetCanvasPointPix( tlat, tlon, &r1 );
668 
669         double lpp = sqrt( pow( (double) (r.x - r1.x), 2) +
670                            pow( (double) (r.y - r1.y), 2 ) );
671         int pix_radius = (int) lpp;
672 
673         wxPen ppPen1( m_wxcWaypointRangeRingsColour, 2 );
674         wxBrush saveBrush = dc.GetBrush();
675         wxPen savePen = dc.GetPen();
676         dc.SetPen( ppPen1 );
677         dc.SetBrush( wxBrush( m_wxcWaypointRangeRingsColour, wxBRUSHSTYLE_TRANSPARENT ) );
678 
679         for( int i = 1; i <= m_iWaypointRangeRingsNumber; i++ )
680             dc.StrokeCircle( r.x, r.y, i * pix_radius );
681         dc.SetPen( savePen );
682         dc.SetBrush( saveBrush );
683     }
684 
685     //  Save the current draw rectangle in the current DC
686     //    This will be useful for fast icon redraws
687     CurrentRect_in_DC.x = r.x + hilitebox.x;
688     CurrentRect_in_DC.y = r.y + hilitebox.y;
689     CurrentRect_in_DC.width = hilitebox.width;
690     CurrentRect_in_DC.height = hilitebox.height;
691 
692     if( m_bBlink ) g_blink_rect = CurrentRect_in_DC;               // also save for global blinker
693 
694     delete pbms;        // the potentially scaled bitmap
695 }
696 
697 #ifdef ocpnUSE_GL
DrawGL(ViewPort & vp,ChartCanvas * canvas,bool use_cached_screen_coords,bool bVizOverride)698 void RoutePoint::DrawGL( ViewPort &vp, ChartCanvas *canvas, bool use_cached_screen_coords, bool bVizOverride )
699 {
700     if ( !IsVisibleSelectable(canvas, bVizOverride) ) return;
701 
702     //    Optimization, especially apparent on tracks in normal cases
703     if( m_IconName == _T("empty") && !m_bShowName && !m_bPtIsSelected ) return;
704 
705     if(m_wpBBox.GetValid() &&
706        vp.view_scale_ppm == m_wpBBox_view_scale_ppm &&
707        vp.rotation == m_wpBBox_rotation) {
708         /* see if this waypoint can intersect with bounding box */
709         LLBBox vpBBox = vp.GetBBox();
710         if( vpBBox.IntersectOut( m_wpBBox ) ){
711 
712             // Are Range Rings enabled?
713             if(m_bShowWaypointRangeRings && (m_iWaypointRangeRingsNumber > 0)){
714                 double factor = 1.00;
715                 if( m_iWaypointRangeRingsStepUnits == 1 )          // convert kilometers to NMi
716                     factor = 1 / 1.852;
717 
718                 double radius = factor * m_iWaypointRangeRingsNumber * m_fWaypointRangeRingsStep  / 60.;
719 
720                 LLBBox radar_box = m_wpBBox;
721                 radar_box.EnLarge(radius * 2 );
722                 if( vpBBox.IntersectOut( radar_box ) ){
723                     return;
724                 }
725             }
726             else
727                 return;
728         }
729     }
730 
731     wxPoint r;
732     wxRect hilitebox;
733     unsigned char transparency = 150;
734 
735     if(use_cached_screen_coords && m_pos_on_screen)
736         r.x = m_screen_pos.m_x, r.y = m_screen_pos.m_y;
737     else
738         canvas->GetCanvasPointPix( m_lat, m_lon, &r );
739 
740     if(r.x == INVALID_COORD)
741         return;
742 
743 //    Substitute icon?
744     wxBitmap *pbm;
745     if( ( m_bIsActive ) && ( m_IconName != _T("mob") ) )
746         pbm = pWayPointMan->GetIconBitmap(  _T ( "activepoint" ) );
747     else
748         pbm = m_pbmIcon;
749 
750     //  If icon is corrupt, there is really nothing else to do...
751     if(!pbm->IsOk())
752         return;
753 
754     int sx2 = pbm->GetWidth() / 2;
755     int sy2 = pbm->GetHeight() / 2;
756 
757 //    Calculate the mark drawing extents
758     wxRect r1( r.x - sx2, r.y - sy2, sx2 * 2, sy2 * 2 );           // the bitmap extents
759 
760     wxRect r3 = r1;
761     if( m_bShowName ) {
762         if( !m_pMarkFont ) {
763             m_pMarkFont = FontMgr::Get().GetFont( _( "Marks" ) );
764             m_FontColor = FontMgr::Get().GetFontColor( _( "Marks" ) );
765             CalculateNameExtents();
766         }
767 
768         if( m_pMarkFont ) {
769             wxRect r2( r.x + m_NameLocationOffsetX, r.y + m_NameLocationOffsetY,
770                        m_NameExtents.x, m_NameExtents.y );
771             r3.Union( r2 );
772         }
773     }
774 
775     hilitebox = r3;
776     hilitebox.x -= r.x;
777     hilitebox.y -= r.y;
778 
779     if(!m_bPreScaled){
780         hilitebox.x *= g_ChartScaleFactorExp;
781         hilitebox.y *= g_ChartScaleFactorExp;
782         hilitebox.width  *= g_ChartScaleFactorExp;
783         hilitebox.height *= g_ChartScaleFactorExp;
784     }
785 
786     float radius;
787     if( g_btouch ){
788         hilitebox.Inflate( 20 );
789         radius = 20.0f;
790     }
791     else{
792         hilitebox.Inflate( 4 );
793         radius = 4.0f;
794     }
795 
796     /* update bounding box */
797     if(!m_wpBBox.GetValid() || vp.view_scale_ppm != m_wpBBox_view_scale_ppm || vp.rotation != m_wpBBox_rotation) {
798         double lat1, lon1, lat2, lon2;
799         canvas->GetCanvasPixPoint(r.x+hilitebox.x, r.y+hilitebox.y+hilitebox.height, lat1, lon1);
800         canvas->GetCanvasPixPoint(r.x+hilitebox.x+hilitebox.width, r.y+hilitebox.y, lat2, lon2);
801 
802         if(lon1 > lon2)
803             m_wpBBox.Set(lat1, lon1, lat2, lon2+360);
804         else
805             m_wpBBox.Set(lat1, lon1, lat2, lon2);
806 
807         m_wpBBox_view_scale_ppm = vp.view_scale_ppm;
808         m_wpBBox_rotation = vp.rotation;
809     }
810 
811 //    if(region.Contains(r3) == wxOutRegion)
812 //        return;
813 
814 
815     ocpnDC dc;
816 
817     //  Highlite any selected point
818     if( m_bPtIsSelected ) {
819         wxColour hi_colour;
820         if( m_bBlink ){
821             wxPen *pen = g_pRouteMan->GetActiveRoutePointPen();
822             hi_colour = pen->GetColour();
823         }
824         else{
825             hi_colour = GetGlobalColor( _T ( "YELO1" ) );
826         }
827 
828         AlphaBlending( dc, r.x + hilitebox.x, r.y + hilitebox.y, hilitebox.width, hilitebox.height, radius,
829                        hi_colour, transparency );
830     }
831 
832     bool bDrawHL = false;
833 
834     if( m_bBlink && ( gFrame->nBlinkerTick & 1 ) ) bDrawHL = true;
835 
836     if( ( !bDrawHL ) && ( NULL != m_pbmIcon ) ) {
837         int glw, glh;
838         unsigned int IconTexture = pWayPointMan->GetIconTexture( pbm, glw, glh );
839 
840         glBindTexture(GL_TEXTURE_2D, IconTexture);
841 
842         glEnable(GL_TEXTURE_2D);
843         glEnable(GL_BLEND);
844 
845         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
846         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
847 
848 
849         int w = r1.width, h = r1.height;
850 
851         float scale = 1.0;
852         if(!m_bPreScaled){
853             scale =  g_ChartScaleFactorExp;
854         }
855 
856         float ws = r1.width * scale;
857         float hs = r1.height * scale;
858         float xs = r.x - ws/2.;
859         float ys = r.y - hs/2.;
860         float u = (float)w/glw, v = (float)h/glh;
861 
862 #ifdef USE_ANDROID_GLES2
863         float coords[8];
864         float uv[8];
865         //normal uv
866         uv[0] = 0; uv[1] = 0; uv[2] = u; uv[3] = 0;
867         uv[4] = u; uv[5] = v; uv[6] = 0; uv[7] = v;
868 
869         // pixels
870         coords[0] = xs; coords[1] = ys; coords[2] = xs+ws; coords[3] = ys;
871         coords[4] = xs+ws; coords[5] = ys+hs; coords[6] = xs, coords[7] = ys+hs;
872 
873         glChartCanvas::RenderSingleTexture(coords, uv, &vp, 0, 0, 0);
874 
875 #else
876         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
877 
878         glColor3f(1, 1, 1);
879 
880         glBegin(GL_QUADS);
881         glTexCoord2f(0, 0); glVertex2f(xs, ys);
882         glTexCoord2f(u, 0); glVertex2f(xs+ws, ys);
883         glTexCoord2f(u, v); glVertex2f(xs+ws, ys+hs);
884         glTexCoord2f(0, v); glVertex2f(xs, ys+hs);
885         glEnd();
886 
887 #endif
888 
889         glDisable(GL_BLEND);
890         glDisable(GL_TEXTURE_2D);
891     }
892 
893     if( m_bShowName && m_pMarkFont ) {
894         int w = m_NameExtents.x, h = m_NameExtents.y;
895         if(!m_iTextTexture && w && h) {
896             wxBitmap tbm(w, h); /* render text on dc */
897             wxMemoryDC dc;
898             dc.SelectObject( tbm );
899             dc.SetBackground( wxBrush( *wxBLACK ) );
900             dc.Clear();
901             dc.SetFont( *m_pMarkFont );
902             dc.SetTextForeground( *wxWHITE );
903             dc.DrawText( m_MarkName, 0, 0);
904             dc.SelectObject( wxNullBitmap );
905 
906             /* make alpha texture for text */
907             wxImage image = tbm.ConvertToImage();
908             unsigned char *d = image.GetData();
909             unsigned char *e = new unsigned char[w * h];
910             if(d && e){
911                 for( int p = 0; p < w*h; p++)
912                     e[p] = d[3*p + 0];
913             }
914 
915             /* create texture for rendered text */
916             glGenTextures(1, &m_iTextTexture);
917             glBindTexture(GL_TEXTURE_2D, m_iTextTexture);
918 
919             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
920             glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
921 
922             m_iTextTextureWidth = NextPow2(w);
923             m_iTextTextureHeight = NextPow2(h);
924             glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_iTextTextureWidth, m_iTextTextureHeight,
925                          0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
926             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
927                             GL_ALPHA, GL_UNSIGNED_BYTE, e);
928             delete [] e;
929         }
930 
931         if(m_iTextTexture) {
932             /* draw texture with text */
933             glBindTexture(GL_TEXTURE_2D, m_iTextTexture);
934 
935             glEnable(GL_TEXTURE_2D);
936             glEnable(GL_BLEND);
937 
938 
939             int x = r.x + m_NameLocationOffsetX, y = r.y + m_NameLocationOffsetY;
940             float u = (float)w/m_iTextTextureWidth, v = (float)h/m_iTextTextureHeight;
941 #ifndef USE_ANDROID_GLES2
942             glColor3ub(m_FontColor.Red(), m_FontColor.Green(), m_FontColor.Blue());
943 
944             glBegin(GL_QUADS);
945             glTexCoord2f(0, 0); glVertex2f(x, y);
946             glTexCoord2f(u, 0); glVertex2f(x+w, y);
947             glTexCoord2f(u, v); glVertex2f(x+w, y+h);
948             glTexCoord2f(0, v); glVertex2f(x, y+h);
949             glEnd();
950 
951 #else
952             float coords[8];
953             float uv[8];
954             //normal uv
955             uv[0] = 0; uv[1] = 0; uv[2] = u; uv[3] = 0;
956             uv[4] = u; uv[5] = v; uv[6] = 0; uv[7] = v;
957 
958             // pixels
959             coords[0] = x; coords[1] = y; coords[2] = x+w; coords[3] = y;
960             coords[4] = x+w; coords[5] = y+h; coords[6] = x, coords[7] = y+h;
961 
962             glChartCanvas::RenderSingleTexture(coords, uv, &vp, 0, 0, 0);
963 
964 #endif
965             glDisable(GL_BLEND);
966             glDisable(GL_TEXTURE_2D);
967         }
968     }
969 
970     // Draw waypoint radar rings if activated
971     if( m_iWaypointRangeRingsNumber && m_bShowWaypointRangeRings ) {
972         double factor = 1.00;
973         if( m_iWaypointRangeRingsStepUnits == 1 )          // nautical miles
974             factor = 1 / 1.852;
975 
976         factor *= m_fWaypointRangeRingsStep;
977 
978         double tlat, tlon;
979         wxPoint r1;
980         ll_gc_ll( m_lat, m_lon, 0, factor, &tlat, &tlon );
981         canvas->GetCanvasPointPix( tlat, tlon, &r1 );
982 
983         double lpp = sqrt( pow( (double) (r.x - r1.x), 2) +
984         pow( (double) (r.y - r1.y), 2 ) );
985         int pix_radius = (int) lpp;
986 
987         extern wxColor GetDimColor(wxColor c);
988         wxColor ring_dim_color = GetDimColor(m_wxcWaypointRangeRingsColour);
989 
990         double platform_pen_width = wxRound(wxMax(1.0, g_Platform->GetDisplayDPmm() / 2));             // 0.5 mm nominal, but not less than 1 pixel
991         wxPen ppPen1( ring_dim_color, platform_pen_width );
992         wxBrush saveBrush = dc.GetBrush();
993         wxPen savePen = dc.GetPen();
994         dc.SetPen( ppPen1 );
995         dc.SetBrush( wxBrush( ring_dim_color, wxBRUSHSTYLE_TRANSPARENT ) );
996 
997         for( int i = 1; i <= m_iWaypointRangeRingsNumber; i++ )
998             dc.StrokeCircle( r.x, r.y, i * pix_radius );
999         dc.SetPen( savePen );
1000         dc.SetBrush( saveBrush );
1001     }
1002 
1003     // Render Drag handle if enabled
1004     if(m_bDrawDragHandle){
1005 
1006         //  A line, southeast, scaled to the size of the icon
1007         double platform_pen_width = wxRound(wxMax(1.0, g_Platform->GetDisplayDPmm() / 2));             // 0.5 mm nominal, but not less than 1 pixel
1008 
1009         wxColor dh_color = GetGlobalColor( _T ( "YELO1" ) );
1010         wxPen ppPen1( dh_color, 3 * platform_pen_width );
1011         dc.SetPen( ppPen1 );
1012         dc.DrawLine(r.x + hilitebox.width/4, r.y + hilitebox.height/4, r.x + m_drag_line_length_man, r.y + m_drag_line_length_man);
1013 
1014         dh_color = wxColor(0,0,0);
1015         wxPen ppPen2( dh_color, platform_pen_width );
1016         dc.SetPen( ppPen2 );
1017         dc.DrawLine(r.x + hilitebox.width/4, r.y + hilitebox.height/4, r.x + m_drag_line_length_man, r.y + m_drag_line_length_man);
1018 
1019         // The drag handle
1020         glBindTexture(GL_TEXTURE_2D, m_dragIconTexture);
1021 
1022         glEnable(GL_TEXTURE_2D);
1023         glEnable(GL_BLEND);
1024 
1025         int x = r.x + m_drag_icon_offset, y = r.y + m_drag_icon_offset, w = m_dragIcon.GetWidth(), h = m_dragIcon.GetHeight();
1026 
1027         float scale =  1.0;
1028 
1029         float ws = w * scale;
1030         float hs = h * scale;
1031         float xs = x - ws/2.;
1032         float ys = y - hs/2.;
1033         float u = (float)w/m_dragIconTextureWidth, v = (float)h/m_dragIconTextureWidth;
1034 
1035 #ifndef USE_ANDROID_GLES2
1036         glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
1037 
1038         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1039         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1040 
1041         glColor3f(1, 1, 1);
1042 
1043         glBegin(GL_QUADS);
1044         glTexCoord2f(0, 0); glVertex2f(xs, ys);
1045         glTexCoord2f(u, 0); glVertex2f(xs+ws, ys);
1046         glTexCoord2f(u, v); glVertex2f(xs+ws, ys+hs);
1047         glTexCoord2f(0, v); glVertex2f(xs, ys+hs);
1048         glEnd();
1049 
1050 #else
1051         float coords[8];
1052         float uv[8];
1053         //normal uv
1054         uv[0] = 0; uv[1] = 0; uv[2] = u; uv[3] = 0;
1055         uv[4] = u; uv[5] = v; uv[6] = 0; uv[7] = v;
1056 
1057         // pixels
1058         coords[0] = xs; coords[1] = ys; coords[2] = xs+ws; coords[3] = ys;
1059         coords[4] = xs+ws; coords[5] = ys+hs; coords[6] = xs, coords[7] = ys+hs;
1060 
1061         glChartCanvas::RenderSingleTexture(coords, uv, &vp, 0, 0, 0);
1062 
1063 #endif
1064         glDisable(GL_BLEND);
1065         glDisable(GL_TEXTURE_2D);
1066 
1067     }
1068 
1069 
1070     if( m_bBlink ) g_blink_rect = CurrentRect_in_DC;               // also save for global blinker
1071 
1072     //    This will be useful for fast icon redraws
1073     CurrentRect_in_DC.x = r.x + hilitebox.x;
1074     CurrentRect_in_DC.y = r.y + hilitebox.y;
1075     CurrentRect_in_DC.width = hilitebox.width;
1076     CurrentRect_in_DC.height = hilitebox.height;
1077 
1078     if( m_bBlink ) g_blink_rect = CurrentRect_in_DC;               // also save for global blinker
1079 }
1080 #endif
1081 
SetPosition(double lat,double lon)1082 void RoutePoint::SetPosition( double lat, double lon )
1083 {
1084     m_lat = lat;
1085     m_lon = lon;
1086 }
1087 
CalculateDCRect(wxDC & dc,ChartCanvas * canvas,wxRect * prect)1088 void RoutePoint::CalculateDCRect( wxDC& dc, ChartCanvas *canvas, wxRect *prect )
1089 {
1090     dc.ResetBoundingBox();
1091     dc.DestroyClippingRegion();
1092 
1093     // Draw the mark on the dc
1094     ocpnDC odc( dc );
1095     Draw( odc, canvas, NULL );
1096 
1097     //  Retrieve the drawing extents
1098     prect->x = dc.MinX() - 1;
1099     prect->y = dc.MinY() - 1;
1100     prect->width = dc.MaxX() - dc.MinX() + 2; // Mouse Poop?
1101     prect->height = dc.MaxY() - dc.MinY() + 2;
1102 
1103 }
1104 
IsSame(RoutePoint * pOtherRP)1105 bool RoutePoint::IsSame( RoutePoint *pOtherRP )
1106 {
1107     bool IsSame = false;
1108 
1109     if( this->m_MarkName == pOtherRP->m_MarkName ) {
1110         if( fabs( this->m_lat - pOtherRP->m_lat ) < 1.e-6
1111                 && fabs( this->m_lon - pOtherRP->m_lon ) < 1.e-6 ) IsSame = true;
1112     }
1113     return IsSame;
1114 }
1115 
SendToGPS(const wxString & com_name,wxGauge * pProgress)1116 bool RoutePoint::SendToGPS(const wxString & com_name, wxGauge *pProgress)
1117 {
1118     int result = 0;
1119     if( g_pMUX )
1120         result = g_pMUX->SendWaypointToGPS( this, com_name, pProgress );
1121 
1122     wxString msg;
1123     if( 0 == result )
1124         msg = _("Waypoint(s) Transmitted.");
1125     else{
1126         if( result == ERR_GARMIN_INITIALIZE )
1127             msg = _("Error on Waypoint Upload.  Garmin GPS not connected");
1128         else
1129             msg = _("Error on Waypoint Upload.  Please check logfiles...");
1130     }
1131 
1132     OCPNMessageBox( NULL, msg, _("OpenCPN Info"), wxOK | wxICON_INFORMATION );
1133 
1134     return (result == 0);
1135 }
1136 
GetWaypointArrivalRadius()1137 double RoutePoint::GetWaypointArrivalRadius() {
1138     if ((m_WaypointArrivalRadius >= 0) && (m_WaypointArrivalRadius < 0.001)) {
1139         SetWaypointArrivalRadius( g_n_arrival_circle_radius );
1140         return m_WaypointArrivalRadius;
1141     }
1142     else
1143         return m_WaypointArrivalRadius;
1144 }
1145 
GetWaypointRangeRingsNumber()1146 int   RoutePoint::GetWaypointRangeRingsNumber() {
1147     if ( m_iWaypointRangeRingsNumber == -1 )
1148         return g_iWaypointRangeRingsNumber;
1149     else
1150         return m_iWaypointRangeRingsNumber;
1151 }
1152 
GetWaypointRangeRingsStep()1153 float RoutePoint::GetWaypointRangeRingsStep() {
1154     if ( m_fWaypointRangeRingsStep == -1 )
1155         return g_fWaypointRangeRingsStep;
1156     else
1157         return m_fWaypointRangeRingsStep;
1158 }
1159 
GetWaypointRangeRingsStepUnits()1160 int   RoutePoint::GetWaypointRangeRingsStepUnits() {
1161     if ( m_iWaypointRangeRingsStepUnits == -1 )
1162         return g_iWaypointRangeRingsStepUnits;
1163     else
1164         return m_iWaypointRangeRingsStepUnits ;
1165 }
1166 
GetWaypointRangeRingsColour(void)1167 wxColour RoutePoint::GetWaypointRangeRingsColour(void) {
1168     if ( m_wxcWaypointRangeRingsColour.GetAsString(wxC2S_HTML_SYNTAX) == _T("#FFFFFF") )
1169         return g_colourWaypointRangeRingsColour;
1170     else
1171         return m_wxcWaypointRangeRingsColour;
1172 }
1173 
SetScaMin(long val)1174 void RoutePoint::SetScaMin(long val) {
1175     if(val < SCAMIN_MIN) val = SCAMIN_MIN; //prevent from waypoints hiding always with a nonlogic value
1176     if(val < (long)m_ScaMax*5) val = (long)m_ScaMax*5;
1177     m_ScaMin = val;
1178 }
SetScaMin(wxString str)1179 void RoutePoint::SetScaMin(wxString str) {
1180     long val;
1181     if(!str.ToLong(&val)) val = MAX_INT_VAL;
1182     SetScaMin(val);
1183 }
1184 
SetScaMax(long val)1185 void RoutePoint::SetScaMax(long val){
1186     if( val > (int) m_ScaMin/5 ) m_ScaMax = (int) m_ScaMin/5; //prevent from waypoints hiding always with a nonlogic value
1187 }
SetScaMax(wxString str)1188 void RoutePoint::SetScaMax(wxString str) {
1189     long val;
1190     if(!str.ToLong(&val)) val = 0;
1191     SetScaMax(val);
1192 }
1193 
1194 
ShowScaleWarningMessage(ChartCanvas * canvas)1195 void RoutePoint::ShowScaleWarningMessage(ChartCanvas *canvas)
1196 {
1197     wxString strA = _("The ScaMin value for new waypoints is set to");
1198     wxString strB = _("but current chartscale is");
1199     wxString strC = _("Therefore the new waypoint will not be visible at this zoom level.");
1200     wxString MessStr = wxString::Format(_T("%s %i,\n %s %i.\n%s"),strA, (int)GetScaMin(), strB, canvas->GetScaleValue(), strC);
1201     OCPNMessageBox( canvas, MessStr);
1202 }
1203 
SetPlannedSpeed(double spd)1204 void RoutePoint::SetPlannedSpeed(double spd)
1205 {
1206     if( spd >= 0.0 && spd <= 1000.0 ) m_PlannedSpeed = spd;
1207 }
1208 
GetPlannedSpeed()1209 double RoutePoint::GetPlannedSpeed() {
1210     if( m_PlannedSpeed < 0.0001 && m_MarkDescription.Find( _T("VMG=") ) != wxNOT_FOUND ) {
1211         // In case there was speed encoded in the name of the waypoint, do the conversion here.
1212         wxString s_vmg = ( m_MarkDescription.Mid(m_MarkDescription.Find( _T("VMG=") ) + 4 ) ).BeforeFirst( ';' );
1213         double vmg;
1214         if( !s_vmg.ToDouble( &vmg ) ) {
1215             m_MarkDescription.Replace( _T("VMG=") + s_vmg + ";", wxEmptyString);
1216             SetPlannedSpeed(vmg);
1217         }
1218     }
1219     return m_PlannedSpeed;
1220 }
1221 
GetETD()1222 wxDateTime RoutePoint::GetETD()
1223 {
1224     if( m_seg_etd.IsValid() ) {
1225         if(!GetETA().IsValid() || m_seg_etd > GetETA()) {
1226             return m_seg_etd;
1227         } else {
1228             return GetETA();
1229         }
1230     } else {
1231     if( m_MarkDescription.Find( _T("ETD=") ) != wxNOT_FOUND ) {
1232             wxDateTime etd = wxInvalidDateTime;
1233             wxString s_etd = ( m_MarkDescription.Mid(m_MarkDescription.Find( _T("ETD=") ) + 4 ) ).BeforeFirst( ';' );
1234             const wxChar *parse_return = etd.ParseDateTime( s_etd );
1235             if( parse_return ) {
1236                 wxString tz( parse_return );
1237 
1238                 if( tz.Find( _T("UT") ) != wxNOT_FOUND ) {
1239                     m_seg_etd = etd;
1240                 }
1241                 else {
1242                     if( tz.Find( _T("LMT") ) != wxNOT_FOUND ) {
1243                         m_seg_etd = etd;
1244                         long lmt_offset = (long) ( ( m_lon * 3600. ) / 15. );
1245                         wxTimeSpan lmt( 0, 0, (int) lmt_offset, 0 );
1246                         m_seg_etd -= lmt;
1247                     } else {
1248                         m_seg_etd = etd.ToUTC();
1249                     }
1250                 }
1251                 if( etd.IsValid() && (!GetETA().IsValid() || etd > GetETA()) ) {
1252                     m_MarkDescription.Replace( s_etd, wxEmptyString);
1253                     m_seg_etd = etd;
1254                     return m_seg_etd;
1255                 } else {
1256                     return GetETA();
1257                 }
1258                 }
1259             }
1260     }
1261     return wxInvalidDateTime;
1262 }
1263 
GetManualETD()1264 wxDateTime RoutePoint::GetManualETD()
1265 {
1266     if( m_manual_etd && m_seg_etd.IsValid() ) {
1267         return m_seg_etd;
1268     }
1269     return wxInvalidDateTime;
1270 }
1271 
GetETA()1272 wxDateTime RoutePoint::GetETA()
1273 {
1274     if( m_seg_eta.IsValid() ) {
1275         return m_seg_eta;
1276     }
1277     return wxInvalidDateTime;
1278 }
1279 
GetETE()1280 wxString RoutePoint::GetETE()
1281 {
1282     if( m_seg_ete != 0 ) {
1283         return formatTimeDelta(m_seg_ete);
1284     }
1285     return wxEmptyString;
1286 }
1287 
SetETE(wxLongLong secs)1288 void RoutePoint::SetETE(wxLongLong secs)
1289 {
1290     m_seg_ete = secs;
1291 }
1292 
SetETD(const wxDateTime & etd)1293 void RoutePoint::SetETD(const wxDateTime &etd) {
1294     m_seg_etd = etd;
1295     m_manual_etd = TRUE;
1296 }
1297 
SetETD(const wxString & ts)1298 bool RoutePoint::SetETD(const wxString &ts)
1299 {
1300     if( ts.IsEmpty() ) {
1301         m_seg_etd = wxInvalidDateTime;
1302         m_manual_etd = false;
1303         return true;
1304     }
1305     wxDateTime tmp;
1306     wxString::const_iterator end;
1307     if ( tmp.ParseISOCombined(ts) ) {
1308         SetETD(tmp);
1309         return TRUE;
1310     } else if( tmp.ParseDateTime(ts, &end) ) {
1311         SetETD(tmp);
1312         return TRUE;
1313     }
1314     return FALSE;
1315 }
1316