Subversion Repositories public

Rev

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