Merge branch 'master' of https://vcs.maemo.org/git/gpssportsniffer
[gpssportsniffer] / track.cpp
1 /****************************************************************************
2 **
3 **  Copyright (C) 2011  Tito Eritja Real <jtitoo@gmail.com>
4 **
5 **  This program is free software: you can redistribute it and/or modify
6 **  it under the terms of the GNU General Public License as published by
7 **  the Free Software Foundation, either version 3 of the License, or
8 **  (at your option) any later version.
9 **
10 **  This program is distributed in the hope that it will be useful,
11 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 **  GNU General Public License for more details.
14 **
15 **  You should have received a copy of the GNU General Public License
16 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 **
18 ****************************************************************************/
19
20 #include "track.h"
21 #include <QDebug>
22 #include <QXmlStreamWriter>
23 #include <QFile>
24
25 #include <QtCore/qmath.h>
26 #include "utils.h"
27
28
29 Summarized::Summarized(){
30
31     startTime=QDateTime::currentDateTime();
32
33     numPoints=0;
34
35     duration=0.0;
36     distance=0.0;
37     avgSpeed=0.0;
38     maxSpeed=0.0;
39     elevationGain=0.0;
40     elevationLoss=0.0;
41     minElevation=0.0;
42     maxElevation=0.0;
43     lastElevation=0.0;
44     avgPace=0.0;
45     bestPace=0.0;
46     lastElevationAccuracy=ACCURACY_V_GAIN+1;
47
48 }
49
50 QString Summarized::toSumString(){
51     return QString("Summarized={numPoints: %1, startTime: %2, duration: %3, distance: %4, avgSpeed: %5, maxSpeed: %6, elevationGain: %7, elevationLoss: %8, minElevation: %9, maxElevation: %10, avgPace: %11, minPace: %12}")
52             .arg(numPoints).arg(startTime.toString(CLEAN_DATE_FORMAT)).arg(duration).arg(distance)
53             .arg(avgSpeed).arg(maxSpeed).arg(elevationGain).arg(elevationLoss).arg(minElevation).arg(maxElevation).arg(avgPace).arg(bestPace);
54 }
55 void Summarized::addSummary(Summarized summary){
56
57     duration+=summary.getDuration();
58     distance+=summary.getDistance();
59
60     // avgSpeed of summary is not Avg, is speed of last point!
61     if(summary.getAvgPace()!=-1)
62         avgSpeed=((avgSpeed*numPoints)+summary.getAvgSpeed())/(numPoints+1);
63
64     if(summary.lastElevationAccuracy<=ACCURACY_V_GAIN){
65
66         elevationGain+=summary.getElevationGain();
67         elevationLoss+=summary.getElevationLoss();
68
69         if(minElevation!=0 && summary.minElevation!=0){
70             minElevation=min(minElevation,summary.minElevation);
71         }else if(minElevation==0 && summary.minElevation!=0){
72             minElevation=summary.minElevation;
73         }
74
75         maxElevation=max(maxElevation,summary.maxElevation);
76         lastElevation=summary.lastElevation;
77         lastElevationAccuracy=summary.lastElevationAccuracy;
78     }
79
80     maxSpeed=max(maxSpeed,summary.maxSpeed);
81
82     if(bestPace!=0 && summary.avgPace!=0){
83         bestPace=min(bestPace,summary.avgPace);
84     }else if(bestPace==0 && summary.avgPace!=0){
85         bestPace=summary.avgPace;
86     }
87
88     if(distance>0)
89         avgPace=(1000*duration)/(60*distance);
90
91     numPoints++;
92 }
93
94 Summarized& Summarized::account(GpsPoint point){
95
96     // this just is usefull for accounting the diference
97     // between two points. The summary MUST be new!!!
98     // data in here is not secure and not controlled.
99
100     numPoints++;
101     startTime=point.getTime();
102     endTime=point.getTime();
103     maxSpeed=point.getSpeed();
104     avgSpeed=point.getSpeed();
105
106     lastElevationAccuracy = point.getVerticalAccuracy();
107
108
109     if(lastElevationAccuracy<=ACCURACY_V_GAIN){
110         minElevation=point.getElevation();
111         maxElevation=point.getElevation();
112         lastElevation=point.getElevation();
113     }
114
115     return (*this);
116 }
117
118 Summarized& Summarized::account(GpsPoint first, GpsPoint last){
119
120     // this just is usefull for accounting the diference
121     // between two points. The summary MUST be new!!!
122     // data in here is not secure and not controlled.
123
124     numPoints++;
125
126     qDebug() << "first:" << first.toString();
127     qDebug() << "last:" << last.toString();
128
129     distance=first.distance(last);
130     duration=first.getTime().secsTo(last.getTime());
131     startTime=first.getTime();
132     endTime=last.getTime();
133     double lastEle=last.getElevation();
134     double firstEle=first.getElevation();
135
136     lastElevationAccuracy = last.getVerticalAccuracy();
137
138     qDebug() <<"firstElevation:" << firstEle;
139     qDebug() <<"lastElevation:" << lastEle;
140     qDebug() << "vertical accuracy:" << lastElevationAccuracy;
141
142
143     if(lastElevationAccuracy<=ACCURACY_V_GAIN){
144         if(lastEle>=firstEle){
145             elevationGain=lastEle-firstEle;
146         }else{
147             elevationLoss=firstEle-lastEle;
148         }
149
150         minElevation=min(first.getElevation(),last.getElevation());
151         maxElevation=max(first.getElevation(),last.getElevation());
152         lastElevation = last.getElevation();
153     }
154
155     maxSpeed=max(first.getSpeed(),last.getSpeed());
156     // keep speed of last point, will be usefull
157     // in add function
158     avgSpeed=last.getSpeed();
159
160
161     if(distance>0)
162         avgPace=(1000*duration)/(60*distance);
163
164     return (*this);
165 }
166
167
168 GpsPoint::GpsPoint(double latitude, double longitude, double elevation)
169     :latitude(latitude),
170     longitude(longitude),
171       elevation(elevation),speed(0),direction(0),magneticVariation(0),horizontalAccuracy(0),verticalAccuracy(0){
172 }
173
174 GpsPoint::GpsPoint(double latitude, double longitude, double elevation,QDateTime time,
175                    double speed, double direction, double magneticVariation, double horizontalAccuracy, double verticalAccuracy)
176 :latitude(latitude),longitude(longitude),elevation(elevation), time(time),
177   speed(speed), direction(direction), magneticVariation(magneticVariation),
178   horizontalAccuracy(horizontalAccuracy),verticalAccuracy(verticalAccuracy){
179
180     //qDebug() << "elevation:" << elevation;
181 }
182
183 GpsPoint::GpsPoint(double latitude, double longitude, double elevation,QDateTime time,
184                    double speed, double direction, double magneticVariation, double horizontalAccuracy, double verticalAccuracy,
185                    double distancePrev, QTime timeToPrev)
186 :latitude(latitude),longitude(longitude),elevation(elevation),time(time),
187   speed(speed), direction(direction), magneticVariation(magneticVariation),
188   horizontalAccuracy(horizontalAccuracy),verticalAccuracy(verticalAccuracy),distancePrev(distancePrev), timeToPrev(timeToPrev){
189
190 }
191
192 QString GpsPoint::toString(){
193     QString point = QString("GpsPoint{latitude: %1, longitude: %2, elevation: %3, time: %4, speed: %5, direction: %6, magneticVariation: %7, horizontalAccuracy: %8, verticalAccuracy: %9:, distancePrev: %10}")
194             .arg(latitude).arg(longitude).arg(elevation).arg(time.toString("dd-MM-yyyy hh:mm:ss")).arg(speed).arg(direction).arg(magneticVariation)
195             .arg(horizontalAccuracy).arg(verticalAccuracy).arg(QString::number(distancePrev,'f',10));
196     return point;
197 }
198
199 GpsPoint& GpsPoint::operator= (const GpsPoint &p){
200
201
202     latitude=p.latitude;
203     longitude=p.longitude;
204     elevation=p.elevation;
205     speed=p.speed;
206     direction=p.direction;
207     magneticVariation=p.magneticVariation;
208     time=p.time;
209     distancePrev=p.distancePrev;
210     timeToPrev=p.timeToPrev;
211     horizontalAccuracy=p.horizontalAccuracy;
212     verticalAccuracy=p.verticalAccuracy;
213
214     return(*this);
215 }
216
217
218
219 double GpsPoint::distance(GpsPoint p){
220
221     double dist=0;
222
223     double latitude1 = toRad(latitude);
224     double longitude1 = toRad(longitude);
225
226     double latitude2 = toRad(p.latitude);
227     double longitude2 = toRad(p.longitude);
228
229     if(latitude1==latitude2 && longitude1==longitude2){
230         dist=0;
231     }else{
232
233         double temp = sin(latitude1)*sin(latitude2)+cos(latitude1)*cos(latitude2)*cos(longitude2-longitude1);
234         dist= acos(temp)*EARTH_RADIUS*1000;
235     }
236     qDebug() << "distance between points: " << QString::number(dist,'f',10);
237     return dist;
238 }
239
240 int operator== (const GpsPoint& a, const GpsPoint& b)
241 {
242 if (a.latitude==b.latitude&&a.longitude==b.longitude&&a.elevation==b.elevation&&a.speed==b.speed
243         &&a.direction==b.direction &&a.magneticVariation==b.magneticVariation&&a.time==b.time
244         &&a.horizontalAccuracy==b.horizontalAccuracy&&a.verticalAccuracy==b.verticalAccuracy
245         &&a.distancePrev==b.distancePrev&&a.timeToPrev==b.timeToPrev)
246     return 1;
247 else
248     return 0;
249 }
250
251 int operator!= (const GpsPoint& a, const GpsPoint& b)
252 {
253 if (a.latitude!=b.latitude||a.longitude!=b.longitude||a.elevation!=b.elevation||a.speed!=b.speed
254         ||a.direction!=b.direction || a.magneticVariation!=b.magneticVariation||a.time!=b.time
255         ||a.horizontalAccuracy!=b.horizontalAccuracy||a.verticalAccuracy!=b.verticalAccuracy
256         ||a.distancePrev!=b.distancePrev||a.timeToPrev!=b.timeToPrev)
257     return 1;
258 else
259     return 0;
260 }
261
262
263
264 Lap::Lap(int number)
265     :number(number){
266     numPoints=0;
267
268 }
269
270 Lap::~Lap(){
271     for (int i = 0; i < points.size(); ++i) {
272         delete points.at(i);
273     }
274 }
275
276
277 Summarized* Lap::addPoint(GpsPoint *point){
278     //qDebug() <<"--- Lap::addPoint ---";
279     Summarized *summary = new Summarized();
280     if(points.size()>0){
281         GpsPoint* last = points.last();
282         point->setDistancePrev(point->distance(*last));
283         //qDebug() << "DistancePrev:" << QString::number(point->getDistancePrev(),'f',10);
284         summary->account(*last,*point);
285     }else{
286         //qDebug() << "First lap point!";
287         summary->account(*point);
288     }
289     addSummary(*summary);
290     //qDebug() << "Track summary:" << toSumString();
291
292     points.append(point);
293     return summary;
294 }
295
296 QString Lap::toString(){
297     QString lap = QString("Lap={number:").append(QString::number(number)).append(", numPoints:").append(QString::number(numPoints));
298
299     QListIterator<GpsPoint*> i(points);
300     while (i.hasNext()) {
301        GpsPoint* p = i.next();
302        lap.append(QString(", %1").arg(p->toString()));
303     }
304     lap.append(QString("}"));
305     return lap;
306 }
307
308 Activity::Activity(QString sport)
309     :sport(sport){
310     id=QDateTime::currentDateTime();
311     numLaps=0;
312     addLap();
313 }
314
315 Activity::~Activity(){
316     for (int i = 0; i < laps.size(); ++i) {
317         delete laps.at(i);
318     }
319 }
320
321 QString Activity::toString(){
322     QString activity = QString("Activity={sport:%1, id:%2, numLaps:%3").arg(sport).arg(id.toString(CLEAN_DATE_FORMAT)).arg(numLaps);
323
324     QListIterator<Lap*> i(laps);
325     while(i.hasNext()){
326         Lap* l=i.next();
327         activity.append(QString(", %1").arg(l->toString()));
328     }
329     activity.append(QString("}"));
330     return activity;
331 }
332
333 void Activity::addLap(){
334     numLaps++;
335     Lap* newLap = new Lap(numLaps);
336     laps.append(newLap);
337 }
338
339 Summarized* Activity::addPoint(GpsPoint* point){
340
341     //qDebug() <<"--- Activity::addPoint ---";
342     Lap* lastLap = laps.last();
343     Summarized* summary = lastLap->addPoint(point);
344     addSummary(*summary);
345     //qDebug() << "Track summary:" << toSumString();
346
347     return summary;
348
349 }
350
351
352 Track::Track(QString file,QString nameTrack)
353     :fileName(file),name(nameTrack){
354     numActivities=0;
355     addActivity(nameTrack);
356 }
357
358 Track::Track(QString fname)
359     :fileName(fname),name(fname){
360     numActivities=0;
361     addActivity(name);
362 }
363
364 Track::Track()
365     :fileName("noname"),name("noname"){
366     numActivities=0;
367     addActivity(name);
368 }
369
370
371 Track::~Track(){
372     for (int i = 0; i < activities.size(); ++i) {
373         delete activities.at(i);
374     }
375 }
376
377 void Track::addLap(){
378
379     if(activities.size()>0){
380         Activity* activity = activities.last();
381         activity->addLap();
382     }
383 }
384
385
386 void Track::addActivity(QString sport){
387
388     name=sport;
389     Activity* activity = new Activity(sport);
390     activities.append(activity);
391     numActivities++;
392 }
393
394 Summarized*  Track::addPoint(GpsPoint* point){
395
396     //qDebug() <<"--- Track::addPoint ---";
397     //qDebug() << "Adding point: " << point->toString();
398
399     Activity* actualActivity = activities.last();
400     Summarized* summary = actualActivity->addPoint(point);
401     addSummary(*summary);
402     //qDebug() << "Track summary:" << toSumString();
403
404     return summary;
405 }
406
407 QString Track::toString(){
408
409     QString track = QString("Track={fileName:%2, numActivities:%3").arg(fileName).arg(numActivities);
410
411     QListIterator<Activity*> i(activities);
412     while(i.hasNext()){
413         Activity* a = i.next();
414         track.append(QString(", %1").arg(a->toString()));
415     }
416     track.append(QString("}"));
417     return track;
418 }
419
420  QList<GpsPoint*> Track::getGpsPoints(){
421
422      QList<GpsPoint*> points=QList<GpsPoint*>();
423
424      if(numPoints==0)
425          return points;
426
427      QListIterator<Activity*> iAct(activities);
428      while (iAct.hasNext()) {
429          Activity* act = iAct.next();
430
431          QListIterator<Lap*> iLap(act->getLaps());
432          while (iLap.hasNext()) {
433              Lap* lap = iLap.next();
434
435              QListIterator<GpsPoint*> iPts(lap->getPoints());
436              while (iPts.hasNext()) {
437                  GpsPoint* point = iPts.next();
438                  points.append(point);
439              }
440          }
441      }
442      return points;
443  }
444
445 bool Track::saveToXML(){
446
447     XMLFileType fileType;
448     QFile file(fileName);
449
450     if(file.exists())
451         file.remove();
452
453     if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate)) {
454         qDebug() << "Error: Cannot write file "
455                  << qPrintable(fileName) << ": "
456                  << qPrintable(file.errorString());
457         return false;
458     }
459
460     if(fileName.endsWith(".tcx",Qt::CaseInsensitive)){
461         fileType=XMLFile_TCX;
462     }else if(fileName.endsWith(".gpx",Qt::CaseInsensitive)){
463         fileType=XMLFile_GPX;
464     }else{
465         qDebug()<<"Error: XML not recognized. " << "nameFile:" << fileName;
466     }
467
468     QXmlStreamWriter xmlWriter(&file);
469
470     xmlWriter.setAutoFormatting(true);
471
472     xmlWriter.writeStartDocument();
473
474     if(fileType==XMLFile_TCX){
475         xmlWriter.writeStartElement("TrainingCenterDatabase");
476         xmlWriter.writeAttribute("xmlns", TCX_XMLNS);
477         xmlWriter.writeAttribute("xmlns:xsi", TCX_XMLNS_XSI);
478         xmlWriter.writeAttribute("xsi:schemaLocation", TCX_XSI_SCHEMALOCATION);
479
480         writeTCXCourses(&xmlWriter);
481     }else{
482
483         xmlWriter.writeStartElement("gpx");
484         xmlWriter.writeAttribute("version",GPX_VERSION);
485         xmlWriter.writeAttribute("creator",GPX_CREATOR);
486         xmlWriter.writeAttribute("xmlns",GPX_XMLNS);
487
488         writeGPXTracks(&xmlWriter);
489     }
490
491
492
493
494     //xmlWriter->writeEndElement();
495     xmlWriter.writeEndDocument();
496     file.close();
497     qDebug() << "file closed";
498     if (file.error()) {
499         qDebug() << "Error on close: Cannot write file "
500                  << qPrintable(fileName) << ": "
501                  << qPrintable(file.errorString());
502         return false;
503     }
504     return true;
505 }
506
507 void Track::writeGPXTracks(QXmlStreamWriter* xmlWriter){
508
509     QListIterator<Activity*> i(activities);
510
511     while (i.hasNext()) {
512         Activity* act = i.next();
513         //xmlWriter->writeStartElement("NextSport");
514         writeGPXTrack(xmlWriter, act);
515         xmlWriter->writeEndElement();
516     }
517     xmlWriter->writeEndElement();
518     xmlWriter->writeEndElement();
519 }
520
521
522 void Track::writeTCXCourses(QXmlStreamWriter* xmlWriter){
523
524     xmlWriter->writeStartElement("Courses");
525     //xmlWriter->writeStartElement("MultiSportSession");
526
527     //xmlWriter->writeTextElement("Id",startTime.toString(XML_DATE_FORMAT));
528
529     QListIterator<Activity*> i(activities);
530
531     while (i.hasNext()) {
532         Activity* act = i.next();
533         //xmlWriter->writeStartElement("NextSport");
534         writeTCXCourse(xmlWriter, act);
535         xmlWriter->writeEndElement();
536     }
537     xmlWriter->writeEndElement();
538     xmlWriter->writeEndElement();
539 }
540
541 void Track::writeGPXTrack(QXmlStreamWriter* xmlWriter, Activity* act){
542
543     xmlWriter->writeStartElement("trk");
544
545     xmlWriter->writeTextElement("Name",act->getSport());
546
547     QListIterator<Lap*> i(act->getLaps());
548     while (i.hasNext()) {
549         Lap* lap = i.next();
550         writeGPXTrkseg(xmlWriter, lap);
551     }
552     xmlWriter->writeEndElement();
553
554
555 }
556
557 void Track::writeTCXCourse(QXmlStreamWriter* xmlWriter, Activity* act){
558
559
560     xmlWriter->writeStartElement("Course");
561     //xmlWriter->writeAttribute("Sport", act->getSport());
562     xmlWriter->writeTextElement("Name",act->getSport());
563
564     QListIterator<Lap*> i(act->getLaps());
565     while (i.hasNext()) {
566         Lap* lap = i.next();
567         writeTCXLap(xmlWriter, lap);
568     }
569     xmlWriter->writeEndElement();
570 }
571
572 void Track::writeGPXTrkseg(QXmlStreamWriter* xmlWriter, Lap* lap){
573
574     xmlWriter->writeStartElement("trkseg");
575
576     QListIterator<GpsPoint*> i(lap->getPoints());
577     while (i.hasNext()) {
578         GpsPoint* point = i.next();
579         writeGPXPoint(xmlWriter,point);
580     }
581     xmlWriter->writeEndElement();
582 }
583
584 void Track::writeTCXLap(QXmlStreamWriter* xmlWriter, Lap* lap){
585
586     xmlWriter->writeStartElement("Lap");
587     xmlWriter->writeAttribute("StartTime", lap->getStartTime().toString(XML_DATE_FORMAT));
588     xmlWriter->writeTextElement("TotalTimeSeconds",QString::number(lap->getDuration()));
589     xmlWriter->writeTextElement("DistanceMeters",QString::number(lap->getDistance(),'f',10));
590     xmlWriter->writeTextElement("MaximumSpeed",QString::number(lap->getMaxSpeed()));
591
592     QListIterator<GpsPoint*> i(lap->getPoints());
593     xmlWriter->writeStartElement("Track");
594     while (i.hasNext()) {
595         GpsPoint* point = i.next();
596         writeTCXPoint(xmlWriter,point);
597     }
598     xmlWriter->writeEndElement();
599     xmlWriter->writeEndElement();
600 }
601
602 void Track::writeGPXPoint(QXmlStreamWriter* xmlWriter, GpsPoint* point){
603
604     xmlWriter->writeStartElement("trkpt");
605     xmlWriter->writeAttribute("lat",QString::number(point->getLatitude(),'f',20));
606     xmlWriter->writeAttribute("lon",QString::number(point->getLatitude(),'f',20));
607
608
609     xmlWriter->writeTextElement("ele",QString::number(point->getElevation(),'f',20));
610     xmlWriter->writeTextElement("time",point->getTime().toString(XML_DATE_FORMAT));
611
612     xmlWriter->writeEndElement();
613 }
614
615 void Track::writeTCXPoint(QXmlStreamWriter* xmlWriter, GpsPoint* point){
616
617     xmlWriter->writeStartElement("Trackpoint");
618     xmlWriter->writeTextElement("Time",point->getTime().toString(XML_DATE_FORMAT));
619     xmlWriter->writeStartElement("Position");
620         xmlWriter->writeTextElement("LatitudeDegrees",QString::number(point->getLatitude(),'f',20));
621         xmlWriter->writeTextElement("LongitudeDegrees",QString::number(point->getLongitude(),'f',20));
622     xmlWriter->writeEndElement();
623     xmlWriter->writeTextElement("AltitudeMeters",QString::number(point->getElevation(),'f',20));
624     xmlWriter->writeTextElement("DistanceMeters",QString::number(point->getDistancePrev(),'f',20));
625     xmlWriter->writeEndElement();
626 }
627