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