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