1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-06 07:32:51 +02:00

Compare commits

..

14 Commits
11.3 ... 11.4

Author SHA1 Message Date
fafe6c4b03 Various KML/KMZ parsing fixes and code cleanup 2022-09-02 22:53:38 +02:00
b156e25023 Properly handle PhotoOverlay icon URLs 2022-09-02 09:55:11 +02:00
e15deccfca Added Catalan localization stub 2022-09-02 08:42:28 +02:00
9b24bccfed Added PhotoOverlay support
+ non-shared styles
2022-09-02 08:36:58 +02:00
589c602d21 Document styles are not inherited 2022-09-01 00:49:21 +02:00
b7df3cbe0e Added support for KMZ data files + KML icons
fixes #312
2022-09-01 00:28:00 +02:00
f08f5893a3 Only offset the waypoint icons when they are using the symbol icons 2022-09-01 00:25:48 +02:00
1d5f7db12f Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (464 of 464 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2022-08-30 08:58:07 +02:00
532a1eb69f Translated using Weblate (Norwegian Bokmål)
Currently translated at 98.7% (458 of 464 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2022-08-20 11:22:56 +02:00
1efb4e494d Properly abort the connections on timeout 2022-08-17 01:50:24 +02:00
4653f771a6 Code cleanup 2022-08-15 09:12:37 +02:00
22e5ffaa0c Added support for non-SQL Orux maps
+ error handling fixes/improvements
2022-08-11 23:39:36 +02:00
9a4514a464 Version++ 2022-08-11 23:38:48 +02:00
7d47f243ea Added Orux maps to supported offline maps 2022-08-11 01:02:58 +02:00
16 changed files with 3039 additions and 179 deletions

View File

@ -1,4 +1,4 @@
version: 11.3.{build}
version: 11.4.{build}
configuration:
- Release

View File

@ -11,7 +11,8 @@ GPS log file formats.
* Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases,
Garmin IMG/GMAP & JNX maps, TwoNav RMaps, GeoTIFF images, BSB charts, KMZ 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
graphs.
* Support for DEM files (SRTM HGT).

View File

@ -3,7 +3,7 @@ unix:!macx:!android {
} else {
TARGET = GPXSee
}
VERSION = 11.3
VERSION = 11.4
QT += core \
gui \

2523
lang/gpxsee_ca.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -200,13 +200,13 @@
<location filename="../src/GUI/elevationgraph.cpp" line="60"/>
<source>Up</source>
<extracomment>Use an Unicode arrow (U+2197) when there is no abbreviation or extremly short term for &quot;Up&quot; in your language</extracomment>
<translation type="unfinished">Opp</translation>
<translation>Opp</translation>
</message>
<message>
<location filename="../src/GUI/elevationgraph.cpp" line="64"/>
<source>Down</source>
<extracomment>Use an Unicode arrow (U+2198) when there is no abbreviation or extremly short term for &quot;Down&quot; in your language</extracomment>
<translation type="unfinished">Ned</translation>
<translation>Ned</translation>
</message>
<message>
<location filename="../src/GUI/elevationgraph.cpp" line="67"/>
@ -374,7 +374,7 @@
<message>
<location filename="../src/GUI/gui.cpp" line="219"/>
<source>Open directory...</source>
<translation type="unfinished">Åpne mappe </translation>
<translation>Åpne mappe </translation>
</message>
<message>
<location filename="../src/GUI/gui.cpp" line="224"/>
@ -598,7 +598,7 @@
<message>
<location filename="../src/GUI/gui.cpp" line="477"/>
<source>Show tabs</source>
<translation type="unfinished"></translation>
<translation>Vis faner</translation>
</message>
<message>
<location filename="../src/GUI/gui.cpp" line="486"/>
@ -712,7 +712,7 @@
<message>
<location filename="../src/GUI/gui.cpp" line="955"/>
<source>Open directory</source>
<translation type="unfinished">Åpne mappe</translation>
<translation>Åpne mappe</translation>
</message>
<message>
<location filename="../src/GUI/gui.cpp" line="1959"/>
@ -1029,7 +1029,7 @@
<message>
<location filename="../src/GUI/gearratiograph.cpp" line="28"/>
<source>Top</source>
<translation type="unfinished">Topp</translation>
<translation>Topp</translation>
</message>
<message>
<location filename="../src/GUI/gearratiograph.cpp" line="30"/>
@ -1236,7 +1236,7 @@
<message>
<location filename="../src/map/maplist.cpp" line="157"/>
<source>GEMF maps</source>
<translation type="unfinished"></translation>
<translation>GEMF-kart</translation>
</message>
<message>
<location filename="../src/map/maplist.cpp" line="162"/>
@ -1256,7 +1256,7 @@
<message>
<location filename="../src/map/maplist.cpp" line="166"/>
<source>Orux maps</source>
<translation type="unfinished"></translation>
<translation>Orux-kart</translation>
</message>
<message>
<location filename="../src/map/maplist.cpp" line="167"/>
@ -1266,7 +1266,7 @@
<message>
<location filename="../src/map/maplist.cpp" line="169"/>
<source>Osmdroid SQLite maps</source>
<translation type="unfinished"></translation>
<translation>Osmdroid SQLite-kart</translation>
</message>
<message>
<location filename="../src/map/maplist.cpp" line="170"/>
@ -1286,7 +1286,7 @@
<message>
<location filename="../src/map/maplist.cpp" line="177"/>
<source>All files</source>
<translation type="unfinished">Alle filer</translation>
<translation>Alle filer</translation>
</message>
<message>
<location filename="../src/map/maplist.cpp" line="168"/>
@ -1921,7 +1921,7 @@
<message>
<location filename="../src/GUI/optionsdialog.cpp" line="779"/>
<source>Initial paths</source>
<translation type="unfinished">Forvalgte stier</translation>
<translation>Forvalgte stier</translation>
</message>
<message>
<location filename="../src/GUI/optionsdialog.cpp" line="814"/>

View File

@ -37,7 +37,7 @@ Unicode true
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "11.3"
!define VERSION "11.4"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"

View File

@ -99,18 +99,26 @@ void WaypointItem::updateCache()
QPainterPath p;
qreal pointSize = _font.bold() ? HS(_size) : _size;
if (_showLabel) {
if (_showLabel && !_waypoint.name().isEmpty()) {
QFontMetrics fm(_font);
_labelBB = fm.tightBoundingRect(_waypoint.name());
if (_showIcon && _icon) {
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);
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());
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 {
p.addRect(-pointSize/2, -pointSize/2, pointSize, pointSize);
p.addRect(pointSize/2, pointSize/2, _labelBB.width(),
@ -119,10 +127,12 @@ void WaypointItem::updateCache()
} else {
if (_showIcon && _icon) {
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);
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());
} else
p.addRect(-pointSize/2, -pointSize/2, pointSize, pointSize);
@ -140,12 +150,16 @@ void WaypointItem::paint(QPainter *painter,
painter->setPen(_color);
if (_showLabel) {
if (_showLabel && !_waypoint.name().isEmpty()) {
painter->setFont(_font);
if (_showIcon && _icon)
painter->drawText(-qMax(_labelBB.x(), 0), _labelBB.height(),
_waypoint.name());
else
if (_showIcon && _icon) {
if (_waypoint.icon().isNull())
painter->drawText(-qMax(_labelBB.x(), 0), _labelBB.height(),
_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
+ _labelBB.height(), _waypoint.name());
}
@ -153,11 +167,13 @@ void WaypointItem::paint(QPainter *painter,
painter->setBrush(QBrush(_color, Qt::SolidPattern));
if (_showIcon && _icon) {
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,
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
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
painter->drawEllipse(-pointSize/2, -pointSize/2, pointSize, pointSize);

View File

@ -68,7 +68,7 @@ void NetworkTimeout::timerEvent(QTimerEvent *ev)
return;
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning())
reply->close();
reply->abort();
_timer.stop();
}

View File

@ -1,6 +1,7 @@
#include <cctype>
#include <cmath>
#include <QFileInfo>
#include <QTemporaryDir>
#ifdef Q_OS_ANDROID
#include <QUrl>
#include <QCoreApplication>
@ -139,3 +140,9 @@ QString Util::displayName(const QString &path)
return path;
#endif // Q_OS_ANDROID
}
const QTemporaryDir &Util::tempDir()
{
static QTemporaryDir dir;
return dir;
}

View File

@ -3,12 +3,15 @@
#include <QString>
class QTemporaryDir;
namespace Util
{
int str2int(const char *str, int len);
double niceNum(double x, bool round);
QString file2name(const QString &path);
QString displayName(const QString &path);
const QTemporaryDir &tempDir();
}
#endif // UTIL_H

View File

@ -53,6 +53,7 @@ static QMultiMap<QString, Parser*> parsers()
map.insert("gpx", &gpx);
map.insert("tcx", &tcx);
map.insert("kml", &kml);
map.insert("kmz", &kml);
map.insert("fit", &fit);
map.insert("csv", &csv);
map.insert("igc", &igc);
@ -162,7 +163,7 @@ QString Data::formats()
+ qApp->translate("Data", "IGC files") + " (*.igc);;"
+ qApp->translate("Data", "ITN files") + " (*.itn);;"
+ 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", "NMEA files") + " (*.nmea);;"
+ qApp->translate("Data", "ONmove files") + " (*.omd *.ghp);;"

View File

@ -11,6 +11,7 @@
#include "common/garmin.h"
#include "common/textcodec.h"
#include "common/color.h"
#include "common/util.h"
#include "address.h"
#include "gpiparser.h"
@ -388,12 +389,6 @@ static quint32 readAddress(DataStream &stream, Waypoint &waypoint)
return rs + rh.size;
}
static const QTemporaryDir &tempDir()
{
static QTemporaryDir dir;
return dir;
}
static quint32 readImageInfo(DataStream &stream, Waypoint &waypoint,
const QString &fileName, int &imgId)
{
@ -408,12 +403,12 @@ static quint32 readImageInfo(DataStream &stream, Waypoint &waypoint,
ba.resize(size);
stream.readRawData(ba.data(), ba.size());
if (tempDir().isValid()) {
if (Util::tempDir().isValid()) {
QBuffer buf(&ba);
QImageReader ir(&buf);
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(),
QString(ir.format())));
imgFile.open(QIODevice::WriteOnly);

View File

@ -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"
static bool isZIP(QFile *file)
{
quint32 magic;
return (file->read((char *)&magic, sizeof(magic)) == (qint64)sizeof(magic)
&& qFromLittleEndian(magic) == 0x04034b50);
}
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,
const QDateTime &timestamp)
{
TrackData *tp = 0;
Area *ap = 0;
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Point")) {
waypoints.append(Waypoint());
@ -472,28 +503,90 @@ void KMLParser::multiGeometry(QList<TrackData> &tracks, QList<Area> &areas,
w.setTimestamp(timestamp);
point(w);
} else if (_reader.name() == QLatin1String("LineString")) {
tracks.append(TrackData());
TrackData &t = tracks.last();
t.append(SegmentData());
t.setName(name);
t.setDescription(desc);
lineString(t.last());
if (!tp) {
tracks.append(TrackData());
tp = &tracks.last();
tp->setName(name);
tp->setDescription(desc);
}
tp->append(SegmentData());
lineString(tp->last());
} else if (_reader.name() == QLatin1String("Polygon")) {
areas.append(Area());
Area &a = areas.last();
a.setName(name);
a.setDescription(desc);
polygon(a);
if (!ap) {
areas.append(Area());
ap = &areas.last();
ap->setName(name);
ap->setDescription(desc);
}
polygon(*ap);
} else
_reader.skipCurrentElement();
}
}
void KMLParser::placemark(QList<TrackData> &tracks, QList<Area> &areas,
QVector<Waypoint> &waypoints)
void KMLParser::photoOverlay(const Ctx &ctx, 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;
Waypoint *wp = 0;
TrackData *tp = 0;
Area *ap = 0;
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("name"))
@ -506,87 +599,185 @@ void KMLParser::placemark(QList<TrackData> &tracks, QList<Area> &areas,
address = _reader.readElementText();
else if (_reader.name() == QLatin1String("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"))
multiGeometry(tracks, areas, waypoints, name, desc, timestamp);
else if (_reader.name() == QLatin1String("Point")) {
waypoints.append(Waypoint());
Waypoint &w = waypoints.last();
w.setName(name);
w.setDescription(desc);
w.setTimestamp(timestamp);
w.setAddress(address);
w.setPhone(phone);
point(w);
wp = &waypoints.last();
point(*wp);
} else if (_reader.name() == QLatin1String("LineString")
|| _reader.name() == QLatin1String("LinearRing")) {
tracks.append(TrackData());
TrackData &t = tracks.last();
t.append(SegmentData());
t.setName(name);
t.setDescription(desc);
lineString(t.last());
tp = &tracks.last();
tp->append(SegmentData());
lineString(tp->last());
} else if (_reader.name() == QLatin1String("Track")) {
tracks.append(TrackData());
TrackData &t = tracks.last();
t.append(SegmentData());
t.setName(name);
t.setDescription(desc);
track(t.last());
tp = &tracks.last();
tp->append(SegmentData());
track(tp->last());
} else if (_reader.name() == QLatin1String("MultiTrack")) {
tracks.append(TrackData());
TrackData &t = tracks.last();
t.setName(name);
t.setDescription(desc);
multiTrack(t);
tp = &tracks.last();
multiTrack(*tp);
} else if (_reader.name() == QLatin1String("Polygon")) {
areas.append(Area());
Area &a = areas.last();
a.setName(name);
a.setDescription(desc);
polygon(a);
} else
ap = &areas.last();
polygon(*ap);
} else if (_reader.name() == QLatin1String("styleUrl"))
id = styleUrl();
else
_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,
QVector<Waypoint> &waypoints)
QString KMLParser::icon()
{
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()) {
if (_reader.name() == QLatin1String("Placemark"))
placemark(tracks, areas, waypoints);
else if (_reader.name() == QLatin1String("Folder"))
folder(tracks, areas, waypoints);
if (_reader.name() == QLatin1String("Icon"))
icons.insert(id, QPixmap(dir.absoluteFilePath(icon())));
else
_reader.skipCurrentElement();
}
}
void KMLParser::document(QList<TrackData> &tracks, QList<Area> &areas,
QVector<Waypoint> &waypoints)
void KMLParser::styleMapPair(const QString &id, QMap<QString, QPixmap> &icons)
{
QString key, url;
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Document"))
document(tracks, areas, waypoints);
else if (_reader.name() == QLatin1String("Placemark"))
placemark(tracks, areas, waypoints);
else if (_reader.name() == QLatin1String("Folder"))
folder(tracks, areas, waypoints);
if (_reader.name() == QLatin1String("key"))
key = _reader.readElementText();
else if (_reader.name() == QLatin1String("styleUrl"))
url = styleUrl();
else
_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
_reader.skipCurrentElement();
}
}
void KMLParser::kml(QList<TrackData> &tracks, QList<Area> &areas,
QVector<Waypoint> &waypoints)
void KMLParser::style(const QDir &dir, QMap<QString, QPixmap> &icons)
{
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()) {
if (_reader.name() == QLatin1String("Document"))
document(tracks, areas, waypoints);
else if (_reader.name() == QLatin1String("Placemark"))
placemark(tracks, areas, waypoints);
document(ctx, tracks, areas, waypoints);
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
_reader.skipCurrentElement();
}
@ -596,15 +787,50 @@ bool KMLParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Area> &areas, QVector<Waypoint> &waypoints)
{
Q_UNUSED(routes);
QFileInfo fi(*file);
_reader.clear();
_reader.setDevice(file);
if (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("kml"))
kml(tracks, areas, waypoints);
else
_reader.raiseError("Not a KML file");
if (isZIP(file)) {
QZipReader zip(fi.absoluteFilePath(), QIODevice::ReadOnly);
QTemporaryDir tempDir;
if (!tempDir.isValid() || !zip.extractAll(tempDir.path()))
_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();

View File

@ -3,6 +3,7 @@
#include <QXmlStreamReader>
#include <QDateTime>
#include <QDir>
#include "parser.h"
class KMLParser : public Parser
@ -14,17 +15,28 @@ public:
int errorLine() const {return _reader.lineNumber();}
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);
void document(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,
void document(const Ctx &ctx, QList<TrackData> &tracks, QList<Area> &areas,
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,
QVector<Waypoint> &waypoints, const QString &name, const QString &desc,
const QDateTime &timestamp);
void photoOverlay(const Ctx &ctx, QVector<Waypoint> &waypoints,
QMap<QString, QPixmap> &icons);
void track(SegmentData &segment);
void multiTrack(TrackData &t);
void lineString(SegmentData &segment);
@ -45,6 +57,13 @@ private:
QDateTime timeStamp();
qreal number();
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;
};

View File

@ -234,6 +234,10 @@ void OruxMap::calibrationPoints(QXmlStreamReader &reader, const QSize &size,
return;
CalibrationPoint p(corner2point(corner, size), Coordinates(lon, lat));
if (!p.isValid()) {
reader.raiseError(QString("invalid calibration point"));
return;
}
points.append(p);
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;
QSize tileSize, size, calibrationSize;
QString datum, projection;
QList<CalibrationPoint> points;
QString fileName;
Projection proj;
Transform t;
QXmlStreamAttributes attr = reader.attributes();
if (!intAttr(reader, attr, "layerLevel", zoom))
@ -256,13 +261,14 @@ void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("OruxTracker"))
oruxTracker(reader, level + 1);
oruxTracker(reader, dir, level + 1);
else if (reader.name() == QLatin1String("MapName")) {
QString name(reader.readElementText());
if (!level)
if (!level && dir.isEmpty())
_name = name;
} else if (reader.name() == QLatin1String("MapChunks")) {
int xMax, yMax, width, height;
QString datum, projection;
QXmlStreamAttributes attr = reader.attributes();
if (!intAttr(reader, attr, "xMax", xMax))
@ -277,6 +283,8 @@ void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
return;
if (!strAttr(reader, attr, "projection", projection))
return;
if (!strAttr(reader, attr, "file_name", fileName))
return;
tileSize = QSize(width, height);
size = QSize(xMax * width, yMax * height);
@ -309,29 +317,52 @@ void OruxMap::mapCalibration(QXmlStreamReader &reader, int level)
calibrationSize = QSize(width, height);
reader.readElementText();
} else if (reader.name() == QLatin1String("CalibrationPoints"))
} else if (reader.name() == QLatin1String("CalibrationPoints")) {
QList<CalibrationPoint> points;
calibrationPoints(reader, calibrationSize, points);
else
t = computeTransformation(proj, points);
if (!t.isValid()) {
reader.raiseError(t.errorString());
return;
}
} else
reader.skipCurrentElement();
}
if (tileSize.isValid()) {
Transform t(computeTransformation(proj, points));
_zooms.append(Zoom(zoom, tileSize, size, proj, t));
if (!t.isValid()) {
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()) {
if (reader.name() == QLatin1String("MapCalibration"))
mapCalibration(reader, level);
mapCalibration(reader, dir, level);
else
reader.skipCurrentElement();
}
}
bool OruxMap::readXML(const QString &path)
bool OruxMap::readXML(const QString &path, const QString &dir)
{
QFile file(path);
@ -341,7 +372,7 @@ bool OruxMap::readXML(const QString &path)
QXmlStreamReader reader(&file);
if (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("OruxTracker"))
oruxTracker(reader, 0);
oruxTracker(reader, dir, 0);
else
reader.raiseError("Not a Orux map calibration file");
}
@ -354,51 +385,67 @@ bool OruxMap::readXML(const QString &path)
return true;
}
OruxMap::OruxMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _zoom(0), _mapRatio(1.0), _valid(false)
{
QFileInfo fi(fileName);
QDir dir(fi.absoluteDir());
if (!readXML(fileName))
return;
if (_zooms.isEmpty()) {
_errorString = "No usable zoom level found";
return;
QStringList list(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot));
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());
QFileInfo fi(fileName);
QDir dir(fi.absoluteDir());
QString dbFile(dir.absoluteFilePath("OruxMapsImages.db"));
if (!QFileInfo::exists(dbFile)) {
_errorString = "Image DB file not found";
return;
if (dir.exists("OruxMapsImages.db")) {
QString dbFile(dir.absoluteFilePath("OruxMapsImages.db"));
_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();
} 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;
}
@ -436,12 +483,14 @@ int OruxMap::zoomOut()
void OruxMap::load()
{
_db.open();
if (_db.isValid())
_db.open();
}
void OruxMap::unload()
{
_db.close();
if (_db.isValid())
_db.close();
}
void OruxMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
@ -450,19 +499,37 @@ void OruxMap::setDevicePixelRatio(qreal deviceRatio, qreal 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);
query.prepare("SELECT image FROM tiles WHERE z=:z AND x=:x AND y=:y");
query.bindValue(":z", zoom);
query.bindValue(":x", x);
query.bindValue(":y", y);
query.exec();
if (!query.first())
return QPixmap();
if (_db.isValid()) {
QSqlQuery query(_db);
query.prepare("SELECT image FROM tiles WHERE z=:z AND x=:x AND y=:y");
query.bindValue(":z", z.zoom);
query.bindValue(":x", x);
query.bindValue(":y", y);
query.exec();
QImage img(QImage::fromData(query.value(0).toByteArray()));
return QPixmap::fromImage(img);
if (!query.first()) {
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)
@ -484,14 +551,12 @@ void OruxMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
+ "_" + QString::number(x/z.tileSize.width())
+ "_" + QString::number(y/z.tileSize.height());
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())
QPixmapCache::insert(key, pixmap);
}
if (pixmap.isNull())
qWarning("%s: error loading tile image", qPrintable(key));
else {
if (!pixmap.isNull()) {
pixmap.setDevicePixelRatio(_mapRatio);
QPointF tp(tl.x() + i * ts.width(), tl.y() + j * ts.height());
painter->drawPixmap(tp, pixmap);

View File

@ -3,6 +3,7 @@
#include <QDebug>
#include <QSqlDatabase>
#include <QDir>
#include "map.h"
#include "projection.h"
#include "transform.h"
@ -44,9 +45,10 @@ public:
private:
struct Zoom {
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),
transform(transform) {}
transform(transform), fileName(fileName), set(set) {}
bool operator<(const Zoom &other) const
{return zoom < other.zoom;}
@ -55,14 +57,16 @@ private:
QSize size;
Projection projection;
Transform transform;
QString fileName;
QDir set;
};
bool readXML(const QString &path);
void oruxTracker(QXmlStreamReader &reader, int level);
void mapCalibration(QXmlStreamReader &reader, int level);
bool readXML(const QString &path, const QString &dir = QString());
void oruxTracker(QXmlStreamReader &reader, const QString &dir, int level);
void mapCalibration(QXmlStreamReader &reader, const QString &dir, int level);
void calibrationPoints(QXmlStreamReader &reader, const QSize &size,
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);