1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-24 11:45:53 +01:00

Added support for Trekbuddy atlases

This commit is contained in:
Martin Tůma 2017-03-26 15:32:55 +02:00
parent bd23120c2c
commit bde3c8cc3d
7 changed files with 335 additions and 13 deletions

View File

@ -83,7 +83,8 @@ HEADERS += src/config.h \
src/offlinemap.h \ src/offlinemap.h \
src/mapdir.h \ src/mapdir.h \
src/matrix.h \ src/matrix.h \
src/tar.h src/tar.h \
src/atlas.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/gui.cpp \ src/gui.cpp \
src/poi.cpp \ src/poi.cpp \
@ -142,7 +143,8 @@ SOURCES += src/main.cpp \
src/offlinemap.cpp \ src/offlinemap.cpp \
src/mapdir.cpp \ src/mapdir.cpp \
src/matrix.cpp \ src/matrix.cpp \
src/tar.cpp src/tar.cpp \
src/atlas.cpp
RESOURCES += gpxsee.qrc RESOURCES += gpxsee.qrc
TRANSLATIONS = lang/gpxsee_cs.ts \ TRANSLATIONS = lang/gpxsee_cs.ts \
lang/gpxsee_sv.ts lang/gpxsee_sv.ts

261
src/atlas.cpp Normal file
View File

@ -0,0 +1,261 @@
#include <QDir>
#include <QtAlgorithms>
#include <QPainter>
#include "tar.h"
#include "atlas.h"
#define ZOOM_THRESHOLD 0.9
#define TL(m) ((m)->xy2ll((m)->bounds().topLeft()))
#define BR(m) ((m)->xy2ll((m)->bounds().bottomRight()))
static bool resCmp(const OfflineMap *m1, const OfflineMap *m2)
{
qreal r1, r2;
r1 = m1->resolution(m1->bounds().center());
r2 = m2->resolution(m2->bounds().center());
return r1 > r2;
}
static bool lonCmp(const OfflineMap *m1, const OfflineMap *m2)
{
return TL(m1).lon() < TL(m2).lon();
}
static bool latCmp(const OfflineMap *m1, const OfflineMap *m2)
{
return TL(m1).lat() > TL(m2).lat();
}
bool Atlas::isAtlas(const QFileInfoList &files)
{
for (int i = 0; i < files.count(); i++) {
const QString &fileName = files.at(i).fileName();
if (fileName.endsWith(".tar")) {
Tar tar;
if (!tar.load(files.at(i).absoluteFilePath())) {
qWarning("%s: %s: error loading tar file", qPrintable(_name),
qPrintable(fileName));
return false;
}
QStringList tarFiles = tar.files();
for (int j = 0; j < tarFiles.size(); j++)
if (tarFiles.at(j).endsWith(".tba"))
return true;
} else if (fileName.endsWith(".tba"))
return true;
}
return false;
}
void Atlas::computeZooms()
{
qSort(_maps.begin(), _maps.end(), resCmp);
_zooms.append(QPair<int, int>(0, _maps.count() - 1));
for (int i = 1; i < _maps.count(); i++) {
qreal last = _maps.at(i-1)->resolution(_maps.at(i)->bounds().center());
qreal cur = _maps.at(i)->resolution(_maps.at(i)->bounds().center());
if (cur < last * ZOOM_THRESHOLD) {
_zooms.last().second = i-1;
_zooms.append(QPair<int, int>(i, _maps.count() - 1));
}
}
}
void Atlas::computeBounds()
{
QList<QPointF> offsets;
for (int i = 0; i < _maps.count(); i++)
offsets.append(QPointF());
for (int z = 0; z < _zooms.count(); z++) {
qreal w = 0, h = 0;
QList<OfflineMap*> m;
for (int i = _zooms.at(z).first; i <= _zooms.at(z).second; i++)
m.append(_maps.at(i));
qSort(m.begin(), m.end(), lonCmp);
offsets[_maps.indexOf(m.first())].setX(w);
for (int i = 1; i < m.size(); i++) {
if (TL(m.at(i)).lon() > TL(m.at(i-1)).lon()) {
w += m.at(i-1)->ll2xy(Coordinates(TL(m.at(i)).lon(),
TL(m.at(i-1)).lat())).x() - m.at(i-1)->bounds().left();
offsets[_maps.indexOf(m.at(i))].setX(round(w));
}
}
qSort(m.begin(), m.end(), latCmp);
offsets[_maps.indexOf(m.first())].setY(h);
for (int i = 1; i < m.size(); i++) {
if (TL(m.at(i)).lat() < TL(m.at(i-1)).lat()) {
h += m.at(i-1)->ll2xy(Coordinates(TL(m.at(i-1)).lon(),
TL(m.at(i)).lat())).y() - m.at(i-1)->bounds().top();
offsets[_maps.indexOf(m.at(i))].setY(round(h));
}
}
}
for (int i = 0; i < _maps.count(); i++)
_bounds.append(QPair<QRectF, QRectF>(QRectF(TL(_maps.at(i)).toPointF(),
BR(_maps.at(i)).toPointF()), QRectF(offsets.at(i),
_maps.at(i)->bounds().size())));
}
Atlas::Atlas(const QString &path, QObject *parent) : Map(parent)
{
_valid = false;
QFileInfo fi(path);
_name = fi.fileName();
QDir dir(path);
QFileInfoList files = dir.entryInfoList(QDir::Files);
if (!isAtlas(files))
return;
QFileInfoList layers = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int n = 0; n < layers.count(); n++) {
QDir zdir(layers.at(n).absoluteFilePath());
QFileInfoList maps = zdir.entryInfoList(QDir::Dirs
| QDir::NoDotAndDotDot);
for (int i = 0; i < maps.count(); i++) {
OfflineMap *map = new OfflineMap(maps.at(i).absoluteFilePath());
if (map->isValid())
_maps.append(map);
}
}
if (_maps.isEmpty()) {
qWarning("%s: No usable maps available", qPrintable(_name));
return;
}
computeZooms();
computeBounds();
_zoom = 0;
_valid = true;
}
QRectF Atlas::bounds() const
{
QSizeF s(0, 0);
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).second.right() > s.width())
s.setWidth(_bounds.at(i).second.right());
if (_bounds.at(i).second.bottom() > s.height())
s.setHeight(_bounds.at(i).second.bottom());
}
return QRectF(QPointF(0, 0), s);
}
qreal Atlas::resolution(const QPointF &p) const
{
int idx = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).second.contains(p)) {
idx = i;
break;
}
}
return _maps.at(idx)->resolution(p);
}
qreal Atlas::zoom() const
{
return _zoom;
}
qreal Atlas::zoomFit(const QSize &size, const QRectF &br)
{
_zoom = 0;
for (int z = 0; z < _zooms.count(); z++) {
for (int i = _zooms.at(z).first; i <= _zooms.at(z).second; i++) {
if (_bounds.at(i).first.contains(br.center()))
continue;
QRect sbr = QRectF(_maps.at(i)->ll2xy(br.topLeft()),
_maps.at(i)->ll2xy(br.bottomRight())).toRect().normalized();
if (sbr.size().width() > size.width()
|| sbr.size().height() > size.height())
return _zoom;
_zoom = z;
break;
}
}
return _zoom;
}
qreal Atlas::zoomIn()
{
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
return _zoom;
}
qreal Atlas::zoomOut()
{
_zoom = qMax(_zoom - 1, 0);
return _zoom;
}
QPointF Atlas::ll2xy(const Coordinates &c) const
{
int idx = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).first.contains(c.lon(), c.lat())) {
idx = i;
break;
}
}
QPointF p = _maps.at(idx)->ll2xy(c);
return p + _bounds.at(idx).second.topLeft();
}
Coordinates Atlas::xy2ll(const QPointF &p) const
{
int idx = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).second.contains(p)) {
idx = i;
break;
}
}
QPointF p2 = p - _bounds.at(idx).second.topLeft();
return _maps.at(idx)->xy2ll(p2);
}
void Atlas::draw(QPainter *painter, const QRectF &rect)
{
painter->fillRect(rect, Qt::white);
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (rect.intersects(_bounds.at(i).second)) {
OfflineMap *map = _maps.at(i);
QRectF ir = rect.intersected(_bounds.at(i).second);
const QPointF offset = _bounds.at(i).second.topLeft();
QRectF pr = QRectF(ir.topLeft() - offset, ir.size());
painter->translate(offset);
map->draw(painter, pr);
painter->translate(-offset);
}
}
}

47
src/atlas.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef ATLAS_H
#define ATLAS_H
#include <QFileInfoList>
#include "map.h"
#include "offlinemap.h"
class Atlas : public Map
{
Q_OBJECT
public:
Atlas(const QString &path, QObject *parent = 0);
const QString &name() const {return _name;}
QRectF bounds() const;
qreal resolution(const QPointF &p) const;
qreal zoom() const;
qreal zoomFit(const QSize &size, const QRectF &br);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const;
Coordinates xy2ll(const QPointF &p) const;
void draw(QPainter *painter, const QRectF &rect);
bool isValid() {return _valid;}
private:
bool isAtlas(const QFileInfoList &files);
void computeZooms();
void computeBounds();
QString _name;
bool _valid;
QList<OfflineMap*> _maps;
QVector<QPair<int, int> > _zooms;
QVector<QPair<QRectF, QRectF> > _bounds;
int _zoom;
};
#endif // ATLAS_H

View File

@ -32,6 +32,8 @@ public:
QPointF toMercator() const; QPointF toMercator() const;
static Coordinates fromMercator(const QPointF &m); static Coordinates fromMercator(const QPointF &m);
QPointF toPointF() const {return QPointF(_lon, _lat);}
private: private:
qreal _lat, _lon; qreal _lat, _lon;
}; };

View File

@ -1,8 +1,7 @@
#include <QDir> #include <QDir>
#include "mapdir.h" #include "atlas.h"
#include <QDebug>
#include "offlinemap.h" #include "offlinemap.h"
#include "mapdir.h"
QList<Map*> MapDir::load(const QString &path, QObject *parent) QList<Map*> MapDir::load(const QString &path, QObject *parent)
{ {
@ -21,12 +20,21 @@ QList<Map*> MapDir::load(const QString &path, QObject *parent)
QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
QFileInfo fileInfo = list.at(i); QFileInfo fileInfo = list.at(i);
OfflineMap *map = new OfflineMap(fileInfo.absoluteFilePath(), parent);
Atlas *atlas = new Atlas(fileInfo.absoluteFilePath(), parent);
if (atlas->isValid())
maps.append(atlas);
else {
delete atlas;
OfflineMap *map = new OfflineMap(fileInfo.absoluteFilePath(),
parent);
if (map->isValid()) if (map->isValid())
maps.append(map); maps.append(map);
else else
delete map; delete map;
} }
}
return maps; return maps;
} }

View File

@ -353,6 +353,7 @@ void OfflineMap::draw(QPainter *painter, const QRectF &rect)
if (!QPixmapCache::find(key, &pixmap)) { if (!QPixmapCache::find(key, &pixmap)) {
QByteArray ba = _tar.file(tileName); QByteArray ba = _tar.file(tileName);
pixmap = QPixmap::fromImage(QImage::fromData(ba)); pixmap = QPixmap::fromImage(QImage::fromData(ba));
if (!pixmap.isNull())
QPixmapCache::insert(key, pixmap); QPixmapCache::insert(key, pixmap);
} }
} else } else

View File

@ -65,7 +65,8 @@ bool Tar::load(const QString &path)
QByteArray Tar::file(const QString &name) QByteArray Tar::file(const QString &name)
{ {
QMap<QString, Tar::Info>::const_iterator it = _index.find(name); QMap<QString, Tar::Info>::const_iterator it = _index.find(name);
Q_ASSERT(it != _index.end()); if (it == _index.end())
return QByteArray();
if (_file.seek(it.value().offset())) if (_file.seek(it.value().offset()))
return _file.read(it.value().size()); return _file.read(it.value().size());