80#include <wx/datetime.h>
82#include <wx/jsonval.h>
84#include <wx/progdlg.h>
91#include "json_event.h"
92#include "nav_object_database.h"
93#include "navutil_base.h"
100extern double g_PlanSpeed;
101extern int g_nTrackPrecision;
102extern bool g_bTrackDaily;
103extern bool g_bHighliteTracks;
104extern double g_TrackDeltaDistance;
105extern float g_GLMinSymbolLineWidth;
106extern wxColour g_colourTrackLineColour;
107extern wxColor GetDimColor(wxColor c);
108extern int g_trackFilterMax;
111#if defined(__UNIX__) && \
116 void Reset() { clock_gettime(CLOCK_REALTIME, &tp); }
120 clock_gettime(CLOCK_REALTIME, &tp_end);
121 return (tp_end.tv_sec - tp.tv_sec) * 1.e3 +
122 (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
130TrackPoint::TrackPoint(
double lat,
double lon, wxString ts)
131 : m_lat(lat), m_lon(lon), m_GPXTrkSegNo(1) {
135TrackPoint::TrackPoint(
double lat,
double lon, wxDateTime dt)
136 : m_lat(lat), m_lon(lon), m_GPXTrkSegNo(1) {
142 : m_lat(orig->m_lat),
145 SetCreateTime(orig->GetCreateTime());
148TrackPoint::~TrackPoint() { }
150wxDateTime TrackPoint::GetCreateTime() {
151 wxDateTime CreateTimeX;
152 ParseGPXDateTime(CreateTimeX, wxString(m_stimestring.c_str()));
156void TrackPoint::SetCreateTime(wxDateTime dt) {
159 ts = dt.FormatISODate()
161 .Append(dt.FormatISOTime())
167void TrackPoint::SetCreateTime(wxString ts) {
169 m_stimestring = ts.mb_str();
178double _distance2(vector2D &a, vector2D &b) {
179 return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
181double _distance(vector2D &a, vector2D &b) {
return sqrt(_distance2(a, b)); }
182double _magnitude2(vector2D &a) {
return a.x * a.x + a.y * a.y; }
188 m_width = WIDTH_UNDEFINED;
189 m_style = wxPENSTYLE_INVALID;
191 m_GUID = pWayPointMan->CreateGUID(NULL);
192 m_bIsInLayer =
false;
195 m_HyperlinkList =
new HyperlinkList;
196 m_HighlightedTrackPoint = -1;
200 for (
size_t i = 0; i < TrackPoints.size(); i++)
delete TrackPoints[i];
202 delete m_HyperlinkList;
205#define TIMER_TRACK1 778
208EVT_TIMER(TIMER_TRACK1, ActiveTrack::OnTimerTrack)
212 m_TimerTrack.SetOwner(
this, TIMER_TRACK1);
216 SetPrecision(g_nTrackPrecision);
218 m_prev_time = wxInvalidDateTime;
219 m_lastStoredTP = NULL;
221 wxDateTime now = wxDateTime::Now();
223 trackPointState = firstPoint;
224 m_lastStoredTP = NULL;
228 m_CurrentTrackSeg = 0;
232ActiveTrack::~ActiveTrack() { Stop(); }
234void ActiveTrack::SetPrecision(
int prec) {
236 switch (m_nPrecision) {
238 m_allowedMaxAngle = 10;
239 m_allowedMaxXTE = 0.008;
241 m_minTrackpoint_delta = .004;
245 m_allowedMaxAngle = 10;
246 m_allowedMaxXTE = 0.004;
248 m_minTrackpoint_delta = .002;
252 m_allowedMaxAngle = 10;
253 m_allowedMaxXTE = 0.0015;
255 m_minTrackpoint_delta = .001;
261void ActiveTrack::Start(
void) {
264 m_TimerTrack.Start(1000, wxTIMER_CONTINUOUS);
269void ActiveTrack::Stop(
bool do_add_point) {
276 delta = DistGreatCircle(gLat, gLon, m_lastStoredTP->m_lat,
277 m_lastStoredTP->m_lon);
279 if (delta > m_minTrackpoint_delta) AddPointNow(
true);
288extern std::vector<Track*> g_TrackList;
289Track *ActiveTrack::DoExtendDaily() {
290 Track *pExtendTrack = NULL;
294 if (!pLastPoint->GetCreateTime().IsValid())
297 for (
Track* ptrack : g_TrackList) {
298 if (!ptrack->m_bIsInLayer && ptrack->m_GUID != m_GUID) {
299 TrackPoint *track_node = ptrack->GetLastPoint();
300 if (!track_node->GetCreateTime().IsValid())
302 if (track_node->GetCreateTime() <= pLastPoint->GetCreateTime()) {
304 track_node->GetCreateTime() > pExtendPoint->GetCreateTime()) {
305 pExtendPoint = track_node;
306 pExtendTrack = ptrack;
311 if (pExtendTrack && pExtendTrack->GetPoint(0)
313 .FromTimezone(wxDateTime::GMT0)
314 .IsSameDate(pLastPoint->GetCreateTime().FromTimezone(
315 wxDateTime::GMT0))) {
317 if (pLastPoint->GetCreateTime() == pExtendPoint->GetCreateTime()) begin = 2;
318 pSelect->DeleteAllSelectableTrackSegments(pExtendTrack);
319 wxString suffix = _T(
"");
320 if (GetName().IsNull()) {
321 suffix = pExtendTrack->GetName();
322 if (suffix.IsNull()) suffix = wxDateTime::Today().FormatISODate();
324 pExtendTrack->Clone(
this, begin, GetnPoints(), suffix);
325 pSelect->AddAllSelectableTrackSegments(pExtendTrack);
326 pSelect->DeleteAllSelectableTrackSegments(
this);
330 if (GetName().IsNull()) SetName(wxDateTime::Today().FormatISODate());
335void Track::Clone(
Track *psourcetrack,
int start_nPoint,
int end_nPoint,
336 const wxString &suffix) {
337 if (psourcetrack->m_bIsInLayer)
return;
339 m_TrackNameString = psourcetrack->m_TrackNameString + suffix;
340 m_TrackStartString = psourcetrack->m_TrackStartString;
341 m_TrackEndString = psourcetrack->m_TrackEndString;
343 bool b_splitting = GetnPoints() == 0;
347 startTrkSegNo = psourcetrack->GetPoint(start_nPoint)->m_GPXTrkSegNo;
349 startTrkSegNo = GetLastPoint()->m_GPXTrkSegNo;
352 for (i = start_nPoint; i <= end_nPoint; i++) {
353 TrackPoint *psourcepoint = psourcetrack->GetPoint(i);
357 AddPoint(ptargetpoint);
362void ActiveTrack::AdjustCurrentTrackPoint(
TrackPoint *prototype) {
364 *m_lastStoredTP = *prototype;
365 m_prev_time = prototype->GetCreateTime().FromUTC();
369void ActiveTrack::OnTimerTrack(wxTimerEvent &event) {
374 m_prev_dist = DistGreatCircle(gLat, gLon, m_lastStoredTP->m_lat,
375 m_lastStoredTP->m_lon);
379 bool b_addpoint =
false;
381 if ((m_TrackTimerSec > 0.) && ((
double)m_track_run >= m_TrackTimerSec) &&
382 (m_prev_dist > m_minTrackpoint_delta)) {
390 if ((trackPointState == firstPoint) && !g_bTrackDaily) {
391 wxDateTime now = wxDateTime::Now();
392 if (TrackPoints.empty()) TrackPoints.front()->SetCreateTime(now.ToUTC());
395 m_TimerTrack.Start(1000, wxTIMER_CONTINUOUS);
398void ActiveTrack::AddPointNow(
bool do_add_point) {
399 wxDateTime now = wxDateTime::Now();
401 if (m_prev_dist < 0.0005)
402 if (!do_add_point)
return;
404 if (m_prev_time.IsValid())
405 if (m_prev_time == now)
406 if (!do_add_point)
return;
408 vector2D gpsPoint(gLon, gLat);
415 if (g_trackFilterMax){
416 if (trackPointState != firstPoint)
418 double distToLastGpsPoint = DistGreatCircle(m_lastStoredTP->m_lat, m_lastStoredTP->m_lon, gLon, gLat);
419 if (distToLastGpsPoint > g_trackFilterMax)
return;
427 switch (trackPointState) {
429 TrackPoint *pTrackPoint = AddNewPoint(gpsPoint, now.ToUTC());
430 m_lastStoredTP = pTrackPoint;
431 trackPointState = secondPoint;
432 do_add_point =
false;
436 vector2D pPoint(gLon, gLat);
437 skipPoints.push_back(pPoint);
438 skipTimes.push_back(now.ToUTC());
439 trackPointState = potentialPoint;
442 case potentialPoint: {
443 if (gpsPoint == skipPoints[skipPoints.size() - 1])
break;
445 unsigned int xteMaxIndex = 0;
450 for (
unsigned int i = 0; i < skipPoints.size(); i++) {
451 double xte = GetXTE(m_lastStoredTP->m_lat, m_lastStoredTP->m_lon, gLat,
452 gLon, skipPoints[i].lat, skipPoints[i].lon);
458 if (xteMax > m_allowedMaxXTE) {
460 AddNewPoint(skipPoints[xteMaxIndex], skipTimes[xteMaxIndex]);
461 pSelect->AddSelectableTrackSegment(
462 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon, pTrackPoint->m_lat,
463 pTrackPoint->m_lon, m_lastStoredTP, pTrackPoint,
this);
465 m_prevFixedTP = m_fixedTP;
466 m_fixedTP = m_removeTP;
467 m_removeTP = m_lastStoredTP;
468 m_lastStoredTP = pTrackPoint;
469 for (
unsigned int i = 0; i <= xteMaxIndex; i++) {
470 skipPoints.pop_front();
471 skipTimes.pop_front();
478 if (GetnPoints() > 2) {
480 DistGreatCircle(m_fixedTP->m_lat, m_fixedTP->m_lon,
481 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon);
482 double xte = GetXTE(m_fixedTP, m_lastStoredTP, m_removeTP);
483 if (xte < m_allowedMaxXTE / wxMax(1.0, 2.0 - dist * 2.0)) {
484 TrackPoints.pop_back();
485 TrackPoints.pop_back();
486 TrackPoints.push_back(m_lastStoredTP);
487 pSelect->DeletePointSelectableTrackSegments(m_removeTP);
488 pSelect->AddSelectableTrackSegment(
489 m_fixedTP->m_lat, m_fixedTP->m_lon, m_lastStoredTP->m_lat,
490 m_lastStoredTP->m_lon, m_fixedTP, m_lastStoredTP,
this);
492 m_removeTP = m_fixedTP;
493 m_fixedTP = m_prevFixedTP;
498 skipPoints.push_back(gpsPoint);
499 skipTimes.push_back(now.ToUTC());
506 TrackPoint *pTrackPoint = AddNewPoint(gpsPoint, now.ToUTC());
507 pSelect->AddSelectableTrackSegment(
508 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon, pTrackPoint->m_lat,
509 pTrackPoint->m_lon, m_lastStoredTP, pTrackPoint,
this);
516void Track::ClearHighlights() { m_HighlightedTrackPoint = -1; }
520 if (nWhichPoint < (
int)TrackPoints.size())
521 return TrackPoints[nWhichPoint];
527 if (TrackPoints.empty())
return NULL;
529 return TrackPoints.back();
532static double heading_diff(
double x) {
533 if (x > 180)
return 360 - x;
534 if (x < -180)
return -360 + x;
542double Track::ComputeScale(
int left,
int right) {
543 const double z = WGS84_semimajor_axis_meters * mercator_k0;
544 const double mult = DEGREE * z;
549 double lata = TrackPoints[left]->m_lat, lona = TrackPoints[left]->m_lon;
550 double latb = TrackPoints[right]->m_lat, lonb = TrackPoints[right]->m_lon;
552 double bx = heading_diff(lonb - lona), by = latb - lata;
554 double lengthSquared = bx * bx + by * by;
558 if (lengthSquared > 3)
return INFINITY;
560 if (lengthSquared == 0.0) {
561 for (
int i = left + 1; i < right; i++) {
562 double lat = TrackPoints[i]->m_lat, lon = TrackPoints[i]->m_lon;
564 double vx = heading_diff(lon - lona);
565 double vy = lat - lata;
566 double dist = vx * vx + vy * vy;
568 if (dist > max_dist) max_dist = dist;
571 double invLengthSquared = 1 / lengthSquared;
572 for (
int i = left + 1; i < right; i++) {
573 double lat = TrackPoints[i]->m_lat, lon = TrackPoints[i]->m_lon;
575 double vx = heading_diff(lon - lona);
576 double vy = lat - lata;
577 double t = (vx * bx + vy * by) * invLengthSquared;
581 dist = vx * vx + vy * vy;
583 double wx = heading_diff(lona - lon);
584 double wy = lata - lat;
585 dist = wx * wx + wy * wy;
587 double projx = vx - t * bx;
588 double projy = vy - t * by;
589 dist = projx * projx + projy * projy;
592 if (dist > max_dist) max_dist = dist;
596 return max_dist * mult * mult;
603 TrackPoints.push_back(pNewPoint);
608void Track::Finalize() {
609 if (SubTracks.size())
614 int n = TrackPoints.size() - 1;
617 std::vector<SubTrack> new_level;
620 for (
int i = 0; i < n; i++) {
621 new_level[i].m_box.SetFromSegment(
622 TrackPoints[i]->m_lat, TrackPoints[i]->m_lon,
623 TrackPoints[i + 1]->m_lat, TrackPoints[i + 1]->m_lon);
624 new_level[i].m_scale = 0;
627 for (
int i = 0; i < n; i++) {
629 new_level[i].m_box = SubTracks[level - 1][p].m_box;
630 if (p + 1 < (
int)SubTracks[level - 1].size())
631 new_level[i].m_box.Expand(SubTracks[level - 1][p + 1].m_box);
633 int left = i << level;
634 int right = wxMin(left + (1 << level), TrackPoints.size() - 1);
635 new_level[i].m_scale = ComputeScale(left, right);
638 SubTracks.push_back(new_level);
640 if (n > 1 && n & 1) n++;
649void Track::InsertSubTracks(LLBBox &box,
int level,
int pos) {
650 if (level == (
int)SubTracks.size()) {
651 std::vector<SubTrack> new_level;
652 if (level > 0) box.Expand(SubTracks[level - 1][0].m_box);
654 new_level[pos].m_box = box;
655 SubTracks.push_back(new_level);
656 }
else if (pos < (
int)SubTracks[level].size())
657 SubTracks[level][pos].m_box.Expand(box);
659 SubTracks[level].push_back(
SubTrack());
660 SubTracks[level][pos].m_box = box;
664 SubTracks[level][pos].m_scale = 0;
666 int left = pos << level;
667 int right = wxMin(left + (1 << level), TrackPoints.size() - 1);
668 SubTracks[level][pos].m_scale = ComputeScale(left, right);
671 if (pos > 0) InsertSubTracks(box, level + 1, pos >> 1);
680void Track::AddPointFinalized(
TrackPoint *pNewPoint) {
681 TrackPoints.push_back(pNewPoint);
683 int pos = TrackPoints.size() - 1;
687 box.SetFromSegment(TrackPoints[pos - 1]->m_lat, TrackPoints[pos - 1]->m_lon,
688 TrackPoints[pos]->m_lat, TrackPoints[pos]->m_lon);
689 InsertSubTracks(box, 0, pos - 1);
693TrackPoint *Track::AddNewPoint(vector2D point, wxDateTime time) {
696 AddPointFinalized(tPoint);
698 NavObjectChanges::getInstance()->AddNewTrackPoint(
703 v[
"lat"] = tPoint->m_lat;
704 v[
"lon"] = tPoint->m_lon;
705 v[
"Track_ID"] = m_GUID;
706 std::string msg_id(
"OCPN_TRK_POINT_ADDED");
707 JsonEvent::getInstance().Notify(msg_id, std::make_shared<wxJSONValue>(v));
712void Track::DouglasPeuckerReducer(std::vector<TrackPoint *> &list,
713 std::vector<bool> &keeplist,
int from,
int to,
715 keeplist[from] =
true;
718 int maxdistIndex = -1;
721 for (
int i = from + 1; i < to; i++) {
722 double dist = 1852.0 * GetXTE(list[from], list[to], list[i]);
724 if (dist > maxdist) {
730 if (maxdist > delta) {
731 DouglasPeuckerReducer(list, keeplist, from, maxdistIndex, delta);
732 DouglasPeuckerReducer(list, keeplist, maxdistIndex, to, delta);
736double Track::Length() {
739 for (
size_t i = 0; i < TrackPoints.size(); i++) {
742 const double offsetLat = 1e-6;
743 const double deltaLat = l->m_lat - t->m_lat;
744 if (fabs(deltaLat) > offsetLat)
745 total += DistGreatCircle(l->m_lat, l->m_lon, t->m_lat, t->m_lon);
747 total += DistGreatCircle(l->m_lat + copysign(offsetLat, deltaLat),
748 l->m_lon, t->m_lat, t->m_lon);
756int Track::Simplify(
double maxDelta) {
759 std::vector<TrackPoint *> pointlist;
760 std::vector<bool> keeplist;
762 ::wxBeginBusyCursor();
764 for (
size_t i = 0; i < TrackPoints.size(); i++) {
767 pointlist.push_back(trackpoint);
768 keeplist.push_back(
false);
771 DouglasPeuckerReducer(pointlist, keeplist, 0, pointlist.size() - 1, maxDelta);
773 pSelect->DeleteAllSelectableTrackSegments(
this);
776 for (
size_t i = 0; i < pointlist.size(); i++) {
778 TrackPoints.push_back(pointlist[i]);
785 pSelect->AddAllSelectableTrackSegments(
this);
792Route *Track::RouteFromTrack(wxGenericProgressDialog *pprog) {
801 wxString icon = _T(
"xmblue");
802 if (g_TrackDeltaDistance >= 0.1) icon = _T(
"diamond");
806 int nPoints = TrackPoints.size();
807 bool isProminent =
true;
808 double delta_dist = 0.;
809 double delta_hdg, xte;
810 double leg_speed = 0.1;
812 leg_speed = g_PlanSpeed;
816 pWP_dst =
new RoutePoint(pWP_src->m_lat, pWP_src->m_lon, icon, _T (
"" ),
818 route->AddPoint(pWP_dst);
820 pWP_dst->m_bShowName =
false;
822 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst);
826 for (
size_t i = 1; i < TrackPoints.size();) {
829 pWP_dst->m_lat = pWP_prev->m_lat;
830 pWP_dst->m_lon = pWP_prev->m_lon;
837 DistanceBearingMercator(prp->m_lat, prp->m_lon, pWP_prev->m_lat,
838 pWP_prev->m_lon, &delta_hdg, &delta_dist);
840 if ((delta_dist > (leg_speed * 6.0)) && !prp_OK) {
841 int delta_inserts = floor(delta_dist / (leg_speed * 4.0));
842 delta_dist = delta_dist / (delta_inserts + 1);
846 while (delta_inserts--) {
847 ll_gc_ll(pWP_prev->m_lat, pWP_prev->m_lon, delta_hdg, delta_dist, &tlat,
849 pWP_dst =
new RoutePoint(tlat, tlon, icon, _T (
"" ), wxEmptyString);
850 route->AddPoint(pWP_dst);
851 pWP_dst->m_bShowName =
false;
852 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon,
855 pSelect->AddSelectableRouteSegment(pWP_prev->m_lat, pWP_prev->m_lon,
856 pWP_dst->m_lat, pWP_dst->m_lon,
857 pWP_prev, pWP_dst, route);
870 if (delta_dist >= (leg_speed * 4.0)) isProminent =
true;
871 if (!prp_OK) prp_OK = prp;
873 while (prpnodeX < TrackPoints.size()) {
876 xte = GetXTE(pWP_src, prpX, prp);
877 if (isProminent || (xte > g_TrackDeltaDistance)) {
878 pWP_dst =
new RoutePoint(prp_OK->m_lat, prp_OK->m_lon, icon, _T (
"" ),
881 route->AddPoint(pWP_dst);
882 pWP_dst->m_bShowName =
false;
884 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon,
887 pSelect->AddSelectableRouteSegment(pWP_prev->m_lat, pWP_prev->m_lon,
888 pWP_dst->m_lat, pWP_dst->m_lon,
889 pWP_prev, pWP_dst, route);
893 prpnodeX = TrackPoints.size();
897 if (prpnodeX != TrackPoints.size()) prpnodeX--;
898 if (back_ic-- <= 0) {
899 prpnodeX = TrackPoints.size();
907 DistanceBearingMercator(prp->m_lat, prp->m_lon, pWP_prev->m_lat,
908 pWP_prev->m_lon, NULL, &delta_dist);
910 if (!((delta_dist > (g_TrackDeltaDistance)) && !prp_OK)) {
914 int iProg = (i * 100) / nPoints;
915 if (pprog && (iProg > dProg)) {
917 pprog->Update(dProg);
922 if (delta_dist >= g_TrackDeltaDistance) {
924 new RoutePoint(TrackPoints.back()->m_lat, TrackPoints.back()->m_lon,
925 icon, _T (
"" ), wxEmptyString);
926 route->AddPoint(pWP_dst);
928 pWP_dst->m_bShowName =
false;
930 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst);
932 pSelect->AddSelectableRouteSegment(pWP_prev->m_lat, pWP_prev->m_lon,
933 pWP_dst->m_lat, pWP_dst->m_lon, pWP_prev,
936 route->m_RouteNameString = m_TrackNameString;
937 route->m_RouteStartString = m_TrackStartString;
938 route->m_RouteEndString = m_TrackEndString;
939 route->m_bDeleteOnArrival =
false;
944double Track::GetXTE(
double fm1Lat,
double fm1Lon,
double fm2Lat,
double fm2Lon,
945 double toLat,
double toLon) {
951 double brg1, dist1, brg2, dist2;
952 DistanceBearingMercator(toLat, toLon, fm1Lat, fm1Lon, &brg1, &dist1);
953 w.x = dist1 * sin(brg1 * PI / 180.);
954 w.y = dist1 * cos(brg1 * PI / 180.);
956 DistanceBearingMercator(toLat, toLon, fm2Lat, fm2Lon, &brg2, &dist2);
957 v.x = dist2 * sin(brg2 * PI / 180.);
958 v.y = dist2 * cos(brg2 * PI / 180.);
963 const double lengthSquared = _distance2(v, w);
964 if (lengthSquared == 0.0) {
966 return _distance(p, v);
976 double t = vDotProduct(&a, &b) / lengthSquared;
979 return _distance(p, v);
981 return _distance(p, w);
982 vector2D projection = v + t * (w - v);
983 return _distance(p, projection);
987 if (!fm1 || !fm2 || !to)
return 0.0;
988 if (fm1 == to)
return 0.0;
989 if (fm2 == to)
return 0.0;
990 return GetXTE(fm1->m_lat, fm1->m_lon, fm2->m_lat, fm2->m_lon, to->m_lat,