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
766 if (pRoute) {
767 if (pRoute == pAISMOBRoute) {
768 if (!m_route_dlg_ctx.confirm_delete_ais_mob()) {
769 return false;
770 }
771 pAISMOBRoute = 0;
772 }
773 ::wxBeginBusyCursor();
774
775 if (GetpActiveRoute() == pRoute) DeactivateRoute();
776
777 if (pRoute->m_bIsInLayer) {
778 ::wxEndBusyCursor();
779 return false;
780 }
785 m_prop_dlg_ctx.hide(pRoute);
786
787 // if (nav_obj_changes) nav_obj_changes->DeleteConfigRoute(pRoute);
788
789 // Remove the route from associated lists
790 pSelect->DeleteAllSelectableRouteSegments(pRoute);
791 auto pos = std::find(pRouteList->begin(), pRouteList->end(), pRoute);
792 if (pos != pRouteList->end()) pRouteList->erase(pos);
793
794 m_route_dlg_ctx.route_mgr_dlg_update_list_ctrl();
795
796 // walk the route, tentatively deleting/marking points used only by this
797 // route
798 auto &list = pRoute->pRoutePointList;
799 auto pnode = list->begin();
800 while (pnode != list->end()) {
801 RoutePoint *prp = *pnode;
802
803 // check all other routes to see if this point appears in any other route
804 Route *pcontainer_route = FindRouteContainingWaypoint(prp);
805
806 if (pcontainer_route == NULL && prp->m_bIsInRoute) {
807 prp->m_bIsInRoute =
808 false; // Take this point out of this (and only) route
809 if (!prp->IsShared()) {
810 pSelect->DeleteSelectablePoint(prp, SELTYPE_ROUTEPOINT);
811
812 // Remove all instances of this point from the list.
813 auto pdnode = pnode;
814 while (pdnode != list->end()) {
815 pRoute->pRoutePointList->erase(pdnode);
816 pdnode = std::find(list->begin(), list->end(), prp);
817 }
818
819 pnode = list->end();
820 NavObj_dB::GetInstance().DeleteRoutePoint(prp);
821 delete prp;
822 } else {
823 prp->m_bIsolatedMark = true; // This has become an isolated mark
824 prp->SetShared(false); // and is no longer part of a route
825 NavObj_dB::GetInstance().UpdateRoutePoint(prp);
826 }
827 }
828 if (pnode != list->end())
829 ++pnode;
830 else
831 pnode = list->begin();
832 }
833
834 NavObj_dB::GetInstance().DeleteRoute(pRoute);
835 delete pRoute;
836
837 ::wxEndBusyCursor();
838 }
839 return true;
840}
841
842void Routeman::DeleteAllRoutes() {
843 ::wxBeginBusyCursor();
844
845 std::vector<Route *> delete_list;
846
847 // Iterate on the RouteList and prepare list of deletion candidates
848 for (Route *proute : *pRouteList) {
849 if (proute == pAISMOBRoute) {
850 if (!m_route_dlg_ctx.confirm_delete_ais_mob()) {
851 continue;
852 }
853 pAISMOBRoute = 0;
854 }
855 if (proute->m_bIsInLayer) continue;
856
857 delete_list.push_back(proute);
858 }
859
860 // Delete the listed candidates
861
862 while (delete_list.size()) {
863 DeleteRoute(delete_list.back());
864 delete_list.pop_back();
865 }
866
867 ::wxEndBusyCursor();
868}
869
870void Routeman::SetColorScheme(ColorScheme cs, double displayDPmm) {
871 // Re-Create the pens and colors
872
873 int scaled_line_width = g_route_line_width;
874 int track_scaled_line_width = g_track_line_width;
875 if (g_btouch) {
876 // 0.4 mm nominal, but not less than 2 pixel
877 double nominal_line_width_pix = wxMax(2.0, floor(displayDPmm * 0.4));
878
879 double sline_width = wxMax(nominal_line_width_pix, g_route_line_width);
880 sline_width *= g_ChartScaleFactorExp;
881 scaled_line_width = wxMax(sline_width, 2);
882
883 double tsline_width = wxMax(nominal_line_width_pix, g_track_line_width);
884 tsline_width *= g_ChartScaleFactorExp;
885 track_scaled_line_width = wxMax(tsline_width, 2);
886 }
887
888 m_pActiveRoutePointPen = wxThePenList->FindOrCreatePen(
889 wxColour(0, 0, 255), scaled_line_width, wxPENSTYLE_SOLID);
890 m_pRoutePointPen = wxThePenList->FindOrCreatePen(
891 wxColour(0, 0, 255), scaled_line_width, wxPENSTYLE_SOLID);
892
893 // Or in something like S-52 compliance
894
895 m_pRoutePen =
896 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("UINFB"),
897 scaled_line_width, wxPENSTYLE_SOLID);
898 m_pSelectedRoutePen =
899 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("UINFO"),
900 scaled_line_width, wxPENSTYLE_SOLID);
901 m_pActiveRoutePen =
902 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("UARTE"),
903 scaled_line_width, wxPENSTYLE_SOLID);
904 m_pTrackPen =
905 wxThePenList->FindOrCreatePen(m_route_dlg_ctx.get_global_colour("CHMGD"),
906 track_scaled_line_width, wxPENSTYLE_SOLID);
907 m_pRouteBrush = wxTheBrushList->FindOrCreateBrush(
908 m_route_dlg_ctx.get_global_colour("UINFB"), wxBRUSHSTYLE_SOLID);
909 m_pSelectedRouteBrush = wxTheBrushList->FindOrCreateBrush(
910 m_route_dlg_ctx.get_global_colour("UINFO"), wxBRUSHSTYLE_SOLID);
911 m_pActiveRouteBrush = wxTheBrushList->FindOrCreateBrush(
912 m_route_dlg_ctx.get_global_colour("PLRTE"), wxBRUSHSTYLE_SOLID);
913}
914
915wxString Routeman::GetRouteReverseMessage() {
916 return wxString(
917 _("Waypoints can be renamed to reflect the new order, the names will be "
918 "'001', '002' etc.\n\nDo you want to rename the waypoints?"));
919}
920
921wxString Routeman::GetRouteResequenceMessage() {
922 return wxString(
923 _("Waypoints will be renamed to reflect the natural order, the names "
924 "will be '001', '002' etc.\n\nDo you want to rename the waypoints?"));
925}
926
927Route *Routeman::FindRouteByGUID(const wxString &guid) {
928 for (Route *pRoute : *pRouteList) {
929 if (pRoute->m_GUID == guid) return pRoute;
930 }
931 return NULL;
932}
933
934Track *Routeman::FindTrackByGUID(const wxString &guid) {
935 for (Track *pTrack : g_TrackList) {
936 if (pTrack->m_GUID == guid) return pTrack;
937 }
938 return NULL;
939}
940
941void Routeman::ZeroCurrentXTEToActivePoint() {
942 // When zeroing XTE create a "virtual" waypoint at present position
943 if (pRouteActivatePoint) delete pRouteActivatePoint;
944 pRouteActivatePoint = new RoutePoint(gLat, gLon, wxString(""), wxString(""),
945 "", false); // Current location
946 pRouteActivatePoint->m_bShowName = false;
947
948 pActiveRouteSegmentBeginPoint = pRouteActivatePoint;
949 m_arrival_min = 1e6;
950}
951
952//--------------------------------------------------------------------------------
953// WayPointman Implementation
954//--------------------------------------------------------------------------------
955
956WayPointman::WayPointman(GlobalColourFunc color_func)
957 : m_get_global_colour(color_func) {
958 m_pWayPointList = new RoutePointList;
959
960 pmarkicon_image_list = NULL;
961
962 // ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
963 m_pIconArray = new ArrayOfMarkIcon;
964 m_pLegacyIconArray = NULL;
965 m_pExtendedIconArray = NULL;
966
967 m_cs = (ColorScheme)-1;
968
969 m_nGUID = 0;
970 m_iconListScale = -999.0;
971 m_iconListHeight = -1;
972}
973
974WayPointman::~WayPointman() {
975 // Two step here, since the RoutePoint dtor also touches the
976 // RoutePoint list.
977 // Copy the master RoutePoint list to a temporary list,
978 // then clear and delete objects from the temp list
979
980 RoutePointList temp_list;
981
982 for (RoutePoint *pr : *m_pWayPointList) {
983 temp_list.push_back(pr);
984 }
985
986 for (RoutePoint *rp : temp_list) delete rp;
987 temp_list.clear();
988
989 m_pWayPointList->clear();
990 delete m_pWayPointList;
991
992 for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
993 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(i);
994 delete pmi->piconBitmap;
995 delete pmi;
996 }
997
998 m_pIconArray->Clear();
999 delete m_pIconArray;
1000
1001 if (pmarkicon_image_list) pmarkicon_image_list->RemoveAll();
1002 delete pmarkicon_image_list;
1003 if (m_pLegacyIconArray) {
1004 m_pLegacyIconArray->Clear();
1005 delete m_pLegacyIconArray;
1006 }
1007 if (m_pExtendedIconArray) {
1008 m_pExtendedIconArray->Clear();
1009 delete m_pExtendedIconArray;
1010 }
1011}
1012
1014 if (!prp) return false;
1015
1016 m_pWayPointList->push_back(prp);
1017 prp->SetManagerListNode(prp);
1018
1019 return true;
1020}
1021
1023 if (!prp) return false;
1024
1025 auto *prpnode = (RoutePoint *)(prp->GetManagerListNode());
1026
1027 if (prpnode) {
1028 auto pos =
1029 std::find(m_pWayPointList->begin(), m_pWayPointList->end(), prpnode);
1030 if (pos != m_pWayPointList->end()) m_pWayPointList->erase(pos);
1031 } else {
1032 auto pos = std::find(m_pWayPointList->begin(), m_pWayPointList->end(), prp);
1033 if (pos != m_pWayPointList->end()) m_pWayPointList->erase(pos);
1034 }
1035
1036 prp->SetManagerListNode(NULL);
1037
1038 return true;
1039}
1040
1041wxImageList *WayPointman::Getpmarkicon_image_list(int nominal_height) {
1042 // Cached version available?
1043 if (pmarkicon_image_list && (nominal_height == m_iconListHeight)) {
1044 return pmarkicon_image_list;
1045 }
1046
1047 // Build an image list large enough
1048 if (NULL != pmarkicon_image_list) {
1049 pmarkicon_image_list->RemoveAll();
1050 delete pmarkicon_image_list;
1051 }
1052 pmarkicon_image_list = new wxImageList(nominal_height, nominal_height);
1053
1054 m_iconListHeight = nominal_height;
1055 m_bitmapSizeForList = nominal_height;
1056
1057 return pmarkicon_image_list;
1058}
1059
1060wxBitmap *WayPointman::CreateDimBitmap(wxBitmap *pBitmap, double factor) {
1061 wxImage img = pBitmap->ConvertToImage();
1062 int sx = img.GetWidth();
1063 int sy = img.GetHeight();
1064
1065 wxImage new_img(img);
1066
1067 for (int i = 0; i < sx; i++) {
1068 for (int j = 0; j < sy; j++) {
1069 if (!img.IsTransparent(i, j)) {
1070 new_img.SetRGB(i, j, (unsigned char)(img.GetRed(i, j) * factor),
1071 (unsigned char)(img.GetGreen(i, j) * factor),
1072 (unsigned char)(img.GetBlue(i, j) * factor));
1073 }
1074 }
1075 }
1076
1077 wxBitmap *pret = new wxBitmap(new_img);
1078
1079 return pret;
1080}
1081
1082wxImage WayPointman::CreateDimImage(wxImage &image, double factor) {
1083 int sx = image.GetWidth();
1084 int sy = image.GetHeight();
1085
1086 wxImage new_img(image);
1087
1088 for (int i = 0; i < sx; i++) {
1089 for (int j = 0; j < sy; j++) {
1090 if (!image.IsTransparent(i, j)) {
1091 new_img.SetRGB(i, j, (unsigned char)(image.GetRed(i, j) * factor),
1092 (unsigned char)(image.GetGreen(i, j) * factor),
1093 (unsigned char)(image.GetBlue(i, j) * factor));
1094 }
1095 }
1096 }
1097
1098 return wxImage(new_img);
1099}
1100
1101bool WayPointman::DoesIconExist(const wxString &icon_key) const {
1102 MarkIcon *pmi;
1103 unsigned int i;
1104
1105 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1106 pmi = (MarkIcon *)m_pIconArray->Item(i);
1107 if (pmi->icon_name.IsSameAs(icon_key)) return true;
1108 }
1109
1110 return false;
1111}
1112
1113wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) const {
1114 wxBitmap *pret = NULL;
1115 MarkIcon *pmi = NULL;
1116 unsigned int i;
1117
1118 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1119 pmi = (MarkIcon *)m_pIconArray->Item(i);
1120 if (pmi->icon_name.IsSameAs(icon_key)) break;
1121 }
1122
1123 if (i == m_pIconArray->GetCount()) // key not found
1124 {
1125 // find and return bitmap for "circle"
1126 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1127 pmi = (MarkIcon *)m_pIconArray->Item(i);
1128 // if( pmi->icon_name.IsSameAs( "circle" ) )
1129 // break;
1130 }
1131 }
1132
1133 if (i == m_pIconArray->GetCount()) // "circle" not found
1134 pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1135
1136 if (pmi) {
1137 if (pmi->piconBitmap)
1138 pret = pmi->piconBitmap;
1139 else {
1140 if (pmi->iconImage.IsOk()) {
1141 pmi->piconBitmap = new wxBitmap(pmi->iconImage);
1142 pret = pmi->piconBitmap;
1143 }
1144 }
1145 }
1146 return pret;
1147}
1148
1149bool WayPointman::GetIconPrescaled(const wxString &icon_key) const {
1150 MarkIcon *pmi = NULL;
1151 unsigned int i;
1152
1153 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1154 pmi = (MarkIcon *)m_pIconArray->Item(i);
1155 if (pmi->icon_name.IsSameAs(icon_key)) break;
1156 }
1157
1158 if (i == m_pIconArray->GetCount()) // key not found
1159 {
1160 // find and return bitmap for "circle"
1161 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1162 pmi = (MarkIcon *)m_pIconArray->Item(i);
1163 // if( pmi->icon_name.IsSameAs( "circle" ) )
1164 // break;
1165 }
1166 }
1167
1168 if (i == m_pIconArray->GetCount()) // "circle" not found
1169 pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1170
1171 if (pmi)
1172 return pmi->preScaled;
1173 else
1174 return false;
1175}
1176
1177wxBitmap WayPointman::GetIconBitmapForList(int index, int height) const {
1178 wxBitmap pret;
1179 MarkIcon *pmi;
1180
1181 if (index >= 0) {
1182 pmi = (MarkIcon *)m_pIconArray->Item(index);
1183 // Scale the icon to "list size" if necessary
1184 if (pmi->iconImage.GetHeight() != height) {
1185 int w = height;
1186 int h = height;
1187 int w0 = pmi->iconImage.GetWidth();
1188 int h0 = pmi->iconImage.GetHeight();
1189
1190 wxImage icon_resized = pmi->iconImage; // make a copy
1191 if (h0 <= h && w0 <= w) {
1192 icon_resized = pmi->iconImage.Resize(
1193 wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1194 } else {
1195 // rescale in one or two directions to avoid cropping, then resize to
1196 // fit to cell
1197 int h1 = h;
1198 int w1 = w;
1199 if (h0 > h)
1200 w1 = wxRound((double)w0 * ((double)h / (double)h0));
1201
1202 else if (w0 > w)
1203 h1 = wxRound((double)h0 * ((double)w / (double)w0));
1204
1205 icon_resized = pmi->iconImage.Rescale(w1, h1);
1206 icon_resized = pmi->iconImage.Resize(
1207 wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1208 }
1209
1210 pret = wxBitmap(icon_resized);
1211
1212 } else
1213 pret = wxBitmap(pmi->iconImage);
1214 }
1215
1216 return pret;
1217}
1218
1219wxString *WayPointman::GetIconDescription(int index) const {
1220 wxString *pret = NULL;
1221
1222 if (index >= 0) {
1223 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1224 pret = &pmi->icon_description;
1225 }
1226 return pret;
1227}
1228
1229wxString WayPointman::GetIconDescription(wxString icon_key) const {
1230 MarkIcon *pmi;
1231 unsigned int i;
1232
1233 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1234 pmi = (MarkIcon *)m_pIconArray->Item(i);
1235 if (pmi->icon_name.IsSameAs(icon_key))
1236 return wxString(pmi->icon_description);
1237 }
1238
1239 return "";
1240}
1241
1242wxString *WayPointman::GetIconKey(int index) const {
1243 wxString *pret = NULL;
1244
1245 if ((index >= 0) && ((unsigned int)index < m_pIconArray->GetCount())) {
1246 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1247 pret = &pmi->icon_name;
1248 }
1249 return pret;
1250}
1251
1252int WayPointman::GetIconIndex(const wxBitmap *pbm) const {
1253 unsigned int ret = 0;
1254 MarkIcon *pmi;
1255
1256 wxASSERT(m_pIconArray->GetCount() >= 1);
1257 for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
1258 pmi = (MarkIcon *)m_pIconArray->Item(i);
1259 if (pmi->piconBitmap == pbm) {
1260 ret = i;
1261 break;
1262 }
1263 }
1264
1265 return ret;
1266}
1267
1268int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) const {
1269 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(GetIconIndex(pbm));
1270
1271 // Build a "list - sized" image
1272 if (pmarkicon_image_list && !pmi->m_blistImageOK) {
1273 int h0 = pmi->iconImage.GetHeight();
1274 int w0 = pmi->iconImage.GetWidth();
1275 int h = m_bitmapSizeForList;
1276 int w = m_bitmapSizeForList;
1277
1278 wxImage icon_larger = pmi->iconImage; // make a copy
1279 if (h0 <= h && w0 <= w) {
1280 icon_larger = pmi->iconImage.Resize(
1281 wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1282 } else {
1283 // We want to maintain the aspect ratio of the original image, but need
1284 // the canvas to fit the fixed cell size rescale in one or two directions
1285 // to avoid cropping, then resize to fit to cell (Adds border/croops as
1286 // necessary)
1287 int h1 = h;
1288 int w1 = w;
1289 if (h0 > h)
1290 w1 = wxRound((double)w0 * ((double)h / (double)h0));
1291
1292 else if (w0 > w)
1293 h1 = wxRound((double)h0 * ((double)w / (double)w0));
1294
1295 icon_larger = pmi->iconImage.Rescale(w1, h1).Resize(
1296 wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1297 }
1298
1299 int index = pmarkicon_image_list->Add(wxBitmap(icon_larger));
1300
1301 // Create and replace "x-ed out" and "fixed visibility" icon,
1302 // Being careful to preserve (some) transparency
1303
1304 icon_larger.ConvertAlphaToMask(128);
1305
1306 unsigned char r, g, b;
1307 icon_larger.GetOrFindMaskColour(&r, &g, &b);
1308 wxColour unused_color(r, g, b);
1309
1310 // X-out
1311 wxBitmap xIcon(icon_larger);
1312
1313 wxBitmap xbmp(w, h, -1);
1314 wxMemoryDC mdc(xbmp);
1315 mdc.SetBackground(wxBrush(unused_color));
1316 mdc.Clear();
1317 mdc.DrawBitmap(xIcon, 0, 0);
1318 int xm = xbmp.GetWidth() / 2;
1319 int ym = xbmp.GetHeight() / 2;
1320 int dp = xm / 2;
1321 int width = wxMax(xm / 10, 2);
1322 wxPen red(m_get_global_colour("URED"), width);
1323 mdc.SetPen(red);
1324 mdc.DrawLine(xm - dp, ym - dp, xm + dp, ym + dp);
1325 mdc.DrawLine(xm - dp, ym + dp, xm + dp, ym - dp);
1326 mdc.SelectObject(wxNullBitmap);
1327
1328 wxMask *pmask = new wxMask(xbmp, unused_color);
1329 xbmp.SetMask(pmask);
1330
1331 pmarkicon_image_list->Add(xbmp);
1332
1333 // fixed Viz
1334 wxBitmap fIcon(icon_larger);
1335
1336 wxBitmap fbmp(w, h, -1);
1337 wxMemoryDC fmdc(fbmp);
1338 fmdc.SetBackground(wxBrush(unused_color));
1339 fmdc.Clear();
1340 fmdc.DrawBitmap(xIcon, 0, 0);
1341 xm = fbmp.GetWidth() / 2;
1342 ym = fbmp.GetHeight() / 2;
1343 dp = xm / 2;
1344 width = wxMax(xm / 10, 2);
1345 wxPen fred(m_get_global_colour("UGREN"), width);
1346 fmdc.SetPen(fred);
1347 fmdc.DrawLine(xm - dp, ym + dp, xm + dp, ym + dp);
1348 fmdc.SelectObject(wxNullBitmap);
1349
1350 wxMask *pfmask = new wxMask(fbmp, unused_color);
1351 fbmp.SetMask(pfmask);
1352
1353 pmarkicon_image_list->Add(fbmp);
1354
1355 pmi->m_blistImageOK = true;
1356 pmi->listIndex = index;
1357 }
1358
1359 return pmi->listIndex;
1360}
1361
1362int WayPointman::GetXIconImageListIndex(const wxBitmap *pbm) const {
1363 return GetIconImageListIndex(pbm) + 1;
1364}
1365
1366int WayPointman::GetFIconImageListIndex(const wxBitmap *pbm) const {
1367 return GetIconImageListIndex(pbm) + 2;
1368}
1369
1370// Create the unique identifier
1371wxString WayPointman::CreateGUID(RoutePoint *pRP) {
1372 return GpxDocument::GetUUID();
1373}
1374
1375RoutePoint *WayPointman::FindRoutePointByGUID(const wxString &guid) {
1376 for (RoutePoint *prp : *m_pWayPointList) {
1377 if (prp->m_GUID == guid) return (prp);
1378 }
1379 return NULL;
1380}
1381
1382RoutePoint *WayPointman::GetNearbyWaypoint(double lat, double lon,
1383 double radius_meters) {
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) return pr;
1392 }
1393 return NULL;
1394}
1395
1396RoutePoint *WayPointman::GetOtherNearbyWaypoint(double lat, double lon,
1397 double radius_meters,
1398 const wxString &guid) {
1399 // Iterate on the RoutePoint list, checking distance
1400
1401 for (RoutePoint *pr : *m_pWayPointList) {
1402 double a = lat - pr->m_lat;
1403 double b = lon - pr->m_lon;
1404 double l = sqrt((a * a) + (b * b));
1405
1406 if ((l * 60. * 1852.) < radius_meters)
1407 if (pr->m_GUID != guid) return pr;
1408 }
1409 return NULL;
1410}
1411
1412bool WayPointman::IsReallyVisible(RoutePoint *pWP) {
1413 if (pWP->m_bIsolatedMark) return pWP->IsVisible(); // isolated point
1414 for (Route *proute : *pRouteList) {
1415 if (proute && proute->pRoutePointList) {
1416 auto &list = proute->pRoutePointList;
1417 auto pos = std::find(list->begin(), list->end(), pWP);
1418 if (pos != list->end()) {
1419 if (proute->IsVisible()) return true;
1420 }
1421 }
1422 }
1423 if (pWP->IsShared()) // is not visible as part of route, but still exists as
1424 // a waypoint
1425 return pWP->IsVisible(); // so treat as isolated point
1426
1427 return false;
1428}
1429
1430void WayPointman::ClearRoutePointFonts() {
1431 // Iterate on the RoutePoint list, clearing Font pointers
1432 // This is typically done globally after a font switch
1433 for (RoutePoint *pr : *m_pWayPointList) {
1434 pr->m_pMarkFont = NULL;
1435 }
1436}
1437
1438bool WayPointman::SharedWptsExist() {
1439 for (RoutePoint *prp : *m_pWayPointList) {
1440 if (prp->IsShared() && (prp->m_bIsInRoute || prp == pAnchorWatchPoint1 ||
1441 prp == pAnchorWatchPoint2))
1442 return true;
1443 }
1444 return false;
1445}
1446
1447void WayPointman::DeleteAllWaypoints(bool b_delete_used) {
1448 // Iterate on the RoutePoint list, deleting all
1449 auto it = m_pWayPointList->begin();
1450 while (it != m_pWayPointList->end()) {
1451 RoutePoint *prp = *it;
1452 // if argument is false, then only delete non-route waypoints
1453 if (!prp->m_bIsInLayer && (prp->GetIconName() != "mob") &&
1454 ((b_delete_used && prp->IsShared()) ||
1455 ((!prp->m_bIsInRoute) && !(prp == pAnchorWatchPoint1) &&
1456 !(prp == pAnchorWatchPoint2)))) {
1457 DestroyWaypoint(prp);
1458 delete prp;
1459 it = m_pWayPointList->begin();
1460 } else
1461 ++it;
1462 }
1463 return;
1464}
1465
1466RoutePoint *WayPointman::FindWaypointByGuid(const std::string &guid) {
1467 for (RoutePoint *rp : *m_pWayPointList) {
1468 if (guid == rp->m_GUID) return rp;
1469 }
1470 return 0;
1471}
1472void WayPointman::DestroyWaypoint(RoutePoint *pRp, bool b_update_changeset) {
1473 if (pRp) {
1474 // Get a list of all routes containing this point
1475 // and remove the point from them all
1476 wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining(pRp);
1477 if (proute_array) {
1478 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1479 Route *pr = (Route *)proute_array->Item(ir);
1480
1481 /* FS#348
1482 if ( g_pRouteMan->GetpActiveRoute() == pr ) // Deactivate
1483 any route containing this point g_pRouteMan->DeactivateRoute();
1484 */
1485 pr->RemovePoint(pRp);
1486 }
1487
1488 // Scrub the routes, looking for one-point routes
1489 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1490 Route *pr = (Route *)proute_array->Item(ir);
1491 if (pr->GetnPoints() < 2) {
1493 }
1494 }
1495
1496 delete proute_array;
1497 }
1498
1499 // Now it is safe to delete the point
1500 NavObj_dB::GetInstance().DeleteRoutePoint(pRp);
1501
1502 pSelect->DeleteSelectableRoutePoint(pRp);
1503
1504 // The RoutePoint might be currently in use as an anchor watch point
1505 if (pRp == pAnchorWatchPoint1) pAnchorWatchPoint1 = NULL;
1506 if (pRp == pAnchorWatchPoint2) pAnchorWatchPoint2 = NULL;
1507
1508 RemoveRoutePoint(pRp);
1509 }
1510}
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:765
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.
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.