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);
76 m_NameLocationOffsetX = -10;
77 m_NameLocationOffsetY = 8;
78 m_pMarkFont = NULL;
79 m_btemp = false;
80 m_SelectNode = NULL;
81 m_ManagerNode = NULL;
82
83 m_iTextTexture = 0;
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
129 m_bPtIsSelected = orig->m_bPtIsSelected;
130 m_bRPIsBeingEdited = orig->m_bRPIsBeingEdited;
131 m_bIsActive = orig->m_bIsActive;
132 m_bBlink = orig->m_bBlink;
133 m_bIsInRoute = orig->m_bIsInRoute;
134 m_CreateTimeX = orig->m_CreateTimeX;
136 m_bShowName = orig->m_bShowName;
137 SetShared(orig->IsShared());
138 m_bIsVisible = orig->m_bIsVisible;
139 m_bIsListed = orig->m_bIsListed;
140 CurrentRect_in_DC = orig->CurrentRect_in_DC;
141 m_NameLocationOffsetX = orig->m_NameLocationOffsetX;
142 m_NameLocationOffsetY = orig->m_NameLocationOffsetY;
143 m_pMarkFont = orig->m_pMarkFont;
144 m_MarkDescription = orig->m_MarkDescription;
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;
150 m_TideStation = orig->m_TideStation;
151 SetPlannedSpeed(orig->GetPlannedSpeed());
152
153 m_bIsInLayer = orig->m_bIsInLayer;
154 m_GUID = pWayPointMan->CreateGUID(this);
155
156 m_SelectNode = NULL;
157 m_ManagerNode = NULL;
158
159 m_WaypointArrivalRadius = orig->GetWaypointArrivalRadius();
160 m_bShowWaypointRangeRings = orig->m_bShowWaypointRangeRings;
161 m_iWaypointRangeRingsNumber = orig->m_iWaypointRangeRingsNumber;
162 m_fWaypointRangeRingsStep = orig->m_fWaypointRangeRingsStep;
163 m_iWaypointRangeRingsStepUnits = orig->m_iWaypointRangeRingsStepUnits;
164 m_wxcWaypointRangeRingsColour = orig->m_wxcWaypointRangeRingsColour;
165 m_ScaMin = orig->m_ScaMin;
166 m_ScaMax = orig->m_ScaMax;
167 b_UseScamin = orig->b_UseScamin;
168 m_IconIsDirty = orig->m_IconIsDirty;
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);
207 m_NameLocationOffsetX = -10;
208 m_NameLocationOffsetY = 8;
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 m_HyperlinkList->DeleteContents(true);
267 delete m_HyperlinkList;
268 }
269 RoutePoint::delete_gl_textures(1, &m_dragIconTexture);
270}
271
272wxDateTime RoutePoint::GetCreateTime() {
273 if (!m_CreateTimeX.IsValid()) {
274 if (m_timestring.Len()) ParseGPXDateTime(m_CreateTimeX, m_timestring);
275 }
276 return m_CreateTimeX;
277}
278
279void RoutePoint::SetCreateTime(wxDateTime dt) { m_CreateTimeX = dt; }
280
281void RoutePoint::SetName(const wxString &name) {
282 if (m_iTextTexture) {
283 RoutePoint::delete_gl_textures(1, &m_iTextTexture);
284 m_iTextTexture = 0;
285 }
286 m_MarkName = name;
287 CalculateNameExtents();
288}
289
290void RoutePoint::CalculateNameExtents(void) {
291 if (m_pMarkFont) {
292 wxScreenDC dc;
293
294#ifdef __WXQT__ // avoiding "painter not active" warning
295 int w, h;
296 dc.GetTextExtent(m_MarkName, &w, &h, NULL, NULL, m_pMarkFont);
297 m_NameExtents = wxSize(w, h);
298#else
299 dc.SetFont(*m_pMarkFont);
300 m_NameExtents = dc.GetMultiLineTextExtent(m_MarkName);
301#endif
302 } else
303 m_NameExtents = wxSize(0, 0);
304}
305
306bool RoutePoint::IsVisibleSelectable(double scale_val, bool boverrideViz) {
307 if (m_bIsActive) // An active route point must always be visible
308 return true;
309
310 if (!boverrideViz) {
311 if (!m_bIsVisible) // if not visible nevermind the rest.
312 return false;
313 }
314
315 if (b_UseScamin) {
316 if (g_bOverruleScaMin)
317 return true;
318 else if (scale_val >= (double)(m_ScaMin + 1))
319 return false;
320 }
321 return true;
322}
323
324bool RoutePoint::IsSharedInVisibleRoute() {
325 if (IsShared()) {
326 // Get an array of all routes using this point
327 wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining(this);
328
329 // Use route array (if any) to determine actual visibility for this point
330 bool brp_viz = false;
331 if (proute_array) {
332 for (unsigned int ir = 0; ir < proute_array->GetCount(); ir++) {
333 Route *pr = (Route *)proute_array->Item(ir);
334 if (pr->IsVisible()) {
335 brp_viz = true;
336 break;
337 }
338 }
339 delete proute_array;
340 }
341
342 return brp_viz;
343 } else // point is not shared
344 return false;
345}
346
347void RoutePoint::SetPosition(double lat, double lon) {
348 m_lat = lat;
349 m_lon = lon;
350}
351
352bool RoutePoint::IsSame(RoutePoint *pOtherRP) {
353 bool IsSame = false;
354
355 if (this->m_MarkName == pOtherRP->m_MarkName) {
356 if (fabs(this->m_lat - pOtherRP->m_lat) < 1.e-6 &&
357 fabs(this->m_lon - pOtherRP->m_lon) < 1.e-6)
358 IsSame = true;
359 }
360 return IsSame;
361}
362
369 bool b_numeric = false;
370 if (m_bIsInRoute) {
371 if (GetName().Len() >= 2) {
372 wxString substring = GetName().Left(2);
373 if (substring == "NM") {
374 substring = GetName().substr(2, 3);
375 } else {
376 substring = GetName().Left(3);
377 }
378 b_numeric = true; // assume it is numeric
379 for (unsigned int i = 0; i < substring.Len(); i++) {
380 if (b_numeric == true) {
381 b_numeric = wxIsdigit(substring[i]);
382 } // don't change the value if it is already false
383 }
384 }
385 }
386 return b_numeric;
387}
388
389double RoutePoint::GetWaypointArrivalRadius() {
390 if ((m_WaypointArrivalRadius >= 0) && (m_WaypointArrivalRadius < 0.001)) {
391 SetWaypointArrivalRadius(g_n_arrival_circle_radius);
392 return m_WaypointArrivalRadius;
393 } else
394 return m_WaypointArrivalRadius;
395}
396
397int RoutePoint::GetWaypointRangeRingsNumber() {
398 if (m_iWaypointRangeRingsNumber == -1)
399 return g_iWaypointRangeRingsNumber;
400 else
401 return m_iWaypointRangeRingsNumber;
402}
403
404float RoutePoint::GetWaypointRangeRingsStep() {
405 if (m_fWaypointRangeRingsStep == -1)
406 return g_fWaypointRangeRingsStep;
407 else
408 return m_fWaypointRangeRingsStep;
409}
410
411int RoutePoint::GetWaypointRangeRingsStepUnits() {
412 if (m_iWaypointRangeRingsStepUnits == -1)
413 return g_iWaypointRangeRingsStepUnits;
414 else
415 return m_iWaypointRangeRingsStepUnits;
416}
417
418void RoutePoint::SetScaMin(long val) {
419 if (val < SCAMIN_MIN)
420 val = SCAMIN_MIN; // prevent from waypoints hiding always with a nonlogic
421 // value
422 if (val < (long)m_ScaMax * 5) val = (long)m_ScaMax * 5;
423 m_ScaMin = val;
424}
425void RoutePoint::SetScaMin(wxString str) {
426 long val;
427 if (!str.ToLong(&val)) val = MAX_INT_VAL;
428 SetScaMin(val);
429}
430
431void RoutePoint::SetScaMax(long val) {
432 if (val > (int)m_ScaMin / 5)
433 m_ScaMax = (int)m_ScaMin /
434 5; // prevent from waypoints hiding always with a nonlogic value
435}
436void RoutePoint::SetScaMax(wxString str) {
437 long val;
438 if (!str.ToLong(&val)) val = 0;
439 SetScaMax(val);
440}
441
442void RoutePoint::SetPlannedSpeed(double spd) {
443 if (spd >= 0.0 && spd <= 1000.0) m_PlannedSpeed = spd;
444}
445
446double RoutePoint::GetPlannedSpeed() {
447 if (m_PlannedSpeed < 0.0001 &&
448 m_MarkDescription.Find(_T("VMG=")) != wxNOT_FOUND) {
449 // In case there was speed encoded in the name of the waypoint, do the
450 // conversion here.
451 wxString s_vmg =
452 (m_MarkDescription.Mid(m_MarkDescription.Find(_T("VMG=")) + 4))
453 .BeforeFirst(';');
454 double vmg;
455 if (!s_vmg.ToDouble(&vmg)) {
456 m_MarkDescription.Replace(_T("VMG=") + s_vmg + ";", wxEmptyString);
457 SetPlannedSpeed(vmg);
458 }
459 }
460 return m_PlannedSpeed;
461}
462
463wxDateTime RoutePoint::GetETD() {
464 if (m_seg_etd.IsValid()) {
465 if (!GetETA().IsValid() || m_seg_etd > GetETA()) {
466 return m_seg_etd;
467 } else {
468 return GetETA();
469 }
470 } else {
471 if (m_MarkDescription.Find(_T("ETD=")) != wxNOT_FOUND) {
472 wxDateTime etd = wxInvalidDateTime;
473 wxString s_etd =
474 (m_MarkDescription.Mid(m_MarkDescription.Find(_T("ETD=")) + 4))
475 .BeforeFirst(';');
476 const wxChar *parse_return = etd.ParseDateTime(s_etd);
477 if (parse_return) {
478 wxString tz(parse_return);
479
480 if (tz.Find(_T("UT")) != wxNOT_FOUND) {
481 // TODO: This is error-prone. It would match any string containing
482 // these characters, not just time zone codes For example, "UT" would
483 // match "UTC+2".
484 m_seg_etd = etd;
485 } else {
486 if (tz.Find(_T("LMT")) != wxNOT_FOUND) {
487 m_seg_etd = etd;
488 long lmt_offset = (long)((m_lon * 3600.) / 15.);
489 wxTimeSpan lmt(0, 0, (int)lmt_offset, 0);
490 m_seg_etd -= lmt;
491 } else {
492 m_seg_etd = etd.ToUTC();
493 }
494 }
495 if (etd.IsValid() && (!GetETA().IsValid() || etd > GetETA())) {
496 m_MarkDescription.Replace(s_etd, wxEmptyString);
497 m_seg_etd = etd;
498 return m_seg_etd;
499 } else {
500 return GetETA();
501 }
502 }
503 }
504 }
505 return wxInvalidDateTime;
506}
507
509 if (m_manual_etd && m_seg_etd.IsValid()) {
510 return m_seg_etd;
511 }
512 return wxInvalidDateTime;
513}
514
515wxDateTime RoutePoint::GetETA() {
516 if (m_seg_eta.IsValid()) {
517 return m_seg_eta;
518 }
519 return wxInvalidDateTime;
520}
521
523 if (m_seg_ete != 0) {
524 return formatTimeDelta(m_seg_ete);
525 }
526 return wxEmptyString;
527}
528
529void RoutePoint::SetETE(wxLongLong secs) { m_seg_ete = secs; }
530
531void RoutePoint::SetETD(const wxDateTime &etd) {
532 m_seg_etd = etd;
533 m_manual_etd = TRUE;
534}
535
536bool RoutePoint::SetETD(const wxString &ts) {
537 if (ts.IsEmpty()) {
538 m_seg_etd = wxInvalidDateTime;
539 m_manual_etd = false;
540 return true;
541 }
542 wxDateTime tmp;
543 wxString::const_iterator end;
544 // No timezone conversion is done because the serialized string
545 // does not include timezone information, e.g., "2025-03-26T18:57:01"
546 // The input string is assumed to be in UTC format.
547 if (tmp.ParseISOCombined(ts)) {
548 SetETD(tmp);
549 return TRUE;
550 } else if (tmp.ParseDateTime(ts, &end)) {
551 SetETD(tmp);
552 return TRUE;
553 }
554 return FALSE;
555}
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
static std::function< void(unsigned, const unsigned *)> delete_gl_textures
Horrible Hack (tm).
Definition route_point.h:50
wxDateTime GetETD()
Retrieves the Estimated Time of Departure for this waypoint, in UTC.
bool IsNameDynamic()
bool m_bIsolatedMark
Flag indicating if the waypoint is a standalone mark.
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.
wxString GetETE()
Retrieves the Estimated Time En route as a formatted string.
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.
wxDateTime m_seg_eta
Estimated Time of Arrival at this waypoint, in UTC.
wxLongLong m_seg_ete
Estimated Time Enroute for the leg leading to this 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.
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:174
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.