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