Skip to content

Commit

Permalink
a) curve smoothing, b) track distances
Browse files Browse the repository at this point in the history
  • Loading branch information
t-weber committed Jan 26, 2025
1 parent 49da43d commit 5e616a6
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 60 deletions.
56 changes: 52 additions & 4 deletions src/gui/docks/track_infos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ TrackInfos::TrackInfos(QWidget* parent) : QWidget{parent}
m_time_check->setChecked(false);
connect(m_time_check.get(), &QCheckBox::toggled, this, &TrackInfos::PlotAlt);

m_smooth_check = std::make_shared<QCheckBox>(alt_panel);
m_smooth_check->setText("Smooth Curve");
m_smooth_check->setToolTip("Smooth altitude curve.");
m_smooth_check->setChecked(true);
connect(m_smooth_check.get(), &QCheckBox::toggled, this, &TrackInfos::PlotAlt);

QPushButton *btn_replot_alt = new QPushButton(alt_panel);
btn_replot_alt->setText("Reset Plot");
btn_replot_alt->setToolTip("Reset the plotting range.");
Expand All @@ -142,6 +148,7 @@ TrackInfos::TrackInfos(QWidget* parent) : QWidget{parent}
alt_panel_layout->setHorizontalSpacing(4);
alt_panel_layout->addWidget(m_alt_plot.get(), 0, 0, 1, 4);
alt_panel_layout->addWidget(m_time_check.get(), 1, 0, 1, 1);
alt_panel_layout->addWidget(m_smooth_check.get(), 1, 1, 1, 1);
alt_panel_layout->addWidget(btn_replot_alt, 1, 3, 1, 1);

// pace plot panel
Expand Down Expand Up @@ -485,7 +492,6 @@ void TrackInfos::PlotAlt()
m_alt_plot->xAxis->setLabel("Distance (km)");

QVector<t_real> dist, alt;

dist.reserve(m_track->GetPoints().size());
alt.reserve(m_track->GetPoints().size());

Expand All @@ -495,15 +501,27 @@ void TrackInfos::PlotAlt()
for(const t_track_pt& pt : m_track->GetPoints())
{
if(plot_time)
{
if(std::abs(pt.elapsed) < g_eps)
continue;
dist.push_back(pt.elapsed_total / 60.);
}
else
{
if(std::abs(pt.distance) < g_eps)
continue;
dist.push_back(pt.distance_total / 1000.);
}
alt.push_back(pt.elevation);

m_min_alt = std::min(m_min_alt, pt.elevation);
m_max_alt = std::max(m_max_alt, pt.elevation);
}

// smooth curve
if(m_smooth_check->isChecked() && g_smooth_rad > 0)
alt = smooth_data(alt, g_smooth_rad);

if(!alt.size())
return;

Expand All @@ -521,7 +539,7 @@ void TrackInfos::PlotAlt()

QCPGraph *curve = new QCPGraph(m_alt_plot->xAxis, m_alt_plot->yAxis);
curve->setData(dist, alt);
//curve->setLineStyle(QCPCurve::lsLine);
curve->setLineStyle(QCPGraph::lsLine);

QPen pen = curve->pen();
pen.setWidthF(2.);
Expand Down Expand Up @@ -960,7 +978,20 @@ void TrackInfos::TrackPlotMouseMove(QMouseEvent *evt)
return;

auto [ longitude, latitude ] = GetTrackPlotCoords(evt, true);
emit PlotCoordsChanged(longitude, latitude);
t_real dist = -1., time = -1.;

if(m_track)
{
auto [ lon_rad, lat_rad ] = GetTrackPlotCoords(evt, false);

if(const t_track_pt* pt = m_track->GetClosestPoint(lon_rad, lat_rad); pt)
{
dist = pt->distance_total;
time = pt->elapsed_total;
}
}

emit PlotCoordsChanged(longitude, latitude, dist, time);
}


Expand Down Expand Up @@ -1088,7 +1119,20 @@ void TrackInfos::MapMouseMove(QMouseEvent *evt)
return;

auto [ lon, lat ] = GetMapCoords(evt, true);
emit PlotCoordsChanged(lon, lat);
t_real dist = -1., time = -1.;

if(m_track)
{
auto [ lon_rad, lat_rad ] = GetMapCoords(evt, false);

if(const t_track_pt* pt = m_track->GetClosestPoint(lon_rad, lat_rad); pt)
{
dist = pt->distance_total;
time = pt->elapsed_total;
}
}

emit PlotCoordsChanged(lon, lat, dist, time);
}


Expand Down Expand Up @@ -1153,6 +1197,7 @@ void TrackInfos::SaveSettings(QSettings& settings)
settings.setValue("track_info/distance_bin", m_dist_binlen->value());
settings.setValue("track_info/speed_check", m_speed_check->isChecked());
settings.setValue("track_info/time_check", m_time_check->isChecked());
settings.setValue("track_info/smooth_check", m_smooth_check->isChecked());
}


Expand Down Expand Up @@ -1189,4 +1234,7 @@ void TrackInfos::RestoreSettings(QSettings& settings)

if(settings.contains("track_info/time_check"))
m_time_check->setChecked(settings.value("track_info/time_check").toBool());

if(settings.contains("track_info/smooth_check"))
m_smooth_check->setChecked(settings.value("track_info/smooth_check").toBool());
}
7 changes: 4 additions & 3 deletions src/gui/docks/track_infos.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class TrackInfos : public QWidget
// altitude tab
std::shared_ptr<QCustomPlot> m_alt_plot{};
std::shared_ptr<QCheckBox> m_time_check{};
std::shared_ptr<QCheckBox> m_smooth_check{};
std::shared_ptr<QMenu> m_alt_context{};

// pace tab
Expand Down Expand Up @@ -145,10 +146,10 @@ class TrackInfos : public QWidget


signals:
void PlotCoordsChanged(t_real, t_real); // mouse moved in track or map
void PlotCoordsSelected(t_real, t_real); // mouse clicked in track or map
void PlotCoordsChanged(t_real, t_real, t_real, t_real); // mouse moved in track or map
void PlotCoordsSelected(t_real, t_real); // mouse clicked in track or map
void StatusMessageChanged(const QString&);
void TrackChanged(); // the track has been modified
void TrackChanged(); // the track has been modified
};


Expand Down
5 changes: 4 additions & 1 deletion src/gui/globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ t_real g_eps = 1e-6;
// precision for showing numbers in the gui
t_int g_prec_gui = 4;

// radius for data smoothing
t_int g_smooth_rad = 10;


bool g_use_recent_dir = true;
bool g_reload_last = true;
Expand All @@ -28,7 +31,7 @@ bool g_reload_last = true;
int g_dist_func = 0;

// minimum height difference [m] before being counted as climb
t_real g_asc_eps = 10.;
t_real g_asc_eps = 5.;


// assumed time interval if non is given (for import)
Expand Down
5 changes: 4 additions & 1 deletion src/gui/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// track types
using t_tracks = MultipleTracks<t_real, t_size>;
using t_track = typename t_tracks::t_track;
using t_track_pt = typename t_track::t_TrackPoint;
using t_track_pt = typename t_track::t_trackpt;


// map types
Expand All @@ -30,6 +30,9 @@ using t_map = Map<t_real_map, t_size_map>;
extern t_real g_eps;
extern t_int g_prec_gui;

// radius for data smoothing
extern t_int g_smooth_rad;


// use recently used directory or a special document folder
extern bool g_use_recent_dir;
Expand Down
16 changes: 14 additions & 2 deletions src/gui/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,11 +602,17 @@ void TracksWnd::TrackDeleted(t_size idx)
}


void TracksWnd::PlotCoordsChanged(t_real longitude, t_real latitude)
void TracksWnd::PlotCoordsChanged(t_real longitude, t_real latitude,
t_real distance, t_real time)
{
std::ostringstream ostr;
ostr.precision(g_prec_gui);
ostr << "Longitude: " << longitude << "°, Latitude: " << latitude << "°.";
ostr << "Cursor longitude: " << longitude << "°, latitude: " << latitude << "°.";
if(distance >= 0. && time >= 0.)
{
ostr << " Closest track point: " << (distance/1000.)
<< " km and " << (time/60.) << " min from start.";
}
SetStatusMessage(ostr.str().c_str());
}

Expand Down Expand Up @@ -840,6 +846,7 @@ bool TracksWnd::ImportFiles(const QStringList& filenames)
t_track track{};
track.SetDistanceFunction(g_dist_func);
track.SetAscentEpsilon(g_asc_eps);
track.SetSmoothRadius(g_smooth_rad);

if(!track.Import(filename.toStdString(), g_assume_dt))
{
Expand Down Expand Up @@ -876,6 +883,8 @@ void TracksWnd::ShowSettings(bool only_create)
"Calculation epsilon:", g_eps, 0., 1., 1e-6, 8);
m_settings->AddDoubleSpinbox("settings/epsilon_ascent",
"Ascent epsilon:", g_asc_eps, 0.1, 999., 1., 1, " m");
m_settings->AddSpinbox("settings/smooth_radius",
"Data smooth radius:", g_smooth_rad, 0, 999, 1);
m_settings->AddCombobox("settings/distance_function",
"Distance calculation:",
{ "Haversine Formula", "Thomas Formula",
Expand Down Expand Up @@ -919,6 +928,8 @@ void TracksWnd::ApplySettings()
value<decltype(g_eps)>();
g_asc_eps = m_settings->GetValue("settings/epsilon_ascent").
value<decltype(g_asc_eps)>();
g_smooth_rad = m_settings->GetValue("settings/smooth_radius").
value<decltype(g_smooth_rad)>();
g_dist_func = m_settings->GetValue("settings/distance_function").
value<decltype(g_dist_func)>();
g_assume_dt = m_settings->GetValue("settings/assume_dt").
Expand All @@ -938,6 +949,7 @@ void TracksWnd::ApplySettings()
CreateTempDir();
m_trackdb.SetDistanceFunction(g_dist_func);
m_trackdb.SetAscentEpsilon(g_asc_eps);
m_trackdb.SetSmoothRadius(g_smooth_rad);
update();
}

Expand Down
3 changes: 2 additions & 1 deletion src/gui/gui.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ protected slots:
void TrackNameChanged(t_size idx, const std::string& name);
void TrackDeleted(t_size idx);

void PlotCoordsChanged(t_real longitude, t_real latitude);
void PlotCoordsChanged(t_real longitude, t_real latitude,
t_real distance, t_real time);
void SetStatusMessage(const QString& msg, int display_ms = 0) const;
void PopulateTrackList(bool resort = true);
};
Expand Down
52 changes: 52 additions & 0 deletions src/lib/calc.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include <tuple>
#include <cmath>
#include <concepts>
#include <type_traits>

#include <boost/geometry.hpp>

Expand All @@ -21,6 +23,7 @@
*/
template<typename t_real = double>
t_real havsin(t_real th)
requires std::floating_point<t_real>
{
return t_real(0.5) - t_real(0.5)*std::cos(th);
}
Expand All @@ -33,6 +36,7 @@ t_real havsin(t_real th)
*/
template<typename t_real = double>
t_real arcaversin(t_real x)
requires std::floating_point<t_real>
{
return std::acos(t_real(1) - t_real(2)*x);
}
Expand All @@ -45,6 +49,7 @@ t_real arcaversin(t_real x)
*/
template<typename t_real = double>
t_real earth_radius(t_real lat)
requires std::floating_point<t_real>
{
t_real rad_pol = 6.3567523e6;
t_real rad_equ = 6.3781370e6;
Expand Down Expand Up @@ -73,6 +78,7 @@ std::tuple<t_real, t_real> // [ planar distance, distance including heights ]
geo_dist(t_real lat1, t_real lat2,
t_real lon1, t_real lon2,
t_real elev1, t_real elev2)
requires std::floating_point<t_real>
{
t_real rad = earth_radius<t_real>((lat1 + lat2) / t_real(2));
rad += (elev1 + elev2) * t_real(0.5);
Expand All @@ -98,6 +104,7 @@ std::tuple<t_real, t_real> // [ planar distance, distance including heights ]
geo_dist_2(t_real lat1, t_real lat2,
t_real lon1, t_real lon2,
[[__maybe_unused__]] t_real elev1, [[__maybe_unused__]] t_real elev2)
requires std::floating_point<t_real>
{
namespace geo = boost::geometry;
using t_pt = geo::model::point<t_real, 2, geo::cs::geographic<geo::radian>>;
Expand Down Expand Up @@ -144,9 +151,54 @@ geo_dist_2(t_real lat1, t_real lat2,
*/
template<typename t_real = double>
t_real speed_to_pace(t_real speed)
requires std::floating_point<t_real>
{
return t_real(60.) / speed;
}



/**
* smoothing of data points
* see: https://en.wikipedia.org/wiki/Laplacian_smoothing
*/
template<class t_cont>
t_cont smooth_data(const t_cont& vec, int N = 1)
requires requires(t_cont cont)
{
typename t_cont::value_type;
cont.size();
cont[0] = cont[1];
}
{
if(N <= 0)
return vec;

using t_val = typename t_cont::value_type;
const int SIZE = static_cast<int>(vec.size());

t_cont smoothed = vec;

for(int i = 0; i < SIZE; ++i)
{
t_val elem{};
t_val num{};

for(int j = -N; j <= N; ++j)
{
int idx = i + j;
if(idx >= SIZE || idx < 0)
continue;

elem += vec[i + j];
num += 1;
}

smoothed[i] = elem / num;
}

return smoothed;
}


#endif
9 changes: 7 additions & 2 deletions src/lib/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
#include <list>
#include <vector>
#include <tuple>
#include <optional>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <optional>
#include <functional>
#include <concepts>
#include <stdexcept>
#include <cstdint>

Expand Down Expand Up @@ -52,6 +53,7 @@


template<class t_tags, class t_real = double>
requires std::floating_point<t_real>
struct MapVertex
{
t_real longitude{};
Expand All @@ -64,6 +66,7 @@ struct MapVertex


template<class t_tags, class t_size = std::size_t>
requires std::integral<t_size>
struct MapSegment
{
std::list<t_size> vertex_ids{};
Expand All @@ -76,6 +79,7 @@ struct MapSegment


template<class t_tags, class t_size = std::size_t>
requires std::integral<t_size>
struct MapMultiSegment
{
std::list<t_size> vertex_ids{};
Expand All @@ -97,6 +101,7 @@ enum class MapObjType


template<class t_real = double, class t_size = std::size_t>
requires std::floating_point<t_real> && std::integral<t_size>
class Map
{
public:
Expand Down
Loading

0 comments on commit 5e616a6

Please sign in to comment.