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