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