OpenCPN Partial API docs
routeman.cpp
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 #include <cmath>
26 #include <memory>
27 #include <vector>
28 
29 #include <math.h>
30 #include <stdlib.h>
31 #include <time.h>
32 
33 #include <wx/wxprec.h>
34 
35 #include <wx/image.h>
36 #include <wx/jsonval.h>
37 #include <wx/listimpl.cpp>
38 #include <wx/tokenzr.h>
39 
40 #include "model/ais_decoder.h"
41 #include "model/base_platform.h"
42 #include "model/comm_n0183_output.h"
43 #include "model/comm_vars.h"
44 #include "model/config_vars.h"
45 #include "model/cutil.h"
46 #include "model/georef.h"
47 #include "model/nav_object_database.h"
48 #include "model/navutil_base.h"
49 #include "model/nmea_ctx_factory.h"
50 #include "model/own_ship.h"
51 #include "model/route.h"
52 #include "model/routeman.h"
53 #include "model/track.h"
54 
55 #include "observable_globvar.h"
56 
57 #ifdef __ANDROID__
58 #include "androidUTIL.h"
59 #endif
60 
61 
62 bool g_bPluginHandleAutopilotRoute;
63 
64 Routeman* g_pRouteMan;
65 Route *pAISMOBRoute;
66 
67 RoutePoint *pAnchorWatchPoint1;
68 RoutePoint *pAnchorWatchPoint2;
69 
70 RouteList *pRouteList;
71 
72 float g_ChartScaleFactorExp;
73 
74 // List definitions for Waypoint Manager Icons
75 WX_DECLARE_LIST(wxBitmap, markicon_bitmap_list_type);
76 WX_DECLARE_LIST(wxString, markicon_key_list_type);
77 WX_DECLARE_LIST(wxString, markicon_description_list_type);
78 
79 // List implementation for Waypoint Manager Icons
80 #include <wx/listimpl.cpp>
81 WX_DEFINE_LIST(markicon_bitmap_list_type);
82 WX_DEFINE_LIST(markicon_key_list_type);
83 WX_DEFINE_LIST(markicon_description_list_type);
84 
85 // Helper conditional file name dir slash
86 void appendOSDirSlash(wxString *pString);
87 
88 static void ActivatePersistedRoute(Routeman* routeman) {
89  if (g_active_route == "") {
90  wxLogWarning("\"Persist route\" but no persisted route configured");
91  return;
92  }
93  Route* route = routeman->FindRouteByGUID(g_active_route);
94  if (!route) {
95  wxLogWarning("Persisted route GUID not available");
96  return;
97  }
98  routeman->ActivateRoute(route); // FIXME (leamas) better start point
99 }
100 
101 
102 //--------------------------------------------------------------------------------
103 // Routeman "Route Manager"
104 //--------------------------------------------------------------------------------
105 
106 Routeman::Routeman(struct RoutePropDlgCtx ctx,
107  struct RoutemanDlgCtx route_dlg_ctx,
108  NmeaLog& nmea_log)
109  : pActiveRoute(0),
110  pActivePoint(0),
111  pRouteActivatePoint(0),
112  m_NMEA0183(NmeaCtxFactory()),
113  m_prop_dlg_ctx(ctx),
114  m_route_dlg_ctx(route_dlg_ctx),
115  m_nmea_log(nmea_log) {
116 
117  GlobalVar<wxString> active_route(&g_active_route);
118  auto route_action = [&] (wxCommandEvent) {
119  if (g_persist_active_route) ActivatePersistedRoute(this); };
120  active_route_listener.Init(active_route, route_action);
121 }
122 
123 Routeman::~Routeman() {
124  if (pRouteActivatePoint) delete pRouteActivatePoint;
125 }
126 
127 bool Routeman::IsRouteValid(Route *pRoute) {
128  wxRouteListNode *node = pRouteList->GetFirst();
129  while (node) {
130  if (pRoute == node->GetData()) return true;
131  node = node->GetNext();
132  }
133  return false;
134 }
135 
136 // Make a 2-D search to find the route containing a given waypoint
137 Route *Routeman::FindRouteContainingWaypoint(RoutePoint *pWP) {
138  wxRouteListNode *node = pRouteList->GetFirst();
139  while (node) {
140  Route *proute = node->GetData();
141 
142  wxRoutePointListNode *pnode = (proute->pRoutePointList)->GetFirst();
143  while (pnode) {
144  RoutePoint *prp = pnode->GetData();
145  if (prp == pWP) return proute;
146  pnode = pnode->GetNext();
147  }
148 
149  node = node->GetNext();
150  }
151 
152  return NULL; // not found
153 }
154 
155 // Make a 2-D search to find the visual route containing a given waypoint
156 Route *Routeman::FindVisibleRouteContainingWaypoint(RoutePoint *pWP) {
157  wxRouteListNode *node = pRouteList->GetFirst();
158  while (node) {
159  Route *proute = node->GetData();
160  if (proute->IsVisible()) {
161  wxRoutePointListNode *pnode = (proute->pRoutePointList)->GetFirst();
162  while (pnode) {
163  RoutePoint *prp = pnode->GetData();
164  if (prp == pWP) return proute;
165  pnode = pnode->GetNext();
166  }
167  }
168 
169  node = node->GetNext();
170  }
171 
172  return NULL; // not found
173 }
174 
175 wxArrayPtrVoid *Routeman::GetRouteArrayContaining(RoutePoint *pWP) {
176  wxArrayPtrVoid *pArray = new wxArrayPtrVoid;
177 
178  wxRouteListNode *route_node = pRouteList->GetFirst();
179  while (route_node) {
180  Route *proute = route_node->GetData();
181 
182  wxRoutePointListNode *waypoint_node = (proute->pRoutePointList)->GetFirst();
183  while (waypoint_node) {
184  RoutePoint *prp = waypoint_node->GetData();
185  if (prp == pWP) { // success
186  pArray->Add((void *)proute);
187  break; // only add a route to the array once, even if there are
188  // duplicate points in the route...See FS#1743
189  }
190 
191  waypoint_node = waypoint_node->GetNext(); // next waypoint
192  }
193 
194  route_node = route_node->GetNext(); // next route
195  }
196 
197  if (pArray->GetCount())
198  return pArray;
199 
200  else {
201  delete pArray;
202  return NULL;
203  }
204 }
205 
206 void Routeman::RemovePointFromRoute(RoutePoint *point, Route *route,
207  int route_state) {
208  // Rebuild the route selectables
209  pSelect->DeleteAllSelectableRoutePoints(route);
210  pSelect->DeleteAllSelectableRouteSegments(route);
211 
212  route->RemovePoint(point);
213 
214  // Check for 1 point routes. If we are creating a route, this is an undo, so
215  // keep the 1 point.
216  if (route->GetnPoints() <= 1 && route_state == 0) {
217  NavObjectChanges::getInstance()->DeleteConfigRoute(route);
218  g_pRouteMan->DeleteRoute(route, NavObjectChanges::getInstance());
219  route = NULL;
220  }
221  // Add this point back into the selectables
222  pSelect->AddSelectableRoutePoint(point->m_lat, point->m_lon, point);
223 
224  //if (pRoutePropDialog && (pRoutePropDialog->IsShown())) {
225  // pRoutePropDialog->SetRouteAndUpdate(route, true);
226  //}
227  m_prop_dlg_ctx.set_route_and_update(route);
228 
229 }
230 
231 RoutePoint *Routeman::FindBestActivatePoint(Route *pR, double lat, double lon,
232  double cog, double sog) {
233  if (!pR) return NULL;
234 
235  // Walk thru all the points to find the "best"
236  RoutePoint *best_point = NULL;
237  double min_time_found = 1e6;
238 
239  wxRoutePointListNode *node = (pR->pRoutePointList)->GetFirst();
240  while (node) {
241  RoutePoint *pn = node->GetData();
242 
243  double brg, dist;
244  DistanceBearingMercator(pn->m_lat, pn->m_lon, lat, lon, &brg, &dist);
245 
246  double angle = brg - cog;
247  double soa = cos(angle * PI / 180.);
248 
249  double time_to_wp = dist / soa;
250 
251  if (time_to_wp > 0) {
252  if (time_to_wp < min_time_found) {
253  min_time_found = time_to_wp;
254  best_point = pn;
255  }
256  }
257  node = node->GetNext();
258  }
259  return best_point;
260 }
261 
262 bool Routeman::ActivateRoute(Route *pRouteToActivate, RoutePoint *pStartPoint) {
263  g_bAllowShipToActive = false;
264  wxJSONValue v;
265  v[_T("Route_activated")] = pRouteToActivate->m_RouteNameString;
266  v[_T("GUID")] = pRouteToActivate->m_GUID;
267  json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_RTE_ACTIVATED");
268  if (g_bPluginHandleAutopilotRoute) return true;
269 
270  pActiveRoute = pRouteToActivate;
271  g_active_route = pActiveRoute->GetGUID();
272 
273  if (pStartPoint) {
274  pActivePoint = pStartPoint;
275  } else {
276  wxRoutePointListNode *node = (pActiveRoute->pRoutePointList)->GetFirst();
277  pActivePoint = node->GetData(); // start at beginning
278  }
279 
280  ActivateRoutePoint(pRouteToActivate, pActivePoint);
281 
282  m_bArrival = false;
283  m_arrival_min = 1e6;
284  m_arrival_test = 0;
285 
286  pRouteToActivate->m_bRtIsActive = true;
287 
288  m_bDataValid = false;
289 
290  m_route_dlg_ctx.show_with_fresh_fonts();
291  return true;
292 }
293 
295  g_bAllowShipToActive = false;
296  wxJSONValue v;
297  v[_T("GUID")] = pRP_target->m_GUID;
298  v[_T("WP_activated")] = pRP_target->GetName();
299 
300  json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_WPT_ACTIVATED");
301 
302  if (g_bPluginHandleAutopilotRoute) return true;
303 
304  pActiveRoute = pA;
305 
306  pActivePoint = pRP_target;
307  pActiveRoute->m_pRouteActivePoint = pRP_target;
308 
309  wxRoutePointListNode *node = (pActiveRoute->pRoutePointList)->GetFirst();
310  while (node) {
311  RoutePoint *pn = node->GetData();
312  pn->m_bBlink = false; // turn off all blinking points
313  pn->m_bIsActive = false;
314 
315  node = node->GetNext();
316  }
317 
318  node = (pActiveRoute->pRoutePointList)->GetFirst();
319  RoutePoint *prp_first = node->GetData();
320 
321  // If activating first point in route, create a "virtual" waypoint at present
322  // position
323  if (pRP_target == prp_first) {
324  if (pRouteActivatePoint) delete pRouteActivatePoint;
325 
326  pRouteActivatePoint =
327  new RoutePoint(gLat, gLon, wxString(_T("")), wxString(_T("")),
328  wxEmptyString, false); // Current location
329  pRouteActivatePoint->m_bShowName = false;
330 
331  pActiveRouteSegmentBeginPoint = pRouteActivatePoint;
332  }
333 
334  else {
335  prp_first->m_bBlink = false;
336  node = node->GetNext();
337  RoutePoint *np_prev = prp_first;
338  while (node) {
339  RoutePoint *pnext = node->GetData();
340  if (pnext == pRP_target) {
341  pActiveRouteSegmentBeginPoint = np_prev;
342  break;
343  }
344 
345  np_prev = pnext;
346  node = node->GetNext();
347  }
348  }
349 
350  pRP_target->m_bBlink = true; // blink the active point
351  pRP_target->m_bIsActive = true; // and active
352 
353  g_blink_rect = pRP_target->CurrentRect_in_DC; // set up global blinker
354 
355  m_bArrival = false;
356  m_arrival_min = 1e6;
357  m_arrival_test = 0;
358 
359  // Update the RouteProperties Dialog, if currently shown
365  m_prop_dlg_ctx.set_enroute_point(pA, pActivePoint);
366  return true;
367 }
368 
369 bool Routeman::ActivateNextPoint(Route *pr, bool skipped) {
370  g_bAllowShipToActive = false;
371  wxJSONValue v;
372  if (pActivePoint) {
373  pActivePoint->m_bBlink = false;
374  pActivePoint->m_bIsActive = false;
375 
376  v[_T("isSkipped")] = skipped;
377  v[_T("GUID")] = pActivePoint->m_GUID;
378  v[_T("GUID_WP_arrived")] = pActivePoint->m_GUID;
379  v[_T("WP_arrived")] = pActivePoint->GetName();
380  }
381  int n_index_active = pActiveRoute->GetIndexOf(pActivePoint);
382  if ((n_index_active + 1) <= pActiveRoute->GetnPoints()) {
383  pActiveRouteSegmentBeginPoint = pActivePoint;
384 
385  pActiveRoute->m_pRouteActivePoint =
386  pActiveRoute->GetPoint(n_index_active + 1);
387 
388  pActivePoint = pActiveRoute->GetPoint(n_index_active + 1);
389  v[_T("Next_WP")] = pActivePoint->GetName();
390  v[_T("GUID_Next_WP")] = pActivePoint->m_GUID;
391 
392  pActivePoint->m_bBlink = true;
393  pActivePoint->m_bIsActive = true;
394  g_blink_rect = pActivePoint->CurrentRect_in_DC; // set up global blinker
395 
396  m_bArrival = false;
397  m_arrival_min = 1e6;
398  m_arrival_test = 0;
399 
400  // Update the RouteProperties Dialog, if currently shown
406  m_prop_dlg_ctx.set_enroute_point(pr, pActivePoint);
407 
408  json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_WPT_ARRIVED");
409  return true;
410  }
411 
412  return false;
413 }
414 
415 bool Routeman::DeactivateRoute(bool b_arrival) {
416  if (pActivePoint) {
417  pActivePoint->m_bBlink = false;
418  pActivePoint->m_bIsActive = false;
419  }
420 
421  if (pActiveRoute) {
422  pActiveRoute->m_bRtIsActive = false;
423  pActiveRoute->m_pRouteActivePoint = NULL;
424  g_active_route.Clear();
425 
426  wxJSONValue v;
427  if (!b_arrival) {
428  v[_T("Route_deactivated")] = pActiveRoute->m_RouteNameString;
429  v[_T("GUID")] = pActiveRoute->m_GUID;
430  json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_RTE_DEACTIVATED");
431  } else {
432  v[_T("GUID")] = pActiveRoute->m_GUID;
433  v[_T("Route_ended")] = pActiveRoute->m_RouteNameString;
434  json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_RTE_ENDED");
435  }
436  }
437 
438  pActiveRoute = NULL;
439 
440  if (pRouteActivatePoint) delete pRouteActivatePoint;
441  pRouteActivatePoint = NULL;
442 
443  pActivePoint = NULL;
444 
445  m_route_dlg_ctx.clear_console_background();
446  m_bDataValid = false;
447 
448  return true;
449 }
450 
451 bool Routeman::UpdateAutopilot() {
452  if (!bGPSValid)
453  return false;
454 
455  // Send all known Autopilot messages upstream
456 
457  // Set max WP name length
458  int maxName = 6;
459  if ((g_maxWPNameLength >= 3) && (g_maxWPNameLength <= 32))
460  maxName = g_maxWPNameLength;
461 
462  // Avoid a possible not initiated SOG/COG. APs can be confused if in NAV mode
463  // wo valid GPS
464  double r_Sog(0.0), r_Cog(0.0);
465  if (!std::isnan(gSog)) r_Sog = gSog;
466  if (!std::isnan(gCog)) r_Cog = gCog;
467 
468  // Send active leg info directly to plugins
469 
470  ActiveLegDat leg_info;
471  leg_info.Btw = CurrentBrgToActivePoint;
472  leg_info.Dtw = CurrentRngToActivePoint;
473  leg_info.Xte = CurrentXTEToActivePoint;
474  if (XTEDir < 0) {
475  leg_info.Xte = -leg_info.Xte; // Left side of the track -> negative XTE
476  }
477  leg_info.wp_name = pActivePoint->GetName().Truncate(maxName);
478  leg_info.arrival = m_bArrival;
479 
480  json_leg_info.Notify(std::make_shared<ActiveLegDat>(leg_info), "");
481 
482  // RMB
483  {
484  m_NMEA0183.TalkerID = "EC";
485  SENTENCE snt;
486  m_NMEA0183.Rmb.IsDataValid = bGPSValid ? NTrue : NFalse;
487  m_NMEA0183.Rmb.CrossTrackError = CurrentXTEToActivePoint;
488  m_NMEA0183.Rmb.DirectionToSteer = XTEDir < 0 ? Left : Right;
489  m_NMEA0183.Rmb.RangeToDestinationNauticalMiles = CurrentRngToActivePoint;
490  m_NMEA0183.Rmb.BearingToDestinationDegreesTrue = CurrentBrgToActivePoint;
491 
492  if (pActivePoint->m_lat < 0.)
493  m_NMEA0183.Rmb.DestinationPosition.Latitude.Set(
494  -pActivePoint->m_lat, "S");
495  else
496  m_NMEA0183.Rmb.DestinationPosition.Latitude.Set(
497  pActivePoint->m_lat, "N");
498 
499  if (pActivePoint->m_lon < 0.)
500  m_NMEA0183.Rmb.DestinationPosition.Longitude.Set(
501  -pActivePoint->m_lon, "W");
502  else
503  m_NMEA0183.Rmb.DestinationPosition.Longitude.Set(
504  pActivePoint->m_lon, "E");
505 
506  m_NMEA0183.Rmb.DestinationClosingVelocityKnots =
507  r_Sog * cos((r_Cog - CurrentBrgToActivePoint) * PI / 180.0);
508  m_NMEA0183.Rmb.IsArrivalCircleEntered = m_bArrival ? NTrue : NFalse;
509  m_NMEA0183.Rmb.FAAModeIndicator = bGPSValid ? "A" : "N";
510  // RMB is close to NMEA0183 length limit
511  // Restrict WP names further if necessary
512  int wp_len = maxName;
513  do {
514  m_NMEA0183.Rmb.To = pActivePoint->GetName().Truncate(wp_len);
515  m_NMEA0183.Rmb.From =
516  pActiveRouteSegmentBeginPoint->GetName().Truncate(wp_len);
517  m_NMEA0183.Rmb.Write(snt);
518  wp_len -= 1;
519  } while (snt.Sentence.size() > 82 && wp_len > 0);
520 
521  BroadcastNMEA0183Message(snt.Sentence, m_nmea_log, on_message_sent);
522  }
523 
524  // RMC
525  {
526  m_NMEA0183.TalkerID = _T("EC");
527 
528  SENTENCE snt;
529  m_NMEA0183.Rmc.IsDataValid = NTrue;
530  if (!bGPSValid)
531  m_NMEA0183.Rmc.IsDataValid = NFalse;
532 
533  if (gLat < 0.)
534  m_NMEA0183.Rmc.Position.Latitude.Set(-gLat, _T("S"));
535  else
536  m_NMEA0183.Rmc.Position.Latitude.Set(gLat, _T("N"));
537 
538  if (gLon < 0.)
539  m_NMEA0183.Rmc.Position.Longitude.Set(-gLon, _T("W"));
540  else
541  m_NMEA0183.Rmc.Position.Longitude.Set(gLon, _T("E"));
542 
543  m_NMEA0183.Rmc.SpeedOverGroundKnots = r_Sog;
544  m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue = r_Cog;
545 
546  if (!std::isnan(gVar)) {
547  if (gVar < 0.) {
548  m_NMEA0183.Rmc.MagneticVariation = -gVar;
549  m_NMEA0183.Rmc.MagneticVariationDirection = West;
550  } else {
551  m_NMEA0183.Rmc.MagneticVariation = gVar;
552  m_NMEA0183.Rmc.MagneticVariationDirection = East;
553  }
554  } else
555  m_NMEA0183.Rmc.MagneticVariation =
556  361.; // A signal to NMEA converter, gVAR is unknown
557 
558  // Send GPS time to autopilot if available else send local system time
559  if (!gRmcTime.IsEmpty() && !gRmcDate.IsEmpty()) {
560  m_NMEA0183.Rmc.UTCTime = gRmcTime;
561  m_NMEA0183.Rmc.Date = gRmcDate;
562  } else {
563  wxDateTime now = wxDateTime::Now();
564  wxDateTime utc = now.ToUTC();
565  wxString time = utc.Format(_T("%H%M%S"));
566  m_NMEA0183.Rmc.UTCTime = time;
567  wxString date = utc.Format(_T("%d%m%y"));
568  m_NMEA0183.Rmc.Date = date;
569  }
570 
571  m_NMEA0183.Rmc.FAAModeIndicator = "A";
572  if (!bGPSValid)
573  m_NMEA0183.Rmc.FAAModeIndicator = "N";
574 
575  m_NMEA0183.Rmc.Write(snt);
576 
577  BroadcastNMEA0183Message(snt.Sentence, m_nmea_log, on_message_sent);
578  }
579 
580  // APB
581  {
582  m_NMEA0183.TalkerID = _T("EC");
583 
584  SENTENCE snt;
585 
586  m_NMEA0183.Apb.IsLoranBlinkOK = NTrue; // considered as "generic invalid fix" flag
587  if (!bGPSValid)
588  m_NMEA0183.Apb.IsLoranBlinkOK = NFalse;
589 
590  m_NMEA0183.Apb.IsLoranCCycleLockOK = NTrue;
591  if (!bGPSValid)
592  m_NMEA0183.Apb.IsLoranCCycleLockOK = NFalse;
593 
594  m_NMEA0183.Apb.CrossTrackErrorMagnitude = CurrentXTEToActivePoint;
595 
596  if (XTEDir < 0)
597  m_NMEA0183.Apb.DirectionToSteer = Left;
598  else
599  m_NMEA0183.Apb.DirectionToSteer = Right;
600 
601  m_NMEA0183.Apb.CrossTrackUnits = _T("N");
602 
603  if (m_bArrival)
604  m_NMEA0183.Apb.IsArrivalCircleEntered = NTrue;
605  else
606  m_NMEA0183.Apb.IsArrivalCircleEntered = NFalse;
607 
608  // We never pass the perpendicular, since we declare arrival before
609  // reaching this point
610  m_NMEA0183.Apb.IsPerpendicular = NFalse;
611 
612  m_NMEA0183.Apb.To = pActivePoint->GetName().Truncate(maxName);
613 
614  double brg1, dist1;
615  DistanceBearingMercator(pActivePoint->m_lat, pActivePoint->m_lon,
616  pActiveRouteSegmentBeginPoint->m_lat,
617  pActiveRouteSegmentBeginPoint->m_lon, &brg1,
618  &dist1);
619 
620  if (g_bMagneticAPB && !std::isnan(gVar)) {
621  double brg1m =
622  ((brg1 - gVar) >= 0.) ? (brg1 - gVar) : (brg1 - gVar + 360.);
623  double bapm = ((CurrentBrgToActivePoint - gVar) >= 0.)
624  ? (CurrentBrgToActivePoint - gVar)
625  : (CurrentBrgToActivePoint - gVar + 360.);
626 
627  m_NMEA0183.Apb.BearingOriginToDestination = brg1m;
628  m_NMEA0183.Apb.BearingOriginToDestinationUnits = _T("M");
629 
630  m_NMEA0183.Apb.BearingPresentPositionToDestination = bapm;
631  m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = _T("M");
632 
633  m_NMEA0183.Apb.HeadingToSteer = bapm;
634  m_NMEA0183.Apb.HeadingToSteerUnits = _T("M");
635  } else {
636  m_NMEA0183.Apb.BearingOriginToDestination = brg1;
637  m_NMEA0183.Apb.BearingOriginToDestinationUnits = _T("T");
638 
639  m_NMEA0183.Apb.BearingPresentPositionToDestination =
640  CurrentBrgToActivePoint;
641  m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = _T("T");
642 
643  m_NMEA0183.Apb.HeadingToSteer = CurrentBrgToActivePoint;
644  m_NMEA0183.Apb.HeadingToSteerUnits = _T("T");
645  }
646 
647  m_NMEA0183.Apb.Write(snt);
648  BroadcastNMEA0183Message(snt.Sentence, m_nmea_log, on_message_sent);
649  }
650 
651  // XTE
652  {
653  m_NMEA0183.TalkerID = _T("EC");
654 
655  SENTENCE snt;
656 
657  m_NMEA0183.Xte.IsLoranBlinkOK = NTrue; // considered as "generic invalid fix" flag
658  if (!bGPSValid)
659  m_NMEA0183.Xte.IsLoranBlinkOK = NFalse;
660 
661  m_NMEA0183.Xte.IsLoranCCycleLockOK = NTrue;
662  if (!bGPSValid)
663  m_NMEA0183.Xte.IsLoranCCycleLockOK = NFalse;
664 
665  m_NMEA0183.Xte.CrossTrackErrorDistance = CurrentXTEToActivePoint;
666 
667  if (XTEDir < 0)
668  m_NMEA0183.Xte.DirectionToSteer = Left;
669  else
670  m_NMEA0183.Xte.DirectionToSteer = Right;
671 
672  m_NMEA0183.Xte.CrossTrackUnits = _T("N");
673 
674  m_NMEA0183.Xte.Write(snt);
675  BroadcastNMEA0183Message(snt.Sentence, m_nmea_log, on_message_sent);
676  }
677 
678  return true;
679 }
680 
681 bool Routeman::DoesRouteContainSharedPoints(Route *pRoute) {
682  if (pRoute) {
683  // walk the route, looking at each point to see if it is used by another
684  // route or is isolated
685  wxRoutePointListNode *pnode = (pRoute->pRoutePointList)->GetFirst();
686  while (pnode) {
687  RoutePoint *prp = pnode->GetData();
688 
689  // check all other routes to see if this point appears in any other route
690  wxArrayPtrVoid *pRA = GetRouteArrayContaining(prp);
691 
692  if (pRA) {
693  for (unsigned int ir = 0; ir < pRA->GetCount(); ir++) {
694  Route *pr = (Route *)pRA->Item(ir);
695  if (pr == pRoute)
696  continue; // self
697  else
698  return true;
699  }
700  }
701 
702  if (pnode) pnode = pnode->GetNext();
703  }
704 
705  // Now walk the route again, looking for isolated type shared waypoints
706  pnode = (pRoute->pRoutePointList)->GetFirst();
707  while (pnode) {
708  RoutePoint *prp = pnode->GetData();
709  if (prp->IsShared()) return true;
710 
711  if (pnode) pnode = pnode->GetNext();
712  }
713  }
714 
715  return false;
716 }
717 
718 bool Routeman::DeleteTrack(Track *pTrack) {
719  if (pTrack && !pTrack->m_bIsInLayer) {
720  ::wxBeginBusyCursor();
721  /*
722  wxGenericProgressDialog *pprog = nullptr;
723 
724  int count = pTrack->GetnPoints();
725  if (count > 10000) {
726  pprog = new wxGenericProgressDialog(
727  _("OpenCPN Track Delete"), _T("0/0"), count, NULL,
728  wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_ELAPSED_TIME |
729  wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME);
730  pprog->SetSize(400, wxDefaultCoord);
731  pprog->Centre();
732  }
733  */
734 
735  // Remove the track from associated lists
736  pSelect->DeleteAllSelectableTrackSegments(pTrack);
737  auto it = std::find(g_TrackList.begin(), g_TrackList.end(), pTrack);
738  if (it != g_TrackList.end()) {
739  g_TrackList.erase(it);
740  }
741  delete pTrack;
742 
743  ::wxEndBusyCursor();
744 
745  //delete pprog;
746  return true;
747  }
748  return false;
749 }
750 
751 bool Routeman::DeleteRoute(Route *pRoute, NavObjectChanges* nav_obj_changes) {
752  if (pRoute) {
753  if (pRoute == pAISMOBRoute) {
754  if (!m_route_dlg_ctx.confirm_delete_ais_mob()) {
755  return false;
756  }
757  pAISMOBRoute = 0;
758  }
759  ::wxBeginBusyCursor();
760 
761  if (GetpActiveRoute() == pRoute) DeactivateRoute();
762 
763  if (pRoute->m_bIsInLayer) {
764  ::wxEndBusyCursor();
765  return false;
766  }
771  m_prop_dlg_ctx.hide(pRoute);
772 
773  nav_obj_changes->DeleteConfigRoute(pRoute);
774 
775  // Remove the route from associated lists
776  pSelect->DeleteAllSelectableRouteSegments(pRoute);
777  pRouteList->DeleteObject(pRoute);
778 
779  m_route_dlg_ctx.route_mgr_dlg_update_list_ctrl();
780 
781  // walk the route, tentatively deleting/marking points used only by this
782  // route
783  wxRoutePointListNode *pnode = (pRoute->pRoutePointList)->GetFirst();
784  while (pnode) {
785  RoutePoint *prp = pnode->GetData();
786 
787  // check all other routes to see if this point appears in any other route
788  Route *pcontainer_route = FindRouteContainingWaypoint(prp);
789 
790  if (pcontainer_route == NULL && prp->m_bIsInRoute) {
791  prp->m_bIsInRoute =
792  false; // Take this point out of this (and only) route
793  if (!prp->IsShared()) {
794  // This does not need to be done with navobj.xml storage, since the
795  // waypoints are stored with the route
796  // pConfig->DeleteWayPoint(prp);
797 
798  pSelect->DeleteSelectablePoint(prp, SELTYPE_ROUTEPOINT);
799 
800  // Remove all instances of this point from the list.
801  wxRoutePointListNode *pdnode = pnode;
802  while (pdnode) {
803  pRoute->pRoutePointList->DeleteNode(pdnode);
804  pdnode = pRoute->pRoutePointList->Find(prp);
805  }
806 
807  pnode = NULL;
808  delete prp;
809  } else {
810  prp->m_bDynamicName = false;
811  prp->m_bIsolatedMark = true; // This has become an isolated mark
812  prp->SetShared(false); // and is no longer part of a route
813  }
814  }
815  if (pnode)
816  pnode = pnode->GetNext();
817  else
818  pnode = pRoute->pRoutePointList->GetFirst(); // restart the list
819  }
820 
821  delete pRoute;
822 
823  ::wxEndBusyCursor();
824  }
825  return true;
826 }
827 
828 void Routeman::DeleteAllRoutes(NavObjectChanges* nav_obj_changes) {
829  ::wxBeginBusyCursor();
830 
831  // Iterate on the RouteList
832  wxRouteListNode *node = pRouteList->GetFirst();
833  while (node) {
834  Route *proute = node->GetData();
835  if (proute == pAISMOBRoute) {
836  if (!m_route_dlg_ctx.confirm_delete_ais_mob()) {
837  return;
838  }
839  pAISMOBRoute = 0;
840  ::wxBeginBusyCursor();
841  }
842 
843  node = node->GetNext();
844  if (proute->m_bIsInLayer) continue;
845 
846  nav_obj_changes->m_bSkipChangeSetUpdate = true;
847  nav_obj_changes->DeleteConfigRoute(proute);
848  DeleteRoute(proute, nav_obj_changes);
849  nav_obj_changes->m_bSkipChangeSetUpdate = false;
850  }
851 
852  ::wxEndBusyCursor();
853 }
854 
855 
856 void Routeman::SetColorScheme(ColorScheme cs, double displayDPmm) {
857  // Re-Create the pens and colors
858 
859  int scaled_line_width = g_route_line_width;
860  int track_scaled_line_width = g_track_line_width;
861  if (g_btouch) {
862  // 0.2 mm nominal, but not less than 1 pixel
863  double nominal_line_width_pix = wxMax(1.5, floor(displayDPmm / 5.0));
864 
865  double sline_width = wxMax(nominal_line_width_pix, g_route_line_width);
866  sline_width *= g_ChartScaleFactorExp;
867  scaled_line_width = wxMax(sline_width, 2);
868 
869  double tsline_width = wxMax(nominal_line_width_pix, g_track_line_width);
870  tsline_width *= g_ChartScaleFactorExp;
871  track_scaled_line_width = wxMax(tsline_width, 2);
872  }
873 
874  m_pActiveRoutePointPen = wxThePenList->FindOrCreatePen(
875  wxColour(0, 0, 255), scaled_line_width, wxPENSTYLE_SOLID);
876  m_pRoutePointPen = wxThePenList->FindOrCreatePen(
877  wxColour(0, 0, 255), scaled_line_width, wxPENSTYLE_SOLID);
878 
879  // Or in something like S-52 compliance
880 
881  m_pRoutePen = wxThePenList->FindOrCreatePen(
882  m_route_dlg_ctx.get_global_colour("UINFB"), scaled_line_width,
883  wxPENSTYLE_SOLID);
884  m_pSelectedRoutePen = wxThePenList->FindOrCreatePen(
885  m_route_dlg_ctx.get_global_colour("UINFO"), scaled_line_width,
886  wxPENSTYLE_SOLID);
887  m_pActiveRoutePen = wxThePenList->FindOrCreatePen(
888  m_route_dlg_ctx.get_global_colour("UARTE"), scaled_line_width,
889  wxPENSTYLE_SOLID);
890  m_pTrackPen = wxThePenList->FindOrCreatePen(
891  m_route_dlg_ctx.get_global_colour("CHMGD"), track_scaled_line_width,
892  wxPENSTYLE_SOLID);
893  m_pRouteBrush = wxTheBrushList->FindOrCreateBrush(
894  m_route_dlg_ctx.get_global_colour("UINFB"), wxBRUSHSTYLE_SOLID);
895  m_pSelectedRouteBrush = wxTheBrushList->FindOrCreateBrush(
896  m_route_dlg_ctx.get_global_colour("UINFO"), wxBRUSHSTYLE_SOLID);
897  m_pActiveRouteBrush = wxTheBrushList->FindOrCreateBrush(
898  m_route_dlg_ctx.get_global_colour("PLRTE"), wxBRUSHSTYLE_SOLID);
899 }
900 
901 wxString Routeman::GetRouteReverseMessage(void) {
902  return wxString(
903  _("Waypoints can be renamed to reflect the new order, the names will be "
904  "'001', '002' etc.\n\nDo you want to rename the waypoints?"));
905 }
906 
907 wxString Routeman::GetRouteResequenceMessage(void) {
908  return wxString(
909  _("Waypoints will be renamed to reflect the natural order, the names "
910  "will be '001', '002' etc.\n\nDo you want to rename the waypoints?"));
911 }
912 
913 Route *Routeman::FindRouteByGUID(const wxString &guid) {
914  wxRouteListNode *node1 = pRouteList->GetFirst();
915  while (node1) {
916  Route *pRoute = node1->GetData();
917 
918  if (pRoute->m_GUID == guid) return pRoute;
919  node1 = node1->GetNext();
920  }
921 
922  return NULL;
923 }
924 
925 Track *Routeman::FindTrackByGUID(const wxString &guid) {
926  for (Track* pTrack : g_TrackList) {
927  if (pTrack->m_GUID == guid) return pTrack;
928  }
929 
930  return NULL;
931 }
932 
933 void Routeman::ZeroCurrentXTEToActivePoint() {
934  // When zeroing XTE create a "virtual" waypoint at present position
935  if (pRouteActivatePoint) delete pRouteActivatePoint;
936  pRouteActivatePoint =
937  new RoutePoint(gLat, gLon, wxString(_T("")), wxString(_T("")),
938  wxEmptyString, false); // Current location
939  pRouteActivatePoint->m_bShowName = false;
940 
941  pActiveRouteSegmentBeginPoint = pRouteActivatePoint;
942  m_arrival_min = 1e6;
943 }
944 
945 //--------------------------------------------------------------------------------
946 // WayPointman Implementation
947 //--------------------------------------------------------------------------------
948 
949 WayPointman::WayPointman(GlobalColourFunc color_func)
950  : m_get_global_colour(color_func) {
951  m_pWayPointList = new RoutePointList;
952 
953  pmarkicon_image_list = NULL;
954 
955  //ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
956  m_pIconArray = new ArrayOfMarkIcon;
957  m_pLegacyIconArray = NULL;
958  m_pExtendedIconArray = NULL;
959 
960  m_cs = (ColorScheme)-1;
961 
962  m_nGUID = 0;
963  m_iconListScale = -999.0;
964  m_iconListHeight = -1;
965 }
966 
967 WayPointman::~WayPointman() {
968  // Two step here, since the RoutePoint dtor also touches the
969  // RoutePoint list.
970  // Copy the master RoutePoint list to a temporary list,
971  // then clear and delete objects from the temp list
972 
973  RoutePointList temp_list;
974 
975  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
976  while (node) {
977  RoutePoint *pr = node->GetData();
978 
979  temp_list.Append(pr);
980  node = node->GetNext();
981  }
982 
983  temp_list.DeleteContents(true);
984  temp_list.Clear();
985 
986  m_pWayPointList->Clear();
987  delete m_pWayPointList;
988 
989  for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
990  MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(i);
991  delete pmi->piconBitmap;
992  delete pmi;
993  }
994 
995  m_pIconArray->Clear();
996  delete m_pIconArray;
997 
998  if (pmarkicon_image_list) pmarkicon_image_list->RemoveAll();
999  delete pmarkicon_image_list;
1000  m_pLegacyIconArray->Clear();
1001  delete m_pLegacyIconArray;
1002  m_pExtendedIconArray->Clear();
1003  delete m_pExtendedIconArray;
1004 }
1005 
1006 bool WayPointman::AddRoutePoint(RoutePoint *prp) {
1007  if (!prp) return false;
1008 
1009  wxRoutePointListNode *prpnode = m_pWayPointList->Append(prp);
1010  prp->SetManagerListNode(prpnode);
1011 
1012  return true;
1013 }
1014 
1015 bool WayPointman::RemoveRoutePoint(RoutePoint *prp) {
1016  if (!prp) return false;
1017 
1018  wxRoutePointListNode *prpnode =
1019  (wxRoutePointListNode *)prp->GetManagerListNode();
1020 
1021  if (prpnode)
1022  delete prpnode;
1023  else
1024  m_pWayPointList->DeleteObject(prp);
1025 
1026  prp->SetManagerListNode(NULL);
1027 
1028  return true;
1029 }
1030 
1031 wxImageList *WayPointman::Getpmarkicon_image_list(int nominal_height) {
1032  // Cached version available?
1033  if (pmarkicon_image_list && (nominal_height == m_iconListHeight)) {
1034  return pmarkicon_image_list;
1035  }
1036 
1037  // Build an image list large enough
1038  if (NULL != pmarkicon_image_list) {
1039  pmarkicon_image_list->RemoveAll();
1040  delete pmarkicon_image_list;
1041  }
1042  pmarkicon_image_list = new wxImageList(nominal_height, nominal_height);
1043 
1044  m_iconListHeight = nominal_height;
1045  m_bitmapSizeForList = nominal_height;
1046 
1047  return pmarkicon_image_list;
1048 }
1049 
1050 wxBitmap *WayPointman::CreateDimBitmap(wxBitmap *pBitmap, double factor) {
1051  wxImage img = pBitmap->ConvertToImage();
1052  int sx = img.GetWidth();
1053  int sy = img.GetHeight();
1054 
1055  wxImage new_img(img);
1056 
1057  for (int i = 0; i < sx; i++) {
1058  for (int j = 0; j < sy; j++) {
1059  if (!img.IsTransparent(i, j)) {
1060  new_img.SetRGB(i, j, (unsigned char)(img.GetRed(i, j) * factor),
1061  (unsigned char)(img.GetGreen(i, j) * factor),
1062  (unsigned char)(img.GetBlue(i, j) * factor));
1063  }
1064  }
1065  }
1066 
1067  wxBitmap *pret = new wxBitmap(new_img);
1068 
1069  return pret;
1070 }
1071 
1072 wxImage WayPointman::CreateDimImage(wxImage &image, double factor) {
1073  int sx = image.GetWidth();
1074  int sy = image.GetHeight();
1075 
1076  wxImage new_img(image);
1077 
1078  for (int i = 0; i < sx; i++) {
1079  for (int j = 0; j < sy; j++) {
1080  if (!image.IsTransparent(i, j)) {
1081  new_img.SetRGB(i, j, (unsigned char)(image.GetRed(i, j) * factor),
1082  (unsigned char)(image.GetGreen(i, j) * factor),
1083  (unsigned char)(image.GetBlue(i, j) * factor));
1084  }
1085  }
1086  }
1087 
1088  return wxImage(new_img);
1089 }
1090 
1091 bool WayPointman::DoesIconExist(const wxString &icon_key) const {
1092  MarkIcon *pmi;
1093  unsigned int i;
1094 
1095  for (i = 0; i < m_pIconArray->GetCount(); i++) {
1096  pmi = (MarkIcon *)m_pIconArray->Item(i);
1097  if (pmi->icon_name.IsSameAs(icon_key)) return true;
1098  }
1099 
1100  return false;
1101 }
1102 
1103 wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) {
1104  wxBitmap *pret = NULL;
1105  MarkIcon *pmi = NULL;
1106  unsigned int i;
1107 
1108  for (i = 0; i < m_pIconArray->GetCount(); i++) {
1109  pmi = (MarkIcon *)m_pIconArray->Item(i);
1110  if (pmi->icon_name.IsSameAs(icon_key)) break;
1111  }
1112 
1113  if (i == m_pIconArray->GetCount()) // key not found
1114  {
1115  // find and return bitmap for "circle"
1116  for (i = 0; i < m_pIconArray->GetCount(); i++) {
1117  pmi = (MarkIcon *)m_pIconArray->Item(i);
1118  // if( pmi->icon_name.IsSameAs( _T("circle") ) )
1119  // break;
1120  }
1121  }
1122 
1123  if (i == m_pIconArray->GetCount()) // "circle" not found
1124  pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1125 
1126  if (pmi) {
1127  if (pmi->piconBitmap)
1128  pret = pmi->piconBitmap;
1129  else {
1130  if (pmi->iconImage.IsOk()) {
1131  pmi->piconBitmap = new wxBitmap(pmi->iconImage);
1132  pret = pmi->piconBitmap;
1133  }
1134  }
1135  }
1136  return pret;
1137 }
1138 
1139 bool WayPointman::GetIconPrescaled(const wxString &icon_key) {
1140  MarkIcon *pmi = NULL;
1141  unsigned int i;
1142 
1143  for (i = 0; i < m_pIconArray->GetCount(); i++) {
1144  pmi = (MarkIcon *)m_pIconArray->Item(i);
1145  if (pmi->icon_name.IsSameAs(icon_key)) break;
1146  }
1147 
1148  if (i == m_pIconArray->GetCount()) // key not found
1149  {
1150  // find and return bitmap for "circle"
1151  for (i = 0; i < m_pIconArray->GetCount(); i++) {
1152  pmi = (MarkIcon *)m_pIconArray->Item(i);
1153  // if( pmi->icon_name.IsSameAs( _T("circle") ) )
1154  // break;
1155  }
1156  }
1157 
1158  if (i == m_pIconArray->GetCount()) // "circle" not found
1159  pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1160 
1161  if (pmi)
1162  return pmi->preScaled;
1163  else
1164  return false;
1165 }
1166 
1167 wxBitmap WayPointman::GetIconBitmapForList(int index, int height) {
1168  wxBitmap pret;
1169  MarkIcon *pmi;
1170 
1171  if (index >= 0) {
1172  pmi = (MarkIcon *)m_pIconArray->Item(index);
1173  // Scale the icon to "list size" if necessary
1174  if (pmi->iconImage.GetHeight() != height) {
1175  int w = height;
1176  int h = height;
1177  int w0 = pmi->iconImage.GetWidth();
1178  int h0 = pmi->iconImage.GetHeight();
1179 
1180  wxImage icon_resized = pmi->iconImage; // make a copy
1181  if (h0 <= h && w0 <= w) {
1182  icon_resized = pmi->iconImage.Resize(
1183  wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1184  } else {
1185  // rescale in one or two directions to avoid cropping, then resize to
1186  // fit to cell
1187  int h1 = h;
1188  int w1 = w;
1189  if (h0 > h)
1190  w1 = wxRound((double)w0 * ((double)h / (double)h0));
1191 
1192  else if (w0 > w)
1193  h1 = wxRound((double)h0 * ((double)w / (double)w0));
1194 
1195  icon_resized = pmi->iconImage.Rescale(w1, h1);
1196  icon_resized = pmi->iconImage.Resize(
1197  wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1198  }
1199 
1200  pret = wxBitmap(icon_resized);
1201 
1202  } else
1203  pret = wxBitmap(pmi->iconImage);
1204  }
1205 
1206  return pret;
1207 }
1208 
1209 wxString *WayPointman::GetIconDescription(int index) {
1210  wxString *pret = NULL;
1211 
1212  if (index >= 0) {
1213  MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1214  pret = &pmi->icon_description;
1215  }
1216  return pret;
1217 }
1218 
1219 wxString WayPointman::GetIconDescription(wxString icon_key) {
1220  MarkIcon *pmi;
1221  unsigned int i;
1222 
1223  for (i = 0; i < m_pIconArray->GetCount(); i++) {
1224  pmi = (MarkIcon *)m_pIconArray->Item(i);
1225  if (pmi->icon_name.IsSameAs(icon_key))
1226  return wxString(pmi->icon_description);
1227  }
1228 
1229  return wxEmptyString;
1230 }
1231 
1232 wxString *WayPointman::GetIconKey(int index) {
1233  wxString *pret = NULL;
1234 
1235  if ((index >= 0) && ((unsigned int)index < m_pIconArray->GetCount())) {
1236  MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1237  pret = &pmi->icon_name;
1238  }
1239  return pret;
1240 }
1241 
1242 int WayPointman::GetIconIndex(const wxBitmap *pbm) {
1243  unsigned int ret = 0;
1244  MarkIcon *pmi;
1245 
1246  wxASSERT(m_pIconArray->GetCount() >= 1);
1247  for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
1248  pmi = (MarkIcon *)m_pIconArray->Item(i);
1249  if (pmi->piconBitmap == pbm) {
1250  ret = i;
1251  break;
1252  }
1253  }
1254 
1255  return ret;
1256 }
1257 
1258 int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) {
1259  MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(GetIconIndex(pbm));
1260 
1261  // Build a "list - sized" image
1262  if (pmarkicon_image_list && !pmi->m_blistImageOK) {
1263  int h0 = pmi->iconImage.GetHeight();
1264  int w0 = pmi->iconImage.GetWidth();
1265  int h = m_bitmapSizeForList;
1266  int w = m_bitmapSizeForList;
1267 
1268  wxImage icon_larger = pmi->iconImage; // make a copy
1269  if (h0 <= h && w0 <= w) {
1270  icon_larger = pmi->iconImage.Resize(
1271  wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1272  } else {
1273  // We want to maintain the aspect ratio of the original image, but need the canvas to fit the fixed cell size
1274  // rescale in one or two directions to avoid cropping, then resize to fit to cell (Adds border/croops as necessary)
1275  int h1 = h;
1276  int w1 = w;
1277  if (h0 > h)
1278  w1 = wxRound((double)w0 * ((double)h / (double)h0));
1279 
1280  else if (w0 > w)
1281  h1 = wxRound((double)h0 * ((double)w / (double)w0));
1282 
1283  icon_larger = pmi->iconImage.Rescale(w1, h1).Resize(wxSize(w, h),
1284  wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1285  }
1286 
1287  int index = pmarkicon_image_list->Add(wxBitmap(icon_larger));
1288 
1289  // Create and replace "x-ed out" and "fixed visibility" icon,
1290  // Being careful to preserve (some) transparency
1291 
1292  icon_larger.ConvertAlphaToMask(128);
1293 
1294  unsigned char r, g, b;
1295  icon_larger.GetOrFindMaskColour(&r, &g, &b);
1296  wxColour unused_color(r, g, b);
1297 
1298  // X-out
1299  wxBitmap xIcon(icon_larger);
1300 
1301  wxBitmap xbmp(w, h, -1);
1302  wxMemoryDC mdc(xbmp);
1303  mdc.SetBackground(wxBrush(unused_color));
1304  mdc.Clear();
1305  mdc.DrawBitmap(xIcon, 0, 0);
1306  int xm = xbmp.GetWidth() / 2;
1307  int ym = xbmp.GetHeight() / 2;
1308  int dp = xm / 2;
1309  int width = wxMax(xm / 10, 2);
1310  wxPen red(m_get_global_colour("URED"), width);
1311  mdc.SetPen(red);
1312  mdc.DrawLine(xm - dp, ym - dp, xm + dp, ym + dp);
1313  mdc.DrawLine(xm - dp, ym + dp, xm + dp, ym - dp);
1314  mdc.SelectObject(wxNullBitmap);
1315 
1316  wxMask *pmask = new wxMask(xbmp, unused_color);
1317  xbmp.SetMask(pmask);
1318 
1319  pmarkicon_image_list->Add(xbmp);
1320 
1321  // fixed Viz
1322  wxBitmap fIcon(icon_larger);
1323 
1324  wxBitmap fbmp(w, h, -1);
1325  wxMemoryDC fmdc(fbmp);
1326  fmdc.SetBackground(wxBrush(unused_color));
1327  fmdc.Clear();
1328  fmdc.DrawBitmap(xIcon, 0, 0);
1329  xm = fbmp.GetWidth() / 2;
1330  ym = fbmp.GetHeight() / 2;
1331  dp = xm / 2;
1332  width = wxMax(xm / 10, 2);
1333  wxPen fred(m_get_global_colour("UGREN"), width);
1334  fmdc.SetPen(fred);
1335  fmdc.DrawLine(xm - dp, ym + dp, xm + dp, ym + dp);
1336  fmdc.SelectObject(wxNullBitmap);
1337 
1338  wxMask *pfmask = new wxMask(fbmp, unused_color);
1339  fbmp.SetMask(pfmask);
1340 
1341  pmarkicon_image_list->Add(fbmp);
1342 
1343  pmi->m_blistImageOK = true;
1344  pmi->listIndex = index;
1345  }
1346 
1347  return pmi->listIndex;
1348 }
1349 
1350 int WayPointman::GetXIconImageListIndex(const wxBitmap *pbm) {
1351  return GetIconImageListIndex(pbm) +
1352  1; // index of "X-ed out" icon in the image list
1353 }
1354 
1355 int WayPointman::GetFIconImageListIndex(const wxBitmap *pbm) {
1356  return GetIconImageListIndex(pbm) +
1357  2; // index of "fixed viz" icon in the image list
1358 }
1359 
1360 // Create the unique identifier
1361 wxString WayPointman::CreateGUID(RoutePoint *pRP) {
1362  // FIXME: this method is not needed at all (if GetUUID works...)
1363  /*wxDateTime now = wxDateTime::Now();
1364  time_t ticks = now.GetTicks();
1365  wxString GUID;
1366  GUID.Printf(_T("%d-%d-%d-%d"), ((int)fabs(pRP->m_lat * 1e4)),
1367  ((int)fabs(pRP->m_lon * 1e4)), (int)ticks, m_nGUID);
1368 
1369  m_nGUID++;
1370 
1371  return GUID;*/
1372  return GpxDocument::GetUUID();
1373 }
1374 
1375 RoutePoint *WayPointman::FindRoutePointByGUID(const wxString &guid) {
1376  wxRoutePointListNode *prpnode = m_pWayPointList->GetFirst();
1377  while (prpnode) {
1378  RoutePoint *prp = prpnode->GetData();
1379 
1380  if (prp->m_GUID == guid) return (prp);
1381 
1382  prpnode = prpnode->GetNext(); // RoutePoint
1383  }
1384 
1385  return NULL;
1386 }
1387 
1388 RoutePoint *WayPointman::GetNearbyWaypoint(double lat, double lon,
1389  double radius_meters) {
1390  // Iterate on the RoutePoint list, checking distance
1391 
1392  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1393  while (node) {
1394  RoutePoint *pr = node->GetData();
1395 
1396  double a = lat - pr->m_lat;
1397  double b = lon - pr->m_lon;
1398  double l = sqrt((a * a) + (b * b));
1399 
1400  if ((l * 60. * 1852.) < radius_meters) return pr;
1401 
1402  node = node->GetNext();
1403  }
1404  return NULL;
1405 }
1406 
1407 RoutePoint *WayPointman::GetOtherNearbyWaypoint(double lat, double lon,
1408  double radius_meters,
1409  const wxString &guid) {
1410  // Iterate on the RoutePoint list, checking distance
1411 
1412  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1413  while (node) {
1414  RoutePoint *pr = node->GetData();
1415 
1416  double a = lat - pr->m_lat;
1417  double b = lon - pr->m_lon;
1418  double l = sqrt((a * a) + (b * b));
1419 
1420  if ((l * 60. * 1852.) < radius_meters)
1421  if (pr->m_GUID != guid) return pr;
1422 
1423  node = node->GetNext();
1424  }
1425  return NULL;
1426 }
1427 
1428 bool WayPointman::IsReallyVisible(RoutePoint *pWP) {
1429  if (pWP->m_bIsolatedMark)
1430  return pWP->IsVisible(); // isolated point
1431  else {
1432  wxRouteListNode *node = pRouteList->GetFirst();
1433  while (node) {
1434  Route *proute = node->GetData();
1435  if (proute && proute->pRoutePointList) {
1436  if (proute->pRoutePointList->IndexOf(pWP) != wxNOT_FOUND) {
1437  if (proute->IsVisible()) return true;
1438  }
1439  }
1440  node = node->GetNext();
1441  }
1442  }
1443  if (pWP->IsShared()) // is not visible as part of route, but still exists as
1444  // a waypoint
1445  return pWP->IsVisible(); // so treat as isolated point
1446 
1447  return false;
1448 }
1449 
1450 void WayPointman::ClearRoutePointFonts(void) {
1451  // Iterate on the RoutePoint list, clearing Font pointers
1452  // This is typically done globally after a font switch
1453 
1454  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1455  while (node) {
1456  RoutePoint *pr = node->GetData();
1457 
1458  pr->m_pMarkFont = NULL;
1459  node = node->GetNext();
1460  }
1461 }
1462 
1463 bool WayPointman::SharedWptsExist() {
1464  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1465  while (node) {
1466  RoutePoint *prp = node->GetData();
1467  if (prp->IsShared() && (prp->m_bIsInRoute || prp == pAnchorWatchPoint1 ||
1468  prp == pAnchorWatchPoint2))
1469  return true;
1470  node = node->GetNext();
1471  }
1472  return false;
1473 }
1474 
1475 void WayPointman::DeleteAllWaypoints(bool b_delete_used) {
1476  // Iterate on the RoutePoint list, deleting all
1477  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1478  while (node) {
1479  RoutePoint *prp = node->GetData();
1480  // if argument is false, then only delete non-route waypoints
1481  if (!prp->m_bIsInLayer && (prp->GetIconName() != _T("mob")) &&
1482  ((b_delete_used && prp->IsShared()) ||
1483  ((!prp->m_bIsInRoute) && !(prp == pAnchorWatchPoint1) &&
1484  !(prp == pAnchorWatchPoint2)))) {
1485  DestroyWaypoint(prp);
1486  delete prp;
1487  node = m_pWayPointList->GetFirst();
1488  } else
1489  node = node->GetNext();
1490  }
1491  return;
1492 }
1493 
1494 RoutePoint* WayPointman::FindWaypointByGuid(const std::string& guid) {
1495  wxRoutePointListNode *node = m_pWayPointList->GetFirst();
1496  while (node) {
1497  RoutePoint* rp = node->GetData();
1498  if (guid == rp->m_GUID) return rp;
1499  node = node->GetNext();
1500  }
1501  return 0;
1502 }
1503 void WayPointman::DestroyWaypoint(RoutePoint *pRp, bool b_update_changeset) {
1504  if (!b_update_changeset)
1505  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate = true;
1506  // turn OFF change-set updating if requested
1507 
1508  if (pRp) {
1509  // Get a list of all routes containing this point
1510  // and remove the point from them all
1511  wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining(pRp);
1512  if (proute_array) {
1513  for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1514  Route *pr = (Route *)proute_array->Item(ir);
1515 
1516  /* FS#348
1517  if ( g_pRouteMan->GetpActiveRoute() == pr ) // Deactivate
1518  any route containing this point g_pRouteMan->DeactivateRoute();
1519  */
1520  pr->RemovePoint(pRp);
1521  }
1522 
1523  // Scrub the routes, looking for one-point routes
1524  for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1525  Route *pr = (Route *)proute_array->Item(ir);
1526  if (pr->GetnPoints() < 2) {
1527  bool prev_bskip =
1528  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate;
1529  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate = true;
1530  NavObjectChanges::getInstance()->DeleteConfigRoute(pr);
1531  g_pRouteMan->DeleteRoute(pr, NavObjectChanges::getInstance());
1532  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate = prev_bskip;
1533  }
1534  }
1535 
1536  delete proute_array;
1537  }
1538 
1539  // Now it is safe to delete the point
1540  NavObjectChanges::getInstance()->DeleteWayPoint(pRp);
1541  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate = false;
1542 
1543  pSelect->DeleteSelectableRoutePoint(pRp);
1544 
1545  // The RoutePoint might be currently in use as an anchor watch point
1546  if (pRp == pAnchorWatchPoint1) pAnchorWatchPoint1 = NULL;
1547  if (pRp == pAnchorWatchPoint2) pAnchorWatchPoint2 = NULL;
1548 
1549  RemoveRoutePoint(pRp);
1550  }
1551 }
const void Notify()
Notify all listeners, no data supplied.
Wrapper for global variable, supports notification events when value changes.
Definition: route.h:75
bool ActivateRoutePoint(Route *pA, RoutePoint *pRP)
Definition: routeman.cpp:294
bool ActivateNextPoint(Route *pr, bool skipped)
Definition: routeman.cpp:369
bool DeleteRoute(Route *pRoute, NavObjectChanges *nav_obj_changes)
Definition: routeman.cpp:751
EventVar json_msg
Notified with message targeting all plugins.
Definition: routeman.h:187
EventVar json_leg_info
Notified with a shared_ptr<ActiveLegDat>, leg info to all plugins.
Definition: routeman.h:190
EventVar on_message_sent
Notified when a message available as GetString() is sent to garmin.
Definition: routeman.h:193
Definition: track.h:78
Callbacks for RoutePropDlg.
Definition: routeman.h:83
Routeman callbacks.
Definition: routeman.h:96