75#include <wx/datetime.h>
77#include <wx/jsonval.h>
79#include <wx/progdlg.h>
88#include "model/nav_object_database.h"
102#if defined(__UNIX__) && !defined(__WXOSX__)
106 void Reset() { clock_gettime(CLOCK_REALTIME, &tp); }
110 clock_gettime(CLOCK_REALTIME, &tp_end);
111 return (tp_end.tv_sec - tp.tv_sec) * 1.e3 +
112 (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
120TrackPoint::TrackPoint(
double lat,
double lon, wxString ts)
121 : m_lat(lat), m_lon(lon), m_GPXTrkSegNo(1) {
125TrackPoint::TrackPoint(
double lat,
double lon, wxDateTime dt)
126 : m_lat(lat), m_lon(lon), m_GPXTrkSegNo(1) {
132 : m_lat(orig->m_lat), m_lon(orig->m_lon), m_GPXTrkSegNo(1) {
136TrackPoint::~TrackPoint() {}
139 wxDateTime CreateTimeX;
147 ts = dt.FormatISODate().Append(
"T").Append(dt.FormatISOTime()).Append(
"Z");
154 m_stimestring = ts.mb_str();
163double _distance2(vector2D &a, vector2D &b) {
164 return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
166double _distance(vector2D &a, vector2D &b) {
return sqrt(_distance2(a, b)); }
172 m_width = WIDTH_UNDEFINED;
173 m_style = wxPENSTYLE_INVALID;
175 m_GUID = pWayPointMan->CreateGUID(NULL);
176 m_bIsInLayer =
false;
179 m_TrackHyperlinkList =
new HyperlinkList;
180 m_HighlightedTrackPoint = -1;
184 for (
size_t i = 0; i < TrackPoints.size(); i++)
delete TrackPoints[i];
186 delete m_TrackHyperlinkList;
189#define TIMER_TRACK1 778
192EVT_TIMER(TIMER_TRACK1, ActiveTrack::OnTimerTrack)
196 m_TimerTrack.SetOwner(
this, TIMER_TRACK1);
200 SetPrecision(g_nTrackPrecision);
202 m_prev_time = wxInvalidDateTime;
203 m_lastStoredTP = NULL;
205 wxDateTime now = wxDateTime::Now();
207 trackPointState = firstPoint;
208 m_lastStoredTP = NULL;
212 m_CurrentTrackSeg = 0;
216ActiveTrack::~ActiveTrack() { Stop(); }
218void ActiveTrack::SetPrecision(
int prec) {
220 switch (m_nPrecision) {
222 m_allowedMaxAngle = 10;
223 m_allowedMaxXTE = 0.008;
225 m_minTrackpoint_delta = .004;
229 m_allowedMaxAngle = 10;
230 m_allowedMaxXTE = 0.004;
232 m_minTrackpoint_delta = .002;
236 m_allowedMaxAngle = 10;
237 m_allowedMaxXTE = 0.0015;
239 m_minTrackpoint_delta = .001;
245void ActiveTrack::Start() {
248 m_TimerTrack.Start(1000, wxTIMER_CONTINUOUS);
253void ActiveTrack::Stop(
bool do_add_point) {
260 delta = DistGreatCircle(
gLat,
gLon, m_lastStoredTP->m_lat,
261 m_lastStoredTP->m_lon);
263 if (delta > m_minTrackpoint_delta) AddPointNow(
true);
272Track *ActiveTrack::DoExtendDaily() {
273 Track *pExtendTrack = NULL;
280 if (!ptrack->m_bIsInLayer && ptrack->m_GUID != m_GUID) {
281 TrackPoint *track_node = ptrack->GetLastPoint();
287 pExtendPoint = track_node;
288 pExtendTrack = ptrack;
293 if (pExtendTrack && pExtendTrack->GetPoint(0)
295 .FromTimezone(wxDateTime::GMT0)
297 wxDateTime::GMT0))) {
300 pSelect->DeleteAllSelectableTrackSegments(pExtendTrack);
301 wxString suffix =
"";
302 if (GetName().IsNull()) {
303 suffix = pExtendTrack->GetName();
304 if (suffix.IsNull()) suffix = wxDateTime::Today().FormatISODate();
306 pExtendTrack->Clone(
this, begin, GetnPoints(), suffix);
307 pSelect->AddAllSelectableTrackSegments(pExtendTrack);
308 pSelect->DeleteAllSelectableTrackSegments(
this);
312 if (GetName().IsNull()) SetName(wxDateTime::Today().FormatISODate());
317void Track::Clone(
Track *psourcetrack,
int start_nPoint,
int end_nPoint,
318 const wxString &suffix) {
319 if (psourcetrack->m_bIsInLayer)
return;
321 m_TrackNameString = psourcetrack->m_TrackNameString + suffix;
322 m_TrackStartString = psourcetrack->m_TrackStartString;
323 m_TrackEndString = psourcetrack->m_TrackEndString;
325 bool b_splitting = GetnPoints() == 0;
329 startTrkSegNo = psourcetrack->GetPoint(start_nPoint)->m_GPXTrkSegNo;
331 startTrkSegNo = GetLastPoint()->m_GPXTrkSegNo;
334 for (i = start_nPoint; i <= end_nPoint; i++) {
335 TrackPoint *psourcepoint = psourcetrack->GetPoint(i);
339 AddPoint(ptargetpoint);
344void ActiveTrack::AdjustCurrentTrackPoint(
TrackPoint *prototype) {
346 *m_lastStoredTP = *prototype;
351void ActiveTrack::OnTimerTrack(wxTimerEvent &event) {
356 m_prev_dist = DistGreatCircle(
gLat,
gLon, m_lastStoredTP->m_lat,
357 m_lastStoredTP->m_lon);
361 bool b_addpoint =
false;
363 if ((m_TrackTimerSec > 0.) && ((
double)m_track_run >= m_TrackTimerSec) &&
364 (m_prev_dist > m_minTrackpoint_delta)) {
372 if ((trackPointState == firstPoint) && !g_bTrackDaily) {
373 wxDateTime now = wxDateTime::Now();
374 if (TrackPoints.empty()) TrackPoints.front()->SetCreateTime(now.ToUTC());
377 m_TimerTrack.Start(1000, wxTIMER_CONTINUOUS);
380void ActiveTrack::AddPointNow(
bool do_add_point) {
381 wxDateTime now = wxDateTime::Now();
383 if (m_prev_dist < 0.0005)
384 if (!do_add_point)
return;
386 if (m_prev_time.IsValid())
387 if (m_prev_time == now)
388 if (!do_add_point)
return;
397 if (g_trackFilterMax) {
398 if (trackPointState == potentialPoint) {
399 double distToLastGpsPoint = DistLoxodrome(
400 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon,
gLat,
gLon);
401 if (distToLastGpsPoint > g_trackFilterMax)
return;
409 switch (trackPointState) {
411 TrackPoint *pTrackPoint = AddNewPoint(gpsPoint, now.ToUTC());
412 m_lastStoredTP = pTrackPoint;
413 trackPointState = secondPoint;
414 do_add_point =
false;
419 skipPoints.push_back(pPoint);
420 skipTimes.push_back(now.ToUTC());
421 trackPointState = potentialPoint;
424 case potentialPoint: {
425 if (gpsPoint == skipPoints[skipPoints.size() - 1])
break;
427 unsigned int xteMaxIndex = 0;
432 for (
unsigned int i = 0; i < skipPoints.size(); i++) {
433 double xte = GetXTE(m_lastStoredTP->m_lat, m_lastStoredTP->m_lon,
gLat,
434 gLon, skipPoints[i].lat, skipPoints[i].lon);
440 if (xteMax > m_allowedMaxXTE) {
442 AddNewPoint(skipPoints[xteMaxIndex], skipTimes[xteMaxIndex]);
443 pSelect->AddSelectableTrackSegment(
444 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon, pTrackPoint->m_lat,
445 pTrackPoint->m_lon, m_lastStoredTP, pTrackPoint,
this);
447 m_prevFixedTP = m_fixedTP;
448 m_fixedTP = m_removeTP;
449 m_removeTP = m_lastStoredTP;
450 m_lastStoredTP = pTrackPoint;
451 for (
unsigned int i = 0; i <= xteMaxIndex; i++) {
452 skipPoints.pop_front();
453 skipTimes.pop_front();
460 if (GetnPoints() > 2) {
462 DistGreatCircle(m_fixedTP->m_lat, m_fixedTP->m_lon,
463 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon);
464 double xte = GetXTE(m_fixedTP, m_lastStoredTP, m_removeTP);
465 if (xte < m_allowedMaxXTE / wxMax(1.0, 2.0 - dist * 2.0)) {
466 TrackPoints.pop_back();
467 TrackPoints.pop_back();
468 TrackPoints.push_back(m_lastStoredTP);
469 pSelect->DeletePointSelectableTrackSegments(m_removeTP);
470 pSelect->AddSelectableTrackSegment(
471 m_fixedTP->m_lat, m_fixedTP->m_lon, m_lastStoredTP->m_lat,
472 m_lastStoredTP->m_lon, m_fixedTP, m_lastStoredTP,
this);
474 m_removeTP = m_fixedTP;
475 m_fixedTP = m_prevFixedTP;
480 skipPoints.push_back(gpsPoint);
481 skipTimes.push_back(now.ToUTC());
488 TrackPoint *pTrackPoint = AddNewPoint(gpsPoint, now.ToUTC());
489 pSelect->AddSelectableTrackSegment(
490 m_lastStoredTP->m_lat, m_lastStoredTP->m_lon, pTrackPoint->m_lat,
491 pTrackPoint->m_lon, m_lastStoredTP, pTrackPoint,
this);
497void Track::ClearHighlights() { m_HighlightedTrackPoint = -1; }
500 if (nWhichPoint < (
int)TrackPoints.size())
501 return TrackPoints[nWhichPoint];
507 if (TrackPoints.empty())
return NULL;
509 return TrackPoints.back();
512static double heading_diff(
double x) {
513 if (x > 180)
return 360 - x;
514 if (x < -180)
return -360 + x;
522double Track::ComputeScale(
int left,
int right) {
523 const double z = WGS84_semimajor_axis_meters * mercator_k0;
524 const double mult = DEGREE * z;
529 double lata = TrackPoints[left]->m_lat, lona = TrackPoints[left]->m_lon;
530 double latb = TrackPoints[right]->m_lat, lonb = TrackPoints[right]->m_lon;
532 double bx = heading_diff(lonb - lona), by = latb - lata;
534 double lengthSquared = bx * bx + by * by;
538 if (lengthSquared > 3)
return INFINITY;
540 if (lengthSquared == 0.0) {
541 for (
int i = left + 1; i < right; i++) {
542 double lat = TrackPoints[i]->m_lat, lon = TrackPoints[i]->m_lon;
544 double vx = heading_diff(lon - lona);
545 double vy = lat - lata;
546 double dist = vx * vx + vy * vy;
548 if (dist > max_dist) max_dist = dist;
551 double invLengthSquared = 1 / lengthSquared;
552 for (
int i = left + 1; i < right; i++) {
553 double lat = TrackPoints[i]->m_lat, lon = TrackPoints[i]->m_lon;
555 double vx = heading_diff(lon - lona);
556 double vy = lat - lata;
557 double t = (vx * bx + vy * by) * invLengthSquared;
561 dist = vx * vx + vy * vy;
563 double wx = heading_diff(lona - lon);
564 double wy = lata - lat;
565 dist = wx * wx + wy * wy;
567 double projx = vx - t * bx;
568 double projy = vy - t * by;
569 dist = projx * projx + projy * projy;
572 if (dist > max_dist) max_dist = dist;
576 return max_dist * mult * mult;
583 TrackPoints.push_back(pNewPoint);
588void Track::Finalize() {
589 if (SubTracks.size())
594 int n = TrackPoints.size() - 1;
597 std::vector<SubTrack> new_level;
600 for (
int i = 0; i < n; i++) {
601 new_level[i].m_box.SetFromSegment(
602 TrackPoints[i]->m_lat, TrackPoints[i]->m_lon,
603 TrackPoints[i + 1]->m_lat, TrackPoints[i + 1]->m_lon);
604 new_level[i].m_scale = 0;
607 for (
int i = 0; i < n; i++) {
609 new_level[i].m_box = SubTracks[level - 1][p].m_box;
610 if (p + 1 < (
int)SubTracks[level - 1].size())
611 new_level[i].m_box.Expand(SubTracks[level - 1][p + 1].m_box);
613 int left = i << level;
614 int right = wxMin(left + (1 << level), TrackPoints.size() - 1);
615 new_level[i].m_scale = ComputeScale(left, right);
618 SubTracks.push_back(new_level);
620 if (n > 1 && n & 1) n++;
629void Track::InsertSubTracks(LLBBox &box,
int level,
int pos) {
630 if (level == (
int)SubTracks.size()) {
631 std::vector<SubTrack> new_level;
632 if (level > 0) box.Expand(SubTracks[level - 1][0].m_box);
634 new_level[pos].m_box = box;
635 SubTracks.push_back(new_level);
636 }
else if (pos < (
int)SubTracks[level].size())
637 SubTracks[level][pos].m_box.Expand(box);
639 SubTracks[level].push_back(
SubTrack());
640 SubTracks[level][pos].m_box = box;
644 SubTracks[level][pos].m_scale = 0;
646 int left = pos << level;
647 int right = wxMin(left + (1 << level), TrackPoints.size() - 1);
648 SubTracks[level][pos].m_scale = ComputeScale(left, right);
651 if (pos > 0) InsertSubTracks(box, level + 1, pos >> 1);
660void Track::AddPointFinalized(
TrackPoint *pNewPoint) {
661 TrackPoints.push_back(pNewPoint);
663 int pos = TrackPoints.size() - 1;
667 box.SetFromSegment(TrackPoints[pos - 1]->m_lat, TrackPoints[pos - 1]->m_lon,
668 TrackPoints[pos]->m_lat, TrackPoints[pos]->m_lon);
669 InsertSubTracks(box, 0, pos - 1);
673TrackPoint *Track::AddNewPoint(vector2D point, wxDateTime time) {
676 AddPointFinalized(tPoint);
681 NavObj_dB::GetInstance().AddTrackPoint(
this, tPoint);
685 v[
"lat"] = tPoint->m_lat;
686 v[
"lon"] = tPoint->m_lon;
687 v[
"Track_ID"] = m_GUID;
688 std::string msg_id(
"OCPN_TRK_POINT_ADDED");
689 JsonEvent::getInstance().Notify(msg_id, std::make_shared<wxJSONValue>(v));
694void Track::DouglasPeuckerReducer(std::vector<TrackPoint *> &list,
695 std::vector<bool> &keeplist,
int from,
int to,
697 keeplist[from] =
true;
700 int maxdistIndex = -1;
703 for (
int i = from + 1; i < to; i++) {
704 double dist = 1852.0 * GetXTE(list[from], list[to], list[i]);
706 if (dist > maxdist) {
712 if (maxdist > delta) {
713 DouglasPeuckerReducer(list, keeplist, from, maxdistIndex, delta);
714 DouglasPeuckerReducer(list, keeplist, maxdistIndex, to, delta);
718double Track::Length() {
721 for (
size_t i = 0; i < TrackPoints.size(); i++) {
724 const double offsetLat = 1e-6;
725 const double deltaLat = l->m_lat - t->m_lat;
726 if (fabs(deltaLat) > offsetLat)
727 total += DistGreatCircle(l->m_lat, l->m_lon, t->m_lat, t->m_lon);
729 total += DistGreatCircle(l->m_lat + copysign(offsetLat, deltaLat),
730 l->m_lon, t->m_lat, t->m_lon);
738int Track::Simplify(
double maxDelta) {
741 std::vector<TrackPoint *> pointlist;
742 std::vector<bool> keeplist;
744 ::wxBeginBusyCursor();
746 for (
size_t i = 0; i < TrackPoints.size(); i++) {
749 pointlist.push_back(trackpoint);
750 keeplist.push_back(
false);
753 DouglasPeuckerReducer(pointlist, keeplist, 0, pointlist.size() - 1, maxDelta);
755 pSelect->DeleteAllSelectableTrackSegments(
this);
759 for (
size_t i = 0; i < pointlist.size(); i++) {
761 TrackPoints.push_back(pointlist[i]);
769 pSelect->AddAllSelectableTrackSegments(
this);
776Route *Track::RouteFromTrack(wxGenericProgressDialog *pprog) {
785 wxString icon =
"xmblue";
786 if (g_TrackDeltaDistance >= 0.1) icon =
"diamond";
790 int nPoints = TrackPoints.size();
791 bool isProminent =
true;
792 double delta_dist = 0.;
793 double delta_hdg, xte;
794 double leg_speed = 0.1;
796 leg_speed = g_PlanSpeed;
800 pWP_dst =
new RoutePoint(pWP_src->m_lat, pWP_src->m_lon, icon,
"",
"");
801 route->AddPoint(pWP_dst);
805 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst);
809 for (
size_t i = 1; i < TrackPoints.size();) {
812 pWP_dst->m_lat = pWP_prev->m_lat;
813 pWP_dst->m_lon = pWP_prev->m_lon;
820 DistanceBearingMercator(prp->m_lat, prp->m_lon, pWP_prev->m_lat,
821 pWP_prev->m_lon, &delta_hdg, &delta_dist);
823 if ((delta_dist > (leg_speed * 6.0)) && !prp_OK) {
824 int delta_inserts = floor(delta_dist / (leg_speed * 4.0));
825 delta_dist = delta_dist / (delta_inserts + 1);
829 while (delta_inserts--) {
830 ll_gc_ll(pWP_prev->m_lat, pWP_prev->m_lon, delta_hdg, delta_dist, &tlat,
832 pWP_dst =
new RoutePoint(tlat, tlon, icon,
"",
"");
833 route->AddPoint(pWP_dst);
835 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon,
838 pSelect->AddSelectableRouteSegment(pWP_prev->m_lat, pWP_prev->m_lon,
839 pWP_dst->m_lat, pWP_dst->m_lon,
840 pWP_prev, pWP_dst, route);
853 if (delta_dist >= (leg_speed * 4.0)) isProminent =
true;
854 if (!prp_OK) prp_OK = prp;
856 while (prpnodeX < TrackPoints.size()) {
859 xte = GetXTE(pWP_src, prpX, prp);
860 if (isProminent || (xte > g_TrackDeltaDistance)) {
861 pWP_dst =
new RoutePoint(prp_OK->m_lat, prp_OK->m_lon, icon,
"",
"");
863 route->AddPoint(pWP_dst);
866 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon,
869 pSelect->AddSelectableRouteSegment(pWP_prev->m_lat, pWP_prev->m_lon,
870 pWP_dst->m_lat, pWP_dst->m_lon,
871 pWP_prev, pWP_dst, route);
875 prpnodeX = TrackPoints.size();
879 if (prpnodeX != TrackPoints.size()) prpnodeX--;
880 if (back_ic-- <= 0) {
881 prpnodeX = TrackPoints.size();
889 DistanceBearingMercator(prp->m_lat, prp->m_lon, pWP_prev->m_lat,
890 pWP_prev->m_lon, NULL, &delta_dist);
892 if (!((delta_dist > (g_TrackDeltaDistance)) && !prp_OK)) {
896 int iProg = (i * 100) / nPoints;
897 if (pprog && (iProg > dProg)) {
899 pprog->Update(dProg);
904 if (delta_dist >= g_TrackDeltaDistance) {
905 pWP_dst =
new RoutePoint(TrackPoints.back()->m_lat,
906 TrackPoints.back()->m_lon, icon,
"",
"");
907 route->AddPoint(pWP_dst);
911 pSelect->AddSelectableRoutePoint(pWP_dst->m_lat, pWP_dst->m_lon, pWP_dst);
913 pSelect->AddSelectableRouteSegment(pWP_prev->m_lat, pWP_prev->m_lon,
914 pWP_dst->m_lat, pWP_dst->m_lon, pWP_prev,
925double Track::GetXTE(
double fm1Lat,
double fm1Lon,
double fm2Lat,
double fm2Lon,
926 double toLat,
double toLon) {
932 double brg1, dist1, brg2, dist2;
933 DistanceBearingMercator(toLat, toLon, fm1Lat, fm1Lon, &brg1, &dist1);
934 w.x = dist1 * sin(brg1 * PI / 180.);
935 w.y = dist1 * cos(brg1 * PI / 180.);
937 DistanceBearingMercator(toLat, toLon, fm2Lat, fm2Lon, &brg2, &dist2);
938 v.x = dist2 * sin(brg2 * PI / 180.);
939 v.y = dist2 * cos(brg2 * PI / 180.);
944 const double lengthSquared = _distance2(v, w);
945 if (lengthSquared == 0.0) {
947 return _distance(p, v);
957 double t = vDotProduct(&a, &b) / lengthSquared;
960 return _distance(p, v);
962 return _distance(p, w);
963 vector2D projection = v + t * (w - v);
964 return _distance(p, projection);
968 if (!fm1 || !fm2 || !to)
return 0.0;
969 if (fm1 == to)
return 0.0;
970 if (fm2 == to)
return 0.0;
971 return GetXTE(fm1->m_lat, fm1->m_lon, fm2->m_lat, fm2->m_lon, to->m_lat,
976wxString Track::GetIsoDateTime(
const wxString label_for_invalid_date)
const {
979 if ((
int)TrackPoints.size() > 0) rp = TrackPoints[0];
983 name = label_for_invalid_date;
987wxString Track::GetDateTime(
const wxString label_for_invalid_date)
const {
990 if ((
int)TrackPoints.size() > 0) rp = TrackPoints[0];
992 name = ocpn::toUsrDateTimeFormat(rp->
GetCreateTime().FromUTC());
994 name = label_for_invalid_date;
Represents an active track that is currently being recorded.
Represents a waypoint or mark within the navigation system.
bool m_bShowName
Flag indicating if the waypoint name should be shown.
Represents a navigational route in the navigation system.
wxString m_RouteStartString
Name or description of the route's starting point.
bool m_bDeleteOnArrival
Flag indicating whether the route should be deleted once navigation reaches the end.
wxString m_RouteEndString
Name or description of the route's ending point.
wxString m_RouteNameString
User-assigned name for the route.
Represents a single point in a track.
wxDateTime GetCreateTime(void)
Retrieves the creation timestamp of a track point as a wxDateTime object.
void SetCreateTime(wxDateTime dt)
Sets the creation timestamp for a track point.
Represents a track, which is a series of connected track points.
The JSON value class implementation.
Global variables stored in configuration file.
JSON event definition used in internal communications to/from plugins.
MySQL based storage for routes, tracks, etc.
const wxChar * ParseGPXDateTime(wxDateTime &dt, const wxChar *datetime)
This function parses a string containing a GPX time representation and returns a wxDateTime containin...
Navigation Utility Functions without GUI dependencies.
PlugIn Object Definition/API.
double gLat
Vessel's current latitude in decimal degrees.
double gLon
Vessel's current longitude in decimal degrees.
Position, course, speed, etc.
Select * pSelect
Global instance.
Selected route, segment, waypoint, etc.
ActiveTrack * g_pActiveTrack
global instance
std::vector< Track * > g_TrackList
Global instance.
Recorded track abstraction.
std::vector< Track * > g_TrackList
Global instance.