Route Search Refactor Part 1 of 2

.. clean code and get ready to add background
   scanning for routes

.. next update will add background scanning
This commit is contained in:
Mark Liversedge
2015-05-23 13:38:07 +01:00
parent f7f4578241
commit 47afd82d9e
4 changed files with 71 additions and 211 deletions

View File

@@ -526,12 +526,15 @@ AnalysisSidebar::showIntervalMenu(const QPoint &pos)
// care as we should only really operate on user intervals
QTreeWidgetItem *trItem = intervalTree->itemAt(pos);
QTreeWidgetItem *root = trItem ? trItem->parent() : NULL;
QVariant v = trItem ? trItem->data(0, Qt::UserRole) : QVariant();
IntervalItem *interval = static_cast<IntervalItem*>(v.value<void*>());
if (trItem != NULL && interval) {
if (trItem != NULL && root && interval) {
// what kind if interval are we looking at ?
RideFileInterval::IntervalType type = trees.key(root, static_cast<RideFileInterval::IntervalType>(-1));
bool isUser = interval->rideInterval != NULL;
activeInterval = interval;
@@ -540,13 +543,10 @@ AnalysisSidebar::showIntervalMenu(const QPoint &pos)
// ZOOM IN AND OUT FOR ALL
QAction *actZoomOut = new QAction(tr("Zoom Out"), intervalTree);
QAction *actZoomInt = new QAction(tr("Zoom to interval"), intervalTree);
QAction *actRoute = new QAction(tr("Create as a route"), intervalTree);
connect(actZoomOut, SIGNAL(triggered(void)), this, SLOT(zoomOut(void)));
connect(actZoomInt, SIGNAL(triggered(void)), this, SLOT(zoomInterval(void)));
connect(actRoute, SIGNAL(triggered(void)), this, SLOT(createRouteIntervalSelected(void)));
menu.addAction(actZoomOut);
menu.addAction(actZoomInt);
menu.addAction(actRoute);
// EDIT / DELETE USER ONLY
if (isUser) {
@@ -566,6 +566,15 @@ AnalysisSidebar::showIntervalMenu(const QPoint &pos)
menu.addSeparator();
// CREATE NEW ROUTE SEGMENT IF GPS PRESENT (AND NOT ALREADY A ROUTE!)
if (type != RideFileInterval::ROUTE &&
context->currentRideItem() && context->currentRideItem()->present.contains("G")) {
QAction *actRoute = new QAction(tr("Create as a route"), intervalTree);
connect(actRoute, SIGNAL(triggered(void)), this, SLOT(createRouteIntervalSelected(void)));
menu.addAction(actRoute);
}
menu.exec(intervalTree->mapToGlobal(pos));
}
}

View File

@@ -86,52 +86,25 @@ RouteSegment::addRide(RouteRide _ride)
int
RouteSegment::addRideForRideFile(const RideFile *ride, double start, double stop, double precision)
{
qDebug() << "addRideForRideFile" << ride->getTag("Filename","");
RouteRide _route = RouteRide(ride->getTag("Filename",""), ride->startTime(), start, stop, precision);
rides.append(_route);
return rides.count();
}
bool
RouteSegment::parseRideFileName(Context *context, const QString &name, QString *notesFileName, QDateTime *dt)
{
static char rideFileRegExp[] = "^((\\d\\d\\d\\d)_(\\d\\d)_(\\d\\d)"
"_(\\d\\d)_(\\d\\d)_(\\d\\d))\\.(.+)$";
QRegExp rx(rideFileRegExp);
if (!rx.exactMatch(name))
return false;
//assert(rx.captureCount() == 8);
QDate date(rx.cap(2).toInt(), rx.cap(3).toInt(),rx.cap(4).toInt());
QTime time(rx.cap(5).toInt(), rx.cap(6).toInt(),rx.cap(7).toInt());
if ((! date.isValid()) || (! time.isValid())) {
QMessageBox::warning(context->mainWindow,
tr("Invalid File Name"),
tr("Invalid date/time in filename:\n%1\nSkipping file...").arg(name));
return false;
}
*dt = QDateTime(date, time);
*notesFileName = rx.cap(1) + ".notes";
return true;
}
void
RouteSegment::removeRideInRoute(RideFile* ride)
RouteSegment::removeRideInRoute(RideItem* ride)
{
for (int n=0; n< this->getRides().count();n++) {
const RouteRide* routeride = &getRides().at(n);
if (ride && routeride->startTime == ride->startTime()) {
if (ride && routeride->startTime == ride->dateTime) {
rides.removeAt(n);
}
}
}
void
RouteSegment::searchRouteInRide(RideFile* ride, bool freememory, QTextStream* out)
RouteSegment::searchRouteInRide(RideFile* ride)
{
//*out << "searchRouteInRide " << ride->startTime().toString() << "\r\n";
bool candidate = false; // This RideFile is candidate
double minimumprecision = 0.100; //100m
double maximumprecision = 0.010; //10m
double precision = -1;
@@ -140,7 +113,6 @@ RouteSegment::searchRouteInRide(RideFile* ride, bool freememory, QTextStream* ou
int lastpoint = -1; // Last point to match
double start = -1, stop = -1; // Start and stop secs
//foreach (RoutePoint routepoint, this->getPoints()) {
for (int n=0; n< this->getPoints().count();n++) {
RoutePoint routepoint = this->getPoints().at(n);
@@ -171,7 +143,6 @@ RouteSegment::searchRouteInRide(RideFile* ride, bool freememory, QTextStream* ou
} else {
if (_dist<minimumprecision) {
start = 0; //try to start
*out << " Start point identified...";
}
}
}
@@ -209,7 +180,6 @@ RouteSegment::searchRouteInRide(RideFile* ride, bool freememory, QTextStream* ou
if (start == 0) {
start = point->secs;
precision = 0;
*out << "start time " << start << "\r\n";
}
if (minimumdistance>precision)
@@ -218,10 +188,8 @@ RouteSegment::searchRouteInRide(RideFile* ride, bool freememory, QTextStream* ou
break;
}
else {
*out << " WARNING route diverge at " << point->secs << "(" << i <<") after " << (point->secs-start)<< "secs for " << minimumdistance << "km " << routepoint.lat << "-" << routepoint.lon << "/" << point->lat << "-" << point->lon << "\r\n";
diverge++;
if (diverge>2) {
*out << " STOP route diverge at " << point->secs << "(" << i <<") after " << (point->secs-start)<< "secs for " << minimumdistance << "km " << routepoint.lat << "-" << routepoint.lon << "/" << point->lat << "-" << point->lon << "\r\n";
start = -1; //try to restart
n = 0;
}
@@ -238,112 +206,20 @@ RouteSegment::searchRouteInRide(RideFile* ride, bool freememory, QTextStream* ou
if (!present) {
*out << " Route not identified (distance " << precision << "km)\r\n";
break;
} else {
if (n == this->getPoints().count()-1){
// OK
//Add the interval and continue search
*out << " >>> Route identified in: " << name << " start: " << start << " stop: " << stop << " (distance " << precision << "km)\r\n";
this->addRideForRideFile(ride, start, stop, precision);
candidate = true;
*out << " search again..." << "\r\n";
start = -1;
n=0;
//break;
}
}
}
if (freememory && !candidate && ride) delete ride; // free memory - if needed
}
void
RouteSegment::searchRouteInAllRides(Context* context)
{
qDebug() << "searchRouteInAllRides()";
// log of progress
QFile log(context->athlete->home->logs().canonicalPath() + "/" + "routes.log");
log.open(QIODevice::ReadWrite);
QTextStream out(&log);
out << "SEARCH NEW ROUTE STARTS: " << QDateTime::currentDateTime().toString() + "\r\n";
QStringList filenames;
foreach(RideItem *item, context->athlete->rideCache->rides())
filenames << item->fileName;
QStringListIterator iterator(filenames);
QStringList errors;
// update statistics for ride files which are out of date
// showing a progress bar as we go
QTime elapsed;
elapsed.start();
QString title = tr("Searching route");
GProgressDialog *bar = NULL;
int processed=0;
while (iterator.hasNext()) {
QString name = iterator.next();
QFile file(context->athlete->home->activities().canonicalPath() + "/" + name);
out << "Opening: " << name;
RideFile *ride = NULL;
// update progress bar
long elapsedtime = elapsed.elapsed();
QString elapsedString = QString("%1:%2:%3").arg(elapsedtime/3600000,2)
.arg((elapsedtime%3600000)/60000,2,10,QLatin1Char('0'))
.arg((elapsedtime%60000)/1000,2,10,QLatin1Char('0'));
// create the dialog if we need to show progress for long running uodate
if ((elapsedtime > 2000) && bar == NULL) {
bar = new GProgressDialog(title, 0, filenames.count(), context->mainWindow->init, context->mainWindow);
bar->show(); // lets hide until elapsed time is > 2 seconds
// lets make sure it goes to the center!
QApplication::processEvents();
}
if (bar) {
QString title = tr("Searching route in all rides...\nElapsed: %1\n%2").arg(elapsedString).arg(name);
bar->setLabelText(title);
bar->setValue(++processed);
}
QApplication::processEvents();
ride = RideFileFactory::instance().openRideFile(context, file, errors);
if (ride->isDataPresent(RideFile::lat)) {
out << " with GPS datas " << "\r\n";
searchRouteInRide(ride, true, &out);
}
else
out << " no GPS datas " << "\r\n";
if (bar && bar->wasCanceled()) {
out << "SEARCH NEW ROUTE CANCELED: " << QDateTime::currentDateTime().toString() + "\r\n";
break;
}
}
// now zap the progress bar
if (bar) delete bar;
// stop logging
out << "SEARCH NEW ROUTE ENDS: " << QDateTime::currentDateTime().toString() + "\r\n";
QMessageBox::information(context->mainWindow, tr("Route"), tr("This route '%1' was found %2 times in %3 activities.").arg(this->getName()).arg(this->getRides().count()).arg(processed));
log.close();
}
// This function converts decimal degrees to radians
double deg2rad(double deg) {
@@ -372,8 +248,6 @@ RouteSegment::distance(double lat1, double lon1, double lat2, double lon2) {
/*
* Routes (list of RouteSegment)
*
@@ -448,19 +322,13 @@ Routes::writeRoutes()
void
Routes::addRide(RideItem* ride)
{
searchRoutesInRide(ride->ride());
searchRoutesInRide(ride);
writeRoutes();
}
// delete a ride
void
Routes::deleteRide(RideItem* ride)
{
removeRideInRoutes(ride->ride());
}
void
Routes::removeRideInRoutes(RideFile* ride)
{
for (int routecount=0;routecount<routes.count();routecount++) {
RouteSegment *route = &routes[routecount];
@@ -469,24 +337,25 @@ Routes::removeRideInRoutes(RideFile* ride)
}
void
Routes::searchRoutesInRide(RideFile* ride)
{
// log of progress
QFile log(context->athlete->home->logs().canonicalPath() + "/" + "routes2.log");
log.open(QIODevice::ReadWrite);
QTextStream out(&log);
Routes::searchRoutesInRide(RideItem* item)
{
if (item->present.contains("G")) {
out << "SEARCH ROUTES IN NEW FILE STARTS: " << QDateTime::currentDateTime().toString() << "\r\n";
bool doclose=true;
if(item->isOpen()) doclose=false;
RideFile *ride = item->ride();
for (int routecount=0;routecount<routes.count();routecount++) {
RouteSegment *route = &routes[routecount];
out << "search route " << route->getName() << "(" << route->id().toString() << ")" << (routecount+1) << "/" << routes.count() << "\r\n";
route->searchRouteInRide(ride, false, &out);
if (ride) {
// we opened the ride so search all segments
for (int routecount=0;routecount<routes.count();routecount++) {
RouteSegment *segment = &routes[routecount];
segment->searchRouteInRide(ride);
}
if (doclose) item->close();
}
}
// stop logging
out << "SEARCH ROUTES IN NEW FILE ENDS: " << QDateTime::currentDateTime().toString() << "\r\n";
log.close();
}
void
@@ -530,10 +399,4 @@ Routes::createRouteFromInterval(IntervalItem *activeInterval) {
lastLon = point->lon;
}
}
// Search this route in all rides
route->searchRouteInAllRides(context);
// Save routes
writeRoutes();
}

View File

@@ -35,19 +35,23 @@ class Routes;
struct RouteRide;
struct RoutePoint;
class RouteSegment
class RouteSegment // represents a segment we match against
{
public:
RouteSegment();
RouteSegment(Routes *routes);
Routes *routes;
// accessors
QString getName();
void setName(QString _name);
QUuid id() const { return _id; }
QList<RoutePoint> getPoints();
QList<RouteRide> getRides();
QList<RouteRide2> getRouteRides();
void setId(QUuid x) { _id = x; }
// managing points and matched rides
int addPoint(RoutePoint _point);
int addRide(RouteRide _ride);
int addRideForRideFile(const RideFile *ride, double start, double stop, double precision);
@@ -55,52 +59,53 @@ class RouteSegment
double distance(double lat1, double lon1, double lat2, double lon2);
void searchRouteInAllRides(Context *context);
void searchRouteInRide(RideFile* ride, bool freememory, QTextStream* log);
void removeRideInRoute(RideFile* ride);
QList<RoutePoint> getPoints();
QList<RouteRide> getRides();
QList<RouteRide2> getRouteRides();
// segments always work on actual ridefiles
void searchRouteInRide(RideFile* ride);
void removeRideInRoute(RideItem* ride);
private:
Routes *routes;
QUuid _id; // unique id
QString name; // name, typically users name them by year e.g. "Col de Saxel"
QList<RoutePoint> points;
QList<RouteRide> rides;
};
class Routes : public QObject {
class Routes : public QObject { // top-level object with API and map of segments/rides
Q_OBJECT;
friend class ::RideItem; // access the route/ride map
public:
Routes(Context *context, const QDir &home);
// managing the list of route segments
void readRoutes();
int newRoute(QString name);
void createRouteFromInterval(IntervalItem *activeInterval);
void updateRoute(int index, QString name);
void deleteRoute(int);
void writeRoutes();
QList<RouteSegment> routes;
void createRouteFromInterval(IntervalItem *activeInterval);
void searchRoutesInRide(RideFile* ride);
// remove/searching rides
void removeRideInRoutes(RideFile* ride);
void searchRoutesInRide(RideItem* ride);
public slots:
// adding and deleting rides
void addRide(RideItem*);
void deleteRide(RideItem* ride);
signals:
void routesChanged();
protected:
QList<RouteSegment> routes;
private:
QDir home;
@@ -108,43 +113,26 @@ class Routes : public QObject {
};
/*class RouteRide2 : public QObject {
struct RouteRide { // represents a section of a ride that matches a segment
Q_OBJECT;
RouteRide() {}
RouteRide(QString filename, QDateTime startTime, double start, double stop, double precision) :
filename(filename), startTime(startTime), start(start), stop(stop), precision(precision) {}
public :
RouteRide2();
RouteRide2(QDateTime startTime, double start, double stop, double precision);// : startTime(startTime), start(start), stop(stop), precision(precision) {}
QDateTime startTime;
double start, stop;
double precision;
};*/
struct RouteRide {
QString filename;
QDateTime startTime;
double start, stop;
double precision;
RouteRide(QString filename, QDateTime startTime, double start, double stop, double precision) : filename(filename), startTime(startTime), start(start), stop(stop), precision(precision) {}
RouteRide() {}
};
struct RoutePoint
struct RoutePoint // represents a point within a segment
{
double lon, lat;
RoutePoint() : lon(0.0), lat(0.0) {}
RoutePoint(double lon, double lat) : lon(lon), lat(lat) {}
//double value(RideFile::SeriesType series) const;
double lon, lat;
};
#endif // ROUTE_H

View File

@@ -56,14 +56,14 @@ RouteItem::RouteItem(RouteSegment *route, const RouteRide *routeRide,
setTextAlignment(1, Qt::AlignRight);
setTextAlignment(2, Qt::AlignRight);
QDateTime dt;
foreach(RideItem *item, context->athlete->rideCache->rides()) {
//QDateTime dt;
//foreach(RideItem *item, context->athlete->rideCache->rides()) {
//XXX what is this code trying to do ??? XXX
if ((route->parseRideFileName(context, item->fileName, &notesFileName, &dt)) && (dt == routeRide->startTime)) { //
fileName = item->fileName;
}
}
//if ((route->parseRideFileName(context, item->fileName, &notesFileName, &dt)) && (dt == routeRide->startTime)) { //
// fileName = item->fileName;
//}
//}
}
RouteItem::RouteItem(RouteSegment *route, int type,