OpenCPN Partial API docs
routeman_gui.cpp
1 
2 /***************************************************************************
3  *
4  * Project: OpenCPN
5  * Purpose: implement routeman_gui.h: Routeman drawing stuff
6  * Author: David Register, Alec Leamas
7  *
8  ***************************************************************************
9  * Copyright (C) 2022 by David Register, Alec Leamas *
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  * This program is distributed in the hope that it will be useful, *
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19  * GNU General Public License for more details. *
20  * *
21  * You should have received a copy of the GNU General Public License *
22  * along with this program; if not, write to the *
23  * Free Software Foundation, Inc., *
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
25  ******************A********************************************************/
26 
27 // For compilers that support precompilation, includes "wx.h".
28 #include <wx/wxprec.h>
29 
30 #ifndef WX_PRECOMP
31 #include <wx/wx.h>
32 #endif // precompiled headers
33 
34 #include <wx/utils.h>
35 #include <wx/gdicmn.h>
36 
37 #include "model/georef.h"
38 #include "model/nav_object_database.h"
39 #include "model/own_ship.h"
40 #include "model/route.h"
41 #include "model/route_point.h"
42 #include "model/select.h"
43 #include "model/track.h"
44 
45 #include "chcanv.h"
46 #include "concanv.h"
47 #include "model/ais_decoder.h"
48 #include "navutil.h"
49 #include "ocpn_app.h"
50 #include "ocpn_frame.h"
51 #include "routemanagerdialog.h"
52 #include "routeman_gui.h"
53 #include "TrackPropDlg.h"
54 #include "vector2D.h"
55 
56 extern bool g_bShowShipToActive;
57 extern bool g_bAdvanceRouteWaypointOnArrivalOnly;
58 
59 extern MyFrame* gFrame;
60 
61 extern ConsoleCanvas *console;
62 
63 extern std::vector<Track*> g_TrackList;
64 extern ActiveTrack* g_pActiveTrack;
65 extern TrackPropDlg *pTrackPropDialog;
66 extern RouteManagerDialog *pRouteManagerDialog;
67 extern MyConfig *pConfig;
68 
69 static bool ConfirmDeleteAisMob() {
70  int r = OCPNMessageBox(NULL,
71  _("You are trying to delete an active AIS MOB "
72  "route, are you REALLY sure?"),
73  _("OpenCPN Warning"), wxYES_NO);
74 
75  return r == wxID_YES;
76 }
77 
78 RoutemanDlgCtx RoutemanGui::GetDlgCtx() {
79  RoutemanDlgCtx ctx;
80  ctx.confirm_delete_ais_mob = []() { return ConfirmDeleteAisMob(); };
81  ctx.get_global_colour = [](wxString c) { return GetGlobalColor(c); };
82  ctx.show_with_fresh_fonts =
83  []{ if (console) console->ShowWithFreshFonts(); };
84  ctx.clear_console_background = [] () {
85  console->pCDI->ClearBackground();
86  console->Show(false); };
87  ctx.route_mgr_dlg_update_list_ctrl = []() {
88  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
89  pRouteManagerDialog->UpdateRouteListCtrl();
90  };
91  return ctx;
92 }
93 
94 
95 bool RoutemanGui::UpdateProgress() {
96  bool bret_val = false;
97 
98  if (m_routeman.pActiveRoute) {
99  // Update bearing, range, and crosstrack error
100 
101  // Bearing is calculated as Mercator Sailing, i.e. a cartographic
102  // "bearing"
103  double north, east;
104  toSM(m_routeman.pActivePoint->m_lat, m_routeman.pActivePoint->m_lon,
105  gLat, gLon, &east, &north);
106  double a = atan(north / east);
107  if (fabs(m_routeman.pActivePoint->m_lon - gLon) < 180.) {
108  if (m_routeman.pActivePoint->m_lon > gLon)
109  m_routeman.CurrentBrgToActivePoint = 90. - (a * 180 / PI);
110  else
111  m_routeman.CurrentBrgToActivePoint = 270. - (a * 180 / PI);
112  } else {
113  if (m_routeman.pActivePoint->m_lon > gLon)
114  m_routeman.CurrentBrgToActivePoint = 270. - (a * 180 / PI);
115  else
116  m_routeman.CurrentBrgToActivePoint = 90. - (a * 180 / PI);
117  }
118 
119  // Calculate range using Great Circle Formula
120 
121  double d5 =
122  DistGreatCircle(gLat, gLon, m_routeman.pActivePoint->m_lat, m_routeman.pActivePoint->m_lon);
123  m_routeman.CurrentRngToActivePoint = d5;
124 
125  // Get the XTE vector, normal to current segment
126  vector2D va, vb, vn;
127 
128  double brg1, dist1, brg2, dist2;
129  DistanceBearingMercator(m_routeman.pActivePoint->m_lat, m_routeman.pActivePoint->m_lon,
130  m_routeman.pActiveRouteSegmentBeginPoint->m_lat,
131  m_routeman.pActiveRouteSegmentBeginPoint->m_lon, &brg1,
132  &dist1);
133  vb.x = dist1 * sin(brg1 * PI / 180.);
134  vb.y = dist1 * cos(brg1 * PI / 180.);
135 
136  DistanceBearingMercator(m_routeman.pActivePoint->m_lat, m_routeman.pActivePoint->m_lon, gLat,
137  gLon, &brg2, &dist2);
138  va.x = dist2 * sin(brg2 * PI / 180.);
139  va.y = dist2 * cos(brg2 * PI / 180.);
140 
141  double sdelta = vGetLengthOfNormal(&va, &vb, &vn); // NM
142  m_routeman.CurrentXTEToActivePoint = sdelta;
143 
144  // Calculate the distance to the arrival line, which is perpendicular to
145  // the current route segment Taking advantage of the calculated normal
146  // from current position to route segment vn
147  vector2D vToArriveNormal;
148  vSubtractVectors(&va, &vn, &vToArriveNormal);
149 
150  m_routeman.CurrentRangeToActiveNormalCrossing =
151  vVectorMagnitude(&vToArriveNormal);
152 
153  // Compute current segment course
154  // Using simple Mercater projection
155  double x1, y1, x2, y2;
156  toSM(m_routeman.pActiveRouteSegmentBeginPoint->m_lat,
157  m_routeman.pActiveRouteSegmentBeginPoint->m_lon,
158  m_routeman.pActiveRouteSegmentBeginPoint->m_lat,
159  m_routeman.pActiveRouteSegmentBeginPoint->m_lon, &x1, &y1);
160 
161  toSM(m_routeman.pActivePoint->m_lat, m_routeman.pActivePoint->m_lon,
162  m_routeman.pActiveRouteSegmentBeginPoint->m_lat,
163  m_routeman.pActiveRouteSegmentBeginPoint->m_lon, &x2, &y2);
164 
165  double e1 = atan2((x2 - x1), (y2 - y1));
166  m_routeman.CurrentSegmentCourse = e1 * 180 / PI;
167  if (m_routeman.CurrentSegmentCourse < 0)
168  m_routeman.CurrentSegmentCourse += 360;
169 
170  // Compute XTE direction
171  double h = atan(vn.y / vn.x);
172  if (vn.x > 0)
173  m_routeman.CourseToRouteSegment = 90. - (h * 180 / PI);
174  else
175  m_routeman.CourseToRouteSegment = 270. - (h * 180 / PI);
176 
177  h = m_routeman.CurrentBrgToActivePoint - m_routeman.CourseToRouteSegment;
178  if (h < 0) h = h + 360;
179 
180  if (h > 180)
181  m_routeman.XTEDir = 1;
182  else
183  m_routeman.XTEDir = -1;
184 
185  // Allow DirectShipToActivePoint line (distance XTE in mm is > 3 (arbitrary)
186  // or when active point is the first
187  if (g_bShowShipToActive) {
188  if (m_routeman.pActiveRoute->GetIndexOf(m_routeman.pActivePoint) == 1)
189  g_bAllowShipToActive = true;
190  else {
191  // compute XTE in pixels
192  double tlat, tlon;
193  wxPoint r, r1;
194  ll_gc_ll(gLat, gLon, m_routeman.CourseToRouteSegment,
195  (m_routeman.CurrentXTEToActivePoint / 1.852), &tlat, &tlon);
196  gFrame->GetFocusCanvas()->GetCanvasPointPix(gLat, gLon, &r1);
197  gFrame->GetFocusCanvas()->GetCanvasPointPix(tlat, tlon, &r);
198  double xtepix =
199  sqrt(pow((double)(r1.x - r.x), 2) + pow((double)(r1.y - r.y), 2));
200  // xte in mm
201  double xtemm = xtepix / gFrame->GetFocusCanvas()->GetPixPerMM();
202  // allow display (or not)
203  g_bAllowShipToActive = (xtemm > 3.0) ? true : false;
204  }
205  }
206 
207  // Determine Arrival
208 
209  bool bDidArrival = false;
210 
211  // Special signal: if ArrivalRadius < 0, NEVER arrive...
212  // Used for MOB auto-created routes.
213  if (m_routeman.pActivePoint->GetWaypointArrivalRadius() > 0) {
214  if (m_routeman.CurrentRangeToActiveNormalCrossing <=
215  m_routeman.pActivePoint->GetWaypointArrivalRadius()) {
216  m_routeman.m_bArrival = true;
217  m_routeman.UpdateAutopilot();
218 
219  bDidArrival = true;
220  DoAdvance();
221 
222  } else {
223  // Test to see if we are moving away from the arrival point, and
224  // have been moving away for 2 seconds.
225  // If so, we should declare "Arrival"
226  if ((m_routeman.CurrentRangeToActiveNormalCrossing - m_routeman.m_arrival_min) >
227  m_routeman.pActivePoint->GetWaypointArrivalRadius()) {
228  if (++m_routeman.m_arrival_test > 2 &&
229  !g_bAdvanceRouteWaypointOnArrivalOnly)
230  {
231  m_routeman.m_bArrival = true;
232  m_routeman.UpdateAutopilot();
233 
234  bDidArrival = true;
235  DoAdvance();
236  }
237  } else
238  m_routeman.m_arrival_test = 0;
239  }
240  }
241  if (!bDidArrival)
242  m_routeman.m_arrival_min = wxMin(m_routeman.m_arrival_min,
243  m_routeman.CurrentRangeToActiveNormalCrossing);
244  // Only once on arrival
245  if (!bDidArrival) m_routeman.UpdateAutopilot();
246  bret_val = true; // a route is active
247  }
248  m_routeman.m_bDataValid = true;
249  return bret_val;
250 }
251 
252 void RoutemanGui::DeleteTrack(Track *pTrack) {
253  if (pTrack) {
254  if (pTrack->m_bIsInLayer) return;
255 
256  ::wxBeginBusyCursor();
257 
258  wxGenericProgressDialog *pprog = nullptr;
259 
260  int count = pTrack->GetnPoints();
261  if (count > 10000) {
262  pprog = new wxGenericProgressDialog(
263  _("OpenCPN Track Delete"), _T("0/0"), count, NULL,
264  wxPD_APP_MODAL | wxPD_SMOOTH | wxPD_ELAPSED_TIME |
265  wxPD_ESTIMATED_TIME | wxPD_REMAINING_TIME);
266  pprog->SetSize(400, wxDefaultCoord);
267  pprog->Centre();
268  }
269  if (TrackPropDlg::getInstanceFlag() && pTrackPropDialog &&
270  (pTrackPropDialog->IsShown()) &&
271  (pTrack == pTrackPropDialog->GetTrack())) {
272  pTrackPropDialog->Hide();
273  }
274 
275  if ((pTrack == g_pActiveTrack) && pTrack->IsRunning()){
276  pTrack = gFrame->TrackOff();
277  }
278  // Remove the track from associated lists
279  pSelect->DeleteAllSelectableTrackSegments(pTrack);
280  auto it = std::find(g_TrackList.begin(), g_TrackList.end(), pTrack);
281  if (it != g_TrackList.end()) {
282  g_TrackList.erase(it);
283  }
284  delete pTrack;
285 
286  ::wxEndBusyCursor();
287 
288  delete pprog;
289  }
290 }
291 
292 void RoutemanGui::DeleteAllTracks() {
293  gFrame->TrackOff();
294 
295  ::wxBeginBusyCursor();
296 
297  // Iterate on the RouteList, we delete from g_TrackList in DeleteTrack,
298  // bigger refactoring is viable, but for now, we simply make a copy
299  // that goes out of scope soon.
300  std::vector<Track*> to_del = g_TrackList;
301  for (Track *ptrack : to_del) {
302  if (ptrack->m_bIsInLayer) continue;
303 
304  g_pAIS->DeletePersistentTrack(ptrack);
305  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate = true;
306  NavObjectChanges::getInstance()->DeleteConfigTrack(ptrack);
307  DeleteTrack(ptrack);
308  NavObjectChanges::getInstance()->m_bSkipChangeSetUpdate = false;
309  }
310 
311  if (pConfig && pConfig->IsChangesFileDirty()) {
312  pConfig->UpdateNavObj(true);
313  }
314 
315  ::wxEndBusyCursor();
316 }
317 
318 void RoutemanGui::DoAdvance(void) {
319  if (!m_routeman.ActivateNextPoint(m_routeman.pActiveRoute, false)) // at the end?
320  {
321  Route *pthis_route = m_routeman.pActiveRoute;
322  m_routeman.DeactivateRoute(true); // this is an arrival
323 
324  if (pthis_route->m_bDeleteOnArrival && !pthis_route->m_bIsBeingEdited) {
325  NavObjectChanges::getInstance()->DeleteConfigRoute(pthis_route);
326  m_routeman.DeleteRoute(pthis_route, NavObjectChanges::getInstance());
327  }
328 
329  if (pRouteManagerDialog) pRouteManagerDialog->UpdateRouteListCtrl();
330  }
331 }
Definition: route.h:75
bool ActivateNextPoint(Route *pr, bool skipped)
Definition: routeman.cpp:369
bool DeleteRoute(Route *pRoute, NavObjectChanges *nav_obj_changes)
Definition: routeman.cpp:751
Class TrackPropDlg.
Definition: TrackPropDlg.h:93
Definition: track.h:78
Routeman callbacks.
Definition: routeman.h:96