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