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