OpenCPN Partial API docs
Loading...
Searching...
No Matches
route_point.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Route Point Object
5 *
6 ***************************************************************************
7 * Copyright (C) 2013 by David S. Register *
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 * This program is distributed in the hope that it will be useful, *
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17 * GNU General Public License for more details. *
18 * *
19 * You should have received a copy of the GNU General Public License *
20 * along with this program; if not, write to the *
21 * Free Software Foundation, Inc., *
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
23 **************************************************************************/
24
25#include <wx/colour.h>
26#include <wx/datetime.h>
27#include <wx/dynarray.h>
28#include <wx/string.h>
29#include <wx/tokenzr.h>
30
31#include "model/base_platform.h"
32#include "model/cutil.h"
33#include "model/georef.h"
34#include "model/navutil_base.h"
35#include "model/route.h"
36#include "model/routeman.h"
37#include "model/route_point.h"
38#include "model/select.h"
39
40#include <wx/listimpl.cpp>
41
42WX_DEFINE_LIST(RoutePointList);
43
44wxColour g_colourWaypointRangeRingsColour;
45
46int g_LayerIdx;
47
48wxRect g_blink_rect;
49
50std::function<void(unsigned, const unsigned *)> RoutePoint::delete_gl_textures =
51 [](unsigned, const unsigned *) { assert(true); };
52
53RoutePoint::RoutePoint() {
54 m_pbmIcon = NULL;
55
56 // Nice defaults
57 m_seg_len = 0.0;
58 m_seg_vmg = 0.0;
59
60 m_seg_etd = wxInvalidDateTime;
61 m_manual_etd = false;
62
63 m_seg_eta = wxInvalidDateTime;
64 m_bPtIsSelected = false;
65 m_bRPIsBeingEdited = false;
66 m_bIsActive = false;
67 m_bBlink = false;
68 m_bIsInRoute = false;
69 m_CreateTimeX = wxDateTime::Now();
70 m_bIsolatedMark = false;
71 m_bShowName = true;
72 SetShared(false);
73 m_bIsVisible = true;
74 m_bIsListed = true;
75 CurrentRect_in_DC = wxRect(0, 0, 0, 0);
78 m_pMarkFont = NULL;
79 m_btemp = false;
80 m_SelectNode = NULL;
81 m_ManagerNode = NULL;
82
84
85 m_HyperlinkList = new HyperlinkList;
86
87 m_GUID = pWayPointMan->CreateGUID(this);
88
89 m_IconName = wxEmptyString;
90
91 m_MarkName = wxEmptyString;
92
93 m_bIsInLayer = false;
94 m_LayerID = 0;
95
96 m_WaypointArrivalRadius = g_n_arrival_circle_radius;
97
98 m_bShowWaypointRangeRings = (bool)g_iWaypointRangeRingsNumber;
99
100 m_iWaypointRangeRingsNumber = g_iWaypointRangeRingsNumber;
101 m_fWaypointRangeRingsStep = g_fWaypointRangeRingsStep;
102 m_iWaypointRangeRingsStepUnits = g_iWaypointRangeRingsStepUnits;
103 m_wxcWaypointRangeRingsColour = g_colourWaypointRangeRingsColour;
104 m_ScaMin = g_iWpt_ScaMin;
105 m_bShowName = g_bShowWptName;
106 m_ScaMax = 0;
107 b_UseScamin = g_bUseWptScaMin;
108
109 m_pos_on_screen = false;
110 m_bDrawDragHandle = false;
111 m_dragIconTexture = 0;
112 m_draggingOffsetx = m_draggingOffsety = 0;
113
114 m_PlannedSpeed = 0.;
115 m_IconIsDirty = true;
116}
117
118// Copy Constructor
119RoutePoint::RoutePoint(RoutePoint *orig) {
120 m_MarkName = orig->GetName();
121 m_lat = orig->m_lat;
122 m_lon = orig->m_lon;
123 m_seg_len = orig->m_seg_len;
124 m_seg_vmg = orig->m_seg_vmg;
125
126 m_seg_etd = orig->m_seg_etd;
127 m_manual_etd = false;
128
131 m_bIsActive = orig->m_bIsActive;
132 m_bBlink = orig->m_bBlink;
136 m_bShowName = orig->m_bShowName;
137 SetShared(orig->IsShared());
139 m_bIsListed = orig->m_bIsListed;
143 m_pMarkFont = orig->m_pMarkFont;
145 m_btemp = orig->m_btemp;
146 m_ScaMin = orig->m_ScaMin;
147 m_ScaMax = orig->m_ScaMax;
148 m_HyperlinkList = new HyperlinkList;
149 m_IconName = orig->m_IconName;
151 SetPlannedSpeed(orig->GetPlannedSpeed());
152
154 m_GUID = pWayPointMan->CreateGUID(this);
155
156 m_SelectNode = NULL;
157 m_ManagerNode = NULL;
158
159 m_WaypointArrivalRadius = orig->GetWaypointArrivalRadius();
165 m_ScaMin = orig->m_ScaMin;
166 m_ScaMax = orig->m_ScaMax;
167 b_UseScamin = orig->b_UseScamin;
169
170 m_bDrawDragHandle = false;
171 m_dragIconTexture = 0;
172 m_draggingOffsetx = m_draggingOffsety = 0;
173}
174
175RoutePoint::RoutePoint(double lat, double lon, const wxString &icon_ident,
176 const wxString &name, const wxString &pGUID,
177 bool bAddToList) {
178 // Establish points
179 m_lat = lat;
180 m_lon = lon;
181
182 // Normalize the longitude, to fix any old poorly formed points
183 if (m_lon < -180.)
184 m_lon += 360.;
185 else if (m_lon > 180.)
186 m_lon -= 360.;
187
188 // Nice defaults
189 m_seg_len = 0.0;
190 m_seg_vmg = 0.0;
191
192 m_seg_etd = wxInvalidDateTime;
193 m_manual_etd = false;
194
195 m_bPtIsSelected = false;
196 m_bRPIsBeingEdited = false;
197 m_bIsActive = false;
198 m_bBlink = false;
199 m_bIsInRoute = false;
200 m_CreateTimeX = wxDateTime::Now();
201 m_bIsolatedMark = false;
202 m_bShowName = true;
203 SetShared(false);
204 m_bIsVisible = true;
205 m_bIsListed = true;
206 CurrentRect_in_DC = wxRect(0, 0, 0, 0);
209 m_pMarkFont = NULL;
210 m_btemp = false;
211 m_bPreScaled = false;
212
213 m_SelectNode = NULL;
214 m_ManagerNode = NULL;
215 m_IconScaleFactor = 1.0;
216 m_ScaMin = MAX_INT_VAL;
217 m_ScaMax = 0;
218 m_HyperlinkList = new HyperlinkList;
219 m_IconIsDirty = true;
220
221 m_iTextTexture = 0;
222
223 if (!pGUID.IsEmpty())
224 m_GUID = pGUID;
225 else
226 m_GUID = pWayPointMan->CreateGUID(this);
227
228 // Get Icon bitmap
229 m_IconName = icon_ident;
230
231 SetName(name);
232
233 // Possibly add the waypoint to the global list maintained by the waypoint
234 // manager
235
236 if (bAddToList && NULL != pWayPointMan) pWayPointMan->AddRoutePoint(this);
237
238 m_bIsInLayer = false;
239 m_LayerID = 0;
240
241 SetWaypointArrivalRadius(g_n_arrival_circle_radius);
242
243 m_bShowWaypointRangeRings = (bool)g_iWaypointRangeRingsNumber;
244
245 m_iWaypointRangeRingsNumber = g_iWaypointRangeRingsNumber;
246 m_fWaypointRangeRingsStep = g_fWaypointRangeRingsStep;
247 m_iWaypointRangeRingsStepUnits = g_iWaypointRangeRingsStepUnits;
248 m_wxcWaypointRangeRingsColour = g_colourWaypointRangeRingsColour;
249 m_ScaMin = g_iWpt_ScaMin;
250 m_ScaMax = 0;
251 b_UseScamin = g_bUseWptScaMin;
252 m_bShowName = g_bShowWptName;
253
254 m_bDrawDragHandle = false;
255 m_dragIconTexture = 0;
256 m_draggingOffsetx = m_draggingOffsety = 0;
257
258 m_PlannedSpeed = 0.;
259}
260
261RoutePoint::~RoutePoint() {
262 // Remove this point from the global waypoint list
263 if (NULL != pWayPointMan) pWayPointMan->RemoveRoutePoint(this);
264
265 if (m_HyperlinkList) {
266 auto &list = m_HyperlinkList;
267 for (auto it = list->begin(); it != list->end(); ++it) delete *it;
268 delete m_HyperlinkList;
269 }
270 RoutePoint::delete_gl_textures(1, &m_dragIconTexture);
271}
272
274 if (!m_CreateTimeX.IsValid()) {
275 if (m_timestring.Len()) ParseGPXDateTime(m_CreateTimeX, m_timestring);
276 }
277 return m_CreateTimeX;
278}
279
280void RoutePoint::SetCreateTime(wxDateTime dt) { m_CreateTimeX = dt; }
281
282void RoutePoint::SetName(const wxString &name) {
283 if (m_iTextTexture) {
285 m_iTextTexture = 0;
286 }
287 m_MarkName = name;
288 CalculateNameExtents();
289}
290
291void RoutePoint::CalculateNameExtents(void) {
292 if (m_pMarkFont) {
293 wxScreenDC dc;
294
295#ifdef __WXQT__ // avoiding "painter not active" warning
296 int w, h;
297 dc.GetTextExtent(m_MarkName, &w, &h, NULL, NULL, m_pMarkFont);
298 m_NameExtents = wxSize(w, h);
299#else
300 dc.SetFont(*m_pMarkFont);
301 m_NameExtents = dc.GetMultiLineTextExtent(m_MarkName);
302#endif
303 } else
304 m_NameExtents = wxSize(0, 0);
305}
306
307bool RoutePoint::IsVisibleSelectable(double scale_val, bool boverrideViz) {
308 if (m_bIsActive) // An active route point must always be visible
309 return true;
310
311 if (!boverrideViz) {
312 if (!m_bIsVisible) // if not visible nevermind the rest.
313 return false;
314 }
315
316 if (b_UseScamin) {
317 if (g_bOverruleScaMin)
318 return true;
319 else if (scale_val >= (double)(m_ScaMin + 1))
320 return false;
321 }
322 return true;
323}
324
325bool RoutePoint::IsSharedInVisibleRoute() {
326 if (IsShared()) {
327 // Get an array of all routes using this point
328 wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining(this);
329
330 // Use route array (if any) to determine actual visibility for this point
331 bool brp_viz = false;
332 if (proute_array) {
333 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
334 Route *pr = (Route *)proute_array->Item(ir);
335 if (pr->IsVisible()) {
336 brp_viz = true;
337 break;
338 }
339 }
340 delete proute_array;
341 }
342
343 return brp_viz;
344 } else // point is not shared
345 return false;
346}
347
348void RoutePoint::SetPosition(double lat, double lon) {
349 m_lat = lat;
350 m_lon = lon;
351}
352
353bool RoutePoint::IsSame(RoutePoint *pOtherRP) {
354 bool IsSame = false;
355
356 if (this->m_MarkName == pOtherRP->m_MarkName) {
357 if (fabs(this->m_lat - pOtherRP->m_lat) < 1.e-6 &&
358 fabs(this->m_lon - pOtherRP->m_lon) < 1.e-6)
359 IsSame = true;
360 }
361 return IsSame;
362}
363
370 bool b_numeric = false;
371 if (m_bIsInRoute) {
372 if (GetName().Len() >= 2) {
373 wxString substring = GetName().Left(2);
374 if (substring == "NM") {
375 substring = GetName().substr(2, 3);
376 } else {
377 substring = GetName().Left(3);
378 }
379 b_numeric = true; // assume it is numeric
380 for (unsigned int i = 0; i < substring.Len(); i++) {
381 if (b_numeric == true) {
382 b_numeric = wxIsdigit(substring[i]);
383 } // don't change the value if it is already false
384 }
385 }
386 }
387 return b_numeric;
388}
389
390double RoutePoint::GetWaypointArrivalRadius() {
391 if ((m_WaypointArrivalRadius >= 0) && (m_WaypointArrivalRadius < 0.001)) {
392 SetWaypointArrivalRadius(g_n_arrival_circle_radius);
394 } else
396}
397
398int RoutePoint::GetWaypointRangeRingsNumber() {
400 return g_iWaypointRangeRingsNumber;
401 else
403}
404
405float RoutePoint::GetWaypointRangeRingsStep() {
407 return g_fWaypointRangeRingsStep;
408 else
410}
411
412int RoutePoint::GetWaypointRangeRingsStepUnits() {
414 return g_iWaypointRangeRingsStepUnits;
415 else
417}
418
419void RoutePoint::SetScaMin(long val) {
420 if (val < SCAMIN_MIN)
421 val = SCAMIN_MIN; // prevent from waypoints hiding always with a nonlogic
422 // value
423 if (val < (long)m_ScaMax * 5) val = (long)m_ScaMax * 5;
424 m_ScaMin = val;
425}
426void RoutePoint::SetScaMin(wxString str) {
427 long val;
428 if (!str.ToLong(&val)) val = MAX_INT_VAL;
429 SetScaMin(val);
430}
431
432void RoutePoint::SetScaMax(long val) {
433 if (val > (int)m_ScaMin / 5)
434 m_ScaMax = (int)m_ScaMin /
435 5; // prevent from waypoints hiding always with a nonlogic value
436}
437void RoutePoint::SetScaMax(wxString str) {
438 long val;
439 if (!str.ToLong(&val)) val = 0;
440 SetScaMax(val);
441}
442
443void RoutePoint::SetPlannedSpeed(double spd) {
444 if (spd >= 0.0 && spd <= 1000.0) m_PlannedSpeed = spd;
445}
446
448 if (m_PlannedSpeed < 0.0001 &&
449 m_MarkDescription.Find(_T("VMG=")) != wxNOT_FOUND) {
450 // In case there was speed encoded in the name of the waypoint, do the
451 // conversion here.
452 wxString s_vmg =
453 (m_MarkDescription.Mid(m_MarkDescription.Find(_T("VMG=")) + 4))
454 .BeforeFirst(';');
455 double vmg;
456 if (!s_vmg.ToDouble(&vmg)) {
457 m_MarkDescription.Replace(_T("VMG=") + s_vmg + ";", wxEmptyString);
458 SetPlannedSpeed(vmg);
459 }
460 }
461 return m_PlannedSpeed;
462}
463
464wxDateTime RoutePoint::GetETD() {
465 if (m_seg_etd.IsValid()) {
466 if (!GetETA().IsValid() || m_seg_etd > GetETA()) {
467 return m_seg_etd;
468 } else {
469 return GetETA();
470 }
471 } else {
472 if (m_MarkDescription.Find(_T("ETD=")) != wxNOT_FOUND) {
473 wxDateTime etd = wxInvalidDateTime;
474 wxString s_etd =
475 (m_MarkDescription.Mid(m_MarkDescription.Find(_T("ETD=")) + 4))
476 .BeforeFirst(';');
477 const wxChar *parse_return = etd.ParseDateTime(s_etd);
478 if (parse_return) {
479 wxString tz(parse_return);
480
481 if (tz.Find(_T("UT")) != wxNOT_FOUND) {
482 // TODO: This is error-prone. It would match any string containing
483 // these characters, not just time zone codes For example, "UT" would
484 // match "UTC+2".
485 m_seg_etd = etd;
486 } else {
487 if (tz.Find(_T("LMT")) != wxNOT_FOUND) {
488 m_seg_etd = etd;
489 long lmt_offset = (long)((m_lon * 3600.) / 15.);
490 wxTimeSpan lmt(0, 0, (int)lmt_offset, 0);
491 m_seg_etd -= lmt;
492 } else {
493 m_seg_etd = etd.ToUTC();
494 }
495 }
496 if (etd.IsValid() && (!GetETA().IsValid() || etd > GetETA())) {
497 m_MarkDescription.Replace(s_etd, wxEmptyString);
498 m_seg_etd = etd;
499 return m_seg_etd;
500 } else {
501 return GetETA();
502 }
503 }
504 }
505 }
506 return wxInvalidDateTime;
507}
508
510 if (m_manual_etd && m_seg_etd.IsValid()) {
511 return m_seg_etd;
512 }
513 return wxInvalidDateTime;
514}
515
516wxDateTime RoutePoint::GetETA() {
517 if (m_seg_eta.IsValid()) {
518 return m_seg_eta;
519 }
520 return wxInvalidDateTime;
521}
522
524 if (m_seg_ete != 0) {
525 return formatTimeDelta(m_seg_ete);
526 }
527 return wxEmptyString;
528}
529
530void RoutePoint::SetETE(wxLongLong secs) { m_seg_ete = secs; }
531
532void RoutePoint::SetETD(const wxDateTime &etd) {
533 m_seg_etd = etd;
534 m_manual_etd = TRUE;
535}
536
537bool RoutePoint::SetETD(const wxString &ts) {
538 if (ts.IsEmpty()) {
539 m_seg_etd = wxInvalidDateTime;
540 m_manual_etd = false;
541 return true;
542 }
543 wxDateTime tmp;
544 wxString::const_iterator end;
545 // No timezone conversion is done because the serialized string
546 // does not include timezone information, e.g., "2025-03-26T18:57:01"
547 // The input string is assumed to be in UTC format.
548 if (tmp.ParseISOCombined(ts)) {
549 SetETD(tmp);
550 return TRUE;
551 } else if (tmp.ParseDateTime(ts, &end)) {
552 SetETD(tmp);
553 return TRUE;
554 }
555 return FALSE;
556}
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
HyperlinkList * m_HyperlinkList
List of hyperlinks associated with this waypoint.
wxColour m_wxcWaypointRangeRingsColour
Color for the range rings display.
wxString m_MarkDescription
Description text for the waypoint.
int m_iWaypointRangeRingsNumber
Number of range rings to display around the waypoint.
int m_NameLocationOffsetX
Horizontal offset for waypoint name placement relative to the icon.
void SetCreateTime(wxDateTime dt)
Sets the create time of this RoutePoint in UTC.
static std::function< void(unsigned, const unsigned *)> delete_gl_textures
Horrible Hack (tm).
Definition route_point.h:50
bool m_bRPIsBeingEdited
Flag indicating if this waypoint is currently being edited.
wxRect CurrentRect_in_DC
Current rectangle occupied by the waypoint in the display.
unsigned int m_iTextTexture
Texture identifier for rendered text.
wxString m_GUID
Globally Unique Identifier for the waypoint.
wxDateTime GetETD()
Retrieves the Estimated Time of Departure for this waypoint, in UTC.
wxDateTime m_CreateTimeX
Creation timestamp for the waypoint, in UTC.
int m_NameLocationOffsetY
Vertical offset for waypoint name placement relative to the icon.
bool IsNameDynamic()
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
bool m_pos_on_screen
Flag indicating if the waypoint is currently visible on screen.
bool m_bIsActive
Flag indicating if this waypoint is active for navigation.
wxDateTime GetManualETD()
Retrieves the manually set Estimated Time of Departure for this waypoint, in UTC.
wxDateTime m_seg_etd
Estimated Time of Departure from this waypoint, in UTC.
bool m_bIsInRoute
Flag indicating if this waypoint is part of a route.
bool m_IconIsDirty
Flag indicating if the waypoint icon needs to be reloaded or redrawn.
wxFont * m_pMarkFont
Font used for rendering the waypoint name.
double m_seg_len
Length of the leg from previous waypoint to this waypoint in nautical miles.
bool m_bShowName
Flag indicating if the waypoint name should be shown.
wxString GetETE()
Retrieves the Estimated Time En route as a formatted string.
wxString m_timestring
String representation of the waypoint creation time.
bool m_bBlink
Flag indicating if the waypoint should blink when displayed.
double GetPlannedSpeed()
Return the planned speed associated with this waypoint.
bool m_bPtIsSelected
Flag indicating if this waypoint is currently selected.
bool m_bIsVisible
Flag indicating if the waypoint should be drawn on the chart.
bool m_bIsInLayer
Flag indicating if the waypoint belongs to a layer.
double m_WaypointArrivalRadius
Arrival radius in nautical miles.
bool m_btemp
Flag indicating if this is a temporary waypoint.
bool m_manual_etd
Flag indicating whether the ETD has been manually set by the user.
double m_seg_vmg
Planned speed for traveling FROM this waypoint TO the next waypoint.
wxSize m_NameExtents
Size of the waypoint name text when rendered.
wxDateTime m_seg_eta
Estimated Time of Arrival at this waypoint, in UTC.
int m_LayerID
Layer identifier if the waypoint belongs to a layer.
int m_iWaypointRangeRingsStepUnits
Units for the range rings step (0=nm, 1=km).
wxDateTime GetCreateTime(void)
Returns the Create Time of this RoutePoint in UTC.
float m_fWaypointRangeRingsStep
Distance between consecutive range rings.
wxLongLong m_seg_ete
Estimated Time Enroute for the leg leading to this waypoint.
wxString m_TideStation
Associated tide station identifier.
bool m_bShowWaypointRangeRings
Flag indicating if range rings should be shown around the waypoint.
void SetETD(const wxDateTime &etd)
Sets the Estimated Time of Departure for this waypoint, in UTC.
wxDateTime GetETA()
Retrieves the Estimated Time of Arrival for this waypoint, in UTC.
bool m_bIsListed
Flag indicating if the waypoint should appear in the Route Manager dialog's waypoint list.
Represents a navigational route in the navigation system.
Definition route.h:98
wxArrayPtrVoid * GetRouteArrayContaining(RoutePoint *pWP)
Find all routes that contain the given waypoint.
Definition routeman.cpp:178
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.