OpenCPN Partial API docs
route.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  *
5  ***************************************************************************
6  * Copyright (C) 2013 by David S. Register *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the *
20  * Free Software Foundation, Inc., *
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22  **************************************************************************/
23 
24 // For compilers that support precompilation, includes "wx.h".
25 #include <wx/wxprec.h>
26 
27 #ifndef WX_PRECOMP
28 #include <wx/wx.h>
29 #endif // precompiled headers
30 
31 #ifndef WX_PRECOMP
32 #include "wx/wx.h"
33 #endif // precompiled headers
34 
35 #include <wx/arrstr.h>
36 #include <wx/datetime.h>
37 #include <wx/gdicmn.h>
38 #include <wx/log.h>
39 #include <wx/pen.h>
40 #include <wx/string.h>
41 
42 #include "model/config_vars.h"
43 #include "model/cutil.h"
44 #include "model/georef.h"
45 #include "model/georef.h"
46 #include "model/config_vars.h"
47 #include "model/nav_object_database.h"
48 #include "model/route.h"
49 #include "model/routeman.h"
50 #include "model/select.h"
51 
52 WayPointman *pWayPointMan;
53 double g_defaultBoatSpeed;
54 
55 #include <wx/listimpl.cpp>
56 WX_DEFINE_LIST(RouteList);
57 
58 Route::Route() {
59  m_bRtIsSelected = false;
60  m_bRtIsActive = false;
61  m_pRouteActivePoint = NULL;
62  m_bIsBeingEdited = false;
63  m_bIsBeingCreated = false;
64  m_nm_sequence = 1;
65  m_route_length = 0.0;
66  m_route_time = 0.0;
67  m_bVisible = true;
68  m_bListed = true;
69  m_bDeleteOnArrival = false;
70  m_width = WIDTH_UNDEFINED;
71  m_style = wxPENSTYLE_INVALID;
72  m_hiliteWidth = 0;
73 
74  pRoutePointList = new RoutePointList;
75  m_GUID = pWayPointMan->CreateGUID(NULL);
76  m_btemp = false;
77 
78  m_ArrivalRadius = g_n_arrival_circle_radius; // Nautical Miles
79 
80  m_LayerID = 0;
81  m_bIsInLayer = false;
82 
83  m_Colour = wxEmptyString;
84 
85  m_lastMousePointIndex = 0;
86  m_NextLegGreatCircle = false;
87 
88  m_PlannedSpeed = ROUTE_DEFAULT_SPEED;
89  if (g_defaultBoatSpeed != ROUTE_DEFAULT_SPEED) m_PlannedSpeed = g_defaultBoatSpeed;
90 
91  m_PlannedDeparture = RTE_UNDEF_DEPARTURE;
92  m_TimeDisplayFormat = RTE_TIME_DISP_PC;
93  m_HyperlinkList = new HyperlinkList;
94 
95  m_bsharedWPViz = false;
96 }
97 
98 Route::~Route() {
99  pRoutePointList->DeleteContents(false); // do not delete Marks
100  delete pRoutePointList;
101  delete m_HyperlinkList;
102 }
103 
104 // The following is used only for route splitting, assumes just created, empty
105 // route
106 //
107 void Route::CloneRoute(Route *psourceroute, int start_nPoint, int end_nPoint,
108  const wxString &suffix,
109  const bool duplicate_first_point) {
110  m_RouteNameString = psourceroute->m_RouteNameString + suffix;
111  m_RouteStartString = psourceroute->m_RouteStartString;
112  m_RouteEndString = psourceroute->m_RouteEndString;
113 
114  int i;
115  for (i = start_nPoint; i <= end_nPoint; i++) {
116  if (!psourceroute->m_bIsInLayer &&
117  !(i == start_nPoint && duplicate_first_point)) {
118  AddPoint(psourceroute->GetPoint(i), false);
119  }
120  else {
121  RoutePoint *psourcepoint = psourceroute->GetPoint(i);
122  RoutePoint *ptargetpoint = new RoutePoint(
123  psourcepoint->m_lat, psourcepoint->m_lon, psourcepoint->GetIconName(),
124  psourcepoint->GetName(), wxEmptyString, true);
125  ptargetpoint->m_bShowName =
126  psourcepoint->m_bShowName; // do not change new wpt's name visibility
127  AddPoint(ptargetpoint, false);
128  }
129  }
130 
131  FinalizeForRendering();
132 }
133 
134 void Route::AddPoint(RoutePoint *pNewPoint, bool b_rename_in_sequence,
135  bool b_deferBoxCalc) {
136  if (pNewPoint->m_bIsolatedMark) {
137  pNewPoint->SetShared(true);
138  }
139  pNewPoint->m_bIsolatedMark = false; // definitely no longer isolated
140  pNewPoint->m_bIsInRoute = true;
141 
142  RoutePoint *prev = GetLastPoint();
143  pRoutePointList->Append(pNewPoint);
144 
145  if (!b_deferBoxCalc) FinalizeForRendering();
146 
147  if (prev) UpdateSegmentDistance(prev, pNewPoint);
148 
149  if (b_rename_in_sequence && pNewPoint->GetName().IsEmpty() &&
150  !pNewPoint->IsShared()) {
151  wxString name;
152  name.Printf(_T("%03d"), GetnPoints());
153  pNewPoint->SetName(name);
154  pNewPoint->m_bDynamicName = true;
155  }
156  return;
157 }
158 
159 void Route::AddPointAndSegment(RoutePoint *pNewPoint, bool b_rename_in_sequence,
160  bool b_deferBoxCalc) {
161  int npoints = GetnPoints();
162  RoutePoint *newpoint = pNewPoint;
163  if (newpoint->m_bIsInLayer) {
164  newpoint = new RoutePoint(pNewPoint->m_lat, pNewPoint->m_lon,
165  pNewPoint->GetIconName(), pNewPoint->GetName(), wxEmptyString, false);
166  newpoint->m_bShowName = pNewPoint->m_bShowName; //do not change new wpt's name visibility
167  }
168  AddPoint(newpoint, false);
169  if (npoints != 0) {
170  double rlat = GetPoint(npoints)->m_lat;
171  double rlon = GetPoint(npoints)->m_lon;
172  npoints = GetnPoints();
173  pSelect->AddSelectableRouteSegment(rlat, rlon,
174  GetPoint(npoints)->m_lat, GetPoint(npoints)->m_lon, GetPoint(npoints - 1), GetPoint(npoints), this);
175  }
176  m_lastMousePointIndex = GetnPoints();
177 }
178 
179 void Route::InsertPointAndSegment(RoutePoint *pNewPoint, int insert_after, bool bRenamePoints, bool b_deferBoxCalc)
180 {
181  {
182  bool add = false;
183 
184  if (pNewPoint->m_bIsolatedMark) {
185  pNewPoint->SetShared(true);
186  }
187  pNewPoint->m_bIsolatedMark = false; // definitely no longer isolated
188  pNewPoint->m_bIsInRoute = true;
189 
190  if (insert_after >= GetnPoints() - 1) {
191  wxLogMessage(wxT("Error insert after last point"));
192  return;
193  }
194 
195  int insert = insert_after++;
196  pNewPoint->m_bIsInRoute = true;
197  pNewPoint->m_bDynamicName = true;
198  pNewPoint->SetNameShown(false);
199  pRoutePointList->Insert(insert, pNewPoint);
200  if (bRenamePoints) RenameRoutePoints();
201  m_lastMousePointIndex = GetnPoints();
202  FinalizeForRendering();
203  UpdateSegmentDistances();
204  return;
205  }
206 }
207 
208 RoutePoint *Route::GetPoint(int nWhichPoint) {
209  RoutePoint *prp;
210  wxRoutePointListNode *node = pRoutePointList->GetFirst();
211 
212  int i = 1;
213  while (node) {
214  prp = node->GetData();
215  if (i == nWhichPoint) {
216  return prp;
217  }
218  i++;
219  node = node->GetNext();
220  }
221 
222  return (NULL);
223 }
224 
225 RoutePoint *Route::GetPoint(const wxString &guid) {
226  RoutePoint *prp;
227  wxRoutePointListNode *node = pRoutePointList->GetFirst();
228 
229  while (node) {
230  prp = node->GetData();
231  if (guid == prp->m_GUID) return prp;
232 
233  node = node->GetNext();
234  }
235 
236  return (NULL);
237 }
238 
239 static void TestLongitude(double lon, double min, double max, bool &lonl,
240  bool &lonr) {
241  double clon = (min + max) / 2;
242  if (min - lon > 180) lon += 360;
243 
244  lonl = lonr = false;
245  if (lon < min) {
246  if (lon < clon - 180)
247  lonr = true;
248  else
249  lonl = true;
250  } else if (lon > max) {
251  if (lon > clon + 180)
252  lonl = true;
253  else
254  lonr = true;
255  }
256 }
257 
258 bool Route::ContainsSharedWP() {
259  for (wxRoutePointListNode *node = pRoutePointList->GetFirst(); node;
260  node = node->GetNext()) {
261  RoutePoint *prp = node->GetData();
262  if (prp->IsShared()) return true;
263  }
264  return false;
265 }
266 
267 // FIXME (leamas): can this be moved to GUI?
268 int s_arrow_icon[] = {0, 0, 5, 2, 18, 6, 12, 0, 18, -6, 5, -2, 0, 0};
269 void Route::ClearHighlights(void) {
270  RoutePoint *prp = NULL;
271  wxRoutePointListNode *node = pRoutePointList->GetFirst();
272 
273  while (node) {
274  prp = node->GetData();
275  if (prp) prp->m_bPtIsSelected = false;
276  node = node->GetNext();
277  }
278 }
279 
280 RoutePoint *Route::InsertPointBefore(RoutePoint *pRP, double rlat, double rlon,
281  bool bRenamePoints) {
282  RoutePoint *newpoint = new RoutePoint(rlat, rlon, g_default_routepoint_icon,
283  GetNewMarkSequenced(), wxEmptyString);
284  newpoint->m_bIsInRoute = true;
285  newpoint->m_bDynamicName = true;
286  newpoint->SetNameShown(false);
287 
288  int nRP = pRoutePointList->IndexOf(pRP);
289  pRoutePointList->Insert(nRP, newpoint);
290 
291  if (bRenamePoints) RenameRoutePoints();
292 
293  FinalizeForRendering();
294  UpdateSegmentDistances();
295 
296  return (newpoint);
297 }
298 
299 RoutePoint *Route::InsertPointAfter(RoutePoint *pRP, double rlat, double rlon,
300  bool bRenamePoints) {
301  int nRP = pRoutePointList->IndexOf(pRP);
302  if (nRP >= GetnPoints() - 1) return NULL;
303  nRP++;
304 
305  RoutePoint *newpoint = new RoutePoint(rlat, rlon, g_default_routepoint_icon,
306  GetNewMarkSequenced(), wxEmptyString);
307  newpoint->m_bIsInRoute = true;
308  newpoint->m_bDynamicName = true;
309  newpoint->SetNameShown(false);
310 
311  pRoutePointList->Insert(nRP, newpoint);
312 
313  if (bRenamePoints) RenameRoutePoints();
314 
315  FinalizeForRendering();
316  UpdateSegmentDistances();
317 
318  return (newpoint);
319 }
320 
321 wxString Route::GetNewMarkSequenced(void) {
322  wxString ret;
323  ret.Printf(_T ( "NM%03d" ), m_nm_sequence);
324  m_nm_sequence++;
325 
326  return ret;
327 }
328 
329 RoutePoint *Route::GetLastPoint() {
330  if (pRoutePointList->IsEmpty()) return NULL;
331 
332  return pRoutePointList->GetLast()->GetData();
333 }
334 
335 int Route::GetIndexOf(RoutePoint *prp) {
336  int ret = pRoutePointList->IndexOf(prp) + 1;
337  if (ret == wxNOT_FOUND)
338  return 0;
339  else
340  return ret;
341 }
342 
343 void Route::DeletePoint(RoutePoint *rp, bool bRenamePoints) {
344  // n.b. must delete Selectables and update config before deleting the
345  // point
346  if (rp->m_bIsInLayer) return;
347 
348  pSelect->DeleteAllSelectableRoutePoints(this);
349  pSelect->DeleteAllSelectableRouteSegments(this);
350  NavObjectChanges::getInstance()->DeleteWayPoint(rp);
351 
352  pRoutePointList->DeleteObject(rp);
353 
354  delete rp;
355 
356  if (bRenamePoints) RenameRoutePoints();
357 
358  if (GetnPoints() > 1) {
359  pSelect->AddAllSelectableRouteSegments(this);
360  pSelect->AddAllSelectableRoutePoints(this);
361 
362  NavObjectChanges::getInstance()->UpdateRoute(this);
363 
364  FinalizeForRendering();
365  UpdateSegmentDistances();
366  }
367 }
368 
369 void Route::RemovePoint(RoutePoint *rp, bool bRenamePoints) {
370  if (rp->m_bIsActive && this->IsActive()) // FS#348
371  g_pRouteMan->DeactivateRoute();
372 
373  pSelect->DeleteAllSelectableRoutePoints(this);
374  pSelect->DeleteAllSelectableRouteSegments(this);
375 
376  pRoutePointList->DeleteObject(rp);
377 
378  // check all other routes to see if this point appears in any other route
379  Route *pcontainer_route = FindRouteContainingWaypoint(rp);
380 
381  if (pcontainer_route == NULL) {
382  rp->m_bIsInRoute = false; // Take this point out of this (and only) route
383  rp->m_bDynamicName = false;
384  rp->m_bIsolatedMark = true; // This has become an isolated mark
385  }
386 
387  if (bRenamePoints) RenameRoutePoints();
388 
389  // if ( m_nPoints > 1 )
390  {
391  pSelect->AddAllSelectableRouteSegments(this);
392  pSelect->AddAllSelectableRoutePoints(this);
393 
394  NavObjectChanges::getInstance()->UpdateRoute(this);
395 
396  FinalizeForRendering();
397  UpdateSegmentDistances();
398  }
399 }
400 
401 void Route::DeSelectRoute() {
402  wxRoutePointListNode *node = pRoutePointList->GetFirst();
403 
404  RoutePoint *rp;
405  while (node) {
406  rp = node->GetData();
407  rp->m_bPtIsSelected = false;
408 
409  node = node->GetNext();
410  }
411 }
412 
413 void Route::ReloadRoutePointIcons() {
414  wxRoutePointListNode *node = pRoutePointList->GetFirst();
415 
416  RoutePoint *rp;
417  while (node) {
418  rp = node->GetData();
419  rp->ReLoadIcon();
420 
421  node = node->GetNext();
422  }
423 }
424 
425 void Route::FinalizeForRendering() { RBBox.Invalidate(); }
426 
427 LLBBox &Route::GetBBox(void) {
428  if (RBBox.GetValid()) return RBBox;
429 
430  double bbox_lonmin, bbox_lonmax, bbox_latmin, bbox_latmax;
431 
432  wxRoutePointListNode *node = pRoutePointList->GetFirst();
433  RoutePoint *data = node->GetData();
434 
435  if (data->m_wpBBox.GetValid()) {
436  bbox_lonmax = data->m_wpBBox.GetMaxLon();
437  bbox_lonmin = data->m_wpBBox.GetMinLon();
438  bbox_latmax = data->m_wpBBox.GetMaxLat();
439  bbox_latmin = data->m_wpBBox.GetMinLat();
440  } else {
441  bbox_lonmax = bbox_lonmin = data->m_lon;
442  bbox_latmax = bbox_latmin = data->m_lat;
443  }
444 
445  double lastlon = data->m_lon, wrap = 0;
446 
447  node = node->GetNext();
448  while (node) {
449  data = node->GetData();
450 
451  if (lastlon - data->m_lon > 180)
452  wrap += 360;
453  else if (data->m_lon - lastlon > 180)
454  wrap -= 360;
455 
456  double lon = data->m_lon + wrap;
457 
458  if (lon > bbox_lonmax) bbox_lonmax = lon;
459  if (lon < bbox_lonmin) bbox_lonmin = lon;
460 
461  if (data->m_lat > bbox_latmax) bbox_latmax = data->m_lat;
462  if (data->m_lat < bbox_latmin) bbox_latmin = data->m_lat;
463 
464  lastlon = data->m_lon;
465  node = node->GetNext();
466  }
467 
468  if (bbox_lonmin < -360)
469  bbox_lonmin += 360, bbox_lonmax += 360;
470  else if (bbox_lonmax > 360)
471  bbox_lonmin -= 360, bbox_lonmax -= 360;
472 
473  if (bbox_lonmax - bbox_lonmin > 360) bbox_lonmin = -180, bbox_lonmax = 180;
474 
475  RBBox.Set(bbox_latmin, bbox_lonmin, bbox_latmax, bbox_lonmax);
476 
477  return RBBox;
478 }
479 
480 /*
481  Update a single route segment lengths
482  Also, compute total route length by summing segment distances.
483  */
484 void Route::UpdateSegmentDistance(RoutePoint *prp0, RoutePoint *prp,
485  double planspeed) {
486  double slat1 = prp0->m_lat, slon1 = prp0->m_lon;
487  double slat2 = prp->m_lat, slon2 = prp->m_lon;
488 
489  // Calculate the absolute distance from 1->2
490 
491  double dd;
492  double br;
493  // why are we using mercator rather than great circle here?? [sean 8-11-2015]
494  DistanceBearingMercator(slat2, slon2, slat1, slon1, &br, &dd);
495 
496  prp->SetCourse(br);
497  prp->SetDistance(dd);
498 
499  // And store in Point 2
500  prp->m_seg_len = dd;
501 
502  m_route_length += dd;
503 
504  // If Point1 Description contains VMG, store it for Properties Dialog in
505  // Point2 If Point1 Description contains ETD, store it in Point1
506 
507  if (planspeed > 0.) {
508  wxDateTime etd;
509 
510  double legspeed = planspeed;
511  if (prp->GetPlannedSpeed() > 0.1 && prp->GetPlannedSpeed() < 1000.)
512  legspeed = prp->GetPlannedSpeed();
513  if (legspeed > 0.1 && legspeed < 1000.) {
514  m_route_time += 3600. * dd / legspeed;
515  prp->m_seg_vmg = legspeed;
516  }
517  wxLongLong duration = wxLongLong(3600.0 * prp->m_seg_len / prp->m_seg_vmg);
518  prp->SetETE(duration);
519  wxTimeSpan ts(0, 0, duration);
520  if (!prp0->GetManualETD().IsValid()) {
521  prp0->m_manual_etd = false;
522  if (prp0->GetETA().IsValid()) {
523  prp0->m_seg_etd = prp0->GetETA();
524  } else {
525  prp0->m_seg_etd =
526  m_PlannedDeparture + wxTimeSpan(0, 0, m_route_time - duration);
527  }
528  }
529 
530  prp->m_seg_eta = prp0->GetETD() + ts;
531  if (!prp->m_manual_etd || !prp->GetETD().IsValid()) {
532  prp->m_seg_etd = prp->m_seg_eta;
533  prp->m_manual_etd = false;
534  }
535  }
536 }
537 
538 /*
539  Update the route segment lengths, storing each segment length in <destination>
540  point. Also, compute total route length by summing segment distances.
541  */
542 void Route::UpdateSegmentDistances(double planspeed) {
543  wxPoint rpt, rptn;
544 
545  m_route_length = 0.0;
546  m_route_time = 0.0;
547 
548  wxRoutePointListNode *node = pRoutePointList->GetFirst();
549 
550  if (node) {
551  // Route start point
552  RoutePoint *prp0 = node->GetData();
553  if (!prp0->m_manual_etd) {
554  prp0->m_seg_eta = m_PlannedDeparture;
555  prp0->m_seg_etd = m_PlannedDeparture;
556  }
557  node = node->GetNext();
558 
559  while (node) {
560  RoutePoint *prp = node->GetData();
561  UpdateSegmentDistance(prp0, prp, planspeed);
562 
563  prp0 = prp;
564 
565  node = node->GetNext();
566  }
567  }
568 }
569 
570 void Route::Reverse(bool bRenamePoints) {
571  // Reverse the GUID list
572  wxArrayString RoutePointGUIDList;
573 
574  int ncount = pRoutePointList->GetCount();
575  for (int i = 0; i < ncount; i++)
576  RoutePointGUIDList.Add(GetPoint(ncount - i)->m_GUID);
577 
578  pRoutePointList->DeleteContents(false);
579  pRoutePointList->Clear();
580  m_route_length = 0.0;
581 
582  // iterate over the RoutePointGUIDs
583  for (unsigned int ip = 0; ip < RoutePointGUIDList.GetCount(); ip++) {
584  wxString GUID = RoutePointGUIDList[ip];
585 
586  // And on the RoutePoints themselves
587  wxRoutePointListNode *prpnode = pWayPointMan->GetWaypointList()->GetFirst();
588  while (prpnode) {
589  RoutePoint *prp = prpnode->GetData();
590 
591  if (prp->m_GUID == GUID) {
592  AddPoint(prp);
593  break;
594  }
595  prpnode = prpnode->GetNext(); // RoutePoint
596  }
597  }
598 
599  if (bRenamePoints) RenameRoutePoints();
600 
601  // Switch start/end strings. anders, 2010-01-29
602  wxString tmp = m_RouteStartString;
603  m_RouteStartString = m_RouteEndString;
604  m_RouteEndString = tmp;
605 }
606 
607 void Route::SetVisible(bool visible, bool includeWpts) {
608  m_bVisible = visible;
609 
610  if (!includeWpts) return;
611 
612  wxRoutePointListNode *node = pRoutePointList->GetFirst();
613  RoutePoint *rp;
614  while (node) {
615  rp = node->GetData();
616 
617  // if this is a "shared" point, then do not turn off visibility.
618  // This step keeps the point available for selection to other routes,
619  // or may be manaully hidden in route-manager dialog.
620  if (rp->IsShared()) {
621  if (visible) rp->SetVisible(visible);
622  }
623  node = node->GetNext();
624  }
625 }
626 
627 void Route::SetListed(bool visible) { m_bListed = visible; }
628 
629 void Route::AssembleRoute(void) {}
630 
631 void Route::ShowWaypointNames(bool bshow) {
632  wxRoutePointListNode *node = pRoutePointList->GetFirst();
633 
634  while (node) {
635  RoutePoint *prp = node->GetData();
636  prp->SetNameShown(bshow);
637 
638  node = node->GetNext();
639  }
640 }
641 
642 bool Route::AreWaypointNamesVisible() {
643  bool bvis = false;
644  wxRoutePointListNode *node = pRoutePointList->GetFirst();
645 
646  while (node) {
647  RoutePoint *prp = node->GetData();
648  if (prp->GetNameShown()) bvis = true;
649 
650  node = node->GetNext();
651  }
652 
653  return bvis;
654 }
655 
656 void Route::RenameRoutePoints(void) {
657  // iterate on the route points.
658  // If dynamically named, rename according to current list position
659 
660  wxRoutePointListNode *node = pRoutePointList->GetFirst();
661 
662  int i = 1;
663  while (node) {
664  RoutePoint *prp = node->GetData();
665  if (prp->m_bDynamicName) {
666  wxString name;
667  name.Printf(_T ( "%03d" ), i);
668  prp->SetName(name);
669  }
670 
671  node = node->GetNext();
672  i++;
673  }
674 }
675 
676 // Is this route equal to another, meaning,
677 // Do all routepoint positions and names match?
678 bool Route::IsEqualTo(Route *ptargetroute) {
679  wxRoutePointListNode *pthisnode = (this->pRoutePointList)->GetFirst();
680  wxRoutePointListNode *pthatnode = (ptargetroute->pRoutePointList)->GetFirst();
681 
682  if (NULL == pthisnode) return false;
683 
684  if (this->m_bIsInLayer || ptargetroute->m_bIsInLayer) return false;
685 
686  if (this->GetnPoints() != ptargetroute->GetnPoints()) return false;
687 
688  while (pthisnode) {
689  if (NULL == pthatnode) return false;
690 
691  RoutePoint *pthisrp = pthisnode->GetData();
692  RoutePoint *pthatrp = pthatnode->GetData();
693 
694  if ((fabs(pthisrp->m_lat - pthatrp->m_lat) > 1.0e-6) ||
695  (fabs(pthisrp->m_lon - pthatrp->m_lon) > 1.0e-6))
696  return false;
697 
698  if (!pthisrp->GetName().IsSameAs(pthatrp->GetName())) return false;
699 
700  pthisnode = pthisnode->GetNext();
701  pthatnode = pthatnode->GetNext();
702  }
703 
704  return true; // success, they are the same
705 }
Definition: route.h:75