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