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