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 int step = 1;
372 while (n_index_active == pActiveRoute->GetIndexOf(pActivePoint)) {
373 if ((n_index_active + step) <= pActiveRoute->GetnPoints()) {
374 pActiveRouteSegmentBeginPoint = pActivePoint;
375 pActiveRoute->m_pRouteActivePoint =
376 pActiveRoute->GetPoint(n_index_active + step);
377 pActivePoint = pActiveRoute->GetPoint(n_index_active + step);
378 step++;
379 result = true;
380 } else {
381 n_index_active = -1; // stop the while loop
382 result = false;
383 }
384 }
385 if (result) {
386 v["Next_WP"] = pActivePoint->GetName();
387 v["GUID_Next_WP"] = pActivePoint->m_GUID;
388
389 pActivePoint->m_bBlink = true;
390 pActivePoint->m_bIsActive = true;
391 g_blink_rect = pActivePoint->CurrentRect_in_DC; // set up global blinker
392 m_bArrival = false;
393 m_arrival_min = 1e6;
394 m_arrival_test = 0;
395
396 // Update the RouteProperties Dialog, if currently shown
402 m_prop_dlg_ctx.set_enroute_point(pr, pActivePoint);
403
404 json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_WPT_ARRIVED");
405 }
406 return result;
407}
408
409bool Routeman::DeactivateRoute(bool b_arrival) {
410 if (pActivePoint) {
411 pActivePoint->m_bBlink = false;
412 pActivePoint->m_bIsActive = false;
413 }
414
415 if (pActiveRoute) {
416 pActiveRoute->m_bRtIsActive = false;
417 pActiveRoute->m_pRouteActivePoint = NULL;
418 g_active_route.Clear();
419
420 wxJSONValue v;
421 if (!b_arrival) {
422 v["Route_deactivated"] = pActiveRoute->m_RouteNameString;
423 v["GUID"] = pActiveRoute->m_GUID;
424 json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_RTE_DEACTIVATED");
425 } else {
426 v["GUID"] = pActiveRoute->m_GUID;
427 v["Route_ended"] = pActiveRoute->m_RouteNameString;
428 json_msg.Notify(std::make_shared<wxJSONValue>(v), "OCPN_RTE_ENDED");
429 }
430 }
431
432 pActiveRoute = NULL;
433
434 if (pRouteActivatePoint) delete pRouteActivatePoint;
435 pRouteActivatePoint = NULL;
436
437 pActivePoint = NULL;
438
439 m_route_dlg_ctx.clear_console_background();
440 m_bDataValid = false;
441
442 return true;
443}
444
445bool Routeman::UpdateAutopilot() {
446 if (!pActiveRoute) return false;
447
448 if (!bGPSValid) return false;
449 bool rv = false;
450
451 // Set max WP name length
452 int maxName = 6;
453 if ((g_maxWPNameLength >= 3) && (g_maxWPNameLength <= 32))
454 maxName = g_maxWPNameLength;
455
456 if (m_have_n0183_out) rv |= UpdateAutopilotN0183(*this);
457 if (m_have_n2000_out) rv |= UpdateAutopilotN2K(*this);
458
459 // Route may have been deactivated or deleted during the
460 // N2K port setup conversation. The message loop runs...
461 if (!pActiveRoute) return false;
462
463 // Send active leg info directly to plugins
464 ActiveLegDat leg_info;
465 leg_info.Btw = CurrentBrgToActivePoint;
466 leg_info.Dtw = CurrentRngToActivePoint;
467 leg_info.Xte = CurrentXTEToActivePoint;
468 if (XTEDir < 0) {
469 leg_info.Xte = -leg_info.Xte; // Left side of the track -> negative XTE
470 }
471 leg_info.wp_name = pActivePoint->GetName().Truncate(maxName);
472 leg_info.arrival = m_bArrival;
473
474 json_leg_info.Notify(std::make_shared<ActiveLegDat>(leg_info), "");
475
476#if 0
477
478
479 // Send all known Autopilot messages upstream
480
481 // Set max WP name length
482 int maxName = 6;
483 if ((g_maxWPNameLength >= 3) && (g_maxWPNameLength <= 32))
484 maxName = g_maxWPNameLength;
485
486 // Avoid a possible not initiated SOG/COG. APs can be confused if in NAV mode
487 // wo valid GPS
488 double r_Sog(0.0), r_Cog(0.0);
489 if (!std::isnan(gSog)) r_Sog = gSog;
490 if (!std::isnan(gCog)) r_Cog = gCog;
491
492 // Send active leg info directly to plugins
493
494 ActiveLegDat leg_info;
495 leg_info.Btw = CurrentBrgToActivePoint;
496 leg_info.Dtw = CurrentRngToActivePoint;
497 leg_info.Xte = CurrentXTEToActivePoint;
498 if (XTEDir < 0) {
499 leg_info.Xte = -leg_info.Xte; // Left side of the track -> negative XTE
500 }
501 leg_info.wp_name = pActivePoint->GetName().Truncate(maxName);
502 leg_info.arrival = m_bArrival;
503
504 json_leg_info.Notify(std::make_shared<ActiveLegDat>(leg_info), "");
505
506 // RMB
507 {
508 m_NMEA0183.TalkerID = "EC";
509 SENTENCE snt;
510 m_NMEA0183.Rmb.IsDataValid = bGPSValid ? NTrue : NFalse;
511 m_NMEA0183.Rmb.CrossTrackError = CurrentXTEToActivePoint;
512 m_NMEA0183.Rmb.DirectionToSteer = XTEDir < 0 ? Left : Right;
513 m_NMEA0183.Rmb.RangeToDestinationNauticalMiles = CurrentRngToActivePoint;
514 m_NMEA0183.Rmb.BearingToDestinationDegreesTrue = CurrentBrgToActivePoint;
515
516 if (pActivePoint->m_lat < 0.)
517 m_NMEA0183.Rmb.DestinationPosition.Latitude.Set(-pActivePoint->m_lat,
518 "S");
519 else
520 m_NMEA0183.Rmb.DestinationPosition.Latitude.Set(pActivePoint->m_lat, "N");
521
522 if (pActivePoint->m_lon < 0.)
523 m_NMEA0183.Rmb.DestinationPosition.Longitude.Set(-pActivePoint->m_lon,
524 "W");
525 else
526 m_NMEA0183.Rmb.DestinationPosition.Longitude.Set(pActivePoint->m_lon,
527 "E");
528
529 m_NMEA0183.Rmb.DestinationClosingVelocityKnots =
530 r_Sog * cos((r_Cog - CurrentBrgToActivePoint) * PI / 180.0);
531 m_NMEA0183.Rmb.IsArrivalCircleEntered = m_bArrival ? NTrue : NFalse;
532 m_NMEA0183.Rmb.FAAModeIndicator = bGPSValid ? "A" : "N";
533 // RMB is close to NMEA0183 length limit
534 // Restrict WP names further if necessary
535 int wp_len = maxName;
536 do {
537 m_NMEA0183.Rmb.To = pActivePoint->GetName().Truncate(wp_len);
538 m_NMEA0183.Rmb.From =
539 pActiveRouteSegmentBeginPoint->GetName().Truncate(wp_len);
540 m_NMEA0183.Rmb.Write(snt);
541 wp_len -= 1;
542 } while (snt.Sentence.size() > 82 && wp_len > 0);
543
544 BroadcastNMEA0183Message(snt.Sentence, *m_nmea_log, on_message_sent);
545 }
546
547 // RMC
548 {
549 m_NMEA0183.TalkerID = "EC";
550
551 SENTENCE snt;
552 m_NMEA0183.Rmc.IsDataValid = NTrue;
553 if (!bGPSValid) m_NMEA0183.Rmc.IsDataValid = NFalse;
554
555 if (gLat < 0.)
556 m_NMEA0183.Rmc.Position.Latitude.Set(-gLat, "S");
557 else
558 m_NMEA0183.Rmc.Position.Latitude.Set(gLat, "N");
559
560 if (gLon < 0.)
561 m_NMEA0183.Rmc.Position.Longitude.Set(-gLon, "W");
562 else
563 m_NMEA0183.Rmc.Position.Longitude.Set(gLon, "E");
564
565 m_NMEA0183.Rmc.SpeedOverGroundKnots = r_Sog;
566 m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue = r_Cog;
567
568 if (!std::isnan(gVar)) {
569 if (gVar < 0.) {
570 m_NMEA0183.Rmc.MagneticVariation = -gVar;
571 m_NMEA0183.Rmc.MagneticVariationDirection = West;
572 } else {
573 m_NMEA0183.Rmc.MagneticVariation = gVar;
574 m_NMEA0183.Rmc.MagneticVariationDirection = East;
575 }
576 } else
577 m_NMEA0183.Rmc.MagneticVariation =
578 361.; // A signal to NMEA converter, gVAR is unknown
579
580 // Send GPS time to autopilot if available else send local system time
581 if (!gRmcTime.IsEmpty() && !gRmcDate.IsEmpty()) {
582 m_NMEA0183.Rmc.UTCTime = gRmcTime;
583 m_NMEA0183.Rmc.Date = gRmcDate;
584 } else {
585 wxDateTime now = wxDateTime::Now();
586 wxDateTime utc = now.ToUTC();
587 wxString time = utc.Format("%H%M%S");
588 m_NMEA0183.Rmc.UTCTime = time;
589 wxString date = utc.Format("%d%m%y");
590 m_NMEA0183.Rmc.Date = date;
591 }
592
593 m_NMEA0183.Rmc.FAAModeIndicator = "A";
594 if (!bGPSValid) m_NMEA0183.Rmc.FAAModeIndicator = "N";
595
596 m_NMEA0183.Rmc.Write(snt);
597
598 BroadcastNMEA0183Message(snt.Sentence, *m_nmea_log, on_message_sent);
599 }
600
601 // APB
602 {
603 m_NMEA0183.TalkerID = "EC";
604
605 SENTENCE snt;
606
607 m_NMEA0183.Apb.IsLoranBlinkOK =
608 NTrue; // considered as "generic invalid fix" flag
609 if (!bGPSValid) m_NMEA0183.Apb.IsLoranBlinkOK = NFalse;
610
611 m_NMEA0183.Apb.IsLoranCCycleLockOK = NTrue;
612 if (!bGPSValid) m_NMEA0183.Apb.IsLoranCCycleLockOK = NFalse;
613
614 m_NMEA0183.Apb.CrossTrackErrorMagnitude = CurrentXTEToActivePoint;
615
616 if (XTEDir < 0)
617 m_NMEA0183.Apb.DirectionToSteer = Left;
618 else
619 m_NMEA0183.Apb.DirectionToSteer = Right;
620
621 m_NMEA0183.Apb.CrossTrackUnits = "N";
622
623 if (m_bArrival)
624 m_NMEA0183.Apb.IsArrivalCircleEntered = NTrue;
625 else
626 m_NMEA0183.Apb.IsArrivalCircleEntered = NFalse;
627
628 // We never pass the perpendicular, since we declare arrival before
629 // reaching this point
630 m_NMEA0183.Apb.IsPerpendicular = NFalse;
631
632 m_NMEA0183.Apb.To = pActivePoint->GetName().Truncate(maxName);
633
634 double brg1, dist1;
635 DistanceBearingMercator(pActivePoint->m_lat, pActivePoint->m_lon,
636 pActiveRouteSegmentBeginPoint->m_lat,
637 pActiveRouteSegmentBeginPoint->m_lon, &brg1,
638 &dist1);
639
640 if (g_bMagneticAPB && !std::isnan(gVar)) {
641 double brg1m =
642 ((brg1 - gVar) >= 0.) ? (brg1 - gVar) : (brg1 - gVar + 360.);
643 double bapm = ((CurrentBrgToActivePoint - gVar) >= 0.)
644 ? (CurrentBrgToActivePoint - gVar)
645 : (CurrentBrgToActivePoint - gVar + 360.);
646
647 m_NMEA0183.Apb.BearingOriginToDestination = brg1m;
648 m_NMEA0183.Apb.BearingOriginToDestinationUnits = "M";
649
650 m_NMEA0183.Apb.BearingPresentPositionToDestination = bapm;
651 m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = "M";
652
653 m_NMEA0183.Apb.HeadingToSteer = bapm;
654 m_NMEA0183.Apb.HeadingToSteerUnits = "M";
655 } else {
656 m_NMEA0183.Apb.BearingOriginToDestination = brg1;
657 m_NMEA0183.Apb.BearingOriginToDestinationUnits = "T";
658
659 m_NMEA0183.Apb.BearingPresentPositionToDestination =
660 CurrentBrgToActivePoint;
661 m_NMEA0183.Apb.BearingPresentPositionToDestinationUnits = "T";
662
663 m_NMEA0183.Apb.HeadingToSteer = CurrentBrgToActivePoint;
664 m_NMEA0183.Apb.HeadingToSteerUnits = "T";
665 }
666
667 m_NMEA0183.Apb.Write(snt);
668 BroadcastNMEA0183Message(snt.Sentence, *m_nmea_log, on_message_sent);
669 }
670
671 // XTE
672 {
673 m_NMEA0183.TalkerID = "EC";
674
675 SENTENCE snt;
676
677 m_NMEA0183.Xte.IsLoranBlinkOK =
678 NTrue; // considered as "generic invalid fix" flag
679 if (!bGPSValid) m_NMEA0183.Xte.IsLoranBlinkOK = NFalse;
680
681 m_NMEA0183.Xte.IsLoranCCycleLockOK = NTrue;
682 if (!bGPSValid) m_NMEA0183.Xte.IsLoranCCycleLockOK = NFalse;
683
684 m_NMEA0183.Xte.CrossTrackErrorDistance = CurrentXTEToActivePoint;
685
686 if (XTEDir < 0)
687 m_NMEA0183.Xte.DirectionToSteer = Left;
688 else
689 m_NMEA0183.Xte.DirectionToSteer = Right;
690
691 m_NMEA0183.Xte.CrossTrackUnits = "N";
692
693 m_NMEA0183.Xte.Write(snt);
694 BroadcastNMEA0183Message(snt.Sentence, *m_nmea_log, on_message_sent);
695 }
696#endif
697
698 return true;
699}
700
701bool Routeman::DoesRouteContainSharedPoints(Route *pRoute) {
702 if (pRoute) {
703 // walk the route, looking at each point to see if it is used by another
704 // route or is isolated
705 for (RoutePoint *prp : *pRoute->pRoutePointList) {
706 // check all other routes to see if this point appears in any other route
707 wxArrayPtrVoid *pRA = GetRouteArrayContaining(prp);
708 if (pRA) {
709 for (unsigned int ir = 0; ir < pRA->GetCount(); ir++) {
710 Route *pr = (Route *)pRA->Item(ir);
711 if (pr == pRoute)
712 continue; // self
713 else
714 return true;
715 }
716 delete pRA;
717 }
718 }
719
720 // Now walk the route again, looking for isolated type shared waypoints
721 for (RoutePoint *prp : *pRoute->pRoutePointList) {
722 if (prp->IsShared()) return true;
723 }
724 }
725
726 return false;
727}
728
729bool Routeman::DeleteTrack(Track *pTrack) {
730 if (pTrack && !pTrack->m_bIsInLayer) {
731 ::wxBeginBusyCursor();
732 /*
733 wxGenericProgressDialog *pprog = nullptr;
734
735 int count = pTrack->GetnPoints();
736 if (count > 10000) {
737 pprog = new wxGenericProgressDialog(
738 _("OpenCPN Track Delete"), "0/0", count, NULL,
739 wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_ELAPSED_TIME |
740 wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME);
741 pprog->SetSize(400, wxDefaultCoord);
742 pprog->Centre();
743 }
744 */
745
746 // Remove the track from associated lists
747 pSelect->DeleteAllSelectableTrackSegments(pTrack);
748 auto it = std::find(g_TrackList.begin(), g_TrackList.end(), pTrack);
749 if (it != g_TrackList.end()) {
750 g_TrackList.erase(it);
751 }
752 delete pTrack;
753
754 ::wxEndBusyCursor();
755
756 // delete pprog;
757 return true;
758 }
759 return false;
760}
761
763 if (pRoute) {
764 if (pRoute == pAISMOBRoute) {
765 if (!m_route_dlg_ctx.confirm_delete_ais_mob()) {
766 return false;
767 }
768 pAISMOBRoute = 0;
769 }
770 ::wxBeginBusyCursor();
771
772 if (GetpActiveRoute() == pRoute) DeactivateRoute();
773
774 if (pRoute->m_bIsInLayer) {
775 ::wxEndBusyCursor();
776 return false;
777 }
782 m_prop_dlg_ctx.hide(pRoute);
783
784 // if (nav_obj_changes) nav_obj_changes->DeleteConfigRoute(pRoute);
785
786 // Remove the route from associated lists
787 pSelect->DeleteAllSelectableRouteSegments(pRoute);
788 auto pos = std::find(pRouteList->begin(), pRouteList->end(), pRoute);
789 if (pos != pRouteList->end()) pRouteList->erase(pos);
790
791 m_route_dlg_ctx.route_mgr_dlg_update_list_ctrl();
792
793 // walk the route, tentatively deleting/marking points used only by this
794 // route
795 auto &list = pRoute->pRoutePointList;
796 auto pnode = list->begin();
797 while (pnode != list->end()) {
798 RoutePoint *prp = *pnode;
799
800 // check all other routes to see if this point appears in any other route
801 Route *pcontainer_route = FindRouteContainingWaypoint(prp);
802
803 if (pcontainer_route == NULL && prp->m_bIsInRoute) {
804 prp->m_bIsInRoute =
805 false; // Take this point out of this (and only) route
806 if (!prp->IsShared()) {
807 pSelect->DeleteSelectablePoint(prp, SELTYPE_ROUTEPOINT);
808
809 // Remove all instances of this point from the list.
810 auto pdnode = pnode;
811 while (pdnode != list->end()) {
812 pRoute->pRoutePointList->erase(pdnode);
813 pdnode = std::find(list->begin(), list->end(), prp);
814 }
815
816 pnode = list->end();
817 NavObj_dB::GetInstance().DeleteRoutePoint(prp);
818 delete prp;
819 } else {
820 prp->m_bIsolatedMark = true; // This has become an isolated mark
821 prp->SetShared(false); // and is no longer part of a route
822 NavObj_dB::GetInstance().UpdateRoutePoint(prp);
823 }
824 }
825 if (pnode != list->end())
826 ++pnode;
827 else
828 pnode = list->begin();
829 }
830
831 NavObj_dB::GetInstance().DeleteRoute(pRoute);
832 delete pRoute;
833
834 ::wxEndBusyCursor();
835 }
836 return true;
837}
838
839void Routeman::DeleteAllRoutes() {
840 ::wxBeginBusyCursor();
841
842 // Iterate on the RouteList
843 for (Route *proute : *pRouteList) {
844 if (proute == pAISMOBRoute) {
845 if (!m_route_dlg_ctx.confirm_delete_ais_mob()) {
846 return;
847 }
848 pAISMOBRoute = 0;
849 ::wxBeginBusyCursor();
850 }
851 if (proute->m_bIsInLayer) continue;
852
853 DeleteRoute(proute);
854 }
855
856 ::wxEndBusyCursor();
857}
858
859void Routeman::SetColorScheme(ColorScheme cs, double displayDPmm) {
860 // Re-Create the pens and colors
861
862 int scaled_line_width = g_route_line_width;
863 int track_scaled_line_width = g_track_line_width;
864 if (g_btouch) {
865 // 0.4 mm nominal, but not less than 2 pixel
866 double nominal_line_width_pix = wxMax(2.0, floor(displayDPmm * 0.4));
867
868 double sline_width = wxMax(nominal_line_width_pix, g_route_line_width);
869 sline_width *= g_ChartScaleFactorExp;
870 scaled_line_width = wxMax(sline_width, 2);
871
872 double tsline_width = wxMax(nominal_line_width_pix, g_track_line_width);
873 tsline_width *= g_ChartScaleFactorExp;
874 track_scaled_line_width = wxMax(tsline_width, 2);
875 }
876
877 m_pActiveRoutePointPen = wxThePenList->FindOrCreatePen(
878 wxColour(0, 0, 255), scaled_line_width, wxPENSTYLE_SOLID);
879 m_pRoutePointPen = wxThePenList->FindOrCreatePen(
880 wxColour(0, 0, 255), scaled_line_width, wxPENSTYLE_SOLID);
881
882 // Or in something like S-52 compliance
883
884 m_pRoutePen =
885 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("UINFB"),
886 scaled_line_width, wxPENSTYLE_SOLID);
887 m_pSelectedRoutePen =
888 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("UINFO"),
889 scaled_line_width, wxPENSTYLE_SOLID);
890 m_pActiveRoutePen =
891 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("UARTE"),
892 scaled_line_width, wxPENSTYLE_SOLID);
893 m_pTrackPen =
894 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("CHMGD"),
895 track_scaled_line_width, wxPENSTYLE_SOLID);
896 m_pRouteBrush = wxTheBrushList->FindOrCreateBrush(
897 m_route_dlg_ctx.get_global_colour("UINFB"), wxBRUSHSTYLE_SOLID);
898 m_pSelectedRouteBrush = wxTheBrushList->FindOrCreateBrush(
899 m_route_dlg_ctx.get_global_colour("UINFO"), wxBRUSHSTYLE_SOLID);
900 m_pActiveRouteBrush = wxTheBrushList->FindOrCreateBrush(
901 m_route_dlg_ctx.get_global_colour("PLRTE"), wxBRUSHSTYLE_SOLID);
902}
903
904wxString Routeman::GetRouteReverseMessage() {
905 return wxString(
906 _("Waypoints can be renamed to reflect the new order, the names will be "
907 "'001', '002' etc.\n\nDo you want to rename the waypoints?"));
908}
909
910wxString Routeman::GetRouteResequenceMessage() {
911 return wxString(
912 _("Waypoints will be renamed to reflect the natural order, the names "
913 "will be '001', '002' etc.\n\nDo you want to rename the waypoints?"));
914}
915
916Route *Routeman::FindRouteByGUID(const wxString &guid) {
917 for (Route *pRoute : *pRouteList) {
918 if (pRoute->m_GUID == guid) return pRoute;
919 }
920 return NULL;
921}
922
923Track *Routeman::FindTrackByGUID(const wxString &guid) {
924 for (Track *pTrack : g_TrackList) {
925 if (pTrack->m_GUID == guid) return pTrack;
926 }
927 return NULL;
928}
929
930void Routeman::ZeroCurrentXTEToActivePoint() {
931 // When zeroing XTE create a "virtual" waypoint at present position
932 if (pRouteActivatePoint) delete pRouteActivatePoint;
933 pRouteActivatePoint = new RoutePoint(gLat, gLon, wxString(""), wxString(""),
934 "", false); // Current location
935 pRouteActivatePoint->m_bShowName = false;
936
937 pActiveRouteSegmentBeginPoint = pRouteActivatePoint;
938 m_arrival_min = 1e6;
939}
940
941//--------------------------------------------------------------------------------
942// WayPointman Implementation
943//--------------------------------------------------------------------------------
944
945WayPointman::WayPointman(GlobalColourFunc color_func)
946 : m_get_global_colour(color_func) {
947 m_pWayPointList = new RoutePointList;
948
949 pmarkicon_image_list = NULL;
950
951 // ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
952 m_pIconArray = new ArrayOfMarkIcon;
953 m_pLegacyIconArray = NULL;
954 m_pExtendedIconArray = NULL;
955
956 m_cs = (ColorScheme)-1;
957
958 m_nGUID = 0;
959 m_iconListScale = -999.0;
960 m_iconListHeight = -1;
961}
962
963WayPointman::~WayPointman() {
964 // Two step here, since the RoutePoint dtor also touches the
965 // RoutePoint list.
966 // Copy the master RoutePoint list to a temporary list,
967 // then clear and delete objects from the temp list
968
969 RoutePointList temp_list;
970
971 for (RoutePoint *pr : *m_pWayPointList) {
972 temp_list.push_back(pr);
973 }
974
975 for (RoutePoint *rp : temp_list) delete rp;
976 temp_list.clear();
977
978 m_pWayPointList->clear();
979 delete m_pWayPointList;
980
981 for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
982 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(i);
983 delete pmi->piconBitmap;
984 delete pmi;
985 }
986
987 m_pIconArray->Clear();
988 delete m_pIconArray;
989
990 if (pmarkicon_image_list) pmarkicon_image_list->RemoveAll();
991 delete pmarkicon_image_list;
992 m_pLegacyIconArray->Clear();
993 delete m_pLegacyIconArray;
994 m_pExtendedIconArray->Clear();
995 delete m_pExtendedIconArray;
996}
997
999 if (!prp) return false;
1000
1001 m_pWayPointList->push_back(prp);
1002 prp->SetManagerListNode(prp);
1003
1004 return true;
1005}
1006
1008 if (!prp) return false;
1009
1010 auto *prpnode = (RoutePoint *)(prp->GetManagerListNode());
1011
1012 if (prpnode) {
1013 auto pos =
1014 std::find(m_pWayPointList->begin(), m_pWayPointList->end(), prpnode);
1015 if (pos != m_pWayPointList->end()) m_pWayPointList->erase(pos);
1016 } else {
1017 auto pos = std::find(m_pWayPointList->begin(), m_pWayPointList->end(), prp);
1018 if (pos != m_pWayPointList->end()) m_pWayPointList->erase(pos);
1019 }
1020
1021 prp->SetManagerListNode(NULL);
1022
1023 return true;
1024}
1025
1026wxImageList *WayPointman::Getpmarkicon_image_list(int nominal_height) {
1027 // Cached version available?
1028 if (pmarkicon_image_list && (nominal_height == m_iconListHeight)) {
1029 return pmarkicon_image_list;
1030 }
1031
1032 // Build an image list large enough
1033 if (NULL != pmarkicon_image_list) {
1034 pmarkicon_image_list->RemoveAll();
1035 delete pmarkicon_image_list;
1036 }
1037 pmarkicon_image_list = new wxImageList(nominal_height, nominal_height);
1038
1039 m_iconListHeight = nominal_height;
1040 m_bitmapSizeForList = nominal_height;
1041
1042 return pmarkicon_image_list;
1043}
1044
1045wxBitmap *WayPointman::CreateDimBitmap(wxBitmap *pBitmap, double factor) {
1046 wxImage img = pBitmap->ConvertToImage();
1047 int sx = img.GetWidth();
1048 int sy = img.GetHeight();
1049
1050 wxImage new_img(img);
1051
1052 for (int i = 0; i < sx; i++) {
1053 for (int j = 0; j < sy; j++) {
1054 if (!img.IsTransparent(i, j)) {
1055 new_img.SetRGB(i, j, (unsigned char)(img.GetRed(i, j) * factor),
1056 (unsigned char)(img.GetGreen(i, j) * factor),
1057 (unsigned char)(img.GetBlue(i, j) * factor));
1058 }
1059 }
1060 }
1061
1062 wxBitmap *pret = new wxBitmap(new_img);
1063
1064 return pret;
1065}
1066
1067wxImage WayPointman::CreateDimImage(wxImage &image, double factor) {
1068 int sx = image.GetWidth();
1069 int sy = image.GetHeight();
1070
1071 wxImage new_img(image);
1072
1073 for (int i = 0; i < sx; i++) {
1074 for (int j = 0; j < sy; j++) {
1075 if (!image.IsTransparent(i, j)) {
1076 new_img.SetRGB(i, j, (unsigned char)(image.GetRed(i, j) * factor),
1077 (unsigned char)(image.GetGreen(i, j) * factor),
1078 (unsigned char)(image.GetBlue(i, j) * factor));
1079 }
1080 }
1081 }
1082
1083 return wxImage(new_img);
1084}
1085
1086bool WayPointman::DoesIconExist(const wxString &icon_key) const {
1087 MarkIcon *pmi;
1088 unsigned int i;
1089
1090 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1091 pmi = (MarkIcon *)m_pIconArray->Item(i);
1092 if (pmi->icon_name.IsSameAs(icon_key)) return true;
1093 }
1094
1095 return false;
1096}
1097
1098wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) const {
1099 wxBitmap *pret = NULL;
1100 MarkIcon *pmi = NULL;
1101 unsigned int i;
1102
1103 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1104 pmi = (MarkIcon *)m_pIconArray->Item(i);
1105 if (pmi->icon_name.IsSameAs(icon_key)) break;
1106 }
1107
1108 if (i == m_pIconArray->GetCount()) // key not found
1109 {
1110 // find and return bitmap for "circle"
1111 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1112 pmi = (MarkIcon *)m_pIconArray->Item(i);
1113 // if( pmi->icon_name.IsSameAs( "circle" ) )
1114 // break;
1115 }
1116 }
1117
1118 if (i == m_pIconArray->GetCount()) // "circle" not found
1119 pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1120
1121 if (pmi) {
1122 if (pmi->piconBitmap)
1123 pret = pmi->piconBitmap;
1124 else {
1125 if (pmi->iconImage.IsOk()) {
1126 pmi->piconBitmap = new wxBitmap(pmi->iconImage);
1127 pret = pmi->piconBitmap;
1128 }
1129 }
1130 }
1131 return pret;
1132}
1133
1134bool WayPointman::GetIconPrescaled(const wxString &icon_key) const {
1135 MarkIcon *pmi = NULL;
1136 unsigned int i;
1137
1138 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1139 pmi = (MarkIcon *)m_pIconArray->Item(i);
1140 if (pmi->icon_name.IsSameAs(icon_key)) break;
1141 }
1142
1143 if (i == m_pIconArray->GetCount()) // key not found
1144 {
1145 // find and return bitmap for "circle"
1146 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1147 pmi = (MarkIcon *)m_pIconArray->Item(i);
1148 // if( pmi->icon_name.IsSameAs( "circle" ) )
1149 // break;
1150 }
1151 }
1152
1153 if (i == m_pIconArray->GetCount()) // "circle" not found
1154 pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1155
1156 if (pmi)
1157 return pmi->preScaled;
1158 else
1159 return false;
1160}
1161
1162wxBitmap WayPointman::GetIconBitmapForList(int index, int height) const {
1163 wxBitmap pret;
1164 MarkIcon *pmi;
1165
1166 if (index >= 0) {
1167 pmi = (MarkIcon *)m_pIconArray->Item(index);
1168 // Scale the icon to "list size" if necessary
1169 if (pmi->iconImage.GetHeight() != height) {
1170 int w = height;
1171 int h = height;
1172 int w0 = pmi->iconImage.GetWidth();
1173 int h0 = pmi->iconImage.GetHeight();
1174
1175 wxImage icon_resized = pmi->iconImage; // make a copy
1176 if (h0 <= h && w0 <= w) {
1177 icon_resized = pmi->iconImage.Resize(
1178 wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1179 } else {
1180 // rescale in one or two directions to avoid cropping, then resize to
1181 // fit to cell
1182 int h1 = h;
1183 int w1 = w;
1184 if (h0 > h)
1185 w1 = wxRound((double)w0 * ((double)h / (double)h0));
1186
1187 else if (w0 > w)
1188 h1 = wxRound((double)h0 * ((double)w / (double)w0));
1189
1190 icon_resized = pmi->iconImage.Rescale(w1, h1);
1191 icon_resized = pmi->iconImage.Resize(
1192 wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1193 }
1194
1195 pret = wxBitmap(icon_resized);
1196
1197 } else
1198 pret = wxBitmap(pmi->iconImage);
1199 }
1200
1201 return pret;
1202}
1203
1204wxString *WayPointman::GetIconDescription(int index) const {
1205 wxString *pret = NULL;
1206
1207 if (index >= 0) {
1208 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1209 pret = &pmi->icon_description;
1210 }
1211 return pret;
1212}
1213
1214wxString WayPointman::GetIconDescription(wxString icon_key) const {
1215 MarkIcon *pmi;
1216 unsigned int i;
1217
1218 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1219 pmi = (MarkIcon *)m_pIconArray->Item(i);
1220 if (pmi->icon_name.IsSameAs(icon_key))
1221 return wxString(pmi->icon_description);
1222 }
1223
1224 return "";
1225}
1226
1227wxString *WayPointman::GetIconKey(int index) const {
1228 wxString *pret = NULL;
1229
1230 if ((index >= 0) && ((unsigned int)index < m_pIconArray->GetCount())) {
1231 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1232 pret = &pmi->icon_name;
1233 }
1234 return pret;
1235}
1236
1237int WayPointman::GetIconIndex(const wxBitmap *pbm) const {
1238 unsigned int ret = 0;
1239 MarkIcon *pmi;
1240
1241 wxASSERT(m_pIconArray->GetCount() >= 1);
1242 for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
1243 pmi = (MarkIcon *)m_pIconArray->Item(i);
1244 if (pmi->piconBitmap == pbm) {
1245 ret = i;
1246 break;
1247 }
1248 }
1249
1250 return ret;
1251}
1252
1253int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) const {
1254 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(GetIconIndex(pbm));
1255
1256 // Build a "list - sized" image
1257 if (pmarkicon_image_list && !pmi->m_blistImageOK) {
1258 int h0 = pmi->iconImage.GetHeight();
1259 int w0 = pmi->iconImage.GetWidth();
1260 int h = m_bitmapSizeForList;
1261 int w = m_bitmapSizeForList;
1262
1263 wxImage icon_larger = pmi->iconImage; // make a copy
1264 if (h0 <= h && w0 <= w) {
1265 icon_larger = pmi->iconImage.Resize(
1266 wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1267 } else {
1268 // We want to maintain the aspect ratio of the original image, but need
1269 // the canvas to fit the fixed cell size rescale in one or two directions
1270 // to avoid cropping, then resize to fit to cell (Adds border/croops as
1271 // necessary)
1272 int h1 = h;
1273 int w1 = w;
1274 if (h0 > h)
1275 w1 = wxRound((double)w0 * ((double)h / (double)h0));
1276
1277 else if (w0 > w)
1278 h1 = wxRound((double)h0 * ((double)w / (double)w0));
1279
1280 icon_larger = pmi->iconImage.Rescale(w1, h1).Resize(
1281 wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1282 }
1283
1284 int index = pmarkicon_image_list->Add(wxBitmap(icon_larger));
1285
1286 // Create and replace "x-ed out" and "fixed visibility" icon,
1287 // Being careful to preserve (some) transparency
1288
1289 icon_larger.ConvertAlphaToMask(128);
1290
1291 unsigned char r, g, b;
1292 icon_larger.GetOrFindMaskColour(&r, &g, &b);
1293 wxColour unused_color(r, g, b);
1294
1295 // X-out
1296 wxBitmap xIcon(icon_larger);
1297
1298 wxBitmap xbmp(w, h, -1);
1299 wxMemoryDC mdc(xbmp);
1300 mdc.SetBackground(wxBrush(unused_color));
1301 mdc.Clear();
1302 mdc.DrawBitmap(xIcon, 0, 0);
1303 int xm = xbmp.GetWidth() / 2;
1304 int ym = xbmp.GetHeight() / 2;
1305 int dp = xm / 2;
1306 int width = wxMax(xm / 10, 2);
1307 wxPen red(m_get_global_colour("URED"), width);
1308 mdc.SetPen(red);
1309 mdc.DrawLine(xm - dp, ym - dp, xm + dp, ym + dp);
1310 mdc.DrawLine(xm - dp, ym + dp, xm + dp, ym - dp);
1311 mdc.SelectObject(wxNullBitmap);
1312
1313 wxMask *pmask = new wxMask(xbmp, unused_color);
1314 xbmp.SetMask(pmask);
1315
1316 pmarkicon_image_list->Add(xbmp);
1317
1318 // fixed Viz
1319 wxBitmap fIcon(icon_larger);
1320
1321 wxBitmap fbmp(w, h, -1);
1322 wxMemoryDC fmdc(fbmp);
1323 fmdc.SetBackground(wxBrush(unused_color));
1324 fmdc.Clear();
1325 fmdc.DrawBitmap(xIcon, 0, 0);
1326 xm = fbmp.GetWidth() / 2;
1327 ym = fbmp.GetHeight() / 2;
1328 dp = xm / 2;
1329 width = wxMax(xm / 10, 2);
1330 wxPen fred(m_get_global_colour("UGREN"), width);
1331 fmdc.SetPen(fred);
1332 fmdc.DrawLine(xm - dp, ym + dp, xm + dp, ym + dp);
1333 fmdc.SelectObject(wxNullBitmap);
1334
1335 wxMask *pfmask = new wxMask(fbmp, unused_color);
1336 fbmp.SetMask(pfmask);
1337
1338 pmarkicon_image_list->Add(fbmp);
1339
1340 pmi->m_blistImageOK = true;
1341 pmi->listIndex = index;
1342 }
1343
1344 return pmi->listIndex;
1345}
1346
1347int WayPointman::GetXIconImageListIndex(const wxBitmap *pbm) const {
1348 return GetIconImageListIndex(pbm) + 1;
1349}
1350
1351int WayPointman::GetFIconImageListIndex(const wxBitmap *pbm) const {
1352 return GetIconImageListIndex(pbm) + 2;
1353}
1354
1355// Create the unique identifier
1356wxString WayPointman::CreateGUID(RoutePoint *pRP) {
1357 return GpxDocument::GetUUID();
1358}
1359
1360RoutePoint *WayPointman::FindRoutePointByGUID(const wxString &guid) {
1361 for (RoutePoint *prp : *m_pWayPointList) {
1362 if (prp->m_GUID == guid) return (prp);
1363 }
1364 return NULL;
1365}
1366
1367RoutePoint *WayPointman::GetNearbyWaypoint(double lat, double lon,
1368 double radius_meters) {
1369 // Iterate on the RoutePoint list, checking distance
1370
1371 for (RoutePoint *pr : *m_pWayPointList) {
1372 double a = lat - pr->m_lat;
1373 double b = lon - pr->m_lon;
1374 double l = sqrt((a * a) + (b * b));
1375
1376 if ((l * 60. * 1852.) < radius_meters) return pr;
1377 }
1378 return NULL;
1379}
1380
1381RoutePoint *WayPointman::GetOtherNearbyWaypoint(double lat, double lon,
1382 double radius_meters,
1383 const wxString &guid) {
1384 // Iterate on the RoutePoint list, checking distance
1385
1386 for (RoutePoint *pr : *m_pWayPointList) {
1387 double a = lat - pr->m_lat;
1388 double b = lon - pr->m_lon;
1389 double l = sqrt((a * a) + (b * b));
1390
1391 if ((l * 60. * 1852.) < radius_meters)
1392 if (pr->m_GUID != guid) return pr;
1393 }
1394 return NULL;
1395}
1396
1397bool WayPointman::IsReallyVisible(RoutePoint *pWP) {
1398 if (pWP->m_bIsolatedMark) return pWP->IsVisible(); // isolated point
1399 for (Route *proute : *pRouteList) {
1400 if (proute && proute->pRoutePointList) {
1401 auto &list = proute->pRoutePointList;
1402 auto pos = std::find(list->begin(), list->end(), pWP);
1403 if (pos != list->end()) {
1404 if (proute->IsVisible()) return true;
1405 }
1406 }
1407 }
1408 if (pWP->IsShared()) // is not visible as part of route, but still exists as
1409 // a waypoint
1410 return pWP->IsVisible(); // so treat as isolated point
1411
1412 return false;
1413}
1414
1415void WayPointman::ClearRoutePointFonts() {
1416 // Iterate on the RoutePoint list, clearing Font pointers
1417 // This is typically done globally after a font switch
1418 for (RoutePoint *pr : *m_pWayPointList) {
1419 pr->m_pMarkFont = NULL;
1420 }
1421}
1422
1423bool WayPointman::SharedWptsExist() {
1424 for (RoutePoint *prp : *m_pWayPointList) {
1425 if (prp->IsShared() && (prp->m_bIsInRoute || prp == pAnchorWatchPoint1 ||
1426 prp == pAnchorWatchPoint2))
1427 return true;
1428 }
1429 return false;
1430}
1431
1432void WayPointman::DeleteAllWaypoints(bool b_delete_used) {
1433 // Iterate on the RoutePoint list, deleting all
1434 auto it = m_pWayPointList->begin();
1435 while (it != m_pWayPointList->end()) {
1436 RoutePoint *prp = *it;
1437 // if argument is false, then only delete non-route waypoints
1438 if (!prp->m_bIsInLayer && (prp->GetIconName() != "mob") &&
1439 ((b_delete_used && prp->IsShared()) ||
1440 ((!prp->m_bIsInRoute) && !(prp == pAnchorWatchPoint1) &&
1441 !(prp == pAnchorWatchPoint2)))) {
1442 DestroyWaypoint(prp);
1443 delete prp;
1444 it = m_pWayPointList->begin();
1445 } else
1446 ++it;
1447 }
1448 return;
1449}
1450
1451RoutePoint *WayPointman::FindWaypointByGuid(const std::string &guid) {
1452 for (RoutePoint *rp : *m_pWayPointList) {
1453 if (guid == rp->m_GUID) return rp;
1454 }
1455 return 0;
1456}
1457void WayPointman::DestroyWaypoint(RoutePoint *pRp, bool b_update_changeset) {
1458 if (pRp) {
1459 // Get a list of all routes containing this point
1460 // and remove the point from them all
1461 wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining(pRp);
1462 if (proute_array) {
1463 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1464 Route *pr = (Route *)proute_array->Item(ir);
1465
1466 /* FS#348
1467 if ( g_pRouteMan->GetpActiveRoute() == pr ) // Deactivate
1468 any route containing this point g_pRouteMan->DeactivateRoute();
1469 */
1470 pr->RemovePoint(pRp);
1471 }
1472
1473 // Scrub the routes, looking for one-point routes
1474 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1475 Route *pr = (Route *)proute_array->Item(ir);
1476 if (pr->GetnPoints() < 2) {
1478 }
1479 }
1480
1481 delete proute_array;
1482 }
1483
1484 // Now it is safe to delete the point
1485 NavObj_dB::GetInstance().DeleteRoutePoint(pRp);
1486
1487 pSelect->DeleteSelectableRoutePoint(pRp);
1488
1489 // The RoutePoint might be currently in use as an anchor watch point
1490 if (pRp == pAnchorWatchPoint1) pAnchorWatchPoint1 = NULL;
1491 if (pRp == pAnchorWatchPoint2) pAnchorWatchPoint2 = NULL;
1492
1493 RemoveRoutePoint(pRp);
1494 }
1495}
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:762
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:256
EventVar json_leg_info
Notified with a shared_ptr<ActiveLegDat>, leg info to all plugins.
Definition routeman.h:259
EventVar on_message_sent
Notified when a message available as GetString() is sent to garmin.
Definition routeman.h:262
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.
Definition routeman.cpp:998
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.