1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  Route Manager
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 #include "wx/wxprec.h"
27 
28 #ifndef  WX_PRECOMP
29   #include "wx/wx.h"
30 #endif //precompiled headers
31 
32 #include "wx/image.h"
33 #include "wx/tokenzr.h"
34 #include <wx/progdlg.h>
35 
36 #include "dychart.h"
37 
38 #include <stdlib.h>
39 #include <math.h>
40 #include <time.h>
41 
42 #include <wx/listimpl.cpp>
43 
44 #include "styles.h"
45 #include "routeman.h"
46 #include "concanv.h"
47 #include "navutil.h"
48 #include "georef.h"
49 #include "RoutePropDlgImpl.h"
50 #include "TrackPropDlg.h"
51 #include "routemanagerdialog.h"
52 #include "pluginmanager.h"
53 #include "multiplexer.h"
54 #include "MarkIcon.h"
55 #include "cutil.h"
56 #include "AIS_Decoder.h"
57 #include "wx28compat.h"
58 #include "Route.h"
59 
60 #include <wx/dir.h>
61 #include <wx/filename.h>
62 #include <wx/stdpaths.h>
63 #include <wx/apptrait.h>
64 #include "OCPNPlatform.h"
65 #include "Track.h"
66 #include "chcanv.h"
67 
68 #ifdef ocpnUSE_SVG
69 #include "wxSVG/svg.h"
70 #endif // ocpnUSE_SVG
71 
72 #ifdef __OCPN__ANDROID__
73 #include "androidUTIL.h"
74 #endif
75 
76 void appendOSDirSlash(wxString* pString);
77 
78 extern MyFrame          *gFrame;
79 extern OCPNPlatform     *g_Platform;
80 extern ConsoleCanvas    *console;
81 
82 extern RouteList        *pRouteList;
83 extern TrackList        *pTrackList;
84 extern Select           *pSelect;
85 extern MyConfig         *pConfig;
86 extern Routeman         *g_pRouteMan;
87 
88 extern wxRect           g_blink_rect;
89 
90 extern double           gLat, gLon, gSog, gCog;
91 extern double           gVar;
92 extern wxString         gRmcDate, gRmcTime;
93 extern bool             g_bMagneticAPB;
94 
95 extern RoutePoint       *pAnchorWatchPoint1;
96 extern RoutePoint       *pAnchorWatchPoint2;
97 
98 extern TrackPropDlg     *pTrackPropDialog;
99 extern ActiveTrack      *g_pActiveTrack;
100 extern int              g_track_line_width;
101 
102 extern RoutePropDlgImpl *pRoutePropDialog;
103 extern RouteManagerDialog *pRouteManagerDialog;
104 extern RoutePoint      *pAnchorWatchPoint1;
105 extern RoutePoint      *pAnchorWatchPoint2;
106 extern int              g_route_line_width;
107 extern Multiplexer     *g_pMUX;
108 extern AIS_Decoder     *g_pAIS;
109 
110 extern PlugInManager    *g_pi_manager;
111 extern ocpnStyle::StyleManager* g_StyleManager;
112 extern bool             g_bAdvanceRouteWaypointOnArrivalOnly;
113 extern Route            *pAISMOBRoute;
114 extern bool             g_btouch;
115 extern float            g_ChartScaleFactorExp;
116 
117 bool g_bPluginHandleAutopilotRoute;
118 
119 //    List definitions for Waypoint Manager Icons
120 WX_DECLARE_LIST(wxBitmap, markicon_bitmap_list_type);
121 WX_DECLARE_LIST(wxString, markicon_key_list_type);
122 WX_DECLARE_LIST(wxString, markicon_description_list_type);
123 
124 //    List implementation for Waypoint Manager Icons
125 #include <wx/listimpl.cpp>
126 WX_DEFINE_LIST(markicon_bitmap_list_type);
127 WX_DEFINE_LIST(markicon_key_list_type);
128 WX_DEFINE_LIST(markicon_description_list_type);
129 
130 //Helper conditional file name dir slash
131 void appendOSDirSlash(wxString* pString);
132 
LoadSVGIcon(wxString filename,int width,int height)133 wxImage LoadSVGIcon( wxString filename, int width, int height )
134 {
135 #ifdef ocpnUSE_SVG
136 
137 #ifndef USE_ANDROID_GLES2
138     wxSVGDocument svgDoc;
139     if( svgDoc.Load(filename) )
140         return  svgDoc.Render( width, height, NULL, true, true ) ;
141     else{
142         wxLogMessage(filename);
143         return wxImage(32, 32);
144     }
145 #else
146     wxBitmap bmp = loadAndroidSVG( filename, width, height );
147     if(bmp.IsOk())
148         return bmp.ConvertToImage();
149     else
150         return wxImage(32, 32);
151 
152 #endif
153 
154 #else
155         return wxImage(32, 32);
156 #endif // ocpnUSE_SVG
157 }
158 
159 
CompareMarkIcons(MarkIcon * mi1,MarkIcon * mi2)160 static int CompareMarkIcons( MarkIcon *mi1, MarkIcon *mi2 )
161 {
162     return (mi1->icon_name.CmpNoCase( mi2->icon_name));
163 }
164 
165 //--------------------------------------------------------------------------------
166 //      Routeman   "Route Manager"
167 //--------------------------------------------------------------------------------
168 
Routeman(MyApp * parent)169 Routeman::Routeman( MyApp *parent )
170 {
171     m_pparent_app = parent;
172     pActiveRoute = NULL;
173     pActivePoint = NULL;
174     pRouteActivatePoint = NULL;
175 }
176 
~Routeman()177 Routeman::~Routeman()
178 {
179     if( pRouteActivatePoint ) delete pRouteActivatePoint;
180 }
181 
IsRouteValid(Route * pRoute)182 bool Routeman::IsRouteValid( Route *pRoute )
183 {
184     wxRouteListNode *node = pRouteList->GetFirst();
185     while( node ) {
186         if( pRoute == node->GetData() ) return true;
187         node = node->GetNext();
188     }
189     return false;
190 }
191 
192 //    Make a 2-D search to find the route containing a given waypoint
FindRouteContainingWaypoint(RoutePoint * pWP)193 Route *Routeman::FindRouteContainingWaypoint( RoutePoint *pWP )
194 {
195     wxRouteListNode *node = pRouteList->GetFirst();
196     while( node ) {
197         Route *proute = node->GetData();
198 
199         wxRoutePointListNode *pnode = ( proute->pRoutePointList )->GetFirst();
200         while( pnode ) {
201             RoutePoint *prp = pnode->GetData();
202             if( prp == pWP )  return proute;
203             pnode = pnode->GetNext();
204         }
205 
206         node = node->GetNext();
207     }
208 
209     return NULL;                              // not found
210 }
211 
GetRouteArrayContaining(RoutePoint * pWP)212 wxArrayPtrVoid *Routeman::GetRouteArrayContaining( RoutePoint *pWP )
213 {
214     wxArrayPtrVoid *pArray = new wxArrayPtrVoid;
215 
216     wxRouteListNode *route_node = pRouteList->GetFirst();
217     while( route_node ) {
218         Route *proute = route_node->GetData();
219 
220         wxRoutePointListNode *waypoint_node = ( proute->pRoutePointList )->GetFirst();
221         while( waypoint_node ) {
222             RoutePoint *prp = waypoint_node->GetData();
223             if( prp == pWP ){              // success
224                 pArray->Add( (void *) proute );
225                 break;          // only add a route to the array once, even if there are duplicate points
226                                 // in the route...See FS#1743
227             }
228 
229             waypoint_node = waypoint_node->GetNext();           // next waypoint
230         }
231 
232         route_node = route_node->GetNext();                         // next route
233     }
234 
235     if( pArray->GetCount() ) return pArray;
236 
237     else {
238         delete pArray;
239         return NULL;
240     }
241 }
242 
RemovePointFromRoute(RoutePoint * point,Route * route,ChartCanvas * cc)243 void Routeman::RemovePointFromRoute( RoutePoint* point, Route* route, ChartCanvas *cc )
244 {
245     //  Rebuild the route selectables
246     pSelect->DeleteAllSelectableRoutePoints( route );
247     pSelect->DeleteAllSelectableRouteSegments( route );
248 
249     route->RemovePoint( point );
250 
251     //  Check for 1 point routes. If we are creating a route, this is an undo, so keep the 1 point.
252     if( cc && (route->GetnPoints() <= 1) && (cc->m_routeState == 0) ) {
253          pConfig->DeleteConfigRoute( route );
254          g_pRouteMan->DeleteRoute( route );
255          route = NULL;
256      }
257     //  Add this point back into the selectables
258     pSelect->AddSelectableRoutePoint( point->m_lat, point->m_lon, point );
259 
260     if( pRoutePropDialog && ( pRoutePropDialog->IsShown() ) ) {
261         pRoutePropDialog->SetRouteAndUpdate( route, true );
262     }
263 
264     gFrame->InvalidateAllGL();
265 }
266 
FindBestActivatePoint(Route * pR,double lat,double lon,double cog,double sog)267 RoutePoint *Routeman::FindBestActivatePoint( Route *pR, double lat, double lon, double cog,
268         double sog )
269 {
270     if( !pR ) return NULL;
271 
272     // Walk thru all the points to find the "best"
273     RoutePoint *best_point = NULL;
274     double min_time_found = 1e6;
275 
276     wxRoutePointListNode *node = ( pR->pRoutePointList )->GetFirst();
277     while( node ) {
278         RoutePoint *pn = node->GetData();
279 
280         double brg, dist;
281         DistanceBearingMercator( pn->m_lat, pn->m_lon, lat, lon, &brg, &dist );
282 
283         double angle = brg - cog;
284         double soa = cos( angle * PI / 180. );
285 
286         double time_to_wp = dist / soa;
287 
288         if( time_to_wp > 0 ) {
289             if( time_to_wp < min_time_found ) {
290                 min_time_found = time_to_wp;
291                 best_point = pn;
292             }
293         }
294         node = node->GetNext();
295     }
296     return best_point;
297 }
298 
ActivateRoute(Route * pRouteToActivate,RoutePoint * pStartPoint)299 bool Routeman::ActivateRoute( Route *pRouteToActivate, RoutePoint *pStartPoint )
300 {
301     wxJSONValue v;
302     v[_T("Route_activated")] = pRouteToActivate->m_RouteNameString;
303     v[_T("GUID")] = pRouteToActivate->m_GUID;
304     wxString msg_id( _T("OCPN_RTE_ACTIVATED") );
305     g_pi_manager->SendJSONMessageToAllPlugins( msg_id, v );
306     if(g_bPluginHandleAutopilotRoute)
307         return true;
308 
309     pActiveRoute = pRouteToActivate;
310 
311     if( pStartPoint ) {
312         pActivePoint = pStartPoint;
313     } else {
314         wxRoutePointListNode *node = ( pActiveRoute->pRoutePointList )->GetFirst();
315         pActivePoint = node->GetData();               // start at beginning
316     }
317 
318     ActivateRoutePoint( pRouteToActivate, pActivePoint );
319 
320     m_bArrival = false;
321     m_arrival_min = 1e6;
322     m_arrival_test = 0;
323 
324     pRouteToActivate->m_bRtIsActive = true;
325 
326     m_bDataValid = false;
327 
328     console->ShowWithFreshFonts();
329 
330     return true;
331 }
332 
ActivateRoutePoint(Route * pA,RoutePoint * pRP_target)333 bool Routeman::ActivateRoutePoint( Route *pA, RoutePoint *pRP_target )
334 {
335     wxJSONValue v;
336     v[_T("GUID")] = pRP_target->m_GUID;
337     v[_T("WP_activated")] = pRP_target->GetName();
338 
339     wxString msg_id( _T("OCPN_WPT_ACTIVATED") );
340     g_pi_manager->SendJSONMessageToAllPlugins( msg_id, v );
341 
342     if(g_bPluginHandleAutopilotRoute)
343         return true;
344 
345     pActiveRoute = pA;
346 
347     pActivePoint = pRP_target;
348     pActiveRoute->m_pRouteActivePoint = pRP_target;
349 
350     wxRoutePointListNode *node = ( pActiveRoute->pRoutePointList )->GetFirst();
351     while( node ) {
352         RoutePoint *pn = node->GetData();
353         pn->m_bBlink = false;                     // turn off all blinking points
354         pn->m_bIsActive = false;
355 
356         node = node->GetNext();
357     }
358 
359     node = ( pActiveRoute->pRoutePointList )->GetFirst();
360     RoutePoint *prp_first = node->GetData();
361 
362     //  If activating first point in route, create a "virtual" waypoint at present position
363     if( pRP_target == prp_first ) {
364         if( pRouteActivatePoint ) delete pRouteActivatePoint;
365 
366         pRouteActivatePoint = new RoutePoint( gLat, gLon, wxString( _T("") ), wxString( _T("") ),
367                 wxEmptyString, false ); // Current location
368         pRouteActivatePoint->m_bShowName = false;
369 
370         pActiveRouteSegmentBeginPoint = pRouteActivatePoint;
371     }
372 
373     else {
374         prp_first->m_bBlink = false;
375         node = node->GetNext();
376         RoutePoint *np_prev = prp_first;
377         while( node ) {
378             RoutePoint *pnext = node->GetData();
379             if( pnext == pRP_target ) {
380                 pActiveRouteSegmentBeginPoint = np_prev;
381                 break;
382             }
383 
384             np_prev = pnext;
385             node = node->GetNext();
386         }
387     }
388 
389     pRP_target->m_bBlink = true;                               // blink the active point
390     pRP_target->m_bIsActive = true;                            // and active
391 
392     g_blink_rect = pRP_target->CurrentRect_in_DC;               // set up global blinker
393 
394     m_bArrival = false;
395     m_arrival_min = 1e6;
396     m_arrival_test = 0;
397 
398 
399     //    Update the RouteProperties Dialog, if currently shown
400     if( pRoutePropDialog && pRoutePropDialog->IsShown() ) {
401         if( pRoutePropDialog->GetRoute() == pA ) {
402             pRoutePropDialog->SetEnroutePoint(pActivePoint);
403 
404         }
405     }
406     return true;
407 }
408 
ActivateNextPoint(Route * pr,bool skipped)409 bool Routeman::ActivateNextPoint( Route *pr, bool skipped )
410 {
411     wxJSONValue v;
412     if( pActivePoint ) {
413         pActivePoint->m_bBlink = false;
414         pActivePoint->m_bIsActive = false;
415 
416         v[_T("isSkipped")] = skipped;
417         v[_T("GUID")] = pActivePoint->m_GUID;
418         v[_T("WP_arrived")] = pActivePoint->GetName();
419     }
420     int n_index_active = pActiveRoute->GetIndexOf( pActivePoint );
421     if( ( n_index_active + 1 ) <= pActiveRoute->GetnPoints() ) {
422         pActiveRouteSegmentBeginPoint = pActivePoint;
423 
424         pActiveRoute->m_pRouteActivePoint = pActiveRoute->GetPoint( n_index_active + 1 );
425 
426         pActivePoint = pActiveRoute->GetPoint( n_index_active + 1 );
427         v[_T("Next_WP")] = pActivePoint->GetName();
428         v[_T("GUID")] = pActivePoint->m_GUID;
429 
430         pActivePoint->m_bBlink = true;
431         pActivePoint->m_bIsActive = true;
432         g_blink_rect = pActivePoint->CurrentRect_in_DC;               // set up global blinker
433 
434         m_bArrival = false;
435         m_arrival_min = 1e6;
436         m_arrival_test = 0;
437 
438         //    Update the RouteProperties Dialog, if currently shown
439         if( pRoutePropDialog && pRoutePropDialog->IsShown() ) {
440             if( pRoutePropDialog->GetRoute() == pr ) {
441                 pRoutePropDialog->SetEnroutePoint(pActivePoint);
442             }
443         }
444 
445         wxString msg_id( _T("OCPN_WPT_ARRIVED") );
446         g_pi_manager->SendJSONMessageToAllPlugins( msg_id, v );
447 
448         return true;
449     }
450 
451     return false;
452 }
453 
UpdateProgress()454 bool Routeman::UpdateProgress()
455 {
456     bool bret_val = false;
457 
458     if( pActiveRoute ) {
459 //      Update bearing, range, and crosstrack error
460 
461 //  Bearing is calculated as Mercator Sailing, i.e. a  cartographic "bearing"
462         double north, east;
463         toSM( pActivePoint->m_lat, pActivePoint->m_lon, gLat, gLon, &east, &north );
464         double a = atan( north / east );
465         if( fabs( pActivePoint->m_lon - gLon ) < 180. ) {
466             if( pActivePoint->m_lon > gLon ) CurrentBrgToActivePoint = 90. - ( a * 180 / PI );
467             else
468                 CurrentBrgToActivePoint = 270. - ( a * 180 / PI );
469         } else {
470             if( pActivePoint->m_lon > gLon ) CurrentBrgToActivePoint = 270. - ( a * 180 / PI );
471             else
472                 CurrentBrgToActivePoint = 90. - ( a * 180 / PI );
473         }
474 
475 //      Calculate range using Great Circle Formula
476 
477         double d5 = DistGreatCircle( gLat, gLon, pActivePoint->m_lat, pActivePoint->m_lon );
478         CurrentRngToActivePoint = d5;
479 
480 //      Get the XTE vector, normal to current segment
481         vector2D va, vb, vn;
482 
483         double brg1, dist1, brg2, dist2;
484         DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon,
485                 pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &brg1,
486                 &dist1 );
487         vb.x = dist1 * sin( brg1 * PI / 180. );
488         vb.y = dist1 * cos( brg1 * PI / 180. );
489 
490         DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon, gLat, gLon, &brg2,
491                 &dist2 );
492         va.x = dist2 * sin( brg2 * PI / 180. );
493         va.y = dist2 * cos( brg2 * PI / 180. );
494 
495         double sdelta = vGetLengthOfNormal( &va, &vb, &vn );             // NM
496         CurrentXTEToActivePoint = sdelta;
497 
498 //    Calculate the distance to the arrival line, which is perpendicular to the current route segment
499 //    Taking advantage of the calculated normal from current position to route segment vn
500         vector2D vToArriveNormal;
501         vSubtractVectors( &va, &vn, &vToArriveNormal );
502 
503         CurrentRangeToActiveNormalCrossing = vVectorMagnitude( &vToArriveNormal );
504 
505 //          Compute current segment course
506 //          Using simple Mercater projection
507         double x1, y1, x2, y2;
508         toSM( pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon,
509                 pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon, &x1,
510                 &y1 );
511 
512         toSM( pActivePoint->m_lat, pActivePoint->m_lon, pActiveRouteSegmentBeginPoint->m_lat,
513                 pActiveRouteSegmentBeginPoint->m_lon, &x2, &y2 );
514 
515         double e1 = atan2( ( x2 - x1 ), ( y2 - y1 ) );
516         CurrentSegmentCourse = e1 * 180 / PI;
517         if( CurrentSegmentCourse < 0 ) CurrentSegmentCourse += 360;
518 
519         //      Compute XTE direction
520         double h = atan( vn.y / vn.x );
521         if( vn.x > 0 ) CourseToRouteSegment = 90. - ( h * 180 / PI );
522         else
523             CourseToRouteSegment = 270. - ( h * 180 / PI );
524 
525         h = CurrentBrgToActivePoint - CourseToRouteSegment;
526         if( h < 0 ) h = h + 360;
527 
528         if( h > 180 ) XTEDir = 1;
529         else
530             XTEDir = -1;
531 
532 //      Determine Arrival
533 
534         bool bDidArrival = false;
535 
536         // Special signal:  if ArrivalRadius < 0, NEVER arrive...
537         //  Used for MOB auto-created routes.
538         if( pActivePoint->GetWaypointArrivalRadius() > 0){
539             if( CurrentRangeToActiveNormalCrossing <= pActivePoint->GetWaypointArrivalRadius() ) {
540                 m_bArrival = true;
541                 UpdateAutopilot();
542 
543                 bDidArrival = true;
544                 DoAdvance();
545 
546             }
547             else {
548             //      Test to see if we are moving away from the arrival point, and
549             //      have been moving away for 2 seconds.
550             //      If so, we should declare "Arrival"
551                 if( (CurrentRangeToActiveNormalCrossing - m_arrival_min) >  pActivePoint->GetWaypointArrivalRadius() ){
552                     if(++m_arrival_test > 2 && !g_bAdvanceRouteWaypointOnArrivalOnly) {
553                         m_bArrival = true;
554                         UpdateAutopilot();
555 
556                         bDidArrival = true;
557                         DoAdvance();
558                     }
559                 }
560                 else
561                     m_arrival_test = 0;
562 
563             }
564         }
565 
566         if( !bDidArrival )
567             m_arrival_min = wxMin( m_arrival_min, CurrentRangeToActiveNormalCrossing );
568 
569         if( !bDidArrival )                                        // Only once on arrival
570             UpdateAutopilot();
571 
572         bret_val = true;                                        // a route is active
573     }
574 
575     m_bDataValid = true;
576 
577     return bret_val;
578 }
579 
DoAdvance(void)580 void Routeman::DoAdvance(void)
581 {
582     if( !ActivateNextPoint( pActiveRoute, false ) )            // at the end?
583     {
584         Route *pthis_route = pActiveRoute;
585         DeactivateRoute( true );                  // this is an arrival
586 
587         if( pthis_route->m_bDeleteOnArrival && !pthis_route->m_bIsBeingEdited) {
588             pConfig->DeleteConfigRoute( pthis_route );
589             DeleteRoute( pthis_route );
590         }
591 
592         if( pRouteManagerDialog )
593             pRouteManagerDialog->UpdateRouteListCtrl();
594 
595     }
596 }
597 
598 
599 
DeactivateRoute(bool b_arrival)600 bool Routeman::DeactivateRoute( bool b_arrival )
601 {
602     if( pActivePoint ) {
603         pActivePoint->m_bBlink = false;
604         pActivePoint->m_bIsActive = false;
605     }
606 
607     if( pActiveRoute ) {
608         pActiveRoute->m_bRtIsActive = false;
609         pActiveRoute->m_pRouteActivePoint = NULL;
610 
611         wxJSONValue v;
612         if( !b_arrival ) {
613             v[_T("Route_deactivated")] = pActiveRoute->m_RouteNameString;
614             v[_T("GUID")] = pActiveRoute->m_GUID;
615             wxString msg_id( _T("OCPN_RTE_DEACTIVATED") );
616             g_pi_manager->SendJSONMessageToAllPlugins( msg_id, v );
617         } else {
618             v[_T("GUID")] = pActiveRoute->m_GUID;
619             v[_T("Route_ended")] = pActiveRoute->m_RouteNameString;
620             wxString msg_id( _T("OCPN_RTE_ENDED") );
621             g_pi_manager->SendJSONMessageToAllPlugins( msg_id, v );
622         }
623     }
624 
625     pActiveRoute = NULL;
626 
627     if( pRouteActivatePoint ) delete pRouteActivatePoint;
628     pRouteActivatePoint = NULL;
629 
630     console->pCDI->ClearBackground();
631 
632     console->Show( false );
633 
634     m_bDataValid = false;
635 
636     return true;
637 }
638 
UpdateAutopilot()639 bool Routeman::UpdateAutopilot()
640 {
641    //Send all known Autopilot messages upstream
642 
643    //Avoid a possible not initiated SOG/COG. APs can be confused if in NAV mode wo valid GPS
644    double r_Sog(0.0), r_Cog(0.0);
645    if (!std::isnan(gSog)) r_Sog = gSog;
646    if (!std::isnan(gCog)) r_Cog = gCog;
647 
648    // Send active leg info directly to plugins
649 
650    ActiveLegDat leg_info;
651    leg_info.Btw = CurrentBrgToActivePoint;
652    leg_info.Dtw = CurrentRngToActivePoint;
653    leg_info.Xte = CurrentXTEToActivePoint;
654    if (XTEDir < 0) {
655      leg_info.Xte = -leg_info.Xte;    // Left side of the track -> negative XTE
656    }
657    leg_info.wp_name = pActivePoint->GetName().Truncate(6);
658    leg_info.arrival = m_bArrival;
659    g_pi_manager->SendActiveLegInfoToAllPlugIns(&leg_info);
660 
661     //RMB
662         {
663 
664             m_NMEA0183.TalkerID = _T("EC");
665 
666             SENTENCE snt;
667             m_NMEA0183.Rmb.IsDataValid = NTrue;
668             m_NMEA0183.Rmb.CrossTrackError = CurrentXTEToActivePoint;
669 
670             if( XTEDir < 0 ) m_NMEA0183.Rmb.DirectionToSteer = Left;
671             else
672                 m_NMEA0183.Rmb.DirectionToSteer = Right;
673 
674             m_NMEA0183.Rmb.To = pActivePoint->GetName().Truncate( 6 );
675             m_NMEA0183.Rmb.From = pActiveRouteSegmentBeginPoint->GetName().Truncate( 6 );
676 
677             if( pActivePoint->m_lat < 0. ) m_NMEA0183.Rmb.DestinationPosition.Latitude.Set(
678                     -pActivePoint->m_lat, _T("S") );
679             else
680                 m_NMEA0183.Rmb.DestinationPosition.Latitude.Set( pActivePoint->m_lat, _T("N") );
681 
682             if( pActivePoint->m_lon < 0. ) m_NMEA0183.Rmb.DestinationPosition.Longitude.Set(
683                     -pActivePoint->m_lon, _T("W") );
684             else
685                 m_NMEA0183.Rmb.DestinationPosition.Longitude.Set( pActivePoint->m_lon, _T("E") );
686 
687             m_NMEA0183.Rmb.RangeToDestinationNauticalMiles = CurrentRngToActivePoint;
688             m_NMEA0183.Rmb.BearingToDestinationDegreesTrue = CurrentBrgToActivePoint;
689 			m_NMEA0183.Rmb.DestinationClosingVelocityKnots = r_Sog * cos( (r_Cog - CurrentBrgToActivePoint) * PI / 180.0 );
690 
691             if( m_bArrival ) m_NMEA0183.Rmb.IsArrivalCircleEntered = NTrue;
692             else
693                 m_NMEA0183.Rmb.IsArrivalCircleEntered = NFalse;
694 
695             m_NMEA0183.Rmb.FAAModeIndicator = "A";
696             m_NMEA0183.Rmb.Write( snt );
697 
698             g_pMUX->SendNMEAMessage( snt.Sentence );
699         }
700 
701         // RMC
702         {
703 
704             m_NMEA0183.TalkerID = _T("EC");
705 
706             SENTENCE snt;
707             m_NMEA0183.Rmc.IsDataValid = NTrue;
708 
709             if( gLat < 0. ) m_NMEA0183.Rmc.Position.Latitude.Set( -gLat, _T("S") );
710             else
711                 m_NMEA0183.Rmc.Position.Latitude.Set( gLat, _T("N") );
712 
713             if( gLon < 0. ) m_NMEA0183.Rmc.Position.Longitude.Set( -gLon, _T("W") );
714             else
715                 m_NMEA0183.Rmc.Position.Longitude.Set( gLon, _T("E") );
716 
717             m_NMEA0183.Rmc.SpeedOverGroundKnots = r_Sog;
718             m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue = r_Cog;
719 
720             if( !std::isnan(gVar) ) {
721                 if( gVar < 0. ) {
722                     m_NMEA0183.Rmc.MagneticVariation = -gVar;
723                     m_NMEA0183.Rmc.MagneticVariationDirection = West;
724                 } else {
725                     m_NMEA0183.Rmc.MagneticVariation = gVar;
726                     m_NMEA0183.Rmc.MagneticVariationDirection = East;
727                 }
728             } else
729                 m_NMEA0183.Rmc.MagneticVariation = 361.; // A signal to NMEA converter, gVAR is unknown
730 
731             // Send GPS time to autopilot if available else send local system time
732             if ( !gRmcTime.IsEmpty() && !gRmcDate.IsEmpty() ) {
733                 m_NMEA0183.Rmc.UTCTime = gRmcTime;
734                 m_NMEA0183.Rmc.Date = gRmcDate;
735             }
736             else {
737                 wxDateTime now = wxDateTime::Now();
738                 wxDateTime utc = now.ToUTC();
739                 wxString time = utc.Format( _T("%H%M%S") );
740                 m_NMEA0183.Rmc.UTCTime = time;
741                 wxString date = utc.Format( _T("%d%m%y") );
742                 m_NMEA0183.Rmc.Date = date;
743             }
744 
745             m_NMEA0183.Rmc.FAAModeIndicator = "A";
746             m_NMEA0183.Rmc.Write( snt );
747 
748             g_pMUX->SendNMEAMessage( snt.Sentence );
749         }
750 
751         // APB
752         {
753             m_NMEA0183.TalkerID = _T("EC");
754 
755             SENTENCE snt;
756 
757             m_NMEA0183.Apb.IsLoranBlinkOK = NTrue;
758             m_NMEA0183.Apb.IsLoranCCycleLockOK = NTrue;
759 
760             m_NMEA0183.Apb.CrossTrackErrorMagnitude = CurrentXTEToActivePoint;
761 
762             if( XTEDir < 0 ) m_NMEA0183.Apb.DirectionToSteer = Left;
763             else
764                 m_NMEA0183.Apb.DirectionToSteer = Right;
765 
766             m_NMEA0183.Apb.CrossTrackUnits = _T("N");
767 
768             if( m_bArrival )
769                 m_NMEA0183.Apb.IsArrivalCircleEntered = NTrue;
770             else
771                 m_NMEA0183.Apb.IsArrivalCircleEntered = NFalse;
772 
773             //  We never pass the perpendicular, since we declare arrival before reaching this point
774             m_NMEA0183.Apb.IsPerpendicular = NFalse;
775 
776             m_NMEA0183.Apb.To = pActivePoint->GetName().Truncate( 6 );
777 
778             double brg1, dist1;
779             DistanceBearingMercator( pActivePoint->m_lat, pActivePoint->m_lon,
780                                      pActiveRouteSegmentBeginPoint->m_lat, pActiveRouteSegmentBeginPoint->m_lon,
781                                      &brg1,
782                                      &dist1 );
783 
784             if( g_bMagneticAPB && !std::isnan(gVar) ) {
785 
786                 double brg1m = ((brg1 - gVar) >= 0.) ? (brg1 - gVar) : (brg1 - gVar + 360.);
787                 double bapm = ((CurrentBrgToActivePoint - gVar) >= 0.) ? (CurrentBrgToActivePoint - gVar) : (CurrentBrgToActivePoint - gVar + 360.);
788 
789                 m_NMEA0183.Apb.BearingOriginToDestination = brg1m;
790                 m_NMEA0183.Apb.BearingOriginToDestinationUnits = _T("M");
791 
792                 m_NMEA0183.Apb.BearingPresentPositionToDestination = bapm;
793                 m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = _T("M");
794 
795                 m_NMEA0183.Apb.HeadingToSteer = bapm;
796                 m_NMEA0183.Apb.HeadingToSteerUnits = _T("M");
797             }
798             else {
799                 m_NMEA0183.Apb.BearingOriginToDestination = brg1;
800                 m_NMEA0183.Apb.BearingOriginToDestinationUnits = _T("T");
801 
802                 m_NMEA0183.Apb.BearingPresentPositionToDestination = CurrentBrgToActivePoint;
803                 m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = _T("T");
804 
805 
806                 m_NMEA0183.Apb.HeadingToSteer = CurrentBrgToActivePoint;
807                 m_NMEA0183.Apb.HeadingToSteerUnits = _T("T");
808             }
809 
810             m_NMEA0183.Apb.Write( snt );
811             g_pMUX->SendNMEAMessage( snt.Sentence );
812         }
813 
814         // XTE
815         {
816             m_NMEA0183.TalkerID = _T("EC");
817 
818             SENTENCE snt;
819 
820             m_NMEA0183.Xte.IsLoranBlinkOK = NTrue;
821             m_NMEA0183.Xte.IsLoranCCycleLockOK = NTrue;
822 
823             m_NMEA0183.Xte.CrossTrackErrorDistance = CurrentXTEToActivePoint;
824 
825             if( XTEDir < 0 ) m_NMEA0183.Xte.DirectionToSteer = Left;
826             else
827                 m_NMEA0183.Xte.DirectionToSteer = Right;
828 
829             m_NMEA0183.Xte.CrossTrackUnits = _T("N");
830 
831             m_NMEA0183.Xte.Write( snt );
832             g_pMUX->SendNMEAMessage( snt.Sentence );
833         }
834 
835 
836     return true;
837 }
838 
DoesRouteContainSharedPoints(Route * pRoute)839 bool Routeman::DoesRouteContainSharedPoints( Route *pRoute )
840 {
841     if( pRoute ) {
842         // walk the route, looking at each point to see if it is used by another route
843         // or is isolated
844         wxRoutePointListNode *pnode = ( pRoute->pRoutePointList )->GetFirst();
845         while( pnode ) {
846             RoutePoint *prp = pnode->GetData();
847 
848             // check all other routes to see if this point appears in any other route
849             wxArrayPtrVoid *pRA = GetRouteArrayContaining( prp );
850 
851              if( pRA ) {
852                  for( unsigned int ir = 0; ir < pRA->GetCount(); ir++ ) {
853                     Route *pr = (Route *) pRA->Item( ir );
854                     if( pr == pRoute)
855                         continue;               // self
856                     else
857                         return true;
858                 }
859             }
860 
861             if( pnode ) pnode = pnode->GetNext();
862         }
863 
864         //      Now walk the route again, looking for isolated type shared waypoints
865         pnode = ( pRoute->pRoutePointList )->GetFirst();
866         while( pnode ) {
867             RoutePoint *prp = pnode->GetData();
868             if( prp->m_bKeepXRoute == true )
869                 return true;
870 
871            if( pnode ) pnode = pnode->GetNext();
872         }
873     }
874 
875     return false;
876 }
877 
878 
879 
DeleteRoute(Route * pRoute)880 bool Routeman::DeleteRoute( Route *pRoute )
881 {
882     if( pRoute ) {
883         if( pRoute == pAISMOBRoute )
884         {
885             int ret = OCPNMessageBox( NULL, _("You are trying to delete an active AIS MOB route, are you REALLY sure?"), _("OpenCPN Warning"), wxYES_NO );
886 
887             if( ret == wxID_NO )
888                 return false;
889             else
890                 pAISMOBRoute = NULL;
891         }
892         ::wxBeginBusyCursor();
893 
894         if( GetpActiveRoute() == pRoute ) DeactivateRoute();
895 
896         if (pRoute->m_bIsInLayer) {
897             ::wxEndBusyCursor();
898             return false;
899         }
900         if( pRoutePropDialog && ( pRoutePropDialog->IsShown()) && (pRoute == pRoutePropDialog->GetRoute()) ) {
901             pRoutePropDialog->Hide();
902         }
903 
904         pConfig->DeleteConfigRoute( pRoute );
905 
906         //    Remove the route from associated lists
907         pSelect->DeleteAllSelectableRouteSegments( pRoute );
908         pRouteList->DeleteObject( pRoute );
909 
910         // walk the route, tentatively deleting/marking points used only by this route
911         wxRoutePointListNode *pnode = ( pRoute->pRoutePointList )->GetFirst();
912         while( pnode ) {
913             RoutePoint *prp = pnode->GetData();
914 
915             // check all other routes to see if this point appears in any other route
916             Route *pcontainer_route = FindRouteContainingWaypoint( prp );
917 
918             if( pcontainer_route == NULL && prp->m_bIsInRoute ) {
919                 prp->m_bIsInRoute = false;          // Take this point out of this (and only) route
920                 if( !prp->m_bKeepXRoute ) {
921 //    This does not need to be done with navobj.xml storage, since the waypoints are stored with the route
922 //                              pConfig->DeleteWayPoint(prp);
923 
924                     pSelect->DeleteSelectablePoint( prp, SELTYPE_ROUTEPOINT );
925 
926                     // Remove all instances of this point from the list.
927                     wxRoutePointListNode *pdnode = pnode;
928                     while( pdnode ) {
929                         pRoute->pRoutePointList->DeleteNode( pdnode );
930                         pdnode = pRoute->pRoutePointList->Find( prp );
931                     }
932 
933                     pnode = NULL;
934                     delete prp;
935                 } else {
936                     prp->m_bDynamicName = false;
937                     prp->m_bIsolatedMark = true;        // This has become an isolated mark
938                     prp->m_bKeepXRoute = false;         // and is no longer part of a route
939                 }
940 
941             }
942             if( pnode ) pnode = pnode->GetNext();
943             else
944                 pnode = pRoute->pRoutePointList->GetFirst();                // restart the list
945         }
946 
947         delete pRoute;
948 
949         ::wxEndBusyCursor();
950 
951     }
952     return true;
953 }
954 
DeleteAllRoutes(void)955 void Routeman::DeleteAllRoutes( void )
956 {
957     ::wxBeginBusyCursor();
958 
959     //    Iterate on the RouteList
960     wxRouteListNode *node = pRouteList->GetFirst();
961     while( node ) {
962         Route *proute = node->GetData();
963         if( proute == pAISMOBRoute )
964         {
965             ::wxEndBusyCursor();
966             int ret = OCPNMessageBox( NULL, _("You are trying to delete an active AIS MOB route, are you REALLY sure?"), _("OpenCPN Warning"), wxYES_NO );
967             if( ret == wxID_NO )
968                 return;
969             else
970                 pAISMOBRoute = NULL;
971             ::wxBeginBusyCursor();
972         }
973 
974         node = node->GetNext();
975         if( proute->m_bIsInLayer )
976             continue;
977 
978         pConfig->m_bSkipChangeSetUpdate = true;
979         pConfig->DeleteConfigRoute( proute );
980         DeleteRoute( proute );
981         pConfig->m_bSkipChangeSetUpdate = false;
982     }
983 
984     ::wxEndBusyCursor();
985 
986 }
987 
DeleteAllTracks(void)988 void Routeman::DeleteAllTracks( void )
989 {
990     ::wxBeginBusyCursor();
991 
992     //    Iterate on the RouteList
993     wxTrackListNode *node = pTrackList->GetFirst();
994     while( node ) {
995         Track *ptrack = node->GetData();
996             node = node->GetNext();
997 
998         if( ptrack->m_bIsInLayer )
999             continue;
1000 
1001         g_pAIS->DeletePersistentTrack( ptrack );
1002         pConfig->m_bSkipChangeSetUpdate = true;
1003         pConfig->DeleteConfigTrack( ptrack );
1004         DeleteTrack( ptrack );
1005         pConfig->m_bSkipChangeSetUpdate = false;
1006     }
1007 
1008     ::wxEndBusyCursor();
1009 
1010 }
1011 
DeleteTrack(Track * pTrack)1012 void Routeman::DeleteTrack( Track *pTrack )
1013 {
1014     if( pTrack ) {
1015         if( pTrack->m_bIsInLayer ) return;
1016 
1017         ::wxBeginBusyCursor();
1018 
1019         wxGenericProgressDialog *pprog = nullptr;
1020 
1021         int count = pTrack->GetnPoints();
1022         if( count > 10000) {
1023             pprog = new wxGenericProgressDialog( _("OpenCPN Track Delete"), _T("0/0"), count, NULL,
1024                                           wxPD_APP_MODAL | wxPD_SMOOTH |
1025                                           wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME );
1026             pprog->SetSize( 400, wxDefaultCoord );
1027             pprog->Centre();
1028 
1029         }
1030         if( pTrackPropDialog && ( pTrackPropDialog->IsShown()) && (pTrack == pTrackPropDialog->GetTrack()) ) {
1031                 pTrackPropDialog->Hide();
1032         }
1033 
1034         //    Remove the track from associated lists
1035         pSelect->DeleteAllSelectableTrackSegments( pTrack );
1036         pTrackList->DeleteObject( pTrack );
1037 
1038 #if 0
1039         // walk the track, deleting points used by this track
1040         int ic = 0;
1041         wxTrackPointListNode *pnode = ( pTrack->pTrackPointList )->GetFirst();
1042         while( pnode )
1043         {
1044             if(pprog)
1045             {
1046                 wxString msg;
1047                 msg.Printf(_T("%d/%d"), ic, count);
1048                 if(ic % 100 == 0)
1049                    pprog->Update( ic, msg );
1050                 ic++;
1051             }
1052 
1053             TrackPoint *prp = pnode->GetData();
1054             delete prp;
1055 
1056             pnode = pnode->GetNext();
1057         }
1058 #endif
1059         if( pTrack == g_pActiveTrack ) {
1060             g_pActiveTrack = NULL;
1061             m_pparent_app->TrackOff();
1062         }
1063 
1064         delete pTrack;
1065 
1066         ::wxEndBusyCursor();
1067 
1068         delete pprog;
1069     }
1070 }
1071 
SetColorScheme(ColorScheme cs)1072 void Routeman::SetColorScheme( ColorScheme cs )
1073 {
1074     // Re-Create the pens and colors
1075 
1076     int scaled_line_width = g_route_line_width;
1077     int track_scaled_line_width = g_track_line_width;
1078     if(g_btouch){
1079         double nominal_line_width_pix = wxMax(1.5, floor(g_Platform->GetDisplayDPmm() / 5.0));             //0.2 mm nominal, but not less than 1 pixel
1080 
1081         double sline_width = wxMax(nominal_line_width_pix, g_route_line_width);
1082         sline_width *= g_ChartScaleFactorExp;
1083         scaled_line_width = wxMax( sline_width, 2);
1084 
1085         double tsline_width = wxMax(nominal_line_width_pix, g_track_line_width);
1086         tsline_width *= g_ChartScaleFactorExp;
1087         track_scaled_line_width = wxMax( tsline_width, 2);
1088     }
1089 
1090     m_pActiveRoutePointPen = wxThePenList->FindOrCreatePen( wxColour( 0, 0, 255 ),
1091                                                             scaled_line_width, wxPENSTYLE_SOLID );
1092     m_pRoutePointPen = wxThePenList->FindOrCreatePen( wxColour( 0, 0, 255 ), scaled_line_width,
1093             wxPENSTYLE_SOLID );
1094 
1095 //    Or in something like S-52 compliance
1096 
1097     m_pRoutePen = wxThePenList->FindOrCreatePen( GetGlobalColor( _T("UINFB") ), scaled_line_width,
1098             wxPENSTYLE_SOLID );
1099     m_pSelectedRoutePen = wxThePenList->FindOrCreatePen( GetGlobalColor( _T("UINFO") ),
1100                                                          scaled_line_width, wxPENSTYLE_SOLID );
1101     m_pActiveRoutePen = wxThePenList->FindOrCreatePen( GetGlobalColor( _T("UARTE") ),
1102                                                        scaled_line_width, wxPENSTYLE_SOLID );
1103     m_pTrackPen = wxThePenList->FindOrCreatePen( GetGlobalColor( _T("CHMGD") ), track_scaled_line_width,
1104                                                  wxPENSTYLE_SOLID );
1105 
1106     m_pRouteBrush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor( _T("UINFB") ), wxBRUSHSTYLE_SOLID );
1107     m_pSelectedRouteBrush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor( _T("UINFO") ),
1108             wxBRUSHSTYLE_SOLID );
1109     m_pActiveRouteBrush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor( _T("PLRTE") ),
1110             wxBRUSHSTYLE_SOLID );
1111 
1112 }
1113 
GetRouteReverseMessage(void)1114 wxString Routeman::GetRouteReverseMessage( void )
1115 {
1116     return wxString(
1117             _("Waypoints can be renamed to reflect the new order, the names will be '001', '002' etc.\n\nDo you want to rename the waypoints?") );
1118 }
1119 
FindRouteByGUID(const wxString & guid)1120 Route *Routeman::FindRouteByGUID(const wxString &guid)
1121 {
1122     wxRouteListNode *node1 = pRouteList->GetFirst();
1123     while( node1 ) {
1124         Route *pRoute = node1->GetData();
1125 
1126         if( pRoute->m_GUID == guid )
1127             return pRoute;
1128         node1 = node1->GetNext();
1129     }
1130 
1131     return NULL;
1132 }
1133 
FindTrackByGUID(const wxString & guid)1134 Track *Routeman::FindTrackByGUID(const wxString &guid)
1135 {
1136     wxTrackListNode *node1 = pTrackList->GetFirst();
1137     while( node1 ) {
1138         Track *pTrack = node1->GetData();
1139 
1140         if( pTrack->m_GUID == guid )
1141             return pTrack;
1142         node1 = node1->GetNext();
1143     }
1144 
1145     return NULL;
1146 }
1147 
ZeroCurrentXTEToActivePoint()1148 void Routeman::ZeroCurrentXTEToActivePoint()
1149 {
1150     // When zeroing XTE create a "virtual" waypoint at present position
1151     if( pRouteActivatePoint ) delete pRouteActivatePoint;
1152     pRouteActivatePoint = new RoutePoint( gLat, gLon, wxString( _T("") ), wxString( _T("") ),
1153     wxEmptyString, false ); // Current location
1154     pRouteActivatePoint->m_bShowName = false;
1155 
1156     pActiveRouteSegmentBeginPoint = pRouteActivatePoint;
1157     m_arrival_min = 1e6;
1158 }
1159 
1160 //--------------------------------------------------------------------------------
1161 //      WayPointman   Implementation
1162 //--------------------------------------------------------------------------------
1163 
WayPointman()1164 WayPointman::WayPointman()
1165 {
1166 
1167     m_pWayPointList = new RoutePointList;
1168 
1169     pmarkicon_image_list = NULL;
1170 
1171     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1172     m_pIconArray = new ArrayOfMarkIcon;
1173     m_pLegacyIconArray = NULL;
1174     m_pExtendedIconArray = NULL;
1175 
1176     m_cs = (ColorScheme)-1;
1177 
1178     m_nGUID = 0;
1179     m_iconListScale = -999.0;
1180     m_iconListHeight = -1;
1181 }
1182 
~WayPointman()1183 WayPointman::~WayPointman()
1184 {
1185     //    Two step here, since the RoutePoint dtor also touches the
1186     //    RoutePoint list.
1187     //    Copy the master RoutePoint list to a temporary list,
1188     //    then clear and delete objects from the temp list
1189 
1190     RoutePointList temp_list;
1191 
1192     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1193     while( node ) {
1194         RoutePoint *pr = node->GetData();
1195 
1196         temp_list.Append( pr );
1197         node = node->GetNext();
1198     }
1199 
1200     temp_list.DeleteContents( true );
1201     temp_list.Clear();
1202 
1203     m_pWayPointList->Clear();
1204     delete m_pWayPointList;
1205 
1206     for( unsigned int i = 0; i < m_pIconArray->GetCount(); i++ ) {
1207         MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( i );
1208         delete pmi->piconBitmap;
1209         delete pmi;
1210     }
1211 
1212     m_pIconArray->Clear();
1213     delete m_pIconArray;
1214 
1215     if( pmarkicon_image_list ) pmarkicon_image_list->RemoveAll();
1216     delete pmarkicon_image_list;
1217 }
1218 
AddRoutePoint(RoutePoint * prp)1219 bool WayPointman::AddRoutePoint(RoutePoint *prp)
1220 {
1221     if(!prp)
1222         return false;
1223 
1224     wxRoutePointListNode *prpnode = m_pWayPointList->Append(prp);
1225     prp->SetManagerListNode( prpnode );
1226 
1227     return true;
1228 }
1229 
RemoveRoutePoint(RoutePoint * prp)1230 bool WayPointman::RemoveRoutePoint(RoutePoint *prp)
1231 {
1232     if(!prp)
1233         return false;
1234 
1235     wxRoutePointListNode *prpnode = (wxRoutePointListNode *)prp->GetManagerListNode();
1236 
1237     if(prpnode)
1238         delete prpnode;
1239     else
1240         m_pWayPointList->DeleteObject(prp);
1241 
1242     prp->SetManagerListNode( NULL );
1243 
1244     return true;
1245 }
1246 
ProcessUserIcons(ocpnStyle::Style * style)1247 void WayPointman::ProcessUserIcons( ocpnStyle::Style* style )
1248 {
1249     wxString msg;
1250     msg.Printf(_T("DPMM: %g   ScaleFactorExp: %g"), g_Platform->GetDisplayDPmm(), g_ChartScaleFactorExp);
1251     wxLogMessage(msg);
1252 
1253     wxString UserIconPath = g_Platform->GetPrivateDataDir();
1254     wxChar sep = wxFileName::GetPathSeparator();
1255     if( UserIconPath.Last() != sep ) UserIconPath.Append( sep );
1256     UserIconPath.Append( _T("UserIcons/") );
1257 
1258     wxLogMessage(_T("Looking for UserIcons at ") + UserIconPath );
1259 
1260     if( wxDir::Exists( UserIconPath ) ) {
1261         wxLogMessage(_T("Loading UserIcons from ") + UserIconPath );
1262         wxArrayString FileList;
1263 
1264         int n_files = wxDir::GetAllFiles( UserIconPath, &FileList, _T(""), wxDIR_FILES  );
1265 
1266         for( int ifile = 0; ifile < n_files; ifile++ ) {
1267             wxString name = FileList[ifile];
1268 
1269             wxFileName fn( name );
1270             wxString iconname = fn.GetName();
1271             wxBitmap icon1;
1272 
1273             if( fn.GetExt().Lower() == _T("xpm") ) {
1274                 if( icon1.LoadFile( name, wxBITMAP_TYPE_XPM ) ) {
1275                     wxLogMessage(_T("Adding icon: ") + iconname);
1276                     ProcessIcon( icon1, iconname, iconname );
1277                 }
1278             }
1279             if( fn.GetExt().Lower() == _T("png") ) {
1280                 if( icon1.LoadFile( name, wxBITMAP_TYPE_PNG ) ) {
1281                     wxLogMessage(_T("Adding icon: ") + iconname);
1282                     ProcessIcon( icon1, iconname, iconname );
1283                 }
1284             }
1285             if( fn.GetExt().Lower() == _T("svg") ) {
1286                 //double bm_size = 16.0 * g_Platform->GetDisplayDPmm() * g_ChartScaleFactorExp;
1287                 double bm_size = 62 * g_ChartScaleFactorExp;
1288                 wxBitmap iconSVG = LoadSVGIcon( name, bm_size, bm_size );
1289                 MarkIcon * pmi = ProcessIcon( iconSVG, iconname, iconname );
1290                 if(pmi)
1291                     pmi->preScaled = true;
1292             }
1293 
1294         }
1295     }
1296 }
1297 
1298 
ProcessIcons(ocpnStyle::Style * style)1299 void WayPointman::ProcessIcons( ocpnStyle::Style* style )
1300 {
1301     m_pIconArray->Clear();
1302 
1303     ProcessDefaultIcons();
1304 
1305     // Load user defined icons.
1306     // Done after default icons are initialized,
1307     // so that user may substitute an icon by using the same name in the Usericons file.
1308     ProcessUserIcons( style );
1309 
1310     if( NULL != pmarkicon_image_list ) {
1311         pmarkicon_image_list->RemoveAll();
1312         delete pmarkicon_image_list;
1313         pmarkicon_image_list = NULL;
1314     }
1315 
1316     // First find the largest bitmap size, to use as the base size for lists of icons
1317     int w = 0;
1318     int h = 0;
1319 
1320     for( unsigned int i = 0; i < m_pIconArray->GetCount(); i++ ) {
1321         MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( i );
1322         w = wxMax(w, pmi->iconImage.GetWidth());
1323         h = wxMax(h, pmi->iconImage.GetHeight());
1324     }
1325 
1326     m_bitmapSizeForList = wxMax(w,h);
1327     m_bitmapSizeForList = wxMin(100, m_bitmapSizeForList);
1328 
1329 
1330 }
1331 
ProcessDefaultIcons()1332 void WayPointman::ProcessDefaultIcons()
1333 {
1334     wxString iconDir = g_Platform->GetSharedDataDir();
1335     appendOSDirSlash(&iconDir);
1336     iconDir.append(_T("uidata"));
1337     appendOSDirSlash(&iconDir);
1338     iconDir.append(_T("markicons"));
1339     appendOSDirSlash(&iconDir);
1340 
1341     MarkIcon *pmi = 0;
1342 
1343     // Add the legacy icons to their own sorted array
1344     if(m_pLegacyIconArray)
1345         m_pLegacyIconArray->Clear();
1346     else
1347         m_pLegacyIconArray = new SortedArrayOfMarkIcon(CompareMarkIcons);
1348 
1349     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-Empty.svg"), _T("empty"), _T("Empty") ); if(pmi)pmi->preScaled = true;
1350     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-Triangle.svg"), _T("triangle"), _T("Triangle") ); if(pmi)pmi->preScaled = true;
1351     pmi = ProcessLegacyIcon( iconDir + _T("1st-Active-Waypoint.svg"), _T("activepoint"), _T("Active WP") ); if(pmi)pmi->preScaled = true;
1352     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Boarding-Location.svg"), _T("boarding"), _T("Boarding Location") ); if(pmi)pmi->preScaled = true;
1353     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Airplane.svg"), _T("airplane"), _T("Airplane") ); if(pmi)pmi->preScaled = true;
1354     pmi = ProcessLegacyIcon( iconDir + _T("1st-Anchorage.svg"), _T("anchorage"), _T("Anchorage") ); if(pmi)pmi->preScaled = true;
1355     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-Anchor2.svg"), _T("anchor"), _T("Anchor") ); if(pmi)pmi->preScaled = true;
1356     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Boundary.svg"), _T("boundary"), _T("Boundary Mark") ); if(pmi)pmi->preScaled = true;
1357     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Buoy-TypeA.svg"), _T("bouy1"), _T("Bouy Type A") ); if(pmi)pmi->preScaled = true;
1358     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Buoy-TypeB.svg"), _T("bouy2"), _T("Bouy Type B") ); if(pmi)pmi->preScaled = true;
1359     pmi = ProcessLegacyIcon( iconDir + _T("Activity-Campfire.svg"), _T("campfire"), _T("Campfire") ); if(pmi)pmi->preScaled = true;
1360     pmi = ProcessLegacyIcon( iconDir + _T("Activity-Camping.svg"), _T("camping"), _T("Camping Spot") ); if(pmi)pmi->preScaled = true;
1361     pmi = ProcessLegacyIcon( iconDir + _T("Sea-Floor-Coral.svg"), _T("coral"), _T("Coral") ); if(pmi)pmi->preScaled = true;
1362     pmi = ProcessLegacyIcon( iconDir + _T("Activity-Fishing.svg"), _T("fishhaven"), _T("Fish Haven") ); if(pmi)pmi->preScaled = true;
1363     pmi = ProcessLegacyIcon( iconDir + _T("Activity-Fishing.svg"), _T("fishing"), _T("Fishing Spot") ); if(pmi)pmi->preScaled = true;
1364     pmi = ProcessLegacyIcon( iconDir + _T("Activity-Fishing.svg"), _T("fish"), _T("Fish") ); if(pmi)pmi->preScaled = true;
1365     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Mooring-Buoy.svg"), _T("float"), _T("Float") ); if(pmi)pmi->preScaled = true;
1366     pmi = ProcessLegacyIcon( iconDir + _T("Service-Food.svg"), _T("food"), _T("Food") ); if(pmi)pmi->preScaled = true;
1367     pmi = ProcessLegacyIcon( iconDir + _T("Service-Fuel-Pump-Diesel-Petrol.svg"), _T("fuel"), _T("Fuel") ); if(pmi)pmi->preScaled = true;
1368     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Light-Green.svg"), _T("greenlite"), _T("Green Light") ); if(pmi)pmi->preScaled = true;
1369     pmi = ProcessLegacyIcon( iconDir + _T("Sea-Floor-Sea-Weed.svg"), _T("kelp"), _T("Kelp") ); if(pmi)pmi->preScaled = true;
1370     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Light-TypeA.svg"), _T("light"), _T("Light Type A") ); if(pmi)pmi->preScaled = true;
1371     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Light-TypeB.svg"), _T("light1"), _T("Light Type B") ); if(pmi)pmi->preScaled = true;
1372     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Light-Vessel.svg"), _T("litevessel"), _T("litevessel") ); if(pmi)pmi->preScaled = true;
1373     pmi = ProcessLegacyIcon( iconDir + _T("1st-Man-Overboard.svg"), _T("mob"), _T("MOB") ); if(pmi)pmi->preScaled = true;
1374     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Mooring-Buoy.svg"), _T("mooring"), _T("Mooring Bouy") ); if(pmi)pmi->preScaled = true;
1375     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Mooring-Buoy-Super.svg"), _T("oilbouy"), _T("Oil Bouy") ); if(pmi)pmi->preScaled = true;
1376     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Oil-Platform.svg"), _T("platform"), _T("Platform") ); if(pmi)pmi->preScaled = true;
1377     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Light-Red-Green.svg"), _T("redgreenlite"), _T("Red/Green Light") ); if(pmi)pmi->preScaled = true;
1378     pmi = ProcessLegacyIcon( iconDir + _T("Marks-Light-Red.svg"), _T("redlite"), _T("Red Light") ); if(pmi)pmi->preScaled = true;
1379     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Rock-Exposed.svg"), _T("rock1"), _T("Rock (exposed)") ); if(pmi)pmi->preScaled = true;
1380     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Rock-Awash.svg"), _T("rock2"), _T("Rock, (awash)") ); if(pmi)pmi->preScaled = true;
1381     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Sandbar.svg"), _T("sand"), _T("Sand") ); if(pmi)pmi->preScaled = true;
1382     pmi = ProcessLegacyIcon( iconDir + _T("Activity-Diving-Scuba-Flag.svg"), _T("scuba"), _T("Scuba") ); if(pmi)pmi->preScaled = true;
1383     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Sandbar.svg"), _T("shoal"), _T("Shoal") ); if(pmi)pmi->preScaled = true;
1384     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Snag.svg"), _T("snag"), _T("Snag") ); if(pmi)pmi->preScaled = true;
1385     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-Square.svg"), _T("square"), _T("Square") ); if(pmi)pmi->preScaled = true;
1386     pmi = ProcessLegacyIcon( iconDir + _T("1st-Diamond.svg"), _T("diamond"), _T("Diamond") ); if(pmi)pmi->preScaled = true;
1387     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-Circle.svg"), _T("circle"), _T("Circle") ); if(pmi)pmi->preScaled = true;
1388     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Wreck1.svg"), _T("wreck1"), _T("Wreck A") ); if(pmi)pmi->preScaled = true;
1389     pmi = ProcessLegacyIcon( iconDir + _T("Hazard-Wreck2.svg"), _T("wreck2"), _T("Wreck B") ); if(pmi)pmi->preScaled = true;
1390     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-X-Small-Blue.svg"), _T("xmblue"), _T("Blue X") ); if(pmi)pmi->preScaled = true;
1391     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-X-Small-Green.svg"), _T("xmgreen"), _T("Green X") ); if(pmi)pmi->preScaled = true;
1392     pmi = ProcessLegacyIcon( iconDir + _T("Symbol-X-Small-Red.svg"), _T("xmred"), _T("Red X") ); if(pmi)pmi->preScaled = true;
1393 
1394 
1395     // Add the extended icons to their own sorted array
1396     if(m_pExtendedIconArray)
1397         m_pExtendedIconArray->Clear();
1398     else
1399         m_pExtendedIconArray = new SortedArrayOfMarkIcon(CompareMarkIcons);
1400 
1401 #if 0
1402     wxArrayString FileList;
1403     double bm_size = -1;
1404 
1405     int n_files = wxDir::GetAllFiles( iconDir, &FileList );
1406 
1407     // If the scale factor is not unity, measure the first icon in the list
1408     //  So that we may apply the scale factor exactly to all
1409     if( fabs(g_ChartScaleFactorExp - 1.0) > 0.1){
1410 
1411         for( int ifile = 0; ifile < n_files; ifile++ ) {
1412             wxString name = FileList[ifile];
1413 
1414             wxFileName fn( name );
1415 
1416             if( fn.GetExt().Lower() == _T("svg") ) {
1417                 wxBitmap bmt = LoadSVGIcon(name, -1, -1 );
1418                 bm_size = bmt.GetWidth() * g_ChartScaleFactorExp;
1419                 break;
1420             }
1421         }
1422     }
1423 
1424     for( int ifile = 0; ifile < n_files; ifile++ ) {
1425         wxString name = FileList[ifile];
1426 
1427         wxFileName fn( name );
1428         wxString iconname = fn.GetName();
1429         wxBitmap icon1;
1430         if( fn.GetExt().Lower() == _T("svg") ) {
1431             wxImage iconSVG = LoadSVGIcon( name, (int)bm_size, (int)bm_size );
1432             MarkIcon * pmi = ProcessExtendedIcon( iconSVG, iconname, iconname );
1433             if(pmi)
1434                 pmi->preScaled = true;
1435         }
1436     }
1437 #else
1438    // Look for cached icons
1439 
1440     wxString iconCacheDir = g_Platform->GetPrivateDataDir();
1441     appendOSDirSlash(&iconCacheDir);
1442     iconCacheDir.append(_T("iconCache"));
1443     appendOSDirSlash(&iconCacheDir);
1444 
1445     //  Create the cache dir here if necessary
1446     if(!wxDir::Exists(iconCacheDir))
1447         wxFileName::Mkdir(iconCacheDir);
1448 
1449 
1450     wxArrayString FileList;
1451     double bm_size = wxMax(4.0, floor(g_Platform->GetDisplayDPmm() * 12.0));             // nominal size, but not less than 4 pixel
1452     bm_size *= g_ChartScaleFactorExp;
1453 
1454     bool bcacheLoaded = false;
1455 
1456     int n_files = wxDir::GetAllFiles( iconDir, &FileList );
1457 
1458     // To expedite icon loading, look in the iconCache first
1459     wxArrayString cacheFileList;
1460     int n_cache_files = wxDir::GetAllFiles( iconCacheDir, &cacheFileList );
1461     if(n_cache_files){
1462 
1463         bool bReload = false;
1464         //  Load a cached icon file and get it's size for comparison
1465         for( int ifile = 0; ifile < n_cache_files; ifile++ ) {
1466             wxString name = cacheFileList[ifile];
1467 
1468             wxImage imagePNG;
1469             if(imagePNG.LoadFile(name)){
1470                 int w = imagePNG.GetWidth();
1471                 if(fabs(w - bm_size) > 1)
1472                     bReload = true;
1473                 break;
1474             }
1475         }
1476 
1477         // If cached files are the proper size(scale)...
1478         if(!bReload){
1479             for( int ifile = 0; ifile < n_cache_files; ifile++ ) {
1480                 wxString name = cacheFileList[ifile];
1481 
1482                 wxFileName fn( name );
1483                 wxString iconname = fn.GetName();
1484 
1485                 wxImage imagePNG;
1486                 if(imagePNG.LoadFile(name)){
1487                     MarkIcon * pmi = ProcessExtendedIcon( imagePNG, iconname, iconname );
1488                     if(pmi)
1489                         pmi->preScaled = true;
1490                     bcacheLoaded = true;  //At least one icon was loaded
1491                 }
1492             }
1493         }
1494     }
1495 
1496     // Possibly an upgrade of App, with more icons available
1497     //  So, reload them all
1498     if(n_cache_files < 50)
1499         bcacheLoaded = false;
1500 
1501     //  Cache was unusable, so load from original
1502     if(!bcacheLoaded)
1503     {
1504         g_Platform->ShowBusySpinner();
1505 
1506         for( int ifile = 0; ifile < n_files; ifile++ ) {
1507             wxString name = FileList[ifile];
1508 
1509             wxFileName fn( name );
1510             wxString iconname = fn.GetName();
1511             wxBitmap icon1;
1512 
1513             if( fn.GetExt().Lower() == _T("svg") ) {
1514                 wxImage iconSVG = LoadSVGIcon( name, (int)bm_size, (int)bm_size );
1515 
1516                 // Cache the icon as .png file
1517                 wxString filePNG = iconCacheDir + iconname + _T(".png");
1518                 iconSVG.SaveFile(filePNG, wxBITMAP_TYPE_PNG);
1519                 MarkIcon * pmi = ProcessExtendedIcon( iconSVG, iconname, iconname );
1520                 if(pmi)
1521                     pmi->preScaled = true;
1522             }
1523         }
1524         g_Platform->HideBusySpinner();
1525 
1526     }
1527 #endif
1528 
1529 
1530     // Walk the two sorted lists, adding icons to the un-sorted master list
1531 
1532     for( unsigned int i = 0; i < m_pLegacyIconArray->GetCount(); i++ ) {
1533         pmi = (MarkIcon *) m_pLegacyIconArray->Item( i );
1534         m_pIconArray->Add( pmi );
1535     }
1536 
1537     for( unsigned int i = 0; i < m_pExtendedIconArray->GetCount(); i++ ) {
1538         pmi = (MarkIcon *) m_pExtendedIconArray->Item( i );
1539 
1540         //  Do not add any icons from the extended array if they have already been used as legacy substitutes
1541         bool noAdd = false;
1542         for( unsigned int j = 0; j < m_pLegacyIconArray->GetCount(); j++ ) {
1543             MarkIcon *pmiLegacy = (MarkIcon *) m_pLegacyIconArray->Item( j );
1544             if(pmiLegacy->icon_name.IsSameAs(pmi->icon_name)){
1545                 noAdd = true;
1546                 break;
1547             }
1548         }
1549         if(!noAdd)
1550             m_pIconArray->Add( pmi );
1551 
1552     }
1553 }
1554 
1555 
1556 
ProcessIcon(wxBitmap pimage,const wxString & key,const wxString & description)1557 MarkIcon *WayPointman::ProcessIcon(wxBitmap pimage, const wxString & key, const wxString & description)
1558 {
1559     MarkIcon *pmi = 0;
1560 
1561     bool newIcon = true;
1562 
1563     // avoid adding duplicates
1564     for( unsigned int i = 0; i < m_pIconArray->GetCount(); i++ ) {
1565         pmi = (MarkIcon *) m_pIconArray->Item( i );
1566         if( pmi->icon_name.IsSameAs( key ) ) {
1567             newIcon = false;
1568             delete pmi->piconBitmap;
1569             break;
1570         }
1571     }
1572 
1573     if( newIcon ) {
1574         pmi = new MarkIcon;
1575         pmi->icon_name = key;                   // Used for sorting
1576         m_pIconArray->Add( pmi );
1577     }
1578 
1579     wxBitmap *pbm = new wxBitmap( pimage );
1580     pmi->icon_name = key;
1581     pmi->icon_description = description;
1582     pmi->piconBitmap = NULL;
1583     pmi->icon_texture = 0; /* invalidate */
1584     pmi->preScaled = false;
1585     pmi->iconImage = pbm->ConvertToImage();
1586     pmi->m_blistImageOK = false;
1587     delete pbm;
1588 
1589     return pmi;
1590 }
1591 
ProcessExtendedIcon(wxImage & image,const wxString & key,const wxString & description)1592 MarkIcon *WayPointman::ProcessExtendedIcon(wxImage &image, const wxString & key, const wxString & description)
1593 {
1594     MarkIcon *pmi = 0;
1595 
1596     bool newIcon = true;
1597 
1598     // avoid adding duplicates
1599     for( unsigned int i = 0; i < m_pExtendedIconArray->GetCount(); i++ ) {
1600         pmi = (MarkIcon *) m_pExtendedIconArray->Item( i );
1601         if( pmi->icon_name.IsSameAs( key ) ) {
1602             newIcon = false;
1603             delete pmi->piconBitmap;
1604             break;
1605         }
1606     }
1607 
1608     if( newIcon ) {
1609         pmi = new MarkIcon;
1610         pmi->icon_name = key;                   // Used for sorting
1611         m_pExtendedIconArray->Add( pmi );
1612     }
1613 
1614     wxRect rClip = CropImageOnAlpha(image);
1615     wxImage imageClip = image.GetSubImage(rClip);
1616 
1617     pmi->icon_name = key;
1618     pmi->icon_description = description;
1619     pmi->piconBitmap = NULL;
1620     pmi->icon_texture = 0; /* invalidate */
1621     pmi->preScaled = false;
1622     pmi->iconImage = imageClip;
1623     pmi->m_blistImageOK = false;
1624 
1625     return pmi;
1626 }
1627 
ProcessLegacyIcon(wxString fileName,const wxString & key,const wxString & description)1628 MarkIcon *WayPointman::ProcessLegacyIcon( wxString fileName, const wxString & key, const wxString & description)
1629 {
1630     double bm_size = -1.0;
1631 
1632 #ifndef __OCPN__ANDROID__
1633     if( fabs(g_ChartScaleFactorExp - 1.0) > 0.1){
1634         wxImage img = LoadSVGIcon(fileName, -1, -1 );
1635         bm_size = img.GetWidth() * g_ChartScaleFactorExp;
1636     }
1637 #else
1638     //  Set the onscreen size of the symbol
1639     //  Compensate for various display resolutions
1640     //  Develop empirically, making a "diamond" symbol about 4 mm square
1641     //  Android uses "density buckets", so simpple math produces poor results.
1642     //  Thus, these factors have been empirically tweaked to provide good results on a variety of devices
1643     float nominal_legacy_icon_size_pixels = wxMax(4.0, floor(g_Platform->GetDisplayDPmm() * 12.0));
1644     float pix_factor = nominal_legacy_icon_size_pixels / 68.0;          // legacy icon size
1645 
1646     wxImage img = LoadSVGIcon(fileName, -1, -1 );
1647     bm_size = img.GetWidth() * pix_factor * g_ChartScaleFactorExp;
1648 #endif
1649 
1650     wxImage image = LoadSVGIcon(fileName, (int)bm_size, (int)bm_size );
1651     wxRect rClip = CropImageOnAlpha(image);
1652     wxImage imageClip = image.GetSubImage(rClip);
1653 
1654     MarkIcon *pmi = 0;
1655 
1656     bool newIcon = true;
1657 
1658     // avoid adding duplicates
1659     for( unsigned int i = 0; i < m_pLegacyIconArray->GetCount(); i++ ) {
1660         pmi = (MarkIcon *) m_pLegacyIconArray->Item( i );
1661         if( pmi->icon_name.IsSameAs( key ) ) {
1662             newIcon = false;
1663             delete pmi->piconBitmap;
1664             break;
1665         }
1666     }
1667 
1668     if( newIcon ) {
1669         pmi = new MarkIcon;
1670         pmi->icon_name = key;                   // Used for sorting
1671         m_pLegacyIconArray->Add( pmi );
1672     }
1673 
1674     pmi->icon_name = key;
1675     pmi->icon_description = description;
1676     pmi->piconBitmap = NULL;
1677     pmi->icon_texture = 0; /* invalidate */
1678     pmi->preScaled = false;
1679     pmi->iconImage = imageClip;
1680     pmi->m_blistImageOK = false;
1681 
1682     return pmi;
1683 }
1684 
CropImageOnAlpha(wxImage & image)1685 wxRect WayPointman::CropImageOnAlpha(wxImage &image)
1686 {
1687     const int w = image.GetWidth();
1688     const int h = image.GetHeight();
1689 
1690     wxRect rv = wxRect(0,0, w, h);
1691     if(!image.HasAlpha())
1692         return rv;
1693 
1694     unsigned char *pAlpha = image.GetAlpha();
1695 
1696     int leftCrop = w;
1697     int topCrop = h;
1698     int rightCrop = w;
1699     int bottomCrop = h;
1700 
1701     // Horizontal
1702     for(int i=0 ; i < h ; i++){
1703         int lineStartIndex = i *w;
1704 
1705         int j = 0;
1706         while((j < w) && (pAlpha[lineStartIndex+j] == 0) )
1707             j++;
1708         leftCrop = wxMin(leftCrop, j);
1709 
1710         int k = w - 1;
1711         while( k && (pAlpha[lineStartIndex+k] == 0) )
1712             k--;
1713         rightCrop = wxMin(rightCrop, image.GetWidth() - k - 2);
1714     }
1715 
1716     // Vertical
1717     for(int i=0 ; i < w ; i++){
1718         int columnStartIndex = i;
1719 
1720         int j = 0;
1721         while((j < h) && (pAlpha[columnStartIndex+ (j * w)] == 0) )
1722             j++;
1723         topCrop = wxMin(topCrop, j);
1724 
1725         int k = h - 1;
1726         while( k && (pAlpha[columnStartIndex+(k * w)] == 0) )
1727             k--;
1728         bottomCrop = wxMin(bottomCrop, h - k - 2);
1729     }
1730 
1731     int xcrop = wxMin(rightCrop, leftCrop);
1732     int ycrop = wxMin(topCrop, bottomCrop);
1733     int crop = wxMin(xcrop, ycrop);
1734 
1735     rv.x = wxMax(crop, 0);
1736     rv.width = wxMax(1, w - (2 * crop));
1737     rv.width = wxMin(rv.width, w);
1738     rv.y = rv.x;
1739     rv.height = rv.width;
1740 
1741     return rv;
1742 
1743 }
1744 
Getpmarkicon_image_list(int nominal_height)1745 wxImageList *WayPointman::Getpmarkicon_image_list( int nominal_height )
1746 {
1747     // Cached version available?
1748     if( pmarkicon_image_list && (nominal_height == m_iconListHeight)){
1749         return pmarkicon_image_list;
1750     }
1751 
1752     // Build an image list large enough
1753     if( NULL != pmarkicon_image_list ) {
1754         pmarkicon_image_list->RemoveAll();
1755         delete pmarkicon_image_list;
1756     }
1757     pmarkicon_image_list = new wxImageList( nominal_height, nominal_height );
1758 
1759     m_iconListHeight = nominal_height;
1760     m_bitmapSizeForList = nominal_height;
1761 
1762     return pmarkicon_image_list;
1763 }
1764 
CreateDimBitmap(wxBitmap * pBitmap,double factor)1765 wxBitmap *WayPointman::CreateDimBitmap( wxBitmap *pBitmap, double factor )
1766 {
1767     wxImage img = pBitmap->ConvertToImage();
1768     int sx = img.GetWidth();
1769     int sy = img.GetHeight();
1770 
1771     wxImage new_img( img );
1772 
1773     for( int i = 0; i < sx; i++ ) {
1774         for( int j = 0; j < sy; j++ ) {
1775             if( !img.IsTransparent( i, j ) ) {
1776                 new_img.SetRGB( i, j, (unsigned char) ( img.GetRed( i, j ) * factor ),
1777                         (unsigned char) ( img.GetGreen( i, j ) * factor ),
1778                         (unsigned char) ( img.GetBlue( i, j ) * factor ) );
1779             }
1780         }
1781     }
1782 
1783     wxBitmap *pret = new wxBitmap( new_img );
1784 
1785     return pret;
1786 
1787 }
1788 
CreateDimImage(wxImage & image,double factor)1789 wxImage WayPointman::CreateDimImage( wxImage &image, double factor )
1790 {
1791     int sx = image.GetWidth();
1792     int sy = image.GetHeight();
1793 
1794     wxImage new_img( image );
1795 
1796     for( int i = 0; i < sx; i++ ) {
1797         for( int j = 0; j < sy; j++ ) {
1798             if( !image.IsTransparent( i, j ) ) {
1799                 new_img.SetRGB( i, j, (unsigned char) ( image.GetRed( i, j ) * factor ),
1800                                 (unsigned char) ( image.GetGreen( i, j ) * factor ),
1801                                 (unsigned char) ( image.GetBlue( i, j ) * factor ) );
1802             }
1803         }
1804     }
1805 
1806 
1807     return wxImage(new_img);
1808 
1809 }
1810 
SetColorScheme(ColorScheme cs)1811 void WayPointman::SetColorScheme( ColorScheme cs )
1812 {
1813     m_cs = cs;
1814     ReloadAllIcons();
1815 }
1816 
ReloadAllIcons()1817 void WayPointman::ReloadAllIcons(  )
1818 {
1819     ProcessIcons( g_StyleManager->GetCurrentStyle() );
1820 
1821     for( unsigned int i = 0; i < m_pIconArray->GetCount(); i++ ) {
1822         MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( i );
1823         wxImage dim_image;
1824         if(m_cs == GLOBAL_COLOR_SCHEME_DUSK){
1825             dim_image = CreateDimImage(pmi->iconImage, .50);
1826             pmi->iconImage = dim_image;
1827         }
1828         else if(m_cs == GLOBAL_COLOR_SCHEME_NIGHT){
1829             dim_image = CreateDimImage(pmi->iconImage, .20);
1830             pmi->iconImage = dim_image;
1831         }
1832     }
1833 
1834     ReloadRoutepointIcons();
1835 }
1836 
ReloadRoutepointIcons()1837 void WayPointman::ReloadRoutepointIcons()
1838 {
1839     //    Iterate on the RoutePoint list, requiring each to reload icon
1840 
1841     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1842     while( node ) {
1843         RoutePoint *pr = node->GetData();
1844         pr->ReLoadIcon();
1845         node = node->GetNext();
1846     }
1847 }
1848 
DoesIconExist(const wxString & icon_key) const1849 bool WayPointman::DoesIconExist(const wxString & icon_key) const
1850 {
1851     MarkIcon *pmi;
1852     unsigned int i;
1853 
1854     for( i = 0; i < m_pIconArray->GetCount(); i++ ) {
1855         pmi = (MarkIcon *) m_pIconArray->Item( i );
1856         if( pmi->icon_name.IsSameAs( icon_key ) ) return true;
1857     }
1858 
1859     return false;
1860 }
1861 
GetIconBitmap(const wxString & icon_key)1862 wxBitmap *WayPointman::GetIconBitmap( const wxString& icon_key )
1863 {
1864     wxBitmap *pret = NULL;
1865     MarkIcon *pmi = NULL;
1866     unsigned int i;
1867 
1868     for( i = 0; i < m_pIconArray->GetCount(); i++ ) {
1869         pmi = (MarkIcon *) m_pIconArray->Item( i );
1870         if( pmi->icon_name.IsSameAs( icon_key ) )
1871             break;
1872     }
1873 
1874     if( i == m_pIconArray->GetCount() )              // key not found
1875     {
1876         // find and return bitmap for "circle"
1877         for( i = 0; i < m_pIconArray->GetCount(); i++ ) {
1878             pmi = (MarkIcon *) m_pIconArray->Item( i );
1879 //            if( pmi->icon_name.IsSameAs( _T("circle") ) )
1880 //                break;
1881         }
1882     }
1883 
1884     if( i == m_pIconArray->GetCount() )              // "circle" not found
1885         pmi = (MarkIcon *) m_pIconArray->Item( 0 );       // use item 0
1886 
1887     if( pmi ){
1888         if(pmi->piconBitmap)
1889             pret = pmi->piconBitmap;
1890         else{
1891             if(pmi->iconImage.IsOk()){
1892                 pmi->piconBitmap = new wxBitmap(pmi->iconImage);
1893                 pret = pmi->piconBitmap;
1894             }
1895         }
1896     }
1897     return pret;
1898 }
1899 
GetIconPrescaled(const wxString & icon_key)1900 bool WayPointman::GetIconPrescaled( const wxString& icon_key )
1901 {
1902     MarkIcon *pmi = NULL;
1903     unsigned int i;
1904 
1905     for( i = 0; i < m_pIconArray->GetCount(); i++ ) {
1906         pmi = (MarkIcon *) m_pIconArray->Item( i );
1907         if( pmi->icon_name.IsSameAs( icon_key ) )
1908             break;
1909     }
1910 
1911     if( i == m_pIconArray->GetCount() )              // key not found
1912     {
1913         // find and return bitmap for "circle"
1914         for( i = 0; i < m_pIconArray->GetCount(); i++ ) {
1915             pmi = (MarkIcon *) m_pIconArray->Item( i );
1916             //            if( pmi->icon_name.IsSameAs( _T("circle") ) )
1917             //                break;
1918         }
1919     }
1920 
1921     if( i == m_pIconArray->GetCount() )              // "circle" not found
1922         pmi = (MarkIcon *) m_pIconArray->Item( 0 );       // use item 0
1923 
1924     if( pmi )
1925         return pmi->preScaled;
1926     else
1927         return false;
1928 }
1929 
GetIconTexture(const wxBitmap * pbm,int & glw,int & glh)1930 unsigned int WayPointman::GetIconTexture( const wxBitmap *pbm, int &glw, int &glh )
1931 {
1932 #ifdef ocpnUSE_GL
1933     int index = GetIconIndex( pbm );
1934     MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( index );
1935 
1936     if(!pmi->icon_texture) {
1937         /* make rgba texture */
1938         wxImage image = pbm->ConvertToImage();
1939         unsigned char *d = image.GetData();
1940         if (d == 0) {
1941             // don't create a texture with junk
1942             return 0;
1943         }
1944 
1945         glGenTextures(1, &pmi->icon_texture);
1946         glBindTexture(GL_TEXTURE_2D, pmi->icon_texture);
1947 
1948         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1949         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
1950         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
1951 
1952 
1953         int w = image.GetWidth(), h = image.GetHeight();
1954 
1955         pmi->tex_w = NextPow2(w);
1956         pmi->tex_h = NextPow2(h);
1957 
1958         unsigned char *a = image.GetAlpha();
1959 
1960         unsigned char mr, mg, mb;
1961         if (!a)
1962             image.GetOrFindMaskColour( &mr, &mg, &mb );
1963 
1964         unsigned char *e = new unsigned char[4 * w * h];
1965         for( int y = 0; y < h; y++ ) {
1966                 for( int x = 0; x < w; x++ ) {
1967                     unsigned char r, g, b;
1968                     int off = ( y * w + x );
1969                     r = d[off * 3 + 0];
1970                     g = d[off * 3 + 1];
1971                     b = d[off * 3 + 2];
1972                     e[off * 4 + 0] = r;
1973                     e[off * 4 + 1] = g;
1974                     e[off * 4 + 2] = b;
1975 
1976                     e[off * 4 + 3] =  a ? a[off] : ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
1977                 }
1978         }
1979 
1980         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pmi->tex_w, pmi->tex_h,
1981                      0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1982         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
1983                         GL_RGBA, GL_UNSIGNED_BYTE, e);
1984 
1985         delete [] e;
1986     }
1987 
1988     glw = pmi->tex_w;
1989     glh = pmi->tex_h;
1990 
1991     return pmi->icon_texture;
1992 #else
1993     return 0;
1994 #endif
1995 }
1996 
1997 
GetIconBitmapForList(int index,int height)1998 wxBitmap WayPointman::GetIconBitmapForList( int index, int height )
1999 {
2000     wxBitmap pret;
2001     MarkIcon *pmi;
2002 
2003     if( index >= 0 ) {
2004         pmi = (MarkIcon *) m_pIconArray->Item( index );
2005         // Scale the icon to "list size" if necessary
2006         if(pmi->iconImage.GetHeight() != height){
2007             int w = height;
2008             int h = height;
2009             int w0 = pmi->iconImage.GetWidth();
2010             int h0 = pmi->iconImage.GetHeight();
2011 
2012             wxImage icon_resized = pmi->iconImage;       // make a copy
2013             if( h0 <= h && w0 <= w ) {
2014                 icon_resized = pmi->iconImage.Resize( wxSize( w, h ), wxPoint( w/2 -w0/2, h/2-h0/2 ) );
2015             } else {
2016                 // rescale in one or two directions to avoid cropping, then resize to fit to cell
2017                 int h1 = h;
2018                 int w1 = w;
2019                 if( h0 > h ) w1 = wxRound( (double) w0 * ( (double) h / (double) h0 ) );
2020 
2021                 else if( w0 > w ) h1 = wxRound( (double) h0 * ( (double) w / (double) w0 ) );
2022 
2023                 icon_resized = pmi->iconImage.Rescale( w1, h1 );
2024                 icon_resized = pmi->iconImage.Resize( wxSize( w, h ), wxPoint( w/2 -w1/2, h/2-h1/2 ) );
2025             }
2026 
2027             pret = wxBitmap(icon_resized);
2028 
2029         }
2030         else
2031             pret = wxBitmap(pmi->iconImage);
2032 
2033 
2034     }
2035 
2036 
2037     return pret;
2038 }
2039 
2040 
2041 
2042 
2043 
GetIconDescription(int index)2044 wxString *WayPointman::GetIconDescription( int index )
2045 {
2046     wxString *pret = NULL;
2047 
2048     if( index >= 0 ) {
2049         MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( index );
2050         pret = &pmi->icon_description;
2051     }
2052     return pret;
2053 }
2054 
GetIconKey(int index)2055 wxString *WayPointman::GetIconKey( int index )
2056 {
2057     wxString *pret = NULL;
2058 
2059     if( (index >= 0)  && ((unsigned int)index < m_pIconArray->GetCount()) ) {
2060         MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( index );
2061         pret = &pmi->icon_name;
2062     }
2063     return pret;
2064 }
2065 
GetIconIndex(const wxBitmap * pbm)2066 int WayPointman::GetIconIndex( const wxBitmap *pbm )
2067 {
2068     unsigned int ret = 0;
2069     MarkIcon *pmi;
2070 
2071     wxASSERT(m_pIconArray->GetCount() >= 1);
2072     for( unsigned int i = 0; i < m_pIconArray->GetCount(); i++ ) {
2073         pmi = (MarkIcon *) m_pIconArray->Item( i );
2074         if( pmi->piconBitmap == pbm ){
2075             ret = i;
2076             break;
2077         }
2078     }
2079 
2080     return ret;
2081 }
2082 
2083 
GetIconImageListIndex(const wxBitmap * pbm)2084 int WayPointman::GetIconImageListIndex( const wxBitmap *pbm )
2085 {
2086     MarkIcon *pmi = (MarkIcon *) m_pIconArray->Item( GetIconIndex (pbm) );
2087 
2088     // Build a "list - sized" image
2089     if(pmarkicon_image_list && !pmi->m_blistImageOK){
2090         int h0 = pmi->iconImage.GetHeight();
2091         int w0 = pmi->iconImage.GetWidth();
2092         int h = m_bitmapSizeForList;
2093         int w = m_bitmapSizeForList;
2094 
2095         wxImage icon_larger = pmi->iconImage;           // make a copy
2096         if( h0 <= h && w0 <= w ) {
2097             icon_larger =  pmi->iconImage.Resize( wxSize( w, h ), wxPoint( w/2 -w0/2, h/2-h0/2 ) );
2098         } else {
2099             // rescale in one or two directions to avoid cropping, then resize to fit to cell
2100             int h1 = h;
2101             int w1 = w;
2102             if( h0 > h ) w1 = wxRound( (double) w0 * ( (double) h / (double) h0 ) );
2103 
2104             else if( w0 > w ) h1 = wxRound( (double) h0 * ( (double) w / (double) w0 ) );
2105 
2106             icon_larger =  pmi->iconImage.Rescale( w1, h1 );
2107             icon_larger = icon_larger.Resize( wxSize( w, h ), wxPoint( w/2 -w1/2, h/2-h1/2  ) );
2108         }
2109 
2110         int index = pmarkicon_image_list->Add( wxBitmap(icon_larger));
2111 
2112         // Create and replace "x-ed out" icon,
2113         // Being careful to preserve (some) transparency
2114 
2115         icon_larger.ConvertAlphaToMask( 128 );
2116 
2117         unsigned char r,g,b;
2118         icon_larger.GetOrFindMaskColour(&r, &g, &b);
2119         wxColour unused_color(r,g,b);
2120 
2121         wxBitmap bmp0( icon_larger );
2122 
2123         wxBitmap bmp(w, h, -1 );
2124         wxMemoryDC mdc( bmp );
2125         mdc.SetBackground( wxBrush( unused_color) );
2126         mdc.Clear();
2127         mdc.DrawBitmap( bmp0, 0, 0 );
2128         int xm = bmp.GetWidth() / 2;
2129         int ym = bmp.GetHeight() / 2;
2130         int dp = xm / 2;
2131         int width = wxMax(xm / 10, 2);
2132         wxPen red(GetGlobalColor(_T( "URED" )), width );
2133         mdc.SetPen( red );
2134         mdc.DrawLine( xm-dp, ym-dp, xm+dp, ym+dp );
2135         mdc.DrawLine( xm-dp, ym+dp, xm+dp, ym-dp );
2136         mdc.SelectObject( wxNullBitmap );
2137 
2138         wxMask *pmask = new wxMask(bmp, unused_color);
2139         bmp.SetMask( pmask );
2140 
2141         pmarkicon_image_list->Add( bmp );
2142 
2143         pmi->m_blistImageOK = true;
2144         pmi->listIndex = index;
2145 
2146     }
2147 
2148     return pmi->listIndex;
2149 
2150 }
2151 
2152 
GetXIconImageListIndex(const wxBitmap * pbm)2153 int WayPointman::GetXIconImageListIndex( const wxBitmap *pbm )
2154 {
2155     return GetIconImageListIndex( pbm ) +1; // index of "X-ed out" icon in the image list
2156 }
2157 
2158 //  Create the unique identifier
CreateGUID(RoutePoint * pRP)2159 wxString WayPointman::CreateGUID( RoutePoint *pRP )
2160 {
2161     //FIXME: this method is not needed at all (if GetUUID works...)
2162     /*wxDateTime now = wxDateTime::Now();
2163      time_t ticks = now.GetTicks();
2164      wxString GUID;
2165      GUID.Printf(_T("%d-%d-%d-%d"), ((int)fabs(pRP->m_lat * 1e4)), ((int)fabs(pRP->m_lon * 1e4)), (int)ticks, m_nGUID);
2166 
2167      m_nGUID++;
2168 
2169      return GUID;*/
2170     return GpxDocument::GetUUID();
2171 }
2172 
FindRoutePointByGUID(const wxString & guid)2173 RoutePoint *WayPointman::FindRoutePointByGUID(const wxString &guid)
2174 {
2175     wxRoutePointListNode *prpnode = m_pWayPointList->GetFirst();
2176     while( prpnode ) {
2177         RoutePoint *prp = prpnode->GetData();
2178 
2179         if( prp->m_GUID == guid ) return ( prp );
2180 
2181         prpnode = prpnode->GetNext(); //RoutePoint
2182     }
2183 
2184     return NULL;
2185 }
2186 
GetNearbyWaypoint(double lat,double lon,double radius_meters)2187 RoutePoint *WayPointman::GetNearbyWaypoint( double lat, double lon, double radius_meters )
2188 {
2189     //    Iterate on the RoutePoint list, checking distance
2190 
2191     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
2192     while( node ) {
2193         RoutePoint *pr = node->GetData();
2194 
2195         double a = lat - pr->m_lat;
2196         double b = lon - pr->m_lon;
2197         double l = sqrt( ( a * a ) + ( b * b ) );
2198 
2199         if( ( l * 60. * 1852. ) < radius_meters ) return pr;
2200 
2201         node = node->GetNext();
2202     }
2203     return NULL;
2204 
2205 }
2206 
GetOtherNearbyWaypoint(double lat,double lon,double radius_meters,const wxString & guid)2207 RoutePoint *WayPointman::GetOtherNearbyWaypoint( double lat, double lon, double radius_meters,
2208         const wxString &guid )
2209 {
2210     //    Iterate on the RoutePoint list, checking distance
2211 
2212     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
2213     while( node ) {
2214         RoutePoint *pr = node->GetData();
2215 
2216         double a = lat - pr->m_lat;
2217         double b = lon - pr->m_lon;
2218         double l = sqrt( ( a * a ) + ( b * b ) );
2219 
2220         if( ( l * 60. * 1852. ) < radius_meters ) if( pr->m_GUID != guid ) return pr;
2221 
2222         node = node->GetNext();
2223     }
2224     return NULL;
2225 
2226 }
2227 
ClearRoutePointFonts(void)2228 void WayPointman::ClearRoutePointFonts( void )
2229 {
2230     //    Iterate on the RoutePoint list, clearing Font pointers
2231     //    This is typically done globally after a font switch
2232 
2233     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
2234     while( node ) {
2235         RoutePoint *pr = node->GetData();
2236 
2237         pr->m_pMarkFont = NULL;
2238         node = node->GetNext();
2239     }
2240 }
2241 
SharedWptsExist()2242 bool WayPointman::SharedWptsExist()
2243 {
2244     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
2245     while( node ) {
2246         RoutePoint *prp = node->GetData();
2247         if (prp->m_bKeepXRoute && ( prp->m_bIsInRoute || prp == pAnchorWatchPoint1 || prp == pAnchorWatchPoint2))
2248             return true;
2249         node = node->GetNext();
2250     }
2251     return false;
2252 }
2253 
DeleteAllWaypoints(bool b_delete_used)2254 void WayPointman::DeleteAllWaypoints( bool b_delete_used )
2255 {
2256     //    Iterate on the RoutePoint list, deleting all
2257     wxRoutePointListNode *node = m_pWayPointList->GetFirst();
2258     while( node ) {
2259         RoutePoint *prp = node->GetData();
2260         // if argument is false, then only delete non-route waypoints
2261         if( !prp->m_bIsInLayer && ( prp->GetIconName() != _T("mob") )
2262             && ( ( b_delete_used && prp->m_bKeepXRoute )
2263                         || ( ( !prp->m_bIsInRoute )
2264                                 && !( prp == pAnchorWatchPoint1 ) && !( prp == pAnchorWatchPoint2 ) ) ) ) {
2265             DestroyWaypoint(prp);
2266             delete prp;
2267             node = m_pWayPointList->GetFirst();
2268         } else
2269             node = node->GetNext();
2270     }
2271     return;
2272 
2273 }
2274 
DestroyWaypoint(RoutePoint * pRp,bool b_update_changeset)2275 void WayPointman::DestroyWaypoint( RoutePoint *pRp, bool b_update_changeset )
2276 {
2277     if( ! b_update_changeset )
2278         pConfig->m_bSkipChangeSetUpdate = true;             // turn OFF change-set updating if requested
2279 
2280     if( pRp ) {
2281         // Get a list of all routes containing this point
2282         // and remove the point from them all
2283         wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining( pRp );
2284         if( proute_array ) {
2285             for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ ) {
2286                 Route *pr = (Route *) proute_array->Item( ir );
2287 
2288                 /*  FS#348
2289                  if ( g_pRouteMan->GetpActiveRoute() == pr )            // Deactivate any route containing this point
2290                  g_pRouteMan->DeactivateRoute();
2291                  */
2292                 pr->RemovePoint( pRp );
2293 
2294             }
2295 
2296             //    Scrub the routes, looking for one-point routes
2297             for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ ) {
2298                 Route *pr = (Route *) proute_array->Item( ir );
2299                 if( pr->GetnPoints() < 2 ) {
2300                     bool prev_bskip = pConfig->m_bSkipChangeSetUpdate;
2301                     pConfig->m_bSkipChangeSetUpdate = true;
2302                     pConfig->DeleteConfigRoute( pr );
2303                     g_pRouteMan->DeleteRoute( pr );
2304                     pConfig->m_bSkipChangeSetUpdate = prev_bskip;
2305                 }
2306             }
2307 
2308             delete proute_array;
2309         }
2310 
2311         // Now it is safe to delete the point
2312         pConfig->DeleteWayPoint( pRp );
2313         pConfig->m_bSkipChangeSetUpdate = false;
2314 
2315         pSelect->DeleteSelectableRoutePoint( pRp );
2316 
2317         //    The RoutePoint might be currently in use as an anchor watch point
2318         if( pRp == pAnchorWatchPoint1 ) pAnchorWatchPoint1 = NULL;
2319         if( pRp == pAnchorWatchPoint2 ) pAnchorWatchPoint2 = NULL;
2320 
2321         RemoveRoutePoint( pRp);
2322 
2323     }
2324 }
2325