Subversion Repositories public

Rev

Rev 275 | Rev 280 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
88 andreas 1
/***************************************************************************
221 andreas 2
 *   Copyright (C) 2007 - 2009 by Andreas Theofilu                         *
119 andreas 3
 *   andreas@theosys.at                                                    *
88 andreas 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 version 3 of the License.                *
8
 *                                                                         *
9
 *   This program is distributed in the hope that it will be useful,       *
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
 *   GNU General Public License for more details.                          *
13
 *                                                                         *
14
 *   You should have received a copy of the GNU General Public License     *
15
 *   along with this program; if not, write to the                         *
16
 *   Free Software Foundation, Inc.,                                       *
17
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18
 ***************************************************************************/
19
 
119 andreas 20
#include "config.h"
88 andreas 21
#include "managefile.h"
22
#include "sportwatcherwidget.h"
23
#include "settingswidget.h"
96 andreas 24
#include "progresswidget.h"
152 andreas 25
#include "wmsbase.h"
158 andreas 26
#include "coordinateswidget.h"
274 andreas 27
#include "shapewidget.h"
151 andreas 28
#include <string.h>
96 andreas 29
 
137 andreas 30
#include <iostream>
88 andreas 31
#include <kfiledialog.h>
32
#include <kmessagebox.h>
232 andreas 33
#include <KConfig>
88 andreas 34
#include <klocale.h>
232 andreas 35
#include <k3listview.h>
100 andreas 36
#include <kaboutdata.h>
232 andreas 37
#include <kaboutapplicationdialog.h>
218 andreas 38
#include <kinputdialog.h>
128 andreas 39
#include <kglobalsettings.h>
218 andreas 40
#include <kcombobox.h>
154 andreas 41
#include <qapplication.h>
88 andreas 42
#include <qstring.h>
43
#include <qdatetime.h>
109 andreas 44
#include <qtoolbutton.h>
132 andreas 45
#include <qcursor.h>
172 andreas 46
#include <qregexp.h>
232 andreas 47
#include <QMouseEvent>
246 andreas 48
#include <QRect>
49
#include <QRegion>
245 andreas 50
#include <KIcon>
151 andreas 51
 
232 andreas 52
#if defined HAVE_GDAL
158 andreas 53
   #include <gdal/ogr_spatialref.h>
54
   #include <gdal/ogrsf_frmts.h>
55
   #include <gdal/gdalwarper.h>
165 andreas 56
   #include <gdal/ogrsf_frmts.h>
157 andreas 57
#endif
58
 
218 andreas 59
#include "garmin.h"
158 andreas 60
#include "transform.h"
217 andreas 61
#include "import.h"
274 andreas 62
#include "render.h"
88 andreas 63
 
232 andreas 64
//#define DEBUG	1
151 andreas 65
 
137 andreas 66
using std::cout;
158 andreas 67
using std::cerr;
68
using std::clog;
137 andreas 69
using std::endl;
70
 
71
 
104 andreas 72
typedef struct
73
{
74
	double lon;
75
	double lat;
76
} posn_type;
77
 
245 andreas 78
sportwatcherWidget::sportwatcherWidget(QWidget *parent)
88 andreas 79
{
245 andreas 80
	ui_sportwatcherWidgetBase.setupUi(this);
232 andreas 81
 
82
#if defined HAVE_GDAL
157 andreas 83
	mFactor = 10;		// Factor to calculate square pixels
84
#endif
88 andreas 85
	mama = parent;
86
	gmn = 0;
87
	min_hr = max_hr = avg_hr = 0;
88
	min_height = max_height = 0.0;
89
	max_time = 0;
104 andreas 90
	zfactor = 0;
132 andreas 91
	mapLap = 0;
92
	mapPan = QRect(0, 0, 0, 0);
93
	stateHand = stateFlag = stateGlas = false;
94
	oldTransX = oldTransY = 0.0;
95
	lmbPressed = 0;
246 andreas 96
	DIRTY = true;
248 andreas 97
	curTab = ui_sportwatcherWidgetBase.tabView->currentIndex();
98
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
230 andreas 99
	kl = new KLocale(QString("kdesktop"), 0);
132 andreas 100
 
88 andreas 101
	// Load the config parameters
232 andreas 102
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
103
	KConfigGroup ic (&cfg, "SportWatcher");
104
	lower1 = ic.readEntry("lower1", 0);
105
	lower2 = ic.readEntry("lower2", 0);
106
	lower3 = ic.readEntry("lower3", 0);
107
	upper1 = ic.readEntry("upper1", 0);
108
	upper2 = ic.readEntry("upper2", 0);
109
	upper3 = ic.readEntry("upper3", 0);
110
	MaxHr = ic.readEntry("maxHr", 180);
111
	restHr = ic.readEntry("restHr", 60);
112
	vo2max = ic.readEntry("vo2max", 50);
113
	weight = ic.readEntry("weight", 70);
114
	sampleTime = ic.readEntry("seconds", 15);
115
	Serial = ic.readEntry("Serial", false);
116
	Forerunner = ic.readEntry("Forerunner", false);
117
	Contour = ic.readEntry("Contour", false);
118
	Device = ic.readEntry("Device", "/dev/ttyUSB0");
119
	Data = ic.readEntry("Data", QDir::home().absolutePath() + "/.sportwatcher");
120
	HRM = ic.readEntry("HRM", QDir::home().absolutePath() + "/polar");
121
	MAP = ic.readEntry("MAP", QDir::home().absolutePath() + "/.sportwatcher/track.wms");
122
	Units = ic.readEntry("Units", 0);
123
	MapType = ic.readEntry("MapType", 7);
88 andreas 124
}
125
 
126
sportwatcherWidget::~sportwatcherWidget()
127
{
104 andreas 128
	destroy();
230 andreas 129
	delete kl;
88 andreas 130
}
131
 
100 andreas 132
void sportwatcherWidget::destroy()
133
{
134
	if (gmn)
135
	   garmin_free_data (gmn);
136
 
137
	gmn = 0;
132 andreas 138
	oldTransX = oldTransY = 0.0;
100 andreas 139
}
140
 
245 andreas 141
void sportwatcherWidget::Initialize()
142
{
143
	// Set some widget settings
144
	ui_sportwatcherWidgetBase.btHand->setIcon(KIcon("hand"));
145
	ui_sportwatcherWidgetBase.btGlas->setIcon(KIcon("glas"));
146
	ui_sportwatcherWidgetBase.btGlasMinus->setIcon(KIcon("glas_minus"));
147
	ui_sportwatcherWidgetBase.btGlasPlus->setIcon(KIcon("glas_plus"));
148
	ui_sportwatcherWidgetBase.btFlag->setIcon(KIcon("flag"));
149
	ui_sportwatcherWidgetBase.btFullscreen->setIcon(KIcon("fullscreen"));
248 andreas 150
 
151
	if (curTab != 0)
152
	{
153
	   ui_sportwatcherWidgetBase.tabView->setCurrentIndex(0);
154
	   curTab = ui_sportwatcherWidgetBase.tabView->currentIndex();
155
	}
156
 
245 andreas 157
//	btHand->setToggleButton(true);
158
//	btGlas->setToggleButton(true);
159
	// Fill the activities
160
	getActivities();
161
#if defined HAVE_GDAL
162
	// Initialize the GDAL
163
	GDALAllRegister();
164
	poDataset = 0;
165
#endif
166
}
167
 
247 andreas 168
QTreeWidgetItem *sportwatcherWidget::findElement(QTreeWidget *wdg, const QString &val)
132 andreas 169
{
247 andreas 170
QTreeWidgetItem *item, *child, *subchild;
132 andreas 171
 
246 andreas 172
	if (!wdg)
247 andreas 173
	   return 0;
246 andreas 174
 
175
	for (int a = 0; a < wdg->topLevelItemCount(); a++)
132 andreas 176
	{
246 andreas 177
	   if (!(item = wdg->topLevelItem(a)))
178
	      continue;
132 andreas 179
 
246 andreas 180
	   for (int j = 0; j < item->childCount(); j++)
181
	   {
182
	      if (!(child = item->child(j)))
183
		 continue;
184
 
185
	      if (child->data(0, Qt::UserRole).toString() == val)
247 andreas 186
		 return child;
187
 
188
	      for (int i = 0; i < child->childCount(); i++)
189
	      {
190
		 if (!(subchild = child->child(i)))
191
		    continue;
192
 
193
		 if (subchild->data(0, Qt::UserRole).toString() == val)
194
		    return subchild;
195
	      }
246 andreas 196
	   }
132 andreas 197
	}
198
 
247 andreas 199
	return 0;
132 andreas 200
}
201
 
88 andreas 202
/*
203
 * Search for a directory named .sportwatcher in the home directory of
204
 * the user and search for *.gmn files. Open the files and read the header
205
 * to find the basic data of activities. Then add the information into
206
 * the activities KListView.
207
 */
208
void sportwatcherWidget::getActivities()
209
{
210
QString path, txt;
232 andreas 211
QDir mdir, dir = QDir::home();
88 andreas 212
QStringList years, months;
247 andreas 213
QTreeWidgetItem *running, *biking, *other, *year_run, *year_bike, *year_other;
246 andreas 214
QTreeWidgetItem *el;
215
QList<QTreeWidgetItem *> item;
232 andreas 216
int i, j;
247 andreas 217
int y_run, y_bike, y_other;
88 andreas 218
RUN_NODE *rn;
219
LAP *lap;
220
 
221
	if (Data.isEmpty())
222
	{
232 andreas 223
	   path = dir.homePath();
88 andreas 224
	   path.append("/.sportwatcher");
225
	}
226
	else
227
	   path = Data;
228
 
229
	dir.setPath(path);
132 andreas 230
	dir.refresh();
88 andreas 231
 
232
	if (!dir.exists())
233
	{
234
	   dir.mkdir(path);
235
	   return;
236
	}
237
 
132 andreas 238
	destroy();
128 andreas 239
 
245 andreas 240
	if (!ui_sportwatcherWidgetBase.liActivities)
241
	{
242
	   KMessageBox::error(this, i18n("Error initializing some widgets of main window!"));
243
	   return;
244
	}
245
 
246
	ui_sportwatcherWidgetBase.liActivities->clear();
246 andreas 247
	ui_sportwatcherWidgetBase.liActivities->setSortingEnabled(false);
248
	ui_sportwatcherWidgetBase.liActivities->setColumnCount(1);
245 andreas 249
 
246 andreas 250
	running = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
251
	biking = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
252
	other = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
253
 
245 andreas 254
	if (!other || !biking || !running)
255
	{
256
	   KMessageBox::error(this, i18n("Not enough memory to initilize application!"));
257
	   return;
258
	}
259
 
246 andreas 260
	running->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
261
	biking->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
262
	other->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
128 andreas 263
 
246 andreas 264
	running->setText(0, i18n("Running"));
265
	biking->setText(0, i18n("Biking"));
266
	other->setText(0, i18n("Others"));
88 andreas 267
 
246 andreas 268
	KIcon fld(QString("history"));
269
	running->setIcon(0, fld);
270
	biking->setIcon(0, fld);
271
	other->setIcon(0, fld);
272
 
273
	running->setStatusTip(0, i18n("\"Running\" activities."));
274
	biking->setStatusTip(0, i18n("\"Biking\" activities."));
275
	other->setStatusTip(0, i18n("\"Other\" and \"Multisport\" activities."));
276
 
88 andreas 277
	dir.cd(path);
278
	dir.setFilter(QDir::Dirs | QDir::NoSymLinks);
279
	dir.setSorting(QDir::Name);
132 andreas 280
	dir.refresh();
232 andreas 281
	QFileInfoList list = dir.entryInfoList();
88 andreas 282
 
232 andreas 283
	if (!list.size())
88 andreas 284
	   return;
285
 
232 andreas 286
	for (i = 0; i < list.size(); i++)		// Years
287
	{
288
	QFileInfo fileInfo = list.at(i);
88 andreas 289
 
232 andreas 290
	   if (fileInfo.fileName() == QString(".") || fileInfo.fileName() == QString(".."))
88 andreas 291
	      continue;
292
 
232 andreas 293
	   years += fileInfo.absoluteFilePath();
88 andreas 294
	}
295
 
232 andreas 296
	for (i = 0; i < years.size(); ++i)
88 andreas 297
	{
232 andreas 298
	   if (months.size() > 0)
88 andreas 299
	      months.clear();
300
 
232 andreas 301
	   dir.setPath(years.at(i));
132 andreas 302
	   dir.refresh();
232 andreas 303
	   list = dir.entryInfoList();
88 andreas 304
 
232 andreas 305
	   if (!list.size())
88 andreas 306
	      continue;
307
 
232 andreas 308
	   for (j = 0; j < list.size(); j++)		// Months
309
	   {
310
	   QFileInfo fileInfo = list.at(j);
88 andreas 311
 
232 andreas 312
	      if (fileInfo.fileName() == QString(".") || fileInfo.fileName() == QString(".."))
88 andreas 313
		 continue;
314
 
232 andreas 315
	      months += fileInfo.absoluteFilePath();
88 andreas 316
	   }
317
 
232 andreas 318
	   for (j = 0; j < months.size(); ++j)
88 andreas 319
	   {
232 andreas 320
	      mdir.setPath(months.at(j));
321
	      mdir.cd(months.at(j));
88 andreas 322
	      mdir.setFilter(QDir::Files | QDir::NoSymLinks);
246 andreas 323
	      mdir.setSorting(QDir::Name);
232 andreas 324
	      mdir.setNameFilters(QStringList("*.gmn"));
132 andreas 325
	      mdir.refresh();
232 andreas 326
	      list = mdir.entryInfoList();
88 andreas 327
 
232 andreas 328
	      if (!list.size())
88 andreas 329
		 continue;
330
 
232 andreas 331
	      for (int a = 0; a < list.size(); a++)		// Files
332
	      {
333
	      QFileInfo fileInfo = list.at(a);
88 andreas 334
 
232 andreas 335
		 files += fileInfo.absoluteFilePath();
88 andreas 336
	      }
337
	   }
338
	}
339
 
247 andreas 340
	y_run = y_bike = y_other = 0;
341
	year_run = year_bike = year_other = 0;
88 andreas 342
	// Open every file and read its head
232 andreas 343
	for (i = 0; i < files.size(); ++i)
88 andreas 344
	{
247 andreas 345
	QTreeWidgetItem *yr, *yb, *yo;
132 andreas 346
 
246 andreas 347
	   if (findElement(ui_sportwatcherWidgetBase.liActivities, files.at(i)))
348
	      continue;
349
 
88 andreas 350
	   spw.destroy();
351
 
232 andreas 352
	   if (spw.setFileName(files.at(i).toAscii().constData()) == -1)
88 andreas 353
	      return;
354
 
355
	   if (gmn)
246 andreas 356
	   {
88 andreas 357
	      garmin_free_data (gmn);
246 andreas 358
	      gmn = 0;
359
	   }
88 andreas 360
 
246 andreas 361
	   if (!(gmn = spw.readFile()))
362
	      continue;
363
 
88 andreas 364
	   ds.destroy();
365
	   ds.garmin_print_data(gmn);
366
	   rn = ds.getRunNode();
367
 
245 andreas 368
	   if (!rn)
369
	      return;
370
 
88 andreas 371
	   lap = ds.getLap(rn->run->first_lap_index);
245 andreas 372
 
373
	   if (!lap)
374
	      return;
375
 
88 andreas 376
	   const QDateTime *qt = garmin_dtime (lap->start_time);
232 andreas 377
	   // By default set the name of the session to the date and time
378
	   // it was recorded.
379
	   QString idx = kl->formatDateTime (*qt, KLocale::ShortDate, true);
248 andreas 380
	   QString stTip = kl->formatDateTime (*qt, KLocale::LongDate, true);
88 andreas 381
 
232 andreas 382
	   // If we have a custom name for this session, set it.
217 andreas 383
	   if (strlen (rn->run->workout.name) > 1 && strlen (rn->run->workout.name) < 16 && isalpha (rn->run->workout.name[0]))
232 andreas 384
	      idx = QString (rn->run->workout.name);
217 andreas 385
 
88 andreas 386
	   switch (rn->run->sport_type)
387
	   {
388
	      case D1000_running:
247 andreas 389
		 yr = findElement(ui_sportwatcherWidgetBase.liActivities, QString("run_%1").arg(qt->date().year()));
390
 
391
		 if (!yr && qt->date().year() != y_run)
392
		 {
393
		    year_run = new QTreeWidgetItem(running, running);
394
		    y_run = qt->date().year();
395
		    year_run->setText(0, QString("%1").arg(y_run));
396
		    year_run->setData(0, Qt::UserRole, QString("run_%1").arg(y_run));
397
		    year_run->setIcon(0, KIcon(QString("today")));
398
		    year_run->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
399
		 }
400
		 else
401
		    year_run = yr;
402
 
403
		 el = new QTreeWidgetItem(year_run, year_run);
246 andreas 404
		 el->setText(0, idx);
405
		 el->setData(0, Qt::UserRole, files.at(i));
247 andreas 406
		 el->setIcon(0, KIcon(QString("spw-running")));
246 andreas 407
		 el->setStatusTip(0, stTip);
408
		 el->setToolTip(0, stTip);
409
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
88 andreas 410
	      break;
411
 
412
	      case D1000_biking:
247 andreas 413
		 yb = findElement(ui_sportwatcherWidgetBase.liActivities, QString("bike_%1").arg(qt->date().year()));
414
 
415
		 if (!yb && qt->date().year() != y_bike)
416
		 {
417
		    year_bike = new QTreeWidgetItem(biking, biking);
418
		    y_bike = qt->date().year();
419
		    year_bike->setText(0, QString("%1").arg(y_bike));
420
		    year_bike->setData(0, Qt::UserRole, QString("bike_%1").arg(y_run));
421
		    year_bike->setIcon(0, KIcon(QString("today")));
422
		    year_bike->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
423
		 }
424
		 else
425
		    year_bike = yb;
426
 
427
		 el = new QTreeWidgetItem(year_bike, year_bike);
246 andreas 428
		 el->setText(0, idx);
429
		 el->setData(0, Qt::UserRole, files.at(i));
430
		 el->setStatusTip(0, stTip);
431
		 el->setToolTip(0, stTip);
432
		 el->setIcon(0, KIcon(QString("bike")));
433
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
88 andreas 434
	      break;
435
 
436
	      case D1000_other:
247 andreas 437
	      default:
438
		 yo = findElement(ui_sportwatcherWidgetBase.liActivities, QString("other_%1").arg(qt->date().year()));
88 andreas 439
 
247 andreas 440
		 if (!yo && qt->date().year() != y_other)
441
		 {
442
		    year_other = new QTreeWidgetItem(other, other);
443
		    y_other = qt->date().year();
444
		    year_other->setText(0, QString("%1").arg(y_other));
445
		    year_other->setData(0, Qt::UserRole, QString("other_%1").arg(y_run));
446
		    year_other->setIcon(0, KIcon(QString("today")));
447
		    year_other->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
448
		 }
449
		 else
450
		    year_other = yo;
451
 
452
		 el = new QTreeWidgetItem(year_other, year_other);
246 andreas 453
		 el->setText(0, idx);
454
		 el->setData(0, Qt::UserRole, files.at(i));
455
		 el->setStatusTip(0, stTip);
456
		 el->setToolTip(0, stTip);
457
		 el->setIcon(0, KIcon(QString("other")));
458
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
88 andreas 459
	   }
460
 
461
	   delete qt;
462
	}
463
 
246 andreas 464
	ui_sportwatcherWidgetBase.liActivities->setItemExpanded(running, true);
88 andreas 465
 
247 andreas 466
	if (year_run)
467
	   ui_sportwatcherWidgetBase.liActivities->setItemExpanded(year_run, true);
468
 
88 andreas 469
	if (gmn)
470
	   garmin_free_data (gmn);
471
 
472
	gmn = 0;
473
}
474
 
475
/*$SPECIALIZATION$*/
476
void sportwatcherWidget::btFullscreenSlot()
477
{
132 andreas 478
	oldTransX = oldTransY = 0.0;
479
	mapPan.setCoords(0, 0, 0, 0);
246 andreas 480
	DIRTY = true;
104 andreas 481
	showTrack(0);
246 andreas 482
	DIRTY = false;
88 andreas 483
}
484
 
485
void sportwatcherWidget::btGlasMinusSlot()
486
{
132 andreas 487
bool sh = stateHand;
488
 
489
	stateHand = false;
246 andreas 490
	DIRTY = true;
104 andreas 491
	showTrack(zfactor - 1000);
246 andreas 492
	DIRTY = false;
132 andreas 493
	stateHand = sh;
88 andreas 494
}
495
 
496
void sportwatcherWidget::btGlasPlusSlot()
497
{
132 andreas 498
bool sh = stateHand;
499
 
500
	stateHand = false;
246 andreas 501
	DIRTY = true;
104 andreas 502
	showTrack(zfactor + 1000);
246 andreas 503
	DIRTY = false;
132 andreas 504
	stateHand = sh;
88 andreas 505
}
506
 
507
void sportwatcherWidget::btHandSlot()
508
{
132 andreas 509
QCursor cs;
510
 
511
	if (stateGlas)
512
	{
513
	   stateGlas = false;
245 andreas 514
	   ui_sportwatcherWidgetBase.btGlas->toggle();
132 andreas 515
	}
516
 
517
	stateHand = (stateHand) ? false : true;
518
 
519
	if (stateHand)
232 andreas 520
	   cs.setShape(Qt::PointingHandCursor);
132 andreas 521
	else
232 andreas 522
	   cs.setShape(Qt::ArrowCursor);
132 andreas 523
 
245 andreas 524
	ui_sportwatcherWidgetBase.imgMap->setCursor(cs);
88 andreas 525
}
526
 
527
void sportwatcherWidget::btGlasSlot()
528
{
132 andreas 529
QCursor cs;
530
 
531
	if (stateHand)
532
	{
533
	   stateHand = false;
245 andreas 534
	   ui_sportwatcherWidgetBase.btHand->toggle();
132 andreas 535
	}
536
 
537
	stateGlas = (stateGlas) ? false : true;
538
 
539
	if (stateGlas)
232 andreas 540
	   cs.setShape(Qt::ForbiddenCursor);
132 andreas 541
	else
232 andreas 542
	   cs.setShape(Qt::ArrowCursor);
132 andreas 543
 
245 andreas 544
	ui_sportwatcherWidgetBase.imgMap->setCursor(cs);
88 andreas 545
}
546
 
547
void sportwatcherWidget::btFlagSlot()
548
{
549
}
550
 
232 andreas 551
void sportwatcherWidget::liLapsSlot(Q3ListViewItem *item)
88 andreas 552
{
132 andreas 553
QString sl;
554
int l;
555
int idx;
556
RUN_NODE *rn;
557
LAP *lap;
558
 
559
	if (!item)
560
	   return;
561
 
246 andreas 562
	DIRTY = true;
132 andreas 563
	sl = item->text(0).mid(4, 3);
564
	l = sl.toInt();
565
 
566
	if (l <= 0)
567
	{
245 andreas 568
	   // Show the whole map and track
132 andreas 569
	   showTrack(zfactor, mapPan, 0);
245 andreas 570
	   // Don't mark any lap. Just display normal.
148 andreas 571
	   showCurves(0);
132 andreas 572
	   return;
573
	}
574
 
575
	rn = ds.getRunNode();
576
	idx = rn->run->first_lap_index;
577
	lap = ds.getLap(idx + l - 1);
245 andreas 578
	// Show the part of the track, corresponding to the selected lap
132 andreas 579
	showTrack(zfactor, mapPan, lap);
245 andreas 580
	// Mark the selected lap
148 andreas 581
	showCurves(lap);
246 andreas 582
	DIRTY = false;
88 andreas 583
}
584
 
246 andreas 585
void sportwatcherWidget::liActivitiesSlot(QTreeWidgetItem *item, int)
88 andreas 586
{
587
	if (!item)
588
	   return;
589
 
245 andreas 590
	spw.destroy();
88 andreas 591
 
246 andreas 592
	if (spw.setFileName(item->data(0, Qt::UserRole).toString().toAscii().constData()) == -1)
245 andreas 593
	   return;
88 andreas 594
 
245 andreas 595
	if (gmn)
596
	   garmin_free_data (gmn);
88 andreas 597
 
245 andreas 598
	gmn = spw.readFile();
599
	zfactor = 0;
248 andreas 600
 
601
	// Make sure, the currently not visible tabs will be redrawn as soon
602
	// as they are visible.
603
	switch (curTab)
604
	{
605
	   case 0: tabDirt1 = tabDirt2 = tabDirt3 = true; break;
606
	   case 1: tabDirt0 = tabDirt2 = tabDirt3 = true; break;
607
	   case 2: tabDirt0 = tabDirt1 = tabDirt3 = true; break;
608
	   case 3: tabDirt0 = tabDirt1 = tabDirt2 = true; break;
609
	}
610
 
245 andreas 611
	// Display the just loaded sport session
246 andreas 612
	DIRTY = true;
245 andreas 613
	showLaps();
614
	showTrack();
615
	showCurves();
250 andreas 616
 
617
	if (curTab == 2)
618
	{
619
	   showThreeCurve();
620
	   tabDirt2 = false;
621
	}
622
 
246 andreas 623
	DIRTY = false;
88 andreas 624
}
625
 
626
void sportwatcherWidget::filePrint()
627
{
230 andreas 628
	KMessageBox::information(this, i18n("This function is currently not implemented!"));
88 andreas 629
}
630
 
104 andreas 631
/*
632
 * This function allows the user to choose a file name, where we save the
633
 * actual lap in Garmins own TCX format. This format is simply a XML-file.
634
 * For details about the schema of this file look at
635
 * http://developer.garmin.com/schemas/tcx/v2/
636
 */
88 andreas 637
void sportwatcherWidget::fileSaveAs()
638
{
104 andreas 639
QString fname;
640
QFile fn;
641
QString buffer;
642
RUN_NODE *rn, *rakt;
643
LAP *lap;
644
POINT *point;
645
int indent, i;
646
QDateTime *qt;
232 andreas 647
KUrl sDir("~/");
246 andreas 648
QRegExp rx("(\\.tcx|\\.gpx|\\.osm)$");
104 andreas 649
 
650
	if (!gmn)
651
	{
652
	   KMessageBox::error(this, i18n("Currently no activity is selected!"));
653
	   return;
654
	}
655
 
246 andreas 656
	rx.setPatternSyntax(QRegExp::RegExp);
232 andreas 657
	fname = KFileDialog::getSaveFileName(sDir, QString("*.tcx|Garmin Training Center (*.tcx)\n*.gpx|GPS Excange Format (*.gpx)\n*.osm|OpenStreetMap (*.osm)"), this, QString("SportWatcher"));
104 andreas 658
 
659
	if (fname.isEmpty())
660
	   return;
661
 
246 andreas 662
	if (rx.indexIn(fname) < 0)
172 andreas 663
	{
232 andreas 664
	   KMessageBox::error(this, i18n("The file %1 has no valid file extension!").arg(fname));
172 andreas 665
	   return;
666
	}
667
 
232 andreas 668
	fn.setFileName(fname);
104 andreas 669
 
670
	if (fn.exists())
671
	{
672
	   if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
673
	      return;
674
	}
675
 
246 andreas 676
	rx.setPattern("*.gpx");
677
	rx.setPatternSyntax(QRegExp::Wildcard);
172 andreas 678
 
246 andreas 679
	if (rx.exactMatch(fname))	// Should we create a *.gpx file?
172 andreas 680
	{
681
	   sportwatcherWidget::saveGPX(fname);
682
	   return;
683
	}
684
 
246 andreas 685
	rx.setPattern("*.osm");
686
	rx.setPatternSyntax(QRegExp::Wildcard);
172 andreas 687
 
246 andreas 688
	if (rx.exactMatch(fname))	// Should we create a *.osm file?
172 andreas 689
	{
690
	   sportwatcherWidget::saveOSM(fname);
691
	   return;
692
	}
693
 
694
	// No, we create a *.tcx file!
171 andreas 695
	indent = 0;
104 andreas 696
	rn = ds.getRunNode();
697
	lap = ds.getLap(rn->run->first_lap_index);
698
 
699
	if ((point = ds.getPoint(lap->start_time)) == 0)
700
	{
701
	   KMessageBox::error(this, i18n("No data to save!"));
702
	   return;
703
	}
704
 
232 andreas 705
	if (!fn.open(QIODevice::ReadWrite | QIODevice::Truncate))
104 andreas 706
	{
232 andreas 707
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fname));
104 andreas 708
	   return;
709
	}
710
 
711
	buffer = QString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
712
	buffer.append("<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
713
	buffer.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
714
	buffer.append("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
715
	buffer.append("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
716
	writeTag (fn, buffer, indent);
717
	buffer = QString("<folders/>\n\n");
718
	writeTag (fn, buffer, indent);
719
 
720
	// Open a course
721
	QFileInfo finfo(fname);
232 andreas 722
	buffer = QString("<Courses>\n   <Course>\n      <name>%1</name>\n").arg(QFileInfo(finfo).baseName());
104 andreas 723
	writeTag (fn, buffer, indent);
724
	indent = 2;
725
 
726
	rakt = rn;
727
 
728
	while (rakt)
729
	{
730
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
731
	   	rakt->run->type != data_D1010)
732
	   {
733
	      rakt = rakt->next;
734
	      continue;
735
	   }
736
 
737
	   for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
738
	   {
739
	      if ((lap = ds.getLap(i)) == NULL)
740
		 continue;
741
 
742
	      // Write the information of the lap
743
	      writeTag (fn, QString("<Lap>\n"), indent);
744
	      indent++;
745
	      buffer.sprintf("<TotalTimeSeconds>%f</TotalTimeSeconds>\n", (double)lap->total_time / 100.0);
746
	      writeTag (fn, buffer, indent);
172 andreas 747
	      qt = garmin_dtime(lap->start_time);
748
	      buffer = QString("<StartTime>%1</StartTime>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
749
	      writeTag (fn, buffer, indent);
104 andreas 750
	      buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", lap->total_distance);
751
	      writeTag (fn, buffer, indent);
752
 
753
	      writeTag (fn, QString("<BeginPosition>\n"), indent);
754
	      indent++;
755
	      buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->begin.lat));
756
	      writeTag (fn, buffer, indent);
757
	      buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->begin.lon));
758
	      writeTag (fn, buffer, indent);
759
	      indent--;
760
	      writeTag (fn, QString("</BeginPosition>\n"), indent);
761
 
762
	      writeTag (fn, QString("<EndPosition>\n"), indent);
763
	      indent++;
764
	      buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->end.lat));
765
	      writeTag (fn, buffer, indent);
766
	      buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->end.lon));
767
	      writeTag (fn, buffer, indent);
768
	      indent--;
769
	      writeTag (fn, QString("</EndPosition>\n"), indent);
770
 
771
	      writeTag (fn, QString("<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
772
	      indent++;
773
	      buffer.sprintf("<Value>%d</Value>\n", lap->avg_heart_rate);
774
	      writeTag (fn, buffer, indent);
775
	      indent--;
776
	      writeTag (fn, QString("</AverageHeartRateBpm>\n"), indent);
777
 
778
	      writeTag (fn, QString("<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
779
	      indent++;
172 andreas 780
	      buffer.sprintf("<Value>%d</Value>\n", lap->max_heart_rate);
104 andreas 781
	      writeTag (fn, buffer, indent);
782
	      indent--;
783
	      writeTag (fn, QString("</MaximumHeartRateBpm>\n"), indent);
784
 
172 andreas 785
	      if (lap->avg_cadence < 255)
786
	      {
787
		 buffer.sprintf("<AverageCadence>%d</AverageCadence>\n", lap->avg_cadence);
788
		 writeTag (fn, buffer, indent);
215 andreas 789
		 buffer.sprintf("<Cadence>%d</Cadence>\n", lap->avg_cadence);
790
		 writeTag (fn, buffer, indent);
172 andreas 791
	      }
792
 
215 andreas 793
	      buffer = QString("<Intensity>%1</Intensity>\n").arg((!lap->intensity) ? "Active" : "Resting");
104 andreas 794
	      writeTag (fn, buffer, indent);
217 andreas 795
 
796
	      buffer.sprintf("<Calories>%d</Calories>\n", lap->calories);
797
	      writeTag (fn, buffer, indent);
798
 
799
	      buffer.sprintf("<MaximumSpeed>%f</MaximumSpeed>\n", lap->max_speed);
800
	      writeTag (fn, buffer, indent);
104 andreas 801
	      indent--;
802
	      writeTag (fn, QString("</Lap>\n"), indent);
803
 
804
	      point = ds.getPoint(lap->start_time);
805
	      writeTag (fn, QString("<Track>\n"), indent);
806
	      indent++;
807
 
808
	      while (point)
809
	      {
810
		 if (point->time > (lap->start_time + (lap->total_time / 100)))
811
		    break;
812
 
813
		 writeTag (fn, QString("<Trackpoint>\n"), indent);
814
		 indent++;
815
		 qt = garmin_dtime(point->time);
816
		 buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
817
		 writeTag (fn, buffer, indent);
818
		 delete qt;
819
		 writeTag (fn, QString("<Position>\n"), indent);
820
		 indent++;
821
		 buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(point->posn.lat));
822
		 writeTag (fn, buffer, indent);
823
		 buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(point->posn.lon));
824
		 writeTag (fn, buffer, indent);
825
		 indent--;
826
		 writeTag (fn, QString("</Position>\n"), indent);
827
 
828
		 if (point->alt < 20000.0)
829
		 {
830
		    buffer.sprintf("<AltitudeMeters>%f</AltitudeMeters>\n", point->alt);
831
		    writeTag (fn, buffer, indent);
832
		 }
833
 
834
		 buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", point->distance);
835
		 writeTag (fn, buffer, indent);
836
 
837
		 if (point->heart_rate > 0 && point->heart_rate < 250)
838
		 {
839
		    writeTag (fn, QString("<HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
840
		    indent++;
841
		    buffer.sprintf("<Value>%d</Value>\n", point->heart_rate);
842
		    writeTag (fn, buffer, indent);
843
		    indent--;
844
		    writeTag (fn, QString("</HeartRateBpm>\n"), indent);
845
		 }
846
 
249 andreas 847
		 if (point->cadence < 0xff)
215 andreas 848
		 {
849
		    buffer.sprintf("<Cadence>%d</Cadence>\n", point->cadence);
850
		    writeTag (fn, buffer, indent);
851
		 }
852
 
853
		 buffer.sprintf("<SensorState>%s</SensorState>\n", (!point->sensor) ? "Absent" : "Present");
104 andreas 854
		 writeTag (fn, buffer, indent);
855
		 indent--;
856
		 writeTag (fn, QString("</Trackpoint>\n"), indent);
857
		 point = ds.getPoint(point->time + 1);
858
	      }
859
 
860
	      indent--;
861
	      writeTag (fn, QString("</Track>\n"), indent);
862
	   }
863
 
864
	   indent--;
865
	   writeTag (fn, QString("</Course>\n"), indent);
866
	   indent--;
867
	   writeTag (fn, QString("</Courses>\n"), indent);
868
	   rakt = rakt->next;
869
	}
870
 
871
	// Write information about device
872
	// Here my personal signature is written :-)
873
	writeTag (fn, QString("<Author xsi:type=\"Application_t\">\n"), indent);
874
	indent++;
875
	writeTag (fn, QString("<Name>SportWatcher</Name>\n"), indent);
876
	writeTag (fn, QString("<Build>\n"), indent);
877
	indent++;
878
	writeTag (fn, QString("<Version>\n"), indent);
879
	indent++;
880
	writeTag (fn, QString("<VersionMajor>0</VersionMajor>\n"), indent);
881
	writeTag (fn, QString("<VersionMinor>1</VersionMinor>\n"), indent);
882
	writeTag (fn, QString("<BuildMajor>0</BuildMajor>\n"), indent);
883
	writeTag (fn, QString("<BuildMinor>0</BuildMinor>\n"), indent);
884
	indent--;
885
	writeTag (fn, QString("</Version>\n"), indent);
886
	writeTag (fn, QString("<Type>Beta</Type>\n"), indent);
887
	writeTag (fn, QString("<Time>Jan 31 2008, 00:00:00</Time>\n"), indent);
888
	writeTag (fn, QString("<Builder>theosys</Builder>\n"), indent);
889
	indent--;
890
	writeTag (fn, QString("</Build>\n"), indent);
891
	writeTag (fn, QString("<LangID>EN</LangID>\n"), indent);
892
	writeTag (fn, QString("<PartNumber>000-00000-00</PartNumber>\n"), indent);
893
	indent--;
894
	writeTag (fn, QString("</Author>\n"), indent);
895
	writeTag (fn, QString("</TrainingCenterDatabase>\n"), indent);
896
 
897
	fn.close();
898
	KMessageBox::information(this, i18n("File ") + fname + i18n(" was written successfully."));
88 andreas 899
}
900
 
901
void sportwatcherWidget::fileSave()
902
{
172 andreas 903
	KMessageBox::information(this, i18n("This function is currently not implemented!"));
88 andreas 904
}
905
 
172 andreas 906
void sportwatcherWidget::saveGPX(const QString &fn)
907
{
908
QFile qf;
909
QString buffer;
910
RUN_NODE *rn, *rakt;
911
LAP *lap;
912
POINT *point;
913
int indent;
914
unsigned int i;
915
QDateTime *qt;
916
double minLat, minLon, maxLat, maxLon;
917
 
918
	indent = 0;
919
	rn = ds.getRunNode();
920
	lap = ds.getLap(rn->run->first_lap_index);
921
 
922
	if ((point = ds.getPoint(lap->start_time)) == 0)
923
	{
924
	   KMessageBox::error(this, i18n("No data to save!"));
925
	   return;
926
	}
927
 
232 andreas 928
	qf.setFileName(fn);
172 andreas 929
 
232 andreas 930
	if (!qf.open(QIODevice::ReadWrite | QIODevice::Truncate))
172 andreas 931
	{
232 andreas 932
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fn));
172 andreas 933
	   return;
934
	}
935
 
936
	buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
937
	buffer.append("<gpx version=\"1.1\" creator=\"TheoSys SportWatcher\" xmlns=\"http://www.topografix.com/GPX/1/1\">\n");
938
	buffer.append("   <metadata>\n");
939
	indent = 0;
940
	writeTag (qf, buffer, indent);
941
 
942
	// Find the edges of our coordinates
943
	// We need this information in the header (metadata)
944
	rakt = rn;
945
	minLat = -90.0;
946
	minLon = -180.0;
947
	maxLat = 90.0;
948
	maxLon = 180.0;
949
 
950
	while (rakt)
951
	{
952
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
953
	   	rakt->run->type != data_D1010)
954
	   {
955
	      rakt = rakt->next;
956
	      continue;
957
	   }
958
 
959
	   i = rakt->run->first_lap_index;
960
	   // get the first lap
961
	   if ((lap = ds.getLap(i)) == NULL)
962
	      continue;
963
 
964
	   i = 0;
965
	   // iterate the points associated with the laps
966
	   while ((point = ds.getPoint(i)) != 0)
967
	   {
968
	      if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
969
	      {
970
		 i = point->time + 1;
971
		 continue;
972
	      }
973
 
974
	      if (SEMI2DEG(point->posn.lat) > minLat)
975
		 minLat = SEMI2DEG(point->posn.lat);
976
 
977
	      if (SEMI2DEG(point->posn.lat) < maxLat)
978
		 maxLat = SEMI2DEG(point->posn.lat);
979
 
980
	      if (SEMI2DEG(point->posn.lon) > minLon)
981
		 minLon = SEMI2DEG(point->posn.lon);
982
 
983
	      if (SEMI2DEG(point->posn.lon) < maxLon)
984
		 maxLon = SEMI2DEG(point->posn.lon);
985
 
986
	      i = point->time + 1;
987
	   }
988
 
989
	   rakt = rakt->next;
990
	}
991
 
992
	buffer.sprintf("      <bounds minlat=\"%f\" minlon=\"%f\" maxlat=\"%f\" maxlon=\"%f\" />\n",
993
		maxLat, minLon, minLat, maxLon);
994
	buffer.append("   </metadata>\n");
995
	buffer.append("   <trk>\n");
996
	buffer.append("      <trkseg>\n");
997
	writeTag (qf, buffer, indent);
998
	indent = 3;
999
	rn = ds.getRunNode();
1000
	lap = ds.getLap(rn->run->first_lap_index);
1001
	i = 0;
1002
 
1003
	while ((point = ds.getPoint(i)) != 0)
1004
	{
1005
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
1006
	   {
1007
	      i = point->time + 1;
1008
	      continue;
1009
	   }
1010
 
1011
	   buffer.sprintf("<trkpt lat=\"%f\" lon=\"%f\">\n",
1012
		SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
1013
	   writeTag(qf, buffer, indent);
1014
	   indent++;
1015
	   buffer.sprintf("<ele>%f</ele>\n", point->alt);
1016
	   writeTag(qf, buffer, indent);
1017
	   qt = garmin_dtime(point->time);
1018
	   buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
1019
	   writeTag(qf, buffer, indent);
1020
	   indent--;
1021
	   writeTag(qf, QString("</trkpt>\n"), indent);
1022
	   i = point->time + 1;
1023
	}
1024
 
1025
	indent = 0;
1026
	buffer = QString("      </trkseg>\n");
1027
	buffer.append("   </trk>\n");
1028
	buffer.append("</gpx>\n");
1029
	writeTag(qf, buffer, indent);
1030
	qf.close();
1031
	KMessageBox::information(this, i18n("File ") + fn + i18n(" was written successfully."));
1032
}
1033
 
1034
void sportwatcherWidget::saveOSM(const QString &fn)
1035
{
1036
QFile qf;
1037
QString buffer;
1038
RUN_NODE *rn, *rakt;
1039
LAP *lap;
1040
POINT *point;
1041
int indent, id, j;
1042
unsigned int i;
1043
double minLat, minLon, maxLat, maxLon;
1044
QDateTime *qt;
1045
 
1046
	indent = 0;
1047
	rn = ds.getRunNode();
1048
	lap = ds.getLap(rn->run->first_lap_index);
1049
 
1050
	if ((point = ds.getPoint(lap->start_time)) == 0)
1051
	{
1052
	   KMessageBox::error(this, i18n("No data to save!"));
1053
	   return;
1054
	}
1055
 
232 andreas 1056
	qf.setFileName(fn);
172 andreas 1057
 
232 andreas 1058
	if (!qf.open(QIODevice::ReadWrite | QIODevice::Truncate))
172 andreas 1059
	{
232 andreas 1060
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fn));
172 andreas 1061
	   return;
1062
	}
1063
 
1064
	buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
1065
	buffer.append("<osm version=\"0.5\" generator=\"TheoSys SportWatcher\">\n");
1066
	indent = 0;
1067
	writeTag (qf, buffer, indent);
1068
	// Find the edges of our coordinates
1069
	// We need this information in the header (metadata)
1070
	rakt = rn;
1071
	minLat = -90.0;
1072
	minLon = -180.0;
1073
	maxLat = 90.0;
1074
	maxLon = 180.0;
1075
 
1076
	while (rakt)
1077
	{
1078
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
1079
	   	rakt->run->type != data_D1010)
1080
	   {
1081
	      rakt = rakt->next;
1082
	      continue;
1083
	   }
1084
 
1085
	   i = rakt->run->first_lap_index;
1086
	   // get the first lap
1087
	   if ((lap = ds.getLap(i)) == NULL)
1088
	      continue;
1089
 
1090
	   i = 0;
1091
	   // iterate the points associated with the laps
1092
	   while ((point = ds.getPoint(i)) != 0)
1093
	   {
1094
	      if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
1095
	      {
1096
		 i = point->time + 1;
1097
		 continue;
1098
	      }
1099
 
1100
	      if (SEMI2DEG(point->posn.lat) > minLat)
1101
		 minLat = SEMI2DEG(point->posn.lat);
1102
 
1103
	      if (SEMI2DEG(point->posn.lat) < maxLat)
1104
		 maxLat = SEMI2DEG(point->posn.lat);
1105
 
1106
	      if (SEMI2DEG(point->posn.lon) > minLon)
1107
		 minLon = SEMI2DEG(point->posn.lon);
1108
 
1109
	      if (SEMI2DEG(point->posn.lon) < maxLon)
1110
		 maxLon = SEMI2DEG(point->posn.lon);
1111
 
1112
	      i = point->time + 1;
1113
	   }
1114
 
1115
	   rakt = rakt->next;
1116
	}
1117
 
1118
	buffer.sprintf("   <bound box='%f,%f,%f,%f' origin='http://www.openstreetmap.org/api/0.5' />\n",
1119
		maxLat, minLon, minLat, maxLon);
1120
	writeTag (qf, buffer, indent);
1121
	indent = 1;
1122
	rn = ds.getRunNode();
1123
	lap = ds.getLap(rn->run->first_lap_index);
1124
	i = 0;
1125
	id = -1;
1126
 
1127
	while ((point = ds.getPoint(i)) != 0)
1128
	{
1129
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
1130
	   {
1131
	      i = point->time + 1;
1132
	      continue;
1133
	   }
1134
 
1135
	   buffer.sprintf("<node id='%d' action='modify' visible='true' lat=\"%f\" lon=\"%f\">\n",
1136
		id, SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
1137
	   writeTag(qf, buffer, indent);
1138
	   indent++;
1139
	   buffer = QString("<tag k='created_by' v='TheoSys Sportwatcher' />\n");
1140
	   writeTag(qf, buffer, indent);
1141
	   buffer = QString("<tag k='highway' v='tertiary' />\n");
1142
	   writeTag(qf, buffer, indent);
1143
	   indent--;
1144
	   writeTag(qf, QString("</node>\n"), indent);
1145
	   id--;
1146
	   i = point->time + 1;
1147
	}
1148
 
1149
	qt = garmin_dtime(lap->start_time);
1150
	buffer.sprintf("<way id='%d' action='modify' visible='true' timestamp='%s'>\n",
232 andreas 1151
		id, QString(qt->toString("yyyy-MM-ddThh:mm:ssZ")).toAscii().data());
172 andreas 1152
	writeTag(qf, buffer, indent);
1153
	indent++;
1154
 
1155
	for (j = -1; j > id; j--)
1156
	{
1157
	   buffer.sprintf("<nd ref='%d' />\n", j);
1158
	   writeTag(qf, buffer, indent);
1159
	}
1160
 
1161
	indent--;
1162
	writeTag(qf, QString("</way>\n"), indent);
1163
	indent = 0;
1164
	writeTag(qf, QString("</osm>\n"), indent);
1165
	qf.close();
232 andreas 1166
	KMessageBox::information(this, i18n("File %1 was written successfully.").arg(fn));
172 andreas 1167
}
1168
 
88 andreas 1169
void sportwatcherWidget::fileOpen()
1170
{
1171
QString fname = KFileDialog::getOpenFileName(Data, QString("*.gmn"), this, QString("SportWatcher"));
137 andreas 1172
int m;
88 andreas 1173
 
1174
        if (fname.isEmpty())
1175
           return;
1176
 
1177
	spw.destroy();
1178
 
232 andreas 1179
        if (spw.setFileName(fname.toAscii().data()) == -1)
88 andreas 1180
	   return;
1181
 
1182
	if (gmn)
1183
	   garmin_free_data (gmn);
1184
 
1185
	gmn = spw.readFile();
104 andreas 1186
	zfactor = 0;
137 andreas 1187
 
1188
	if ((m = garmin_count_error()) > 0)
1189
	{
1190
	int i, key = -1;
1191
 
1192
	   for (i = 0; i < m; i++)
1193
	      KMessageBox::error(this, QString(garmin_get_next_error(&key)));
1194
 
1195
	   garmin_clear_errors();
1196
	   return;
1197
	}
1198
 
246 andreas 1199
	DIRTY = true;
250 andreas 1200
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
88 andreas 1201
	showLaps();
100 andreas 1202
	showTrack();
88 andreas 1203
	showCurves();
250 andreas 1204
 
1205
	if (curTab == 2)
1206
	{
1207
	   showThreeCurve();
1208
	   tabDirt2 = false;
1209
	}
1210
 
1211
	tabDirt0 = tabDirt3 = false;
246 andreas 1212
	DIRTY = false;
88 andreas 1213
}
1214
 
217 andreas 1215
void sportwatcherWidget::fileImport()
1216
{
1217
QString fname = KFileDialog::getOpenFileName(QString("~/"), QString("*.tcx"), this, QString("SportWatcher"));
1218
gmn_import import;
1219
int m;
225 andreas 1220
QString tgfile, fld, px;
232 andreas 1221
QPixmap qpx;
225 andreas 1222
QFileInfo datei;
246 andreas 1223
QList<QTreeWidgetItem *>item;
1224
QTreeWidgetItem *el, *it;
225 andreas 1225
LAP *lap;
1226
RUN_NODE *rn;
217 andreas 1227
 
1228
        if (fname.isEmpty())
1229
           return;
1230
 
1231
        import.setFile(fname);
1232
 
1233
	if ((m = import.import()) != 0)
1234
	{
1235
	   KMessageBox::error(this, QString(import.getError(m)));
1236
	   return;
1237
	}
1238
 
1239
	if (gmn)
246 andreas 1240
	{
217 andreas 1241
	   garmin_free_data (gmn);
246 andreas 1242
	   gmn = 0;
1243
	}
217 andreas 1244
 
246 andreas 1245
	if (!(gmn = import.getGarminData ()))
1246
	   return;
217 andreas 1247
 
246 andreas 1248
	DIRTY = true;
250 andreas 1249
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
217 andreas 1250
	showLaps();
1251
	showTrack();
1252
	showCurves();
250 andreas 1253
 
1254
	if (curTab == 2)
1255
	{
1256
	   showThreeCurve();
1257
	   tabDirt2 = false;
1258
	}
1259
 
1260
	tabDirt0 = tabDirt3 = false;
246 andreas 1261
	DIRTY = false;
225 andreas 1262
 
1263
	// Find the filename;
1264
	// It consists of the date and the time.
1265
	// We need this information to set the correct path to store the file.
1266
	tgfile = Data;		// The base path
1267
	rn = ds.getRunNode();
1268
	lap = ds.getLap(rn->run->first_lap_index);
1269
	QDateTime *qt = garmin_dtime (lap->start_time);
1270
	tgfile.append (qt->toString ("/yyyy")); // year is a folder
1271
	tgfile.append (qt->toString ("/MM"));	// month is a folder
1272
	tgfile.append (qt->toString ("/yyyyMMddThhmmss"));	// The file name
1273
	tgfile.append (".gmn");		// Extension of file name
1274
	datei.setFile (tgfile);
1275
	// save the data to a real file, but only if it doesn't exist allready.
232 andreas 1276
	garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 0);
225 andreas 1277
 
1278
	// in case the item is already in the list on the left side, we
1279
	// only highlight the item.
246 andreas 1280
	item = ui_sportwatcherWidgetBase.liActivities->findItems (tgfile, Qt::MatchExactly);
1281
 
1282
	if (item.size() > 0)
225 andreas 1283
	{
246 andreas 1284
	   ui_sportwatcherWidgetBase.liActivities->setItemSelected (item.at(0), true);
1285
	   ui_sportwatcherWidgetBase.liActivities->setCurrentItem (item.at(0));
225 andreas 1286
	   return;
1287
	}
1288
 
1289
	// insert everything into the list on the left side
1290
	switch (rn->run->sport_type)
1291
	{
1292
	   case D1000_running:
1293
	      fld = i18n("Running");
247 andreas 1294
	      px = QString("spw-running");
225 andreas 1295
	   break;
1296
 
1297
	   case D1000_biking:
1298
	      fld = i18n("Biking");
245 andreas 1299
	      px = QString("bike");
225 andreas 1300
	   break;
1301
 
1302
	   default:
1303
	      fld = i18n("Others");
245 andreas 1304
	      px = QString("other");
225 andreas 1305
	}
1306
 
1307
	// Do we have allready so items in the list?
246 andreas 1308
	item = ui_sportwatcherWidgetBase.liActivities->findItems (fld, Qt::MatchExactly);
1309
 
1310
	if (item.size() > 0)
225 andreas 1311
	{
246 andreas 1312
	   el = new QTreeWidgetItem(item.at(0));
1313
	   el->setText(0, kl->formatDateTime(*qt, KLocale::ShortDate, true));
1314
	   el->setData(0, Qt::UserRole, tgfile);
1315
	   el->setIcon(0, KIcon(px));
225 andreas 1316
	}
1317
	else	// no, this is the first item. (shouldn't be!)
1318
	{
246 andreas 1319
	   it = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
1320
	   it->setText(0, fld);
1321
	   it->setIcon(0, KIcon(QString("history")));
1322
 
1323
	   el = new QTreeWidgetItem(item.at(0));
1324
	   el->setText(0, kl->formatDateTime(*qt, KLocale::ShortDate, true));
1325
	   el->setData(0, Qt::UserRole, tgfile);
1326
	   el->setIcon(0, KIcon(px));
225 andreas 1327
	}
217 andreas 1328
}
1329
 
218 andreas 1330
/*
1331
 * Display a small dialog to rename the currently loaded session.
1332
 */
1333
void sportwatcherWidget::editRename()
1334
{
1335
bool ok;
1336
QString name, inhalt;
246 andreas 1337
QList<QTreeWidgetItem *> item;
1338
QTreeWidgetItem *lvItem;
218 andreas 1339
QFileInfo datei;
1340
RUN_NODE *rn;
1341
LAP *lap;
1342
garmin_list *list;
1343
D1009 *n;
1344
 
1345
	if (!gmn)
1346
	{
1347
	   KMessageBox::error(this, i18n("There is no session selected!"));
1348
	   return;
1349
	}
1350
 
1351
	rn = ds.getRunNode();
246 andreas 1352
	item = ui_sportwatcherWidgetBase.liActivities->selectedItems ();
218 andreas 1353
	lvItem = item.first();
1354
 
1355
	if (!isdigit(rn->run->workout.name[0]))
1356
	   inhalt = lvItem->text(0);
1357
	else
246 andreas 1358
	   inhalt.clear();
218 andreas 1359
 
1360
	name = KInputDialog::getText(i18n("Rename session"), i18n("Session name"),
246 andreas 1361
		inhalt, &ok, this, (QValidator *)0, QString("Nxxxxxxxxxxxxxx"),
1362
		i18n("Enter a new name for the currently selected activity."));
218 andreas 1363
 
1364
	if (!ok)
1365
	   return;
1366
 
1367
	if (name.length() <= 1)
1368
	{
1369
	   lap = ds.getLap(rn->run->first_lap_index);
1370
	   const QDateTime *qt = garmin_dtime (lap->start_time);
232 andreas 1371
	   QString idx = kl->formatDateTime(*qt, KLocale::ShortDate, true);
218 andreas 1372
	   lvItem->setText (0, idx);
246 andreas 1373
	   datei.setFile (lvItem->data(0, Qt::UserRole).toString());
232 andreas 1374
	   garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 1);
218 andreas 1375
	   delete qt;
1376
	   return;
1377
	}
1378
 
232 andreas 1379
	strncpy (rn->run->workout.name, name.toAscii().data(), 16);
218 andreas 1380
 
1381
	if (gmn->type != data_Dlist)
1382
	{
1383
	   KMessageBox::error(this, i18n("editRename: Unexpected structure type %1 found!").arg(gmn->type));
1384
	   return;
1385
	}
1386
 
1387
	list = (garmin_list *)gmn->data;
1388
 
1389
	if (list->head->data->type != data_D1009)	// This should be the run node
1390
	{
1391
	   KMessageBox::error(this, i18n("editRename: The run node was not found!"));
1392
	   return;
1393
	}
1394
 
1395
	n = (D1009 *)list->head->data->data;
246 andreas 1396
	strncpy (n->workout.name, rn->run->workout.name, 15);
218 andreas 1397
	lvItem->setText (0, name);
246 andreas 1398
	datei.setFile (lvItem->data(0, Qt::UserRole).toString());
232 andreas 1399
	garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 1);
218 andreas 1400
}
1401
 
88 andreas 1402
void sportwatcherWidget::fileNew()
1403
{
232 andreas 1404
progressWidget *dlg = new progressWidget(this);
96 andreas 1405
 
100 andreas 1406
	dlg->show();
137 andreas 1407
 
1408
	if (!dlg->Download())
1409
	{
1410
	int m, key;
1411
 
1412
	   key = -1;
1413
 
1414
	   for (m = 0; m < garmin_count_error(); m++)
1415
	      KMessageBox::error(this, QString(garmin_get_next_error(&key)));
1416
	}
1417
	else
1418
	   getActivities();
1419
 
1420
	garmin_clear_errors();
96 andreas 1421
	delete dlg;
88 andreas 1422
}
1423
 
1424
/*
1425
 * This function is called, when the user clicks at the menu point
1426
 * "Save Heart Rate".
1427
 * First, a file dialog box is displayed, where the user can choose a
1428
 * directory and a file name to save the heart rate.
1429
 * If the file could successfully be created, the heart rate is saved
137 andreas 1430
 * in the "HRM"-format. This is the native format Polar uses to store
88 andreas 1431
 * heart rate data. I've choosen this format, because it's popular and
1432
 * used by many other software too.
1433
 */
1434
void sportwatcherWidget::extrasSaveHR()
1435
{
1436
QString fname, str1, str2;
1437
QFile fdfile;
1438
QDateTime *qt, *oldqt;
1439
QDate dat;
1440
QTime t;
1441
QDir dir = QDir::home();
1442
char hv0[256];
1443
RUN_NODE *rn;
1444
LAP *lap, *alap;
1445
POINT *point;
1446
int samples, smp, seconds, anz, nsec, samsec;
1447
int avgHeart, minHeart, maxHeart, aktHeart;
1448
int secRange1, secRange2, secRange3, secAbove, secBeyond;
1449
 
1450
	if (!gmn)
1451
	{
1452
	   KMessageBox::information(this, i18n("There is no activity open"));
1453
	   return;
1454
	}
1455
 
1456
	if (HRM.isEmpty())
1457
	   str1 = dir.path();
1458
	else
1459
	   str1 = HRM;
1460
 
1461
	str1 +=  "/" + StartTime.toString("yyyyMMddThhmmss.zzz.hrm");
1462
	fname = KFileDialog::getSaveFileName(str1, QString("*.hrm"), this, QString("SportWatcher"));
1463
 
1464
	if (fname.isEmpty())
1465
	   return;
1466
 
232 andreas 1467
	fdfile.setFileName(fname);
88 andreas 1468
 
1469
	if (fdfile.exists())
1470
	{
1471
	   if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
1472
	      return;
1473
	}
1474
 
232 andreas 1475
	if (!fdfile.open(QIODevice::ReadWrite | QIODevice::Truncate))
88 andreas 1476
	{
1477
	   KMessageBox::error(this, i18n("Error creating a file!\nPlease check permissions."));
1478
	   return;
1479
	}
1480
 
1481
	rn = ds.getRunNode();
1482
	lap = ds.getLap(rn->run->first_lap_index);
1483
	t = StartTime.time();
1484
	dat = StartTime.date();
1485
 
1486
	if ((point = ds.getPoint(lap->start_time)) == 0)
1487
	{
1488
	   fdfile.close();
1489
	   return;
1490
	}
1491
 
1492
	strcpy (hv0, "[Params]\n");
1493
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1494
	str1 = dat.toString("yyyyMMdd");
1495
	str2 = t.toString("hh:mm:ss.z");
1496
	sprintf(hv0, "Version=106\nMonitor=11\nSMode=000000000\nDate=%s\nStartTime=%s\n",
232 andreas 1497
		str1.toAscii().data(), str2.toAscii().data());
88 andreas 1498
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1499
	t.setHMS(0, 0, 0);
1500
	t = t.addSecs(max_time);
1501
	str2 = t.toString("hh:mm:ss.z");
1502
 
1503
	switch (sampleTime)
1504
	{
1505
	   case 0: samsec = 5; break;
1506
	   case 1: samsec = 15; break;
1507
	   case 2: samsec = 30; break;
1508
	   case 3: samsec = 60; break;
1509
	   default:
1510
	      samsec = 15;
1511
	}
1512
 
1513
	sprintf(hv0, "Length=%s\nInterval=%d\nUpper1=%d\nLower1=%d\nUpper2=%d\n",
232 andreas 1514
	   str2.toAscii().data(), samsec, upper1, lower1, upper2);
88 andreas 1515
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1516
	sprintf(hv0, "Lower2=%d\nUpper3=%d\nLower3=%d\nTimer1=00:00:00.0\n",
1517
		lower2, upper3, lower3);
1518
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1519
	strcpy(hv0, "Timer2=00:00:00.0\nTimer3=00:00:00.0\nActiveLimit=0\n");
1520
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1521
	sprintf(hv0, "MaxHR=%d\nRestHR=%d\nStartDelay=0\nVO2max=%d\nWeight=%d\n\n",
1522
		MaxHr, restHr, vo2max, weight);
1523
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1524
 
1525
	// Write the intervall times. One block for every lap
1526
	secRange1 = secRange2 = secRange3 = secAbove = secBeyond = 0;
1527
	strcpy(hv0, "[IntTimes]\n");
1528
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1529
	t.setHMS(0, 0, 0);
1530
 
1531
	for (unsigned int i = rn->run->first_lap_index; i < rn->run->last_lap_index; i++)
1532
	{
1533
	   alap = ds.getLap(i);
1534
	   point = ds.getPoint(alap->start_time);
1535
	   oldqt = garmin_dtime(point->time);
1536
	   avgHeart = minHeart = maxHeart = aktHeart = 0;
1537
	   anz = 0;
1538
	   unsigned long lastTime = point->time;
1539
	   int totSec = 0;
1540
 
1541
	   while (point)
1542
	   {
1543
	      if (point->time > (alap->start_time + (alap->total_time / 100)))
1544
		 break;
1545
 
1546
	      if (point->heart_rate > 0)
1547
	      {
1548
		 avgHeart += point->heart_rate;
1549
		 nsec = point->time - lastTime;
1550
		 totSec += nsec;
1551
 
1552
		 if (minHeart == 0 || minHeart > point->heart_rate)
1553
		    minHeart = point->heart_rate;
1554
 
1555
		 if (maxHeart < point->heart_rate)
1556
		    maxHeart = point->heart_rate;
1557
 
1558
		 if (aktHeart == 0 && totSec >= samsec)
1559
		    aktHeart = avgHeart / (anz + 1);
1560
 
1561
		 if (point->heart_rate < lower1)
1562
		    secBeyond += nsec;
1563
		 else if (point->heart_rate < lower2)
1564
		    secRange1 += nsec;
1565
		 else if (point->heart_rate < lower3)
1566
		    secRange2 += nsec;
1567
		 else if (point->heart_rate < upper3)
1568
		    secRange3 += nsec;
1569
		 else
1570
		    secAbove += nsec;
1571
 
1572
		 lastTime = point->time;
1573
		 anz++;
1574
	      }
1575
 
1576
	      point = ds.getPoint(point->time+1);
1577
	   }
1578
 
1579
	   t = t.addSecs(alap->total_time / 100);
1580
	   str1 = t.toString("hh:mm:ss.z");
166 andreas 1581
 
1582
	   if (anz > 0)
1583
	      avgHeart = avgHeart / anz;
1584
	   else
1585
	      avgHeart = 0;
1586
 
88 andreas 1587
	   sprintf(hv0, "%s\t %d\t %d\t %d\t %d\n",
232 andreas 1588
	      str1.toAscii().data(), aktHeart, minHeart, avgHeart, maxHeart);
88 andreas 1589
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1590
	   strcpy(hv0, "32\t 0\t 0\t 0\t 0\t 0\n");
1591
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1592
	   strcpy(hv0, "0\t 0\t 0\t 0\t 0\n");
1593
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1594
	   sprintf(hv0, "0\t %d\t 0\t 0\t 0\t 0\n", (int)alap->total_distance);
1595
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1596
	   strcpy(hv0, "0\t 0\t 0\t 0\t 0\t 0\n");
1597
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1598
	}
1599
 
1600
	strcpy(hv0, "\n[IntNotes]\n\n[ExtraData]\n\n");
1601
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1602
 
1603
	strcpy(hv0, "[Summary-123]\n");
1604
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 1
1605
	smp = max_time - secBeyond - secRange1 - secRange2 - secRange3 - secAbove;
245 andreas 1606
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
88 andreas 1607
		max_time, secRange1, secRange2 + secRange3,
1608
		secAbove + secBeyond, smp);
1609
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 1
1610
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
1611
		MaxHr, upper1, lower1, restHr);
1612
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 1
245 andreas 1613
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
88 andreas 1614
		max_time, secRange2, secRange1 + secRange3,
1615
		secAbove + secBeyond, smp);
1616
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 2
1617
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
1618
		MaxHr, upper2, lower2, restHr);
1619
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 2
245 andreas 1620
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
88 andreas 1621
		max_time, secRange3, secRange1 + secRange2,
1622
		secAbove + secBeyond, smp);
1623
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 3
1624
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
1625
		MaxHr, upper3, lower3, restHr);
1626
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 3
1627
	samples = max_time / samsec;
1628
	sprintf(hv0, "0\t %u\n\n", samples);	// samples
1629
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1630
 
1631
	strcpy(hv0, "[Summary-TH]\n");
1632
	write(fdfile.handle(), &hv0[0], strlen(hv0));
245 andreas 1633
	sprintf(hv0, "%lu\t 0\t %lu\t %d\t %d\t 0\n", max_time, max_time - max_hr - restHr, max_hr, restHr);
88 andreas 1634
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1635
	sprintf(hv0, "%d\t %d\t %d\t %d\n", MaxHr, upper3, lower1, restHr);
1636
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1637
	sprintf(hv0, "0\t %u\n\n", samples);	// samples
1638
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1639
 
1640
	sprintf(hv0, "[HRZones]\n%d\n%d\n%d\n%d\n%d\n%d\n0\n0\n0\n0\n0\n\n",
1641
		MaxHr, upper3, upper2, upper1, lower1, restHr);
1642
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1643
 
1644
	strcpy(hv0, "[HRData]\n");
1645
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1646
 
1647
	smp = 0;		// average heart rate of 15 seconds
1648
	seconds = 0;
1649
	anz = 0;
1650
	nsec = samsec;
1651
	oldqt = garmin_dtime(lap->start_time);
1652
	qt = 0;
1653
	point = ds.getPoint(lap->start_time);
1654
 
1655
	while (point)
1656
	{
1657
	   if (seconds >= nsec)
1658
	   {
1659
	      if (anz > 0)
1660
	      {
1661
		 sprintf(hv0, "%d\n", smp / anz);
1662
		 write(fdfile.handle(), &hv0[0], strlen(hv0));
1663
	      }
1664
 
1665
	      if (smp > 0 && seconds >= (nsec + samsec))
1666
	      {
1667
		 if (anz <= 0)
1668
		    anz = 0;
1669
 
1670
		 for (int x = nsec; x < seconds; x += samsec)
1671
		 {
1672
		    sprintf(hv0, "%d\n", smp / anz);
1673
		    write(fdfile.handle(), &hv0[0], strlen(hv0));
1674
		    nsec += samsec;
1675
		 }
1676
	      }
1677
 
1678
	      anz = 0;
1679
	      smp = 0;
1680
	      nsec += samsec;
1681
	   }
1682
 
1683
	   qt = garmin_dtime (point->time);
1684
	   seconds += oldqt->secsTo(*qt);
1685
 
1686
	   if (point->heart_rate > 0)
1687
	   {
1688
	      smp += point->heart_rate;
1689
	      anz++;
1690
	   }
1691
 
1692
	   delete oldqt;
1693
	   oldqt = qt;
1694
	   point = ds.getPoint(point->time + 1);
1695
	}
1696
 
1697
	fdfile.close();
1698
	KMessageBox::information(this, i18n("File successfully written."));
1699
}
1700
 
1701
void sportwatcherWidget::extrasSettings()
1702
{
232 andreas 1703
settingsWidget *dlg = new settingsWidget(this);
88 andreas 1704
 
1705
	if (dlg->exec() == QDialog::Accepted)
1706
	{
232 andreas 1707
	   KConfig cfg(QString("sportwatcher.rc"), KConfig::SimpleConfig);
1708
	   KConfigGroup ic (&cfg, "SportWatcher");
1709
	   lower1 = ic.readEntry("lower1", 0);
1710
	   lower2 = ic.readEntry("lower2", 0);
1711
	   lower3 = ic.readEntry("lower3", 0);
1712
	   upper1 = ic.readEntry("upper1", 0);
1713
	   upper2 = ic.readEntry("upper2", 0);
1714
	   upper3 = ic.readEntry("upper3", 0);
1715
	   MaxHr = ic.readEntry("maxHr", 0);
1716
	   restHr = ic.readEntry("restHr", 0);
1717
	   vo2max = ic.readEntry("vo2max", 0);
1718
	   weight = ic.readEntry("weight", 0);
1719
	   sampleTime = ic.readEntry("seconds", 1);
1720
	   Serial = ic.readEntry("Serial", false);
1721
	   Contour = ic.readEntry("Contour", false);
1722
	   Device = ic.readEntry("Device", QString("/dev/ttyUSB0"));
1723
	   Forerunner = ic.readEntry("Forerunner", false);
1724
	   Data = ic.readEntry("Data", QString("/"));
1725
	   HRM = ic.readEntry("HRM", QString("/"));
1726
	   MAP = ic.readEntry("MAP", QString("/"));
1727
	   Units = ic.readEntry("Units", 0);
1728
	   MapType = ic.readEntry("MapType", 0);
88 andreas 1729
	}
1730
 
1731
	delete dlg;
152 andreas 1732
}
151 andreas 1733
 
152 andreas 1734
void sportwatcherWidget::extrasWMSSettings()
1735
{
232 andreas 1736
#if defined HAVE_GDAL
158 andreas 1737
	if (MapType == MPT_BMP || MapType == MPT_GIF || MapType == MPT_PNG ||
1738
	    MapType == MPT_TIF)
1739
	{
232 andreas 1740
	   coordinatesWidget *idlg = new coordinatesWidget(this);
158 andreas 1741
	   idlg->exec();
1742
	   delete idlg;
1743
	   return;
1744
	}
274 andreas 1745
 
1746
	if (MapType == MPT_WMS)
158 andreas 1747
	{
274 andreas 1748
	   wmsbase *dlg = new wmsbase(this);
1749
	   dlg->exec();
1750
	   delete dlg;
1751
	}
1752
 
276 andreas 1753
	if (MapType == MPT_SHP || MapType == MPT_OSM)
274 andreas 1754
	{
1755
	   shapeWidget *dlg = new shapeWidget(this);
276 andreas 1756
 
1757
	   if (MapType == MPT_SHP)
1758
	      dlg->setMapType(shapeWidget::MAP_SHAPE);
1759
	   else
1760
	      dlg->setMapType(shapeWidget::MAP_OSM);
1761
 
274 andreas 1762
	   dlg->exec();
1763
	   delete dlg;
1764
	}
1765
 
276 andreas 1766
	if (MapType != MPT_WMS && MapType != MPT_SHP && MapType != MPT_OSM)
274 andreas 1767
	{
158 andreas 1768
	   KMessageBox::detailedSorry(this,
274 andreas 1769
	      i18n("You have not choosen a WMS tag file or shape file directory!"),
1770
	      i18n("This dialog is especialy to set map specific parameters. ") +
158 andreas 1771
	      i18n("Therefore this dialog is temporary disabled. It will be ") +
274 andreas 1772
	      i18n("available again, as soon as you choose \"WMS server\" or ") +
1773
	      i18n("Shape file as your map type."));
158 andreas 1774
	      return;
1775
	}
156 andreas 1776
#else
1777
	KMessageBox::detailedSorry(this,
1778
	   i18n("This function was disabled at compile time because of missing GDAL v1.5.x!"),
1779
	   i18n("Sportwatcher needs GDAL v1.5.x to enable this function.\n") +
1780
	   i18n("If you like this to be working, install GDAL version 1.5.x and recompile the source!"));
1781
#endif
88 andreas 1782
}
1783
 
1784
/*
1785
 * Functions to fill in the boxes of the main mask.
1786
 */
1787
void sportwatcherWidget::showLaps()
1788
{
1789
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
1790
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
248 andreas 1791
QString qs_totdist;
88 andreas 1792
QDateTime dt;
1793
QTime t, st;
249 andreas 1794
QDateTime *qt;
1795
LAP *lap;
1796
POINT *point;
88 andreas 1797
RUN_NODE *rakt, *rn;
149 andreas 1798
int laps, i, anz, men, cad;
249 andreas 1799
double alt_asc, alt_dsc, sum_asc, sum_dsc, old_asc, old_dsc;
248 andreas 1800
double totdist;
216 andreas 1801
bool pause;
88 andreas 1802
 
246 andreas 1803
	if (!DIRTY)
1804
	   return;
1805
 
88 andreas 1806
	if (!gmn)
1807
	{
248 andreas 1808
//	   KMessageBox::error(this, i18n("No data were loaded!"));
88 andreas 1809
	   return;
1810
	}
1811
 
1812
	if (gmn->type == data_Dnil)
1813
	{
1814
	   KMessageBox::error(0, i18n("No data found!"));
1815
	   return;
1816
	}
1817
 
1818
	if (gmn->type != data_Dlist)     /* List of data */
1819
	{
221 andreas 1820
	   KMessageBox::error(0, i18n("Found unexpected data type %1!").arg(gmn->type));
88 andreas 1821
	   return;
1822
	}
1823
 
1824
	ds.destroy();
221 andreas 1825
	min_hr = max_hr = avg_hr = 0;
88 andreas 1826
	min_height = max_height = 0.0;
218 andreas 1827
	min_speed = max_speed = 0.0;
248 andreas 1828
	totdist = 0.0;
1829
	// Tab Summary
245 andreas 1830
	ui_sportwatcherWidgetBase.liLaps->clear();
1831
	ui_sportwatcherWidgetBase.liLaps->setAllColumnsShowFocus(true);
248 andreas 1832
	// Tab Laps
1833
	ui_sportwatcherWidgetBase.twLaps->clear();
88 andreas 1834
	ds.garmin_print_data(gmn);
245 andreas 1835
 
1836
	if (!(rn = ds.getRunNode()))
1837
	   return;
1838
 
88 andreas 1839
	rakt = rn;
248 andreas 1840
	// Tab Summary
245 andreas 1841
	ui_sportwatcherWidgetBase.liLaps->setRootIsDecorated(true);
88 andreas 1842
 
248 andreas 1843
	for (i = 1; i < 12; i++)
1844
	   ui_sportwatcherWidgetBase.liLaps->setColumnAlignment(i, Qt::AlignRight);
1845
	// Tab Laps
1846
	ui_sportwatcherWidgetBase.edTotalDistance->clear();
1847
	ui_sportwatcherWidgetBase.edTotalTime->clear();
1848
	ui_sportwatcherWidgetBase.edAvgSpeed->clear();
1849
	ui_sportwatcherWidgetBase.edTotalHeight->clear();
1850
	ui_sportwatcherWidgetBase.edAvgHR->clear();
1851
	ui_sportwatcherWidgetBase.edLapNumber->clear();
1852
 
88 andreas 1853
	qs_name = qs_distance = qs_etime = qs_avgpace = qs_avgspeed = qs_maxspeed = QString("");
1854
	qs_calories = qs_avghr = qs_maxhr = qs_avgcadence = qs_ascent = qs_descent = QString("");
1855
	men = 0;
149 andreas 1856
	cad = 0;
88 andreas 1857
 
249 andreas 1858
	// The main loop.
1859
	// If a supported run type is detected, it will be processed.
88 andreas 1860
	while (rakt)
1861
	{
249 andreas 1862
	   // Check for a supported run type
88 andreas 1863
	   if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
1864
	   	rakt->run->type == data_D1010)
1865
	   {
249 andreas 1866
	   int cal, ahr, mhr;
88 andreas 1867
	   double distance, speed, mspeed;
1868
	   QDate dat;
1869
 
249 andreas 1870
	      // Set the name depending on the sport type
1871
	      // This is used on the tab "Summary"
88 andreas 1872
	      switch (rakt->run->sport_type)
1873
	      {
1874
		 case D1000_running: qs_name = QString("Running: "); break;
1875
		 case D1000_biking:  qs_name = QString("Biking: "); break;
1876
		 case D1000_other:   qs_name = QString("Other: "); break;
1877
		 default:
1878
		    qs_name = QString("Unknown: ");
1879
	      }
1880
 
245 andreas 1881
	      if (!(lap = ds.getLap(rakt->run->first_lap_index)))
1882
		 return;
1883
 
88 andreas 1884
	      qt = garmin_dtime (lap->start_time);
1885
	      StartTime = *qt;
1886
	      st = qt->time();
1887
	      dat = qt->date();
1888
	      delete qt;
213 andreas 1889
	      qt = 0;
1890
	      // Find the last track;
1891
	      //    It is possible to delete laps directly on the watch,
1892
	      //    so we can't be sure the last lap is really the last one.
1893
	      //    Tracks are not deleted and the last track contains the
1894
	      //    summuraries we need.
249 andreas 1895
	      if (!(point = ds.getLastPoint()))
213 andreas 1896
	      {
249 andreas 1897
		 KMessageBox::error(this, i18n("Error getting the last messure point!"));
245 andreas 1898
		 return;
215 andreas 1899
	      }
1900
 
249 andreas 1901
	      qt = garmin_dtime(point->time);
1902
	      t = qt->time();
88 andreas 1903
	      t.setHMS(0, 0, 0);
249 andreas 1904
	      t = t.addSecs(ds.getTotalTime());
88 andreas 1905
	      qt->setDate(dat);
1906
	      qt->setTime(t);
232 andreas 1907
	      qs_name.append(kl->formatDate(dat, KLocale::ShortDate));
88 andreas 1908
	      qs_name.append(" ");
230 andreas 1909
	      qs_name.append(kl->formatTime(st, true));
249 andreas 1910
	      max_time = ds.getTotalTime();
88 andreas 1911
	      qs_etime = QString(qt->toString("hh:mm:ss.zzz"));
1912
 
1913
	      distance = 0.0;
1914
	      cal = 0;
1915
	      mspeed = 0;
1916
	      ahr = mhr = 0;
1917
	      anz = 0;
149 andreas 1918
	      cad = 0;
1919
	      sum_asc = sum_dsc = old_asc = old_dsc = 0;
88 andreas 1920
 
249 andreas 1921
	      // Find out the total distance, calories, maximum speed,
1922
	      // maximum heart rate and the average heart rate. Get this
1923
	      // values from the lap summary the watch made.
88 andreas 1924
	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
1925
	      {
1926
		 if ((lap = ds.getLap(i)) == NULL)
1927
		    continue;
1928
 
1929
		 distance += lap->total_distance;
1930
		 cal += lap->calories;
149 andreas 1931
 
1932
		 if (lap->avg_cadence != 0xff)
1933
		    cad += lap->avg_cadence;
1934
 
88 andreas 1935
		 ahr += lap->avg_heart_rate;
1936
		 anz++;
1937
 
1938
		 if (lap->max_speed > mspeed)
1939
		    mspeed = lap->max_speed;
1940
 
1941
		 if (lap->max_heart_rate > mhr)
1942
		    mhr = lap->max_heart_rate;
1943
	      }
1944
 
249 andreas 1945
	      total_distance = distance = ds.getTotalDistance();
213 andreas 1946
 
149 andreas 1947
	      if (Units == 1)		// Statute?
1948
		 qs_distance.sprintf("%.2f ft", distance / 0.304);
1949
	      else
1950
	         qs_distance.sprintf("%.2f m", distance);
1951
 
88 andreas 1952
	      if (distance > 0)
1953
	      {
1954
		 QTime tt = qt->time();
156 andreas 1955
		 long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0;
1956
 
1957
		 if (Units == 0)
1958
		    secs = secs * (1000.0 / distance * 100.0);
1959
		 else
1960
		    secs = secs * (1609.344 / distance * 100.0);
1961
 
88 andreas 1962
		 int h = secs / 3600;
1963
		 int m = (secs - (h * 3600)) / 60;
1964
		 int s = secs - ((h * 3600) + (m * 60));
1965
		 t = QTime(h, m, s, 0);
230 andreas 1966
		 qs_avgpace = QString("  ") + kl->formatTime(t, true);
156 andreas 1967
 
1968
		 if (Units == 0)
1969
		    qs_avgpace.append(QString(" /km"));
1970
		 else
1971
		    qs_avgpace.append(QString(" /mi"));
88 andreas 1972
	      }
1973
 
149 andreas 1974
	      if (Units == 1)		// Statute?
249 andreas 1975
		 speed = distance / ds.getTotalTime() * 3.6 / 1.609344;
149 andreas 1976
	      else
249 andreas 1977
		 speed = distance / ds.getTotalTime() * 3.6;
149 andreas 1978
 
156 andreas 1979
	      qs_avgspeed.sprintf("%.2f %s", speed, (Units == 1) ? "mph" : "km/h");
1980
	      qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? mspeed * 3.6 / 1.609344 : mspeed * 3.6, (Units == 1) ? "mph" : "km/h");
88 andreas 1981
	      qs_calories.sprintf("%d", cal);
1982
	      qs_avghr.sprintf("%d bpm", ahr / anz);
1983
	      qs_maxhr.sprintf("%d bpm", mhr);
1984
 
149 andreas 1985
	      if (cad > 0)
1986
		 qs_avgcadence.sprintf("%d", cad / anz);
1987
 
248 andreas 1988
	      // Add the summary line columns to the tab "Summary"
1989
	      K3ListViewItem *element = new K3ListViewItem(ui_sportwatcherWidgetBase.liLaps,
1990
	        qs_name, kl->formatNumber(qs_distance, false),
1991
		qs_etime, qs_avgpace, kl->formatNumber(qs_avgspeed, false),
1992
		kl->formatNumber(qs_maxspeed, false), qs_calories, qs_avghr);
88 andreas 1993
	      element->setText(8, qs_maxhr);
1994
	      element->setText(9, qs_avgcadence);
248 andreas 1995
	      element->setText(10, kl->formatNumber(qs_ascent, false));
1996
	      element->setText(11, kl->formatNumber(qs_descent, false));
88 andreas 1997
	      element->sortChildItems(0, false);
1998
	      element->setOpen(true);
245 andreas 1999
	      element->setPixmap(0, KIcon(QString("activity")).pixmap(16));
2000
	      ui_sportwatcherWidgetBase.liLaps->insertItem(element);
248 andreas 2001
	      // Add some of the summaries to the fields on the tab "Lap details"
2002
	      ui_sportwatcherWidgetBase.edTotalDistance->setAlignment(Qt::AlignRight);
2003
	      ui_sportwatcherWidgetBase.edTotalDistance->insert(kl->formatNumber(qs_distance, false));
2004
	      ui_sportwatcherWidgetBase.edTotalTime->setAlignment(Qt::AlignRight);
2005
	      ui_sportwatcherWidgetBase.edTotalTime->insert(qs_etime);
2006
	      ui_sportwatcherWidgetBase.edAvgSpeed->setAlignment(Qt::AlignRight);
2007
	      ui_sportwatcherWidgetBase.edAvgSpeed->insert(kl->formatNumber(qs_avgspeed, false));
2008
	      ui_sportwatcherWidgetBase.edTotalHeight->setAlignment(Qt::AlignRight);
2009
	      ui_sportwatcherWidgetBase.edTotalHeight->insert(kl->formatNumber(qs_ascent, false));
2010
	      ui_sportwatcherWidgetBase.edAvgHR->setAlignment(Qt::AlignRight);
2011
	      ui_sportwatcherWidgetBase.edAvgHR->insert(qs_avghr);
88 andreas 2012
	      delete qt;
2013
	      /* Get the laps. */
2014
	      laps = 1;
2015
 
2016
	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
2017
	      {
149 andreas 2018
		 double spd;
2019
		 char *un;
2020
 
88 andreas 2021
		 if ((lap = ds.getLap(i)) == NULL)
2022
		    continue;
2023
 
2024
		 qt = garmin_dtime (lap->start_time);
2025
		 qs_name.sprintf("Lap %03d - ", laps);
230 andreas 2026
		 qs_name.append(kl->formatTime(qt->time(), true));
149 andreas 2027
		 qs_distance.sprintf("%.2f %s", (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance, (Units == 1) ? "ft" : "m");
248 andreas 2028
		 totdist += (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance;
2029
		 qs_totdist.sprintf("%2.f %s", totdist, (Units == 1) ? "ft" : "m");
88 andreas 2030
		 t = QTime(0, 0, 0, 0);
2031
		 t = t.addMSecs(lap->total_time * 10);
2032
		 qs_etime = t.toString("hh:mm:ss.zzz");
149 andreas 2033
		 spd = lap->total_distance / (lap->total_time / 100.0);
249 andreas 2034
		 delete qt;
2035
		 qt = 0;
149 andreas 2036
 
2037
		 if (Units == 0)
2038
		 {
2039
		    un = (char *)"km/h";
2040
		    spd *= 3.6;
2041
		 }
2042
		 else
156 andreas 2043
		 {
2044
		    spd *= 3.6 / 1.609344;
2045
		    un = (char *)"mph";
2046
		 }
149 andreas 2047
 
2048
		 qs_avgspeed.sprintf("%.2f %s", spd, un);
156 andreas 2049
		 qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? lap->max_speed * 3.6 / 1.609344 : lap->max_speed * 3.6, un);
88 andreas 2050
		 qs_calories.sprintf("%d", lap->calories);
2051
 
2052
		 if (lap->total_distance > 0 && lap->total_time != 0)
2053
		 {
156 andreas 2054
		    double fact;
2055
 
2056
		    if (Units == 0)
2057
		       fact = 1000.0;		// 1 km
2058
		    else
2059
		       fact = 1609.344;		// 1 mile in meters
2060
 
2061
		    long secs = (double)lap->total_time / 10000.0 * (fact / lap->total_distance * 100.0);
88 andreas 2062
		    int h = secs / 3600;
2063
		    int m = (secs - (h * 3600)) / 60;
2064
		    int s = secs - ((h * 3600) + (m * 60));
2065
		    t = QTime(h, m, s, 0);
230 andreas 2066
		    qs_avgpace = kl->formatTime(t, true);
156 andreas 2067
 
2068
		    if (Units == 0)
2069
		       qs_avgpace.append(QString(" /km"));
2070
		    else
2071
		       qs_avgpace.append(QString(" /mi"));
88 andreas 2072
		 }
2073
 
2074
		 qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);
2075
		 qs_maxhr.sprintf("%d bpm", lap->max_heart_rate);
2076
 
2077
		 anz = 0;
2078
		 alt_asc = alt_dsc = 0;
249 andreas 2079
		 // Add a new detail line to the tab "Lap details"
2080
		 QTreeWidgetItem * trdetail = new QTreeWidgetItem(ui_sportwatcherWidgetBase.twLaps);
88 andreas 2081
 
2082
		 if ((point = ds.getPoint(lap->start_time)) != 0)
2083
		 {
2084
		    if (point->alt < 20000)
149 andreas 2085
		    {
88 andreas 2086
		       alt_dsc = alt_asc = point->alt;
149 andreas 2087
 
2088
		       if (old_asc == 0)
2089
			  old_asc = alt_asc;
2090
 
2091
		       if (old_dsc == 0)
2092
			  old_dsc = alt_dsc;
2093
		    }
88 andreas 2094
		    else
2095
		       alt_dsc = alt_asc = 0;
2096
 
218 andreas 2097
		    POINT *oldPoint = 0;
2098
		    double sc, dist, speed;
222 andreas 2099
		    unsigned long t1, t2;
2100
		    t1 = t2 = 0;
2101
		    pause = false;
2102
		    bool ignore = false;
218 andreas 2103
 
88 andreas 2104
		    while (point)
2105
		    {
2106
		       if (point->time > (lap->start_time + (lap->total_time / 100)))
2107
			 break;
2108
 
249 andreas 2109
		       QTreeWidgetItem *child = new QTreeWidgetItem(trdetail);
2110
		       qt = garmin_dtime (point->time);
2111
		       child->setText(0, kl->formatTime(qt->time(), true));
2112
		       child->setTextAlignment(0, Qt::AlignRight);
2113
		       child->setText(8, QString("%1 %2").arg(kl->formatNumber((double)point->heart_rate, 0)).arg(" bpm"));
2114
		       child->setTextAlignment(8, Qt::AlignRight);
2115
 
2116
		       if (point->cadence < 0xff)
2117
		       {
2118
			  child->setText(10, kl->formatNumber((double)point->cadence, 0));
2119
			  child->setTextAlignment(10, Qt::AlignRight);
2120
		       }
2121
 
2122
		       if (point->alt < 20000)
2123
		       {
2124
		       double alt = (Units == 0) ? (double)point->alt : (double)point->alt / 0.304;
2125
 
2126
			  child->setText(11, QString("%1 %2").arg(kl->formatNumber(alt, 2)).arg((Units == 0) ? QString(" m") : QString(" ft")));
2127
			  child->setTextAlignment(11, Qt::AlignRight);
2128
		       }
2129
 
218 andreas 2130
		       if (!oldPoint)
2131
			  oldPoint = point;
2132
 
88 andreas 2133
		       if (point->alt > alt_asc && point->alt < 20000)
2134
		       {
2135
			  alt_asc = point->alt;
2136
 
2137
			  if (alt_dsc == 0)
2138
			     alt_dsc = point->alt;
2139
		       }
2140
 
2141
		       if (point->alt < alt_dsc)
2142
			  alt_dsc = point->alt;
2143
 
2144
		       // save the min and max values. We need this information to
2145
		       // build the graphics.
221 andreas 2146
		       if (point->heart_rate > max_hr && point->heart_rate < 250)
88 andreas 2147
			  max_hr = point->heart_rate;
2148
 
221 andreas 2149
		       if ((min_hr == 0 && point->heart_rate > 0 && point->heart_rate < 250)
2150
			   || (point->heart_rate > 0 && point->heart_rate < min_hr))
88 andreas 2151
			  min_hr = point->heart_rate;
2152
 
2153
		       if (point->alt < 20000 && max_height < point->alt)
2154
			  max_height = point->alt;
2155
 
2156
		       if (point->alt < 20000 && (min_height == 0.0 || min_height > point->alt))
2157
			  min_height = point->alt;
2158
 
218 andreas 2159
		       // Calculate speed of current track
222 andreas 2160
		       if (!pause && point->distance > 1.0e10)
2161
		       {
2162
			  t1 = point->time;
2163
			  pause = true;
2164
			  ignore = true;
2165
		       }
2166
		       else if (pause)
2167
		       {
2168
			  t2 = point->time;
2169
			  pause = false;
2170
			  point = ds.getPoint(point->time + 1);
2171
			  continue;
2172
		       }
218 andreas 2173
 
222 andreas 2174
		       if (!ignore && !pause)
2175
		       {
2176
			  sc = point->time - oldPoint->time;
2177
			  dist = point->distance - oldPoint->distance;
218 andreas 2178
 
249 andreas 2179
			  child->setText(1, QString("%1 %2").arg(kl->formatNumber((Units == 0) ? (double)dist : (double)dist / 0.304, 2)).arg((Units == 0) ? QString(" m") : QString(" ft")));
2180
			  child->setTextAlignment(1, Qt::AlignRight);
2181
 
222 andreas 2182
			  if (t2 > t1)
2183
			     sc -= t2 - t1;
218 andreas 2184
 
222 andreas 2185
			  if (sc > 0.0)
223 andreas 2186
			  {
222 andreas 2187
			     speed = (dist / sc) * 3.6;
223 andreas 2188
 
2189
			     if (speed > lap->max_speed * 3.6)
2190
			        speed = lap->max_speed * 3.6;
2191
			  }
222 andreas 2192
			  else
2193
			     speed = 0.0;
218 andreas 2194
 
222 andreas 2195
			  if (Units == 1)
2196
			     speed /= 1.609344;
218 andreas 2197
 
249 andreas 2198
			  child->setText(5, QString("%1 %2").arg(kl->formatNumber(speed, 2)).arg((Units == 0) ? QString(" Km/h") : QString(" mi/h")));
2199
			  child->setTextAlignment(5, Qt::AlignRight);
2200
 
222 andreas 2201
			  if (speed > 0.0 && speed < 400.0 && max_speed < speed)
2202
			     max_speed = speed;
2203
 
2204
			  if (speed > 0.0 && (min_speed == 0.0 || min_speed > speed))
2205
			     min_speed = speed;
2206
 
2207
			  oldPoint = point;
2208
		       }
2209
 
2210
		       if (!pause && ignore)
2211
			  ignore = false;
2212
 
221 andreas 2213
		       if (point->heart_rate > 0 && point->heart_rate < 250)
88 andreas 2214
		       {
2215
			  avg_hr += point->heart_rate;
2216
			  men++;
2217
		       }
2218
 
2219
		       point = ds.getPoint(point->time + 1);
2220
		    }
2221
 
149 andreas 2222
		    if (old_asc < alt_asc)
2223
		       sum_asc += (alt_asc - old_asc);
2224
 
2225
		    if (old_dsc > alt_dsc)
2226
		       sum_dsc += (old_dsc - alt_dsc);
2227
 
2228
		    old_asc = alt_asc;
2229
		    old_dsc = alt_dsc;
2230
		    qs_ascent.sprintf("%.2f %s", (Units == 1) ? (alt_asc / 0.304) : alt_asc, (Units == 1) ? "ft" : "m");
2231
		    qs_descent.sprintf("%.2f %s", (Units == 1) ? (alt_dsc / 0.304) : alt_dsc, (Units == 1) ? "ft" : "m");
88 andreas 2232
		 }
2233
 
2234
		 if (lap->avg_cadence != 0xff)
2235
		    qs_avgcadence.sprintf("%d", lap->avg_cadence);
248 andreas 2236
		 // Add a new detail line to the tab "Summary"
2237
		 K3ListViewItem *edetail = new K3ListViewItem(element, qs_name,
2238
			kl->formatNumber(qs_distance, false),
2239
			qs_etime, qs_avgpace, kl->formatNumber(qs_avgspeed, false),
2240
			kl->formatNumber(qs_maxspeed, false), qs_calories, qs_avghr);
88 andreas 2241
		 edetail->setText(8, qs_maxhr);
2242
		 edetail->setText(9, qs_avgcadence);
248 andreas 2243
		 edetail->setText(10, kl->formatNumber(qs_ascent, false));
2244
		 edetail->setText(11, kl->formatNumber(qs_descent, false));
245 andreas 2245
		 edetail->setPixmap(0, KIcon(QString("history")).pixmap(16));
2246
		 QPixmap qpx = KIcon(QString("other")).pixmap(16);
249 andreas 2247
 
248 andreas 2248
		 trdetail->setText(0, qs_etime);
2249
		 trdetail->setTextAlignment(0, Qt::AlignRight);
2250
		 trdetail->setText(1, kl->formatNumber(qs_distance));
2251
		 trdetail->setTextAlignment(1, Qt::AlignRight);
2252
		 trdetail->setText(2, kl->formatNumber(qs_totdist));
2253
		 trdetail->setTextAlignment(2, Qt::AlignRight);
2254
		 trdetail->setText(4, qs_avgpace);
2255
		 trdetail->setTextAlignment(4, Qt::AlignRight);
2256
		 trdetail->setText(5, kl->formatNumber(qs_avgspeed, false));
2257
		 trdetail->setTextAlignment(5, Qt::AlignRight);
2258
		 trdetail->setText(6, kl->formatNumber(qs_maxspeed, false));
2259
		 trdetail->setTextAlignment(6, Qt::AlignRight);
2260
		 trdetail->setText(7, qs_calories);
2261
		 trdetail->setTextAlignment(7, Qt::AlignRight);
2262
		 trdetail->setText(8, qs_avghr);
2263
		 trdetail->setTextAlignment(8, Qt::AlignRight);
2264
		 trdetail->setText(9, qs_maxhr);
2265
		 trdetail->setTextAlignment(9, Qt::AlignRight);
2266
		 trdetail->setText(10, qs_avgcadence);
2267
		 trdetail->setTextAlignment(10, Qt::AlignRight);
2268
		 trdetail->setText(11, kl->formatNumber(qs_ascent, false));
2269
		 trdetail->setTextAlignment(11, Qt::AlignRight);
2270
 
128 andreas 2271
		 switch (rakt->run->sport_type)
2272
		 {
232 andreas 2273
		    case D1000_running:
247 andreas 2274
		       edetail->setPixmap(0, KIcon(QString("spw-running")).pixmap(16));
232 andreas 2275
		    break;
2276
 
2277
		    case D1000_biking:
245 andreas 2278
		       edetail->setPixmap(0, KIcon(QString("bike")).pixmap(16));
232 andreas 2279
		    break;
2280
 
2281
		    case D1000_other:
2282
		       edetail->setPixmap(0, qpx);
2283
		    break;
2284
 
128 andreas 2285
		    default:
232 andreas 2286
		       edetail->setPixmap(0, qpx);
128 andreas 2287
		 }
2288
 
245 andreas 2289
		 ui_sportwatcherWidgetBase.liLaps->clearSelection();
88 andreas 2290
		 element->insertItem(edetail);
2291
		 delete qt;
2292
		 laps++;
2293
	      }
149 andreas 2294
 
2295
	      qs_ascent.sprintf("%.2f %s", (Units == 1) ? sum_asc / 0.304 : sum_asc, (Units == 1) ? "ft" : "m");
2296
	      qs_descent.sprintf("%.2f %s", (Units == 1) ? sum_dsc / 0.304 : sum_dsc, (Units == 1) ? "ft" : "m");
2297
      	      element->setText(10, qs_ascent);
2298
	      element->setText(11, qs_descent);
248 andreas 2299
	      qs_ascent.sprintf("%s %s", (Units == 1) ? kl->formatNumber(ds.getAscend() / 0.304, 2).toAscii().data() : kl->formatNumber(ds.getAscend(), 2).toAscii().data(), (Units == 1) ? "ft" : "m");
2300
	      ui_sportwatcherWidgetBase.edTotalHeight->clear();
2301
	      ui_sportwatcherWidgetBase.edTotalHeight->insert(qs_ascent);
2302
	      ui_sportwatcherWidgetBase.edLapNumber->setAlignment(Qt::AlignRight);
2303
	      ui_sportwatcherWidgetBase.edLapNumber->insert(kl->formatNumber((double)laps, 0));
88 andreas 2304
	   }
2305
 
2306
	   rakt = rakt->next;
2307
	}
2308
 
2309
	if (men > 0)
2310
	   avg_hr /= men;
221 andreas 2311
	else
2312
	   min_hr = max_hr = avg_hr = 0;
88 andreas 2313
}
2314
 
100 andreas 2315
void sportwatcherWidget::showTrack()
2316
{
132 andreas 2317
	showTrack(0, QRect(0, 0, 0, 0), 0);
104 andreas 2318
}
2319
 
2320
void sportwatcherWidget::showTrack(int zoom)
2321
{
132 andreas 2322
	showTrack(zoom, mapPan, mapLap);
2323
}
2324
 
2325
void sportwatcherWidget::showTrack(int zoom, const QRect &pan, LAP *lap)
2326
{
100 andreas 2327
int width, height;
2328
double x1, y1, x2, y2;
132 andreas 2329
int a, top, left, panX, panY;
2330
uint32 i;
109 andreas 2331
double coordW, coordH, tick;
156 andreas 2332
double meterW, dist, fact;
100 andreas 2333
QPainter paint;
152 andreas 2334
posn_type posNW, posSE, posLXY, posRXY;
100 andreas 2335
POINT *point;
152 andreas 2336
bool Fgeo = false;
157 andreas 2337
bool Data = false;
232 andreas 2338
#if defined HAVE_GDAL
158 andreas 2339
QString fName = MAP;
2340
//double adfGeoTransform[6];
2341
GDALDataset *poDataset = 0;
246 andreas 2342
GDALRasterBand *poBand = 0;
2343
unsigned char *pafScanline = 0;
2344
unsigned char *pafScanlineRed = 0;
2345
unsigned char *pafScanlineGreen = 0;
2346
unsigned char *pafScanlineBlue = 0;
2347
unsigned char *pafScanlineAlpha = 0;
151 andreas 2348
int nXSize, nYSize;
159 andreas 2349
int xOff, yOff;
158 andreas 2350
double oriLeftLon, oriLeftLat, oriRightLon, oriRightLat;
151 andreas 2351
#endif
100 andreas 2352
 
248 andreas 2353
	if (!DIRTY || curTab == 2 || curTab == 3)
246 andreas 2354
	   return;
2355
 
109 andreas 2356
	if (!gmn)
2357
	   return;
2358
 
154 andreas 2359
	QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));
2360
 
104 andreas 2361
	if (zoom != zfactor)
2362
	   zfactor = zoom;
2363
 
132 andreas 2364
	if (mapLap != lap)
2365
	   mapLap = lap;
2366
 
232 andreas 2367
#if defined HAVE_GDAL
2368
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
2369
	KConfigGroup wms (&cfg, "WMS");
2370
	bool square = wms.readEntry("Square", false);
2371
	int CorrX = wms.readEntry("CorrX", 0);
2372
	int CorrY = wms.readEntry("CorrY", 0);
2373
	KConfigGroup ic (&cfg, "ImageCoords");
2374
	oriLeftLon = ic.readEntry("LeftLon", 0.0);
2375
	oriLeftLat = ic.readEntry("LeftLat", 0.0);
2376
	oriRightLon = ic.readEntry("RightLon", 0.0);
2377
	oriRightLat = ic.readEntry("RightLat", 0.0);
2378
//	int isrs = ic.readEntry("SRS", 0);
157 andreas 2379
#endif
248 andreas 2380
	if (curTab == 0)
2381
	{
2382
	   width = ui_sportwatcherWidgetBase.imgMap->width() - 2;
2383
	   height = ui_sportwatcherWidgetBase.imgMap->height();
2384
	}
2385
	else
2386
	{
2387
	   width = ui_sportwatcherWidgetBase.grMap->width() - 2;
2388
	   height = ui_sportwatcherWidgetBase.grMap->height();
2389
	}
232 andreas 2390
#if defined HAVE_GDAL
158 andreas 2391
	if (MapType == MPT_WMS && square)
232 andreas 2392
	   pmMap = QPixmap(width / (int)mFactor * (int)mFactor, height / (int)mFactor * (int)mFactor);
157 andreas 2393
	else
232 andreas 2394
	   pmMap = QPixmap(width, height);
157 andreas 2395
#else
232 andreas 2396
	pmMap = QPixmap(width, height);
157 andreas 2397
#endif
245 andreas 2398
	if (pmMap.isNull())
2399
	   return;
2400
 
2401
	// Here we begin do draw something
100 andreas 2402
	paint.begin(&pmMap);
2403
 
132 andreas 2404
	panX = panY = 0;
2405
 
2406
	if (stateHand && mapPan != pan)
2407
	{
2408
	   mapPan = pan;
2409
	   panX = mapPan.right() - mapPan.left();
2410
	   panY = mapPan.bottom() - mapPan.top();
2411
	   oldTransX += (double)panX;
2412
	   oldTransY += (double)panY;
2413
	}
2414
 
104 andreas 2415
	memset(&posNW, 0, sizeof(posn_type));
2416
	memset(&posSE, 0, sizeof(posn_type));
100 andreas 2417
 
156 andreas 2418
	posSE.lat = 90.0;
2419
	posSE.lon = 180.0;
2420
	posNW.lat = -90.0;
2421
	posNW.lon = -180.0;
100 andreas 2422
 
2423
	/*
2424
	 * Find out the corners of our track (NW, NE, SE, SW)
2425
	 */
132 andreas 2426
	if (mapLap)
2427
	   i = mapLap->start_time;
2428
	else
2429
	   i = 0;
100 andreas 2430
 
2431
	while ((point = ds.getPoint(i)) != 0)
2432
	{
132 andreas 2433
	   if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
2434
	      break;
2435
 
100 andreas 2436
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
2437
	   {
132 andreas 2438
	      i = point->time + 1;
100 andreas 2439
	      continue;
2440
	   }
2441
 
2442
	   if (SEMI2DEG(point->posn.lat) > posNW.lat)
2443
	      posNW.lat = SEMI2DEG(point->posn.lat);
2444
 
2445
	   if (SEMI2DEG(point->posn.lat) < posSE.lat)
2446
	      posSE.lat = SEMI2DEG(point->posn.lat);
2447
 
156 andreas 2448
	   if (SEMI2DEG(point->posn.lon) > posNW.lon)
100 andreas 2449
	      posNW.lon = SEMI2DEG(point->posn.lon);
2450
 
156 andreas 2451
	   if (SEMI2DEG(point->posn.lon) < posSE.lon)
100 andreas 2452
	      posSE.lon = SEMI2DEG(point->posn.lon);
2453
 
132 andreas 2454
	   i = point->time + 1;
157 andreas 2455
	   Data = true;
100 andreas 2456
	}
104 andreas 2457
 
2458
	coordW = (posNW.lon > posSE.lon) ? posNW.lon - posSE.lon : posSE.lon - posNW.lon;
2459
	coordH = (posNW.lat > posSE.lat) ? posNW.lat - posSE.lat : posSE.lat - posNW.lat;
109 andreas 2460
	meterW = ds.earth_distance(posNW.lat, posNW.lon, posNW.lat, posSE.lon);
104 andreas 2461
 
2462
	// define the ticks to translate the GPS coordinates into pixels.
2463
	// The track should be centered and we have to calculate the
2464
	// rectangular within we draw the track.
152 andreas 2465
	if (coordW < coordH)
100 andreas 2466
	{
152 andreas 2467
	   tick = (double)width / coordW + (double)zoom;
2468
 
2469
	   if ((tick * coordH) > height)
109 andreas 2470
	      tick = (double)height / coordH + (double)zoom;
100 andreas 2471
	}
2472
	else
2473
	{
152 andreas 2474
	   tick = (double)height / coordH + (double)zoom;
2475
 
2476
	   if ((tick * coordW) > width)
109 andreas 2477
	      tick = (double)width / coordW + (double)zoom;
100 andreas 2478
	}
104 andreas 2479
 
156 andreas 2480
	left = width - (width - tick * coordW) / 2;
152 andreas 2481
	top = (height - tick * coordH) / 2;
2482
 
132 andreas 2483
	a = tick * coordW;
156 andreas 2484
 
2485
	if (Units == 0)
2486
	   dist = meterW / a;			// Meters
2487
	else
2488
	   dist = meterW * 1.609344 / a;	// 1/1000 mile (5.28 feet)
2489
 
232 andreas 2490
#if defined HAVE_GDAL
159 andreas 2491
	geoRect.llat = 0.0;
2492
	geoRect.llon = 0.0;
2493
	geoRect.rlat = 0.0;
2494
	geoRect.rlon = 0.0;
158 andreas 2495
	geoRect.width = width;
2496
	geoRect.height = height;
146 andreas 2497
	/*
2498
	 * If we have a map file, we try to read it and if successfull,
2499
	 * we should get a map painted.
154 andreas 2500
	 *
2501
	 * Currently only WMS-Server is supported, allthough GDAL allows
2502
	 * several other formats too.
146 andreas 2503
	 */
157 andreas 2504
	if (!MAP.isEmpty() && Data)
146 andreas 2505
	{
158 andreas 2506
	bool writeTag = true;
2507
	double mtx, mty;
2508
	double vx, vy;
152 andreas 2509
 
158 andreas 2510
	   xOff = yOff = 0;
2511
	   posRXY.lon = posNW.lon + (width - left + oldTransX) / tick;
2512
	   posLXY.lat = posNW.lat + (top + oldTransY) / tick;
2513
	   posLXY.lon = posSE.lon - (left - a - oldTransX) / tick;
2514
	   posRXY.lat = posSE.lat - (height - top - (tick * coordH) - oldTransY) / tick;
159 andreas 2515
	   geoRect.llat = posLXY.lat;
2516
	   geoRect.llon = posLXY.lon;
2517
	   geoRect.rlat = posRXY.lat;
2518
	   geoRect.rlon = posRXY.lon;
158 andreas 2519
	   // width and height of map in meters
2520
	   mtx = ds.earth_distance(posRXY.lat, posRXY.lon, posRXY.lat, posLXY.lon);
2521
	   mty = ds.earth_distance(posRXY.lat, posRXY.lon, posLXY.lat, posRXY.lon);
157 andreas 2522
 
158 andreas 2523
	   // factor to correct the map, in case we use a WMS server
2524
	   if (MapType == MPT_WMS)
2525
	   {
2526
	      vx = (posRXY.lon - posLXY.lon) / mtx * CorrX;
2527
	      vy = (posRXY.lat - posLXY.lat) / mty * CorrY;
2528
	      posRXY.lon += vx;
2529
	      posRXY.lat += vy;
2530
	      posLXY.lon += vx;
2531
	      posLXY.lat += vy;
2532
	   }
2533
 
154 andreas 2534
	   /*
158 andreas 2535
	    * Write a control file for GDAL, if we use a WMS server.
2536
	    * Warp an image if we use PNG or BMP or GIF.
2537
	    * Warp a region if we use TIFF
154 andreas 2538
	    */
158 andreas 2539
	   if (MapType == MPT_WMS)
2540
	      writeTag = writeWMSTag(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat, width, height);
2541
 
159 andreas 2542
	   if (MapType == MPT_GIF || MapType == MPT_BMP || MapType == MPT_PNG ||
2543
	       MapType == MPT_SGI || MapType == MPT_TIF)
158 andreas 2544
	      writeTag = warpImage(MAP, &fName);
2545
 
2546
	   if (writeTag)
151 andreas 2547
	   {
246 andreas 2548
	      if (MapType != MPT_SHP && (poDataset = (GDALDataset *)GDALOpen (fName.toAscii().constData(), GA_ReadOnly)) != NULL)
151 andreas 2549
	      {
2550
		 QPixmap bild;
2551
		 int nRasterCount = poDataset->GetRasterCount();
2552
		 int nXBlock, nYBlock;
154 andreas 2553
		 GDALColorTable *pCT, *pCTb, *pCTr, *pCTg, *pCTa;
146 andreas 2554
 
151 andreas 2555
		 int             bGotMin, bGotMax;
2556
		 int		 tTypeLen, tColor, tColorEntrys;
2557
		 GDALDataType    tRasterType;
2558
		 double          adfMinMax[2];
2559
 
154 andreas 2560
		 pafScanlineRed = pafScanlineGreen = pafScanlineBlue = pafScanlineAlpha = 0;
246 andreas 2561
		 Fgeo = true;
154 andreas 2562
		 /*
2563
		  * Read every raster band.
2564
		  *
2565
		  * If we get 3 raster bands, the image is a 24 bit image.
2566
		  * If we get 4 raster bands, the image is probably a 24 bit
2567
		  * image with an alpha channel. Currently the alpha channel
2568
		  * is ignored!
2569
		  * If we have 1 raster band, the image is 8 bit monochrom.
2570
		  * Otherwise the image is undefined and the results are also.
2571
		  */
151 andreas 2572
		 for (a = 1; a <= nRasterCount; a++)
2573
		 {
246 andreas 2574
		    if (!Fgeo)
2575
		       break;
2576
 
245 andreas 2577
		    if (!(poBand = poDataset->GetRasterBand (a)))
2578
		    {
2579
		       paint.end();
2580
		       return;
2581
		    }
2582
 
151 andreas 2583
		    poBand->GetBlockSize (&nXBlock, &nYBlock);
2584
		    nXSize = poBand->GetXSize();
2585
		    nYSize = poBand->GetYSize();
2586
		    tRasterType = poBand->GetRasterDataType ();
2587
		    tTypeLen = GDALGetDataTypeSize (tRasterType) / 8;	// We need Bytes not Bits!
2588
		    tColor = poBand->GetColorInterpretation ();
2589
 
2590
		    adfMinMax[0] = poBand->GetMinimum (&bGotMin);
2591
		    adfMinMax[1] = poBand->GetMaximum (&bGotMax);
2592
 
2593
		    if (!(bGotMin && bGotMax))
2594
		       GDALComputeRasterMinMax ((GDALRasterBandH)poBand, TRUE, adfMinMax);
2595
 
152 andreas 2596
		    if ((pCT = poBand->GetColorTable()) != NULL)
151 andreas 2597
		       tColorEntrys = poBand->GetColorTable()->GetColorEntryCount();
2598
 
2599
		    switch (a)
2600
		    {
152 andreas 2601
		       case 1: pafScanlineRed   = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineRed; pCTr = pCT; break;
2602
		       case 2: pafScanlineGreen = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineGreen; pCTg = pCT; break;
2603
		       case 3: pafScanlineBlue  = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineBlue; pCTb = pCT; break;
154 andreas 2604
		       case 4: pafScanlineAlpha  = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineAlpha; pCTa = pCT; break;
151 andreas 2605
		    }
2606
 
246 andreas 2607
		    if (!pafScanline)
2608
		    {
2609
		       paint.end();
2610
		       KMessageBox::error(this, i18n("Not enough memory for a raster operation!"));
2611
		       return;
2612
		    }
2613
 
152 andreas 2614
		    memset (pafScanline, 0, tTypeLen * nXSize * nYSize);
151 andreas 2615
 
154 andreas 2616
		    /*
158 andreas 2617
		     * Get the image (from the server) and put the tiles together.
154 andreas 2618
		     *
2619
		     * The function reads only one raster band. This is,
2620
		     * because the function is called for every raster band and
2621
		     * every raster band is stored into a separate array.
2622
		     */
152 andreas 2623
		    if (poBand->RasterIO (GF_Read, 0, 0, nXSize, nYSize, pafScanline, nXSize, nYSize, tRasterType, 0, 0) == CE_Failure)
2624
		    {
248 andreas 2625
		       paint.end();
151 andreas 2626
		       KMessageBox::error(this, i18n("Error reading a raster band!"));
248 andreas 2627
		       paint.begin(&pmMap);
152 andreas 2628
		       Fgeo = false;
2629
		       break;
2630
		    }
2631
		    else
2632
		       Fgeo = true;
151 andreas 2633
		 }
2634
 
154 andreas 2635
		 /*
2636
		  * Only if Fgeo is TRUE, we've read successfully all raster
2637
		  * bands. Now we have to put the bands together to get
2638
		  * an image.
2639
		  */
152 andreas 2640
		 if (Fgeo)
2641
		 {
2642
		 unsigned char *pCombinedBytes = new unsigned char[(tTypeLen * nXSize * nYSize * nRasterCount)];
2643
		 unsigned char *ptr_dest, *ptr_src;
2644
		 int j;
151 andreas 2645
 
245 andreas 2646
		    ptr_dest = ptr_src = 0;
2647
 
154 andreas 2648
		    /*
2649
		     * We need two nested loops to set the pixels in the wanted
2650
		     * order.
2651
		     */
152 andreas 2652
		    for (a = 0, j = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount, j++)
151 andreas 2653
		    {
152 andreas 2654
		       int k = a;
2655
 
2656
		       for (int m = nRasterCount - 1; m >= 0; m--, k++)
2657
		       {
245 andreas 2658
		       unsigned char *pBytes = 0;
152 andreas 2659
 
2660
			  switch (m)
2661
			  {
154 andreas 2662
			     case 3: pBytes = pafScanlineAlpha; pCT = pCTa; break;
152 andreas 2663
			     case 2: pBytes = pafScanlineBlue; pCT = pCTb; break;
2664
			     case 1: pBytes = pafScanlineGreen; pCT = pCTg; break;
2665
			     default: pBytes = pafScanlineRed; pCT = pCTr;
2666
			  }
2667
 
2668
			  ptr_dest = pCombinedBytes + k;
2669
			  unsigned char b = pBytes[j];
2670
 
154 andreas 2671
			  /*
2672
			   * If we have a color table, the pixels are pointers
2673
			   * to the color table. We need to convert them into
2674
			   * 24 bit pixels plus an optional alpha channel.
2675
			   */
152 andreas 2676
			  if (pCT != NULL)
2677
			  {
2678
			     GDALColorEntry ce;
2679
			     unsigned int c = (unsigned int)b;
2680
			     c = pCT->GetColorEntryAsRGB (c, &ce);
2681
 
2682
			     if  (m == 0) c = ce.c1;
2683
			     if  (m == 1) c = ce.c2;
2684
			     if  (m == 2) c = ce.c3;
154 andreas 2685
			     if  (m == 3) c = ce.c4;
152 andreas 2686
 
2687
			     b = (unsigned char)c;
2688
			  }
2689
 
2690
			  ptr_src = &b;
2691
			  memcpy (ptr_dest, ptr_src, 1);
2692
		       }
151 andreas 2693
		    }
152 andreas 2694
 
2695
		    x1 = y1 = 0;
2696
 
154 andreas 2697
		    /*
2698
		     * The following loop is QT specific! It sets the pixels
2699
		     * of the raw image, pixel by pixel. This may be slow, but
2700
		     * everything else didn't work :-(
2701
		     *
2702
		     * FIXME: We need a more effective routine to put the
158 andreas 2703
		     *        raw image into QT's "painter" class.
154 andreas 2704
		     */
2705
		    for (a = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount)
152 andreas 2706
		    {
158 andreas 2707
		       if (x1 < width && y1 < height)
2708
		       {
2709
			  if (nRasterCount == 3)
232 andreas 2710
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a]), Qt::SolidLine));
158 andreas 2711
			  else if (nRasterCount > 3)
232 andreas 2712
			     paint.setPen (QPen(QColor(qRgba((int)pCombinedBytes[a+3], (int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a])), Qt::SolidLine));
158 andreas 2713
			  else if (nRasterCount == 2)
232 andreas 2714
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a+1], (int)pCombinedBytes[a], (int)pCombinedBytes[a+1]), Qt::SolidLine));
158 andreas 2715
			  else if (nRasterCount == 1)
232 andreas 2716
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a], (int)pCombinedBytes[a], (int)pCombinedBytes[a]), Qt::SolidLine));
154 andreas 2717
 
158 andreas 2718
			  paint.drawPoint(x1, y1);
2719
		       }
2720
 
152 andreas 2721
		       x1++;
2722
 
2723
		       if (x1 >= nXSize)
2724
		       {
2725
			  x1 = 0;
2726
			  y1++;
2727
		       }
2728
		    }
2729
 
2730
		    delete pCombinedBytes;
245 andreas 2731
		    pCombinedBytes = 0;
151 andreas 2732
		 }
2733
 
152 andreas 2734
		 if (pafScanlineRed)
2735
		    delete pafScanlineRed;
151 andreas 2736
 
152 andreas 2737
		 if (pafScanlineGreen)
2738
		    delete pafScanlineGreen;
2739
 
2740
		 if (pafScanlineBlue)
2741
		    delete pafScanlineBlue;
2742
 
154 andreas 2743
		 if (pafScanlineAlpha)
2744
		    delete pafScanlineAlpha;
2745
 
152 andreas 2746
		 GDALClose (poDataset);
2747
		 poDataset = 0;
158 andreas 2748
 
2749
		 if (MAP != fName)
232 andreas 2750
		    unlink (fName.toAscii().data());
151 andreas 2751
	      }
158 andreas 2752
	      else if (MapType == MPT_SHP)
2753
	      {
2754
		 QDir shpd(MAP, QString("*.shp"));
2755
 
2756
		 if (shpd.count() < 1)
2757
		 {
275 andreas 2758
		    KMessageBox::error(this, i18n("There is no shape file in directory %1").arg(MAP));
158 andreas 2759
		    Fgeo = false;
2760
		 }
2761
		 else
2762
		 {
274 andreas 2763
		 SRender rd;
275 andreas 2764
		 QColor bg(220, 220, 220);		// background color
158 andreas 2765
 
275 andreas 2766
		    if (curTab == 0)
2767
		       rd.setDrawArea(*ui_sportwatcherWidgetBase.imgMap);
2768
		    else
2769
		       rd.setDrawArea(*ui_sportwatcherWidgetBase.grMap);
2770
 
276 andreas 2771
		    rd.setMapType(SRender::MAP_SHAPE);
2772
 
275 andreas 2773
		    if (rd.getMap(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat))
2774
		    {
2775
		       paint.fillRect(0, 0, width+2, height+2, bg);
276 andreas 2776
 
275 andreas 2777
		       if (curTab == 0)
2778
			  paint.drawPixmap(0, 0, *ui_sportwatcherWidgetBase.imgMap->pixmap());
2779
		       else
2780
			  paint.drawPixmap(0, 0, *ui_sportwatcherWidgetBase.grMap->pixmap());
2781
 
2782
		       Fgeo = true;
2783
		    }
2784
		    else
2785
		       Fgeo = false;
158 andreas 2786
		 }
2787
	      }
276 andreas 2788
	      else if (MapType == MPT_OSM)
2789
	      {
2790
		 QFileInfo osmf(MAP);
2791
 
2792
		 if (!osmf.exists())
2793
		 {
2794
		    KMessageBox::error(this, i18n("The OSM file %1 does not exist!").arg(MAP));
2795
		    Fgeo = false;
2796
		 }
2797
		 else
2798
		 {
2799
		 SRender rd;
2800
		 QColor bg(220, 220, 220);		// background color
2801
 
2802
		    if (curTab == 0)
2803
		       rd.setDrawArea(*ui_sportwatcherWidgetBase.imgMap);
2804
		    else
2805
		       rd.setDrawArea(*ui_sportwatcherWidgetBase.grMap);
2806
 
2807
		    rd.setMapType(SRender::MAP_OSM);
2808
 
2809
		    if (rd.getMap(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat))
2810
		    {
2811
		       paint.fillRect(0, 0, width+2, height+2, bg);
2812
 
2813
		       if (curTab == 0)
2814
			  paint.drawPixmap(0, 0, *ui_sportwatcherWidgetBase.imgMap->pixmap());
2815
		       else
2816
			  paint.drawPixmap(0, 0, *ui_sportwatcherWidgetBase.grMap->pixmap());
2817
 
2818
		       Fgeo = true;
2819
		    }
2820
		    else
2821
		       Fgeo = false;
2822
		 }
2823
	      }
151 andreas 2824
	      else
157 andreas 2825
	      {
158 andreas 2826
		 KMessageBox::error(this, i18n("Error opening map file!"));
157 andreas 2827
		 Fgeo = false;
2828
	      }
151 andreas 2829
	   }
146 andreas 2830
	}
151 andreas 2831
#endif
154 andreas 2832
	/*
2833
	 * Here we come to draw the track. It will be drawn over the previous
2834
	 * created map image.
2835
	 */
100 andreas 2836
	// Colors and fonts
154 andreas 2837
	QColor background(220, 220, 220);		// background color
2838
	QColor red(255, 0, 0);				// mile marker
2839
	QColor black(0, 0, 0);				// Text, center of track
2840
//	QColor yellow(255, 255, 0);
2841
	QColor yellow(0x00cf, 0x00ff, 0x0000);		// color of track
100 andreas 2842
	QFont fntNormal("Helvetica");
2843
	fntNormal.setPixelSize(10);
2844
	fntNormal.setStyleHint(QFont::Helvetica);
232 andreas 2845
	QPen dot(red, 4, Qt::SolidLine);
2846
	QPen line(black, 2, Qt::SolidLine);
247 andreas 2847
	QPen yline(yellow, 4, Qt::SolidLine);
154 andreas 2848
	// Fill background with background colors, if there is no map.
152 andreas 2849
	if (!Fgeo)
2850
	   paint.fillRect(0, 0, width+2, height+2, background);
2851
 
156 andreas 2852
	if (Units == 0)
2853
	   fact = 1000.0;
2854
	else
2855
	   fact = 1609.344;
2856
 
109 andreas 2857
	paint.setPen(line);
2858
	paint.drawLine(10, height - 9, 10, height - 4);
156 andreas 2859
	paint.drawLine(10, height - 4, 10 + (fact / dist), height - 4);
2860
	paint.drawLine(10 + (fact / dist), height - 9, 10 + (fact / dist), height - 4);
109 andreas 2861
	paint.setFont(fntNormal);
2862
 
156 andreas 2863
	if (Units == 0)
2864
	   paint.drawText(10, height - 10, QString("1000 m"));
2865
	else
2866
	   paint.drawText(10, height - 10, QString("5280 ft"));
2867
 
100 andreas 2868
	// Draw track
132 andreas 2869
	if (mapLap)
2870
	   i = mapLap->start_time;
2871
	else
2872
	   i = 0;
2873
 
100 andreas 2874
	x1 = y1 = 0.0;
157 andreas 2875
	bool wStart = false;
104 andreas 2876
 
100 andreas 2877
	while ((point = ds.getPoint(i)) != 0)
2878
	{
132 andreas 2879
	   if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
2880
	      break;
2881
 
100 andreas 2882
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
2883
	   {
132 andreas 2884
	      i = point->time + 1;
100 andreas 2885
	      continue;
2886
	   }
2887
 
157 andreas 2888
	   x2 = (left + ((posNW.lon - SEMI2DEG(point->posn.lon)) * tick * -1)) + oldTransX;
2889
	   y2 = (top + ((posNW.lat - SEMI2DEG(point->posn.lat)) * tick)) + oldTransY;
100 andreas 2890
 
157 andreas 2891
	   if (!wStart && x1 != 0.0 && y1 != 0.0)
2892
	   {
2893
	      // Load the start symbol
245 andreas 2894
	      QPixmap qpx (KIcon(QString("wstart")).pixmap(16));
157 andreas 2895
	      // Find the angle of the track and turn the symbol accordingly
2896
	      // we use Pythagoras to calculate the triangle
2897
	      double xl = (x1 < x2) ? x2 - x1 : x1 - x2;
2898
	      double yl = (y1 < y2) ? y2 - y1 : y1 - y2;
2899
	      double da = fmin (xl, yl);
2900
	      double db = fmax (xl, yl);
2901
	      double zl = sqrt (pow (da, 2) + pow (db, 2));
2902
	      double angle = (asin(da / zl) / M_PIl) * 180.0;
2903
 
2904
	      angle = (angle > 45.0) ? 90.0 - angle : angle;
2905
// cout << "Winkel: " << angle << " ---- X: " << xl << ", Y: " << yl << ", Z: " << zl << ", Point (x1,y1,x2,y2): " << x1 << ", " << y1 << ", " << x2 << ", " << y2 << endl;
2906
	      if (x1 < x2 && y1 < y2)		// right, down
2907
		 angle = 90.0 + angle;
2908
	      else if (x1 > x2 && y1 < y2)	// left, down
2909
		 angle = 270.0 - angle;
2910
	      else if (x1 > x2 && y1 > y2)	// left, up
2911
		 angle = 270.0 + angle;
2912
	      else				// right, up
2913
		 angle = 90.0 - angle;
2914
// cout << "Realer Winkel: " << angle << endl;
2915
	      // Set the center of the symbol
2916
	      paint.save ();
2917
	      paint.translate (x1, y1);
2918
	      // rotate the symbol
2919
	      paint.rotate (angle);
2920
	      paint.drawPixmap (-8, -8, qpx);
2921
	      paint.restore ();
2922
	      wStart = true;
2923
	   }
2924
 
100 andreas 2925
	   if (x1 == 0.0 && y1 == 0.0)
2926
	   {
2927
	      x1 = x2;
2928
	      y1 = y2;
2929
	   }
104 andreas 2930
 
100 andreas 2931
	   paint.setPen(yline);
104 andreas 2932
	   paint.drawLine(x1, y1, x2, y2);
2933
 
156 andreas 2934
	   if ((point->distance - dist) >= fact)	// a dot at every 1000 meters or at 1 mile
104 andreas 2935
	   {
2936
	      paint.setPen(dot);
154 andreas 2937
	      paint.drawEllipse(x2-2, y2-2, 3, 3);
156 andreas 2938
	      dist = (int)(point->distance / fact) * fact;
104 andreas 2939
	   }
2940
 
100 andreas 2941
	   paint.setPen(line);
2942
	   paint.drawLine(x1, y1, x2, y2);
2943
	   x1 = x2;
2944
	   y1 = y2;
132 andreas 2945
	   i = point->time + 1;
100 andreas 2946
	}
2947
 
157 andreas 2948
	bool lastLap = false;
2949
 
2950
	if (mapLap)
2951
	   if (ds.getRunNode()->run->last_lap_index == mapLap->index)
2952
	      lastLap = true;
2953
 
2954
	if ((!mapLap || lastLap) && wStart)
2955
	{
2956
	   // load the end symbol
245 andreas 2957
	   QPixmap qpx (KIcon(QString("wtarget")).pixmap(16));
157 andreas 2958
	   paint.drawPixmap (x2, y2 - 16, qpx);
2959
	}
2960
 
100 andreas 2961
	paint.end();
248 andreas 2962
 
2963
	if (curTab == 0)
2964
	   ui_sportwatcherWidgetBase.imgMap->setPixmap(pmMap);
2965
	else
2966
	   ui_sportwatcherWidgetBase.grMap->setPixmap(pmMap);
2967
 
154 andreas 2968
	QApplication::restoreOverrideCursor();
100 andreas 2969
}
2970
 
245 andreas 2971
void sportwatcherWidget::kcbCurveSlot(int)
2972
{
246 andreas 2973
	DIRTY = true;
245 andreas 2974
	showCurves();
246 andreas 2975
	DIRTY = false;
245 andreas 2976
}
2977
 
248 andreas 2978
void sportwatcherWidget::tabViewSlot(int tab)
2979
{
2980
	curTab = tab;
2981
 
2982
	if (tab == 0 && tabDirt0)
2983
	{
2984
	   DIRTY = true;
2985
	   showLaps();
2986
	   showTrack();
2987
	   showCurves();
2988
	   DIRTY = false;
2989
	   tabDirt0 = false;
2990
	}
2991
	else if (tab == 1 && tabDirt1)
2992
	{
2993
	   DIRTY = true;
2994
 
2995
	   if (tabDirt0)
2996
	      showLaps();
2997
 
2998
	   showTrack();
2999
	   DIRTY = false;
3000
	   tabDirt1 = false;
3001
	}
250 andreas 3002
	else if (tab == 2 && tabDirt2)
3003
	{
3004
	   DIRTY = true;
3005
 
3006
	   if (tabDirt0)
3007
	      showLaps();
3008
 
3009
	   setMouseTracking (true);
3010
	   ui_sportwatcherWidgetBase.tabView->setMouseTracking (true);
3011
	   ui_sportwatcherWidgetBase.grHR->setMouseTracking (true);
3012
	   ui_sportwatcherWidgetBase.grElevation->setMouseTracking (true);
3013
	   ui_sportwatcherWidgetBase.grSpeed->setMouseTracking (true);
3014
	   showThreeCurve();
3015
	   tabDirt2 = false;
3016
	   DIRTY = false;
3017
	}
248 andreas 3018
}
3019
 
88 andreas 3020
void sportwatcherWidget::showCurves()
3021
{
148 andreas 3022
	showCurves (mapLap);
3023
}
3024
 
3025
void sportwatcherWidget::showCurves(LAP *lap)
3026
{
88 andreas 3027
QPainter paint;
3028
int width, height;
218 andreas 3029
int i, secs, cuType;
88 andreas 3030
int lineHeight, margin_left, margin_right, margin_bottom;
3031
int x1, y1, x2, y2;		// Coordinates
3032
bool meter;
218 andreas 3033
double maxHeight, minHeight, maxSpeed, minSpeed;
88 andreas 3034
int maxHr, minHr, rh;
3035
POINT *point;
148 andreas 3036
RUN_NODE *rn;
3037
LAP *lp;
88 andreas 3038
double w_tick, h_tick;		// Number of pixels one "tick" has;
3039
				// This depends on the width and height
3040
				// of the image.
3041
	// First we draw a grid based on the min and max
3042
	// values detected in the function showLap(). In case
3043
	// all values are 0, we exit here.
3044
	if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
3045
	   return;
3046
 
248 andreas 3047
	if (!DIRTY || curTab != 0)
246 andreas 3048
	   return;
3049
 
218 andreas 3050
	// Look up, what curves we should draw
245 andreas 3051
	cuType = ui_sportwatcherWidgetBase.kcbCurveTypes->currentIndex();
218 andreas 3052
	// Get the dimensions of the available draw area
245 andreas 3053
	width = ui_sportwatcherWidgetBase.imgProfile->width() - 2;
3054
	height = ui_sportwatcherWidgetBase.imgProfile->height();
232 andreas 3055
	pmProfile = QPixmap(width, height);
88 andreas 3056
	paint.begin(&pmProfile);
3057
 
3058
	// we need a somewhat bigger area to draw our curves than
3059
	// we have with the real min and max values.
3060
	if (max_height > 0.0)
3061
	{
148 andreas 3062
	double add = (max_height - min_height) / 100.0 * 5.0;	// Percent
88 andreas 3063
 
3064
	   maxHeight = max_height + add;
3065
	   minHeight = min_height - add;
3066
 
3067
	   if (minHeight < 0.0)		// make sure, we are not too deep
3068
	      minHeight = 0.0;
3069
	}
221 andreas 3070
	else
3071
	   maxHeight = minHeight = 0.0;
88 andreas 3072
 
218 andreas 3073
	if (max_speed > 0.0)
3074
	{
3075
	double add = (max_speed - min_speed) / 100.0 * 5.0;	// Percent
3076
 
3077
	   maxSpeed = max_speed + add;
3078
	   minSpeed = min_speed - add;
3079
 
3080
	   if (minSpeed < 0.0)		// make sure, we are not too deep
3081
	      minSpeed = 0.0;
3082
	}
221 andreas 3083
	else
3084
	   maxSpeed = minSpeed = 0.0;
218 andreas 3085
 
88 andreas 3086
	if (max_hr > 0)
3087
	{
3088
	   maxHr = max_hr + 10;
3089
	   minHr = min_hr - 10;
3090
 
3091
	   if (minHr < 0)
3092
	      minHr = 0;
3093
	}
221 andreas 3094
	else
3095
	   maxHr = minHr = 0;
88 andreas 3096
 
3097
	// Define colors
148 andreas 3098
	QColor background(220, 220, 220);	// Background of graphic
3099
	QColor mark(255, 255, 255);		// Lines inside curve area
3100
	QColor hlight(180, 180, 180);		// area of current lap
3101
//	hlight.setAlpha(128);			// 50% transparent
3102
	QColor frame(0, 0, 0);			// Text and borders
3103
	QColor barcol(151, 190, 13);		// heart rate
3104
	QColor barcol2(190, 151, 13);		// height over NN
222 andreas 3105
	QColor red(220, 128, 128);		// speed
88 andreas 3106
	QColor blue(0, 0, 240);
3107
	QFont fntNormal("Helvetica");
3108
//	QFont fntBold("Helvetica", 10, QFont::Bold);
3109
	fntNormal.setPixelSize(10);
3110
	fntNormal.setStyleHint(QFont::Helvetica);
3111
//	fntBold.setPixelSize(10);
3112
//	fntBold.setStyleHint(QFont::Helvetica);
3113
	// Calculate ticks
3114
	margin_left = 52;
3115
	margin_right = 40;
3116
	margin_bottom = 12;
3117
	lineHeight = 10;
3118
	rh = height - margin_bottom - 1;
3119
 
249 andreas 3120
	w_tick = (double)(width - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
88 andreas 3121
 
222 andreas 3122
	if (cuType == 1)	// Speed and heart rate?
88 andreas 3123
	{
218 andreas 3124
	   if ((maxSpeed - minSpeed) > (double)(maxHr - minHr))
3125
	   {
222 andreas 3126
	      h_tick = (double)rh / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
218 andreas 3127
	      meter = true;
3128
	   }
3129
	   else
3130
	   {
3131
	      h_tick = (double)rh / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
3132
	      meter = false;
3133
	   }
88 andreas 3134
	}
222 andreas 3135
	else if (cuType == 2)	// Elevation and speed?
88 andreas 3136
	{
218 andreas 3137
	   if ((maxHeight - minHeight) > (double)(maxHr - minHr))
3138
	   {
3139
	      h_tick = (double)rh / (maxHeight - minHeight);		// 1 tick = 1 meter
3140
	      meter = true;
3141
	   }
3142
	   else
3143
	   {
222 andreas 3144
	      h_tick = (double)rh / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
3145
	      meter = false;
3146
	   }
3147
	}
3148
	else			// Elevation and heart rate
3149
	{
3150
	   if ((maxHeight - minHeight) > (double)(maxHr - minHr))
3151
	   {
3152
	      h_tick = (double)rh / (maxHeight - minHeight);		// 1 tick = 1 meter
3153
	      meter = true;
3154
	   }
3155
	   else
3156
	   {
218 andreas 3157
	      h_tick = (double)rh / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
3158
	      meter = false;
3159
	   }
88 andreas 3160
	}
218 andreas 3161
 
88 andreas 3162
	// Fill background with background colors
3163
	paint.fillRect(0, 0, width + 4, height + 4, background);
3164
	// Draw a grid with markers at every 10 minutes
232 andreas 3165
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
88 andreas 3166
	// Bottom border line
3167
	x1 = margin_left;
3168
	y1 = height - margin_bottom;
3169
	x2 = width - margin_right;
3170
	y2 = y1;
3171
	paint.drawLine(x1, y1, x2, y2);
3172
	// Left border line
3173
	x1 = x2 = margin_left;
3174
	y1 = 2;
3175
	y2 = height - margin_bottom;
3176
	paint.drawLine(x1, y1, x2, y2);
3177
	// right border line
3178
	x1 = x2 = width - margin_right;
3179
	paint.drawLine(x1, y1, x2, y2);
148 andreas 3180
 
3181
	// Draw some darker lines to show the laps, if we have one
3182
	// and, in case we have a given lap, we fill the area.
3183
	QDateTime *qt;
3184
	QTime zeit = StartTime.time();
3185
	rn = ds.getRunNode();
232 andreas 3186
	paint.setPen(QPen(hlight, 1, Qt::SolidLine));
148 andreas 3187
 
3188
	for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
3189
	{
3190
	   if ((lp = ds.getLap(i)) == NULL)
3191
	      continue;
3192
 
3193
	   qt = garmin_dtime(lp->start_time);
3194
	   secs = zeit.secsTo(qt->time());
3195
	   delete qt;
3196
	   x1 = secs * w_tick + margin_left + 1;
3197
 
3198
	   if (lap && lp->start_time == lap->start_time)
3199
	      paint.fillRect(x1, 2, (int)((double)lap->total_time / 100.0 * w_tick), height - margin_bottom - 2, hlight);
3200
	   else
3201
	      paint.drawLine(x1, 2, x1, height - margin_bottom);
3202
	}
3203
 
88 andreas 3204
	// Grid vertical
232 andreas 3205
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
88 andreas 3206
	paint.setFont(fntNormal);
3207
	paint.drawText(margin_left - 20, height - lineHeight, 40, lineHeight, Qt::AlignCenter, QString("00:00"));
3208
	paint.save();
3209
	paint.rotate(270);
218 andreas 3210
 
222 andreas 3211
	if (cuType == 1)
232 andreas 3212
	   paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3213
	else
232 andreas 3214
	   paint.setPen(QPen(barcol, 1, Qt::SolidLine));
222 andreas 3215
 
3216
	// Information on left side
218 andreas 3217
	if (cuType == 0)
3218
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
3219
	else if (cuType == 1)
3220
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
3221
	else
3222
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
3223
 
222 andreas 3224
	if (cuType == 2)
232 andreas 3225
	   paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3226
	else
232 andreas 3227
	   paint.setPen(QPen(blue, 1, Qt::SolidLine));
218 andreas 3228
 
222 andreas 3229
	// Information on right side
218 andreas 3230
	if (cuType < 2)
3231
	   paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
3232
	else
3233
	   paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
3234
 
88 andreas 3235
	paint.restore();
232 andreas 3236
	paint.setPen(QPen(mark, 1, Qt::SolidLine));
222 andreas 3237
	// Draw the time scale on the bottom of the graphic
249 andreas 3238
	for (i = 0; (unsigned int)i < (max_time + ds.getPauseTime()); i++)
88 andreas 3239
	{
3240
	   if (i > 0 && !(i % 600))	// every 10 minutes
3241
	   {
3242
	      x1 = x2 = margin_left + w_tick * i;
3243
 
3244
	      if (x1 == (width - margin_right))
3245
		 continue;
3246
 
3247
	      y1 = 2;
3248
	      y2 = height - margin_bottom;
3249
	      paint.drawLine(x1, y1, x2, y2);
3250
	      QTime tm(0, 0, 0);
3251
	      tm = tm.addSecs(i);
232 andreas 3252
	      paint.setPen(QPen(frame, 1, Qt::SolidLine));
230 andreas 3253
//	      paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((i >= 3600) ? "hh:mm:ss" : "mm:ss"));
3254
	      paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
232 andreas 3255
	      paint.setPen(QPen(mark, 1, Qt::SolidLine));
88 andreas 3256
	   }
3257
	}
222 andreas 3258
 
3259
	// This is the total time, with pauses included, at the lower right
3260
	// corner of the graphic.
88 andreas 3261
	QTime tm(0, 0, 0);
3262
	QString qs;
249 andreas 3263
	tm = tm.addSecs(max_time + ds.getPauseTime());
232 andreas 3264
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
230 andreas 3265
//	paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((max_time >= 3600) ? "hh:mm:ss" : "mm:ss"));
3266
	paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
88 andreas 3267
 
222 andreas 3268
	// Draw the minimal elevation, speed and/or heart rate
218 andreas 3269
	if (max_height > 0.0 || max_speed > 0.0)
88 andreas 3270
	{
222 andreas 3271
	   // left side
218 andreas 3272
	   if (cuType == 1)
222 andreas 3273
	   {
232 andreas 3274
	      paint.setPen(QPen(red, 1, Qt::SolidLine));
218 andreas 3275
	      paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minSpeed));
222 andreas 3276
	   }
218 andreas 3277
	   else
222 andreas 3278
	   {
232 andreas 3279
	      paint.setPen(QPen(barcol, 1, Qt::SolidLine));
218 andreas 3280
	      paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
222 andreas 3281
	   }
88 andreas 3282
	}
3283
 
222 andreas 3284
	if (max_hr > 0 || max_speed > 0.0)
88 andreas 3285
	{
222 andreas 3286
	   // right side
3287
	   if (cuType == 2)
3288
	   {
232 andreas 3289
	      paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3290
	      paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));
3291
	   }
3292
	   else
3293
	   {
232 andreas 3294
	      paint.setPen(QPen(blue, 1, Qt::SolidLine));
222 andreas 3295
	      paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
3296
	   }
88 andreas 3297
	}
3298
 
232 andreas 3299
	paint.setPen(QPen(mark, 1, Qt::SolidLine));
88 andreas 3300
 
3301
	// Grid horizontal
218 andreas 3302
	int factor = 0;
3303
	int target = 0;
3304
 
222 andreas 3305
	if (cuType == 0)	// Elevation and heart rate
218 andreas 3306
	{
222 andreas 3307
	   factor = (meter) ? (maxHeight - minHeight) / (rh / 12) : (maxHr - minHr) / (rh / 12);
3308
	   target = (meter) ? (int)(maxHeight - minHeight) : (maxHr - minHr);
3309
	}
3310
	else if (cuType == 1)	// Speed and heart rate
3311
	{
218 andreas 3312
	   factor = (meter) ? (maxSpeed - minSpeed) / (rh / 12) : (maxHr - minHr) / (rh / 12);
3313
	   target = (meter) ? (int)(maxSpeed - minSpeed) : (maxHr - minHr);
3314
	}
222 andreas 3315
	else			// Elevation and speed
218 andreas 3316
	{
222 andreas 3317
	   factor = (meter) ? (maxHeight - minHeight) / (rh /12) : (maxSpeed - minSpeed) / (rh / 12);
3318
	   target = (meter) ? (int)(maxHeight - minHeight) : (int)(maxSpeed - minSpeed);
218 andreas 3319
	}
3320
 
222 andreas 3321
	// To prevent a division by zero error, we check the <factor>
3322
	if (factor == 0)
3323
	   factor = 1;
3324
 
3325
	// Beside the horizontal part of the grid, we draw the scale on the
3326
	// left and the right side.
88 andreas 3327
	int oldy = height;
3328
 
3329
	for (i = 0; i < target; i++)
3330
	{
3331
	   if (i > 0 && !(i % factor))
3332
	   {
3333
	      x1 = margin_left + 1;
3334
	      x2 = width - margin_right - 1;
3335
	      y1 = y2 = rh - h_tick * i;
3336
 
3337
	      if (y1 < 12)
3338
		 break;
3339
 
3340
	      paint.drawLine(x1, y1, x2, y2);
3341
 
3342
	      if (y1 < (oldy - lineHeight))
3343
	      {
3344
		 if (meter)
3345
		 {
232 andreas 3346
		    paint.setPen(QPen(barcol, 1, Qt::SolidLine));
222 andreas 3347
		    // left side
218 andreas 3348
		    if (cuType == 1)
222 andreas 3349
		    {
232 andreas 3350
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3351
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + i));
3352
		    }
218 andreas 3353
		    else
3354
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));
3355
 
222 andreas 3356
		    // right side
3357
		    if (maxHr > 0 && cuType != 2)
88 andreas 3358
		    {
3359
		       double hrscale = (double)(maxHr - minHr) / (double)target;
232 andreas 3360
		       paint.setPen(QPen(blue, 1, Qt::SolidLine));
88 andreas 3361
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", (int)((double)minHr + hrscale * (double)i)));
3362
		    }
222 andreas 3363
		    else
3364
		    {
3365
		       double spscale = (maxSpeed - minSpeed) / (double)target;
3366
 
3367
		       if (cuType == 2)
232 andreas 3368
			  paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3369
		       else
232 andreas 3370
			  paint.setPen(QPen(blue, 1, Qt::SolidLine));
222 andreas 3371
 
3372
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", (minSpeed + spscale * (double)i)));
3373
		    }
88 andreas 3374
		 }
3375
		 else
3376
		 {
222 andreas 3377
		    // right side
3378
		    if (cuType == 2)
3379
		    {
232 andreas 3380
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3381
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
3382
		    }
3383
		    else
3384
		    {
232 andreas 3385
		       paint.setPen(QPen(blue, 1, Qt::SolidLine));
222 andreas 3386
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
3387
		    }
88 andreas 3388
 
222 andreas 3389
		    // left side
3390
		    if ((cuType == 0 || cuType == 2) && max_height > 0)
88 andreas 3391
		    {
3392
		       double hrscale = (maxHeight - minHeight) / (double)target;
232 andreas 3393
		       paint.setPen(QPen(barcol, 1, Qt::SolidLine));
88 andreas 3394
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + hrscale * (double)i));
3395
		    }
222 andreas 3396
		    else if (max_speed > 0 && cuType == 1)
218 andreas 3397
		    {
3398
		       double hrscale = (maxSpeed - minSpeed) / (double)target;
232 andreas 3399
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3400
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + hrscale * (double)i));
218 andreas 3401
		    }
88 andreas 3402
		}
3403
 
232 andreas 3404
		 paint.setPen(QPen(mark, 1, Qt::SolidLine));
88 andreas 3405
		 oldy = y1;
3406
	      }
3407
	   }
3408
	}
3409
 
168 andreas 3410
	// To make our graphics more beautiful, we draw lines for the
88 andreas 3411
	// heart rate limits and the average heart rate.
222 andreas 3412
	if (max_hr > 0 && cuType != 2)
88 andreas 3413
	{
3414
	int ay1, ay2, ay3, ay4, ay5;
3415
 
3416
	   x1 = margin_left + 1;
3417
	   x2 = width - margin_right - 1;
3418
 
3419
	   if (meter)
3420
	   {
3421
	      double hrscale = rh / (double)(maxHr - minHr);
3422
	      ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
3423
	      ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
3424
	      ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
3425
	      ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
3426
	      ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
3427
	   }
3428
	   else
3429
	   {
3430
	      ay1 = (double)rh - (double)(lower1 - minHr) * h_tick;
3431
	      ay2 = (double)rh - (double)(lower2 - minHr) * h_tick;
3432
	      ay3 = (double)rh - (double)(lower3 - minHr) * h_tick;
3433
	      ay4 = (double)rh - (double)(upper3 - minHr) * h_tick;
3434
	      ay5 = (double)rh - (double)(avg_hr - minHr) * h_tick;
3435
	   }
3436
 
232 andreas 3437
	   paint.setPen(QPen(barcol2, 1, Qt::DashLine));	// color for limits
88 andreas 3438
 
3439
	   if (lower1 > minHr && lower1 < maxHr)
3440
	      paint.drawLine(x1, ay1, x2, ay1);
3441
 
3442
	   if (lower2 > minHr && lower2 < maxHr)
3443
	      paint.drawLine(x1, ay2, x2, ay2);
3444
 
3445
	   if (lower3 > minHr && lower3 < maxHr)
3446
	      paint.drawLine(x1, ay3, x2, ay3);
3447
 
3448
	   if (upper3 > minHr && upper3 < maxHr)
3449
	      paint.drawLine(x1, ay4, x2, ay4);
3450
 
232 andreas 3451
	   paint.setPen(QPen(red, 1, Qt::DashDotLine));	// color for average heart rate
88 andreas 3452
 
3453
	   if (avg_hr > minHr && avg_hr < maxHr)
3454
	      paint.drawLine(x1, ay5, x2, ay5);
3455
	}
3456
 
3457
	// Now we have a grid and we've done the scaling.
3458
	// This is the point where we draw the curves itself.
223 andreas 3459
	// We use different colors to draw the lines:
3460
	//
3461
	// Green: Elevation
3462
	// Red:   Speed
3463
	// Blue   Heart Rate
3464
	//
88 andreas 3465
	x1 = x2 = y1 = y2 = 0;
148 andreas 3466
	int hy1, hy2, hx1, hx2;
222 andreas 3467
	int sy1, sy2, sx1, sx2;
88 andreas 3468
	hy1 = hy2 = hx1 = hx2 = 0;
222 andreas 3469
	sy1 = sy2 = sx1 = sx2 = 0;
169 andreas 3470
	int hEc = 0;
3471
	i = 0;
3472
	AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
170 andreas 3473
	avgHfirst = avgHlast = 0;
169 andreas 3474
	// To even the surface lines, we store every altitude in the
3475
	// memory, by building a chain. Then, if the user has set in the
3476
	// settings (Contour == true), we will even the line by calculating
3477
	// the average of 10 messure points and setting every value to the
3478
	// average, who is up or down more than 2 meters from the average.
3479
	while ((point = ds.getPoint(i)) != 0)
3480
	{
3481
	   if (point->alt > 20000.0 || point->alt < -1000.0)
3482
	   {
3483
	      i++;
3484
	      continue;
3485
	   }
168 andreas 3486
 
169 andreas 3487
	   if (!avgHeight)
3488
	   {
3489
	      avgHeight = new AVGHEIGHT;
3490
	      avgHeight->alt = point->alt;
3491
	      avgHeight->pos = hEc;
3492
	      avgHeight->prev = 0;
3493
	      avgHeight->next = 0;
3494
	      avgHakt = avgHeight;
3495
	      avgHfirst = avgHeight;
3496
	   }
3497
	   else
3498
	   {
3499
	      avgHakt = new AVGHEIGHT;
3500
	      avgHakt->alt = point->alt;
3501
	      avgHakt->pos = hEc;
3502
	      avgHakt->next = 0;
3503
	      avgHakt->prev = avgHeight;
3504
	      avgHeight->next = avgHakt;
3505
	      avgHeight = avgHakt;
3506
	   }
3507
 
3508
	   // FIXME: Currently we can not draw below 0 meters, because the
3509
	   // base line is always 0!
222 andreas 3510
	   if (avgHakt->alt < minHeight)
3511
	      avgHakt->alt = minHeight;
169 andreas 3512
 
3513
	   hEc++;
3514
	   i++;
3515
	}
3516
 
3517
	avgHlast = avgHeight;
3518
	// If wanted, even the lines
218 andreas 3519
	if (Contour && hEc > 0 && cuType != 0)
169 andreas 3520
	{
213 andreas 3521
	double alt[100], avg, avg1, avg2, avg3, avg4;
3522
	int a, pos;
169 andreas 3523
 
213 andreas 3524
	   for (i = 0; i < (hEc + 100); i += 100)
169 andreas 3525
	   {
213 andreas 3526
	      avg = avg1 = avg2 = avg3 = avg4 = 0.0;
3527
	      pos = 0;
169 andreas 3528
 
213 andreas 3529
	      for (a = 0; a < 100; a++)
169 andreas 3530
	      {
3531
		 alt[a] = getAvgAlt(avgHfirst, i + a);
3532
		 avg += alt[a];
213 andreas 3533
 
3534
		 if (a < 25)
3535
		    avg1 += alt[a];
3536
		 else if (a < 50)
3537
		    avg2 += alt[a];
3538
		 else if (a < 75)
3539
		    avg3 += alt[a];
3540
		 else
3541
		    avg4 += alt[a];
169 andreas 3542
	      }
3543
 
213 andreas 3544
	      if ((i + 100) >= hEc)
169 andreas 3545
		 avg /= (double)(hEc - i) + 1.0;
3546
	      else
213 andreas 3547
		 avg /= 100.0;
169 andreas 3548
 
213 andreas 3549
	      for (a = 0; a < 100; a++)
169 andreas 3550
	      {
3551
		 if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
3552
		 {
3553
		    if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
3554
		       avgHakt->alt = avg;
3555
		 }
3556
	      }
3557
	   }
3558
	}
3559
 
222 andreas 3560
	// plot the elevation, speed and/or heart rate. Depends on <cuType>)
168 andreas 3561
	i = 0;
169 andreas 3562
	int j = 0;
218 andreas 3563
	POINT *oldPoint = 0;
222 andreas 3564
	double speed = 0.0;	// calculated speed
3565
	bool pause = false;	// filter pause out of speed
3566
	unsigned long t1, t2;
3567
	t1 = t2 = 0;
218 andreas 3568
 
88 andreas 3569
	while ((point = ds.getPoint(i)) != 0)
3570
	{
218 andreas 3571
	   if (!oldPoint)
3572
	      oldPoint = point;
3573
 
88 andreas 3574
	   // calculate the y position based on the time
3575
	   qt = garmin_dtime(point->time);
3576
	   secs = zeit.secsTo(qt->time());
3577
	   delete qt;
3578
	   x2 = secs * w_tick + margin_left + 1;
3579
	   hx2 = x2;
222 andreas 3580
	   sx2 = x2;
88 andreas 3581
 
3582
	   if (x1 == 0)
3583
	      x1 = x2;
3584
 
3585
	   if (hx1 == 0)
3586
	      hx1 = hx2;
3587
 
222 andreas 3588
	   if (sx1 == 0)
3589
	      sx1 = sx2;
3590
 
3591
	   // The speed is not very exact, because smallest time is
3592
	   // one second. This allows a maximum error of 99 hundredths
3593
	   // of a second, what is very close to one second. Because of
3594
	   // this, speed seems to hop for every messure point. This
3595
	   // looks ugly, but currently I don't know how to make it
3596
	   // better.
3597
	   if (cuType == 1 || cuType == 2)	// Draw speed?
88 andreas 3598
	   {
218 andreas 3599
	      double dist;
3600
	      double sc;
168 andreas 3601
 
222 andreas 3602
	      if (!pause && point->distance > 1.0e10)
218 andreas 3603
	      {
222 andreas 3604
		 pause = true;
3605
		 t1 = point->time;
3606
	      }
3607
	      else if (pause)
3608
	      {
3609
		 pause = false;
3610
		 t2 = point->time;
3611
		 i += 2;
3612
		 continue;
3613
	      }
3614
 
3615
	      if (point->distance >= 0.1 && point->distance < 1.0e10)
3616
	      {
218 andreas 3617
		 dist = point->distance - oldPoint->distance;
3618
		 sc = point->time - oldPoint->time;
223 andreas 3619
		 LAP *runde = ds.getLapT (point->time);
222 andreas 3620
 
3621
		 if (t2 > t1)
3622
		 {
3623
		    sc -= t2 - t1;
3624
 
3625
		    if (sc <= 0.0)
3626
		       sc = 1.0;		// at least 1 second!
3627
		 }
3628
 
218 andreas 3629
		 speed = (dist / sc) * 3.6;
168 andreas 3630
 
223 andreas 3631
		 if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
3632
		    speed = runde->max_speed * 3.6;
3633
 
218 andreas 3634
		 if (Units == 1)
3635
		    speed /= 1.609344;
3636
 
222 andreas 3637
		 if (speed < minSpeed || speed > 400.0)
3638
		    speed = minSpeed;
218 andreas 3639
 
222 andreas 3640
		 if ((meter && cuType == 1) || (!meter && cuType == 2))
218 andreas 3641
		    y2 = (double)rh - (speed - minSpeed) * h_tick;
3642
		 else
3643
		 {
3644
		    double hrscale = rh / (maxSpeed - minSpeed);
3645
		    y2 = (double)rh - (speed - minSpeed) * hrscale;
3646
		 }
3647
 
3648
		 if (y1 == 0)
3649
		    y1 = y2;
3650
 
232 andreas 3651
		 paint.setPen(QPen(red, 1, Qt::SolidLine));
218 andreas 3652
		 paint.drawLine(x1, y1, x2, y2);
3653
		 y1 = y2;
3654
		 x1 = x2;
222 andreas 3655
		 t1 = t2 = 0;
3656
		 oldPoint = point;
218 andreas 3657
	      }
3658
	   }
222 andreas 3659
 
3660
	   if (cuType == 0 || cuType == 2)		// Draw elevation?
218 andreas 3661
	   {
3662
	      if (point->alt < 20000.0 && point->alt > -1000.0)
88 andreas 3663
	      {
218 andreas 3664
	      double alt = getAvgAlt(avgHfirst, j);
88 andreas 3665
 
218 andreas 3666
		 j++;
3667
 
3668
		 if (meter)
222 andreas 3669
		    sy2 = (double)rh - (alt - minHeight) * h_tick;
218 andreas 3670
		 else
3671
		 {
3672
		    double hrscale = rh / (maxHeight - minHeight);
222 andreas 3673
		    sy2 = (double)rh - (alt - minHeight) * hrscale;
218 andreas 3674
		 }
3675
 
222 andreas 3676
		 if (sy1 == 0)
3677
		    sy1 = sy2;
218 andreas 3678
 
232 andreas 3679
		 paint.setPen(QPen(barcol, 1, Qt::SolidLine));
222 andreas 3680
		 paint.drawLine(sx1, sy1, sx2, sy2);
3681
		 sy1 = sy2;
3682
		 sx1 = sx2;
218 andreas 3683
	      }
88 andreas 3684
	   }
3685
 
222 andreas 3686
	   if (point->heart_rate > 0 && cuType < 2)	// Draw heart rate?
88 andreas 3687
	   {
3688
	      if (meter)
3689
	      {
3690
		 double hrscale = rh / (double)(maxHr - minHr);
3691
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * hrscale;
3692
	      }
3693
	      else
3694
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * h_tick;
3695
 
3696
	      if (hy1 == 0)
3697
		 hy1 = hy2;
3698
 
232 andreas 3699
	      paint.setPen(QPen(blue, 1, Qt::SolidLine));
88 andreas 3700
	      paint.drawLine(hx1, hy1, hx2, hy2);
3701
	      hy1 = hy2;
3702
	      hx1 = hx2;
3703
	   }
3704
 
3705
	   i++;
3706
	}
3707
 
3708
	paint.end();
245 andreas 3709
	ui_sportwatcherWidgetBase.imgProfile->setPixmap(pmProfile);
88 andreas 3710
 
169 andreas 3711
	// free the chain of altitudes
3712
	avgHakt = avgHfirst;
3713
 
3714
	while (avgHakt)
3715
	{
3716
	   avgHeight = avgHakt->next;
3717
	   delete avgHakt;
3718
	   avgHakt = avgHeight;
3719
	}
3720
 
246 andreas 3721
	DIRTY = false;
88 andreas 3722
}
3723
 
250 andreas 3724
void sportwatcherWidget::showThreeCurve()
3725
{
3726
QPainter ptHR, ptElevation, ptSpeed;
3727
int width, height, wdHR, htHR, wdElev, htElev, wdSpeed, htSpeed;
3728
int i, secs, cuType;
3729
int lineHeight, margin_left, margin_right, margin_bottom;
3730
int x1, y1, x2, y2;		// Coordinates
274 andreas 3731
bool meterHR;
250 andreas 3732
double maxHeight, minHeight, maxSpeed, minSpeed;
3733
int maxHr, minHr, rh, rhHR, rhElev, rhSpeed;
3734
POINT *point;
3735
RUN_NODE *rn;
3736
LAP *lp;
3737
double wtiHR, htiHR, wtiElev, htiElev, wtiSpeed, htiSpeed;
3738
double w_tick, h_tick;		// Number of pixels one "tick" has;
3739
				// This depends on the width and height
3740
				// of the image.
3741
	// First we draw a grid based on the min and max
3742
	// values detected in the function showLap(). In case
3743
	// all values are 0, we exit here.
3744
	if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
3745
	   return;
3746
 
3747
	if (!DIRTY || curTab != 2)
3748
	   return;
3749
 
3750
	// Get the dimensions of the available draw area
3751
	// First for heart rate
3752
	wdHR = ui_sportwatcherWidgetBase.grHR->width() - 2;
3753
	htHR = ui_sportwatcherWidgetBase.grHR->height();
3754
	pmHR = QPixmap(wdHR, htHR);
3755
	// Then for elevation
3756
	wdElev = ui_sportwatcherWidgetBase.grElevation->width() - 2;
3757
	htElev = ui_sportwatcherWidgetBase.grElevation->height();
3758
	pmElevation = QPixmap(wdElev, htElev);
3759
	// And at last for speed
3760
	wdSpeed = ui_sportwatcherWidgetBase.grSpeed->width() - 2;
3761
	htSpeed = ui_sportwatcherWidgetBase.grSpeed->height();
3762
	pmSpeed = QPixmap(wdSpeed, htSpeed);
3763
	// Initialize QPainter
3764
	ptHR.begin(&pmHR);
3765
	ptElevation.begin(&pmElevation);
3766
	ptSpeed.begin(&pmSpeed);
3767
 
3768
	// we need a somewhat bigger area to draw our curves than
3769
	// we have with the real min and max values.
3770
	if (max_height > 0.0)
3771
	{
3772
	double add = (max_height - min_height) / 100.0 * 5.0;	// Percent
3773
 
3774
	   maxHeight = max_height + add;
3775
	   minHeight = min_height - add;
3776
 
3777
	   if (minHeight < 0.0)		// make sure, we are not too deep
3778
	      minHeight = 0.0;
3779
	}
3780
	else
3781
	   maxHeight = minHeight = 0.0;
3782
 
3783
	if (max_speed > 0.0)
3784
	{
3785
	double add = (max_speed - min_speed) / 100.0 * 5.0;	// Percent
3786
 
3787
	   maxSpeed = max_speed + add;
3788
	   minSpeed = min_speed - add;
3789
 
3790
	   if (minSpeed < 0.0)		// make sure, we are not too deep
3791
	      minSpeed = 0.0;
3792
	}
3793
	else
3794
	   maxSpeed = minSpeed = 0.0;
3795
 
3796
	if (max_hr > 0)
3797
	{
3798
	   maxHr = max_hr + 10;
3799
	   minHr = min_hr - 10;
3800
 
3801
	   if (minHr < 0)
3802
	      minHr = 0;
3803
	}
3804
	else
3805
	   maxHr = minHr = 0;
3806
 
3807
	// Define colors
3808
	QColor background(220, 220, 220);	// Background of graphic
3809
	QColor mark(255, 255, 255);		// Lines inside curve area
3810
	QColor hlight(180, 180, 180);		// area of current lap
3811
	hlight.setAlpha(128);			// 50% transparent
3812
	QColor frame(0, 0, 0);			// Text and borders
3813
	QColor barcol(151, 190, 13);		// heart rate
3814
	QColor barcol2(190, 151, 13);		// height over NN
3815
	QColor red(220, 128, 128);		// speed
3816
	QColor blue(0, 0, 240);
3817
	QFont fntNormal("Helvetica");
3818
//	QFont fntBold("Helvetica", 10, QFont::Bold);
3819
	fntNormal.setPixelSize(10);
3820
	fntNormal.setStyleHint(QFont::Helvetica);
3821
//	fntBold.setPixelSize(10);
3822
//	fntBold.setStyleHint(QFont::Helvetica);
3823
	// Calculate ticks
3824
	margin_left = 1;
3825
	margin_right = 40;
3826
	margin_bottom = 12;
3827
	lineHeight = 10;
3828
//	rh = height - margin_bottom - 1;
3829
 
3830
	// Calculate the ticks for width and height for every draw area
3831
	for (i = 0; i < 3; i++)
3832
	{
3833
	   if (i == 2)	// Speed?
3834
	   {
3835
	      rhSpeed = htSpeed - margin_bottom - 1;
3836
	      wtiSpeed = (double)(wdSpeed - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
3837
	      htiSpeed = (double)rhSpeed / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
3838
	   }
3839
	   else if (i == 1)	// Elevation?
3840
	   {
3841
	      rhElev = htElev - margin_bottom - 1;
3842
	      wtiElev = (double)(wdElev - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
3843
	      htiElev = (double)rhElev / (maxHeight - minHeight);		// 1 tick = 1 meter
3844
	   }
3845
	   else			// heart rate
3846
	   {
3847
	      rhHR = htHR - margin_bottom - 1;
3848
	      wtiHR = (double)(wdHR - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
3849
	      htiHR = (double)rhHR / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
3850
	   }
3851
	}
3852
 
3853
	// Fill background with background colors
3854
	ptHR.fillRect(0, 0, wdHR + 4, htHR + 4, background);
3855
	ptElevation.fillRect(0, 0, wdElev + 4, htElev + 4, background);
3856
	ptSpeed.fillRect(0, 0, wdSpeed + 4, htSpeed + 4, background);
3857
	// Draw a grid with markers at every 10 minutes
3858
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
3859
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
3860
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
3861
	// Bottom border line
3862
	x1 = margin_left;
3863
 
3864
	y1 = htHR - margin_bottom;
3865
	x2 = wdHR - margin_right;
3866
	y2 = y1;
3867
	ptHR.drawLine(x1, y1, x2, y2);
3868
 
3869
	y1 = htElev - margin_bottom;
3870
	x2 = wdElev - margin_right;
3871
	y2 = y1;
3872
	ptElevation.drawLine(x1, y1, x2, y2);
3873
 
3874
	y1 = htSpeed - margin_bottom;
3875
	x2 = wdSpeed - margin_right;
3876
	y2 = y1;
3877
	ptSpeed.drawLine(x1, y1, x2, y2);
3878
 
3879
	// Left border line
3880
	x1 = x2 = margin_left;
3881
	y1 = 2;
3882
	y2 = htHR - margin_bottom;
3883
	ptHR.drawLine(x1, y1, x2, y2);
3884
 
3885
	y2 = htElev - margin_bottom;
3886
	ptElevation.drawLine(x1, y1, x2, y2);
3887
 
3888
	y2 = htSpeed - margin_bottom;
3889
	ptSpeed.drawLine(x1, y1, x2, y2);
3890
 
3891
	// right border line
3892
	x1 = x2 = wdHR - margin_right;
3893
	ptHR.drawLine(x1, y1, x2, y2);
3894
 
3895
	x1 = x2 = wdElev - margin_right;
3896
	ptElevation.drawLine(x1, y1, x2, y2);
3897
 
3898
	x1 = x2 = wdSpeed - margin_right;
3899
	ptSpeed.drawLine(x1, y1, x2, y2);
3900
 
3901
	// Draw some darker lines to show the laps, if we have one
3902
	QDateTime *qt;
3903
	QTime zeit = StartTime.time();
3904
	rn = ds.getRunNode();
3905
	ptHR.setPen(QPen(hlight, 1, Qt::SolidLine));
3906
	ptElevation.setPen(QPen(hlight, 1, Qt::SolidLine));
3907
	ptSpeed.setPen(QPen(hlight, 1, Qt::SolidLine));
3908
 
3909
	for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
3910
	{
3911
	   if ((lp = ds.getLap(i)) == NULL)
3912
	      continue;
3913
 
3914
	   qt = garmin_dtime(lp->start_time);
3915
	   secs = zeit.secsTo(qt->time());
3916
	   delete qt;
3917
	   // heart rate
3918
	   x1 = secs * wtiHR + margin_left + 1;
3919
	   ptHR.drawLine(x1, 2, x1, htHR - margin_bottom);
3920
	   // Elevation
3921
	   x1 = secs * wtiElev + margin_left + 1;
3922
	   ptElevation.drawLine(x1, 2, x1, htElev - margin_bottom);
3923
	   // Speed
3924
	   x1 = secs * wtiSpeed + margin_left + 1;
3925
	   ptSpeed.drawLine(x1, 2, x1, htSpeed - margin_bottom);
3926
	}
3927
 
3928
	// Grid vertical
3929
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
3930
	ptHR.setFont(fntNormal);
3931
	ptHR.drawText(margin_left, htHR - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
3932
 
3933
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
3934
	ptElevation.setFont(fntNormal);
3935
	ptElevation.drawText(margin_left, htElev - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
3936
 
3937
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
3938
	ptSpeed.setFont(fntNormal);
3939
	ptSpeed.drawText(margin_left, htSpeed - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
3940
 
3941
	ptHR.save();
3942
	ptHR.rotate(270);
3943
	ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
3944
 
3945
	ptElevation.save();
3946
	ptElevation.rotate(270);
3947
	ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
3948
 
3949
	ptSpeed.save();
3950
	ptSpeed.rotate(270);
3951
	ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
3952
 
3953
	// Information on right side
3954
	ptHR.drawText((htHR + 4) * -1, wdHR - 1 - lineHeight, htHR - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
3955
	ptElevation.drawText((htElev + 4) * -1, wdElev - 1 - lineHeight, htElev - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
3956
	ptSpeed.drawText((htSpeed + 4) * -1, wdSpeed - 1 - lineHeight, htSpeed - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
3957
 
3958
	// restore to normal
3959
	ptHR.restore();
3960
	ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
3961
 
3962
	ptElevation.restore();
3963
	ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
3964
 
3965
	ptSpeed.restore();
3966
	ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
3967
	// Draw the time scale on the bottom of the graphic
3968
	for (i = 0; (unsigned int)i < (max_time + ds.getPauseTime()); i++)
3969
	{
3970
	bool loop = false;
3971
 
3972
	   if (i > 0 && !(i % 600))	// every 10 minutes
3973
	   {
3974
	      for (int j = 0; j < 3; j++)
3975
	      {
3976
		 switch (j)
3977
		 {
3978
		    case 0: w_tick = wtiHR; width = wdHR; height = htHR; break;
3979
		    case 1: w_tick = wtiElev; width = wdElev; height = htElev; break;
3980
		    case 2: w_tick = wtiSpeed; width = wdSpeed; height = htSpeed; break;
3981
		 }
3982
 
3983
		 x1 = x2 = margin_left + w_tick * i;
3984
 
3985
		 if (x1 == (width - margin_right - 25))
3986
		 {
3987
		    loop = true;
3988
		    break;
3989
		 }
3990
 
3991
		 y1 = 2;
3992
		 y2 = height - margin_bottom;
3993
		 QTime tm(0, 0, 0);
3994
		 tm = tm.addSecs(i);
3995
 
3996
		 if (j == 0)
3997
		 {
3998
		    ptHR.drawLine(x1, y1, x2, y2);
3999
		    ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
4000
		    ptHR.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
4001
		    ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
4002
		 }
4003
		 else if (j == 1)
4004
		 {
4005
		    ptElevation.drawLine(x1, y1, x2, y2);
4006
		    ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
4007
		    ptElevation.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
4008
		    ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
4009
		 }
4010
		 else
4011
		 {
4012
		    ptSpeed.drawLine(x1, y1, x2, y2);
4013
		    ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
4014
		    ptSpeed.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
4015
		    ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
4016
		 }
4017
	      }
4018
 
4019
	      if (loop)
4020
	      {
4021
		 loop = false;
4022
		 continue;
4023
	      }
4024
	   }
4025
	}
4026
 
4027
	// This is the total time, with pauses included, at the lower right
4028
	// corner of the graphic.
4029
	QTime tm(0, 0, 0);
4030
	QString qs;
4031
	tm = tm.addSecs(max_time + ds.getPauseTime());
4032
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
4033
	ptHR.drawText(wdHR - margin_right - 25, htHR - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
4034
 
4035
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
4036
	ptElevation.drawText(wdElev - margin_right - 25, htElev - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
4037
 
4038
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
4039
	ptSpeed.drawText(wdSpeed - margin_right - 25, htSpeed - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
4040
 
4041
	// Draw the minimal elevation, speed and heart rate
4042
	// Heart rate
4043
	ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
4044
	ptHR.drawText(wdHR - margin_right + 2, htHR - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
4045
	// Elevation
4046
	ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
4047
	ptElevation.drawText(wdElev - margin_right + 2, htElev - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
4048
	// Speed
4049
	ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
4050
	ptSpeed.drawText(wdSpeed - margin_right + 2, htSpeed - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));
4051
 
4052
	ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
4053
	ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
4054
	ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
4055
 
4056
	// Grid horizontal
4057
	int factor = 0;
4058
	int target = 0;
4059
 
4060
	for (int j = 0; j < 3; j++)
4061
	{
4062
	   switch (j)
4063
	   {
4064
	      case 0: width = wdHR; height = htHR; w_tick = wtiHR; h_tick = htiHR; rh = rhHR;
4065
		      factor = (maxHr - minHr) / (rhHR / 12);
4066
		      target = (maxHr - minHr);
4067
	      break;
4068
 
4069
	      case 1: width = wdElev; height = htElev; w_tick = wtiElev; h_tick = htiElev; rh = rhElev;
4070
		      factor = (maxHeight - minHeight) / (rhElev / 12);
4071
		      target = (int)(maxHeight - minHeight);
4072
	      break;
4073
 
4074
	      case 2: width = wdSpeed; height = htSpeed; w_tick = wtiSpeed; h_tick = htiSpeed; rh = rhSpeed;
4075
		      factor = (maxSpeed - minSpeed) / (rhSpeed / 12);
4076
		      target = (int)(maxSpeed - minSpeed);
4077
	      break;
4078
	   }
4079
 
4080
	   // To prevent a division by zero error, we check the <factor>
4081
	   if (factor == 0)
4082
	      factor = 1;
4083
 
4084
	   // Beside the horizontal part of the grid, we draw the scale on the
4085
	   // the right side too.
4086
	   int oldy = height;
4087
 
4088
	   for (i = 0; i < target; i++)
4089
	   {
4090
	      if (i > 0 && !(i % factor))
4091
	      {
4092
		 x1 = margin_left + 1;
4093
		 x2 = width - margin_right - 1;
4094
		 y1 = y2 = rh - h_tick * i;
4095
 
4096
		 if (y1 < 12)
4097
		    break;
4098
 
4099
		 switch (j)
4100
		 {
4101
		    case 0:
4102
		       ptHR.drawLine(x1, y1, x2, y2);
4103
		       ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
4104
		       ptHR.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
4105
		       ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
4106
		    break;
4107
 
4108
		    case 1:
4109
		       ptElevation.drawLine(x1, y1, x2, y2);
4110
		       ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
4111
		       ptElevation.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));
4112
		       ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
4113
		    break;
4114
 
4115
		    case 2:
4116
		       ptSpeed.drawLine(x1, y1, x2, y2);
4117
		       ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
4118
		       ptSpeed.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
4119
		       ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
4120
		    break;
4121
		 }
4122
 
4123
		 oldy = y1;
4124
	      }
4125
	   }
4126
	}
4127
 
4128
	// To make our graphics more beautiful, we draw lines for the
4129
	// heart rate limits and the average heart rate.
4130
	if (max_hr > 0)
4131
	{
4132
	int ay1, ay2, ay3, ay4, ay5;
4133
 
4134
	   x1 = margin_left + 1;
4135
	   x2 = wdHR - margin_right - 1;
4136
 
4137
	   if (meterHR)
4138
	   {
4139
	      double hrscale = rh / (double)(maxHr - minHr);
4140
	      ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
4141
	      ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
4142
	      ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
4143
	      ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
4144
	      ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
4145
	   }
4146
	   else
4147
	   {
4148
	      ay1 = (double)rh - (double)(lower1 - minHr) * htiHR;
4149
	      ay2 = (double)rh - (double)(lower2 - minHr) * htiHR;
4150
	      ay3 = (double)rh - (double)(lower3 - minHr) * htiHR;
4151
	      ay4 = (double)rh - (double)(upper3 - minHr) * htiHR;
4152
	      ay5 = (double)rh - (double)(avg_hr - minHr) * htiHR;
4153
	   }
4154
 
4155
	   ptHR.setPen(QPen(barcol2, 1, Qt::DashLine));	// color for limits
4156
 
4157
	   if (lower1 > minHr && lower1 < maxHr)
4158
	      ptHR.drawLine(x1, ay1, x2, ay1);
4159
 
4160
	   if (lower2 > minHr && lower2 < maxHr)
4161
	      ptHR.drawLine(x1, ay2, x2, ay2);
4162
 
4163
	   if (lower3 > minHr && lower3 < maxHr)
4164
	      ptHR.drawLine(x1, ay3, x2, ay3);
4165
 
4166
	   if (upper3 > minHr && upper3 < maxHr)
4167
	      ptHR.drawLine(x1, ay4, x2, ay4);
4168
 
4169
	   ptHR.setPen(QPen(red, 1, Qt::DashDotLine));	// color for average heart rate
4170
 
4171
	   if (avg_hr > minHr && avg_hr < maxHr)
4172
	      ptHR.drawLine(x1, ay5, x2, ay5);
4173
	}
4174
 
4175
	// Now we have a grid and we've done the scaling.
4176
	// This is the point where we draw the curves itself.
4177
	// We use different colors to draw the lines:
4178
	//
4179
	// Green: Elevation
4180
	// Red:   Speed
4181
	// Blue   Heart Rate
4182
	//
4183
	x1 = x2 = y1 = y2 = 0;
4184
	int hy1, hy2, hx1, hx2;
4185
	int sy1, sy2, sx1, sx2;
4186
	hy1 = hy2 = hx1 = hx2 = 0;
4187
	sy1 = sy2 = sx1 = sx2 = 0;
4188
	int hEc = 0;
4189
	i = 0;
4190
	AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
4191
	avgHfirst = avgHlast = 0;
4192
	// To even the surface lines, we store every altitude in the
4193
	// memory, by building a chain. Then, if the user has set in the
4194
	// settings (Contour == true), we will even the line by calculating
4195
	// the average of 10 messure points and setting every value to the
4196
	// average, who is up or down more than 2 meters from the average.
4197
	while ((point = ds.getPoint(i)) != 0)
4198
	{
4199
	   if (point->alt > 20000.0 || point->alt < -1000.0)
4200
	   {
4201
	      i++;
4202
	      continue;
4203
	   }
4204
 
4205
	   if (!avgHeight)
4206
	   {
4207
	      avgHeight = new AVGHEIGHT;
4208
	      avgHeight->alt = point->alt;
4209
	      avgHeight->pos = hEc;
4210
	      avgHeight->prev = 0;
4211
	      avgHeight->next = 0;
4212
	      avgHakt = avgHeight;
4213
	      avgHfirst = avgHeight;
4214
	   }
4215
	   else
4216
	   {
4217
	      avgHakt = new AVGHEIGHT;
4218
	      avgHakt->alt = point->alt;
4219
	      avgHakt->pos = hEc;
4220
	      avgHakt->next = 0;
4221
	      avgHakt->prev = avgHeight;
4222
	      avgHeight->next = avgHakt;
4223
	      avgHeight = avgHakt;
4224
	   }
4225
 
4226
	   // FIXME: Currently we can not draw below 0 meters, because the
4227
	   // base line is always 0!
4228
	   if (avgHakt->alt < minHeight)
4229
	      avgHakt->alt = minHeight;
4230
 
4231
	   hEc++;
4232
	   i++;
4233
	}
4234
 
4235
	avgHlast = avgHeight;
4236
	// If wanted, even the lines
4237
	if (Contour && hEc > 0 && cuType != 0)
4238
	{
4239
	double alt[100], avg, avg1, avg2, avg3, avg4;
4240
	int a, pos;
4241
 
4242
	   for (i = 0; i < (hEc + 100); i += 100)
4243
	   {
4244
	      avg = avg1 = avg2 = avg3 = avg4 = 0.0;
4245
	      pos = 0;
4246
 
4247
	      for (a = 0; a < 100; a++)
4248
	      {
4249
		 alt[a] = getAvgAlt(avgHfirst, i + a);
4250
		 avg += alt[a];
4251
 
4252
		 if (a < 25)
4253
		    avg1 += alt[a];
4254
		 else if (a < 50)
4255
		    avg2 += alt[a];
4256
		 else if (a < 75)
4257
		    avg3 += alt[a];
4258
		 else
4259
		    avg4 += alt[a];
4260
	      }
4261
 
4262
	      if ((i + 100) >= hEc)
4263
		 avg /= (double)(hEc - i) + 1.0;
4264
	      else
4265
		 avg /= 100.0;
4266
 
4267
	      for (a = 0; a < 100; a++)
4268
	      {
4269
		 if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
4270
		 {
4271
		    if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
4272
		       avgHakt->alt = avg;
4273
		 }
4274
	      }
4275
	   }
4276
	}
4277
 
4278
	// plot the elevation, speed and/or heart rate. Depends on <cuType>)
4279
	i = 0;
4280
	int j = 0;
4281
	POINT *oldPoint = 0;
4282
	double speed = 0.0;	// calculated speed
4283
	bool pause = false;	// filter pause out of speed
4284
	unsigned long t1, t2;
4285
	t1 = t2 = 0;
4286
 
4287
	while ((point = ds.getPoint(i)) != 0)
4288
	{
4289
	bool loop = false;
4290
 
4291
	   if (!oldPoint)
4292
	      oldPoint = point;
4293
 
4294
	   // calculate the y position based on the time
4295
	   qt = garmin_dtime(point->time);
4296
	   secs = zeit.secsTo(qt->time());
4297
	   delete qt;
4298
 
4299
	   for (int c = 0; c < 3; c++)
4300
	   {
4301
	      if (c == 0)
4302
	      {
4303
		 x2 = secs * wtiHR + margin_left + 1;
4304
		 hx2 = x2;
4305
		 sx2 = x2;
4306
 
4307
		 if (x1 == 0)
4308
		    x1 = x2;
4309
 
4310
		 if (hx1 == 0)
4311
		    hx1 = hx2;
4312
 
4313
		 if (sx1 == 0)
4314
		    sx1 = sx2;
4315
	      }
4316
	      else if (c == 1)
4317
	      {
4318
		 x2 = secs * wtiElev + margin_left + 1;
4319
		 hx2 = x2;
4320
		 sx2 = x2;
4321
 
4322
		 if (x1 == 0)
4323
		    x1 = x2;
4324
 
4325
		 if (hx1 == 0)
4326
		    hx1 = hx2;
4327
 
4328
		 if (sx1 == 0)
4329
		    sx1 = sx2;
4330
	      }
4331
	      else
4332
	      {
4333
		 x2 = secs * wtiSpeed + margin_left + 1;
4334
		 hx2 = x2;
4335
		 sx2 = x2;
4336
 
4337
		 if (x1 == 0)
4338
		    x1 = x2;
4339
 
4340
		 if (hx1 == 0)
4341
		    hx1 = hx2;
4342
 
4343
		 if (sx1 == 0)
4344
		    sx1 = sx2;
4345
	      }
4346
	      // The speed is not very exact, because smallest time is
4347
	      // one second. This allows a maximum error of 99 hundredths
4348
	      // of a second, what is very close to one second. Because of
4349
	      // this, speed seems to hop for every messure point. This
4350
	      // looks ugly, but currently I don't know how to make it
4351
	      // better.
4352
	      if (c == 2)	// Draw speed?
4353
	      {
4354
	      double dist;
4355
	      double sc;
4356
 
4357
		 if (!pause && point->distance > 1.0e10)
4358
		 {
4359
		    pause = true;
4360
		    t1 = point->time;
4361
		 }
4362
		 else if (pause)
4363
		 {
4364
		    pause = false;
4365
		    t2 = point->time;
4366
		    i += 2;
4367
		    loop = true;
4368
		 }
4369
 
4370
		 if (point->distance >= 0.1 && point->distance < 1.0e10)
4371
		 {
4372
		    dist = point->distance - oldPoint->distance;
4373
		    sc = point->time - oldPoint->time;
4374
		    LAP *runde = ds.getLapT (point->time);
4375
 
4376
		    if (t2 > t1)
4377
		    {
4378
		       sc -= t2 - t1;
4379
 
4380
		       if (sc <= 0.0)
4381
			  sc = 1.0;		// at least 1 second!
4382
		    }
4383
 
4384
		    speed = (dist / sc) * 3.6;
4385
 
4386
		    if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
4387
		       speed = runde->max_speed * 3.6;
4388
 
4389
		    if (Units == 1)
4390
		       speed /= 1.609344;
4391
 
4392
		    if (speed < minSpeed || speed > 400.0)
4393
		       speed = minSpeed;
4394
 
4395
		    y2 = (double)rh - (speed - minSpeed) * htiSpeed;
4396
 
4397
		    if (y1 == 0)
4398
		       y1 = y2;
4399
 
4400
		    ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
4401
		    ptSpeed.drawLine(x1, y1, x2, y2);
4402
		    y1 = y2;
4403
		    x1 = x2;
4404
		    t1 = t2 = 0;
4405
		    oldPoint = point;
4406
		 }
4407
	      }
4408
 
4409
	      if (c == 1)		// Draw elevation?
4410
	      {
4411
		 if (point->alt < 20000.0 && point->alt > -1000.0)
4412
		 {
4413
		 double alt = getAvgAlt(avgHfirst, j);
4414
 
4415
		    j++;
4416
 
4417
		    sy2 = (double)rh - (alt - minHeight) * htiElev;
4418
 
4419
		    if (sy1 == 0)
4420
		       sy1 = sy2;
4421
 
4422
		    ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
4423
		    ptElevation.drawLine(sx1, sy1, sx2, sy2);
4424
		    sy1 = sy2;
4425
		    sx1 = sx2;
4426
		 }
4427
	      }
4428
 
4429
	      if (point->heart_rate > 0 && c == 0)	// Draw heart rate?
4430
	      {
4431
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * htiHR;
4432
 
4433
		 if (hy1 == 0)
4434
		    hy1 = hy2;
4435
 
4436
		 ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
4437
		 ptHR.drawLine(hx1, hy1, hx2, hy2);
4438
		 hy1 = hy2;
4439
		 hx1 = hx2;
4440
	      }
4441
	   }
4442
 
4443
	   if (loop)
4444
	   {
4445
	      loop = false;
4446
	      continue;
4447
	   }
4448
 
4449
	   i++;
4450
	}
4451
 
4452
	ptHR.end();
4453
	ptElevation.end();
4454
	ptSpeed.end();
4455
	ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4456
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4457
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4458
 
4459
	// free the chain of altitudes
4460
	avgHakt = avgHfirst;
4461
 
4462
	while (avgHakt)
4463
	{
4464
	   avgHeight = avgHakt->next;
4465
	   delete avgHakt;
4466
	   avgHakt = avgHeight;
4467
	}
4468
 
4469
	DIRTY = false;
4470
}
4471
 
169 andreas 4472
double sportwatcherWidget::getAvgAlt(AVGHEIGHT *avgHeight, int pos)
4473
{
4474
AVGHEIGHT *akt;
4475
 
170 andreas 4476
	if (!avgHeight)
4477
	   return 0.0;
4478
 
169 andreas 4479
	akt = avgHeight;
4480
 
4481
	while (akt)
4482
	{
4483
	   if (akt->pos == pos)
4484
	      return akt->alt;
4485
 
4486
	   akt = akt->next;
4487
	}
4488
 
4489
	return 0.0;
4490
}
4491
 
4492
AVGHEIGHT *sportwatcherWidget::getAvgPtr(AVGHEIGHT *avgHeight, int pos)
4493
{
4494
AVGHEIGHT *akt;
4495
 
4496
	akt = avgHeight;
4497
 
4498
	while (akt)
4499
	{
4500
	   if (akt->pos == pos)
4501
	      return akt;
4502
 
4503
	   akt = akt->next;
4504
	}
4505
 
4506
	return 0;
4507
}
4508
 
246 andreas 4509
void sportwatcherWidget::resizeEvent(QResizeEvent *e)
88 andreas 4510
{
246 andreas 4511
	if (e->size() != e->oldSize())
4512
	   DIRTY=true;
4513
 
132 andreas 4514
	showTrack(zfactor);
88 andreas 4515
	showCurves();
250 andreas 4516
	tabDirt2 = true;
4517
 
4518
	if (curTab == 2)
4519
	{
4520
	   showThreeCurve();
4521
	   tabDirt2 = false;
4522
	}
4523
 
246 andreas 4524
	DIRTY=false;
88 andreas 4525
}
4526
 
132 andreas 4527
void sportwatcherWidget::mouseMoveEvent(QMouseEvent *e)
4528
{
4529
QPoint pos(0, 0);
245 andreas 4530
QPoint ev = ui_sportwatcherWidgetBase.imgMap->mapFrom(this, e->pos());
132 andreas 4531
static QRect coord;
88 andreas 4532
 
246 andreas 4533
	DIRTY=true;
4534
 
250 andreas 4535
	if (curTab == 0)
132 andreas 4536
	{
250 andreas 4537
	   if (!stateHand)
4538
	      return;
4539
 
4540
	   if (ev.x() >= pos.x() &&
4541
	       ev.y() >= pos.y() &&
4542
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.imgMap->geometry().width()) &&
4543
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.imgMap->geometry().height()))
132 andreas 4544
	   {
250 andreas 4545
	      if (lmbPressed == 1)
4546
	      {
4547
		 coord.setCoords(ev.x(), ev.y(), 0, 0);
4548
		 lmbPressed = 0;
4549
	      }
4550
	      else
4551
	      {
4552
		 coord.setRight(ev.x());
4553
		 coord.setBottom(ev.y());
4554
	      }
4555
 
4556
	      if (lmbPressed == 2)
4557
	      {
4558
		 showTrack(zfactor, coord, mapLap);
4559
		 lmbPressed = 0;
4560
	      }
132 andreas 4561
	   }
250 andreas 4562
	}
4563
 
4564
	if (curTab == 2)
4565
	{
4566
	   // look in which of the three QLabels the mouse is, if it is in
4567
	   // one of them.
4568
	   ev = ui_sportwatcherWidgetBase.grHR->mapFrom(this, e->pos());
4569
 
4570
	   if (ev.x() >= pos.x() &&
4571
	       ev.y() >= pos.y() &&
4572
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grHR->geometry().width()) &&
4573
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grHR->geometry().height()))
132 andreas 4574
	   {
250 andreas 4575
	      drawGrHR (ev.x(), ev.y());
4576
	      drawGrElev (ev.x(), -1);
4577
	      drawGrSpeed (ev.x(), -1);
132 andreas 4578
	   }
4579
 
250 andreas 4580
	   ev = ui_sportwatcherWidgetBase.grElevation->mapFrom(this, e->pos());
4581
 
4582
	   if (ev.x() >= pos.x() &&
4583
	       ev.y() >= pos.y() &&
4584
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grElevation->geometry().width()) &&
4585
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grElevation->geometry().height()))
132 andreas 4586
	   {
250 andreas 4587
	      drawGrHR (ev.x(), -1);
4588
	      drawGrElev (ev.x(), ev.y());
4589
	      drawGrSpeed (ev.x(), -1);
132 andreas 4590
	   }
250 andreas 4591
 
4592
	   ev = ui_sportwatcherWidgetBase.grSpeed->mapFrom(this, e->pos());
4593
 
4594
	   if (ev.x() >= pos.x() &&
4595
	       ev.y() >= pos.y() &&
4596
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grSpeed->geometry().width()) &&
4597
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grSpeed->geometry().height()))
4598
	   {
4599
	      drawGrHR (ev.x(), -1);
4600
	      drawGrElev (ev.x(), -1);
4601
	      drawGrSpeed (ev.x(), ev.y());
4602
	   }
132 andreas 4603
	}
246 andreas 4604
 
4605
	DIRTY=false;
132 andreas 4606
}
4607
 
4608
void sportwatcherWidget::mousePressEvent(QMouseEvent *e)
4609
{
232 andreas 4610
	if (stateHand && e->button() == Qt::LeftButton)
132 andreas 4611
	   lmbPressed = 1;	// Left Mouse Button is pressed
4612
	else if (stateHand)
4613
	   lmbPressed = 0;	// Wrong button is pressed
4614
 
4615
	if (stateGlas)
4616
	{
232 andreas 4617
	   if (e->button() == Qt::LeftButton)
132 andreas 4618
	      btGlasPlusSlot();
232 andreas 4619
	   else if (e->button() == Qt::RightButton)
132 andreas 4620
	      btGlasMinusSlot();
4621
	}
4622
}
4623
 
4624
void sportwatcherWidget::mouseReleaseEvent(QMouseEvent *e)
4625
{
232 andreas 4626
	if (stateHand && e->button() == Qt::LeftButton)
132 andreas 4627
	{
4628
	   lmbPressed = 2;	// Left Mouse Button was released
4629
	   mouseMoveEvent(e);
4630
	}
4631
}
4632
 
88 andreas 4633
/*
250 andreas 4634
 * Private functions to draw cross and/or a bar to reflect the mouse
4635
 * pointer on tab 2.
4636
 */
4637
void sportwatcherWidget::drawGrHR (int x, int y)
4638
{
4639
int width, height;
4640
QPixmap pm = pmHR.copy(pmHR.rect());
4641
QPainter paint;
4642
 
4643
	width = ui_sportwatcherWidgetBase.grHR->width();
4644
	height = ui_sportwatcherWidgetBase.grHR->height();
4645
	ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4646
 
4647
	if (x > (width - 40) || y > (height - 12) || x < 1)
4648
	{
4649
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4650
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4651
	   return;
4652
	}
4653
 
4654
	if (tabDirt2)
4655
	{
4656
	   DIRTY = true;
4657
	   showThreeCurve ();
4658
	   tabDirt2 = false;
4659
	   DIRTY = false;
4660
	}
4661
 
4662
	paint.begin(&pm);
4663
	QColor black(0, 0, 0);
4664
	black.setAlpha (128);
4665
	paint.setPen(QPen(black, 1, Qt::SolidLine));
4666
	// horizontal line, if y != -1
4667
	if (y >= 0)
4668
	   paint.drawLine(2, y, width - 41, y);
4669
 
4670
	// vertical line
4671
	paint.drawLine(x, 1, x, height - 13);
4672
	paint.end();
4673
	ui_sportwatcherWidgetBase.grHR->setPixmap(pm);
4674
}
4675
 
4676
void sportwatcherWidget::drawGrElev (int x, int y)
4677
{
4678
int width, height;
4679
QPixmap pm = pmElevation.copy(pmElevation.rect());
4680
QPainter paint;
4681
 
4682
	width = ui_sportwatcherWidgetBase.grElevation->width();
4683
	height = ui_sportwatcherWidgetBase.grElevation->height();
4684
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4685
 
4686
	if (x > (width - 40) || y > (height - 12) || x < 1)
4687
	{
4688
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4689
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4690
	   return;
4691
	}
4692
 
4693
	if (tabDirt2)
4694
	{
4695
	   DIRTY = true;
4696
	   showThreeCurve ();
4697
	   tabDirt2 = false;
4698
	   DIRTY = false;
4699
	}
4700
 
4701
	paint.begin(&pm);
4702
	QColor black(0, 0, 0);
4703
	black.setAlpha (128);
4704
	paint.setPen(QPen(black, 1, Qt::SolidLine));
4705
	// horizontal line, if y != -1
4706
	if (y >= 0)
4707
	   paint.drawLine(2, y, width - 41, y);
4708
 
4709
	// vertical line
4710
	paint.drawLine(x, 1, x, height - 13);
4711
	paint.end();
4712
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pm);
4713
}
4714
 
4715
void sportwatcherWidget::drawGrSpeed (int x, int y)
4716
{
4717
int width, height;
4718
QPixmap pm = pmSpeed.copy(pmSpeed.rect());
4719
QPainter paint;
4720
 
4721
	width = ui_sportwatcherWidgetBase.grSpeed->width();
4722
	height = ui_sportwatcherWidgetBase.grSpeed->height();
4723
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4724
 
4725
	if (x > (width - 40) || y > (height - 12) || x < 1)
4726
	{
4727
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4728
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4729
	   return;
4730
	}
4731
 
4732
	if (tabDirt2)
4733
	{
4734
	   DIRTY = true;
4735
	   showThreeCurve ();
4736
	   tabDirt2 = false;
4737
	   DIRTY = false;
4738
	}
4739
 
4740
	paint.begin(&pm);
4741
	QColor black(0, 0, 0);
4742
	black.setAlpha (128);
4743
	paint.setPen(QPen(black, 1, Qt::SolidLine));
4744
	// horizontal line, if y != -1
4745
	if (y >= 0)
4746
	   paint.drawLine(2, y, width - 41, y);
4747
 
4748
	// vertical line
4749
	paint.drawLine(x, 1, x, height - 13);
4750
	paint.end();
4751
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pm);
4752
}
4753
 
4754
/*
88 andreas 4755
 * Private functions to help decode the data
4756
 */
4757
QDateTime *sportwatcherWidget::garmin_dtime (uint32 t)
4758
{
4759
time_t     tval;
4760
struct tm  tmval;
4761
QTime ti;
4762
QDate dt;
4763
QDateTime *qt;
4764
 
4765
	if (t == 0)
4766
	   return new QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0));
4767
 
4768
	tval = t + TIME_OFFSET;
4769
	localtime_r (&tval, &tmval);
4770
	qt = new QDateTime();
4771
	qt->setDate(QDate(tmval.tm_year+1900, tmval.tm_mon+1, tmval.tm_mday));
4772
	qt->setTime(QTime(tmval.tm_hour, tmval.tm_min, tmval.tm_sec, 0));
4773
	/* OK.  Done. */
4774
	return qt;
4775
}
4776
 
104 andreas 4777
bool sportwatcherWidget::writeTag(const QFile &fn, const QString &str, int indent)
4778
{
4779
QString qs;
232 andreas 4780
char *p;
4781
//QCString qcs;
104 andreas 4782
int i;
88 andreas 4783
 
171 andreas 4784
	if (indent > 0)
4785
	   qs.fill(' ', indent * 3);
4786
 
104 andreas 4787
	qs.append(str);
232 andreas 4788
//	qcs = qs.utf8();
4789
//	qstrcpy(p, qcs);
4790
	p = strdup (qs.toUtf8().data());
104 andreas 4791
	i = strlen(p);
4792
 
4793
	if (write(fn.handle(), p, i) != i)
4794
	{
232 andreas 4795
	   free (p);
104 andreas 4796
	   return false;
4797
	}
4798
 
232 andreas 4799
	free (p);
104 andreas 4800
	return true;
4801
}
4802
 
232 andreas 4803
#if defined HAVE_GDAL
151 andreas 4804
bool sportwatcherWidget::writeWMSTag(double llat, double llon, double rlat, double rlon, int width, int height)
4805
{
4806
QFile fl(MAP);
152 andreas 4807
QString xml, s, srs, crs, styles, bSize, ext;
4808
QDir dir = QDir::home();
232 andreas 4809
QString path = dir.absolutePath();
157 andreas 4810
int item, isrs;
4811
double _llat, _llon, _rlat, _rlon;
4812
bool offline, square;
151 andreas 4813
 
232 andreas 4814
	if (!fl.open(QIODevice::ReadWrite | QIODevice::Truncate))
151 andreas 4815
	{
4816
	   KMessageBox::error (this, i18n("Error opening or creating the WMS tag file!\nPlease check file name and/or permissions."));
4817
	   return false;
4818
	}
4819
 
232 andreas 4820
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
4821
	KConfigGroup wms (&cfg, "WMS");
4822
	square = wms.readEntry("Square", false);
4823
	styles = wms.readEntry("Styles", QString(""));
152 andreas 4824
 
151 andreas 4825
	xml = "<GDAL_WMS>\n";
4826
	xml += "   <Service name=\"WMS\">\n";
4827
	xml += "      <Version>1.1.1</Version>\n";
232 andreas 4828
	xml += "      <ServerURL>" + wms.readEntry("ServerURL", "http://onearth.jpl.nasa.gov/wms.cgi") + "?</serverURL>\n";
4829
	isrs = wms.readEntry("SRS", 0);
157 andreas 4830
	_llon = llon;
4831
	_llat = llat;
4832
	_rlon = rlon;
4833
	_rlat = rlat;
4834
	offline = false;
152 andreas 4835
 
157 andreas 4836
	switch (isrs)
152 andreas 4837
	{
4838
	   case 0: srs = QString("EPSG:4326"); break;
157 andreas 4839
 
4840
	   case 1:
4841
	      srs = QString("EPSG:31257");
4842
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31257, width, height, square);
4843
	   break;
4844
 
4845
	   case 2:
4846
	      srs = QString("EPSG:31258");
4847
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31258, width, height, square);
4848
	   break;
4849
 
4850
	   case 3:
4851
	      srs = QString("EPSG:31259");
4852
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31259, width, height, square);
4853
	   break;
4854
 
4855
	   case 4:
4856
	      srs = QString("EPSG:31286");
4857
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31286, width, height, square);
4858
	   break;
4859
 
4860
	   case 5:
4861
	      srs = QString("EPSG:31287");
4862
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31287, width, height, square);
4863
	   break;
4864
 
4865
	   case 6:
4866
	      srs = QString("EPSG:31288");
4867
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31288, width, height, square);
4868
	   break;
4869
 
152 andreas 4870
	   default: srs = QString("EPSG:4326");
4871
	}
4872
 
4873
	xml += "      <SRS>" + srs + "</SRS>\n";
232 andreas 4874
	item = wms.readEntry("CRS", 0);
152 andreas 4875
 
4876
	switch (item)
4877
	{
4878
	   case 0: crs = QString("CRS:83"); break;
157 andreas 4879
	   case 1: crs = QString("CRS:84"); break;
4880
	   case 2: crs = QString("EPSG:4326"); break;
4881
	   case 3: crs = QString("EPSG:31259"); break;
4882
	   case 4: crs = QString("EPSG:31287"); break;
152 andreas 4883
	   default: crs = QString("CRS:83"); break;
4884
	}
4885
 
4886
	xml += "      <CRS>" + crs + "</CRS>\n";
232 andreas 4887
	item = wms.readEntry("Image", 2);
152 andreas 4888
	xml += "      <ImageFormat>image/";
4889
 
4890
	switch (item)
4891
	{
4892
	   case 0: xml += "gif"; ext = QString(".gif"); break;
4893
	   case 1: xml += "jpeg"; ext = QString(".jpg"); break;
4894
	   case 2: xml += "png"; ext = QString(".png"); break;
4895
	   case 3: xml += "tiff"; ext = QString(".tif"); break;
4896
	   default: xml += "png"; ext = QString(".png");
4897
	}
4898
 
4899
	xml += "</ImageFormat>\n";
4900
 
232 andreas 4901
	xml += "      <Layers>" + wms.readEntry("Layer", QString("")) + "</Layers>\n";
152 andreas 4902
 
4903
	if (!styles.isEmpty())
4904
	   xml += "      <Styles>" + styles + "</Styles>\n";
4905
 
151 andreas 4906
	xml += "      <BBoxOrder>xyXY</BBoxOrder>\n";
4907
	xml += "   </Service>\n";
4908
	xml += "   <DataWindow>\n";
157 andreas 4909
	s.sprintf ("%f", _llat);
151 andreas 4910
	xml += "      <UpperLeftX>" + s + "</UpperLeftX>\n";
157 andreas 4911
	s.sprintf ("%f", _llon);
151 andreas 4912
	xml += "      <UpperLeftY>" + s + "</UpperLeftY>\n";
157 andreas 4913
	s.sprintf ("%f", _rlat);
151 andreas 4914
	xml += "      <LowerRightX>" + s + "</LowerRightX>\n";
157 andreas 4915
	s.sprintf ("%f", _rlon);
151 andreas 4916
	xml += "      <LowerRightY>" + s + "</LowerRightY>\n";
4917
	s.sprintf ("%d", width);
4918
	xml += "      <SizeX>" + s + "</SizeX>\n";
4919
	s.sprintf ("%d", height);
4920
	xml += "      <SizeY>" + s + "</SizeY>\n";
4921
	xml += "   </DataWindow>\n";
157 andreas 4922
 
4923
/*	switch (isrs)
4924
	{
4925
	   case 0: srs = QString("EPSG:4326"); break;
4926
	   case 1: srs = QString("EPSG:31259"); break;
4927
	   case 2: srs = QString("EPSG:31286"); break;
4928
	   case 3: srs = QString("EPSG:31287"); break;
4929
	   case 4: srs = QString("EPSG:31288"); break;
4930
	   default: srs = QString("EPSG:4326");
4931
	}
4932
*/
4933
//	srs = QString("EPSG:4326");
4934
	xml += "   <Projection>" + srs + "</Projection>\n";
232 andreas 4935
	xml += "   <BandsCount>" + wms.readEntry("Bands", QString("3")) + "</BandsCount>\n";
4936
	item = wms.readEntry("Tile", 2);
152 andreas 4937
 
4938
	switch (item)
4939
	{
4940
	   case 0: bSize = QString("64"); break;
4941
	   case 1: bSize = QString("128"); break;
4942
	   case 2: bSize = QString("256"); break;
4943
	   case 3: bSize = QString("512"); break;
4944
	   case 4: bSize = QString("1024"); break;
4945
	   default: bSize = QString("256");
4946
	}
4947
 
4948
	xml += "   <BlockSizeX>" + bSize + "</BlockSizeX>\n";
4949
	xml += "   <BlockSizeY>" + bSize + "</BlockSizeY>\n";
232 andreas 4950
	xml += "   <OverviewCount>" + wms.readEntry("Overview", QString("10")) + "</OverviewCount>\n";
151 andreas 4951
	xml += "   <Cache>\n";
152 andreas 4952
	xml += "      <Path>" + path + "/.gdalwmscache" + "</Path>\n";
232 andreas 4953
	xml += "      <Depth>" + wms.readEntry("Depth", QString("2")) + "</Depth>\n";
152 andreas 4954
	xml += "      <Extension>" + ext + "</Extension>\n";
151 andreas 4955
	xml += "   </Cache>\n";
232 andreas 4956
	QString off((wms.readEntry("Offline", false)) ? "true" : "false");
4957
	QString adv((wms.readEntry("Advice", false)) ? "true" : "false");
4958
	QString ver((wms.readEntry("Verify", true)) ? "true" : "false");
157 andreas 4959
 
4960
	if (offline)
4961
	   xml += "   <OfflineMode>true</OfflineMode>\n";
4962
	else
4963
	   xml += "   <OfflineMode>" + off + "</OfflineMode>\n";
4964
 
152 andreas 4965
	xml += "   <AdviseRead>" + adv + "</AdviseRead>\n";
4966
	xml += "   <VerifyAdviseRead>" + ver + "</VerifyAdviseRead>\n";
151 andreas 4967
	xml += "</GDAL_WMS>\n";
4968
 
232 andreas 4969
	write (fl.handle(), xml.toAscii().data(), strlen (xml.toAscii().data()));
151 andreas 4970
	fl.close();
4971
	return true;
4972
}
4973
 
157 andreas 4974
bool sportwatcherWidget::transCoords (double *x1, double *y1, double *x2, double *y2, int code, int width, int height, bool square)
151 andreas 4975
{
157 andreas 4976
OGRSpatialReference oSourceSRS, oTargetSRS;
4977
OGRCoordinateTransformation *poCT;
4978
 
4979
	oSourceSRS.SetWellKnownGeogCS ("WGS84");
4980
	oTargetSRS.importFromEPSG(code);
4981
	poCT = OGRCreateCoordinateTransformation (&oSourceSRS, &oTargetSRS);
4982
 
4983
	if (poCT == NULL || !poCT->Transform( 1, x1, y1))
4984
	{
4985
	   KMessageBox::error(this, i18n("Translation between coordinate systems failed!"));
4986
 
4987
	   if (poCT != NULL)
4988
	      delete poCT;
4989
 
4990
	   return true;
4991
	}
4992
 
4993
	if (poCT != NULL)
4994
	{
4995
	   poCT->Transform (1, x2, y2);
4996
	   delete poCT;
4997
	}
4998
 
4999
	if (square)
5000
	{
5001
	double wdiff = (double)((long)(*x1 - *x2) / (width / 2 * 2) * (width / 2 * 2));
5002
	double hdiff = (double)((long)(*y2 - *y1) / (height / 2 * 2) * (height / 2 * 2));
5003
 
5004
	   *x2 = *x1 - wdiff; // * (double)mFactor;
5005
	   *y2 = *y1 + hdiff; // * (double)mFactor;
5006
//	   *x2 = *x1 - (double)((long)(wdiff / mFactor * mFactor));
5007
//	   *y2 = *y1 - (double)((long)(hdiff / mFactor * mFactor));
5008
/*	   wdiff = wdiff - (*x1 - *x2);
5009
	   hdiff = hdiff - (*y2 - *y1);
5010
	   *x1 -= wdiff / 2.0;
5011
	   *x2 -= wdiff / 2.0;
5012
	   *y1 += hdiff / 2.0;
5013
	   *y2 += hdiff / 2.0; */
5014
	}
5015
 
5016
	return false;
151 andreas 5017
}
5018
 
158 andreas 5019
QString *sportwatcherWidget::getProjection (int isrs, QString *srs)
5020
{
5021
	switch (isrs)
5022
	{
5023
	   case 0: *srs = QString("EPSG:4326"); break;
5024
	   case 1: *srs = QString("EPSG:31257"); break;
5025
	   case 2: *srs = QString("EPSG:31258"); break;
5026
	   case 3: *srs = QString("EPSG:31259"); break;
5027
	   case 4: *srs = QString("EPSG:31286"); break;
5028
	   case 5: *srs = QString("EPSG:31287"); break;
5029
	   case 6: *srs = QString("EPSG:31288"); break;
5030
	   default: *srs = QString("EPSG:4326");
5031
	}
5032
 
5033
	return srs;
5034
}
5035
 
5036
bool sportwatcherWidget::warpImage(QString fn, QString *fName)
5037
{
5038
GDALDatasetH  hSrcDS, hDstDS;
5039
GDALDataset *inSet, *outSet;
5040
GDALDataType eDT;
5041
GDALRasterBand *poBand;
5042
GDALDriverH hDriver;
5043
char hv0[256];
5044
int nXSize, nYSize;
5045
double adfGeoTransform[6];
5046
double oriLeftLon, oriRightLon, oriLeftLat, oriRightLat;
5047
 
159 andreas 5048
 
5049
	// Loading the user set geo coords of our source image and
5050
	// load the projection used for that image
232 andreas 5051
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
5052
	KConfigGroup ic (&cfg, "ImageCoords");
5053
	oriLeftLon = ic.readEntry("LeftLon", 0.0);
5054
	oriLeftLat = ic.readEntry("LeftLat", 0.0);
5055
	oriRightLon = ic.readEntry("RightLon", 0.0);
5056
	oriRightLat = ic.readEntry("RightLat", 0.0);
5057
	int isrs = ic.readEntry("SRS", 0);
159 andreas 5058
 
158 andreas 5059
	// Create a temporary file name for our output file
5060
	strcpy (hv0, "/tmp/SportWatcherTIFFXXXXXX");
216 andreas 5061
	mkdtemp (&hv0[0]);
158 andreas 5062
	*fName = QString(hv0) + ".tif";
5063
 
5064
	// Open input and output files.
232 andreas 5065
	if ((hSrcDS = GDALOpen(fn.toAscii().data(), GA_ReadOnly)) == NULL)
158 andreas 5066
	{
5067
	   KMessageBox::error(this, i18n("Error opening an image file!"));
5068
	   return false;
5069
	}
5070
 
5071
	inSet = (GDALDataset *)hSrcDS;
5072
	// Create output with same datatype as first input band.
5073
	poBand = inSet->GetRasterBand (1);
5074
	eDT = poBand->GetRasterDataType ();
5075
 
5076
	if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
5077
	{
5078
	   KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
5079
	   GDALClose (hSrcDS);
5080
	   return false;
5081
	}
5082
 
5083
	// Get dimensions of image and set transform data
5084
	int nRasterCount = inSet->GetRasterCount();
5085
	nXSize = inSet->GetRasterXSize();
5086
	nYSize = inSet->GetRasterYSize();
5087
 
159 andreas 5088
	// cut the wanted region out of image
5089
	transform *tf = new transform (oriLeftLat, oriLeftLon, oriRightLat, oriRightLon);
5090
	tf->setDimensions(nXSize, nYSize);
158 andreas 5091
 
159 andreas 5092
	if (!tf->cutImage (geoRect.llat, geoRect.llon, geoRect.rlat, geoRect.rlon, fn))
5093
	{
5094
	   GDALClose (hSrcDS);
5095
	   return false;
5096
	}
158 andreas 5097
 
159 andreas 5098
	GDALClose (hSrcDS);
5099
	QString nfn = fn + "_tmp.png";
158 andreas 5100
 
159 andreas 5101
	// repeat the part above and open the now cutted part of the image.
5102
	// Open input and output files.
232 andreas 5103
	if ((hSrcDS = GDALOpen(nfn.toAscii().data(), GA_ReadOnly)) == NULL)
159 andreas 5104
	{
5105
	   KMessageBox::error(this, i18n("Error opening an image file!"));
5106
	   return false;
5107
	}
158 andreas 5108
 
159 andreas 5109
	inSet = (GDALDataset *)hSrcDS;
5110
	// Create output with same datatype as first input band.
5111
	poBand = inSet->GetRasterBand (1);
5112
	eDT = poBand->GetRasterDataType ();
158 andreas 5113
 
159 andreas 5114
	if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
158 andreas 5115
	{
159 andreas 5116
	   KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
5117
	   GDALClose (hSrcDS);
5118
	   return false;
158 andreas 5119
	}
5120
 
159 andreas 5121
	// Get dimensions of image and set transform data
5122
	nRasterCount = inSet->GetRasterCount();
5123
	nXSize = inSet->GetRasterXSize();
5124
	nYSize = inSet->GetRasterYSize();
5125
 
158 andreas 5126
	// Set the values needed to transform the image
5127
	OGRSpatialReference iSRS;
5128
	const char *iWKT;
5129
 
5130
	switch (isrs)
5131
	{
5132
	   case 0: iSRS.importFromEPSG(4326); break;
5133
	   case 1: iSRS.importFromEPSG(31257); break;
5134
	   case 2: iSRS.importFromEPSG(31258); break;
5135
	   case 3: iSRS.importFromEPSG(31259); break;
5136
	   case 4: iSRS.importFromEPSG(31286); break;
5137
	   case 5: iSRS.importFromEPSG(31287); break;
5138
	   case 6: iSRS.importFromEPSG(31288); break;
5139
	   default: iSRS.importFromEPSG(4326);
5140
	}
5141
 
5142
	iSRS.exportToWkt ((char **)&iWKT);
5143
 
5144
	if (inSet->SetProjection (iWKT) != CE_None)
5145
	{
5146
	   KMessageBox::error(this, i18n("Error setting projection on source!"));
5147
	   GDALClose (hSrcDS);
5148
	   return false;
5149
	}
5150
 
159 andreas 5151
	adfGeoTransform[0] = geoRect.llon;
5152
	adfGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)nXSize;
158 andreas 5153
	adfGeoTransform[2] = 0.0;
159 andreas 5154
	adfGeoTransform[3] = geoRect.llat;
158 andreas 5155
	adfGeoTransform[4] = 0.0;
159 andreas 5156
	adfGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)nYSize);
158 andreas 5157
 
5158
	if (inSet->SetGeoTransform (&adfGeoTransform[0]) != CE_None)
5159
	{
5160
	   KMessageBox::error(this, i18n("Error setting geo transform data to source!"));
5161
	   GDALClose (hSrcDS);
5162
	   return false;
5163
	}
5164
 
159 andreas 5165
	// Get Source coordinate system.
158 andreas 5166
	const char *pszSrcWKT, *pszDstWKT = NULL;
5167
 
5168
	if ((pszSrcWKT = GDALGetProjectionRef (hSrcDS)) == NULL)
5169
	{
5170
	   KMessageBox::error(this, i18n("Error getting the projection reference"));
5171
	   GDALClose (hSrcDS);
5172
	   return false;
5173
	}
5174
 
5175
	// Setup output coordinate system that is UTM ? WGS84.
5176
	OGRSpatialReference oSRS;
5177
 
159 andreas 5178
//	oSRS.SetUTM( 0, TRUE );
158 andreas 5179
	oSRS.SetWellKnownGeogCS("WGS84");
5180
	oSRS.exportToWkt ((char **)&pszDstWKT);
5181
 
159 andreas 5182
	// Create the output file.
158 andreas 5183
	double adfDstGeoTransform[6];
159 andreas 5184
	adfDstGeoTransform[0] = geoRect.llon;
5185
	adfDstGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)geoRect.width;
158 andreas 5186
	adfDstGeoTransform[2] = 0.0;
5187
	adfDstGeoTransform[3] = geoRect.llat;
5188
	adfDstGeoTransform[4] = 0.0;
159 andreas 5189
	adfDstGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)geoRect.height);
158 andreas 5190
 
232 andreas 5191
	if ((hDstDS = GDALCreate(hDriver, fName->toAscii().data(), geoRect.width, geoRect.height,
158 andreas 5192
			nRasterCount, eDT, NULL )) == NULL)
5193
	{
232 andreas 5194
	   KMessageBox::error(this, i18n("Error creating a temporary image file! (%1)").arg(*fName));
158 andreas 5195
	   GDALClose (hSrcDS);
5196
	   return false;
5197
	}
5198
 
5199
	outSet = (GDALDataset *)hDstDS;
5200
 
159 andreas 5201
	for (int i = 0; i < nRasterCount; i++)
5202
	{
5203
	   poBand = outSet->GetRasterBand (i+1);
5204
	   poBand->Fill (0.0);
5205
	}
5206
 
158 andreas 5207
	if (outSet->SetProjection (pszDstWKT) != CE_None)
5208
	{
5209
	   KMessageBox::error(this, i18n("Error setting projection on destination!"));
5210
	   GDALClose (hDstDS);
5211
	   GDALClose (hSrcDS);
232 andreas 5212
	   unlink (fName->toAscii().data());
158 andreas 5213
	   return false;
5214
	}
5215
 
5216
	if (outSet->SetGeoTransform (&adfDstGeoTransform[0]) != CE_None)
5217
	{
159 andreas 5218
	   KMessageBox::error(this, i18n("Error setting geo transform data to destination!"));
158 andreas 5219
	   GDALClose (hDstDS);
5220
	   GDALClose (hSrcDS);
232 andreas 5221
	   unlink (fName->toAscii().data());
158 andreas 5222
	   return false;
5223
	}
159 andreas 5224
 
158 andreas 5225
	// Copy the color table, if required.
5226
	GDALColorTableH hCT;
5227
 
5228
	for (int i = 0; i < nRasterCount; i++)
5229
	{
5230
	   hCT = GDALGetRasterColorTable (inSet->GetRasterBand (i+1));
5231
 
5232
	   if (hCT != NULL)
5233
              GDALSetRasterColorTable (outSet->GetRasterBand (i+1), hCT);
5234
	}
5235
 
5236
	// Setup warp options.
5237
	GDALWarpOptions *psWarpOptions = GDALCreateWarpOptions();
5238
 
5239
	psWarpOptions->hSrcDS = hSrcDS;
5240
	psWarpOptions->hDstDS = hDstDS;
5241
 
5242
	psWarpOptions->nBandCount = nRasterCount;
5243
	psWarpOptions->panSrcBands =
5244
        	(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
5245
	psWarpOptions->panDstBands =
5246
		(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
5247
 
5248
	for (int i = 0; i < nRasterCount; i++)
5249
	{
5250
	   psWarpOptions->panSrcBands[i] = i+1;
5251
	   psWarpOptions->panDstBands[i] = i+1;
5252
	}
5253
 
159 andreas 5254
//	psWarpOptions->pfnProgress = GDALTermProgress;
158 andreas 5255
 
5256
	// Establish reprojection transformer.
5257
	psWarpOptions->pTransformerArg =
5258
        	GDALCreateGenImgProjTransformer(hSrcDS,
5259
					 GDALGetProjectionRef(hSrcDS), 
5260
					 hDstDS,
5261
					 GDALGetProjectionRef(hDstDS),
159 andreas 5262
					 FALSE, 0.0, 1);
158 andreas 5263
 
5264
	psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
5265
 
5266
	// Initialize and execute the warp operation.
5267
	GDALWarpOperation oOperation;
5268
 
5269
	if (oOperation.Initialize (psWarpOptions) != CE_None)
5270
	{
5271
	   KMessageBox::error(this, i18n("Error initializing warp operation!"));
5272
	   GDALClose (hDstDS);
5273
	   GDALClose (hSrcDS);
232 andreas 5274
	   unlink (fName->toAscii().data());
158 andreas 5275
	   return false;
5276
	}
5277
 
159 andreas 5278
	oOperation.ChunkAndWarpMulti (0, 0, geoRect.width, geoRect.height);
158 andreas 5279
	GDALDestroyGenImgProjTransformer (psWarpOptions->pTransformerArg);
5280
	GDALDestroyWarpOptions(psWarpOptions);
5281
 
5282
	GDALClose (hDstDS);
5283
	GDALClose (hSrcDS);
232 andreas 5284
	unlink (nfn.toAscii().data());
158 andreas 5285
	return true;
5286
}
5287
 
151 andreas 5288
#endif
5289
 
88 andreas 5290
#include "sportwatcherwidget.moc"
5291