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