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 if (m_pLegacyIconArray) {
993 m_pLegacyIconArray->Clear();
994 delete m_pLegacyIconArray;
995 }
996 if (m_pExtendedIconArray) {
997 m_pExtendedIconArray->Clear();
998 delete m_pExtendedIconArray;
999 }
1000}
1001
1003 if (!prp) return false;
1004
1005 m_pWayPointList->push_back(prp);
1006 prp->SetManagerListNode(prp);
1007
1008 return true;
1009}
1010
1012 if (!prp) return false;
1013
1014 auto *prpnode = (RoutePoint *)(prp->GetManagerListNode());
1015
1016 if (prpnode) {
1017 auto pos =
1018 std::find(m_pWayPointList->begin(), m_pWayPointList->end(), prpnode);
1019 if (pos != m_pWayPointList->end()) m_pWayPointList->erase(pos);
1020 } else {
1021 auto pos = std::find(m_pWayPointList->begin(), m_pWayPointList->end(), prp);
1022 if (pos != m_pWayPointList->end()) m_pWayPointList->erase(pos);
1023 }
1024
1025 prp->SetManagerListNode(NULL);
1026
1027 return true;
1028}
1029
1030wxImageList *WayPointman::Getpmarkicon_image_list(int nominal_height) {
1031 // Cached version available?
1032 if (pmarkicon_image_list && (nominal_height == m_iconListHeight)) {
1033 return pmarkicon_image_list;
1034 }
1035
1036 // Build an image list large enough
1037 if (NULL != pmarkicon_image_list) {
1038 pmarkicon_image_list->RemoveAll();
1039 delete pmarkicon_image_list;
1040 }
1041 pmarkicon_image_list = new wxImageList(nominal_height, nominal_height);
1042
1043 m_iconListHeight = nominal_height;
1044 m_bitmapSizeForList = nominal_height;
1045
1046 return pmarkicon_image_list;
1047}
1048
1049wxBitmap *WayPointman::CreateDimBitmap(wxBitmap *pBitmap, double factor) {
1050 wxImage img = pBitmap->ConvertToImage();
1051 int sx = img.GetWidth();
1052 int sy = img.GetHeight();
1053
1054 wxImage new_img(img);
1055
1056 for (int i = 0; i < sx; i++) {
1057 for (int j = 0; j < sy; j++) {
1058 if (!img.IsTransparent(i, j)) {
1059 new_img.SetRGB(i, j, (unsigned char)(img.GetRed(i, j) * factor),
1060 (unsigned char)(img.GetGreen(i, j) * factor),
1061 (unsigned char)(img.GetBlue(i, j) * factor));
1062 }
1063 }
1064 }
1065
1066 wxBitmap *pret = new wxBitmap(new_img);
1067
1068 return pret;
1069}
1070
1071wxImage WayPointman::CreateDimImage(wxImage &image, double factor) {
1072 int sx = image.GetWidth();
1073 int sy = image.GetHeight();
1074
1075 wxImage new_img(image);
1076
1077 for (int i = 0; i < sx; i++) {
1078 for (int j = 0; j < sy; j++) {
1079 if (!image.IsTransparent(i, j)) {
1080 new_img.SetRGB(i, j, (unsigned char)(image.GetRed(i, j) * factor),
1081 (unsigned char)(image.GetGreen(i, j) * factor),
1082 (unsigned char)(image.GetBlue(i, j) * factor));
1083 }
1084 }
1085 }
1086
1087 return wxImage(new_img);
1088}
1089
1090bool WayPointman::DoesIconExist(const wxString &icon_key) const {
1091 MarkIcon *pmi;
1092 unsigned int i;
1093
1094 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1095 pmi = (MarkIcon *)m_pIconArray->Item(i);
1096 if (pmi->icon_name.IsSameAs(icon_key)) return true;
1097 }
1098
1099 return false;
1100}
1101
1102wxBitmap *WayPointman::GetIconBitmap(const wxString &icon_key) const {
1103 wxBitmap *pret = NULL;
1104 MarkIcon *pmi = NULL;
1105 unsigned int i;
1106
1107 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1108 pmi = (MarkIcon *)m_pIconArray->Item(i);
1109 if (pmi->icon_name.IsSameAs(icon_key)) break;
1110 }
1111
1112 if (i == m_pIconArray->GetCount()) // key not found
1113 {
1114 // find and return bitmap for "circle"
1115 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1116 pmi = (MarkIcon *)m_pIconArray->Item(i);
1117 // if( pmi->icon_name.IsSameAs( "circle" ) )
1118 // break;
1119 }
1120 }
1121
1122 if (i == m_pIconArray->GetCount()) // "circle" not found
1123 pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1124
1125 if (pmi) {
1126 if (pmi->piconBitmap)
1127 pret = pmi->piconBitmap;
1128 else {
1129 if (pmi->iconImage.IsOk()) {
1130 pmi->piconBitmap = new wxBitmap(pmi->iconImage);
1131 pret = pmi->piconBitmap;
1132 }
1133 }
1134 }
1135 return pret;
1136}
1137
1138bool WayPointman::GetIconPrescaled(const wxString &icon_key) const {
1139 MarkIcon *pmi = NULL;
1140 unsigned int i;
1141
1142 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1143 pmi = (MarkIcon *)m_pIconArray->Item(i);
1144 if (pmi->icon_name.IsSameAs(icon_key)) break;
1145 }
1146
1147 if (i == m_pIconArray->GetCount()) // key not found
1148 {
1149 // find and return bitmap for "circle"
1150 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1151 pmi = (MarkIcon *)m_pIconArray->Item(i);
1152 // if( pmi->icon_name.IsSameAs( "circle" ) )
1153 // break;
1154 }
1155 }
1156
1157 if (i == m_pIconArray->GetCount()) // "circle" not found
1158 pmi = (MarkIcon *)m_pIconArray->Item(0); // use item 0
1159
1160 if (pmi)
1161 return pmi->preScaled;
1162 else
1163 return false;
1164}
1165
1166wxBitmap WayPointman::GetIconBitmapForList(int index, int height) const {
1167 wxBitmap pret;
1168 MarkIcon *pmi;
1169
1170 if (index >= 0) {
1171 pmi = (MarkIcon *)m_pIconArray->Item(index);
1172 // Scale the icon to "list size" if necessary
1173 if (pmi->iconImage.GetHeight() != height) {
1174 int w = height;
1175 int h = height;
1176 int w0 = pmi->iconImage.GetWidth();
1177 int h0 = pmi->iconImage.GetHeight();
1178
1179 wxImage icon_resized = pmi->iconImage; // make a copy
1180 if (h0 <= h && w0 <= w) {
1181 icon_resized = pmi->iconImage.Resize(
1182 wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1183 } else {
1184 // rescale in one or two directions to avoid cropping, then resize to
1185 // fit to cell
1186 int h1 = h;
1187 int w1 = w;
1188 if (h0 > h)
1189 w1 = wxRound((double)w0 * ((double)h / (double)h0));
1190
1191 else if (w0 > w)
1192 h1 = wxRound((double)h0 * ((double)w / (double)w0));
1193
1194 icon_resized = pmi->iconImage.Rescale(w1, h1);
1195 icon_resized = pmi->iconImage.Resize(
1196 wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1197 }
1198
1199 pret = wxBitmap(icon_resized);
1200
1201 } else
1202 pret = wxBitmap(pmi->iconImage);
1203 }
1204
1205 return pret;
1206}
1207
1208wxString *WayPointman::GetIconDescription(int index) const {
1209 wxString *pret = NULL;
1210
1211 if (index >= 0) {
1212 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1213 pret = &pmi->icon_description;
1214 }
1215 return pret;
1216}
1217
1218wxString WayPointman::GetIconDescription(wxString icon_key) const {
1219 MarkIcon *pmi;
1220 unsigned int i;
1221
1222 for (i = 0; i < m_pIconArray->GetCount(); i++) {
1223 pmi = (MarkIcon *)m_pIconArray->Item(i);
1224 if (pmi->icon_name.IsSameAs(icon_key))
1225 return wxString(pmi->icon_description);
1226 }
1227
1228 return "";
1229}
1230
1231wxString *WayPointman::GetIconKey(int index) const {
1232 wxString *pret = NULL;
1233
1234 if ((index >= 0) && ((unsigned int)index < m_pIconArray->GetCount())) {
1235 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(index);
1236 pret = &pmi->icon_name;
1237 }
1238 return pret;
1239}
1240
1241int WayPointman::GetIconIndex(const wxBitmap *pbm) const {
1242 unsigned int ret = 0;
1243 MarkIcon *pmi;
1244
1245 wxASSERT(m_pIconArray->GetCount() >= 1);
1246 for (unsigned int i = 0; i < m_pIconArray->GetCount(); i++) {
1247 pmi = (MarkIcon *)m_pIconArray->Item(i);
1248 if (pmi->piconBitmap == pbm) {
1249 ret = i;
1250 break;
1251 }
1252 }
1253
1254 return ret;
1255}
1256
1257int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) const {
1258 MarkIcon *pmi = (MarkIcon *)m_pIconArray->Item(GetIconIndex(pbm));
1259
1260 // Build a "list - sized" image
1261 if (pmarkicon_image_list && !pmi->m_blistImageOK) {
1262 int h0 = pmi->iconImage.GetHeight();
1263 int w0 = pmi->iconImage.GetWidth();
1264 int h = m_bitmapSizeForList;
1265 int w = m_bitmapSizeForList;
1266
1267 wxImage icon_larger = pmi->iconImage; // make a copy
1268 if (h0 <= h && w0 <= w) {
1269 icon_larger = pmi->iconImage.Resize(
1270 wxSize(w, h), wxPoint(w / 2 - w0 / 2, h / 2 - h0 / 2));
1271 } else {
1272 // We want to maintain the aspect ratio of the original image, but need
1273 // the canvas to fit the fixed cell size rescale in one or two directions
1274 // to avoid cropping, then resize to fit to cell (Adds border/croops as
1275 // necessary)
1276 int h1 = h;
1277 int w1 = w;
1278 if (h0 > h)
1279 w1 = wxRound((double)w0 * ((double)h / (double)h0));
1280
1281 else if (w0 > w)
1282 h1 = wxRound((double)h0 * ((double)w / (double)w0));
1283
1284 icon_larger = pmi->iconImage.Rescale(w1, h1).Resize(
1285 wxSize(w, h), wxPoint(w / 2 - w1 / 2, h / 2 - h1 / 2));
1286 }
1287
1288 int index = pmarkicon_image_list->Add(wxBitmap(icon_larger));
1289
1290 // Create and replace "x-ed out" and "fixed visibility" icon,
1291 // Being careful to preserve (some) transparency
1292
1293 icon_larger.ConvertAlphaToMask(128);
1294
1295 unsigned char r, g, b;
1296 icon_larger.GetOrFindMaskColour(&r, &g, &b);
1297 wxColour unused_color(r, g, b);
1298
1299 // X-out
1300 wxBitmap xIcon(icon_larger);
1301
1302 wxBitmap xbmp(w, h, -1);
1303 wxMemoryDC mdc(xbmp);
1304 mdc.SetBackground(wxBrush(unused_color));
1305 mdc.Clear();
1306 mdc.DrawBitmap(xIcon, 0, 0);
1307 int xm = xbmp.GetWidth() / 2;
1308 int ym = xbmp.GetHeight() / 2;
1309 int dp = xm / 2;
1310 int width = wxMax(xm / 10, 2);
1311 wxPen red(m_get_global_colour("URED"), width);
1312 mdc.SetPen(red);
1313 mdc.DrawLine(xm - dp, ym - dp, xm + dp, ym + dp);
1314 mdc.DrawLine(xm - dp, ym + dp, xm + dp, ym - dp);
1315 mdc.SelectObject(wxNullBitmap);
1316
1317 wxMask *pmask = new wxMask(xbmp, unused_color);
1318 xbmp.SetMask(pmask);
1319
1320 pmarkicon_image_list->Add(xbmp);
1321
1322 // fixed Viz
1323 wxBitmap fIcon(icon_larger);
1324
1325 wxBitmap fbmp(w, h, -1);
1326 wxMemoryDC fmdc(fbmp);
1327 fmdc.SetBackground(wxBrush(unused_color));
1328 fmdc.Clear();
1329 fmdc.DrawBitmap(xIcon, 0, 0);
1330 xm = fbmp.GetWidth() / 2;
1331 ym = fbmp.GetHeight() / 2;
1332 dp = xm / 2;
1333 width = wxMax(xm / 10, 2);
1334 wxPen fred(m_get_global_colour("UGREN"), width);
1335 fmdc.SetPen(fred);
1336 fmdc.DrawLine(xm - dp, ym + dp, xm + dp, ym + dp);
1337 fmdc.SelectObject(wxNullBitmap);
1338
1339 wxMask *pfmask = new wxMask(fbmp, unused_color);
1340 fbmp.SetMask(pfmask);
1341
1342 pmarkicon_image_list->Add(fbmp);
1343
1344 pmi->m_blistImageOK = true;
1345 pmi->listIndex = index;
1346 }
1347
1348 return pmi->listIndex;
1349}
1350
1351int WayPointman::GetXIconImageListIndex(const wxBitmap *pbm) const {
1352 return GetIconImageListIndex(pbm) + 1;
1353}
1354
1355int WayPointman::GetFIconImageListIndex(const wxBitmap *pbm) const {
1356 return GetIconImageListIndex(pbm) + 2;
1357}
1358
1359// Create the unique identifier
1360wxString WayPointman::CreateGUID(RoutePoint *pRP) {
1361 return GpxDocument::GetUUID();
1362}
1363
1364RoutePoint *WayPointman::FindRoutePointByGUID(const wxString &guid) {
1365 for (RoutePoint *prp : *m_pWayPointList) {
1366 if (prp->m_GUID == guid) return (prp);
1367 }
1368 return NULL;
1369}
1370
1371RoutePoint *WayPointman::GetNearbyWaypoint(double lat, double lon,
1372 double radius_meters) {
1373 // Iterate on the RoutePoint list, checking distance
1374
1375 for (RoutePoint *pr : *m_pWayPointList) {
1376 double a = lat - pr->m_lat;
1377 double b = lon - pr->m_lon;
1378 double l = sqrt((a * a) + (b * b));
1379
1380 if ((l * 60. * 1852.) < radius_meters) return pr;
1381 }
1382 return NULL;
1383}
1384
1385RoutePoint *WayPointman::GetOtherNearbyWaypoint(double lat, double lon,
1386 double radius_meters,
1387 const wxString &guid) {
1388 // Iterate on the RoutePoint list, checking distance
1389
1390 for (RoutePoint *pr : *m_pWayPointList) {
1391 double a = lat - pr->m_lat;
1392 double b = lon - pr->m_lon;
1393 double l = sqrt((a * a) + (b * b));
1394
1395 if ((l * 60. * 1852.) < radius_meters)
1396 if (pr->m_GUID != guid) return pr;
1397 }
1398 return NULL;
1399}
1400
1401bool WayPointman::IsReallyVisible(RoutePoint *pWP) {
1402 if (pWP->m_bIsolatedMark) return pWP->IsVisible(); // isolated point
1403 for (Route *proute : *pRouteList) {
1404 if (proute && proute->pRoutePointList) {
1405 auto &list = proute->pRoutePointList;
1406 auto pos = std::find(list->begin(), list->end(), pWP);
1407 if (pos != list->end()) {
1408 if (proute->IsVisible()) return true;
1409 }
1410 }
1411 }
1412 if (pWP->IsShared()) // is not visible as part of route, but still exists as
1413 // a waypoint
1414 return pWP->IsVisible(); // so treat as isolated point
1415
1416 return false;
1417}
1418
1419void WayPointman::ClearRoutePointFonts() {
1420 // Iterate on the RoutePoint list, clearing Font pointers
1421 // This is typically done globally after a font switch
1422 for (RoutePoint *pr : *m_pWayPointList) {
1423 pr->m_pMarkFont = NULL;
1424 }
1425}
1426
1427bool WayPointman::SharedWptsExist() {
1428 for (RoutePoint *prp : *m_pWayPointList) {
1429 if (prp->IsShared() && (prp->m_bIsInRoute || prp == pAnchorWatchPoint1 ||
1430 prp == pAnchorWatchPoint2))
1431 return true;
1432 }
1433 return false;
1434}
1435
1436void WayPointman::DeleteAllWaypoints(bool b_delete_used) {
1437 // Iterate on the RoutePoint list, deleting all
1438 auto it = m_pWayPointList->begin();
1439 while (it != m_pWayPointList->end()) {
1440 RoutePoint *prp = *it;
1441 // if argument is false, then only delete non-route waypoints
1442 if (!prp->m_bIsInLayer && (prp->GetIconName() != "mob") &&
1443 ((b_delete_used && prp->IsShared()) ||
1444 ((!prp->m_bIsInRoute) && !(prp == pAnchorWatchPoint1) &&
1445 !(prp == pAnchorWatchPoint2)))) {
1446 DestroyWaypoint(prp);
1447 delete prp;
1448 it = m_pWayPointList->begin();
1449 } else
1450 ++it;
1451 }
1452 return;
1453}
1454
1455RoutePoint *WayPointman::FindWaypointByGuid(const std::string &guid) {
1456 for (RoutePoint *rp : *m_pWayPointList) {
1457 if (guid == rp->m_GUID) return rp;
1458 }
1459 return 0;
1460}
1461void WayPointman::DestroyWaypoint(RoutePoint *pRp, bool b_update_changeset) {
1462 if (pRp) {
1463 // Get a list of all routes containing this point
1464 // and remove the point from them all
1465 wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining(pRp);
1466 if (proute_array) {
1467 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1468 Route *pr = (Route *)proute_array->Item(ir);
1469
1470 /* FS#348
1471 if ( g_pRouteMan->GetpActiveRoute() == pr ) // Deactivate
1472 any route containing this point g_pRouteMan->DeactivateRoute();
1473 */
1474 pr->RemovePoint(pRp);
1475 }
1476
1477 // Scrub the routes, looking for one-point routes
1478 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
1479 Route *pr = (Route *)proute_array->Item(ir);
1480 if (pr->GetnPoints() < 2) {
1482 }
1483 }
1484
1485 delete proute_array;
1486 }
1487
1488 // Now it is safe to delete the point
1489 NavObj_dB::GetInstance().DeleteRoutePoint(pRp);
1490
1491 pSelect->DeleteSelectableRoutePoint(pRp);
1492
1493 // The RoutePoint might be currently in use as an anchor watch point
1494 if (pRp == pAnchorWatchPoint1) pAnchorWatchPoint1 = NULL;
1495 if (pRp == pAnchorWatchPoint2) pAnchorWatchPoint2 = NULL;
1496
1497 RemoveRoutePoint(pRp);
1498 }
1499}
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.
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.