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