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