1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-30 22:51:16 +01:00

Added "runtime" offline map loading

Offline map loading & error handling cleanup
This commit is contained in:
Martin Tůma 2017-04-21 21:15:58 +02:00
parent 610ac3d73f
commit 0808f6679e
11 changed files with 361 additions and 286 deletions

View File

@ -23,7 +23,6 @@ HEADERS += src/config.h \
src/filebrowser.h \ src/filebrowser.h \
src/map.h \ src/map.h \
src/onlinemap.h \ src/onlinemap.h \
src/maplist.h \
src/downloader.h \ src/downloader.h \
src/units.h \ src/units.h \
src/scaleitem.h \ src/scaleitem.h \
@ -81,7 +80,6 @@ HEADERS += src/config.h \
src/timetype.h \ src/timetype.h \
src/emptymap.h \ src/emptymap.h \
src/offlinemap.h \ src/offlinemap.h \
src/mapdir.h \
src/matrix.h \ src/matrix.h \
src/tar.h \ src/tar.h \
src/atlas.h \ src/atlas.h \
@ -93,7 +91,8 @@ HEADERS += src/config.h \
src/lambertconic.h \ src/lambertconic.h \
src/ellipsoid.h \ src/ellipsoid.h \
src/ozf.h \ src/ozf.h \
src/datum.h src/datum.h \
src/maplist.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/gui.cpp \ src/gui.cpp \
src/poi.cpp \ src/poi.cpp \
@ -106,7 +105,6 @@ SOURCES += src/main.cpp \
src/sliderinfoitem.cpp \ src/sliderinfoitem.cpp \
src/filebrowser.cpp \ src/filebrowser.cpp \
src/onlinemap.cpp \ src/onlinemap.cpp \
src/maplist.cpp \
src/downloader.cpp \ src/downloader.cpp \
src/scaleitem.cpp \ src/scaleitem.cpp \
src/track.cpp \ src/track.cpp \
@ -150,7 +148,6 @@ SOURCES += src/main.cpp \
src/stylecombobox.cpp \ src/stylecombobox.cpp \
src/emptymap.cpp \ src/emptymap.cpp \
src/offlinemap.cpp \ src/offlinemap.cpp \
src/mapdir.cpp \
src/matrix.cpp \ src/matrix.cpp \
src/tar.cpp \ src/tar.cpp \
src/atlas.cpp \ src/atlas.cpp \
@ -160,7 +157,8 @@ SOURCES += src/main.cpp \
src/lambertconic.cpp \ src/lambertconic.cpp \
src/ellipsoid.cpp \ src/ellipsoid.cpp \
src/ozf.cpp \ src/ozf.cpp \
src/datum.cpp src/datum.cpp \
src/maplist.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 \

View File

@ -30,25 +30,35 @@ static bool yCmp(const OfflineMap *m1, const OfflineMap *m2)
return TL(m1).y() > TL(m2).y(); return TL(m1).y() > TL(m2).y();
} }
bool Atlas::isAtlas(Tar &tar, const QFileInfoList &files) bool Atlas::isAtlas(Tar &tar, const QString &path)
{ {
for (int i = 0; i < files.count(); i++) { QFileInfo fi(path);
const QString &fileName = files.at(i).fileName(); QByteArray ba;
if (fileName.endsWith(".tar")) {
if (!tar.load(files.at(i).absoluteFilePath())) {
qWarning("%s: %s: error loading tar file", qPrintable(_name), if (fi.suffix() == "tar") {
qPrintable(fileName)); if (!tar.load(path)) {
return false; _errorString = "Error reading tar file";
} return false;
QStringList tarFiles = tar.files(); }
for (int j = 0; j < tarFiles.size(); j++) QString tbaFileName = fi.completeBaseName() + ".tba";
if (tarFiles.at(j).endsWith(".tba")) ba = tar.file(tbaFileName);
return true; } else if (fi.suffix() == "tba") {
} else if (fileName.endsWith(".tba")) QFile tbaFile(path);
return true; if (!tbaFile.open(QIODevice::ReadOnly)) {
_errorString = QString("Error opening tba file: %1")
.arg(tbaFile.errorString());
return false;
}
ba = tbaFile.readAll();
} }
return false; if (ba.startsWith("Atlas 1.0"))
return true;
else {
_errorString = "Missing or invalid tba file";
return false;
}
} }
void Atlas::computeZooms() void Atlas::computeZooms()
@ -100,38 +110,45 @@ void Atlas::computeBounds()
BR(_maps.at(i))), QRectF(offsets.at(i), _maps.at(i)->bounds().size()))); BR(_maps.at(i))), QRectF(offsets.at(i), _maps.at(i)->bounds().size())));
} }
Atlas::Atlas(const QString &path, QObject *parent) : Map(parent) Atlas::Atlas(const QString &fileName, QObject *parent) : Map(parent)
{ {
Tar tar; Tar tar;
QFileInfo fi(fileName);
_valid = false; _valid = false;
_zoom = 0; _zoom = 0;
_name = fi.dir().dirName();
QFileInfo fi(path); if (!isAtlas(tar, fileName))
_name = fi.fileName();
QDir dir(path);
QFileInfoList files = dir.entryInfoList(QDir::Files);
if (!isAtlas(tar, files))
return; return;
QDir dir(fi.absolutePath());
QFileInfoList layers = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); QFileInfoList layers = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int n = 0; n < layers.count(); n++) { for (int n = 0; n < layers.count(); n++) {
QDir zdir(layers.at(n).absoluteFilePath()); QDir zdir(layers.at(n).absoluteFilePath());
QFileInfoList maps = zdir.entryInfoList(QDir::Dirs QFileInfoList maps = zdir.entryInfoList(QDir::Dirs
| QDir::NoDotAndDotDot); | QDir::NoDotAndDotDot);
for (int i = 0; i < maps.count(); i++) { for (int i = 0; i < maps.count(); i++) {
QString mapFile = maps.at(i).absoluteFilePath() + "/"
+ maps.at(i).fileName() + ".map";
OfflineMap *map; OfflineMap *map;
if (tar.isOpen()) if (tar.isOpen())
map = new OfflineMap(tar, maps.at(i).absoluteFilePath(), this); map = new OfflineMap(mapFile, tar, this);
else else
map = new OfflineMap(maps.at(i).absoluteFilePath(), this); map = new OfflineMap(mapFile, this);
if (map->isValid()) if (map->isValid())
_maps.append(map); _maps.append(map);
else {
_errorString = QString("Error loading map: %1: %2")
.arg(mapFile, map->errorString());
return;
}
} }
} }
if (_maps.isEmpty()) { if (_maps.isEmpty()) {
qWarning("%s: No usable maps available", qPrintable(_name)); _errorString = "No maps found in atlas";
return; return;
} }

View File

@ -11,7 +11,7 @@ class Atlas : public Map
Q_OBJECT Q_OBJECT
public: public:
Atlas(const QString &path, QObject *parent = 0); Atlas(const QString &fileName, QObject *parent = 0);
~Atlas(); ~Atlas();
const QString &name() const {return _name;} const QString &name() const {return _name;}
@ -29,16 +29,18 @@ public:
void draw(QPainter *painter, const QRectF &rect); void draw(QPainter *painter, const QRectF &rect);
bool isValid() {return _valid;} bool isValid() const {return _valid;}
const QString &errorString() const {return _errorString;}
private: private:
void draw(QPainter *painter, const QRectF &rect, int mapIndex); void draw(QPainter *painter, const QRectF &rect, int mapIndex);
bool isAtlas(Tar &tar, const QFileInfoList &files); bool isAtlas(Tar &tar, const QString &path);
void computeZooms(); void computeZooms();
void computeBounds(); void computeBounds();
QString _name; QString _name;
bool _valid; bool _valid;
QString _errorString;
QList<OfflineMap*> _maps; QList<OfflineMap*> _maps;
QVector<QPair<int, int> > _zooms; QVector<QPair<int, int> > _zooms;

View File

@ -30,7 +30,6 @@
#include "datum.h" #include "datum.h"
#include "map.h" #include "map.h"
#include "maplist.h" #include "maplist.h"
#include "mapdir.h"
#include "emptymap.h" #include "emptymap.h"
#include "elevationgraph.h" #include "elevationgraph.h"
#include "speedgraph.h" #include "speedgraph.h"
@ -166,21 +165,48 @@ void GUI::loadDatums()
void GUI::loadMaps() void GUI::loadMaps()
{ {
QList<Map*> online, offline; _ml = new MapList(this);
QString offline, online;
if (QFile::exists(USER_MAP_FILE)) if (QFile::exists(USER_MAP_FILE))
online = MapList::load(USER_MAP_FILE, this); online = USER_MAP_FILE;
else else if (QFile::exists(GLOBAL_MAP_FILE))
online = MapList::load(GLOBAL_MAP_FILE, this); online = GLOBAL_MAP_FILE;
if (!online.isNull() && !_ml->loadList(online))
qWarning("%s: %s", qPrintable(online), qPrintable(_ml->errorString()));
if (QFile::exists(USER_MAP_DIR)) if (QFile::exists(USER_MAP_DIR))
offline = MapDir::load(USER_MAP_DIR, this); offline = USER_MAP_DIR;
else else if (QFile::exists(GLOBAL_MAP_DIR))
offline = MapDir::load(GLOBAL_MAP_DIR, this); offline = GLOBAL_MAP_DIR;
_maps = online + offline; if (!offline.isNull()) {
QDir md(offline);
QFileInfoList ml = md.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
QStringList filters;
filters << "*.map" << "*.tba" << "*.tar";
_map = _maps.isEmpty() ? new EmptyMap(this) : _maps.first(); for (int i = 0; i < ml.size(); i++) {
QDir dir(ml.at(i).absoluteFilePath());
QFileInfoList fl = dir.entryInfoList(filters, QDir::Files);
if (fl.isEmpty())
qWarning("%s: no map/atlas file found",
qPrintable(ml.at(i).absoluteFilePath()));
else if (fl.size() > 1)
qWarning("%s: ambiguous directory content",
qPrintable(ml.at(i).absoluteFilePath()));
else
if (!_ml->loadMap(fl.first().absoluteFilePath()))
qWarning("%s: %s", qPrintable(fl.first().absoluteFilePath()),
qPrintable(_ml->errorString()));
}
}
_map = _ml->maps().isEmpty() ? new EmptyMap(this) : _ml->maps().first();
} }
void GUI::loadPOIs() void GUI::loadPOIs()
@ -209,33 +235,33 @@ void GUI::loadPOIs()
void GUI::createMapActions() void GUI::createMapActions()
{ {
QActionGroup *ag = new QActionGroup(this); _mapsSignalMapper = new QSignalMapper(this);
ag->setExclusive(true); _mapsActionGroup = new QActionGroup(this);
_mapsActionGroup->setExclusive(true);
QSignalMapper *sm = new QSignalMapper(this); for (int i = 0; i < _ml->maps().count(); i++) {
QAction *a = new QAction(_ml->maps().at(i)->name(), this);
for (int i = 0; i < _maps.count(); i++) {
QAction *a = new QAction(_maps.at(i)->name(), this);
a->setCheckable(true); a->setCheckable(true);
a->setActionGroup(ag); a->setActionGroup(_mapsActionGroup);
sm->setMapping(a, i); _mapsSignalMapper->setMapping(a, i);
connect(a, SIGNAL(triggered()), sm, SLOT(map())); connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
_mapActions.append(a); _mapActions.append(a);
} }
connect(sm, SIGNAL(mapped(int)), this, SLOT(mapChanged(int))); connect(_mapsSignalMapper, SIGNAL(mapped(int)), this,
SLOT(mapChanged(int)));
} }
void GUI::createPOIFilesActions() void GUI::createPOIFilesActions()
{ {
_poiFilesSM = new QSignalMapper(this); _poiFilesSignalMapper = new QSignalMapper(this);
for (int i = 0; i < _poi->files().count(); i++) for (int i = 0; i < _poi->files().count(); i++)
createPOIFileAction(i); createPOIFileAction(i);
connect(_poiFilesSM, SIGNAL(mapped(int)), this, SLOT(poiFileChecked(int))); connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this, SLOT(poiFileChecked(int)));
} }
QAction *GUI::createPOIFileAction(int index) QAction *GUI::createPOIFileAction(int index)
@ -244,8 +270,8 @@ QAction *GUI::createPOIFileAction(int index)
this); this);
a->setCheckable(true); a->setCheckable(true);
_poiFilesSM->setMapping(a, index); _poiFilesSignalMapper->setMapping(a, index);
connect(a, SIGNAL(triggered()), _poiFilesSM, SLOT(map())); connect(a, SIGNAL(triggered()), _poiFilesSignalMapper, SLOT(map()));
_poiFilesActions.append(a); _poiFilesActions.append(a);
@ -341,23 +367,24 @@ void GUI::createActions()
connect(_showMapAction, SIGNAL(triggered(bool)), _pathView, connect(_showMapAction, SIGNAL(triggered(bool)), _pathView,
SLOT(showMap(bool))); SLOT(showMap(bool)));
addAction(_showMapAction); addAction(_showMapAction);
_loadMapAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)), tr("Load map"),
this);
connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap()));
_clearMapCacheAction = new QAction(tr("Clear tile cache"), this); _clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
connect(_clearMapCacheAction, SIGNAL(triggered()), this, connect(_clearMapCacheAction, SIGNAL(triggered()), this,
SLOT(clearMapCache())); SLOT(clearMapCache()));
if (_maps.empty()) { createMapActions();
_nextMapAction = new QAction(tr("Next map"), this);
_nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
connect(_nextMapAction, SIGNAL(triggered()), this, SLOT(nextMap()));
addAction(_nextMapAction);
_prevMapAction = new QAction(tr("Next map"), this);
_prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap()));
addAction(_prevMapAction);
if (_ml->maps().isEmpty()) {
_showMapAction->setEnabled(false); _showMapAction->setEnabled(false);
_clearMapCacheAction->setEnabled(false); _clearMapCacheAction->setEnabled(false);
} else {
createMapActions();
_nextMapAction = new QAction(tr("Next map"), this);
_nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
connect(_nextMapAction, SIGNAL(triggered()), this, SLOT(nextMap()));
addAction(_nextMapAction);
_prevMapAction = new QAction(tr("Next map"), this);
_prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap()));
addAction(_prevMapAction);
} }
// Data actions // Data actions
@ -484,12 +511,13 @@ void GUI::createMenus()
fileMenu->addAction(_exitAction); fileMenu->addAction(_exitAction);
#endif // Q_OS_MAC #endif // Q_OS_MAC
QMenu *mapMenu = menuBar()->addMenu(tr("Map")); _mapMenu = menuBar()->addMenu(tr("Map"));
mapMenu->addActions(_mapActions); _mapMenu->addActions(_mapActions);
mapMenu->addSeparator(); _mapsEnd = _mapMenu->addSeparator();
mapMenu->addAction(_clearMapCacheAction); _mapMenu->addAction(_loadMapAction);
mapMenu->addSeparator(); _mapMenu->addAction(_clearMapCacheAction);
mapMenu->addAction(_showMapAction); _mapMenu->addSeparator();
_mapMenu->addAction(_showMapAction);
QMenu *graphMenu = menuBar()->addMenu(tr("Graph")); QMenu *graphMenu = menuBar()->addMenu(tr("Graph"));
graphMenu->addAction(_distanceGraphAction); graphMenu->addAction(_distanceGraphAction);
@ -1132,6 +1160,32 @@ void GUI::showGraphGrids(bool show)
_tabs.at(i)->showGrid(show); _tabs.at(i)->showGrid(show);
} }
void GUI::loadMap()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Load Map/Atlas"),
QString(), tr("Map/Atlas files (*.map *.tba *.tar)"));
if (fileName.isEmpty())
return;
if (_ml->loadMap(fileName)) {
QAction *a = new QAction(_ml->maps().last()->name(), this);
a->setCheckable(true);
a->setActionGroup(_mapsActionGroup);
_mapsSignalMapper->setMapping(a, _ml->maps().size() - 1);
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
_mapActions.append(a);
_mapMenu->insertAction(_mapsEnd, a);
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
a->activate(QAction::Trigger);
} else {
QString error = tr("Error loading map/atlas:") + "\n\n"
+ fileName + "\n\n" + _ml->errorString();
QMessageBox::critical(this, APP_NAME, error);
}
}
void GUI::clearMapCache() void GUI::clearMapCache()
{ {
_map->clearCache(); _map->clearCache();
@ -1178,26 +1232,27 @@ void GUI::updateWindowTitle()
void GUI::mapChanged(int index) void GUI::mapChanged(int index)
{ {
_map = _maps.at(index); _map = _ml->maps().at(index);
_pathView->setMap(_map); _pathView->setMap(_map);
} }
void GUI::nextMap() void GUI::nextMap()
{ {
if (_maps.count() < 2) if (_ml->maps().count() < 2)
return; return;
int next = (_maps.indexOf(_map) + 1) % _maps.count(); int next = (_ml->maps().indexOf(_map) + 1) % _ml->maps().count();
_mapActions.at(next)->setChecked(true); _mapActions.at(next)->setChecked(true);
mapChanged(next); mapChanged(next);
} }
void GUI::prevMap() void GUI::prevMap()
{ {
if (_maps.count() < 2) if (_ml->maps().count() < 2)
return; return;
int prev = (_maps.indexOf(_map) + _maps.count() - 1) % _maps.count(); int prev = (_ml->maps().indexOf(_map) + _ml->maps().count() - 1)
% _ml->maps().count();
_mapActions.at(prev)->setChecked(true); _mapActions.at(prev)->setChecked(true);
mapChanged(prev); mapChanged(prev);
} }
@ -1578,10 +1633,10 @@ void GUI::readSettings()
settings.beginGroup(MAP_SETTINGS_GROUP); settings.beginGroup(MAP_SETTINGS_GROUP);
if (settings.value(SHOW_MAP_SETTING, SHOW_MAP_DEFAULT).toBool()) if (settings.value(SHOW_MAP_SETTING, SHOW_MAP_DEFAULT).toBool())
_showMapAction->setChecked(true); _showMapAction->setChecked(true);
if (_maps.count()) { if (_ml->maps().count()) {
int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString()); int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString());
_mapActions.at(index)->setChecked(true); _mapActions.at(index)->setChecked(true);
_map = _maps.at(index); _map = _ml->maps().at(index);
_pathView->setMap(_map); _pathView->setMap(_map);
} }
settings.endGroup(); settings.endGroup();
@ -1743,8 +1798,8 @@ void GUI::readSettings()
int GUI::mapIndex(const QString &name) int GUI::mapIndex(const QString &name)
{ {
for (int i = 0; i < _maps.count(); i++) for (int i = 0; i < _ml->maps().count(); i++)
if (_maps.at(i)->name() == name) if (_ml->maps().at(i)->name() == name)
return i; return i;
return 0; return 0;

View File

@ -13,6 +13,7 @@
#include "exportdialog.h" #include "exportdialog.h"
#include "optionsdialog.h" #include "optionsdialog.h"
class QMenu; class QMenu;
class QToolBar; class QToolBar;
class QTabWidget; class QTabWidget;
@ -25,6 +26,7 @@ class FileBrowser;
class GraphTab; class GraphTab;
class PathView; class PathView;
class Map; class Map;
class MapList;
class GUI : public QMainWindow class GUI : public QMainWindow
{ {
@ -53,6 +55,7 @@ private slots:
void showFullscreen(bool show); void showFullscreen(bool show);
void showTracks(bool show); void showTracks(bool show);
void showRoutes(bool show); void showRoutes(bool show);
void loadMap();
void clearMapCache(); void clearMapCache();
void nextMap(); void nextMap();
void prevMap(); void prevMap();
@ -129,9 +132,11 @@ private:
QToolBar *_showToolBar; QToolBar *_showToolBar;
QToolBar *_navigationToolBar; QToolBar *_navigationToolBar;
QMenu *_poiFilesMenu; QMenu *_poiFilesMenu;
QMenu *_mapMenu;
QActionGroup *_fileActionGroup; QActionGroup *_fileActionGroup;
QActionGroup *_navigationActionGroup; QActionGroup *_navigationActionGroup;
QActionGroup *_mapsActionGroup;
QAction *_exitAction; QAction *_exitAction;
QAction *_keysAction; QAction *_keysAction;
QAction *_dataSourcesAction; QAction *_dataSourcesAction;
@ -149,6 +154,7 @@ private:
QAction *_showPOILabelsAction; QAction *_showPOILabelsAction;
QAction *_showMapAction; QAction *_showMapAction;
QAction *_fullscreenAction; QAction *_fullscreenAction;
QAction *_loadMapAction;
QAction *_clearMapCacheAction; QAction *_clearMapCacheAction;
QAction *_showGraphsAction; QAction *_showGraphsAction;
QAction *_showGraphGridAction; QAction *_showGraphGridAction;
@ -171,10 +177,12 @@ private:
QAction *_showWaypointLabelsAction; QAction *_showWaypointLabelsAction;
QAction *_showRouteWaypointsAction; QAction *_showRouteWaypointsAction;
QAction *_openOptionsAction; QAction *_openOptionsAction;
QAction *_mapsEnd;
QList<QAction*> _mapActions; QList<QAction*> _mapActions;
QList<QAction*> _poiFilesActions; QList<QAction*> _poiFilesActions;
QSignalMapper *_poiFilesSM; QSignalMapper *_poiFilesSignalMapper;
QSignalMapper *_mapsSignalMapper;
QLabel *_fileNameLabel; QLabel *_fileNameLabel;
QLabel *_distanceLabel; QLabel *_distanceLabel;
@ -185,7 +193,7 @@ private:
QList<GraphTab*> _tabs; QList<GraphTab*> _tabs;
POI *_poi; POI *_poi;
QList<Map*> _maps; MapList *_ml;
FileBrowser *_browser; FileBrowser *_browser;
QList<QString> _files; QList<QString> _files;

View File

@ -1,40 +0,0 @@
#include <QDir>
#include "atlas.h"
#include "offlinemap.h"
#include "mapdir.h"
QList<Map*> MapDir::load(const QString &path, QObject *parent)
{
QList<Map*> maps;
QDir dir(path);
if (!dir.exists())
return maps;
if (!dir.isReadable()) {
qWarning("Map directory not readable: %s\n", qPrintable(path));
return maps;
}
QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int i = 0; i < list.size(); i++) {
QFileInfo fileInfo = list.at(i);
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())
maps.append(map);
else
delete map;
}
}
return maps;
}

View File

@ -1,16 +0,0 @@
#ifndef MAPDIR_H
#define MAPDIR_H
#include <QList>
#include <QString>
class QObject;
class Map;
class MapDir
{
public:
static QList<Map*> load(const QString &path, QObject *parent = 0);
};
#endif // MAPDIR_H

View File

@ -1,41 +1,100 @@
#include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QDir>
#include "atlas.h"
#include "offlinemap.h"
#include "onlinemap.h" #include "onlinemap.h"
#include "maplist.h" #include "maplist.h"
QList<Map*> MapList::load(const QString &fileName, QObject *parent) bool MapList::loadListEntry(const QByteArray &line)
{ {
QList<Map*> maps; QList<QByteArray> list = line.split('\t');
QFileInfo fi(fileName); if (list.size() != 2)
return false;
if (!fi.exists()) QByteArray ba1 = list[0].trimmed();
return maps; QByteArray ba2 = list[1].trimmed();
if (ba1.isEmpty() || ba2.isEmpty())
return false;
QFile file(fileName); _maps.append(new OnlineMap(QString::fromUtf8(ba1.data(), ba1.size()),
QString::fromLatin1(ba2.data(), ba2.size()), this));
return true;
}
bool MapList::loadList(const QString &path)
{
QFile file(path);
if (!file.open(QFile::ReadOnly | QFile::Text)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
qWarning("Error opening map list file: %s: %s\n", _errorString = file.errorString();
qPrintable(fileName), qPrintable(file.errorString())); return false;
return maps;
} }
int ln = 0; int ln = 0;
while (!file.atEnd()) { while (!file.atEnd()) {
ln++; ln++;
QByteArray line = file.readLine(); QByteArray line = file.readLine();
QList<QByteArray> list = line.split('\t');
if (list.size() != 2) { if (!loadListEntry(line)) {
qWarning("Invalid map list entry on line %d\n", ln); _errorString = QString("Invalid map list entry on line %1.")
continue; .arg(QString::number(ln));
return false;
} }
QByteArray ba1 = list[0].trimmed();
QByteArray ba2 = list[1].trimmed();
maps.append(new OnlineMap(QString::fromUtf8(ba1.data(), ba1.size()),
QString::fromLatin1(ba2.data(), ba2.size()), parent));
} }
return maps; return true;
}
bool MapList::loadMap(const QString &path)
{
QFileInfo fi(path);
QString suffix = fi.suffix().toLower();
if (suffix == "map") {
OfflineMap *om = new OfflineMap(path, this);
if (om->isValid()) {
_maps.append(om);
return true;
} else {
_errorString = om->errorString();
delete om;
return false;
}
} else if (suffix == "tba") {
Atlas *atlas = new Atlas(path, this);
if (atlas->isValid()) {
_maps.append(atlas);
return true;
} else {
_errorString = atlas->errorString();
delete atlas;
return false;
}
} else if (suffix == "tar") {
Atlas *atlas = new Atlas(path, this);
if (atlas->isValid()) {
_maps.append(atlas);
return true;
} else {
_errorString = atlas->errorString();
delete atlas;
OfflineMap *om = new OfflineMap(path, this);
if (om->isValid()) {
_maps.append(om);
return true;
} else {
qWarning("%s: %s", qPrintable(path), qPrintable(_errorString));
qWarning("%s: %s", qPrintable(path),
qPrintable(om->errorString()));
_errorString = "Not a map/atlas file";
delete om;
return false;
}
}
} else {
_errorString = "Not a map/atlas file";
return false;
}
} }

View File

@ -1,16 +1,28 @@
#ifndef MAPLIST_H #ifndef MAPLIST_H
#define MAPLIST_H #define MAPLIST_H
#include <QList> #include <QObject>
#include <QString> #include <QString>
#include "map.h"
class QObject; class MapList : public QObject
class Map;
class MapList
{ {
Q_OBJECT
public: public:
static QList<Map*> load(const QString &fileName, QObject *parent = 0); MapList(QObject *parent = 0) : QObject(parent) {}
bool loadMap(const QString &path);
bool loadList(const QString &path);
QList<Map*> &maps() {return _maps;}
const QString &errorString() const {return _errorString;}
private:
bool loadListEntry(const QByteArray &line);
QList<Map*> _maps;
QString _errorString;
}; };
#endif // MAPLIST_H #endif // MAPLIST_H

View File

@ -46,7 +46,7 @@ static Coordinates toWGS84(Coordinates c, const Datum &datum)
double adb = 1.0 / (1.0 - from_f); double adb = 1.0 / (1.0 - from_f);
double rn = from_a / sqrt(1 - from_esq * ssqlat); double rn = from_a / sqrt(1 - from_esq * ssqlat);
double rm = from_a * (1 - from_esq) / pow((1 - from_esq * ssqlat), 1.5); double rm = from_a * (1 - from_esq) / pow((1 - from_esq * ssqlat), 1.5);
double from_h = 0.0; // we're flat! double from_h = 0.0;
double dlat = (-dX * slat * clon - dY * slat * slon + dZ * clat + da * rn double dlat = (-dX * slat * clon - dY * slat * slon + dZ * clat + da * rn
* from_esq * slat * clat / from_a + +df * (rm * adb + rn / adb) * slat * from_esq * slat * clat / from_a + +df * (rm * adb + rn / adb) * slat
@ -56,23 +56,22 @@ static Coordinates toWGS84(Coordinates c, const Datum &datum)
return Coordinates(c.lon() + rad2deg(dlon), c.lat() + rad2deg(dlat)); return Coordinates(c.lon() + rad2deg(dlon), c.lat() + rad2deg(dlat));
} }
int OfflineMap::parseMapFile(QIODevice &device, QList<ReferencePoint> &points,
int OfflineMap::parse(QIODevice &device, QList<ReferencePoint> &points,
QString &projection, ProjectionSetup &setup, QString &datum) QString &projection, ProjectionSetup &setup, QString &datum)
{ {
bool res; bool res;
int ln = 1; int ln = 1;
if (!device.open(QIODevice::ReadOnly))
return -1;
while (!device.atEnd()) { while (!device.atEnd()) {
QByteArray line = device.readLine(); QByteArray line = device.readLine();
if (ln == 1) { if (ln == 1) {
if (!line.trimmed().startsWith("OziExplorer Map Data File")) if (!line.trimmed().startsWith("OziExplorer Map Data File"))
return ln; return ln;
} else if (ln == 3) } else if (ln == 2)
_name = line.trimmed();
else if (ln == 3)
_imgPath = line.trimmed(); _imgPath = line.trimmed();
else if (ln == 5) else if (ln == 5)
datum = line.split(',').at(0).trimmed(); datum = line.split(',').at(0).trimmed();
@ -173,19 +172,37 @@ int OfflineMap::parseMapFile(QIODevice &device, QList<ReferencePoint> &points,
return 0; return 0;
} }
bool OfflineMap::parseMapFile(QIODevice &device, QList<ReferencePoint> &points,
QString &projection, ProjectionSetup &setup, QString &datum)
{
int el;
if (!device.open(QIODevice::ReadOnly)) {
_errorString = QString("Error opening map file: %1")
.arg(device.errorString());
return false;
}
if ((el = parse(device, points, projection, setup, datum))) {
_errorString = QString("Map file parse error on line %1").arg(el);
return false;
}
return true;
}
bool OfflineMap::createProjection(const QString &datum, bool OfflineMap::createProjection(const QString &datum,
const QString &projection, const ProjectionSetup &setup, const QString &projection, const ProjectionSetup &setup,
QList<ReferencePoint> &points) QList<ReferencePoint> &points)
{ {
if (points.count() < 2) { if (points.count() < 2) {
qWarning("%s: insufficient number of reference points", _errorString = "Insufficient number of reference points";
qPrintable(_name));
return false; return false;
} }
Datum d = Datum::datum(datum); Datum d = Datum::datum(datum);
if (d.isNull()) { if (d.isNull()) {
qWarning("%s: %s: unknown datum", qPrintable(_name), qPrintable(datum)); _errorString = QString("%1: Unknown datum").arg(datum);
return false; return false;
} }
@ -208,12 +225,11 @@ bool OfflineMap::createProjection(const QString &datum,
else if (!points.first().ll.isNull()) else if (!points.first().ll.isNull())
_projection = new UTM(d.ellipsoid(), points.first().ll); _projection = new UTM(d.ellipsoid(), points.first().ll);
else { else {
qWarning("%s: Can not determine UTM zone", qPrintable(_name)); _errorString = "Can not determine UTM zone";
return false; return false;
} }
} else { } else {
qWarning("%s: %s: unsupported map projection", qPrintable(_name), _errorString = QString("%1: Unknown map projection").arg(projection);
qPrintable(projection));
return false; return false;
} }
@ -270,7 +286,7 @@ bool OfflineMap::computeTransformation(const QList<ReferencePoint> &points)
Matrix M = Q.augemented(c); Matrix M = Q.augemented(c);
if (!M.eliminate()) { if (!M.eliminate()) {
qWarning("%s: singular transformation matrix", qPrintable(_name)); _errorString = "Singular transformation matrix";
return false; return false;
} }
@ -327,17 +343,15 @@ bool OfflineMap::getImageInfo(const QString &path)
if (ii.exists()) if (ii.exists())
_imgPath = ii.absoluteFilePath(); _imgPath = ii.absoluteFilePath();
else { else {
qWarning("%s: %s: No such image file", qPrintable(_name), _errorString = QString("%1: No such image file").arg(_imgPath);
qPrintable(_imgPath));
return false; return false;
} }
if (_imgPath.endsWith("ozf3", Qt::CaseInsensitive) if (_imgPath.endsWith("ozf3") || _imgPath.endsWith("ozf4")) {
|| _imgPath.endsWith("ozf4", Qt::CaseInsensitive)) { _errorString = QString("%1: Obfuscated image files not supported")
qWarning("%s: %s: obfuscated image files are not supported", .arg(QFileInfo(_imgPath).fileName());
qPrintable(_name), qPrintable(_imgPath));
return false; return false;
} else if (_imgPath.endsWith("ozf2", Qt::CaseInsensitive)) { } else if (_imgPath.endsWith("ozf2")) {
_ozf.load(_imgPath); _ozf.load(_imgPath);
_size = _ozf.size(); _size = _ozf.size();
} else { } else {
@ -345,8 +359,8 @@ bool OfflineMap::getImageInfo(const QString &path)
_size = img.size(); _size = img.size();
} }
if (!_size.isValid()) { if (!_size.isValid()) {
qWarning("%s: %s: error reading map image", qPrintable(_name), _errorString = QString("%1: Error reading map image")
qPrintable(_imgPath)); .arg(QFileInfo(_imgPath).fileName());
return false; return false;
} }
@ -356,7 +370,7 @@ bool OfflineMap::getImageInfo(const QString &path)
bool OfflineMap::getTileInfo(const QStringList &tiles, const QString &path) bool OfflineMap::getTileInfo(const QStringList &tiles, const QString &path)
{ {
if (tiles.isEmpty()) { if (tiles.isEmpty()) {
qWarning("%s: empty tile set", qPrintable(_name)); _errorString = "Empty tile set.";
return false; return false;
} }
@ -374,9 +388,8 @@ bool OfflineMap::getTileInfo(const QStringList &tiles, const QString &path)
_tileSize = QImageReader(path + "/" + tiles.at(i)).size(); _tileSize = QImageReader(path + "/" + tiles.at(i)).size();
} }
if (!_tileSize.isValid()) { if (!_tileSize.isValid()) {
qWarning("%s: error retrieving tile size: %s: invalid image", _errorString = QString("Error retrieving tile size: "
qPrintable(_name), qPrintable(QFileInfo(tiles.at(i)) "%1: Invalid image").arg(QFileInfo(tiles.at(i)).fileName());
.fileName()));
return false; return false;
} }
@ -384,42 +397,26 @@ bool OfflineMap::getTileInfo(const QStringList &tiles, const QString &path)
} }
} }
qWarning("%s: invalid tile names", qPrintable(_name)); _errorString = "Invalid tile names";
return false; return false;
} }
bool OfflineMap::mapLoaded(int res)
{
if (res) {
if (res == -2)
qWarning("%s: no map file found", qPrintable(_name));
else if (res == -1)
qWarning("%s: error opening map file", qPrintable(_name));
else
qWarning("%s: map file parse error on line: %d", qPrintable(_name),
res);
return false;
}
return true;
}
bool OfflineMap::totalSizeSet() bool OfflineMap::totalSizeSet()
{ {
if (!_size.isValid()) { if (!_size.isValid()) {
qWarning("%s: missing total image size (IWH)", qPrintable(_name)); _errorString = "Missing total image size (IWH)";
return false; return false;
} else } else
return true; return true;
} }
OfflineMap::OfflineMap(const QString &path, QObject *parent) : Map(parent) OfflineMap::OfflineMap(const QString &fileName, QObject *parent)
: Map(parent)
{ {
int errorLine = -2;
QList<ReferencePoint> points; QList<ReferencePoint> points;
QString proj, datum; QString proj, datum;
ProjectionSetup setup; ProjectionSetup setup;
QFileInfo fi(fileName);
_valid = false; _valid = false;
@ -427,38 +424,29 @@ OfflineMap::OfflineMap(const QString &path, QObject *parent) : Map(parent)
_projection = 0; _projection = 0;
_resolution = 0; _resolution = 0;
QFileInfo fi(path); if (fi.suffix() == "tar") {
_name = fi.fileName(); if (!_tar.load(fileName)) {
_errorString = "Error reading tar file";
QDir dir(path); return;
QFileInfoList mapFiles = dir.entryInfoList(QDir::Files);
for (int i = 0; i < mapFiles.count(); i++) {
const QString &fileName = mapFiles.at(i).fileName();
if (fileName.endsWith(".tar")) {
if (!_tar.load(mapFiles.at(i).absoluteFilePath())) {
qWarning("%s: %s: error loading tar file", qPrintable(_name),
qPrintable(fileName));
return;
}
QStringList tarFiles = _tar.files();
for (int j = 0; j < tarFiles.size(); j++) {
if (tarFiles.at(j).endsWith(".map")) {
QByteArray ba = _tar.file(tarFiles.at(j));
QBuffer buffer(&ba);
errorLine = parseMapFile(buffer, points, proj, setup, datum);
_imgPath = QString();
break;
}
}
break;
} else if (fileName.endsWith(".map")) {
QFile mapFile(mapFiles.at(i).absoluteFilePath());
errorLine = parseMapFile(mapFile, points, proj, setup, datum);
break;
} }
}
if (!mapLoaded(errorLine)) QString mapFileName = fi.completeBaseName() + ".map";
QByteArray ba = _tar.file(mapFileName);
if (ba.isNull()) {
_errorString = "Map file not found";
return;
}
QBuffer mapFile(&ba);
if (!parseMapFile(mapFile, points, proj, setup, datum))
return;
} else if (fi.suffix() =="map") {
QFile mapFile(fileName);
if (!parseMapFile(mapFile, points, proj, setup, datum))
return;
} else {
_errorString = "Not a map file";
return; return;
}
if (!createProjection(datum, proj, setup, points)) if (!createProjection(datum, proj, setup, points))
return; return;
@ -471,16 +459,17 @@ OfflineMap::OfflineMap(const QString &path, QObject *parent) : Map(parent)
return; return;
if (!getTileInfo(_tar.files())) if (!getTileInfo(_tar.files()))
return; return;
_imgPath = QString();
} else { } else {
QDir set(fi.absoluteFilePath() + "/" + "set"); QDir set(fi.absolutePath() + "/" + "set");
if (set.exists()) { if (set.exists()) {
if (!totalSizeSet()) if (!totalSizeSet())
return; return;
if (!getTileInfo(set.entryList(), set.canonicalPath())) if (!getTileInfo(set.entryList(), set.absolutePath()))
return; return;
_imgPath = QString(); _imgPath = QString();
} else { } else {
if (!getImageInfo(fi.absoluteFilePath())) if (!getImageInfo(fi.absolutePath()))
return; return;
} }
} }
@ -488,36 +477,31 @@ OfflineMap::OfflineMap(const QString &path, QObject *parent) : Map(parent)
_valid = true; _valid = true;
} }
OfflineMap::OfflineMap(Tar &tar, const QString &path, QObject *parent) OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent)
: Map(parent) : Map(parent)
{ {
int errorLine = -2;
QList<ReferencePoint> points; QList<ReferencePoint> points;
QString proj, datum; QString proj, datum;
ProjectionSetup setup; ProjectionSetup setup;
QFileInfo fi(fileName);
_valid = false; _valid = false;
_img = 0; _img = 0;
_projection = 0; _projection = 0;
QFileInfo fi(path); QFileInfo map(fi.absolutePath());
_name = fi.fileName(); QFileInfo layer(map.absolutePath());
QString mapFile = layer.fileName() + "/" + map.fileName() + "/"
QFileInfo li(fi.absoluteDir().dirName()); + fi.fileName();
QString prefix = li.fileName() + "/" + fi.fileName() + "/"; QByteArray ba = tar.file(mapFile);
QStringList tarFiles = tar.files(); if (ba.isNull()) {
for (int j = 0; j < tarFiles.size(); j++) { _errorString = "Map file not found";
if (tarFiles.at(j).startsWith(prefix)) {
QByteArray ba = tar.file(tarFiles.at(j));
QBuffer buffer(&ba);
errorLine = parseMapFile(buffer, points, proj, setup, datum);
break;
}
}
if (!mapLoaded(errorLine))
return; return;
}
QBuffer buffer(&ba);
if (!parseMapFile(buffer, points, proj, setup, datum))
return;
if (!createProjection(datum, proj, setup, points)) if (!createProjection(datum, proj, setup, points))
return; return;
if (!totalSizeSet()) if (!totalSizeSet())
@ -526,14 +510,8 @@ OfflineMap::OfflineMap(Tar &tar, const QString &path, QObject *parent)
return; return;
computeResolution(points); computeResolution(points);
QDir dir(path);
QFileInfoList mapFiles = dir.entryInfoList(QDir::Files);
for (int i = 0; i < mapFiles.count(); i++) {
const QString &fileName = mapFiles.at(i).absoluteFilePath();
if (fileName.endsWith(".tar"))
_tarPath = fileName;
}
_tarPath = fi.absolutePath() + "/" + fi.completeBaseName() + ".tar";
_imgPath = QString(); _imgPath = QString();
_valid = true; _valid = true;
} }
@ -550,8 +528,7 @@ void OfflineMap::load()
{ {
if (!_tarPath.isNull() && !_tileSize.isValid()) { if (!_tarPath.isNull() && !_tileSize.isValid()) {
if (!_tar.load(_tarPath)) { if (!_tar.load(_tarPath)) {
qWarning("%s: %s: error loading tar file", qPrintable(_name), qWarning("%s: error loading tar file", qPrintable(_tarPath));
qPrintable(_tarPath));
return; return;
} }
getTileInfo(_tar.files()); getTileInfo(_tar.files());

View File

@ -17,8 +17,8 @@ class OfflineMap : public Map
Q_OBJECT Q_OBJECT
public: public:
OfflineMap(const QString &path, QObject *parent = 0); OfflineMap(const QString &fileName, QObject *parent = 0);
OfflineMap(Tar &tar, const QString &path, QObject *parent = 0); OfflineMap(const QString &fileName, Tar &tar, QObject *parent = 0);
~OfflineMap(); ~OfflineMap();
const QString &name() const {return _name;} const QString &name() const {return _name;}
@ -41,7 +41,8 @@ public:
void load(); void load();
void unload(); void unload();
bool isValid() {return _valid;} bool isValid() const {return _valid;}
const QString &errorString() const {return _errorString;}
QPointF ll2pp(const Coordinates &c) const QPointF ll2pp(const Coordinates &c) const
{return _projection->ll2xy(c);} {return _projection->ll2xy(c);}
@ -68,9 +69,10 @@ private:
int zone; int zone;
} ProjectionSetup; } ProjectionSetup;
int parseMapFile(QIODevice &device, QList<ReferencePoint> &points, int parse(QIODevice &device, QList<ReferencePoint> &points,
QString &projection, ProjectionSetup &setup, QString &datum);
bool parseMapFile(QIODevice &device, QList<ReferencePoint> &points,
QString &projection, ProjectionSetup &setup, QString &datum); QString &projection, ProjectionSetup &setup, QString &datum);
bool mapLoaded(int res);
bool totalSizeSet(); bool totalSizeSet();
bool createProjection(const QString &datum, const QString &projection, bool createProjection(const QString &datum, const QString &projection,
const ProjectionSetup &setup, QList<ReferencePoint> &points); const ProjectionSetup &setup, QList<ReferencePoint> &points);
@ -84,6 +86,9 @@ private:
void drawImage(QPainter *painter, const QRectF &rect); void drawImage(QPainter *painter, const QRectF &rect);
QString _name; QString _name;
bool _valid;
QString _errorString;
QSize _size; QSize _size;
Projection *_projection; Projection *_projection;
QTransform _transform, _inverted; QTransform _transform, _inverted;
@ -96,8 +101,6 @@ private:
QString _imgPath; QString _imgPath;
QSize _tileSize; QSize _tileSize;
QString _tileName; QString _tileName;
bool _valid;
}; };
#endif // OFFLINEMAP_H #endif // OFFLINEMAP_H