Rev 225 | Rev 232 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/***************************************************************************
* Copyright (C) 2007 - 2009 by Andreas Theofilu *
* andreas@theosys.at *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation version 3 of the License. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "config.h"
#include "managefile.h"
#include "sportwatcherwidget.h"
#include "settingswidget.h"
#include "progresswidget.h"
#include "wmsbase.h"
#include "coordinateswidget.h"
#include <string.h>
#include <iostream>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <ksimpleconfig.h>
#include <klocale.h>
#include <klistview.h>
#include <kaboutdialog.h>
#include <kaboutdata.h>
#include <kinputdialog.h>
#include <kglobalsettings.h>
#include <kcombobox.h>
#include <qapplication.h>
#include <qstring.h>
#include <qdatetime.h>
#include <qtoolbutton.h>
#include <qcursor.h>
#include <qcstring.h>
#include <qregexp.h>
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
#include <gdal/ogr_spatialref.h>
#include <gdal/ogrsf_frmts.h>
#include <gdal/gdalwarper.h>
#include <gdal/ogrsf_frmts.h>
#endif
#include "garmin.h"
#include "copy.h"
#include "transform.h"
#include "import.h"
// #define DEBUG 1
using std::cout;
using std::cerr;
using std::clog;
using std::endl;
typedef struct
{
double lon;
double lat;
} posn_type;
sportwatcherWidget::sportwatcherWidget(QWidget* parent, const char* name, WFlags fl)
: sportwatcherWidgetBase(parent,name,fl)
{
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
mFactor = 10; // Factor to calculate square pixels
#endif
mama = parent;
gmn = 0;
min_hr = max_hr = avg_hr = 0;
min_height = max_height = 0.0;
max_time = 0;
index = 0;
zfactor = 0;
mapLap = 0;
mapPan = QRect(0, 0, 0, 0);
stateHand = stateFlag = stateGlas = false;
oldTransX = oldTransY = 0.0;
lmbPressed = 0;
kl = new KLocale(QString("kdesktop"), 0);
// Load the config parameters
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"), true);
cfg->setGroup(QString("SportWatcher"));
lower1 = cfg->readNumEntry("lower1", 0);
lower2 = cfg->readNumEntry("lower2", 0);
lower3 = cfg->readNumEntry("lower3", 0);
upper1 = cfg->readNumEntry("upper1", 0);
upper2 = cfg->readNumEntry("upper2", 0);
upper3 = cfg->readNumEntry("upper3", 0);
MaxHr = cfg->readNumEntry("maxHr", 180);
restHr = cfg->readNumEntry("restHr", 60);
vo2max = cfg->readNumEntry("vo2max", 50);
weight = cfg->readNumEntry("weight", 70);
sampleTime = cfg->readNumEntry("seconds", 15);
Serial = cfg->readBoolEntry("Serial", false);
Forerunner = cfg->readBoolEntry("Forerunner", false);
Contour = cfg->readBoolEntry("Contour", false);
Device = cfg->readEntry("Device", "/dev/ttyUSB0");
Data = cfg->readEntry("Data", QDir::home().absPath() + "/.sportwatcher");
HRM = cfg->readEntry("HRM", QDir::home().absPath() + "/polar");
MAP = cfg->readEntry("MAP", QDir::home().absPath() + "/.sportwatcher/track.wms");
Units = cfg->readNumEntry("Units", 0);
MapType = cfg->readNumEntry("MapType", 7);
delete cfg;
// Set some widget settings
btHand->setToggleButton(true);
btGlas->setToggleButton(true);
// Fill the activities
getActivities();
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
// Initialize the GDAL
GDALAllRegister();
poDataset = 0;
#endif
}
sportwatcherWidget::~sportwatcherWidget()
{
destroy();
delete kl;
}
void sportwatcherWidget::destroy()
{
if (gmn)
garmin_free_data (gmn);
if (index)
{
INDEX *n, *akt = index;
while (akt)
{
n = akt;
akt = akt->next;
delete n;
}
}
index = 0;
gmn = 0;
oldTransX = oldTransY = 0.0;
}
bool sportwatcherWidget::findIndex(const QString &key)
{
INDEX *akt = index;
while (akt)
{
if (akt->path == key || akt->activ == key)
return true;
akt = akt->next;
}
return false;
}
/*
* Search for a directory named .sportwatcher in the home directory of
* the user and search for *.gmn files. Open the files and read the header
* to find the basic data of activities. Then add the information into
* the activities KListView.
*/
void sportwatcherWidget::getActivities()
{
QString path, txt;
QDir mdir, dir = QDir::homeDirPath();
QFileInfo *entries;
QStringList years, months;
KListViewItem *running, *biking, *other;
KListViewItem *el;
int anz;
RUN_NODE *rn;
LAP *lap;
if (Data.isEmpty())
{
path = dir.homeDirPath();
path.append("/.sportwatcher");
}
else
path = Data;
dir.setPath(path);
dir.refresh();
if (!dir.exists())
{
dir.mkdir(path);
return;
}
destroy();
liActivities->clear();
liActivities->setRootIsDecorated(true);
liActivities->setSortColumn(-1);
liActivities->addColumn(QString("Path"), 0);
liActivities->hideColumn(1);
other = new KListViewItem(liActivities, i18n("Others"));
biking = new KListViewItem(liActivities, i18n("Biking"));
running = new KListViewItem(liActivities, i18n("Running"));
other->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
biking->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
running->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
liActivities->insertItem(other);
liActivities->insertItem(biking);
liActivities->insertItem(running);
dir.cd(path);
dir.setFilter(QDir::Dirs | QDir::NoSymLinks);
dir.setSorting(QDir::Name);
dir.refresh();
QFileInfoList *list = (QFileInfoList *)dir.entryInfoList();
if (!list)
return;
QFileInfoListIterator it(*list);
while ((entries = it.current()) != 0) // Years
{
if (entries->fileName() == QString(".") || entries->fileName() == QString(".."))
{
++it;
continue;
}
years += entries->absFilePath();
++it;
}
for (QStringList::Iterator strit = years.begin(); strit != years.end(); ++strit)
{
if (months.count() > 0)
months.clear();
dir.setPath(*strit);
dir.refresh();
list = (QFileInfoList *)dir.entryInfoList();
if (!list)
continue;
it = QFileInfoListIterator (*list);
while ((entries = it.current()) != 0) // Months
{
if (entries->fileName() == QString(".") || entries->fileName() == QString(".."))
{
++it;
continue;
}
months += entries->absFilePath();
++it;
}
for (QStringList::Iterator strit1 = months.begin(); strit1 != months.end(); ++strit1)
{
mdir.setPath(*strit1);
mdir.cd(*strit1);
mdir.setFilter(QDir::Files | QDir::NoSymLinks);
mdir.setNameFilter(QString("*.gmn"));
mdir.refresh();
list = (QFileInfoList *)mdir.entryInfoList();
if (!list)
continue;
it = QFileInfoListIterator (*list);
anz = 0;
while ((entries = it.current()) != 0) // Files
{
files += entries->absFilePath();
++it;
}
}
}
INDEX *akt, *n;
// Open every file and read its head
for (QStringList::Iterator strfl = files.begin(); strfl != files.end(); ++strfl)
{
if (findIndex(*strfl)) // avoid duplicate entries
continue;
spw.destroy();
if (spw.setFileName(*strfl) == -1)
return;
if (gmn)
garmin_free_data (gmn);
gmn = spw.readFile();
ds.destroy();
ds.garmin_print_data(gmn);
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
const QDateTime *qt = garmin_dtime (lap->start_time);
// QString idx = qt->toString("dd.MM.yyyy hh:mm.ss");
QString idx = kl->formatDateTime (*qt, true, true);
if (strlen (rn->run->workout.name) > 1 && strlen (rn->run->workout.name) < 16 && isalpha (rn->run->workout.name[0]))
idx.setAscii (rn->run->workout.name);
if (!index)
{
index = new INDEX;
index->path = *strfl;
index->activ = idx;
index->next = 0;
}
else
{
n = new INDEX;
n->path = *strfl;
n->activ = idx;
n->next = 0;
akt = index;
while (akt->next)
akt = akt->next;
akt->next = n;
}
switch (rn->run->sport_type)
{
case D1000_running:
el = new KListViewItem(running, idx);
el->setText(1, *strfl);
el->setPixmap(0, QPixmap::fromMimeSource(QString("run.png")));
running->insertItem(el);
break;
case D1000_biking:
el = new KListViewItem(biking, idx);
el->setText(1, *strfl);
el->setPixmap(0, QPixmap::fromMimeSource(QString("bike.png")));
biking->insertItem(el);
break;
case D1000_other:
el = new KListViewItem(other, idx);
el->setText(1, *strfl);
el->setPixmap(0, QPixmap::fromMimeSource(QString("other.png")));
other->insertItem(el);
break;
default:
el = new KListViewItem(other, idx);
el->setText(1, *strfl);
el->setPixmap(0, QPixmap::fromMimeSource(QString("other.png")));
other->insertItem(el);
}
delete qt;
}
running->setOpen(true);
if (gmn)
garmin_free_data (gmn);
gmn = 0;
}
/*$SPECIALIZATION$*/
void sportwatcherWidget::btFullscreenSlot()
{
oldTransX = oldTransY = 0.0;
mapPan.setCoords(0, 0, 0, 0);
showTrack(0);
}
void sportwatcherWidget::btGlasMinusSlot()
{
bool sh = stateHand;
stateHand = false;
showTrack(zfactor - 1000);
stateHand = sh;
}
void sportwatcherWidget::btGlasPlusSlot()
{
bool sh = stateHand;
stateHand = false;
showTrack(zfactor + 1000);
stateHand = sh;
}
void sportwatcherWidget::btHandSlot()
{
QCursor cs;
if (stateGlas)
{
stateGlas = false;
btGlas->toggle();
}
stateHand = (stateHand) ? false : true;
if (stateHand)
cs.setShape(QCursor::PointingHandCursor);
else
cs.setShape(QCursor::ArrowCursor);
imgMap->setCursor(cs);
}
void sportwatcherWidget::btGlasSlot()
{
QCursor cs;
if (stateHand)
{
stateHand = false;
btHand->toggle();
}
stateGlas = (stateGlas) ? false : true;
if (stateGlas)
cs.setShape(QCursor::ForbiddenCursor);
else
cs.setShape(QCursor::ArrowCursor);
imgMap->setCursor(cs);
}
void sportwatcherWidget::btFlagSlot()
{
}
void sportwatcherWidget::liLapsSlot(QListViewItem *item)
{
QString sl;
int l;
int idx;
RUN_NODE *rn;
LAP *lap;
if (!item)
return;
sl = item->text(0).mid(4, 3);
l = sl.toInt();
if (l <= 0)
{
showTrack(zfactor, mapPan, 0);
showCurves(0);
return;
}
rn = ds.getRunNode();
idx = rn->run->first_lap_index;
lap = ds.getLap(idx + l - 1);
showTrack(zfactor, mapPan, lap);
showCurves(lap);
}
void sportwatcherWidget::liActivitiesSlot(QListViewItem *item)
{
INDEX *akt;
if (!item)
return;
// akt = index;
// while (akt)
// {
// if (akt->activ == item->text(0))
// {
spw.destroy();
// if (spw.setFileName(akt->path.ascii()) == -1)
if (spw.setFileName(item->text(1).ascii()) == -1)
return;
if (gmn)
garmin_free_data (gmn);
gmn = spw.readFile();
zfactor = 0;
showLaps();
showTrack();
showCurves();
// return;
// }
// akt = akt->next;
// }
}
void sportwatcherWidget::helpAbout()
{
KAboutData about("SportWatcher", "SportWatcher", VERSION);
QString ab = ("About");
QString liz = ("License");
KAboutDialog *info = new KAboutDialog(KAboutDialog::AbtProduct|KAboutDialog::AbtTabbed,
QString("SportWatcher"), KAboutDialog::Close, KAboutDialog::Close, 0,
i18n("About"), false, false, QString::null, QString::null, QString::null);
info->setProduct("SportWatcher", QString("Version %1").arg(VERSION), "Andreas Theofilu", "2007 - 2009");
KAboutContainer *infoAppl = info->addContainerPage(ab, AlignCenter, AlignCenter);
infoAppl->addTitle(QString("SportWatcher"), AlignCenter, false, false);
// infoAppl->addTitle(QString("(C) 2007, 2008, Andreas Theofilu <andreas@theosys.at>"));
infoAppl->addPerson(QString("Andreas Theofilu"), QString("andreas@theosys.at"), QString("http://www.theosys.at"), NULL);
infoAppl->addTitle(i18n("About this Program", "\nRead out the data of a Garmin GPS device over USB, and visualize them on screen"));
about.setLicense(KAboutData::License_Custom);
about.setLicenseText(I18N_NOOP(gpl3));
info->addLicensePage(liz, about.license(), 10);
info->setGeometry(0, 0, 800, 400);
info->centerOnScreen(info, 0);
info->exec();
delete info;
}
void sportwatcherWidget::helpContents()
{
KMessageBox::information(this, i18n("This function is currently not implemented!"));
}
void sportwatcherWidget::helpIndex()
{
KMessageBox::information(this, i18n("This function is currently not implemented!"));
}
void sportwatcherWidget::fileExit()
{
if (mama)
mama->close();
}
void sportwatcherWidget::filePrint()
{
KMessageBox::information(this, i18n("This function is currently not implemented!"));
}
/*
* This function allows the user to choose a file name, where we save the
* actual lap in Garmins own TCX format. This format is simply a XML-file.
* For details about the schema of this file look at
* http://developer.garmin.com/schemas/tcx/v2/
*/
void sportwatcherWidget::fileSaveAs()
{
QString fname;
QFile fn;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent, i;
QDateTime *qt;
QRegExp rx("(.tcx|.gpx|.osm)$");
if (!gmn)
{
KMessageBox::error(this, i18n("Currently no activity is selected!"));
return;
}
fname = KFileDialog::getSaveFileName(0, QString("*.tcx|Garmin Training Center (*.tcx)\n*.gpx|GPS Excange Format (*.gpx)\n*.osm|OpenStreetMap (*.osm)"), this, QString("SportWatcher"));
if (fname.isEmpty())
return;
if (rx.search(fname) < 0)
{
KMessageBox::error(this, i18n("The file " + fname + " has no valid file extension!"));
return;
}
fn.setName(fname);
if (fn.exists())
{
if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
return;
}
rx.setPattern(".gpx$");
if (rx.search(fname) >= 0) // Should we create a *.gpx file?
{
sportwatcherWidget::saveGPX(fname);
return;
}
rx.setPattern(".osm$");
if (rx.search(fname) >= 0) // Should we create a *.osm file?
{
sportwatcherWidget::saveOSM(fname);
return;
}
// No, we create a *.tcx file!
indent = 0;
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
if ((point = ds.getPoint(lap->start_time)) == 0)
{
KMessageBox::error(this, i18n("No data to save!"));
return;
}
if (!fn.open(IO_ReadWrite | IO_Truncate))
{
KMessageBox::error(this, i18n("Error creating file " + fname + "!\nPlease check permissions"));
return;
}
buffer = QString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
buffer.append("<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
buffer.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
buffer.append("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
buffer.append("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
writeTag (fn, buffer, indent);
buffer = QString("<folders/>\n\n");
writeTag (fn, buffer, indent);
// Open a course
QFileInfo finfo(fname);
buffer = QString("<Courses>\n <Course>\n <name>%1</name>\n").arg(finfo.baseName(true));
writeTag (fn, buffer, indent);
indent = 2;
rakt = rn;
while (rakt)
{
if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
rakt->run->type != data_D1010)
{
rakt = rakt->next;
continue;
}
for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
{
if ((lap = ds.getLap(i)) == NULL)
continue;
// Write the information of the lap
writeTag (fn, QString("<Lap>\n"), indent);
indent++;
buffer.sprintf("<TotalTimeSeconds>%f</TotalTimeSeconds>\n", (double)lap->total_time / 100.0);
writeTag (fn, buffer, indent);
qt = garmin_dtime(lap->start_time);
buffer = QString("<StartTime>%1</StartTime>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
writeTag (fn, buffer, indent);
buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", lap->total_distance);
writeTag (fn, buffer, indent);
writeTag (fn, QString("<BeginPosition>\n"), indent);
indent++;
buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->begin.lat));
writeTag (fn, buffer, indent);
buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->begin.lon));
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</BeginPosition>\n"), indent);
writeTag (fn, QString("<EndPosition>\n"), indent);
indent++;
buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->end.lat));
writeTag (fn, buffer, indent);
buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->end.lon));
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</EndPosition>\n"), indent);
writeTag (fn, QString("<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
indent++;
buffer.sprintf("<Value>%d</Value>\n", lap->avg_heart_rate);
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</AverageHeartRateBpm>\n"), indent);
writeTag (fn, QString("<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
indent++;
buffer.sprintf("<Value>%d</Value>\n", lap->max_heart_rate);
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</MaximumHeartRateBpm>\n"), indent);
if (lap->avg_cadence < 255)
{
buffer.sprintf("<AverageCadence>%d</AverageCadence>\n", lap->avg_cadence);
writeTag (fn, buffer, indent);
buffer.sprintf("<Cadence>%d</Cadence>\n", lap->avg_cadence);
writeTag (fn, buffer, indent);
}
buffer = QString("<Intensity>%1</Intensity>\n").arg((!lap->intensity) ? "Active" : "Resting");
writeTag (fn, buffer, indent);
buffer.sprintf("<Calories>%d</Calories>\n", lap->calories);
writeTag (fn, buffer, indent);
buffer.sprintf("<MaximumSpeed>%f</MaximumSpeed>\n", lap->max_speed);
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</Lap>\n"), indent);
point = ds.getPoint(lap->start_time);
writeTag (fn, QString("<Track>\n"), indent);
indent++;
while (point)
{
if (point->time > (lap->start_time + (lap->total_time / 100)))
break;
writeTag (fn, QString("<Trackpoint>\n"), indent);
indent++;
qt = garmin_dtime(point->time);
buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
writeTag (fn, buffer, indent);
delete qt;
writeTag (fn, QString("<Position>\n"), indent);
indent++;
buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(point->posn.lat));
writeTag (fn, buffer, indent);
buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(point->posn.lon));
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</Position>\n"), indent);
if (point->alt < 20000.0)
{
buffer.sprintf("<AltitudeMeters>%f</AltitudeMeters>\n", point->alt);
writeTag (fn, buffer, indent);
}
buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", point->distance);
writeTag (fn, buffer, indent);
if (point->heart_rate > 0 && point->heart_rate < 250)
{
writeTag (fn, QString("<HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
indent++;
buffer.sprintf("<Value>%d</Value>\n", point->heart_rate);
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</HeartRateBpm>\n"), indent);
}
if (point->cadence >= 0 && point->cadence < 250)
{
buffer.sprintf("<Cadence>%d</Cadence>\n", point->cadence);
writeTag (fn, buffer, indent);
}
buffer.sprintf("<SensorState>%s</SensorState>\n", (!point->sensor) ? "Absent" : "Present");
writeTag (fn, buffer, indent);
indent--;
writeTag (fn, QString("</Trackpoint>\n"), indent);
point = ds.getPoint(point->time + 1);
}
indent--;
writeTag (fn, QString("</Track>\n"), indent);
}
indent--;
writeTag (fn, QString("</Course>\n"), indent);
indent--;
writeTag (fn, QString("</Courses>\n"), indent);
rakt = rakt->next;
}
// Write information about device
// Here my personal signature is written :-)
writeTag (fn, QString("<Author xsi:type=\"Application_t\">\n"), indent);
indent++;
writeTag (fn, QString("<Name>SportWatcher</Name>\n"), indent);
writeTag (fn, QString("<Build>\n"), indent);
indent++;
writeTag (fn, QString("<Version>\n"), indent);
indent++;
writeTag (fn, QString("<VersionMajor>0</VersionMajor>\n"), indent);
writeTag (fn, QString("<VersionMinor>1</VersionMinor>\n"), indent);
writeTag (fn, QString("<BuildMajor>0</BuildMajor>\n"), indent);
writeTag (fn, QString("<BuildMinor>0</BuildMinor>\n"), indent);
indent--;
writeTag (fn, QString("</Version>\n"), indent);
writeTag (fn, QString("<Type>Beta</Type>\n"), indent);
writeTag (fn, QString("<Time>Jan 31 2008, 00:00:00</Time>\n"), indent);
writeTag (fn, QString("<Builder>theosys</Builder>\n"), indent);
indent--;
writeTag (fn, QString("</Build>\n"), indent);
writeTag (fn, QString("<LangID>EN</LangID>\n"), indent);
writeTag (fn, QString("<PartNumber>000-00000-00</PartNumber>\n"), indent);
indent--;
writeTag (fn, QString("</Author>\n"), indent);
writeTag (fn, QString("</TrainingCenterDatabase>\n"), indent);
fn.close();
KMessageBox::information(this, i18n("File ") + fname + i18n(" was written successfully."));
}
void sportwatcherWidget::fileSave()
{
KMessageBox::information(this, i18n("This function is currently not implemented!"));
}
void sportwatcherWidget::saveGPX(const QString &fn)
{
QFile qf;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent;
unsigned int i;
QDateTime *qt;
double minLat, minLon, maxLat, maxLon;
indent = 0;
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
if ((point = ds.getPoint(lap->start_time)) == 0)
{
KMessageBox::error(this, i18n("No data to save!"));
return;
}
qf.setName(fn);
if (!qf.open(IO_ReadWrite | IO_Truncate))
{
KMessageBox::error(this, i18n("Error creating file " + fn + "!\nPlease check permissions"));
return;
}
buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
buffer.append("<gpx version=\"1.1\" creator=\"TheoSys SportWatcher\" xmlns=\"http://www.topografix.com/GPX/1/1\">\n");
buffer.append(" <metadata>\n");
indent = 0;
writeTag (qf, buffer, indent);
// Find the edges of our coordinates
// We need this information in the header (metadata)
rakt = rn;
minLat = -90.0;
minLon = -180.0;
maxLat = 90.0;
maxLon = 180.0;
while (rakt)
{
if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
rakt->run->type != data_D1010)
{
rakt = rakt->next;
continue;
}
i = rakt->run->first_lap_index;
// get the first lap
if ((lap = ds.getLap(i)) == NULL)
continue;
i = 0;
// iterate the points associated with the laps
while ((point = ds.getPoint(i)) != 0)
{
if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
{
i = point->time + 1;
continue;
}
if (SEMI2DEG(point->posn.lat) > minLat)
minLat = SEMI2DEG(point->posn.lat);
if (SEMI2DEG(point->posn.lat) < maxLat)
maxLat = SEMI2DEG(point->posn.lat);
if (SEMI2DEG(point->posn.lon) > minLon)
minLon = SEMI2DEG(point->posn.lon);
if (SEMI2DEG(point->posn.lon) < maxLon)
maxLon = SEMI2DEG(point->posn.lon);
i = point->time + 1;
}
rakt = rakt->next;
}
buffer.sprintf(" <bounds minlat=\"%f\" minlon=\"%f\" maxlat=\"%f\" maxlon=\"%f\" />\n",
maxLat, minLon, minLat, maxLon);
buffer.append(" </metadata>\n");
buffer.append(" <trk>\n");
buffer.append(" <trkseg>\n");
writeTag (qf, buffer, indent);
indent = 3;
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
i = 0;
while ((point = ds.getPoint(i)) != 0)
{
if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
{
i = point->time + 1;
continue;
}
buffer.sprintf("<trkpt lat=\"%f\" lon=\"%f\">\n",
SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
writeTag(qf, buffer, indent);
indent++;
buffer.sprintf("<ele>%f</ele>\n", point->alt);
writeTag(qf, buffer, indent);
qt = garmin_dtime(point->time);
buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
writeTag(qf, buffer, indent);
indent--;
writeTag(qf, QString("</trkpt>\n"), indent);
i = point->time + 1;
}
indent = 0;
buffer = QString(" </trkseg>\n");
buffer.append(" </trk>\n");
buffer.append("</gpx>\n");
writeTag(qf, buffer, indent);
qf.close();
KMessageBox::information(this, i18n("File ") + fn + i18n(" was written successfully."));
}
void sportwatcherWidget::saveOSM(const QString &fn)
{
QFile qf;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent, id, j;
unsigned int i;
double minLat, minLon, maxLat, maxLon;
QDateTime *qt;
indent = 0;
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
if ((point = ds.getPoint(lap->start_time)) == 0)
{
KMessageBox::error(this, i18n("No data to save!"));
return;
}
qf.setName(fn);
if (!qf.open(IO_ReadWrite | IO_Truncate))
{
KMessageBox::error(this, i18n("Error creating file " + fn + "!\nPlease check permissions"));
return;
}
buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
buffer.append("<osm version=\"0.5\" generator=\"TheoSys SportWatcher\">\n");
indent = 0;
writeTag (qf, buffer, indent);
// Find the edges of our coordinates
// We need this information in the header (metadata)
rakt = rn;
minLat = -90.0;
minLon = -180.0;
maxLat = 90.0;
maxLon = 180.0;
while (rakt)
{
if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
rakt->run->type != data_D1010)
{
rakt = rakt->next;
continue;
}
i = rakt->run->first_lap_index;
// get the first lap
if ((lap = ds.getLap(i)) == NULL)
continue;
i = 0;
// iterate the points associated with the laps
while ((point = ds.getPoint(i)) != 0)
{
if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
{
i = point->time + 1;
continue;
}
if (SEMI2DEG(point->posn.lat) > minLat)
minLat = SEMI2DEG(point->posn.lat);
if (SEMI2DEG(point->posn.lat) < maxLat)
maxLat = SEMI2DEG(point->posn.lat);
if (SEMI2DEG(point->posn.lon) > minLon)
minLon = SEMI2DEG(point->posn.lon);
if (SEMI2DEG(point->posn.lon) < maxLon)
maxLon = SEMI2DEG(point->posn.lon);
i = point->time + 1;
}
rakt = rakt->next;
}
buffer.sprintf(" <bound box='%f,%f,%f,%f' origin='http://www.openstreetmap.org/api/0.5' />\n",
maxLat, minLon, minLat, maxLon);
writeTag (qf, buffer, indent);
indent = 1;
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
i = 0;
id = -1;
while ((point = ds.getPoint(i)) != 0)
{
if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
{
i = point->time + 1;
continue;
}
buffer.sprintf("<node id='%d' action='modify' visible='true' lat=\"%f\" lon=\"%f\">\n",
id, SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
writeTag(qf, buffer, indent);
indent++;
buffer = QString("<tag k='created_by' v='TheoSys Sportwatcher' />\n");
writeTag(qf, buffer, indent);
buffer = QString("<tag k='highway' v='tertiary' />\n");
writeTag(qf, buffer, indent);
indent--;
writeTag(qf, QString("</node>\n"), indent);
id--;
i = point->time + 1;
}
qt = garmin_dtime(lap->start_time);
buffer.sprintf("<way id='%d' action='modify' visible='true' timestamp='%s'>\n",
id, QString(qt->toString("yyyy-MM-ddThh:mm:ssZ")).ascii());
writeTag(qf, buffer, indent);
indent++;
for (j = -1; j > id; j--)
{
buffer.sprintf("<nd ref='%d' />\n", j);
writeTag(qf, buffer, indent);
}
indent--;
writeTag(qf, QString("</way>\n"), indent);
indent = 0;
writeTag(qf, QString("</osm>\n"), indent);
qf.close();
KMessageBox::information(this, i18n("File ") + fn + i18n(" was written successfully."));
}
void sportwatcherWidget::fileOpen()
{
QString fname = KFileDialog::getOpenFileName(Data, QString("*.gmn"), this, QString("SportWatcher"));
int m;
if (fname.isEmpty())
return;
spw.destroy();
if (spw.setFileName(fname.ascii()) == -1)
return;
if (gmn)
garmin_free_data (gmn);
gmn = spw.readFile();
zfactor = 0;
if ((m = garmin_count_error()) > 0)
{
int i, key = -1;
for (i = 0; i < m; i++)
KMessageBox::error(this, QString(garmin_get_next_error(&key)));
garmin_clear_errors();
return;
}
showLaps();
showTrack();
showCurves();
}
void sportwatcherWidget::fileImport()
{
QString fname = KFileDialog::getOpenFileName(QString("~/"), QString("*.tcx"), this, QString("SportWatcher"));
gmn_import import;
int m;
QString tgfile, fld, px;
QFileInfo datei;
QListViewItem *item;
KListViewItem *el, *it;
LAP *lap;
RUN_NODE *rn;
if (fname.isEmpty())
return;
import.setFile(fname);
if ((m = import.import()) != 0)
{
KMessageBox::error(this, QString(import.getError(m)));
return;
}
if (gmn)
garmin_free_data (gmn);
gmn = import.getGarminData ();
showLaps();
showTrack();
showCurves();
// Find the filename;
// It consists of the date and the time.
// We need this information to set the correct path to store the file.
tgfile = Data; // The base path
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
QDateTime *qt = garmin_dtime (lap->start_time);
tgfile.append (qt->toString ("/yyyy")); // year is a folder
tgfile.append (qt->toString ("/MM")); // month is a folder
tgfile.append (qt->toString ("/yyyyMMddThhmmss")); // The file name
tgfile.append (".gmn"); // Extension of file name
datei.setFile (tgfile);
// save the data to a real file, but only if it doesn't exist allready.
garmin_save_all (gmn, datei.fileName().ascii(), datei.dirPath(true).ascii(), 0);
// in case the item is already in the list on the left side, we
// only highlight the item.
if ((item = liActivities->findItem (tgfile, 1, Qt::ExactMatch)) != 0)
{
liActivities->setSelected (item, true);
liActivities->setCurrentItem (item);
return;
}
// insert everything into the list on the left side
switch (rn->run->sport_type)
{
case D1000_running:
fld = i18n("Running");
px.setAscii("run.png");
break;
case D1000_biking:
fld = i18n("Biking");
px.setAscii("bike.png");
break;
default:
fld = i18n("Others");
px.setAscii("other.png");
}
// Do we have allready so items in the list?
if ((item = liActivities->findItem (fld, 0, Qt::ExactMatch)) != 0)
{
// el = new KListViewItem(item, qt->toString("dd.MM.yyyy hh:mm:ss"));
el = new KListViewItem(item, kl->formatDateTime(*qt, true, true));
el->setText(1, tgfile);
el->setPixmap(0, QPixmap::fromMimeSource(px));
item->insertItem(el);
}
else // no, this is the first item. (shouldn't be!)
{
it = new KListViewItem(liActivities, fld);
it->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
liActivities->insertItem(it);
// el = new KListViewItem(item, qt->toString("dd.MM.yyyy hh:mm:ss"));
el = new KListViewItem(item, kl->formatDateTime(*qt, true, true));
el->setText(1, tgfile);
el->setPixmap(0, QPixmap::fromMimeSource(px));
it->insertItem(el);
}
}
/*
* Display a small dialog to rename the currently loaded session.
*/
void sportwatcherWidget::editRename()
{
bool ok;
QString name, inhalt;
QPtrList<QListViewItem> item;
QListViewItem *lvItem;
QFileInfo datei;
RUN_NODE *rn;
LAP *lap;
garmin_list *list;
D1009 *n;
if (!gmn)
{
KMessageBox::error(this, i18n("There is no session selected!"));
return;
}
rn = ds.getRunNode();
item = liActivities->selectedItems (false);
lvItem = item.first();
if (!isdigit(rn->run->workout.name[0]))
inhalt = lvItem->text(0);
else
inhalt = QString::null;
name = KInputDialog::getText(i18n("Rename session"), i18n("Session name"),
inhalt, &ok, this, "dialogRename", 0,
QString("Nxxxxxxxxxxxxxx"));
if (!ok)
return;
if (name.length() <= 1)
{
lap = ds.getLap(rn->run->first_lap_index);
const QDateTime *qt = garmin_dtime (lap->start_time);
// QString idx = qt->toString("dd.MM.yyyy hh:mm.ss");
QString idx = kl->formatDateTime(*qt, true, true);
lvItem->setText (0, idx);
datei.setFile (lvItem->text(1));
garmin_save_all (gmn, datei.fileName().ascii(), datei.dirPath(true).ascii(), 1);
delete qt;
return;
}
strncpy (rn->run->workout.name, name.ascii(), 16);
if (gmn->type != data_Dlist)
{
KMessageBox::error(this, i18n("editRename: Unexpected structure type %1 found!").arg(gmn->type));
return;
}
list = (garmin_list *)gmn->data;
if (list->head->data->type != data_D1009) // This should be the run node
{
KMessageBox::error(this, i18n("editRename: The run node was not found!"));
return;
}
n = (D1009 *)list->head->data->data;
strcpy (n->workout.name, rn->run->workout.name);
lvItem->setText (0, name);
datei.setFile (lvItem->text(1));
garmin_save_all (gmn, datei.fileName().ascii(), datei.dirPath(true).ascii(), 1);
}
void sportwatcherWidget::fileNew()
{
progressWidget *dlg = new progressWidget(this, "progressWidgetBase");
dlg->show();
if (!dlg->Download())
{
int m, key;
key = -1;
for (m = 0; m < garmin_count_error(); m++)
KMessageBox::error(this, QString(garmin_get_next_error(&key)));
}
else
getActivities();
garmin_clear_errors();
delete dlg;
}
/*
* This function is called, when the user clicks at the menu point
* "Save Heart Rate".
* First, a file dialog box is displayed, where the user can choose a
* directory and a file name to save the heart rate.
* If the file could successfully be created, the heart rate is saved
* in the "HRM"-format. This is the native format Polar uses to store
* heart rate data. I've choosen this format, because it's popular and
* used by many other software too.
*/
void sportwatcherWidget::extrasSaveHR()
{
QString fname, str1, str2;
QFile fdfile;
QDateTime *qt, *oldqt;
QDate dat;
QTime t;
QDir dir = QDir::home();
char hv0[256];
RUN_NODE *rn;
LAP *lap, *alap;
POINT *point;
int samples, smp, seconds, anz, nsec, samsec;
int avgHeart, minHeart, maxHeart, aktHeart;
int secRange1, secRange2, secRange3, secAbove, secBeyond;
if (!gmn)
{
KMessageBox::information(this, i18n("There is no activity open"));
return;
}
if (HRM.isEmpty())
str1 = dir.path();
else
str1 = HRM;
str1 += "/" + StartTime.toString("yyyyMMddThhmmss.zzz.hrm");
fname = KFileDialog::getSaveFileName(str1, QString("*.hrm"), this, QString("SportWatcher"));
if (fname.isEmpty())
return;
fdfile.setName(fname);
if (fdfile.exists())
{
if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
return;
}
if (!fdfile.open(IO_ReadWrite | IO_Truncate))
{
KMessageBox::error(this, i18n("Error creating a file!\nPlease check permissions."));
return;
}
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
t = StartTime.time();
dat = StartTime.date();
if ((point = ds.getPoint(lap->start_time)) == 0)
{
fdfile.close();
return;
}
strcpy (hv0, "[Params]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
str1 = dat.toString("yyyyMMdd");
str2 = t.toString("hh:mm:ss.z");
sprintf(hv0, "Version=106\nMonitor=11\nSMode=000000000\nDate=%s\nStartTime=%s\n",
str1.ascii(), str2.ascii());
write(fdfile.handle(), &hv0[0], strlen(hv0));
t.setHMS(0, 0, 0);
t = t.addSecs(max_time);
str2 = t.toString("hh:mm:ss.z");
switch (sampleTime)
{
case 0: samsec = 5; break;
case 1: samsec = 15; break;
case 2: samsec = 30; break;
case 3: samsec = 60; break;
default:
samsec = 15;
}
sprintf(hv0, "Length=%s\nInterval=%d\nUpper1=%d\nLower1=%d\nUpper2=%d\n",
str2.ascii(), samsec, upper1, lower1, upper2);
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "Lower2=%d\nUpper3=%d\nLower3=%d\nTimer1=00:00:00.0\n",
lower2, upper3, lower3);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "Timer2=00:00:00.0\nTimer3=00:00:00.0\nActiveLimit=0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "MaxHR=%d\nRestHR=%d\nStartDelay=0\nVO2max=%d\nWeight=%d\n\n",
MaxHr, restHr, vo2max, weight);
write(fdfile.handle(), &hv0[0], strlen(hv0));
// Write the intervall times. One block for every lap
secRange1 = secRange2 = secRange3 = secAbove = secBeyond = 0;
strcpy(hv0, "[IntTimes]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
t.setHMS(0, 0, 0);
for (unsigned int i = rn->run->first_lap_index; i < rn->run->last_lap_index; i++)
{
alap = ds.getLap(i);
point = ds.getPoint(alap->start_time);
oldqt = garmin_dtime(point->time);
avgHeart = minHeart = maxHeart = aktHeart = 0;
anz = 0;
unsigned long lastTime = point->time;
int totSec = 0;
while (point)
{
if (point->time > (alap->start_time + (alap->total_time / 100)))
break;
if (point->heart_rate > 0)
{
avgHeart += point->heart_rate;
nsec = point->time - lastTime;
totSec += nsec;
if (minHeart == 0 || minHeart > point->heart_rate)
minHeart = point->heart_rate;
if (maxHeart < point->heart_rate)
maxHeart = point->heart_rate;
if (aktHeart == 0 && totSec >= samsec)
aktHeart = avgHeart / (anz + 1);
if (point->heart_rate < lower1)
secBeyond += nsec;
else if (point->heart_rate < lower2)
secRange1 += nsec;
else if (point->heart_rate < lower3)
secRange2 += nsec;
else if (point->heart_rate < upper3)
secRange3 += nsec;
else
secAbove += nsec;
lastTime = point->time;
anz++;
}
point = ds.getPoint(point->time+1);
}
t = t.addSecs(alap->total_time / 100);
str1 = t.toString("hh:mm:ss.z");
if (anz > 0)
avgHeart = avgHeart / anz;
else
avgHeart = 0;
sprintf(hv0, "%s\t %d\t %d\t %d\t %d\n",
str1.ascii(), aktHeart, minHeart, avgHeart, maxHeart);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "32\t 0\t 0\t 0\t 0\t 0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "0\t 0\t 0\t 0\t 0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "0\t %d\t 0\t 0\t 0\t 0\n", (int)alap->total_distance);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "0\t 0\t 0\t 0\t 0\t 0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
}
strcpy(hv0, "\n[IntNotes]\n\n[ExtraData]\n\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "[Summary-123]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 1
smp = max_time - secBeyond - secRange1 - secRange2 - secRange3 - secAbove;
sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
max_time, secRange1, secRange2 + secRange3,
secAbove + secBeyond, smp);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // limits 1
sprintf(hv0, "%d\t %d\t %d\t %d\n",
MaxHr, upper1, lower1, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 1
sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
max_time, secRange2, secRange1 + secRange3,
secAbove + secBeyond, smp);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // limits 2
sprintf(hv0, "%d\t %d\t %d\t %d\n",
MaxHr, upper2, lower2, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 2
sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
max_time, secRange3, secRange1 + secRange2,
secAbove + secBeyond, smp);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // limits 3
sprintf(hv0, "%d\t %d\t %d\t %d\n",
MaxHr, upper3, lower3, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 3
samples = max_time / samsec;
sprintf(hv0, "0\t %u\n\n", samples); // samples
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "[Summary-TH]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "%u\t 0\t %u\t %d\t %d\t 0\n", max_time, max_time - max_hr - restHr, max_hr, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "%d\t %d\t %d\t %d\n", MaxHr, upper3, lower1, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "0\t %u\n\n", samples); // samples
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "[HRZones]\n%d\n%d\n%d\n%d\n%d\n%d\n0\n0\n0\n0\n0\n\n",
MaxHr, upper3, upper2, upper1, lower1, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "[HRData]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
smp = 0; // average heart rate of 15 seconds
seconds = 0;
anz = 0;
nsec = samsec;
oldqt = garmin_dtime(lap->start_time);
qt = 0;
point = ds.getPoint(lap->start_time);
while (point)
{
if (seconds >= nsec)
{
if (anz > 0)
{
sprintf(hv0, "%d\n", smp / anz);
write(fdfile.handle(), &hv0[0], strlen(hv0));
}
if (smp > 0 && seconds >= (nsec + samsec))
{
if (anz <= 0)
anz = 0;
for (int x = nsec; x < seconds; x += samsec)
{
sprintf(hv0, "%d\n", smp / anz);
write(fdfile.handle(), &hv0[0], strlen(hv0));
nsec += samsec;
}
}
anz = 0;
smp = 0;
nsec += samsec;
}
qt = garmin_dtime (point->time);
seconds += oldqt->secsTo(*qt);
if (point->heart_rate > 0)
{
smp += point->heart_rate;
anz++;
}
delete oldqt;
oldqt = qt;
point = ds.getPoint(point->time + 1);
}
fdfile.close();
KMessageBox::information(this, i18n("File successfully written."));
}
void sportwatcherWidget::extrasSettings()
{
settingsWidget *dlg = new settingsWidget(this, "settingsWidgetBase", TRUE, 0);
if (dlg->exec() == QDialog::Accepted)
{
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"), true);
cfg->setGroup(QString("SportWatcher"));
lower1 = cfg->readNumEntry("lower1");
lower2 = cfg->readNumEntry("lower2");
lower3 = cfg->readNumEntry("lower3");
upper1 = cfg->readNumEntry("upper1");
upper2 = cfg->readNumEntry("upper2");
upper3 = cfg->readNumEntry("upper3");
MaxHr = cfg->readNumEntry("maxHr");
restHr = cfg->readNumEntry("restHr");
vo2max = cfg->readNumEntry("vo2max");
weight = cfg->readNumEntry("weight");
sampleTime = cfg->readNumEntry("seconds");
Serial = cfg->readBoolEntry("Serial");
Contour = cfg->readBoolEntry("Contour");
Device = cfg->readEntry("Device");
Forerunner = cfg->readBoolEntry("Forerunner", false);
Data = cfg->readEntry("Data");
HRM = cfg->readEntry("HRM");
MAP = cfg->readEntry("MAP");
Units = cfg->readNumEntry("Units");
MapType = cfg->readNumEntry("MapType");
delete cfg;
}
delete dlg;
}
void sportwatcherWidget::extrasWMSSettings()
{
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
if (MapType == MPT_BMP || MapType == MPT_GIF || MapType == MPT_PNG ||
MapType == MPT_TIF)
{
coordinatesWidget *idlg = new coordinatesWidget(this, "coordinateswidgetbase", true, 0);
idlg->exec();
delete idlg;
return;
}
if (MapType != MPT_WMS)
{
KMessageBox::detailedSorry(this,
i18n("You have not choosen a WMS tag file!"),
i18n("This dialog is especialy to set WMS specific parameters. ") +
i18n("Therefore this dialog is temporary disabled. It will be ") +
i18n("available again, as soon as you choose \"WMS server\" as ") +
i18n("your map type."));
return;
}
wmsbase *dlg = new wmsbase(this, "wmswidgetbase", TRUE, 0);
dlg->exec();
delete dlg;
#else
KMessageBox::detailedSorry(this,
i18n("This function was disabled at compile time because of missing GDAL v1.5.x!"),
i18n("Sportwatcher needs GDAL v1.5.x to enable this function.\n") +
i18n("If you like this to be working, install GDAL version 1.5.x and recompile the source!"));
#endif
}
/*
* Functions to fill in the boxes of the main mask.
*/
void sportwatcherWidget::showLaps()
{
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
QDateTime dt;
QTime t, st;
QDateTime *qt, *ftt, *ltt;
LAP *lap, *flp;
POINT *point, *subPt;
RUN_NODE *rakt, *rn;
int laps, i, anz, men, cad;
double alt_asc, alt_dsc, sum_asc, sum_dsc, old_asc, old_dsc;
bool pause;
long secs;
if (!gmn)
{
KMessageBox::error(this, i18n("No data were loaded!"));
return;
}
if (gmn->type == data_Dnil)
{
KMessageBox::error(0, i18n("No data found!"));
return;
}
if (gmn->type != data_Dlist) /* List of data */
{
KMessageBox::error(0, i18n("Found unexpected data type %1!").arg(gmn->type));
return;
}
ds.destroy();
min_hr = max_hr = avg_hr = 0;
min_height = max_height = 0.0;
min_speed = max_speed = 0.0;
liLaps->clear();
liLaps->setAllColumnsShowFocus(true);
ds.garmin_print_data(gmn);
rn = ds.getRunNode();
rakt = rn;
liLaps->setRootIsDecorated(true);
liLaps->setAlternateBackground(KGlobalSettings::alternateBackgroundColor());
liLaps->setColumnAlignment(1, Qt::AlignRight);
liLaps->setColumnAlignment(2, Qt::AlignRight);
liLaps->setColumnAlignment(3, Qt::AlignRight);
liLaps->setColumnAlignment(4, Qt::AlignRight);
liLaps->setColumnAlignment(5, Qt::AlignRight);
liLaps->setColumnAlignment(6, Qt::AlignRight);
liLaps->setColumnAlignment(7, Qt::AlignRight);
liLaps->setColumnAlignment(8, Qt::AlignRight);
liLaps->setColumnAlignment(9, Qt::AlignRight);
liLaps->setColumnAlignment(10, Qt::AlignRight);
liLaps->setColumnAlignment(11, Qt::AlignRight);
qs_name = qs_distance = qs_etime = qs_avgpace = qs_avgspeed = qs_maxspeed = QString("");
qs_calories = qs_avghr = qs_maxhr = qs_avgcadence = qs_ascent = qs_descent = QString("");
men = 0;
cad = 0;
while (rakt)
{
if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
rakt->run->type == data_D1010)
{
int lt, cal, ahr, mhr;
double distance, speed, mspeed;
QDate dat;
switch (rakt->run->sport_type)
{
case D1000_running: qs_name = QString("Running: "); break;
case D1000_biking: qs_name = QString("Biking: "); break;
case D1000_other: qs_name = QString("Other: "); break;
default:
qs_name = QString("Unknown: ");
}
lap = ds.getLap(rakt->run->first_lap_index);
qt = garmin_dtime (lap->start_time);
StartTime = *qt;
st = qt->time();
dat = qt->date();
delete qt;
qt = 0;
// Find the last track;
// It is possible to delete laps directly on the watch,
// so we can't be sure the last lap is really the last one.
// Tracks are not deleted and the last track contains the
// summuraries we need.
lap = ds.getLap(rakt->run->last_lap_index);
point = ds.getPoint(lap->start_time);
while (point)
{
subPt = ds.getPoint(point->time + 1);
if (!subPt || subPt->distance >= 0x7fffffff)
{
qt = garmin_dtime(point->time);
t = qt->time(); // Every point contains the date and time it was recorded
break;
}
else
point = subPt;
}
if (!qt) // only, if we've not found the last track!
{
qt = garmin_dtime (lap->start_time);
t = qt->addSecs(lap->total_time / 100).time();
}
// There can be pauses during a session. Here we find this pauses
// and subtract the pause seconds from the total time.
subPt = point; // save this track temporary
point = ds.getPoint(0);
pause = false;
ltt = ftt = 0;
secs = 0;
lap = ds.getLap(rakt->run->last_lap_index);
flp = ds.getLap(rakt->run->first_lap_index);
i = rakt->run->first_lap_index;
while (point)
{
if (point->distance >= 0x7fffffff && !pause) // Start of pause?
{
if (ltt)
delete ltt;
ltt = garmin_dtime (point->time);
pause = true;
}
else if (pause) // End of pause
{
QTime t1, t2;
POINT *p;
// Take the track after the stop track to find the real
// time of the pause.
if (point->distance >= 0x7fffffff)
{
if ((p = ds.getPoint(point->time + 1)) == 0)
p = point;
else
point = p;
}
// In case there's no stop track, we take the one
// immediateley after the stop track as our start track.
ftt = garmin_dtime (point->time);
t1 = ltt->time();
t2 = ftt->time();
if (t1 < t2)
secs += t1.secsTo(t2);
delete ltt;
delete ftt;
ltt = ftt = 0;
pause = false;
}
if (point->time >= flp->start_time)
{
if ((unsigned int)i < rakt->run->last_lap_index)
{
i++;
flp = ds.getLap(i);
}
}
point = ds.getPoint(point->time + 1);
}
if (ltt)
delete ltt;
if (ftt)
delete ftt;
point = subPt;
lt = st.secsTo(t);
t.setHMS(0, 0, 0);
t = t.addSecs(lt - secs);
qt->setDate(dat);
qt->setTime(t);
// qs_name.append(dat.toString("dd.MM.yyyy"));
qs_name.append(kl->formatDate(dat, true));
qs_name.append(" ");
// qs_name.append(st.toString("hh:mm:ss"));
qs_name.append(kl->formatTime(st, true));
max_time = lt;
qs_etime = QString(qt->toString("hh:mm:ss.zzz"));
distance = 0.0;
cal = 0;
mspeed = 0;
ahr = mhr = 0;
anz = 0;
cad = 0;
sum_asc = sum_dsc = old_asc = old_dsc = 0;
for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
{
if ((lap = ds.getLap(i)) == NULL)
continue;
distance += lap->total_distance;
cal += lap->calories;
if (lap->avg_cadence != 0xff)
cad += lap->avg_cadence;
ahr += lap->avg_heart_rate;
anz++;
if (lap->max_speed > mspeed)
mspeed = lap->max_speed;
if (lap->max_heart_rate > mhr)
mhr = lap->max_heart_rate;
}
if (point && point->distance > 0 && point->distance < 1.0e20)
{
total_distance = point->distance;
distance = point->distance;
}
else
total_distance = distance;
if (Units == 1) // Statute?
qs_distance.sprintf("%.2f ft", distance / 0.304);
else
qs_distance.sprintf("%.2f m", distance);
if (distance > 0)
{
QTime tt = qt->time();
long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0;
if (Units == 0)
secs = secs * (1000.0 / distance * 100.0);
else
secs = secs * (1609.344 / distance * 100.0);
int h = secs / 3600;
int m = (secs - (h * 3600)) / 60;
int s = secs - ((h * 3600) + (m * 60));
t = QTime(h, m, s, 0);
qs_avgpace = QString(" ") + kl->formatTime(t, true);
if (Units == 0)
qs_avgpace.append(QString(" /km"));
else
qs_avgpace.append(QString(" /mi"));
}
if (Units == 1) // Statute?
speed = distance / lt * 3.6 / 1.609344;
else
speed = distance / lt * 3.6;
qs_avgspeed.sprintf("%.2f %s", speed, (Units == 1) ? "mph" : "km/h");
qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? mspeed * 3.6 / 1.609344 : mspeed * 3.6, (Units == 1) ? "mph" : "km/h");
qs_calories.sprintf("%d", cal);
qs_avghr.sprintf("%d bpm", ahr / anz);
qs_maxhr.sprintf("%d bpm", mhr);
if (cad > 0)
qs_avgcadence.sprintf("%d", cad / anz);
KListViewItem *element = new KListViewItem(liLaps, qs_name, qs_distance,
qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed, qs_calories, qs_avghr);
element->setText(8, qs_maxhr);
element->setText(9, qs_avgcadence);
element->setText(10, qs_ascent);
element->setText(11, qs_descent);
element->sortChildItems(0, false);
element->setOpen(true);
element->setPixmap(0, QPixmap::fromMimeSource(QString("activity.png")));
liLaps->insertItem(element);
delete qt;
/* Get the laps. */
laps = 1;
for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
{
double spd;
char *un;
if ((lap = ds.getLap(i)) == NULL)
continue;
qt = garmin_dtime (lap->start_time);
qs_name.sprintf("Lap %03d - ", laps);
// qs_name.append(qt->toString("hh:mm:ss"));
qs_name.append(kl->formatTime(qt->time(), true));
qs_distance.sprintf("%.2f %s", (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance, (Units == 1) ? "ft" : "m");
t = QTime(0, 0, 0, 0);
t = t.addMSecs(lap->total_time * 10);
qs_etime = t.toString("hh:mm:ss.zzz");
spd = lap->total_distance / (lap->total_time / 100.0);
if (Units == 0)
{
un = (char *)"km/h";
spd *= 3.6;
}
else
{
spd *= 3.6 / 1.609344;
un = (char *)"mph";
}
qs_avgspeed.sprintf("%.2f %s", spd, un);
qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? lap->max_speed * 3.6 / 1.609344 : lap->max_speed * 3.6, un);
qs_calories.sprintf("%d", lap->calories);
if (lap->total_distance > 0 && lap->total_time != 0)
{
double fact;
if (Units == 0)
fact = 1000.0; // 1 km
else
fact = 1609.344; // 1 mile in meters
long secs = (double)lap->total_time / 10000.0 * (fact / lap->total_distance * 100.0);
int h = secs / 3600;
int m = (secs - (h * 3600)) / 60;
int s = secs - ((h * 3600) + (m * 60));
t = QTime(h, m, s, 0);
// qs_avgpace = t.toString("hh:mm:ss");
qs_avgpace = kl->formatTime(t, true);
if (Units == 0)
qs_avgpace.append(QString(" /km"));
else
qs_avgpace.append(QString(" /mi"));
}
qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);
qs_maxhr.sprintf("%d bpm", lap->max_heart_rate);
anz = 0;
alt_asc = alt_dsc = 0;
if ((point = ds.getPoint(lap->start_time)) != 0)
{
if (point->alt < 20000)
{
alt_dsc = alt_asc = point->alt;
if (old_asc == 0)
old_asc = alt_asc;
if (old_dsc == 0)
old_dsc = alt_dsc;
}
else
alt_dsc = alt_asc = 0;
POINT *oldPoint = 0;
double sc, dist, speed;
unsigned long t1, t2;
t1 = t2 = 0;
pause = false;
bool ignore = false;
while (point)
{
if (point->time > (lap->start_time + (lap->total_time / 100)))
break;
if (!oldPoint)
oldPoint = point;
if (point->alt > alt_asc && point->alt < 20000)
{
alt_asc = point->alt;
if (alt_dsc == 0)
alt_dsc = point->alt;
}
if (point->alt < alt_dsc)
alt_dsc = point->alt;
// save the min and max values. We need this information to
// build the graphics.
if (point->heart_rate > max_hr && point->heart_rate < 250)
max_hr = point->heart_rate;
if ((min_hr == 0 && point->heart_rate > 0 && point->heart_rate < 250)
|| (point->heart_rate > 0 && point->heart_rate < min_hr))
min_hr = point->heart_rate;
if (point->alt < 20000 && max_height < point->alt)
max_height = point->alt;
if (point->alt < 20000 && (min_height == 0.0 || min_height > point->alt))
min_height = point->alt;
// Calculate speed of current track
if (!pause && point->distance > 1.0e10)
{
t1 = point->time;
pause = true;
ignore = true;
}
else if (pause)
{
t2 = point->time;
pause = false;
point = ds.getPoint(point->time + 1);
continue;
}
if (!ignore && !pause)
{
sc = point->time - oldPoint->time;
dist = point->distance - oldPoint->distance;
if (t2 > t1)
sc -= t2 - t1;
if (sc > 0.0)
{
speed = (dist / sc) * 3.6;
if (speed > lap->max_speed * 3.6)
speed = lap->max_speed * 3.6;
}
else
speed = 0.0;
if (Units == 1)
speed /= 1.609344;
if (speed > 0.0 && speed < 400.0 && max_speed < speed)
max_speed = speed;
if (speed > 0.0 && (min_speed == 0.0 || min_speed > speed))
min_speed = speed;
oldPoint = point;
}
if (!pause && ignore)
ignore = false;
if (point->heart_rate > 0 && point->heart_rate < 250)
{
avg_hr += point->heart_rate;
men++;
}
point = ds.getPoint(point->time + 1);
}
if (old_asc < alt_asc)
sum_asc += (alt_asc - old_asc);
if (old_dsc > alt_dsc)
sum_dsc += (old_dsc - alt_dsc);
old_asc = alt_asc;
old_dsc = alt_dsc;
qs_ascent.sprintf("%.2f %s", (Units == 1) ? (alt_asc / 0.304) : alt_asc, (Units == 1) ? "ft" : "m");
qs_descent.sprintf("%.2f %s", (Units == 1) ? (alt_dsc / 0.304) : alt_dsc, (Units == 1) ? "ft" : "m");
}
if (lap->avg_cadence != 0xff)
qs_avgcadence.sprintf("%d", lap->avg_cadence);
KListViewItem *edetail = new KListViewItem(element, qs_name, qs_distance,
qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed, qs_calories, qs_avghr);
edetail->setText(8, qs_maxhr);
edetail->setText(9, qs_avgcadence);
edetail->setText(10, qs_ascent);
edetail->setText(11, qs_descent);
edetail->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
switch (rakt->run->sport_type)
{
case D1000_running: edetail->setPixmap(0, QPixmap::fromMimeSource(QString("run.png"))); break;
case D1000_biking: edetail->setPixmap(0, QPixmap::fromMimeSource(QString("bike.png"))); break;
case D1000_other: edetail->setPixmap(0, QPixmap::fromMimeSource(QString("other.png"))); break;
default:
edetail->setPixmap(0, QPixmap::fromMimeSource(QString("other.png")));
}
liLaps->clearSelection();
element->insertItem(edetail);
delete qt;
laps++;
}
qs_ascent.sprintf("%.2f %s", (Units == 1) ? sum_asc / 0.304 : sum_asc, (Units == 1) ? "ft" : "m");
qs_descent.sprintf("%.2f %s", (Units == 1) ? sum_dsc / 0.304 : sum_dsc, (Units == 1) ? "ft" : "m");
element->setText(10, qs_ascent);
element->setText(11, qs_descent);
}
rakt = rakt->next;
}
if (men > 0)
avg_hr /= men;
else
min_hr = max_hr = avg_hr = 0;
}
void sportwatcherWidget::showTrack()
{
showTrack(0, QRect(0, 0, 0, 0), 0);
}
void sportwatcherWidget::showTrack(int zoom)
{
showTrack(zoom, mapPan, mapLap);
}
void sportwatcherWidget::showTrack(int zoom, const QRect &pan, LAP *lap)
{
int width, height;
double x1, y1, x2, y2;
int a, top, left, panX, panY;
uint32 i;
double coordW, coordH, tick;
double meterW, dist, fact;
QPainter paint;
posn_type posNW, posSE, posLXY, posRXY;
POINT *point;
bool Fgeo = false;
bool Data = false;
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
QString fName = MAP;
//double adfGeoTransform[6];
GDALDataset *poDataset = 0;
GDALRasterBand *poBand;
unsigned char *pafScanline;
unsigned char *pafScanlineRed;
unsigned char *pafScanlineGreen;
unsigned char *pafScanlineBlue;
unsigned char *pafScanlineAlpha;
int nXSize, nYSize;
int xOff, yOff;
double oriLeftLon, oriLeftLat, oriRightLon, oriRightLat;
#endif
if (!gmn)
return;
QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));
if (zoom != zfactor)
zfactor = zoom;
if (mapLap != lap)
mapLap = lap;
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"));
cfg->setGroup(QString("WMS"));
bool square = cfg->readBoolEntry("Square", false);
int CorrX = cfg->readNumEntry("CorrX", 0);
int CorrY = cfg->readNumEntry("CorrY", 0);
cfg->setGroup(QString("ImageCoords"));
oriLeftLon = cfg->readDoubleNumEntry("LeftLon", 0.0);
oriLeftLat = cfg->readDoubleNumEntry("LeftLat", 0.0);
oriRightLon = cfg->readDoubleNumEntry("RightLon", 0.0);
oriRightLat = cfg->readDoubleNumEntry("RightLat", 0.0);
// int isrs = cfg->readNumEntry("SRS", 0);
#endif
width = imgMap->width() - 2;
height = imgMap->height();
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
if (MapType == MPT_WMS && square)
pmMap.resize(width / (int)mFactor * (int)mFactor, height / (int)mFactor * (int)mFactor);
else
pmMap.resize(width, height);
#else
pmMap.resize(width, height);
#endif
paint.begin(&pmMap);
panX = panY = 0;
if (stateHand && mapPan != pan)
{
mapPan = pan;
panX = mapPan.right() - mapPan.left();
panY = mapPan.bottom() - mapPan.top();
oldTransX += (double)panX;
oldTransY += (double)panY;
}
memset(&posNW, 0, sizeof(posn_type));
memset(&posSE, 0, sizeof(posn_type));
posSE.lat = 90.0;
posSE.lon = 180.0;
posNW.lat = -90.0;
posNW.lon = -180.0;
/*
* Find out the corners of our track (NW, NE, SE, SW)
*/
if (mapLap)
i = mapLap->start_time;
else
i = 0;
while ((point = ds.getPoint(i)) != 0)
{
if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
break;
if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
{
i = point->time + 1;
continue;
}
if (SEMI2DEG(point->posn.lat) > posNW.lat)
posNW.lat = SEMI2DEG(point->posn.lat);
if (SEMI2DEG(point->posn.lat) < posSE.lat)
posSE.lat = SEMI2DEG(point->posn.lat);
if (SEMI2DEG(point->posn.lon) > posNW.lon)
posNW.lon = SEMI2DEG(point->posn.lon);
if (SEMI2DEG(point->posn.lon) < posSE.lon)
posSE.lon = SEMI2DEG(point->posn.lon);
i = point->time + 1;
Data = true;
}
coordW = (posNW.lon > posSE.lon) ? posNW.lon - posSE.lon : posSE.lon - posNW.lon;
coordH = (posNW.lat > posSE.lat) ? posNW.lat - posSE.lat : posSE.lat - posNW.lat;
meterW = ds.earth_distance(posNW.lat, posNW.lon, posNW.lat, posSE.lon);
// define the ticks to translate the GPS coordinates into pixels.
// The track should be centered and we have to calculate the
// rectangular within we draw the track.
if (coordW < coordH)
{
tick = (double)width / coordW + (double)zoom;
if ((tick * coordH) > height)
tick = (double)height / coordH + (double)zoom;
}
else
{
tick = (double)height / coordH + (double)zoom;
if ((tick * coordW) > width)
tick = (double)width / coordW + (double)zoom;
}
left = width - (width - tick * coordW) / 2;
top = (height - tick * coordH) / 2;
a = tick * coordW;
if (Units == 0)
dist = meterW / a; // Meters
else
dist = meterW * 1.609344 / a; // 1/1000 mile (5.28 feet)
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
geoRect.llat = 0.0;
geoRect.llon = 0.0;
geoRect.rlat = 0.0;
geoRect.rlon = 0.0;
geoRect.width = width;
geoRect.height = height;
/*
* If we have a map file, we try to read it and if successfull,
* we should get a map painted.
*
* Currently only WMS-Server is supported, allthough GDAL allows
* several other formats too.
*/
if (!MAP.isEmpty() && Data)
{
bool writeTag = true;
double mtx, mty;
double vx, vy;
xOff = yOff = 0;
posRXY.lon = posNW.lon + (width - left + oldTransX) / tick;
posLXY.lat = posNW.lat + (top + oldTransY) / tick;
posLXY.lon = posSE.lon - (left - a - oldTransX) / tick;
posRXY.lat = posSE.lat - (height - top - (tick * coordH) - oldTransY) / tick;
geoRect.llat = posLXY.lat;
geoRect.llon = posLXY.lon;
geoRect.rlat = posRXY.lat;
geoRect.rlon = posRXY.lon;
// width and height of map in meters
mtx = ds.earth_distance(posRXY.lat, posRXY.lon, posRXY.lat, posLXY.lon);
mty = ds.earth_distance(posRXY.lat, posRXY.lon, posLXY.lat, posRXY.lon);
// factor to correct the map, in case we use a WMS server
if (MapType == MPT_WMS)
{
vx = (posRXY.lon - posLXY.lon) / mtx * CorrX;
vy = (posRXY.lat - posLXY.lat) / mty * CorrY;
posRXY.lon += vx;
posRXY.lat += vy;
posLXY.lon += vx;
posLXY.lat += vy;
}
/*
* Write a control file for GDAL, if we use a WMS server.
* Warp an image if we use PNG or BMP or GIF.
* Warp a region if we use TIFF
*/
if (MapType == MPT_WMS)
writeTag = writeWMSTag(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat, width, height);
if (MapType == MPT_GIF || MapType == MPT_BMP || MapType == MPT_PNG ||
MapType == MPT_SGI || MapType == MPT_TIF)
writeTag = warpImage(MAP, &fName);
if (writeTag)
{
if (MapType != MPT_SHP && (poDataset = (GDALDataset *)GDALOpen (fName.ascii(), GA_ReadOnly)) != NULL)
{
QPixmap bild;
int nRasterCount = poDataset->GetRasterCount();
int nXBlock, nYBlock;
GDALColorTable *pCT, *pCTb, *pCTr, *pCTg, *pCTa;
int bGotMin, bGotMax;
int tTypeLen, tColor, tColorEntrys;
GDALDataType tRasterType;
double adfMinMax[2];
pafScanlineRed = pafScanlineGreen = pafScanlineBlue = pafScanlineAlpha = 0;
/*
* Read every raster band.
*
* If we get 3 raster bands, the image is a 24 bit image.
* If we get 4 raster bands, the image is probably a 24 bit
* image with an alpha channel. Currently the alpha channel
* is ignored!
* If we have 1 raster band, the image is 8 bit monochrom.
* Otherwise the image is undefined and the results are also.
*/
for (a = 1; a <= nRasterCount; a++)
{
poBand = poDataset->GetRasterBand (a);
poBand->GetBlockSize (&nXBlock, &nYBlock);
nXSize = poBand->GetXSize();
nYSize = poBand->GetYSize();
tRasterType = poBand->GetRasterDataType ();
tTypeLen = GDALGetDataTypeSize (tRasterType) / 8; // We need Bytes not Bits!
tColor = poBand->GetColorInterpretation ();
adfMinMax[0] = poBand->GetMinimum (&bGotMin);
adfMinMax[1] = poBand->GetMaximum (&bGotMax);
if (!(bGotMin && bGotMax))
GDALComputeRasterMinMax ((GDALRasterBandH)poBand, TRUE, adfMinMax);
if ((pCT = poBand->GetColorTable()) != NULL)
tColorEntrys = poBand->GetColorTable()->GetColorEntryCount();
switch (a)
{
case 1: pafScanlineRed = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineRed; pCTr = pCT; break;
case 2: pafScanlineGreen = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineGreen; pCTg = pCT; break;
case 3: pafScanlineBlue = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineBlue; pCTb = pCT; break;
case 4: pafScanlineAlpha = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineAlpha; pCTa = pCT; break;
}
memset (pafScanline, 0, tTypeLen * nXSize * nYSize);
/*
* Get the image (from the server) and put the tiles together.
*
* The function reads only one raster band. This is,
* because the function is called for every raster band and
* every raster band is stored into a separate array.
*/
if (poBand->RasterIO (GF_Read, 0, 0, nXSize, nYSize, pafScanline, nXSize, nYSize, tRasterType, 0, 0) == CE_Failure)
{
KMessageBox::error(this, i18n("Error reading a raster band!"));
Fgeo = false;
break;
}
else
Fgeo = true;
}
/*
* Only if Fgeo is TRUE, we've read successfully all raster
* bands. Now we have to put the bands together to get
* an image.
*/
if (Fgeo)
{
unsigned char *pCombinedBytes = new unsigned char[(tTypeLen * nXSize * nYSize * nRasterCount)];
unsigned char *ptr_dest, *ptr_src;
int j;
/*
* We need two nested loops to set the pixels in the wanted
* order.
*/
for (a = 0, j = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount, j++)
{
int k = a;
for (int m = nRasterCount - 1; m >= 0; m--, k++)
{
unsigned char *pBytes;
switch (m)
{
case 3: pBytes = pafScanlineAlpha; pCT = pCTa; break;
case 2: pBytes = pafScanlineBlue; pCT = pCTb; break;
case 1: pBytes = pafScanlineGreen; pCT = pCTg; break;
default: pBytes = pafScanlineRed; pCT = pCTr;
}
ptr_dest = pCombinedBytes + k;
unsigned char b = pBytes[j];
/*
* If we have a color table, the pixels are pointers
* to the color table. We need to convert them into
* 24 bit pixels plus an optional alpha channel.
*/
if (pCT != NULL)
{
GDALColorEntry ce;
unsigned int c = (unsigned int)b;
c = pCT->GetColorEntryAsRGB (c, &ce);
if (m == 0) c = ce.c1;
if (m == 1) c = ce.c2;
if (m == 2) c = ce.c3;
if (m == 3) c = ce.c4;
b = (unsigned char)c;
}
ptr_src = &b;
memcpy (ptr_dest, ptr_src, 1);
}
}
x1 = y1 = 0;
/*
* The following loop is QT specific! It sets the pixels
* of the raw image, pixel by pixel. This may be slow, but
* everything else didn't work :-(
*
* FIXME: We need a more effective routine to put the
* raw image into QT's "painter" class.
*/
for (a = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount)
{
if (x1 < width && y1 < height)
{
if (nRasterCount == 3)
paint.setPen (QPen(QColor((int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a]), QPen::SolidLine));
else if (nRasterCount > 3)
paint.setPen (QPen(QColor(qRgba((int)pCombinedBytes[a+3], (int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a])), QPen::SolidLine));
else if (nRasterCount == 2)
paint.setPen (QPen(QColor((int)pCombinedBytes[a+1], (int)pCombinedBytes[a], (int)pCombinedBytes[a+1]), QPen::SolidLine));
else if (nRasterCount == 1)
paint.setPen (QPen(QColor((int)pCombinedBytes[a], (int)pCombinedBytes[a], (int)pCombinedBytes[a]), QPen::SolidLine));
paint.drawPoint(x1, y1);
}
x1++;
if (x1 >= nXSize)
{
x1 = 0;
y1++;
}
}
delete pCombinedBytes;
}
if (pafScanlineRed)
delete pafScanlineRed;
if (pafScanlineGreen)
delete pafScanlineGreen;
if (pafScanlineBlue)
delete pafScanlineBlue;
if (pafScanlineAlpha)
delete pafScanlineAlpha;
GDALClose (poDataset);
poDataset = 0;
if (MAP != fName)
unlink (fName.ascii());
}
else if (MapType == MPT_SHP)
{
QDir shpd(MAP, QString("*.shp"));
if (shpd.count() < 1)
{
KMessageBox::error(this, i18n("There is no shape file in directory ") + MAP);
Fgeo = false;
}
else
{
OGRDataSource *poDS = 0;
OGRLayer *poLy;
OGRFeature *poFeat;
OGRFeatureDefn *poFDefn;
OGRFieldDefn *poFieldDefn;
OGRGeometry *poGeometry;
OGRPoint *poPoint;
int nLayers, iField;
QString sfn;
OGRRegisterAll ();
// read all shape files and put them together
for (a = 0; a < shpd.count(); a++)
{
sfn = MAP + shpd[a];
poDS = OGRSFDriverRegistrar::Open(sfn.ascii(), false);
if (!poDS)
{
cerr << "Error opening the file " << sfn.ascii() << "!" << endl;
break;
}
nLayers = poDS->GetLayerCount ();
// Read all layers of a shape file
for (int j = 0; j< nLayers; j++)
{
if ((poLy = poDS->GetLayer (j)) == NULL)
{
cerr << "Error getting layer " << j << "!" << endl;
continue;
}
poLy->SetSpatialFilterRect (geoRect.llon, geoRect.llat, geoRect.rlon, geoRect.rlat);
// Read all features of a layer and get the fields
// of the features
poLy->ResetReading();
while ((poFeat = poLy->GetNextFeature()) != NULL)
{
if ((poFDefn = poLy->GetLayerDefn()) == NULL)
{
cerr << "Error getting a layer defination!" << endl;
continue;
}
poGeometry = poFeat->GetGeometryRef();
if (poGeometry != NULL && wkbFlatten (poGeometry->getGeometryType()) == wkbPoint)
{
poPoint = (OGRPoint *) poGeometry;
cout << "pointX: " << poPoint->getX() << ", pointY: " << poPoint->getY() << endl;
}
for (iField = 0; iField < poFDefn->GetFieldCount(); iField++)
{
poFieldDefn = poFDefn->GetFieldDefn (iField);
if (poFieldDefn->GetType() == OFTInteger)
cout << "Layer: " << j << " int: " << poFeat->GetFieldAsInteger (iField) << endl;
else if (poFieldDefn->GetType() == OFTReal)
cout << "Layer: " << j << " float: " << poFeat->GetFieldAsDouble (iField) << endl;
else if (poFieldDefn->GetType() == OFTString)
cout << "Layer: " << j << " string: " << poFeat->GetFieldAsString (iField) << endl;
else
cout << "Layer: " << j << " Unkn.: " << poFeat->GetFieldAsString (iField) << endl;
}
cout << "----------------------------" << endl;
}
OGRFeature::DestroyFeature (poFeat);
}
OGRDataSource::DestroyDataSource(poDS);
}
}
}
else
{
KMessageBox::error(this, i18n("Error opening map file!"));
Fgeo = false;
}
}
}
#endif
/*
* Here we come to draw the track. It will be drawn over the previous
* created map image.
*/
// Colors and fonts
QColor background(220, 220, 220); // background color
QColor red(255, 0, 0); // mile marker
QColor black(0, 0, 0); // Text, center of track
// QColor yellow(255, 255, 0);
QColor yellow(0x00cf, 0x00ff, 0x0000); // color of track
QFont fntNormal("Helvetica");
fntNormal.setPixelSize(10);
fntNormal.setStyleHint(QFont::Helvetica);
QPen dot(red, 4, QPen::SolidLine);
QPen line(black, 2, QPen::SolidLine);
QPen yline(yellow, 5, QPen::SolidLine);
// Fill background with background colors, if there is no map.
if (!Fgeo)
paint.fillRect(0, 0, width+2, height+2, background);
if (Units == 0)
fact = 1000.0;
else
fact = 1609.344;
paint.setPen(line);
paint.drawLine(10, height - 9, 10, height - 4);
paint.drawLine(10, height - 4, 10 + (fact / dist), height - 4);
paint.drawLine(10 + (fact / dist), height - 9, 10 + (fact / dist), height - 4);
paint.setFont(fntNormal);
if (Units == 0)
paint.drawText(10, height - 10, QString("1000 m"));
else
paint.drawText(10, height - 10, QString("5280 ft"));
// Draw track
if (mapLap)
i = mapLap->start_time;
else
i = 0;
x1 = y1 = 0.0;
bool wStart = false;
while ((point = ds.getPoint(i)) != 0)
{
if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
break;
if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
{
i = point->time + 1;
continue;
}
x2 = (left + ((posNW.lon - SEMI2DEG(point->posn.lon)) * tick * -1)) + oldTransX;
y2 = (top + ((posNW.lat - SEMI2DEG(point->posn.lat)) * tick)) + oldTransY;
if (!wStart && x1 != 0.0 && y1 != 0.0)
{
// Load the start symbol
QPixmap qpx = QPixmap::fromMimeSource(QString("wstart.png"));
// Find the angle of the track and turn the symbol accordingly
// we use Pythagoras to calculate the triangle
double xl = (x1 < x2) ? x2 - x1 : x1 - x2;
double yl = (y1 < y2) ? y2 - y1 : y1 - y2;
double da = fmin (xl, yl);
double db = fmax (xl, yl);
double zl = sqrt (pow (da, 2) + pow (db, 2));
double angle = (asin(da / zl) / M_PIl) * 180.0;
angle = (angle > 45.0) ? 90.0 - angle : angle;
// cout << "Winkel: " << angle << " ---- X: " << xl << ", Y: " << yl << ", Z: " << zl << ", Point (x1,y1,x2,y2): " << x1 << ", " << y1 << ", " << x2 << ", " << y2 << endl;
if (x1 < x2 && y1 < y2) // right, down
angle = 90.0 + angle;
else if (x1 > x2 && y1 < y2) // left, down
angle = 270.0 - angle;
else if (x1 > x2 && y1 > y2) // left, up
angle = 270.0 + angle;
else // right, up
angle = 90.0 - angle;
// cout << "Realer Winkel: " << angle << endl;
// Set the center of the symbol
paint.save ();
paint.translate (x1, y1);
// rotate the symbol
paint.rotate (angle);
paint.drawPixmap (-8, -8, qpx);
paint.restore ();
wStart = true;
}
if (x1 == 0.0 && y1 == 0.0)
{
x1 = x2;
y1 = y2;
}
paint.setPen(yline);
paint.drawLine(x1, y1, x2, y2);
if ((point->distance - dist) >= fact) // a dot at every 1000 meters or at 1 mile
{
paint.setPen(dot);
paint.drawEllipse(x2-2, y2-2, 3, 3);
dist = (int)(point->distance / fact) * fact;
}
paint.setPen(line);
paint.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
i = point->time + 1;
}
bool lastLap = false;
if (mapLap)
if (ds.getRunNode()->run->last_lap_index == mapLap->index)
lastLap = true;
if ((!mapLap || lastLap) && wStart)
{
// load the end symbol
QPixmap qpx = QPixmap::fromMimeSource(QString("wtarget.png"));
paint.drawPixmap (x2, y2 - 16, qpx);
}
paint.end();
imgMap->setPixmap(pmMap);
QApplication::restoreOverrideCursor();
}
void sportwatcherWidget::showCurves()
{
showCurves (mapLap);
}
void sportwatcherWidget::showCurves(LAP *lap)
{
QPainter paint;
int width, height;
int i, secs, cuType;
int lineHeight, margin_left, margin_right, margin_bottom;
int x1, y1, x2, y2; // Coordinates
bool meter;
double maxHeight, minHeight, maxSpeed, minSpeed;
int maxHr, minHr, rh;
POINT *point;
RUN_NODE *rn;
LAP *lp;
double w_tick, h_tick; // Number of pixels one "tick" has;
// This depends on the width and height
// of the image.
// First we draw a grid based on the min and max
// values detected in the function showLap(). In case
// all values are 0, we exit here.
if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
return;
// Look up, what curves we should draw
cuType = kcbCurveTypes->currentItem();
// Get the dimensions of the available draw area
width = imgProfile->width() - 2;
height = imgProfile->height();
pmProfile.resize(width, height);
paint.begin(&pmProfile);
// we need a somewhat bigger area to draw our curves than
// we have with the real min and max values.
if (max_height > 0.0)
{
double add = (max_height - min_height) / 100.0 * 5.0; // Percent
maxHeight = max_height + add;
minHeight = min_height - add;
if (minHeight < 0.0) // make sure, we are not too deep
minHeight = 0.0;
}
else
maxHeight = minHeight = 0.0;
if (max_speed > 0.0)
{
double add = (max_speed - min_speed) / 100.0 * 5.0; // Percent
maxSpeed = max_speed + add;
minSpeed = min_speed - add;
if (minSpeed < 0.0) // make sure, we are not too deep
minSpeed = 0.0;
}
else
maxSpeed = minSpeed = 0.0;
if (max_hr > 0)
{
maxHr = max_hr + 10;
minHr = min_hr - 10;
if (minHr < 0)
minHr = 0;
}
else
maxHr = minHr = 0;
// Define colors
QColor background(220, 220, 220); // Background of graphic
QColor mark(255, 255, 255); // Lines inside curve area
QColor hlight(180, 180, 180); // area of current lap
// hlight.setAlpha(128); // 50% transparent
QColor frame(0, 0, 0); // Text and borders
QColor barcol(151, 190, 13); // heart rate
QColor barcol2(190, 151, 13); // height over NN
QColor red(220, 128, 128); // speed
QColor blue(0, 0, 240);
QFont fntNormal("Helvetica");
// QFont fntBold("Helvetica", 10, QFont::Bold);
fntNormal.setPixelSize(10);
fntNormal.setStyleHint(QFont::Helvetica);
// fntBold.setPixelSize(10);
// fntBold.setStyleHint(QFont::Helvetica);
// Calculate ticks
margin_left = 52;
margin_right = 40;
margin_bottom = 12;
lineHeight = 10;
rh = height - margin_bottom - 1;
w_tick = (double)(width - (margin_left + margin_right)) / max_time; // 1 tick = 1 second
if (cuType == 1) // Speed and heart rate?
{
if ((maxSpeed - minSpeed) > (double)(maxHr - minHr))
{
h_tick = (double)rh / (maxSpeed - minSpeed); // 1 tick = 1 km/h
meter = true;
}
else
{
h_tick = (double)rh / ((double)maxHr - (double)minHr); // 1 tick = 1 bpm
meter = false;
}
}
else if (cuType == 2) // Elevation and speed?
{
if ((maxHeight - minHeight) > (double)(maxHr - minHr))
{
h_tick = (double)rh / (maxHeight - minHeight); // 1 tick = 1 meter
meter = true;
}
else
{
h_tick = (double)rh / (maxSpeed - minSpeed); // 1 tick = 1 km/h
meter = false;
}
}
else // Elevation and heart rate
{
if ((maxHeight - minHeight) > (double)(maxHr - minHr))
{
h_tick = (double)rh / (maxHeight - minHeight); // 1 tick = 1 meter
meter = true;
}
else
{
h_tick = (double)rh / ((double)maxHr - (double)minHr); // 1 tick = 1 bpm
meter = false;
}
}
// Fill background with background colors
paint.fillRect(0, 0, width + 4, height + 4, background);
// Draw a grid with markers at every 10 minutes
paint.setPen(QPen(frame, 1, QPen::SolidLine));
// Bottom border line
x1 = margin_left;
y1 = height - margin_bottom;
x2 = width - margin_right;
y2 = y1;
paint.drawLine(x1, y1, x2, y2);
// Left border line
x1 = x2 = margin_left;
y1 = 2;
y2 = height - margin_bottom;
paint.drawLine(x1, y1, x2, y2);
// right border line
x1 = x2 = width - margin_right;
paint.drawLine(x1, y1, x2, y2);
// Draw some darker lines to show the laps, if we have one
// and, in case we have a given lap, we fill the area.
QDateTime *qt;
QTime zeit = StartTime.time();
rn = ds.getRunNode();
paint.setPen(QPen(hlight, 1, QPen::SolidLine));
for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
{
if ((lp = ds.getLap(i)) == NULL)
continue;
qt = garmin_dtime(lp->start_time);
secs = zeit.secsTo(qt->time());
delete qt;
x1 = secs * w_tick + margin_left + 1;
if (lap && lp->start_time == lap->start_time)
paint.fillRect(x1, 2, (int)((double)lap->total_time / 100.0 * w_tick), height - margin_bottom - 2, hlight);
else
paint.drawLine(x1, 2, x1, height - margin_bottom);
}
// Grid vertical
paint.setPen(QPen(frame, 1, QPen::SolidLine));
paint.setFont(fntNormal);
paint.drawText(margin_left - 20, height - lineHeight, 40, lineHeight, Qt::AlignCenter, QString("00:00"));
paint.save();
paint.rotate(270);
if (cuType == 1)
paint.setPen(QPen(red, 1, QPen::SolidLine));
else
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
// Information on left side
if (cuType == 0)
paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
else if (cuType == 1)
paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
else
paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
if (cuType == 2)
paint.setPen(QPen(red, 1, QPen::SolidLine));
else
paint.setPen(QPen(blue, 1, QPen::SolidLine));
// Information on right side
if (cuType < 2)
paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
else
paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
paint.restore();
paint.setPen(QPen(mark, 1, QPen::SolidLine));
// Draw the time scale on the bottom of the graphic
for (i = 0; (unsigned int)i < max_time; i++)
{
if (i > 0 && !(i % 600)) // every 10 minutes
{
x1 = x2 = margin_left + w_tick * i;
if (x1 == (width - margin_right))
continue;
y1 = 2;
y2 = height - margin_bottom;
paint.drawLine(x1, y1, x2, y2);
QTime tm(0, 0, 0);
tm = tm.addSecs(i);
paint.setPen(QPen(frame, 1, QPen::SolidLine));
// paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((i >= 3600) ? "hh:mm:ss" : "mm:ss"));
paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
paint.setPen(QPen(mark, 1, QPen::SolidLine));
}
}
// This is the total time, with pauses included, at the lower right
// corner of the graphic.
QTime tm(0, 0, 0);
QString qs;
tm = tm.addSecs(max_time);
paint.setPen(QPen(frame, 1, QPen::SolidLine));
// paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((max_time >= 3600) ? "hh:mm:ss" : "mm:ss"));
paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
// Draw the minimal elevation, speed and/or heart rate
if (max_height > 0.0 || max_speed > 0.0)
{
// left side
if (cuType == 1)
{
paint.setPen(QPen(red, 1, QPen::SolidLine));
paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minSpeed));
}
else
{
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
}
}
if (max_hr > 0 || max_speed > 0.0)
{
// right side
if (cuType == 2)
{
paint.setPen(QPen(red, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));
}
else
{
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
}
}
paint.setPen(QPen(mark, 1, QPen::SolidLine));
// Grid horizontal
int factor = 0;
int target = 0;
if (cuType == 0) // Elevation and heart rate
{
factor = (meter) ? (maxHeight - minHeight) / (rh / 12) : (maxHr - minHr) / (rh / 12);
target = (meter) ? (int)(maxHeight - minHeight) : (maxHr - minHr);
}
else if (cuType == 1) // Speed and heart rate
{
factor = (meter) ? (maxSpeed - minSpeed) / (rh / 12) : (maxHr - minHr) / (rh / 12);
target = (meter) ? (int)(maxSpeed - minSpeed) : (maxHr - minHr);
}
else // Elevation and speed
{
factor = (meter) ? (maxHeight - minHeight) / (rh /12) : (maxSpeed - minSpeed) / (rh / 12);
target = (meter) ? (int)(maxHeight - minHeight) : (int)(maxSpeed - minSpeed);
}
// To prevent a division by zero error, we check the <factor>
if (factor == 0)
factor = 1;
// Beside the horizontal part of the grid, we draw the scale on the
// left and the right side.
int oldy = height;
for (i = 0; i < target; i++)
{
if (i > 0 && !(i % factor))
{
x1 = margin_left + 1;
x2 = width - margin_right - 1;
y1 = y2 = rh - h_tick * i;
if (y1 < 12)
break;
paint.drawLine(x1, y1, x2, y2);
if (y1 < (oldy - lineHeight))
{
if (meter)
{
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
// left side
if (cuType == 1)
{
paint.setPen(QPen(red, 1, QPen::SolidLine));
paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + i));
}
else
paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));
// right side
if (maxHr > 0 && cuType != 2)
{
double hrscale = (double)(maxHr - minHr) / (double)target;
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", (int)((double)minHr + hrscale * (double)i)));
}
else
{
double spscale = (maxSpeed - minSpeed) / (double)target;
if (cuType == 2)
paint.setPen(QPen(red, 1, QPen::SolidLine));
else
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", (minSpeed + spscale * (double)i)));
}
}
else
{
// right side
if (cuType == 2)
{
paint.setPen(QPen(red, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
}
else
{
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
}
// left side
if ((cuType == 0 || cuType == 2) && max_height > 0)
{
double hrscale = (maxHeight - minHeight) / (double)target;
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + hrscale * (double)i));
}
else if (max_speed > 0 && cuType == 1)
{
double hrscale = (maxSpeed - minSpeed) / (double)target;
paint.setPen(QPen(red, 1, QPen::SolidLine));
paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + hrscale * (double)i));
}
}
paint.setPen(QPen(mark, 1, QPen::SolidLine));
oldy = y1;
}
}
}
// To make our graphics more beautiful, we draw lines for the
// heart rate limits and the average heart rate.
if (max_hr > 0 && cuType != 2)
{
int ay1, ay2, ay3, ay4, ay5;
x1 = margin_left + 1;
x2 = width - margin_right - 1;
if (meter)
{
double hrscale = rh / (double)(maxHr - minHr);
ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
}
else
{
ay1 = (double)rh - (double)(lower1 - minHr) * h_tick;
ay2 = (double)rh - (double)(lower2 - minHr) * h_tick;
ay3 = (double)rh - (double)(lower3 - minHr) * h_tick;
ay4 = (double)rh - (double)(upper3 - minHr) * h_tick;
ay5 = (double)rh - (double)(avg_hr - minHr) * h_tick;
}
paint.setPen(QPen(barcol2, 1, QPen::DashLine)); // color for limits
if (lower1 > minHr && lower1 < maxHr)
paint.drawLine(x1, ay1, x2, ay1);
if (lower2 > minHr && lower2 < maxHr)
paint.drawLine(x1, ay2, x2, ay2);
if (lower3 > minHr && lower3 < maxHr)
paint.drawLine(x1, ay3, x2, ay3);
if (upper3 > minHr && upper3 < maxHr)
paint.drawLine(x1, ay4, x2, ay4);
paint.setPen(QPen(red, 1, QPen::DashDotLine)); // color for average heart rate
if (avg_hr > minHr && avg_hr < maxHr)
paint.drawLine(x1, ay5, x2, ay5);
}
// Now we have a grid and we've done the scaling.
// This is the point where we draw the curves itself.
// We use different colors to draw the lines:
//
// Green: Elevation
// Red: Speed
// Blue Heart Rate
//
x1 = x2 = y1 = y2 = 0;
int hy1, hy2, hx1, hx2;
int sy1, sy2, sx1, sx2;
hy1 = hy2 = hx1 = hx2 = 0;
sy1 = sy2 = sx1 = sx2 = 0;
int hEc = 0;
i = 0;
AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
avgHfirst = avgHlast = 0;
// To even the surface lines, we store every altitude in the
// memory, by building a chain. Then, if the user has set in the
// settings (Contour == true), we will even the line by calculating
// the average of 10 messure points and setting every value to the
// average, who is up or down more than 2 meters from the average.
while ((point = ds.getPoint(i)) != 0)
{
if (point->alt > 20000.0 || point->alt < -1000.0)
{
i++;
continue;
}
if (!avgHeight)
{
avgHeight = new AVGHEIGHT;
avgHeight->alt = point->alt;
avgHeight->pos = hEc;
avgHeight->prev = 0;
avgHeight->next = 0;
avgHakt = avgHeight;
avgHfirst = avgHeight;
}
else
{
avgHakt = new AVGHEIGHT;
avgHakt->alt = point->alt;
avgHakt->pos = hEc;
avgHakt->next = 0;
avgHakt->prev = avgHeight;
avgHeight->next = avgHakt;
avgHeight = avgHakt;
}
// FIXME: Currently we can not draw below 0 meters, because the
// base line is always 0!
if (avgHakt->alt < minHeight)
avgHakt->alt = minHeight;
hEc++;
i++;
}
avgHlast = avgHeight;
// If wanted, even the lines
if (Contour && hEc > 0 && cuType != 0)
{
double alt[100], avg, avg1, avg2, avg3, avg4;
int a, pos;
for (i = 0; i < (hEc + 100); i += 100)
{
avg = avg1 = avg2 = avg3 = avg4 = 0.0;
pos = 0;
for (a = 0; a < 100; a++)
{
alt[a] = getAvgAlt(avgHfirst, i + a);
avg += alt[a];
if (a < 25)
avg1 += alt[a];
else if (a < 50)
avg2 += alt[a];
else if (a < 75)
avg3 += alt[a];
else
avg4 += alt[a];
}
if ((i + 100) >= hEc)
avg /= (double)(hEc - i) + 1.0;
else
avg /= 100.0;
for (a = 0; a < 100; a++)
{
if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
{
if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
avgHakt->alt = avg;
}
}
}
}
// plot the elevation, speed and/or heart rate. Depends on <cuType>)
i = 0;
int j = 0;
POINT *oldPoint = 0;
double speed = 0.0; // calculated speed
bool pause = false; // filter pause out of speed
unsigned long t1, t2;
t1 = t2 = 0;
while ((point = ds.getPoint(i)) != 0)
{
if (!oldPoint)
oldPoint = point;
// calculate the y position based on the time
qt = garmin_dtime(point->time);
secs = zeit.secsTo(qt->time());
delete qt;
x2 = secs * w_tick + margin_left + 1;
hx2 = x2;
sx2 = x2;
if (x1 == 0)
x1 = x2;
if (hx1 == 0)
hx1 = hx2;
if (sx1 == 0)
sx1 = sx2;
// The speed is not very exact, because smallest time is
// one second. This allows a maximum error of 99 hundredths
// of a second, what is very close to one second. Because of
// this, speed seems to hop for every messure point. This
// looks ugly, but currently I don't know how to make it
// better.
if (cuType == 1 || cuType == 2) // Draw speed?
{
double dist;
double sc;
if (!pause && point->distance > 1.0e10)
{
pause = true;
t1 = point->time;
}
else if (pause)
{
pause = false;
t2 = point->time;
i += 2;
continue;
}
if (point->distance >= 0.1 && point->distance < 1.0e10)
{
dist = point->distance - oldPoint->distance;
sc = point->time - oldPoint->time;
LAP *runde = ds.getLapT (point->time);
if (t2 > t1)
{
sc -= t2 - t1;
if (sc <= 0.0)
sc = 1.0; // at least 1 second!
}
speed = (dist / sc) * 3.6;
if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
speed = runde->max_speed * 3.6;
if (Units == 1)
speed /= 1.609344;
if (speed < minSpeed || speed > 400.0)
speed = minSpeed;
if ((meter && cuType == 1) || (!meter && cuType == 2))
y2 = (double)rh - (speed - minSpeed) * h_tick;
else
{
double hrscale = rh / (maxSpeed - minSpeed);
y2 = (double)rh - (speed - minSpeed) * hrscale;
}
if (y1 == 0)
y1 = y2;
paint.setPen(QPen(red, 1, QPen::SolidLine));
paint.drawLine(x1, y1, x2, y2);
y1 = y2;
x1 = x2;
t1 = t2 = 0;
oldPoint = point;
}
}
if (cuType == 0 || cuType == 2) // Draw elevation?
{
if (point->alt < 20000.0 && point->alt > -1000.0)
{
double alt = getAvgAlt(avgHfirst, j);
j++;
if (meter)
sy2 = (double)rh - (alt - minHeight) * h_tick;
else
{
double hrscale = rh / (maxHeight - minHeight);
sy2 = (double)rh - (alt - minHeight) * hrscale;
}
if (sy1 == 0)
sy1 = sy2;
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawLine(sx1, sy1, sx2, sy2);
sy1 = sy2;
sx1 = sx2;
}
}
if (point->heart_rate > 0 && cuType < 2) // Draw heart rate?
{
if (meter)
{
double hrscale = rh / (double)(maxHr - minHr);
hy2 = (double)rh - (double)(point->heart_rate - minHr) * hrscale;
}
else
hy2 = (double)rh - (double)(point->heart_rate - minHr) * h_tick;
if (hy1 == 0)
hy1 = hy2;
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawLine(hx1, hy1, hx2, hy2);
hy1 = hy2;
hx1 = hx2;
}
i++;
}
paint.end();
imgProfile->setPixmap(pmProfile);
// free the chain of altitudes
avgHakt = avgHfirst;
while (avgHakt)
{
avgHeight = avgHakt->next;
delete avgHakt;
avgHakt = avgHeight;
}
// if (bProfile)
// bitBlt(imgProfile, 0, 0, &pmProfile);
}
double sportwatcherWidget::getAvgAlt(AVGHEIGHT *avgHeight, int pos)
{
AVGHEIGHT *akt;
if (!avgHeight)
return 0.0;
akt = avgHeight;
while (akt)
{
if (akt->pos == pos)
return akt->alt;
akt = akt->next;
}
return 0.0;
}
AVGHEIGHT *sportwatcherWidget::getAvgPtr(AVGHEIGHT *avgHeight, int pos)
{
AVGHEIGHT *akt;
akt = avgHeight;
while (akt)
{
if (akt->pos == pos)
return akt;
akt = akt->next;
}
return 0;
}
void sportwatcherWidget::resizeEvent(QResizeEvent */* *e */)
{
showTrack(zfactor);
showCurves();
}
void sportwatcherWidget::paintEvent(QPaintEvent */* *e */)
{
showTrack(zfactor);
showCurves();
}
void sportwatcherWidget::mouseMoveEvent(QMouseEvent *e)
{
QPoint pos(0, 0);
QPoint ev = imgMap->mapFrom(this, e->pos());
static QRect coord;
if (!stateHand)
return;
if (ev.x() >= pos.x() &&
ev.y() >= pos.y() &&
ev.x() <= (pos.x() + imgMap->geometry().width()) &&
ev.y() <= (pos.y() + imgMap->geometry().height()))
{
if (lmbPressed == 1)
{
coord.setCoords(ev.x(), ev.y(), 0, 0);
lmbPressed = 0;
}
else
{
coord.setRight(ev.x());
coord.setBottom(ev.y());
}
if (lmbPressed == 2)
{
showTrack(zfactor, coord, mapLap);
lmbPressed = 0;
}
}
}
void sportwatcherWidget::mousePressEvent(QMouseEvent *e)
{
if (stateHand && e->button() == QMouseEvent::LeftButton)
lmbPressed = 1; // Left Mouse Button is pressed
else if (stateHand)
lmbPressed = 0; // Wrong button is pressed
if (stateGlas)
{
if (e->button() == QMouseEvent::LeftButton)
btGlasPlusSlot();
else if (e->button() == QMouseEvent::RightButton)
btGlasMinusSlot();
}
}
void sportwatcherWidget::mouseReleaseEvent(QMouseEvent *e)
{
if (stateHand && e->button() == QMouseEvent::LeftButton)
{
lmbPressed = 2; // Left Mouse Button was released
mouseMoveEvent(e);
}
}
/*
* Private functions to help decode the data
*/
QDateTime *sportwatcherWidget::garmin_dtime (uint32 t)
{
time_t tval;
struct tm tmval;
QTime ti;
QDate dt;
QDateTime *qt;
if (t == 0)
return new QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0));
tval = t + TIME_OFFSET;
localtime_r (&tval, &tmval);
qt = new QDateTime();
qt->setDate(QDate(tmval.tm_year+1900, tmval.tm_mon+1, tmval.tm_mday));
qt->setTime(QTime(tmval.tm_hour, tmval.tm_min, tmval.tm_sec, 0));
/* OK. Done. */
return qt;
}
bool sportwatcherWidget::writeTag(const QFile &fn, const QString &str, int indent)
{
QString qs;
char *p = new char[str.length()+100];
QCString qcs;
int i;
if (indent > 0)
qs.fill(' ', indent * 3);
qs.append(str);
qcs = qs.utf8();
qstrcpy(p, qcs);
i = strlen(p);
if (write(fn.handle(), p, i) != i)
{
delete p;
return false;
}
delete p;
return true;
}
#if defined HAVE_GDAL && HAVE_LIBGDAL1_5_0
bool sportwatcherWidget::writeWMSTag(double llat, double llon, double rlat, double rlon, int width, int height)
{
QFile fl(MAP);
QString xml, s, srs, crs, styles, bSize, ext;
QDir dir = QDir::home();
QString path = dir.absPath();
int item, isrs;
double _llat, _llon, _rlat, _rlon;
bool offline, square;
if (!fl.open(IO_ReadWrite | IO_Truncate))
{
KMessageBox::error (this, i18n("Error opening or creating the WMS tag file!\nPlease check file name and/or permissions."));
return false;
}
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"));
cfg->setGroup(QString("WMS"));
square = cfg->readBoolEntry("Square", false);
styles = cfg->readEntry("Styles");
xml = "<GDAL_WMS>\n";
xml += " <Service name=\"WMS\">\n";
xml += " <Version>1.1.1</Version>\n";
xml += " <ServerURL>" + cfg->readEntry("ServerURL", "http://onearth.jpl.nasa.gov/wms.cgi") + "?</serverURL>\n";
isrs = cfg->readNumEntry("SRS", 0);
_llon = llon;
_llat = llat;
_rlon = rlon;
_rlat = rlat;
offline = false;
switch (isrs)
{
case 0: srs = QString("EPSG:4326"); break;
case 1:
srs = QString("EPSG:31257");
offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31257, width, height, square);
break;
case 2:
srs = QString("EPSG:31258");
offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31258, width, height, square);
break;
case 3:
srs = QString("EPSG:31259");
offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31259, width, height, square);
break;
case 4:
srs = QString("EPSG:31286");
offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31286, width, height, square);
break;
case 5:
srs = QString("EPSG:31287");
offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31287, width, height, square);
break;
case 6:
srs = QString("EPSG:31288");
offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31288, width, height, square);
break;
default: srs = QString("EPSG:4326");
}
xml += " <SRS>" + srs + "</SRS>\n";
item = cfg->readNumEntry("CRS", 0);
switch (item)
{
case 0: crs = QString("CRS:83"); break;
case 1: crs = QString("CRS:84"); break;
case 2: crs = QString("EPSG:4326"); break;
case 3: crs = QString("EPSG:31259"); break;
case 4: crs = QString("EPSG:31287"); break;
default: crs = QString("CRS:83"); break;
}
xml += " <CRS>" + crs + "</CRS>\n";
item = cfg->readNumEntry("Image", 2);
xml += " <ImageFormat>image/";
switch (item)
{
case 0: xml += "gif"; ext = QString(".gif"); break;
case 1: xml += "jpeg"; ext = QString(".jpg"); break;
case 2: xml += "png"; ext = QString(".png"); break;
case 3: xml += "tiff"; ext = QString(".tif"); break;
default: xml += "png"; ext = QString(".png");
}
xml += "</ImageFormat>\n";
xml += " <Layers>" + cfg->readEntry("Layer") + "</Layers>\n";
if (!styles.isEmpty())
xml += " <Styles>" + styles + "</Styles>\n";
xml += " <BBoxOrder>xyXY</BBoxOrder>\n";
xml += " </Service>\n";
xml += " <DataWindow>\n";
s.sprintf ("%f", _llat);
xml += " <UpperLeftX>" + s + "</UpperLeftX>\n";
s.sprintf ("%f", _llon);
xml += " <UpperLeftY>" + s + "</UpperLeftY>\n";
s.sprintf ("%f", _rlat);
xml += " <LowerRightX>" + s + "</LowerRightX>\n";
s.sprintf ("%f", _rlon);
xml += " <LowerRightY>" + s + "</LowerRightY>\n";
s.sprintf ("%d", width);
xml += " <SizeX>" + s + "</SizeX>\n";
s.sprintf ("%d", height);
xml += " <SizeY>" + s + "</SizeY>\n";
xml += " </DataWindow>\n";
/* switch (isrs)
{
case 0: srs = QString("EPSG:4326"); break;
case 1: srs = QString("EPSG:31259"); break;
case 2: srs = QString("EPSG:31286"); break;
case 3: srs = QString("EPSG:31287"); break;
case 4: srs = QString("EPSG:31288"); break;
default: srs = QString("EPSG:4326");
}
*/
// srs = QString("EPSG:4326");
xml += " <Projection>" + srs + "</Projection>\n";
xml += " <BandsCount>" + cfg->readEntry("Bands", "3") + "</BandsCount>\n";
item = cfg->readNumEntry("Tile", 2);
switch (item)
{
case 0: bSize = QString("64"); break;
case 1: bSize = QString("128"); break;
case 2: bSize = QString("256"); break;
case 3: bSize = QString("512"); break;
case 4: bSize = QString("1024"); break;
default: bSize = QString("256");
}
xml += " <BlockSizeX>" + bSize + "</BlockSizeX>\n";
xml += " <BlockSizeY>" + bSize + "</BlockSizeY>\n";
xml += " <OverviewCount>" + cfg->readEntry("Overview", "10") + "</OverviewCount>\n";
xml += " <Cache>\n";
xml += " <Path>" + path + "/.gdalwmscache" + "</Path>\n";
xml += " <Depth>" + cfg->readEntry("Depth", "2") + "</Depth>\n";
xml += " <Extension>" + ext + "</Extension>\n";
xml += " </Cache>\n";
QString off((cfg->readBoolEntry("Offline", false)) ? "true" : "false");
QString adv((cfg->readBoolEntry("Advice", false)) ? "true" : "false");
QString ver((cfg->readBoolEntry("Verify", true)) ? "true" : "false");
if (offline)
xml += " <OfflineMode>true</OfflineMode>\n";
else
xml += " <OfflineMode>" + off + "</OfflineMode>\n";
xml += " <AdviseRead>" + adv + "</AdviseRead>\n";
xml += " <VerifyAdviseRead>" + ver + "</VerifyAdviseRead>\n";
xml += "</GDAL_WMS>\n";
write (fl.handle(), xml.ascii(), strlen (xml.ascii()));
fl.close();
delete cfg;
return true;
}
bool sportwatcherWidget::transCoords (double *x1, double *y1, double *x2, double *y2, int code, int width, int height, bool square)
{
OGRSpatialReference oSourceSRS, oTargetSRS;
OGRCoordinateTransformation *poCT;
oSourceSRS.SetWellKnownGeogCS ("WGS84");
oTargetSRS.importFromEPSG(code);
poCT = OGRCreateCoordinateTransformation (&oSourceSRS, &oTargetSRS);
if (poCT == NULL || !poCT->Transform( 1, x1, y1))
{
KMessageBox::error(this, i18n("Translation between coordinate systems failed!"));
if (poCT != NULL)
delete poCT;
return true;
}
if (poCT != NULL)
{
poCT->Transform (1, x2, y2);
delete poCT;
}
if (square)
{
double wdiff = (double)((long)(*x1 - *x2) / (width / 2 * 2) * (width / 2 * 2));
double hdiff = (double)((long)(*y2 - *y1) / (height / 2 * 2) * (height / 2 * 2));
*x2 = *x1 - wdiff; // * (double)mFactor;
*y2 = *y1 + hdiff; // * (double)mFactor;
// *x2 = *x1 - (double)((long)(wdiff / mFactor * mFactor));
// *y2 = *y1 - (double)((long)(hdiff / mFactor * mFactor));
/* wdiff = wdiff - (*x1 - *x2);
hdiff = hdiff - (*y2 - *y1);
*x1 -= wdiff / 2.0;
*x2 -= wdiff / 2.0;
*y1 += hdiff / 2.0;
*y2 += hdiff / 2.0; */
}
return false;
}
QString *sportwatcherWidget::getProjection (int isrs, QString *srs)
{
switch (isrs)
{
case 0: *srs = QString("EPSG:4326"); break;
case 1: *srs = QString("EPSG:31257"); break;
case 2: *srs = QString("EPSG:31258"); break;
case 3: *srs = QString("EPSG:31259"); break;
case 4: *srs = QString("EPSG:31286"); break;
case 5: *srs = QString("EPSG:31287"); break;
case 6: *srs = QString("EPSG:31288"); break;
default: *srs = QString("EPSG:4326");
}
return srs;
}
bool sportwatcherWidget::warpImage(QString fn, QString *fName)
{
GDALDatasetH hSrcDS, hDstDS;
GDALDataset *inSet, *outSet;
GDALDataType eDT;
GDALRasterBand *poBand;
GDALDriverH hDriver;
char hv0[256];
int nXSize, nYSize;
double adfGeoTransform[6];
double oriLeftLon, oriRightLon, oriLeftLat, oriRightLat;
// Loading the user set geo coords of our source image and
// load the projection used for that image
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"));
cfg->setGroup(QString("ImageCoords"));
oriLeftLon = cfg->readDoubleNumEntry("LeftLon", 0.0);
oriLeftLat = cfg->readDoubleNumEntry("LeftLat", 0.0);
oriRightLon = cfg->readDoubleNumEntry("RightLon", 0.0);
oriRightLat = cfg->readDoubleNumEntry("RightLat", 0.0);
int isrs = cfg->readNumEntry("SRS", 0);
// Create a temporary file name for our output file
strcpy (hv0, "/tmp/SportWatcherTIFFXXXXXX");
mkdtemp (&hv0[0]);
*fName = QString(hv0) + ".tif";
// Open input and output files.
if ((hSrcDS = GDALOpen(fn.ascii(), GA_ReadOnly)) == NULL)
{
KMessageBox::error(this, i18n("Error opening an image file!"));
return false;
}
inSet = (GDALDataset *)hSrcDS;
// Create output with same datatype as first input band.
poBand = inSet->GetRasterBand (1);
eDT = poBand->GetRasterDataType ();
if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
{
KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
GDALClose (hSrcDS);
return false;
}
// Get dimensions of image and set transform data
int nRasterCount = inSet->GetRasterCount();
nXSize = inSet->GetRasterXSize();
nYSize = inSet->GetRasterYSize();
// cut the wanted region out of image
transform *tf = new transform (oriLeftLat, oriLeftLon, oriRightLat, oriRightLon);
tf->setDimensions(nXSize, nYSize);
if (!tf->cutImage (geoRect.llat, geoRect.llon, geoRect.rlat, geoRect.rlon, fn))
{
GDALClose (hSrcDS);
return false;
}
GDALClose (hSrcDS);
QString nfn = fn + "_tmp.png";
// repeat the part above and open the now cutted part of the image.
// Open input and output files.
if ((hSrcDS = GDALOpen(nfn.ascii(), GA_ReadOnly)) == NULL)
{
KMessageBox::error(this, i18n("Error opening an image file!"));
return false;
}
inSet = (GDALDataset *)hSrcDS;
// Create output with same datatype as first input band.
poBand = inSet->GetRasterBand (1);
eDT = poBand->GetRasterDataType ();
if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
{
KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
GDALClose (hSrcDS);
return false;
}
// Get dimensions of image and set transform data
nRasterCount = inSet->GetRasterCount();
nXSize = inSet->GetRasterXSize();
nYSize = inSet->GetRasterYSize();
// Set the values needed to transform the image
OGRSpatialReference iSRS;
const char *iWKT;
switch (isrs)
{
case 0: iSRS.importFromEPSG(4326); break;
case 1: iSRS.importFromEPSG(31257); break;
case 2: iSRS.importFromEPSG(31258); break;
case 3: iSRS.importFromEPSG(31259); break;
case 4: iSRS.importFromEPSG(31286); break;
case 5: iSRS.importFromEPSG(31287); break;
case 6: iSRS.importFromEPSG(31288); break;
default: iSRS.importFromEPSG(4326);
}
iSRS.exportToWkt ((char **)&iWKT);
if (inSet->SetProjection (iWKT) != CE_None)
{
KMessageBox::error(this, i18n("Error setting projection on source!"));
GDALClose (hSrcDS);
return false;
}
adfGeoTransform[0] = geoRect.llon;
adfGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)nXSize;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = geoRect.llat;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)nYSize);
if (inSet->SetGeoTransform (&adfGeoTransform[0]) != CE_None)
{
KMessageBox::error(this, i18n("Error setting geo transform data to source!"));
GDALClose (hSrcDS);
return false;
}
// Get Source coordinate system.
const char *pszSrcWKT, *pszDstWKT = NULL;
if ((pszSrcWKT = GDALGetProjectionRef (hSrcDS)) == NULL)
{
KMessageBox::error(this, i18n("Error getting the projection reference"));
GDALClose (hSrcDS);
return false;
}
// Setup output coordinate system that is UTM ? WGS84.
OGRSpatialReference oSRS;
// oSRS.SetUTM( 0, TRUE );
oSRS.SetWellKnownGeogCS("WGS84");
oSRS.exportToWkt ((char **)&pszDstWKT);
// Create the output file.
double adfDstGeoTransform[6];
adfDstGeoTransform[0] = geoRect.llon;
adfDstGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)geoRect.width;
adfDstGeoTransform[2] = 0.0;
adfDstGeoTransform[3] = geoRect.llat;
adfDstGeoTransform[4] = 0.0;
adfDstGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)geoRect.height);
if ((hDstDS = GDALCreate(hDriver, fName->ascii(), geoRect.width, geoRect.height,
nRasterCount, eDT, NULL )) == NULL)
{
KMessageBox::error(this, i18n("Error creating a temporary image file! (" + *fName + ")"));
GDALClose (hSrcDS);
return false;
}
outSet = (GDALDataset *)hDstDS;
for (int i = 0; i < nRasterCount; i++)
{
poBand = outSet->GetRasterBand (i+1);
poBand->Fill (0.0);
}
if (outSet->SetProjection (pszDstWKT) != CE_None)
{
KMessageBox::error(this, i18n("Error setting projection on destination!"));
GDALClose (hDstDS);
GDALClose (hSrcDS);
unlink (fName->ascii());
return false;
}
if (outSet->SetGeoTransform (&adfDstGeoTransform[0]) != CE_None)
{
KMessageBox::error(this, i18n("Error setting geo transform data to destination!"));
GDALClose (hDstDS);
GDALClose (hSrcDS);
unlink (fName->ascii());
return false;
}
// Copy the color table, if required.
GDALColorTableH hCT;
for (int i = 0; i < nRasterCount; i++)
{
hCT = GDALGetRasterColorTable (inSet->GetRasterBand (i+1));
if (hCT != NULL)
GDALSetRasterColorTable (outSet->GetRasterBand (i+1), hCT);
}
// Setup warp options.
GDALWarpOptions *psWarpOptions = GDALCreateWarpOptions();
psWarpOptions->hSrcDS = hSrcDS;
psWarpOptions->hDstDS = hDstDS;
psWarpOptions->nBandCount = nRasterCount;
psWarpOptions->panSrcBands =
(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
psWarpOptions->panDstBands =
(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
for (int i = 0; i < nRasterCount; i++)
{
psWarpOptions->panSrcBands[i] = i+1;
psWarpOptions->panDstBands[i] = i+1;
}
// psWarpOptions->pfnProgress = GDALTermProgress;
// Establish reprojection transformer.
psWarpOptions->pTransformerArg =
GDALCreateGenImgProjTransformer(hSrcDS,
GDALGetProjectionRef(hSrcDS),
hDstDS,
GDALGetProjectionRef(hDstDS),
FALSE, 0.0, 1);
psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
// Initialize and execute the warp operation.
GDALWarpOperation oOperation;
if (oOperation.Initialize (psWarpOptions) != CE_None)
{
KMessageBox::error(this, i18n("Error initializing warp operation!"));
GDALClose (hDstDS);
GDALClose (hSrcDS);
unlink (fName->ascii());
return false;
}
oOperation.ChunkAndWarpMulti (0, 0, geoRect.width, geoRect.height);
GDALDestroyGenImgProjTransformer (psWarpOptions->pTransformerArg);
GDALDestroyWarpOptions(psWarpOptions);
GDALClose (hDstDS);
GDALClose (hSrcDS);
unlink (nfn.ascii());
return true;
}
#endif
#include "sportwatcherwidget.moc"