mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-07-07 16:02:51 +02:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
fafe6c4b03 | |||
b156e25023 | |||
e15deccfca | |||
9b24bccfed | |||
589c602d21 | |||
b7df3cbe0e | |||
f08f5893a3 | |||
1d5f7db12f | |||
532a1eb69f | |||
1efb4e494d | |||
4653f771a6 | |||
22e5ffaa0c | |||
9a4514a464 | |||
7d47f243ea |
@ -1,4 +1,4 @@
|
|||||||
version: 11.3.{build}
|
version: 11.4.{build}
|
||||||
|
|
||||||
configuration:
|
configuration:
|
||||||
- Release
|
- Release
|
||||||
|
@ -11,7 +11,8 @@ GPS log file formats.
|
|||||||
* Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases,
|
* Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases,
|
||||||
Garmin IMG/GMAP & JNX maps, TwoNav RMaps, GeoTIFF images, BSB charts, KMZ maps,
|
Garmin IMG/GMAP & JNX maps, TwoNav RMaps, GeoTIFF images, BSB charts, KMZ maps,
|
||||||
AlpineQuest maps, Locus/OsmAnd/RMaps SQLite maps, Mapsforge vector maps,
|
AlpineQuest maps, Locus/OsmAnd/RMaps SQLite maps, Mapsforge vector maps,
|
||||||
QCT maps, GEMF maps, Osmdroid SQLite maps, ESRI World-File georeferenced images).
|
QCT maps, GEMF maps, Osmdroid SQLite maps, Orux maps,
|
||||||
|
ESRI World-File georeferenced images).
|
||||||
* Elevation, speed, heart rate, cadence, power, temperature and gear ratio/shifts
|
* Elevation, speed, heart rate, cadence, power, temperature and gear ratio/shifts
|
||||||
graphs.
|
graphs.
|
||||||
* Support for DEM files (SRTM HGT).
|
* Support for DEM files (SRTM HGT).
|
||||||
|
@ -3,7 +3,7 @@ unix:!macx:!android {
|
|||||||
} else {
|
} else {
|
||||||
TARGET = GPXSee
|
TARGET = GPXSee
|
||||||
}
|
}
|
||||||
VERSION = 11.3
|
VERSION = 11.4
|
||||||
|
|
||||||
QT += core \
|
QT += core \
|
||||||
gui \
|
gui \
|
||||||
|
2523
lang/gpxsee_ca.ts
Normal file
2523
lang/gpxsee_ca.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -200,13 +200,13 @@
|
|||||||
<location filename="../src/GUI/elevationgraph.cpp" line="60"/>
|
<location filename="../src/GUI/elevationgraph.cpp" line="60"/>
|
||||||
<source>Up</source>
|
<source>Up</source>
|
||||||
<extracomment>Use an Unicode arrow (U+2197) when there is no abbreviation or extremly short term for "Up" in your language</extracomment>
|
<extracomment>Use an Unicode arrow (U+2197) when there is no abbreviation or extremly short term for "Up" in your language</extracomment>
|
||||||
<translation type="unfinished">Opp</translation>
|
<translation>Opp</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/elevationgraph.cpp" line="64"/>
|
<location filename="../src/GUI/elevationgraph.cpp" line="64"/>
|
||||||
<source>Down</source>
|
<source>Down</source>
|
||||||
<extracomment>Use an Unicode arrow (U+2198) when there is no abbreviation or extremly short term for "Down" in your language</extracomment>
|
<extracomment>Use an Unicode arrow (U+2198) when there is no abbreviation or extremly short term for "Down" in your language</extracomment>
|
||||||
<translation type="unfinished">Ned</translation>
|
<translation>Ned</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/elevationgraph.cpp" line="67"/>
|
<location filename="../src/GUI/elevationgraph.cpp" line="67"/>
|
||||||
@ -374,7 +374,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gui.cpp" line="219"/>
|
<location filename="../src/GUI/gui.cpp" line="219"/>
|
||||||
<source>Open directory...</source>
|
<source>Open directory...</source>
|
||||||
<translation type="unfinished">Åpne mappe …</translation>
|
<translation>Åpne mappe …</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gui.cpp" line="224"/>
|
<location filename="../src/GUI/gui.cpp" line="224"/>
|
||||||
@ -598,7 +598,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gui.cpp" line="477"/>
|
<location filename="../src/GUI/gui.cpp" line="477"/>
|
||||||
<source>Show tabs</source>
|
<source>Show tabs</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Vis faner</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gui.cpp" line="486"/>
|
<location filename="../src/GUI/gui.cpp" line="486"/>
|
||||||
@ -712,7 +712,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gui.cpp" line="955"/>
|
<location filename="../src/GUI/gui.cpp" line="955"/>
|
||||||
<source>Open directory</source>
|
<source>Open directory</source>
|
||||||
<translation type="unfinished">Åpne mappe</translation>
|
<translation>Åpne mappe</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gui.cpp" line="1959"/>
|
<location filename="../src/GUI/gui.cpp" line="1959"/>
|
||||||
@ -1029,7 +1029,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gearratiograph.cpp" line="28"/>
|
<location filename="../src/GUI/gearratiograph.cpp" line="28"/>
|
||||||
<source>Top</source>
|
<source>Top</source>
|
||||||
<translation type="unfinished">Topp</translation>
|
<translation>Topp</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/gearratiograph.cpp" line="30"/>
|
<location filename="../src/GUI/gearratiograph.cpp" line="30"/>
|
||||||
@ -1236,7 +1236,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="157"/>
|
<location filename="../src/map/maplist.cpp" line="157"/>
|
||||||
<source>GEMF maps</source>
|
<source>GEMF maps</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>GEMF-kart</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="162"/>
|
<location filename="../src/map/maplist.cpp" line="162"/>
|
||||||
@ -1256,7 +1256,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="166"/>
|
<location filename="../src/map/maplist.cpp" line="166"/>
|
||||||
<source>Orux maps</source>
|
<source>Orux maps</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Orux-kart</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="167"/>
|
<location filename="../src/map/maplist.cpp" line="167"/>
|
||||||
@ -1266,7 +1266,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="169"/>
|
<location filename="../src/map/maplist.cpp" line="169"/>
|
||||||
<source>Osmdroid SQLite maps</source>
|
<source>Osmdroid SQLite maps</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Osmdroid SQLite-kart</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="170"/>
|
<location filename="../src/map/maplist.cpp" line="170"/>
|
||||||
@ -1286,7 +1286,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="177"/>
|
<location filename="../src/map/maplist.cpp" line="177"/>
|
||||||
<source>All files</source>
|
<source>All files</source>
|
||||||
<translation type="unfinished">Alle filer</translation>
|
<translation>Alle filer</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/map/maplist.cpp" line="168"/>
|
<location filename="../src/map/maplist.cpp" line="168"/>
|
||||||
@ -1921,7 +1921,7 @@
|
|||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/optionsdialog.cpp" line="779"/>
|
<location filename="../src/GUI/optionsdialog.cpp" line="779"/>
|
||||||
<source>Initial paths</source>
|
<source>Initial paths</source>
|
||||||
<translation type="unfinished">Forvalgte stier</translation>
|
<translation>Forvalgte stier</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../src/GUI/optionsdialog.cpp" line="814"/>
|
<location filename="../src/GUI/optionsdialog.cpp" line="814"/>
|
||||||
|
@ -37,7 +37,7 @@ Unicode true
|
|||||||
; The name of the installer
|
; The name of the installer
|
||||||
Name "GPXSee"
|
Name "GPXSee"
|
||||||
; Program version
|
; Program version
|
||||||
!define VERSION "11.3"
|
!define VERSION "11.4"
|
||||||
|
|
||||||
; The file to write
|
; The file to write
|
||||||
OutFile "GPXSee-${VERSION}_x64.exe"
|
OutFile "GPXSee-${VERSION}_x64.exe"
|
||||||
|
@ -99,18 +99,26 @@ void WaypointItem::updateCache()
|
|||||||
QPainterPath p;
|
QPainterPath p;
|
||||||
qreal pointSize = _font.bold() ? HS(_size) : _size;
|
qreal pointSize = _font.bold() ? HS(_size) : _size;
|
||||||
|
|
||||||
if (_showLabel) {
|
if (_showLabel && !_waypoint.name().isEmpty()) {
|
||||||
QFontMetrics fm(_font);
|
QFontMetrics fm(_font);
|
||||||
_labelBB = fm.tightBoundingRect(_waypoint.name());
|
_labelBB = fm.tightBoundingRect(_waypoint.name());
|
||||||
|
|
||||||
if (_showIcon && _icon) {
|
if (_showIcon && _icon) {
|
||||||
if (_font.bold())
|
if (_font.bold())
|
||||||
p.addRect(-_icon->width() * 0.625, -_icon->height() * 1.25,
|
p.addRect(-_icon->width() * 0.625, _waypoint.icon().isNull()
|
||||||
|
? -_icon->height() * 1.25 : -_icon->height() * 0.625,
|
||||||
_icon->width() * 1.25, _icon->height() * 1.25);
|
_icon->width() * 1.25, _icon->height() * 1.25);
|
||||||
else
|
else
|
||||||
p.addRect(-_icon->width()/2.0, -_icon->height(), _icon->width(),
|
p.addRect(-_icon->width()/2.0, _waypoint.icon().isNull()
|
||||||
|
? -_icon->height() : -_icon->height()/2, _icon->width(),
|
||||||
_icon->height());
|
_icon->height());
|
||||||
p.addRect(0, 0, _labelBB.width(), _labelBB.height() + fm.descent());
|
|
||||||
|
if (_waypoint.icon().isNull())
|
||||||
|
p.addRect(0, 0, _labelBB.width(), _labelBB.height()
|
||||||
|
+ fm.descent());
|
||||||
|
else
|
||||||
|
p.addRect(_icon->width()/2, _icon->height()/2, _labelBB.width(),
|
||||||
|
_labelBB.height() + fm.descent());
|
||||||
} else {
|
} else {
|
||||||
p.addRect(-pointSize/2, -pointSize/2, pointSize, pointSize);
|
p.addRect(-pointSize/2, -pointSize/2, pointSize, pointSize);
|
||||||
p.addRect(pointSize/2, pointSize/2, _labelBB.width(),
|
p.addRect(pointSize/2, pointSize/2, _labelBB.width(),
|
||||||
@ -119,10 +127,12 @@ void WaypointItem::updateCache()
|
|||||||
} else {
|
} else {
|
||||||
if (_showIcon && _icon) {
|
if (_showIcon && _icon) {
|
||||||
if (_font.bold())
|
if (_font.bold())
|
||||||
p.addRect(-_icon->width() * 0.625, -_icon->height() * 1.25,
|
p.addRect(-_icon->width() * 0.625, _waypoint.icon().isNull()
|
||||||
|
? -_icon->height() * 1.25 : -_icon->height() * 0.625,
|
||||||
_icon->width() * 1.25, _icon->height() * 1.25);
|
_icon->width() * 1.25, _icon->height() * 1.25);
|
||||||
else
|
else
|
||||||
p.addRect(-_icon->width()/2, -_icon->height(), _icon->width(),
|
p.addRect(-_icon->width()/2, _waypoint.icon().isNull()
|
||||||
|
? -_icon->height() : -_icon->height()/2, _icon->width(),
|
||||||
_icon->height());
|
_icon->height());
|
||||||
} else
|
} else
|
||||||
p.addRect(-pointSize/2, -pointSize/2, pointSize, pointSize);
|
p.addRect(-pointSize/2, -pointSize/2, pointSize, pointSize);
|
||||||
@ -140,12 +150,16 @@ void WaypointItem::paint(QPainter *painter,
|
|||||||
|
|
||||||
painter->setPen(_color);
|
painter->setPen(_color);
|
||||||
|
|
||||||
if (_showLabel) {
|
if (_showLabel && !_waypoint.name().isEmpty()) {
|
||||||
painter->setFont(_font);
|
painter->setFont(_font);
|
||||||
if (_showIcon && _icon)
|
if (_showIcon && _icon) {
|
||||||
painter->drawText(-qMax(_labelBB.x(), 0), _labelBB.height(),
|
if (_waypoint.icon().isNull())
|
||||||
_waypoint.name());
|
painter->drawText(-qMax(_labelBB.x(), 0), _labelBB.height(),
|
||||||
else
|
_waypoint.name());
|
||||||
|
else
|
||||||
|
painter->drawText(_icon->width()/2 - qMax(_labelBB.x(), 0),
|
||||||
|
_icon->height()/2 + _labelBB.height(), _waypoint.name());
|
||||||
|
} else
|
||||||
painter->drawText(pointSize/2 - qMax(_labelBB.x(), 0), pointSize/2
|
painter->drawText(pointSize/2 - qMax(_labelBB.x(), 0), pointSize/2
|
||||||
+ _labelBB.height(), _waypoint.name());
|
+ _labelBB.height(), _waypoint.name());
|
||||||
}
|
}
|
||||||
@ -153,11 +167,13 @@ void WaypointItem::paint(QPainter *painter,
|
|||||||
painter->setBrush(QBrush(_color, Qt::SolidPattern));
|
painter->setBrush(QBrush(_color, Qt::SolidPattern));
|
||||||
if (_showIcon && _icon) {
|
if (_showIcon && _icon) {
|
||||||
if (_font.bold())
|
if (_font.bold())
|
||||||
painter->drawPixmap(-_icon->width() * 0.625, -_icon->height() * 1.25,
|
painter->drawPixmap(-_icon->width() * 0.625, _waypoint.icon().isNull()
|
||||||
|
? -_icon->height() * 1.25 : -_icon->height() * 0.625,
|
||||||
_icon->scaled(_icon->width() * 1.25, _icon->height() * 1.25,
|
_icon->scaled(_icon->width() * 1.25, _icon->height() * 1.25,
|
||||||
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
||||||
else
|
else
|
||||||
painter->drawPixmap(-_icon->width()/2.0, -_icon->height(), *_icon);
|
painter->drawPixmap(-_icon->width()/2.0, _waypoint.icon().isNull()
|
||||||
|
? -_icon->height() : -_icon->height()/2, *_icon);
|
||||||
} else
|
} else
|
||||||
painter->drawEllipse(-pointSize/2, -pointSize/2, pointSize, pointSize);
|
painter->drawEllipse(-pointSize/2, -pointSize/2, pointSize, pointSize);
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ void NetworkTimeout::timerEvent(QTimerEvent *ev)
|
|||||||
return;
|
return;
|
||||||
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
|
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
|
||||||
if (reply->isRunning())
|
if (reply->isRunning())
|
||||||
reply->close();
|
reply->abort();
|
||||||
_timer.stop();
|
_timer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QTemporaryDir>
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
@ -139,3 +140,9 @@ QString Util::displayName(const QString &path)
|
|||||||
return path;
|
return path;
|
||||||
#endif // Q_OS_ANDROID
|
#endif // Q_OS_ANDROID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QTemporaryDir &Util::tempDir()
|
||||||
|
{
|
||||||
|
static QTemporaryDir dir;
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
@ -3,12 +3,15 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
class QTemporaryDir;
|
||||||
|
|
||||||
namespace Util
|
namespace Util
|
||||||
{
|
{
|
||||||
int str2int(const char *str, int len);
|
int str2int(const char *str, int len);
|
||||||
double niceNum(double x, bool round);
|
double niceNum(double x, bool round);
|
||||||
QString file2name(const QString &path);
|
QString file2name(const QString &path);
|
||||||
QString displayName(const QString &path);
|
QString displayName(const QString &path);
|
||||||
|
const QTemporaryDir &tempDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // UTIL_H
|
#endif // UTIL_H
|
||||||
|
@ -53,6 +53,7 @@ static QMultiMap<QString, Parser*> parsers()
|
|||||||
map.insert("gpx", &gpx);
|
map.insert("gpx", &gpx);
|
||||||
map.insert("tcx", &tcx);
|
map.insert("tcx", &tcx);
|
||||||
map.insert("kml", &kml);
|
map.insert("kml", &kml);
|
||||||
|
map.insert("kmz", &kml);
|
||||||
map.insert("fit", &fit);
|
map.insert("fit", &fit);
|
||||||
map.insert("csv", &csv);
|
map.insert("csv", &csv);
|
||||||
map.insert("igc", &igc);
|
map.insert("igc", &igc);
|
||||||
@ -162,7 +163,7 @@ QString Data::formats()
|
|||||||
+ qApp->translate("Data", "IGC files") + " (*.igc);;"
|
+ qApp->translate("Data", "IGC files") + " (*.igc);;"
|
||||||
+ qApp->translate("Data", "ITN files") + " (*.itn);;"
|
+ qApp->translate("Data", "ITN files") + " (*.itn);;"
|
||||||
+ qApp->translate("Data", "JPEG images") + " (*.jpg *.jpeg);;"
|
+ qApp->translate("Data", "JPEG images") + " (*.jpg *.jpeg);;"
|
||||||
+ qApp->translate("Data", "KML files") + " (*.kml);;"
|
+ qApp->translate("Data", "KML files") + " (*.kml *.kmz);;"
|
||||||
+ qApp->translate("Data", "LOC files") + " (*.loc);;"
|
+ qApp->translate("Data", "LOC files") + " (*.loc);;"
|
||||||
+ qApp->translate("Data", "NMEA files") + " (*.nmea);;"
|
+ qApp->translate("Data", "NMEA files") + " (*.nmea);;"
|
||||||
+ qApp->translate("Data", "ONmove files") + " (*.omd *.ghp);;"
|
+ qApp->translate("Data", "ONmove files") + " (*.omd *.ghp);;"
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "common/garmin.h"
|
#include "common/garmin.h"
|
||||||
#include "common/textcodec.h"
|
#include "common/textcodec.h"
|
||||||
#include "common/color.h"
|
#include "common/color.h"
|
||||||
|
#include "common/util.h"
|
||||||
#include "address.h"
|
#include "address.h"
|
||||||
#include "gpiparser.h"
|
#include "gpiparser.h"
|
||||||
|
|
||||||
@ -388,12 +389,6 @@ static quint32 readAddress(DataStream &stream, Waypoint &waypoint)
|
|||||||
return rs + rh.size;
|
return rs + rh.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const QTemporaryDir &tempDir()
|
|
||||||
{
|
|
||||||
static QTemporaryDir dir;
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
static quint32 readImageInfo(DataStream &stream, Waypoint &waypoint,
|
static quint32 readImageInfo(DataStream &stream, Waypoint &waypoint,
|
||||||
const QString &fileName, int &imgId)
|
const QString &fileName, int &imgId)
|
||||||
{
|
{
|
||||||
@ -408,12 +403,12 @@ static quint32 readImageInfo(DataStream &stream, Waypoint &waypoint,
|
|||||||
ba.resize(size);
|
ba.resize(size);
|
||||||
stream.readRawData(ba.data(), ba.size());
|
stream.readRawData(ba.data(), ba.size());
|
||||||
|
|
||||||
if (tempDir().isValid()) {
|
if (Util::tempDir().isValid()) {
|
||||||
QBuffer buf(&ba);
|
QBuffer buf(&ba);
|
||||||
QImageReader ir(&buf);
|
QImageReader ir(&buf);
|
||||||
|
|
||||||
QByteArray id(fileName.toUtf8() + QByteArray::number(imgId++));
|
QByteArray id(fileName.toUtf8() + QByteArray::number(imgId++));
|
||||||
QFile imgFile(tempDir().path() + "/" + QString("%0.%1").arg(
|
QFile imgFile(Util::tempDir().path() + "/" + QString("%0.%1").arg(
|
||||||
QCryptographicHash::hash(id, QCryptographicHash::Sha1).toHex(),
|
QCryptographicHash::hash(id, QCryptographicHash::Sha1).toHex(),
|
||||||
QString(ir.format())));
|
QString(ir.format())));
|
||||||
imgFile.open(QIODevice::WriteOnly);
|
imgFile.open(QIODevice::WriteOnly);
|
||||||
|
@ -1,5 +1,33 @@
|
|||||||
|
/*
|
||||||
|
WARNING: This code uses internal Qt API - the QZipReader class for reading
|
||||||
|
ZIP files - and things may break if Qt changes the API. For Qt5 this is not
|
||||||
|
a problem as we can "see the future" now and there are no changes in all
|
||||||
|
the supported Qt5 versions up to the last one (5.15). In Qt6 the class
|
||||||
|
might change or even disappear in the future, but this is very unlikely
|
||||||
|
as there were no changes for several years and The Qt Company's policy
|
||||||
|
is: "do not invest any resources into any desktop related stuff unless
|
||||||
|
absolutely necessary". There is an issue (QTBUG-3897) since the year 2009 to
|
||||||
|
include the ZIP reader into the public API, which aptly illustrates the
|
||||||
|
effort The Qt Company is willing to make about anything desktop related...
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QtEndian>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <private/qzipreader_p.h>
|
||||||
|
#include "common/util.h"
|
||||||
#include "kmlparser.h"
|
#include "kmlparser.h"
|
||||||
|
|
||||||
|
static bool isZIP(QFile *file)
|
||||||
|
{
|
||||||
|
quint32 magic;
|
||||||
|
|
||||||
|
return (file->read((char *)&magic, sizeof(magic)) == (qint64)sizeof(magic)
|
||||||
|
&& qFromLittleEndian(magic) == 0x04034b50);
|
||||||
|
}
|
||||||
|
|
||||||
qreal KMLParser::number()
|
qreal KMLParser::number()
|
||||||
{
|
{
|
||||||
@ -463,6 +491,9 @@ void KMLParser::multiGeometry(QList<TrackData> &tracks, QList<Area> &areas,
|
|||||||
QVector<Waypoint> &waypoints, const QString &name, const QString &desc,
|
QVector<Waypoint> &waypoints, const QString &name, const QString &desc,
|
||||||
const QDateTime ×tamp)
|
const QDateTime ×tamp)
|
||||||
{
|
{
|
||||||
|
TrackData *tp = 0;
|
||||||
|
Area *ap = 0;
|
||||||
|
|
||||||
while (_reader.readNextStartElement()) {
|
while (_reader.readNextStartElement()) {
|
||||||
if (_reader.name() == QLatin1String("Point")) {
|
if (_reader.name() == QLatin1String("Point")) {
|
||||||
waypoints.append(Waypoint());
|
waypoints.append(Waypoint());
|
||||||
@ -472,28 +503,90 @@ void KMLParser::multiGeometry(QList<TrackData> &tracks, QList<Area> &areas,
|
|||||||
w.setTimestamp(timestamp);
|
w.setTimestamp(timestamp);
|
||||||
point(w);
|
point(w);
|
||||||
} else if (_reader.name() == QLatin1String("LineString")) {
|
} else if (_reader.name() == QLatin1String("LineString")) {
|
||||||
tracks.append(TrackData());
|
if (!tp) {
|
||||||
TrackData &t = tracks.last();
|
tracks.append(TrackData());
|
||||||
t.append(SegmentData());
|
tp = &tracks.last();
|
||||||
t.setName(name);
|
tp->setName(name);
|
||||||
t.setDescription(desc);
|
tp->setDescription(desc);
|
||||||
lineString(t.last());
|
}
|
||||||
|
tp->append(SegmentData());
|
||||||
|
lineString(tp->last());
|
||||||
} else if (_reader.name() == QLatin1String("Polygon")) {
|
} else if (_reader.name() == QLatin1String("Polygon")) {
|
||||||
areas.append(Area());
|
if (!ap) {
|
||||||
Area &a = areas.last();
|
areas.append(Area());
|
||||||
a.setName(name);
|
ap = &areas.last();
|
||||||
a.setDescription(desc);
|
ap->setName(name);
|
||||||
polygon(a);
|
ap->setDescription(desc);
|
||||||
|
}
|
||||||
|
polygon(*ap);
|
||||||
} else
|
} else
|
||||||
_reader.skipCurrentElement();
|
_reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMLParser::placemark(QList<TrackData> &tracks, QList<Area> &areas,
|
void KMLParser::photoOverlay(const Ctx &ctx, QVector<Waypoint> &waypoints,
|
||||||
QVector<Waypoint> &waypoints)
|
QMap<QString, QPixmap> &icons)
|
||||||
{
|
{
|
||||||
QString name, desc, phone, address;
|
QString img, id;
|
||||||
|
Waypoint w;
|
||||||
|
static QRegularExpression re("\\$\\[[^\\]]+\\]");
|
||||||
|
|
||||||
|
while (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("name"))
|
||||||
|
w.setName(_reader.readElementText());
|
||||||
|
else if (_reader.name() == QLatin1String("description"))
|
||||||
|
w.setDescription(_reader.readElementText());
|
||||||
|
else if (_reader.name() == QLatin1String("phoneNumber"))
|
||||||
|
w.setPhone(_reader.readElementText());
|
||||||
|
else if (_reader.name() == QLatin1String("address"))
|
||||||
|
w.setAddress(_reader.readElementText());
|
||||||
|
else if (_reader.name() == QLatin1String("TimeStamp"))
|
||||||
|
w.setTimestamp(timeStamp());
|
||||||
|
else if (_reader.name() == QLatin1String("Style")) {
|
||||||
|
style(ctx.dir, icons);
|
||||||
|
id = QString();
|
||||||
|
} else if (_reader.name() == QLatin1String("StyleMap"))
|
||||||
|
styleMap(icons);
|
||||||
|
else if (_reader.name() == QLatin1String("Icon"))
|
||||||
|
img = icon();
|
||||||
|
else if (_reader.name() == QLatin1String("Point"))
|
||||||
|
point(w);
|
||||||
|
else if (_reader.name() == QLatin1String("styleUrl"))
|
||||||
|
id = styleUrl();
|
||||||
|
else
|
||||||
|
_reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!w.coordinates().isNull()) {
|
||||||
|
w.setIcon(icons.value(id));
|
||||||
|
|
||||||
|
img.replace(re, "0");
|
||||||
|
if (!QUrl(img).scheme().isEmpty())
|
||||||
|
w.addLink(Link(img, "Photo Overlay"));
|
||||||
|
else if (ctx.zip && Util::tempDir().isValid()) {
|
||||||
|
QFileInfo fi(img);
|
||||||
|
QByteArray id(ctx.path.toUtf8() + img.toUtf8());
|
||||||
|
QString path(Util::tempDir().path() + "/" + QString("%0.%1")
|
||||||
|
.arg(QCryptographicHash::hash(id, QCryptographicHash::Sha1)
|
||||||
|
.toHex(), QString(fi.suffix())));
|
||||||
|
QFile::rename(ctx.dir.absoluteFilePath(img), path);
|
||||||
|
w.addImage(path);
|
||||||
|
} else if (!ctx.zip)
|
||||||
|
w.addImage(ctx.dir.absoluteFilePath(img));
|
||||||
|
|
||||||
|
waypoints.append(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KMLParser::placemark(const Ctx &ctx, QList<TrackData> &tracks,
|
||||||
|
QList<Area> &areas, QVector<Waypoint> &waypoints,
|
||||||
|
QMap<QString, QPixmap> &icons)
|
||||||
|
{
|
||||||
|
QString name, desc, phone, address, id;
|
||||||
QDateTime timestamp;
|
QDateTime timestamp;
|
||||||
|
Waypoint *wp = 0;
|
||||||
|
TrackData *tp = 0;
|
||||||
|
Area *ap = 0;
|
||||||
|
|
||||||
while (_reader.readNextStartElement()) {
|
while (_reader.readNextStartElement()) {
|
||||||
if (_reader.name() == QLatin1String("name"))
|
if (_reader.name() == QLatin1String("name"))
|
||||||
@ -506,87 +599,185 @@ void KMLParser::placemark(QList<TrackData> &tracks, QList<Area> &areas,
|
|||||||
address = _reader.readElementText();
|
address = _reader.readElementText();
|
||||||
else if (_reader.name() == QLatin1String("TimeStamp"))
|
else if (_reader.name() == QLatin1String("TimeStamp"))
|
||||||
timestamp = timeStamp();
|
timestamp = timeStamp();
|
||||||
|
else if (_reader.name() == QLatin1String("Style")) {
|
||||||
|
style(ctx.dir, icons);
|
||||||
|
id = QString();
|
||||||
|
} else if (_reader.name() == QLatin1String("StyleMap"))
|
||||||
|
styleMap(icons);
|
||||||
else if (_reader.name() == QLatin1String("MultiGeometry"))
|
else if (_reader.name() == QLatin1String("MultiGeometry"))
|
||||||
multiGeometry(tracks, areas, waypoints, name, desc, timestamp);
|
multiGeometry(tracks, areas, waypoints, name, desc, timestamp);
|
||||||
else if (_reader.name() == QLatin1String("Point")) {
|
else if (_reader.name() == QLatin1String("Point")) {
|
||||||
waypoints.append(Waypoint());
|
waypoints.append(Waypoint());
|
||||||
Waypoint &w = waypoints.last();
|
wp = &waypoints.last();
|
||||||
w.setName(name);
|
point(*wp);
|
||||||
w.setDescription(desc);
|
|
||||||
w.setTimestamp(timestamp);
|
|
||||||
w.setAddress(address);
|
|
||||||
w.setPhone(phone);
|
|
||||||
point(w);
|
|
||||||
} else if (_reader.name() == QLatin1String("LineString")
|
} else if (_reader.name() == QLatin1String("LineString")
|
||||||
|| _reader.name() == QLatin1String("LinearRing")) {
|
|| _reader.name() == QLatin1String("LinearRing")) {
|
||||||
tracks.append(TrackData());
|
tracks.append(TrackData());
|
||||||
TrackData &t = tracks.last();
|
tp = &tracks.last();
|
||||||
t.append(SegmentData());
|
tp->append(SegmentData());
|
||||||
t.setName(name);
|
lineString(tp->last());
|
||||||
t.setDescription(desc);
|
|
||||||
lineString(t.last());
|
|
||||||
} else if (_reader.name() == QLatin1String("Track")) {
|
} else if (_reader.name() == QLatin1String("Track")) {
|
||||||
tracks.append(TrackData());
|
tracks.append(TrackData());
|
||||||
TrackData &t = tracks.last();
|
tp = &tracks.last();
|
||||||
t.append(SegmentData());
|
tp->append(SegmentData());
|
||||||
t.setName(name);
|
track(tp->last());
|
||||||
t.setDescription(desc);
|
|
||||||
track(t.last());
|
|
||||||
} else if (_reader.name() == QLatin1String("MultiTrack")) {
|
} else if (_reader.name() == QLatin1String("MultiTrack")) {
|
||||||
tracks.append(TrackData());
|
tracks.append(TrackData());
|
||||||
TrackData &t = tracks.last();
|
tp = &tracks.last();
|
||||||
t.setName(name);
|
multiTrack(*tp);
|
||||||
t.setDescription(desc);
|
|
||||||
multiTrack(t);
|
|
||||||
} else if (_reader.name() == QLatin1String("Polygon")) {
|
} else if (_reader.name() == QLatin1String("Polygon")) {
|
||||||
areas.append(Area());
|
areas.append(Area());
|
||||||
Area &a = areas.last();
|
ap = &areas.last();
|
||||||
a.setName(name);
|
polygon(*ap);
|
||||||
a.setDescription(desc);
|
} else if (_reader.name() == QLatin1String("styleUrl"))
|
||||||
polygon(a);
|
id = styleUrl();
|
||||||
} else
|
else
|
||||||
_reader.skipCurrentElement();
|
_reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wp) {
|
||||||
|
wp->setName(name);
|
||||||
|
wp->setDescription(desc);
|
||||||
|
wp->setTimestamp(timestamp);
|
||||||
|
wp->setAddress(address);
|
||||||
|
wp->setPhone(phone);
|
||||||
|
wp->setIcon(icons.value(id));
|
||||||
|
} else if (tp) {
|
||||||
|
tp->setName(name);
|
||||||
|
tp->setDescription(desc);
|
||||||
|
} else if (ap) {
|
||||||
|
ap->setName(name);
|
||||||
|
ap->setDescription(desc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMLParser::folder(QList<TrackData> &tracks, QList<Area> &areas,
|
QString KMLParser::icon()
|
||||||
QVector<Waypoint> &waypoints)
|
{
|
||||||
|
QString path;
|
||||||
|
|
||||||
|
while (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("href"))
|
||||||
|
path = _reader.readElementText();
|
||||||
|
else
|
||||||
|
_reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString KMLParser::styleUrl()
|
||||||
|
{
|
||||||
|
QString id(_reader.readElementText());
|
||||||
|
return (id.at(0) == '#') ? id.right(id.size() - 1) : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void KMLParser::iconStyle(const QDir &dir, const QString &id,
|
||||||
|
QMap<QString, QPixmap> &icons)
|
||||||
{
|
{
|
||||||
while (_reader.readNextStartElement()) {
|
while (_reader.readNextStartElement()) {
|
||||||
if (_reader.name() == QLatin1String("Placemark"))
|
if (_reader.name() == QLatin1String("Icon"))
|
||||||
placemark(tracks, areas, waypoints);
|
icons.insert(id, QPixmap(dir.absoluteFilePath(icon())));
|
||||||
else if (_reader.name() == QLatin1String("Folder"))
|
|
||||||
folder(tracks, areas, waypoints);
|
|
||||||
else
|
else
|
||||||
_reader.skipCurrentElement();
|
_reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMLParser::document(QList<TrackData> &tracks, QList<Area> &areas,
|
void KMLParser::styleMapPair(const QString &id, QMap<QString, QPixmap> &icons)
|
||||||
QVector<Waypoint> &waypoints)
|
|
||||||
{
|
{
|
||||||
|
QString key, url;
|
||||||
|
|
||||||
while (_reader.readNextStartElement()) {
|
while (_reader.readNextStartElement()) {
|
||||||
if (_reader.name() == QLatin1String("Document"))
|
if (_reader.name() == QLatin1String("key"))
|
||||||
document(tracks, areas, waypoints);
|
key = _reader.readElementText();
|
||||||
else if (_reader.name() == QLatin1String("Placemark"))
|
else if (_reader.name() == QLatin1String("styleUrl"))
|
||||||
placemark(tracks, areas, waypoints);
|
url = styleUrl();
|
||||||
else if (_reader.name() == QLatin1String("Folder"))
|
else
|
||||||
folder(tracks, areas, waypoints);
|
_reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == "normal")
|
||||||
|
icons.insert(id, icons.value(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
void KMLParser::styleMap(QMap<QString, QPixmap> &icons)
|
||||||
|
{
|
||||||
|
QString id = _reader.attributes().value("id").toString();
|
||||||
|
|
||||||
|
while (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("Pair"))
|
||||||
|
styleMapPair(id, icons);
|
||||||
else
|
else
|
||||||
_reader.skipCurrentElement();
|
_reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KMLParser::kml(QList<TrackData> &tracks, QList<Area> &areas,
|
void KMLParser::style(const QDir &dir, QMap<QString, QPixmap> &icons)
|
||||||
QVector<Waypoint> &waypoints)
|
{
|
||||||
|
QString id = _reader.attributes().value("id").toString();
|
||||||
|
|
||||||
|
while (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("IconStyle"))
|
||||||
|
iconStyle(dir, id, icons);
|
||||||
|
else
|
||||||
|
_reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KMLParser::folder(const Ctx &ctx, QList<TrackData> &tracks,
|
||||||
|
QList<Area> &areas, QVector<Waypoint> &waypoints,
|
||||||
|
QMap<QString, QPixmap> &icons)
|
||||||
{
|
{
|
||||||
while (_reader.readNextStartElement()) {
|
while (_reader.readNextStartElement()) {
|
||||||
if (_reader.name() == QLatin1String("Document"))
|
if (_reader.name() == QLatin1String("Document"))
|
||||||
document(tracks, areas, waypoints);
|
document(ctx, tracks, areas, waypoints);
|
||||||
else if (_reader.name() == QLatin1String("Placemark"))
|
|
||||||
placemark(tracks, areas, waypoints);
|
|
||||||
else if (_reader.name() == QLatin1String("Folder"))
|
else if (_reader.name() == QLatin1String("Folder"))
|
||||||
folder(tracks, areas, waypoints);
|
folder(ctx, tracks, areas, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("Placemark"))
|
||||||
|
placemark(ctx, tracks, areas, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("PhotoOverlay"))
|
||||||
|
photoOverlay(ctx, waypoints, icons);
|
||||||
|
else
|
||||||
|
_reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KMLParser::document(const Ctx &ctx, QList<TrackData> &tracks,
|
||||||
|
QList<Area> &areas, QVector<Waypoint> &waypoints)
|
||||||
|
{
|
||||||
|
QMap<QString, QPixmap> icons;
|
||||||
|
|
||||||
|
while (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("Document"))
|
||||||
|
document(ctx, tracks, areas, waypoints);
|
||||||
|
else if (_reader.name() == QLatin1String("Folder"))
|
||||||
|
folder(ctx, tracks, areas, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("Placemark"))
|
||||||
|
placemark(ctx, tracks, areas, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("PhotoOverlay"))
|
||||||
|
photoOverlay(ctx, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("Style"))
|
||||||
|
style(ctx.dir, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("StyleMap"))
|
||||||
|
styleMap(icons);
|
||||||
|
else
|
||||||
|
_reader.skipCurrentElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KMLParser::kml(const Ctx &ctx, QList<TrackData> &tracks,
|
||||||
|
QList<Area> &areas, QVector<Waypoint> &waypoints)
|
||||||
|
{
|
||||||
|
QMap<QString, QPixmap> icons;
|
||||||
|
|
||||||
|
while (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("Document"))
|
||||||
|
document(ctx, tracks, areas, waypoints);
|
||||||
|
else if (_reader.name() == QLatin1String("Folder"))
|
||||||
|
folder(ctx, tracks, areas, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("Placemark"))
|
||||||
|
placemark(ctx, tracks, areas, waypoints, icons);
|
||||||
|
else if (_reader.name() == QLatin1String("PhotoOverlay"))
|
||||||
|
photoOverlay(ctx, waypoints, icons);
|
||||||
else
|
else
|
||||||
_reader.skipCurrentElement();
|
_reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
@ -596,15 +787,50 @@ bool KMLParser::parse(QFile *file, QList<TrackData> &tracks,
|
|||||||
QList<RouteData> &routes, QList<Area> &areas, QVector<Waypoint> &waypoints)
|
QList<RouteData> &routes, QList<Area> &areas, QVector<Waypoint> &waypoints)
|
||||||
{
|
{
|
||||||
Q_UNUSED(routes);
|
Q_UNUSED(routes);
|
||||||
|
QFileInfo fi(*file);
|
||||||
|
|
||||||
_reader.clear();
|
_reader.clear();
|
||||||
_reader.setDevice(file);
|
|
||||||
|
|
||||||
if (_reader.readNextStartElement()) {
|
if (isZIP(file)) {
|
||||||
if (_reader.name() == QLatin1String("kml"))
|
QZipReader zip(fi.absoluteFilePath(), QIODevice::ReadOnly);
|
||||||
kml(tracks, areas, waypoints);
|
QTemporaryDir tempDir;
|
||||||
else
|
if (!tempDir.isValid() || !zip.extractAll(tempDir.path()))
|
||||||
_reader.raiseError("Not a KML file");
|
_reader.raiseError("Error extracting ZIP file");
|
||||||
|
else {
|
||||||
|
QDir zipDir(tempDir.path());
|
||||||
|
QFileInfoList files(zipDir.entryInfoList(QStringList("*.kml"),
|
||||||
|
QDir::Files));
|
||||||
|
|
||||||
|
if (files.isEmpty())
|
||||||
|
_reader.raiseError("No KML file found in ZIP file");
|
||||||
|
else {
|
||||||
|
QFile kmlFile(files.first().absoluteFilePath());
|
||||||
|
if (!kmlFile.open(QIODevice::ReadOnly))
|
||||||
|
_reader.raiseError("Error opening KML file");
|
||||||
|
else {
|
||||||
|
_reader.setDevice(&kmlFile);
|
||||||
|
|
||||||
|
if (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("kml"))
|
||||||
|
kml(Ctx(fi.absoluteFilePath(), zipDir, true),
|
||||||
|
tracks, areas, waypoints);
|
||||||
|
else
|
||||||
|
_reader.raiseError("Not a KML file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
file->reset();
|
||||||
|
_reader.setDevice(file);
|
||||||
|
|
||||||
|
if (_reader.readNextStartElement()) {
|
||||||
|
if (_reader.name() == QLatin1String("kml"))
|
||||||
|
kml(Ctx(fi.absoluteFilePath(), fi.absoluteDir(), false), tracks,
|
||||||
|
areas, waypoints);
|
||||||
|
else
|
||||||
|
_reader.raiseError("Not a KML file");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return !_reader.error();
|
return !_reader.error();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
class KMLParser : public Parser
|
class KMLParser : public Parser
|
||||||
@ -14,17 +15,28 @@ public:
|
|||||||
int errorLine() const {return _reader.lineNumber();}
|
int errorLine() const {return _reader.lineNumber();}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void kml(QList<TrackData> &tracks, QList<Area> &areas,
|
struct Ctx {
|
||||||
|
Ctx(const QString &path, const QDir &dir, bool zip)
|
||||||
|
: path(path), dir(dir), zip(zip) {}
|
||||||
|
|
||||||
|
QString path;
|
||||||
|
QDir dir;
|
||||||
|
bool zip;
|
||||||
|
};
|
||||||
|
|
||||||
|
void kml(const Ctx &ctx, QList<TrackData> &tracks, QList<Area> &areas,
|
||||||
QVector<Waypoint> &waypoints);
|
QVector<Waypoint> &waypoints);
|
||||||
void document(QList<TrackData> &tracks, QList<Area> &areas,
|
void document(const Ctx &ctx, QList<TrackData> &tracks, QList<Area> &areas,
|
||||||
QVector<Waypoint> &waypoints);
|
|
||||||
void folder(QList<TrackData> &tracks, QList<Area> &areas,
|
|
||||||
QVector<Waypoint> &waypoints);
|
|
||||||
void placemark(QList<TrackData> &tracks, QList<Area> &areas,
|
|
||||||
QVector<Waypoint> &waypoints);
|
QVector<Waypoint> &waypoints);
|
||||||
|
void folder(const Ctx &ctx, QList<TrackData> &tracks, QList<Area> &areas,
|
||||||
|
QVector<Waypoint> &waypoints, QMap<QString, QPixmap> &icons);
|
||||||
|
void placemark(const Ctx &ctx, QList<TrackData> &tracks, QList<Area> &areas,
|
||||||
|
QVector<Waypoint> &waypoints, QMap<QString, QPixmap> &icons);
|
||||||
void multiGeometry(QList<TrackData> &tracks, QList<Area> &areas,
|
void multiGeometry(QList<TrackData> &tracks, QList<Area> &areas,
|
||||||
QVector<Waypoint> &waypoints, const QString &name, const QString &desc,
|
QVector<Waypoint> &waypoints, const QString &name, const QString &desc,
|
||||||
const QDateTime ×tamp);
|
const QDateTime ×tamp);
|
||||||
|
void photoOverlay(const Ctx &ctx, QVector<Waypoint> &waypoints,
|
||||||
|
QMap<QString, QPixmap> &icons);
|
||||||
void track(SegmentData &segment);
|
void track(SegmentData &segment);
|
||||||
void multiTrack(TrackData &t);
|
void multiTrack(TrackData &t);
|
||||||
void lineString(SegmentData &segment);
|
void lineString(SegmentData &segment);
|
||||||
@ -45,6 +57,13 @@ private:
|
|||||||
QDateTime timeStamp();
|
QDateTime timeStamp();
|
||||||
qreal number();
|
qreal number();
|
||||||
QDateTime time();
|
QDateTime time();
|
||||||
|
QString icon();
|
||||||
|
QString styleUrl();
|
||||||
|
void style(const QDir &dir, QMap<QString, QPixmap> &icons);
|
||||||
|
void styleMapPair(const QString &id, QMap<QString, QPixmap> &icons);
|
||||||
|
void styleMap(QMap<QString, QPixmap> &icons);
|
||||||
|
void iconStyle(const QDir &dir, const QString &id,
|
||||||
|
QMap<QString, QPixmap> &icons);
|
||||||
|
|
||||||
QXmlStreamReader _reader;
|
QXmlStreamReader _reader;
|
||||||
};
|
};
|
||||||
|
@ -234,6 +234,10 @@ void OruxMap::calibrationPoints(QXmlStreamReader &reader, const QSize &size,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
CalibrationPoint p(corner2point(corner, size), Coordinates(lon, lat));
|
CalibrationPoint p(corner2point(corner, size), Coordinates(lon, lat));
|
||||||
|
if (!p.isValid()) {
|
||||||
|
reader.raiseError(QString("invalid calibration point"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
points.append(p);
|
points.append(p);
|
||||||
|
|
||||||
reader.readElementText();
|
reader.readElementText();
|
||||||
@ -242,13 +246,14 @@ void OruxMap::calibrationPoints(QXmlStreamReader &reader, const QSize &size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
|
void OruxMap::mapCalibration(QXmlStreamReader &reader, const QString &dir,
|
||||||
|
int level)
|
||||||
{
|
{
|
||||||
int zoom;
|
int zoom;
|
||||||
QSize tileSize, size, calibrationSize;
|
QSize tileSize, size, calibrationSize;
|
||||||
QString datum, projection;
|
QString fileName;
|
||||||
QList<CalibrationPoint> points;
|
|
||||||
Projection proj;
|
Projection proj;
|
||||||
|
Transform t;
|
||||||
|
|
||||||
QXmlStreamAttributes attr = reader.attributes();
|
QXmlStreamAttributes attr = reader.attributes();
|
||||||
if (!intAttr(reader, attr, "layerLevel", zoom))
|
if (!intAttr(reader, attr, "layerLevel", zoom))
|
||||||
@ -256,13 +261,14 @@ void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
|
|||||||
|
|
||||||
while (reader.readNextStartElement()) {
|
while (reader.readNextStartElement()) {
|
||||||
if (reader.name() == QLatin1String("OruxTracker"))
|
if (reader.name() == QLatin1String("OruxTracker"))
|
||||||
oruxTracker(reader, level + 1);
|
oruxTracker(reader, dir, level + 1);
|
||||||
else if (reader.name() == QLatin1String("MapName")) {
|
else if (reader.name() == QLatin1String("MapName")) {
|
||||||
QString name(reader.readElementText());
|
QString name(reader.readElementText());
|
||||||
if (!level)
|
if (!level && dir.isEmpty())
|
||||||
_name = name;
|
_name = name;
|
||||||
} else if (reader.name() == QLatin1String("MapChunks")) {
|
} else if (reader.name() == QLatin1String("MapChunks")) {
|
||||||
int xMax, yMax, width, height;
|
int xMax, yMax, width, height;
|
||||||
|
QString datum, projection;
|
||||||
|
|
||||||
QXmlStreamAttributes attr = reader.attributes();
|
QXmlStreamAttributes attr = reader.attributes();
|
||||||
if (!intAttr(reader, attr, "xMax", xMax))
|
if (!intAttr(reader, attr, "xMax", xMax))
|
||||||
@ -277,6 +283,8 @@ void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
|
|||||||
return;
|
return;
|
||||||
if (!strAttr(reader, attr, "projection", projection))
|
if (!strAttr(reader, attr, "projection", projection))
|
||||||
return;
|
return;
|
||||||
|
if (!strAttr(reader, attr, "file_name", fileName))
|
||||||
|
return;
|
||||||
|
|
||||||
tileSize = QSize(width, height);
|
tileSize = QSize(width, height);
|
||||||
size = QSize(xMax * width, yMax * height);
|
size = QSize(xMax * width, yMax * height);
|
||||||
@ -309,29 +317,52 @@ void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
|
|||||||
calibrationSize = QSize(width, height);
|
calibrationSize = QSize(width, height);
|
||||||
|
|
||||||
reader.readElementText();
|
reader.readElementText();
|
||||||
} else if (reader.name() == QLatin1String("CalibrationPoints"))
|
} else if (reader.name() == QLatin1String("CalibrationPoints")) {
|
||||||
|
QList<CalibrationPoint> points;
|
||||||
|
|
||||||
calibrationPoints(reader, calibrationSize, points);
|
calibrationPoints(reader, calibrationSize, points);
|
||||||
else
|
|
||||||
|
t = computeTransformation(proj, points);
|
||||||
|
if (!t.isValid()) {
|
||||||
|
reader.raiseError(t.errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tileSize.isValid()) {
|
if (tileSize.isValid()) {
|
||||||
Transform t(computeTransformation(proj, points));
|
if (!t.isValid()) {
|
||||||
_zooms.append(Zoom(zoom, tileSize, size, proj, t));
|
reader.raiseError("Invalid map calibration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir mapDir(QFileInfo(path()).absoluteDir());
|
||||||
|
QDir subDir = dir.isEmpty()
|
||||||
|
? mapDir : QDir(mapDir.absoluteFilePath(dir));
|
||||||
|
QString set(subDir.absoluteFilePath("set"));
|
||||||
|
|
||||||
|
_zooms.append(Zoom(zoom, tileSize, size, proj, t, fileName, set));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OruxMap::oruxTracker(QXmlStreamReader &reader, int level)
|
void OruxMap::oruxTracker(QXmlStreamReader &reader, const QString &dir,
|
||||||
|
int level)
|
||||||
{
|
{
|
||||||
|
if (level > 1 || (level && !dir.isEmpty())) {
|
||||||
|
reader.raiseError("invalid map nesting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (reader.readNextStartElement()) {
|
while (reader.readNextStartElement()) {
|
||||||
if (reader.name() == QLatin1String("MapCalibration"))
|
if (reader.name() == QLatin1String("MapCalibration"))
|
||||||
mapCalibration(reader, level);
|
mapCalibration(reader, dir, level);
|
||||||
else
|
else
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OruxMap::readXML(const QString &path)
|
bool OruxMap::readXML(const QString &path, const QString &dir)
|
||||||
{
|
{
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
|
|
||||||
@ -341,7 +372,7 @@ bool OruxMap::readXML(const QString &path)
|
|||||||
QXmlStreamReader reader(&file);
|
QXmlStreamReader reader(&file);
|
||||||
if (reader.readNextStartElement()) {
|
if (reader.readNextStartElement()) {
|
||||||
if (reader.name() == QLatin1String("OruxTracker"))
|
if (reader.name() == QLatin1String("OruxTracker"))
|
||||||
oruxTracker(reader, 0);
|
oruxTracker(reader, dir, 0);
|
||||||
else
|
else
|
||||||
reader.raiseError("Not a Orux map calibration file");
|
reader.raiseError("Not a Orux map calibration file");
|
||||||
}
|
}
|
||||||
@ -354,51 +385,67 @@ bool OruxMap::readXML(const QString &path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
OruxMap::OruxMap(const QString &fileName, QObject *parent)
|
OruxMap::OruxMap(const QString &fileName, QObject *parent)
|
||||||
: Map(fileName, parent), _zoom(0), _mapRatio(1.0), _valid(false)
|
: Map(fileName, parent), _zoom(0), _mapRatio(1.0), _valid(false)
|
||||||
{
|
{
|
||||||
|
QFileInfo fi(fileName);
|
||||||
|
QDir dir(fi.absoluteDir());
|
||||||
|
|
||||||
if (!readXML(fileName))
|
if (!readXML(fileName))
|
||||||
return;
|
return;
|
||||||
if (_zooms.isEmpty()) {
|
if (_zooms.isEmpty()) {
|
||||||
_errorString = "No usable zoom level found";
|
QStringList list(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot));
|
||||||
return;
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
QDir subDir(dir.absoluteFilePath(list.at(i)));
|
||||||
|
if (!readXML(subDir.absoluteFilePath(list.at(i) + ".otrk2.xml"),
|
||||||
|
list.at(i))) {
|
||||||
|
_errorString = list.at(i) + ": " + _errorString;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_zooms.isEmpty()) {
|
||||||
|
_errorString = "No usable zoom level found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::sort(_zooms.begin(), _zooms.end());
|
std::sort(_zooms.begin(), _zooms.end());
|
||||||
|
|
||||||
|
|
||||||
QFileInfo fi(fileName);
|
if (dir.exists("OruxMapsImages.db")) {
|
||||||
QDir dir(fi.absoluteDir());
|
QString dbFile(dir.absoluteFilePath("OruxMapsImages.db"));
|
||||||
QString dbFile(dir.absoluteFilePath("OruxMapsImages.db"));
|
_db = QSqlDatabase::addDatabase("QSQLITE", dbFile);
|
||||||
if (!QFileInfo::exists(dbFile)) {
|
_db.setDatabaseName(dbFile);
|
||||||
_errorString = "Image DB file not found";
|
_db.setConnectOptions("QSQLITE_OPEN_READONLY");
|
||||||
return;
|
if (!_db.open()) {
|
||||||
|
_errorString = "Error opening database file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlRecord r = _db.record("tiles");
|
||||||
|
if (r.isEmpty()
|
||||||
|
|| r.field(0).name() != "x"
|
||||||
|
|| META_TYPE(r.field(0).type()) != QMetaType::Int
|
||||||
|
|| r.field(1).name() != "y"
|
||||||
|
|| META_TYPE(r.field(1).type()) != QMetaType::Int
|
||||||
|
|| r.field(2).name() != "z"
|
||||||
|
|| META_TYPE(r.field(2).type()) != QMetaType::Int
|
||||||
|
|| r.field(3).name() != "image"
|
||||||
|
|| META_TYPE(r.field(3).type()) != QMetaType::QByteArray) {
|
||||||
|
_errorString = "Invalid table format";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_db.close();
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < _zooms.size(); i++) {
|
||||||
|
if (!_zooms.at(i).set.exists()) {
|
||||||
|
_errorString = "missing set directory (level "
|
||||||
|
+ QString::number(_zooms.at(i).zoom) + ")";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_db = QSqlDatabase::addDatabase("QSQLITE", dbFile);
|
|
||||||
_db.setDatabaseName(dbFile);
|
|
||||||
_db.setConnectOptions("QSQLITE_OPEN_READONLY");
|
|
||||||
|
|
||||||
if (!_db.open()) {
|
|
||||||
_errorString = "Error opening database file";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSqlRecord r = _db.record("tiles");
|
|
||||||
if (r.isEmpty()
|
|
||||||
|| r.field(0).name() != "x"
|
|
||||||
|| META_TYPE(r.field(0).type()) != QMetaType::Int
|
|
||||||
|| r.field(1).name() != "y"
|
|
||||||
|| META_TYPE(r.field(1).type()) != QMetaType::Int
|
|
||||||
|| r.field(2).name() != "z"
|
|
||||||
|| META_TYPE(r.field(2).type()) != QMetaType::Int
|
|
||||||
|| r.field(3).name() != "image"
|
|
||||||
|| META_TYPE(r.field(3).type()) != QMetaType::QByteArray) {
|
|
||||||
_errorString = "Invalid table format";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_db.close();
|
|
||||||
_valid = true;
|
_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,12 +483,14 @@ int OruxMap::zoomOut()
|
|||||||
|
|
||||||
void OruxMap::load()
|
void OruxMap::load()
|
||||||
{
|
{
|
||||||
_db.open();
|
if (_db.isValid())
|
||||||
|
_db.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OruxMap::unload()
|
void OruxMap::unload()
|
||||||
{
|
{
|
||||||
_db.close();
|
if (_db.isValid())
|
||||||
|
_db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OruxMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
|
void OruxMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
|
||||||
@ -450,19 +499,37 @@ void OruxMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
|
|||||||
_mapRatio = mapRatio;
|
_mapRatio = mapRatio;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap OruxMap::tile(int zoom, int x, int y) const
|
QPixmap OruxMap::tile(const Zoom &z, int x, int y) const
|
||||||
{
|
{
|
||||||
QSqlQuery query(_db);
|
if (_db.isValid()) {
|
||||||
query.prepare("SELECT image FROM tiles WHERE z=:z AND x=:x AND y=:y");
|
QSqlQuery query(_db);
|
||||||
query.bindValue(":z", zoom);
|
query.prepare("SELECT image FROM tiles WHERE z=:z AND x=:x AND y=:y");
|
||||||
query.bindValue(":x", x);
|
query.bindValue(":z", z.zoom);
|
||||||
query.bindValue(":y", y);
|
query.bindValue(":x", x);
|
||||||
query.exec();
|
query.bindValue(":y", y);
|
||||||
if (!query.first())
|
query.exec();
|
||||||
return QPixmap();
|
|
||||||
|
|
||||||
QImage img(QImage::fromData(query.value(0).toByteArray()));
|
if (!query.first()) {
|
||||||
return QPixmap::fromImage(img);
|
qWarning("%s: SQL %d-%d-%d: not found", qPrintable(name()), z.zoom,
|
||||||
|
x, y);
|
||||||
|
return QPixmap();
|
||||||
|
} else {
|
||||||
|
QImage img(QImage::fromData(query.value(0).toByteArray()));
|
||||||
|
return QPixmap::fromImage(img);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QString fileName(z.fileName + "_" + QString::number(x) + "_"
|
||||||
|
+ QString::number(y) + ".omc2");
|
||||||
|
QString path(z.set.absoluteFilePath(fileName));
|
||||||
|
if (!QFileInfo::exists(path)) {
|
||||||
|
qWarning("%s: %s: not found", qPrintable(name()),
|
||||||
|
qPrintable(fileName));
|
||||||
|
return QPixmap();
|
||||||
|
} else {
|
||||||
|
QImage img(path);
|
||||||
|
return QPixmap::fromImage(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OruxMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
void OruxMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||||
@ -484,14 +551,12 @@ void OruxMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
|||||||
+ "_" + QString::number(x/z.tileSize.width())
|
+ "_" + QString::number(x/z.tileSize.width())
|
||||||
+ "_" + QString::number(y/z.tileSize.height());
|
+ "_" + QString::number(y/z.tileSize.height());
|
||||||
if (!QPixmapCache::find(key, &pixmap)) {
|
if (!QPixmapCache::find(key, &pixmap)) {
|
||||||
pixmap = tile(z.zoom, x/z.tileSize.width(), y/z.tileSize.height());
|
pixmap = tile(z, x/z.tileSize.width(), y/z.tileSize.height());
|
||||||
if (!pixmap.isNull())
|
if (!pixmap.isNull())
|
||||||
QPixmapCache::insert(key, pixmap);
|
QPixmapCache::insert(key, pixmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixmap.isNull())
|
if (!pixmap.isNull()) {
|
||||||
qWarning("%s: error loading tile image", qPrintable(key));
|
|
||||||
else {
|
|
||||||
pixmap.setDevicePixelRatio(_mapRatio);
|
pixmap.setDevicePixelRatio(_mapRatio);
|
||||||
QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
|
QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
|
||||||
painter->drawPixmap(tp, pixmap);
|
painter->drawPixmap(tp, pixmap);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
|
#include <QDir>
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "projection.h"
|
#include "projection.h"
|
||||||
#include "transform.h"
|
#include "transform.h"
|
||||||
@ -44,9 +45,10 @@ public:
|
|||||||
private:
|
private:
|
||||||
struct Zoom {
|
struct Zoom {
|
||||||
Zoom(int zoom, const QSize &tileSize, const QSize &size,
|
Zoom(int zoom, const QSize &tileSize, const QSize &size,
|
||||||
const Projection &proj, const Transform &transform)
|
const Projection &proj, const Transform &transform,
|
||||||
|
const QString &fileName, const QString &set)
|
||||||
: zoom(zoom), tileSize(tileSize), size(size), projection(proj),
|
: zoom(zoom), tileSize(tileSize), size(size), projection(proj),
|
||||||
transform(transform) {}
|
transform(transform), fileName(fileName), set(set) {}
|
||||||
bool operator<(const Zoom &other) const
|
bool operator<(const Zoom &other) const
|
||||||
{return zoom < other.zoom;}
|
{return zoom < other.zoom;}
|
||||||
|
|
||||||
@ -55,14 +57,16 @@ private:
|
|||||||
QSize size;
|
QSize size;
|
||||||
Projection projection;
|
Projection projection;
|
||||||
Transform transform;
|
Transform transform;
|
||||||
|
QString fileName;
|
||||||
|
QDir set;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool readXML(const QString &path);
|
bool readXML(const QString &path, const QString &dir = QString());
|
||||||
void oruxTracker(QXmlStreamReader &reader, int level);
|
void oruxTracker(QXmlStreamReader &reader, const QString &dir, int level);
|
||||||
void mapCalibration(QXmlStreamReader &reader, int level);
|
void mapCalibration(QXmlStreamReader &reader, const QString &dir, int level);
|
||||||
void calibrationPoints(QXmlStreamReader &reader, const QSize &size,
|
void calibrationPoints(QXmlStreamReader &reader, const QSize &size,
|
||||||
QList<CalibrationPoint> &points);
|
QList<CalibrationPoint> &points);
|
||||||
QPixmap tile(int zoom, int x, int y) const;
|
QPixmap tile(const Zoom &z, int x, int y) const;
|
||||||
|
|
||||||
friend QDebug operator<<(QDebug dbg, const Zoom &zoom);
|
friend QDebug operator<<(QDebug dbg, const Zoom &zoom);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user