From b21c2267cf1b15f92ad695e91af7b39eba00413f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Sat, 18 Mar 2017 01:30:31 +0100 Subject: [PATCH] Added support for OziExplorer offline maps --- gpxsee.pro | 17 ++- pkg/gpxsee.nsi | 2 +- pkg/gpxsee64.nsi | 2 +- src/app.cpp | 10 +- src/app.h | 2 - src/config.h | 3 + src/coordinates.cpp | 7 +- src/coordinates.h | 6 +- src/data.cpp | 4 +- src/downloader.cpp | 10 +- src/emptymap.cpp | 76 ++++++++++ src/emptymap.h | 33 +++++ src/fitparser.cpp | 3 +- src/gui.cpp | 69 +++++---- src/gui.h | 5 +- src/map.cpp | 136 ------------------ src/map.h | 49 +++---- src/mapdir.cpp | 32 +++++ src/mapdir.h | 16 +++ src/maplist.cpp | 18 +-- src/maplist.h | 9 +- src/matrix.cpp | 117 +++++++++++++++ src/matrix.h | 33 +++++ src/misc.cpp | 6 + src/misc.h | 3 + src/onlinemap.cpp | 255 +++++++++++++++++++++++++++++++++ src/onlinemap.h | 56 ++++++++ src/ozimap.cpp | 271 +++++++++++++++++++++++++++++++++++ src/ozimap.h | 55 ++++++++ src/path.cpp | 24 ++++ src/path.h | 8 +- src/pathitem.cpp | 85 ++++++----- src/pathitem.h | 28 ++-- src/pathview.cpp | 329 ++++++++++++++++--------------------------- src/pathview.h | 13 +- src/poi.cpp | 11 +- src/poi.h | 4 +- src/route.cpp | 10 ++ src/route.h | 8 +- src/routeitem.cpp | 56 +++----- src/routeitem.h | 11 +- src/tcxparser.cpp | 2 +- src/track.h | 1 + src/trackitem.cpp | 35 ++--- src/trackitem.h | 8 +- src/waypointitem.cpp | 35 ++--- src/waypointitem.h | 13 +- 47 files changed, 1379 insertions(+), 607 deletions(-) create mode 100644 src/emptymap.cpp create mode 100644 src/emptymap.h delete mode 100644 src/map.cpp create mode 100644 src/mapdir.cpp create mode 100644 src/mapdir.h create mode 100644 src/matrix.cpp create mode 100644 src/matrix.h create mode 100644 src/onlinemap.cpp create mode 100644 src/onlinemap.h create mode 100644 src/ozimap.cpp create mode 100644 src/ozimap.h diff --git a/gpxsee.pro b/gpxsee.pro index 3ab476e5..e031ade6 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -1,5 +1,5 @@ TARGET = GPXSee -VERSION = 3.10 +VERSION = 4.0 QT += core \ gui \ network @@ -22,6 +22,7 @@ HEADERS += src/config.h \ src/sliderinfoitem.h \ src/filebrowser.h \ src/map.h \ + src/onlinemap.h \ src/maplist.h \ src/downloader.h \ src/units.h \ @@ -77,7 +78,11 @@ HEADERS += src/config.h \ src/colorbox.h \ src/stylecombobox.h \ src/opengl.h \ - src/timetype.h + src/timetype.h \ + src/emptymap.h \ + src/ozimap.h \ + src/mapdir.h \ + src/matrix.h SOURCES += src/main.cpp \ src/gui.cpp \ src/poi.cpp \ @@ -89,7 +94,7 @@ SOURCES += src/main.cpp \ src/speedgraph.cpp \ src/sliderinfoitem.cpp \ src/filebrowser.cpp \ - src/map.cpp \ + src/onlinemap.cpp \ src/maplist.cpp \ src/downloader.cpp \ src/scaleitem.cpp \ @@ -131,7 +136,11 @@ SOURCES += src/main.cpp \ src/nmeaparser.cpp \ src/optionsdialog.cpp \ src/colorbox.cpp \ - src/stylecombobox.cpp + src/stylecombobox.cpp \ + src/emptymap.cpp \ + src/ozimap.cpp \ + src/mapdir.cpp \ + src/matrix.cpp RESOURCES += gpxsee.qrc TRANSLATIONS = lang/gpxsee_cs.ts \ lang/gpxsee_sv.ts diff --git a/pkg/gpxsee.nsi b/pkg/gpxsee.nsi index ae40f19b..5776b9e9 100644 --- a/pkg/gpxsee.nsi +++ b/pkg/gpxsee.nsi @@ -5,7 +5,7 @@ ; The name of the installer Name "GPXSee" ; Program version -!define VERSION "3.10" +!define VERSION "4.0" ; The file to write OutFile "GPXSee-${VERSION}.exe" diff --git a/pkg/gpxsee64.nsi b/pkg/gpxsee64.nsi index 7042c080..160322da 100644 --- a/pkg/gpxsee64.nsi +++ b/pkg/gpxsee64.nsi @@ -5,7 +5,7 @@ ; The name of the installer Name "GPXSee" ; Program version -!define VERSION "3.10" +!define VERSION "4.0" ; The file to write OutFile "GPXSee-${VERSION}_x64.exe" diff --git a/src/app.cpp b/src/app.cpp index e7ffc208..ed014355 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -6,23 +6,26 @@ #include #include "opengl.h" #include "gui.h" +#include "onlinemap.h" +#include "downloader.h" #include "app.h" App::App(int &argc, char **argv) : QApplication(argc, argv), _argc(argc), _argv(argv) { - _translator = new QTranslator(); + QTranslator *translator = new QTranslator(this); QString locale = QLocale::system().name(); - _translator->load(QString(":/lang/gpxsee_") + locale); - installTranslator(_translator); + translator->load(QString(":/lang/gpxsee_") + locale); + installTranslator(translator); #ifdef Q_OS_MAC setAttribute(Qt::AA_DontShowIconsInMenus); #endif // Q_OS_MAC QNetworkProxyFactory::setUseSystemConfiguration(true); QPixmapCache::setCacheLimit(65536); + OnlineMap::setDownloader(new Downloader(this)); OPENGL_SET_SAMPLES(4); _gui = new GUI(); @@ -31,7 +34,6 @@ App::App(int &argc, char **argv) : QApplication(argc, argv), App::~App() { delete _gui; - delete _translator; } void App::run() diff --git a/src/app.h b/src/app.h index d8c13ae0..f1477833 100644 --- a/src/app.h +++ b/src/app.h @@ -4,7 +4,6 @@ #include class GUI; -class QTranslator; class App : QApplication { @@ -22,7 +21,6 @@ private: int &_argc; char **_argv; GUI *_gui; - QTranslator *_translator; }; #endif // APP_H diff --git a/src/config.h b/src/config.h index 745ac095..3f4334ba 100644 --- a/src/config.h +++ b/src/config.h @@ -14,6 +14,7 @@ #define SCREEN_DPI 96.0 #define MAP_FILE QString("maps.txt") +#define MAP_DIR QString("maps") #define POI_DIR QString("POI") #if defined(Q_OS_WIN32) @@ -28,8 +29,10 @@ #define GLOBAL_DIR QString("/usr/share/gpxsee") #endif +#define USER_MAP_DIR USER_DIR + QString("/") + MAP_DIR #define USER_MAP_FILE USER_DIR + QString("/") + MAP_FILE #define USER_POI_DIR USER_DIR + QString("/") + POI_DIR +#define GLOBAL_MAP_DIR GLOBAL_DIR + QString("/") + MAP_DIR #define GLOBAL_MAP_FILE GLOBAL_DIR + QString("/") + MAP_FILE #define GLOBAL_POI_DIR GLOBAL_DIR + QString("/") + POI_DIR #define TILES_DIR USER_DIR + QString("/tiles") diff --git a/src/coordinates.cpp b/src/coordinates.cpp index 34e21e14..02b78396 100644 --- a/src/coordinates.cpp +++ b/src/coordinates.cpp @@ -27,17 +27,12 @@ Coordinates Coordinates::fromMercator(const QPointF &m) return Coordinates(m.x(), rad2deg(2 * atan(exp(deg2rad(m.y()))) - M_PI/2)); } -bool operator==(const Coordinates &c1, const Coordinates &c2) -{ - return (c1.lat() == c2.lat() && c1.lon() == c2.lon()); -} - QDebug operator<<(QDebug dbg, const Coordinates &coordinates) { dbg.nospace() << "Coordinates(" << coordinates.lon() << ", " << coordinates.lat() << ")"; - return dbg.maybeSpace(); + return dbg.space(); } QPair Coordinates::boundingRect(qreal distance) const diff --git a/src/coordinates.h b/src/coordinates.h index 83a26322..9437bc28 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -11,6 +11,7 @@ public: Coordinates() {_lon = NAN; _lat = NAN;} Coordinates(const Coordinates &c) {_lon = c._lon; _lat = c._lat;} Coordinates(qreal lon, qreal lat) {_lon = lon; _lat = lat;} + Coordinates(const QPointF &p) {_lon = p.x(), _lat = p.y();} qreal &rlon() {return _lon;} qreal &rlat() {return _lat;} @@ -35,7 +36,10 @@ private: qreal _lat, _lon; }; -bool operator==(const Coordinates &c1, const Coordinates &c2); +inline bool operator==(const Coordinates &c1, const Coordinates &c2) + {return (c1.lat() == c2.lat() && c1.lon() == c2.lon());} +inline bool operator!=(const Coordinates &c1, const Coordinates &c2) + {return !(c1 == c2);} QDebug operator<<(QDebug dbg, const Coordinates &trackpoint); #endif // COORDINATES_H diff --git a/src/data.cpp b/src/data.cpp index 404ce171..469dda45 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -82,9 +82,9 @@ bool Data::loadFile(const QString &fileName) file.reset(); } - fprintf(stderr, "Error loading data file: %s:\n", qPrintable(fileName)); + qWarning("Error loading data file: %s:\n", qPrintable(fileName)); for (it = _parsers.begin(); it != _parsers.end(); it++) - fprintf(stderr, "%s: line %d: %s\n", qPrintable(it.key()), + qWarning("%s: line %d: %s\n", qPrintable(it.key()), it.value()->errorLine(), qPrintable(it.value()->errorString())); _errorLine = 0; diff --git a/src/downloader.cpp b/src/downloader.cpp index 34ae5d07..c6f91f0a 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -61,7 +61,7 @@ bool Downloader::saveToDisk(const QString &filename, QIODevice *data) QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { - fprintf(stderr, "Error writing map tile: %s: %s\n", + qWarning("Error writing map tile: %s: %s\n", qPrintable(filename), qPrintable(file.errorString())); return false; } @@ -80,11 +80,11 @@ void Downloader::downloadFinished(QNetworkReply *reply) QUrl origin = reply->request().attribute(ATTR_ORIGIN).toUrl(); if (origin.isEmpty()) { _errorDownloads.insert(url); - fprintf(stderr, "Error downloading map tile: %s: %s\n", + qWarning("Error downloading map tile: %s: %s\n", url.toEncoded().constData(), qPrintable(reply->errorString())); } else { _errorDownloads.insert(origin); - fprintf(stderr, "Error downloading map tile: %s -> %s: %s\n", + qWarning("Error downloading map tile: %s -> %s: %s\n", origin.toEncoded().constData(), url.toEncoded().constData(), qPrintable(reply->errorString())); } @@ -99,11 +99,11 @@ void Downloader::downloadFinished(QNetworkReply *reply) if (location == url) { _errorDownloads.insert(url); - fprintf(stderr, "Error downloading map tile: %s: " + qWarning("Error downloading map tile: %s: " "redirect loop\n", url.toEncoded().constData()); } else if (level >= MAX_REDIRECT_LEVEL) { _errorDownloads.insert(origin); - fprintf(stderr, "Error downloading map tile: %s: " + qWarning("Error downloading map tile: %s: " "redirect level limit reached\n", origin.toEncoded().constData()); } else { diff --git a/src/emptymap.cpp b/src/emptymap.cpp new file mode 100644 index 00000000..011c0c76 --- /dev/null +++ b/src/emptymap.cpp @@ -0,0 +1,76 @@ +#include +#include +#include "misc.h" +#include "rd.h" +#include "wgs84.h" +#include "coordinates.h" +#include "emptymap.h" + + +#define SCALE_MIN 0.5 +#define SCALE_MAX 1.0E-6 + +EmptyMap::EmptyMap(QObject *parent) : Map(parent) +{ + _scale = SCALE_MAX; +} + +QRectF EmptyMap::bounds() const +{ + return scaled(QRectF(QPointF(-180, -180), QSizeF(360, 360)), 1.0/_scale); +} + +qreal EmptyMap::zoomFit(const QSize &size, const QRectF &br) +{ + if (br.isNull()) + _scale = SCALE_MAX; + else { + Coordinates topLeft(br.topLeft()); + Coordinates bottomRight(br.bottomRight()); + QRectF tbr(topLeft.toMercator(), bottomRight.toMercator()); + + QPointF sc(tbr.width() / size.width(), tbr.height() / size.height()); + + _scale = qMax(sc.x(), sc.y()); + } + + _scale = qMax(_scale, SCALE_MAX); + _scale = qMin(_scale, SCALE_MIN); + + return _scale; +} + +qreal EmptyMap::resolution(const QPointF &p) const +{ + return (WGS84_RADIUS * 2 * M_PI * _scale / 360.0 + * cos(2.0 * atan(exp(deg2rad(-p.y() * _scale))) - M_PI/2)); +} + +qreal EmptyMap::zoomIn() +{ + _scale = qMax(_scale / 2.0, SCALE_MAX); + return _scale; +} + +qreal EmptyMap::zoomOut() +{ + _scale = qMin(_scale * 2.0, SCALE_MIN); + return _scale; +} + +void EmptyMap::draw(QPainter *painter, const QRectF &rect) +{ + painter->fillRect(rect, Qt::white); +} + +QPointF EmptyMap::ll2xy(const Coordinates &c) const +{ + QPointF m = c.toMercator(); + return QPointF(m.x() / _scale, m.y() / -_scale); +} + +Coordinates EmptyMap::xy2ll(const QPointF &p) const +{ + QPointF m(p.x() * _scale, -p.y() * _scale); + return Coordinates::fromMercator(m); +} diff --git a/src/emptymap.h b/src/emptymap.h new file mode 100644 index 00000000..2efd10c7 --- /dev/null +++ b/src/emptymap.h @@ -0,0 +1,33 @@ +#ifndef EMPTYMAP_H +#define EMPTYMAP_H + +#include "map.h" + +class EmptyMap : public Map +{ + Q_OBJECT + +public: + EmptyMap(QObject *parent = 0); + + const QString &name() const {return _name;} + + QRectF bounds() const; + qreal resolution(const QPointF &p) const; + + qreal zoom() const {return _scale;} + 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); + +private: + QString _name; + qreal _scale; +}; + +#endif // EMPTYMAP_H diff --git a/src/fitparser.cpp b/src/fitparser.cpp index 97c39acf..439aad6c 100644 --- a/src/fitparser.cpp +++ b/src/fitparser.cpp @@ -36,8 +36,7 @@ void FITParser::clearDefinitions() void FITParser::warning(const char *text) const { const QFile *file = static_cast(_device); - fprintf(stderr, "%s:%d: %s\n", qPrintable(file->fileName()), - _len, text); + qWarning("%s:%d: %s\n", qPrintable(file->fileName()), _len, text); } bool FITParser::readData(char *data, size_t size) diff --git a/src/gui.cpp b/src/gui.cpp index f5bc8595..ebc02610 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -28,6 +28,8 @@ #include "data.h" #include "map.h" #include "maplist.h" +#include "mapdir.h" +#include "emptymap.h" #include "elevationgraph.h" #include "speedgraph.h" #include "heartrategraph.h" @@ -36,7 +38,6 @@ #include "powergraph.h" #include "pathview.h" #include "trackinfo.h" -#include "downloader.h" #include "filebrowser.h" #include "cpuarch.h" #include "graphtab.h" @@ -44,7 +45,7 @@ #include "gui.h" -GUI::GUI(QWidget *parent) : QMainWindow(parent) +GUI::GUI() { loadMaps(); loadPOIs(); @@ -118,12 +119,21 @@ void GUI::createBrowser() void GUI::loadMaps() { - MapList ml(new Downloader(this)); + QList online, offline; if (QFile::exists(USER_MAP_FILE)) - _maps = ml.load(USER_MAP_FILE, this); + online = MapList::load(USER_MAP_FILE, this); else - _maps = ml.load(GLOBAL_MAP_FILE, this); + online = MapList::load(GLOBAL_MAP_FILE, this); + + if (QFile::exists(USER_MAP_DIR)) + offline = MapDir::load(USER_MAP_DIR, this); + else + offline = MapDir::load(GLOBAL_MAP_DIR, this); + + _maps = online + offline; + + _map = _maps.isEmpty() ? new EmptyMap(this) : _maps.first(); } void GUI::loadPOIs() @@ -141,11 +151,11 @@ void GUI::loadPOIs() for (int i = 0; i < list.size(); ++i) { if (!_poi->loadFile(list.at(i).absoluteFilePath())) { - fprintf(stderr, "Error loading POI file: %s: %s\n", + qWarning("Error loading POI file: %s: %s\n", qPrintable(list.at(i).fileName()), qPrintable(_poi->errorString())); if (_poi->errorLine()) - fprintf(stderr, "Line: %d\n", _poi->errorLine()); + qWarning("Line: %d\n", _poi->errorLine()); } } } @@ -281,7 +291,8 @@ void GUI::createActions() this); _showMapAction->setCheckable(true); _showMapAction->setShortcut(SHOW_MAP_SHORTCUT); - connect(_showMapAction, SIGNAL(triggered(bool)), this, SLOT(showMap(bool))); + connect(_showMapAction, SIGNAL(triggered(bool)), _pathView, + SLOT(showMap(bool))); addAction(_showMapAction); _clearMapCacheAction = new QAction(tr("Clear tile cache"), this); connect(_clearMapCacheAction, SIGNAL(triggered()), this, @@ -508,15 +519,13 @@ void GUI::createToolBars() void GUI::createPathView() { - _pathView = new PathView(this); + _pathView = new PathView(_map, _poi, this); _pathView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding)); _pathView->setMinimumHeight(200); #ifdef Q_OS_WIN32 _pathView->setFrameShape(QFrame::NoFrame); #endif // Q_OS_WIN32 - - _pathView->setPOI(_poi); } void GUI::createGraphTabs() @@ -697,7 +706,7 @@ bool GUI::loadFile(const QString &fileName) if (data.tracks().count() == 1 && !data.routes().count()) _pathName = data.tracks().first()->name(); else if (data.routes().count() == 1 && !data.tracks().count()) - _pathName = data.routes().first()->routeData().name(); + _pathName = data.routes().first()->name(); } else _pathName = QString(); @@ -987,14 +996,6 @@ void GUI::closeAll() updatePathView(); } -void GUI::showMap(bool show) -{ - if (show) - _pathView->setMap(_currentMap); - else - _pathView->setMap(0); -} - void GUI::showGraphs(bool show) { _graphTabWidget->setHidden(!show); @@ -1072,7 +1073,7 @@ void GUI::showGraphGrids(bool show) void GUI::clearMapCache() { - _currentMap->clearCache(); + _map->clearCache(); _pathView->redraw(); } @@ -1097,7 +1098,8 @@ void GUI::updateStatusBarInfo() _timeLabel->setToolTip(Format::timeSpan(time())); } else { _timeLabel->setText(Format::timeSpan(time())); - _timeLabel->setToolTip(Format::timeSpan(movingTime())); + _timeLabel->setToolTip(Format::timeSpan(movingTime()) + + "M"); } } else { _timeLabel->clear(); @@ -1115,17 +1117,16 @@ void GUI::updateWindowTitle() void GUI::mapChanged(int index) { - _currentMap = _maps.at(index); - - if (_showMapAction->isChecked()) - _pathView->setMap(_currentMap); + _map = _maps.at(index); + _pathView->setMap(_map); } void GUI::nextMap() { if (_maps.count() < 2) return; - int next = (_maps.indexOf(_currentMap) + 1) % _maps.count(); + + int next = (_maps.indexOf(_map) + 1) % _maps.count(); _mapActions.at(next)->setChecked(true); mapChanged(next); } @@ -1134,7 +1135,8 @@ void GUI::prevMap() { if (_maps.count() < 2) return; - int prev = (_maps.indexOf(_currentMap) + _maps.count() - 1) % _maps.count(); + + int prev = (_maps.indexOf(_map) + _maps.count() - 1) % _maps.count(); _mapActions.at(prev)->setChecked(true); mapChanged(prev); } @@ -1368,8 +1370,7 @@ void GUI::writeSettings() settings.endGroup(); settings.beginGroup(MAP_SETTINGS_GROUP); - if (_currentMap) - settings.setValue(CURRENT_MAP_SETTING, _currentMap->name()); + settings.setValue(CURRENT_MAP_SETTING, _map->name()); if (_showMapAction->isChecked() != SHOW_MAP_DEFAULT) settings.setValue(SHOW_MAP_SETTING, _showMapAction->isChecked()); settings.endGroup(); @@ -1515,11 +1516,9 @@ void GUI::readSettings() if (_maps.count()) { int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString()); _mapActions.at(index)->setChecked(true); - _currentMap = _maps.at(index); - if (_showMapAction->isChecked()) - _pathView->setMap(_currentMap); - } else - _currentMap = 0; + _map = _maps.at(index); + _pathView->setMap(_map); + } settings.endGroup(); settings.beginGroup(GRAPH_SETTINGS_GROUP); diff --git a/src/gui.h b/src/gui.h index 8e43eac8..efe8ed05 100644 --- a/src/gui.h +++ b/src/gui.h @@ -31,7 +31,7 @@ class GUI : public QMainWindow Q_OBJECT public: - GUI(QWidget *parent = 0); + GUI(); ~GUI(); bool openFile(const QString &fileName); @@ -47,7 +47,6 @@ private slots: void reloadFile(); void openPOIFile(); void closePOIFiles(); - void showMap(bool show); void showGraphs(bool show); void showGraphGrids(bool show); void showToolbars(bool show); @@ -189,8 +188,8 @@ private: FileBrowser *_browser; QList _files; - Map *_currentMap; + Map *_map; int _trackCount; int _routeCount; int _waypointCount; diff --git a/src/map.cpp b/src/map.cpp deleted file mode 100644 index 0bd18db1..00000000 --- a/src/map.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include -#include "downloader.h" -#include "config.h" -#include "map.h" - - -Map::Map(const QString &name, const QString &url, Downloader *downloader, - QObject *parent) : QObject(parent) -{ - _name = name; - _url = url; - - _downloader = downloader; - - - connect(_downloader, SIGNAL(finished()), this, SLOT(emitLoaded())); - - QString path = TILES_DIR + QString("/") + _name; - if (!QDir().mkpath(path)) - fprintf(stderr, "Error creating tiles dir: %s\n", qPrintable(path)); -} - -void Map::emitLoaded() -{ - emit loaded(); -} - -void Map::loadTiles(QList &list, bool block) -{ - if (block) - loadTilesSync(list); - else - loadTilesAsync(list); -} - -void Map::loadTilesAsync(QList &list) -{ - QList dl; - - for (int i = 0; i < list.size(); i++) { - Tile &t = list[i]; - QString file = tileFile(t); - QFileInfo fi(file); - - if (!fi.exists()) { - fillTile(t); - dl.append(Download(tileUrl(t), file)); - } else - loadTileFile(t, file); - } - - if (!dl.empty()) - _downloader->get(dl); -} - -void Map::loadTilesSync(QList &list) -{ - QList dl; - - for (int i = 0; i < list.size(); i++) { - Tile &t = list[i]; - QString file = tileFile(t); - QFileInfo fi(file); - - if (!fi.exists()) - dl.append(Download(tileUrl(t), file)); - else - loadTileFile(t, file); - } - - if (dl.empty()) - return; - - QEventLoop wait; - connect(_downloader, SIGNAL(finished()), &wait, SLOT(quit())); - if (_downloader->get(dl)) - wait.exec(); - - for (int i = 0; i < list.size(); i++) { - Tile &t = list[i]; - - if (t.pixmap().isNull()) { - QString file = tileFile(t); - QFileInfo fi(file); - - if (!(fi.exists() && loadTileFile(t, file))) - fillTile(t); - } - } -} - -void Map::fillTile(Tile &tile) -{ - tile.pixmap() = QPixmap(Tile::size(), Tile::size()); - tile.pixmap().fill(); -} - -bool Map::loadTileFile(Tile &tile, const QString &file) -{ - if (!tile.pixmap().load(file)) { - fprintf(stderr, "%s: error loading tile file\n", qPrintable(file)); - return false; - } - - return true; -} - -QString Map::tileUrl(const Tile &tile) -{ - QString url(_url); - - url.replace("$z", QString::number(tile.zoom())); - url.replace("$x", QString::number(tile.xy().x())); - url.replace("$y", QString::number(tile.xy().y())); - - return url; -} - -QString Map::tileFile(const Tile &tile) -{ - QString file = TILES_DIR + QString("/%1/%2-%3-%4").arg(_name) - .arg(tile.zoom()).arg(tile.xy().x()).arg(tile.xy().y()); - - return file; -} - -void Map::clearCache() -{ - QString path = TILES_DIR + QString("/") + _name; - QDir dir = QDir(path); - QStringList list = dir.entryList(); - - for (int i = 0; i < list.count(); i++) - dir.remove(list.at(i)); -} diff --git a/src/map.h b/src/map.h index e565a72c..77b6f37e 100644 --- a/src/map.h +++ b/src/map.h @@ -1,41 +1,42 @@ #ifndef MAP_H #define MAP_H -#include "tile.h" +#include +#include +#include -class Downloader; +class QPainter; +class Coordinates; class Map : public QObject { Q_OBJECT public: - Map(const QString &name, const QString &url, Downloader *downloader, - QObject *parent = 0); + Map(QObject *parent = 0) : QObject(parent) {} - const QString &name() const {return _name;} - void loadTiles(QList &list, bool block); - void clearCache(); + virtual const QString &name() const = 0; + + virtual QRectF bounds() const = 0; + virtual qreal resolution(const QPointF &p) const = 0; + + virtual qreal zoom() const = 0; + virtual qreal zoomFit(const QSize &size, const QRectF &br) = 0; + virtual qreal zoomIn() = 0; + virtual qreal zoomOut() = 0; + + virtual QPointF ll2xy(const Coordinates &c) const = 0; + virtual Coordinates xy2ll(const QPointF &p) const = 0; + + virtual void draw(QPainter *painter, const QRectF &rect) = 0; + + virtual void setBlockingMode(bool block) {Q_UNUSED(block);} + virtual void clearCache() {} + virtual void load() {} + virtual void unload() {} signals: void loaded(); - -private slots: - void emitLoaded(); - -private: - QString tileUrl(const Tile &tile); - QString tileFile(const Tile &tile); - bool loadTileFile(Tile &tile, const QString &file); - void fillTile(Tile &tile); - - void loadTilesAsync(QList &list); - void loadTilesSync(QList &list); - - Downloader *_downloader; - - QString _name; - QString _url; }; #endif // MAP_H diff --git a/src/mapdir.cpp b/src/mapdir.cpp new file mode 100644 index 00000000..8b30cbe1 --- /dev/null +++ b/src/mapdir.cpp @@ -0,0 +1,32 @@ +#include +#include "mapdir.h" + +#include +#include "ozimap.h" + +QList MapDir::load(const QString &path, QObject *parent) +{ + QList 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); + OziMap *map = new OziMap(fileInfo.absoluteFilePath(), parent); + if (map->isValid()) + maps.append(map); + else + delete map; + } + + return maps; +} diff --git a/src/mapdir.h b/src/mapdir.h new file mode 100644 index 00000000..c36516e3 --- /dev/null +++ b/src/mapdir.h @@ -0,0 +1,16 @@ +#ifndef MAPDIR_H +#define MAPDIR_H + +#include +#include + +class QObject; +class Map; + +class MapDir +{ +public: + static QList load(const QString &path, QObject *parent = 0); +}; + +#endif // MAPDIR_H diff --git a/src/maplist.cpp b/src/maplist.cpp index f58d4d0f..a2e4091b 100644 --- a/src/maplist.cpp +++ b/src/maplist.cpp @@ -1,23 +1,23 @@ #include #include -#include "map.h" +#include "onlinemap.h" #include "maplist.h" QList MapList::load(const QString &fileName, QObject *parent) { - QList mapList; + QList maps; QFileInfo fi(fileName); if (!fi.exists()) - return mapList; + return maps; QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { - fprintf(stderr, "Error opening map list file: %s: %s\n", + qWarning("Error opening map list file: %s: %s\n", qPrintable(fileName), qPrintable(file.errorString())); - return mapList; + return maps; } int ln = 0; @@ -26,16 +26,16 @@ QList MapList::load(const QString &fileName, QObject *parent) QByteArray line = file.readLine(); QList list = line.split('\t'); if (list.size() != 2) { - fprintf(stderr, "Invalid map list entry on line %d\n", ln); + qWarning("Invalid map list entry on line %d\n", ln); continue; } QByteArray ba1 = list[0].trimmed(); QByteArray ba2 = list[1].trimmed(); - mapList.append(new Map(QString::fromUtf8(ba1.data(), ba1.size()), - QString::fromLatin1(ba2.data(), ba2.size()), _downloader, parent)); + maps.append(new OnlineMap(QString::fromUtf8(ba1.data(), ba1.size()), + QString::fromLatin1(ba2.data(), ba2.size()), parent)); } - return mapList; + return maps; } diff --git a/src/maplist.h b/src/maplist.h index b11edff7..e89df6cd 100644 --- a/src/maplist.h +++ b/src/maplist.h @@ -3,19 +3,14 @@ #include #include -#include +class QObject; class Map; -class Downloader; class MapList { public: - MapList(Downloader *downloader) : _downloader(downloader) {} - QList load(const QString &fileName, QObject *parent = 0); - -private: - Downloader *_downloader; + static QList load(const QString &fileName, QObject *parent = 0); }; #endif // MAPLIST_H diff --git a/src/matrix.cpp b/src/matrix.cpp new file mode 100644 index 00000000..d0e70d32 --- /dev/null +++ b/src/matrix.cpp @@ -0,0 +1,117 @@ +#include "matrix.h" + + +#define abs(x) ((x)<0 ? -(x) : (x)) + +Matrix::~Matrix() +{ + if (isNull()) + return; + + delete[] _m; +} + +Matrix::Matrix(size_t h, size_t w) +{ + _h = h; _w = w; + + if (isNull()) + _m = 0; + else + _m = new double[_h * _w]; +} + +Matrix::Matrix(const Matrix& M) +{ + _h = M._h; _w = M._w; + + if (isNull()) + _m = 0; + else + _m = new double[_h * _w]; + + for (size_t i = 0; i < _h; i++) + for (size_t j = 0; j < _w; j++) + m(i,j) = M.m(i,j); +} + +Matrix &Matrix::operator=(const Matrix &M) +{ + if (_h != M._h || _w != M._w) { + if (!isNull()) + delete[] _m; + + _h = M._h; _w = M._w; + if (isNull()) + _m = 0; + else + _m = new double[_h * _w]; + } + + for (size_t i = 0; i < _h; i++) + for (size_t j = 0; j < _w; j++) + m(i,j) = M.m(i,j); + + return *this; +} + +bool Matrix::eliminate(double epsilon) +{ + size_t i, j, k, maxrow; + double temp; + + + for (i = 0; i < _h; i++) { + maxrow = i; + for (j = i+1; j < _h; j++) + if (abs(m(j, i)) > abs(m(maxrow, i))) + maxrow = j; + for (j = 0; j < _w; j++) { + temp = m(i, j); + m(i, j) = m(maxrow, j); + m(maxrow, j) = temp; + } + if (abs(m(i, i)) <= epsilon) + return false; + for (j = i+1; j<_h; j++) { + temp = m(j, i) / m(i, i); + for (k = i; k < _w; k++) + m(j, k) -= m(i, k) * temp; + } + } + for (i = _h-1; i < i+1; i--) { + temp = m(i, i); + for (j = 0; j < i; j++) + for (k = _w-1; k >= i; k--) + m(j, k) -= m(i, k) * m(j, i) / temp; + m(i, i) /= temp; + for (j = _h; j < _w; j++) + m(i, j) /= temp; + } + + return true; +} + +Matrix Matrix::augemented(const Matrix &M) const +{ + if (_h != M._h) + return Matrix(); + + Matrix A(_h, _w + M._w); + + for (size_t i = 0; i < _h; i++) + for (size_t j = 0; j < _w; j++) + A.m(i, j) = m(i, j); + + for (size_t i = 0; i < _h; i++) + for (size_t j = _w; j < A._w; j++) + A.m(i, j) = M.m(i, j-_w); + + return A; +} + +void Matrix::zeroize() +{ + for (size_t i = 0; i < _h * _w; i++) + _m[i] = 0; +} diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 00000000..ee7c511c --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,33 @@ +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include + +class Matrix { +public: + Matrix() {_h = 0; _w = 0; _m = 0;} + Matrix(size_t h, size_t w); + Matrix(const Matrix& M); + ~Matrix(); + + Matrix &operator=(const Matrix &M); + + size_t h() const {return _h;} + size_t w() const {return _w;} + double &m(size_t i, size_t j) {return _m[_w * i + j];} + double const &m(size_t i, size_t j) const {return _m[_w * i + j];} + + bool isNull() const {return (_h == 0 || _w == 0);} + + void zeroize(); + bool eliminate(double epsilon = DBL_EPSILON); + Matrix augemented(const Matrix &M) const; + +private: + double *_m; + size_t _h; + size_t _w; +}; + +#endif // MATRIX_H diff --git a/src/misc.cpp b/src/misc.cpp index 4f5d90ef..4135c7bb 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -48,3 +48,9 @@ int str2int(const char *str, int len) return res; } + +QRectF scaled(const QRectF &rect, qreal factor) +{ + return QRectF(QPointF(rect.left() * factor, rect.top() * factor), + QSizeF(rect.width() * factor, rect.height() * factor)); +} diff --git a/src/misc.h b/src/misc.h index 67a86139..c4c16ac9 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,7 +1,10 @@ #ifndef MISC_H #define MISC_H +#include + double niceNum(double x, int round); int str2int(const char *str, int len); +QRectF scaled(const QRectF &rect, qreal factor); #endif // MISC_H diff --git a/src/onlinemap.cpp b/src/onlinemap.cpp new file mode 100644 index 00000000..5b6c114d --- /dev/null +++ b/src/onlinemap.cpp @@ -0,0 +1,255 @@ +#include +#include +#include +#include "downloader.h" +#include "config.h" +#include "rd.h" +#include "wgs84.h" +#include "misc.h" +#include "coordinates.h" +#include "onlinemap.h" + + +#define ZOOM_MAX 18 +#define ZOOM_MIN 3 + +static QPoint mercator2tile(const QPointF &m, int z) +{ + QPoint tile; + + tile.setX((int)(floor((m.x() + 180.0) / 360.0 * (1< ZOOM_MAX) + return ZOOM_MAX; + + return zoom; +} + + +Downloader *OnlineMap::downloader; + +OnlineMap::OnlineMap(const QString &name, const QString &url, QObject *parent) + : Map(parent) +{ + _name = name; + _url = url; + downloader = downloader; + _block = false; + _scale = ((360.0/(qreal)(1< &list) +{ + QList dl; + + for (int i = 0; i < list.size(); i++) { + Tile &t = list[i]; + QString file = tileFile(t); + QFileInfo fi(file); + + if (!fi.exists()) { + fillTile(t); + dl.append(Download(tileUrl(t), file)); + } else + loadTileFile(t, file); + } + + if (!dl.empty()) + downloader->get(dl); +} + +void OnlineMap::loadTilesSync(QList &list) +{ + QList dl; + + for (int i = 0; i < list.size(); i++) { + Tile &t = list[i]; + QString file = tileFile(t); + QFileInfo fi(file); + + if (!fi.exists()) + dl.append(Download(tileUrl(t), file)); + else + loadTileFile(t, file); + } + + if (dl.empty()) + return; + + QEventLoop wait; + connect(downloader, SIGNAL(finished()), &wait, SLOT(quit())); + if (downloader->get(dl)) + wait.exec(); + + for (int i = 0; i < list.size(); i++) { + Tile &t = list[i]; + + if (t.pixmap().isNull()) { + QString file = tileFile(t); + QFileInfo fi(file); + + if (!(fi.exists() && loadTileFile(t, file))) + fillTile(t); + } + } +} + +void OnlineMap::fillTile(Tile &tile) +{ + tile.pixmap() = QPixmap(Tile::size(), Tile::size()); + tile.pixmap().fill(); +} + +bool OnlineMap::loadTileFile(Tile &tile, const QString &file) +{ + if (!tile.pixmap().load(file)) { + qWarning("%s: error loading tile file\n", qPrintable(file)); + return false; + } + + return true; +} + +QString OnlineMap::tileUrl(const Tile &tile) +{ + QString url(_url); + + url.replace("$z", QString::number(tile.zoom())); + url.replace("$x", QString::number(tile.xy().x())); + url.replace("$y", QString::number(tile.xy().y())); + + return url; +} + +QString OnlineMap::tileFile(const Tile &tile) +{ + QString file = TILES_DIR + QString("/%1/%2-%3-%4").arg(name()) + .arg(tile.zoom()).arg(tile.xy().x()).arg(tile.xy().y()); + + return file; +} + +void OnlineMap::clearCache() +{ + QString path = TILES_DIR + QString("/") + name(); + QDir dir = QDir(path); + QStringList list = dir.entryList(); + + for (int i = 0; i < list.count(); i++) + dir.remove(list.at(i)); +} + +QRectF OnlineMap::bounds() const +{ + return scaled(QRectF(QPointF(-180, -180), QSizeF(360, 360)), 1.0/_scale); +} + +qreal OnlineMap::zoomFit(const QSize &size, const QRectF &br) +{ + if (br.isNull()) + _scale = ((360.0/(qreal)(1< tiles; + QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y()); + for (int i = 0; i < ceil(s.width() / Tile::size()); i++) + for (int j = 0; j < ceil(s.height() / Tile::size()); j++) + tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), zoom)); + + if (_block) + loadTilesSync(tiles); + else + loadTilesAsync(tiles); + + for (int i = 0; i < tiles.count(); i++) { + Tile &t = tiles[i]; + QPoint tp(tl.x() + (t.xy().x() - tile.x()) * Tile::size(), + tl.y() + (t.xy().y() - tile.y()) * Tile::size()); + painter->drawPixmap(tp, t.pixmap()); + } +} + +QPointF OnlineMap::ll2xy(const Coordinates &c) const +{ + QPointF m = c.toMercator(); + return QPointF(m.x() / _scale, m.y() / -_scale); +} + +Coordinates OnlineMap::xy2ll(const QPointF &p) const +{ + QPointF m(p.x() * _scale, -p.y() * _scale); + return Coordinates::fromMercator(m); +} diff --git a/src/onlinemap.h b/src/onlinemap.h new file mode 100644 index 00000000..22b40553 --- /dev/null +++ b/src/onlinemap.h @@ -0,0 +1,56 @@ +#ifndef ONLINEMAP_H +#define ONLINEMAP_H + +#include "map.h" +#include "tile.h" + +class Downloader; + +class OnlineMap : public Map +{ + Q_OBJECT + +public: + OnlineMap(const QString &name, const QString &url, QObject *parent = 0); + + const QString &name() const {return _name;} + + QRectF bounds() const; + qreal resolution(const QPointF &p) const; + + qreal zoom() const {return _scale;} + 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); + + void setBlockingMode(bool block) {_block = block;} + void clearCache(); + + static void setDownloader(Downloader *downloader) + {OnlineMap::downloader = downloader;} + +private slots: + void emitLoaded(); + +private: + QString tileUrl(const Tile &tile); + QString tileFile(const Tile &tile); + bool loadTileFile(Tile &tile, const QString &file); + void fillTile(Tile &tile); + void loadTilesAsync(QList &list); + void loadTilesSync(QList &list); + + qreal _scale; + QString _name; + QString _url; + bool _block; + + static Downloader *downloader; +}; + +#endif // ONLINEMAP_H diff --git a/src/ozimap.cpp b/src/ozimap.cpp new file mode 100644 index 00000000..fa149f2b --- /dev/null +++ b/src/ozimap.cpp @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include "misc.h" +#include "rd.h" +#include "wgs84.h" +#include "coordinates.h" +#include "matrix.h" +#include "ozimap.h" + + +int OziMap::parseMapFile(QIODevice &device, QList &points) +{ + bool res; + int ln = 1; + + + if (!device.open(QIODevice::ReadOnly)) + return -1; + + while (!device.atEnd()) { + QByteArray line = device.readLine(); + + if (ln == 1) { + if (line.trimmed() != "OziExplorer Map Data File Version 2.2") + return ln; + } else if (ln == 2) + _name = line.trimmed(); + else if (ln == 3) + _imgPath = line.trimmed(); + else if (ln >= 10 && ln < 40) { + QList list = line.split(','); + if (list.count() == 17 && !list.at(2).trimmed().isEmpty()) { + int x = list.at(2).trimmed().toInt(&res); + if (!res) + return ln; + int y = list.at(3).trimmed().toInt(&res); + if (!res) + return ln; + int latd = list.at(6).trimmed().toInt(&res); + if (!res) + return ln; + qreal latm = list.at(7).trimmed().toFloat(&res); + if (!res) + return ln; + int lond = list.at(9).trimmed().toInt(&res); + if (!res) + return ln; + qreal lonm = list.at(10).trimmed().toFloat(&res); + if (!res) + return ln; + + if (list.at(8).trimmed() == "S") + latd = -latd; + if (list.at(11).trimmed() == "W") + lond = -lond; + points.append(QPair(QPoint(x, y), + Coordinates(lond + lonm/60.0, latd + latm/60.0))); + } + } else { + QList list = line.split(','); + if (list.at(0).trimmed() == "IWH") { + int w = list.at(2).trimmed().toInt(&res); + if (!res) + return ln; + int h = list.at(3).trimmed().toInt(&res); + if (!res) + return ln; + _size = QSize(w, h); + } + } + + ln++; + } + + return 0; +} + +bool OziMap::computeTransformation(const QList &points) +{ + if (points.count() < 2) + return false; + + Matrix c(3, 2); + c.zeroize(); + for (size_t j = 0; j < c.w(); j++) { + for (size_t k = 0; k < c.h(); k++) { + for (int i = 0; i < points.size(); i++) { + double f[3], t[2]; + + f[0] = points.at(i).second.lon(); + f[1] = points.at(i).second.lat(); + f[2] = 1.0; + t[0] = points.at(i).first.x(); + t[1] = points.at(i).first.y(); + c.m(k,j) += f[k] * t[j]; + } + } + } + + Matrix Q(3, 3); + Q.zeroize(); + for (int qi = 0; qi < points.size(); qi++) { + double v[3]; + + v[0] = points.at(qi).second.lon(); + v[1] = points.at(qi).second.lat(); + v[2] = 1.0; + for (size_t i = 0; i < Q.h(); i++) + for (size_t j = 0; j < Q.w(); j++) + Q.m(i,j) += v[i] * v[j]; + } + + Matrix M = Q.augemented(c); + if (!M.eliminate()) + return false; + + _transform = QTransform(M.m(0,3), M.m(1,3), M.m(0,4), M.m(1,4), + M.m(2,3), M.m(2,4)); + + return true; +} + +bool OziMap::computeResolution(QList &points) +{ + if (points.count() < 2) + return false; + + int maxLon = 0, minLon = 0, maxLat = 0, minLat = 0; + qreal dLon, pLon, dLat, pLat; + + for (int i = 1; i < points.size(); i++) { + if (points.at(i).second.lon() < points.at(minLon).second.lon()) + minLon = i; + if (points.at(i).second.lon() > points.at(maxLon).second.lon()) + maxLon = i; + if (points.at(i).second.lat() < points.at(minLat).second.lon()) + minLat = i; + if (points.at(i).second.lat() > points.at(maxLat).second.lon()) + maxLat = i; + } + + dLon = points.at(minLon).second.distanceTo(points.at(maxLon).second); + pLon = QLineF(points.at(minLon).first, points.at(maxLon).first).length(); + dLat = points.at(minLat).second.distanceTo(points.at(maxLat).second); + pLat = QLineF(points.at(minLat).first, points.at(maxLat).first).length(); + + _resolution = (dLon/pLon + dLat/pLat) / 2.0; + + return true; +} + +OziMap::OziMap(const QString &path, QObject *parent) : Map(parent) +{ + int errorLine; + QList points; + + + _valid = false; + + QFileInfo fi(path); + _name = fi.baseName(); + + QDir dir(path); + QFileInfoList list = dir.entryInfoList(QStringList("*.map"), QDir::Files); + if (!list.count()) { + qWarning("%s: map file not found", qPrintable(_name)); + return; + } + + QFile mapFile(list[0].absoluteFilePath()); + if ((errorLine = parseMapFile(mapFile, points))) { + if (errorLine < 0) + qWarning("%s: error opening map file", qPrintable(_name)); + else + qWarning("%s: map file parse error on line: %d", qPrintable(_name), + errorLine); + return; + } + + if (!computeTransformation(points)) { + qWarning("%s: error computing map transformation", qPrintable(_name)); + return; + } + computeResolution(points); + + QFileInfo ii(_imgPath); + if (ii.isRelative()) + _imgPath = fi.absoluteFilePath() + "/" + _imgPath; + ii = QFileInfo(_imgPath); + if (!ii.exists()) { + qWarning("%s: %s: no such image", qPrintable(_name), + qPrintable(ii.absoluteFilePath())); + return; + } + if (_size.isNull()) { + qWarning("%s: missing or invalid image size (IWH)", qPrintable(_name)); + return; + } + + _img = 0; + _valid = true; +} + +void OziMap::load() +{ + Q_ASSERT(_img == 0); + + _img = new QImage(_imgPath); + if (_img->isNull()) + qWarning("%s: error loading map image", qPrintable(_name)); +} + +void OziMap::unload() +{ + Q_ASSERT(_img != 0); + + delete _img; + _img = 0; +} + +QRectF OziMap::bounds() const +{ + return QRectF(QPointF(0, 0), _size); +} + +qreal OziMap::zoomFit(const QSize &size, const QRectF &br) +{ + Q_UNUSED(size); + Q_UNUSED(br); + + return 1.0; +} + +qreal OziMap::resolution(const QPointF &p) const +{ + Q_UNUSED(p); + + return _resolution; +} + +qreal OziMap::zoomIn() +{ + return 1.0; +} + +qreal OziMap::zoomOut() +{ + return 1.0; +} + +void OziMap::draw(QPainter *painter, const QRectF &rect) +{ + Q_ASSERT(_img != 0); + + QPoint p = rect.topLeft().toPoint(); + QImage crop = _img->copy(QRect(p, rect.size().toSize())); + painter->drawImage(rect.topLeft(), crop); +} + +QPointF OziMap::ll2xy(const Coordinates &c) const +{ + return _transform.map(QPointF(c.lon(), c.lat())); +} + +Coordinates OziMap::xy2ll(const QPointF &p) const +{ + return _transform.inverted().map(p); +} diff --git a/src/ozimap.h b/src/ozimap.h new file mode 100644 index 00000000..a1327c0c --- /dev/null +++ b/src/ozimap.h @@ -0,0 +1,55 @@ +#ifndef OZIMAP_H +#define OZIMAP_H + +#include +#include +#include "map.h" +#include "coordinates.h" + +class QIODevice; + +class OziMap : public Map +{ + Q_OBJECT + +public: + OziMap(const QString &path, QObject *parent = 0); + + const QString &name() const {return _name;} + + QRectF bounds() const; + qreal resolution(const QPointF &p) const; + + qreal zoom() const {return 1.0;} + 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); + + void load(); + void unload(); + + bool isValid() {return _valid;} + +private: + typedef QPair ReferencePoint; + + int parseMapFile(QIODevice &device, QList &points); + bool computeTransformation(const QList &points); + bool computeResolution(QList &points); + + QString _name; + QString _imgPath; + QSize _size; + QTransform _transform; + qreal _resolution; + + bool _valid; + QImage *_img; +}; + +#endif // OZIMAP_H diff --git a/src/path.cpp b/src/path.cpp index d71d01a1..8fb14ad7 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -1,5 +1,29 @@ #include "path.h" +QRectF Path::boundingRect() const +{ + if (size() < 2) + return QRectF(); + + QPointF topLeft(at(0).coordinates().lon(), at(0).coordinates().lat()); + QPointF bottomRight(topLeft); + + for (int i = 1; i < size(); i++) { + qreal x = at(i).coordinates().lon(); + qreal y = at(i).coordinates().lat(); + + if (x < topLeft.x()) + topLeft.setX(x); + if (y < topLeft.y()) + topLeft.setY(y); + if (x > bottomRight.x()) + bottomRight.setX(x); + if (y > bottomRight.y()) + bottomRight.setY(y); + } + return QRectF(topLeft, bottomRight); +} + QDebug operator<<(QDebug dbg, const PathPoint &point) { dbg.nospace() << "PathPoint(" << point.distance() << ", " diff --git a/src/path.h b/src/path.h index 9b75b2f0..b0cff53b 100644 --- a/src/path.h +++ b/src/path.h @@ -2,6 +2,7 @@ #define PATH_H #include +#include #include "coordinates.h" class PathPoint @@ -23,6 +24,11 @@ private: Q_DECLARE_TYPEINFO(PathPoint, Q_PRIMITIVE_TYPE); QDebug operator<<(QDebug dbg, const PathPoint &point); -typedef QVector Path; + +class Path : public QVector +{ +public: + QRectF boundingRect() const; +}; #endif // PATH_H diff --git a/src/pathitem.cpp b/src/pathitem.cpp index 8360f193..930b48e3 100644 --- a/src/pathitem.cpp +++ b/src/pathitem.cpp @@ -3,29 +3,47 @@ #include #include #include "tooltip.h" +#include "map.h" +#include "misc.h" #include "pathitem.h" +PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent) + : QGraphicsObject(parent) +{ + Q_ASSERT(path.count() >= 2); + _path = path; + _map = map; + + updatePainterPath(map); + updateShape(); + + _width = 3; + QBrush brush(Qt::SolidPattern); + _pen = QPen(brush, _width); + + _marker = new MarkerItem(this); + _marker->setPos(position(_path.at(0).distance())); + _md = _path.at(0).distance(); + + setCursor(Qt::ArrowCursor); + setAcceptHoverEvents(true); +} + void PathItem::updateShape() { QPainterPathStroker s; s.setWidth((_width + 1) * 1.0/scale()); - _shape = s.createStroke(_path); + _shape = s.createStroke(_painterPath); } -PathItem::PathItem(QGraphicsItem *parent) : QGraphicsObject(parent) +void PathItem::updatePainterPath(Map *map) { - _width = 3; + _painterPath = QPainterPath(); - QBrush brush(Qt::SolidPattern); - _pen = QPen(brush, _width); - - _units = Metric; - - _marker = new MarkerItem(this); - - setCursor(Qt::ArrowCursor); - setAcceptHoverEvents(true); + _painterPath.moveTo(map->ll2xy(_path.first().coordinates())); + for (int i = 1; i < _path.size(); i++) + _painterPath.lineTo(map->ll2xy(_path.at(i).coordinates())); } void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, @@ -35,7 +53,7 @@ void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q_UNUSED(widget); painter->setPen(_pen); - painter->drawPath(_path); + painter->drawPath(_painterPath); /* QPen p = QPen(QBrush(Qt::red), 0); @@ -44,15 +62,15 @@ void PathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, */ } -void PathItem::setScale(qreal scale) +void PathItem::setMap(Map *map) { + _map = map; prepareGeometryChange(); - _pen.setWidthF(_width * 1.0/scale); - QGraphicsItem::setScale(scale); - _marker->setScale(1.0/scale); - + updatePainterPath(map); updateShape(); + + _marker->setPos(position(_md)); } void PathItem::setColor(const QColor &color) @@ -77,41 +95,36 @@ void PathItem::setStyle(Qt::PenStyle style) update(); } -void PathItem::setUnits(enum Units units) -{ - _units = units; -} - QPointF PathItem::position(qreal x) const { int low = 0; - int high = _distance.count() - 1; + int high = _path.count() - 1; int mid = 0; - - Q_ASSERT(_distance.count() == _path.elementCount()); Q_ASSERT(high > low); - Q_ASSERT(x >= _distance.at(low) && x <= _distance.at(high)); + Q_ASSERT(x >= _path.at(low).distance() && x <= _path.at(high).distance()); while (low <= high) { mid = low + ((high - low) / 2); - qreal val = _distance.at(mid); + qreal val = _path.at(mid).distance(); if (val > x) high = mid - 1; else if (val < x) low = mid + 1; else - return _path.elementAt(mid); + return _map->ll2xy(_path.at(mid).coordinates()); } QLineF l; qreal p1, p2; - if (_distance.at(mid) < x) { - l = QLineF(_path.elementAt(mid), _path.elementAt(mid+1)); - p1 = _distance.at(mid); p2 = _distance.at(mid+1); + if (_path.at(mid).distance() < x) { + l = QLineF(_map->ll2xy(_path.at(mid).coordinates()), + _map->ll2xy(_path.at(mid+1).coordinates())); + p1 = _path.at(mid).distance(); p2 = _path.at(mid+1).distance(); } else { - l = QLineF(_path.elementAt(mid-1), _path.elementAt(mid)); - p1 = _distance.at(mid-1); p2 = _distance.at(mid); + l = QLineF(_map->ll2xy(_path.at(mid-1).coordinates()), + _map->ll2xy(_path.at(mid).coordinates())); + p1 = _path.at(mid-1).distance(); p2 = _path.at(mid).distance(); } return l.pointAt((x - p1) / (p2 - p1)); @@ -119,9 +132,11 @@ QPointF PathItem::position(qreal x) const void PathItem::moveMarker(qreal distance) { - if (distance >= _distance.first() && distance <= _distance.last()) { + if (distance >= _path.first().distance() + && distance <= _path.last().distance()) { _marker->setVisible(true); _marker->setPos(position(distance)); + _md = distance; } else _marker->setVisible(false); } diff --git a/src/pathitem.h b/src/pathitem.h index 83e7a3e6..17be16d0 100644 --- a/src/pathitem.h +++ b/src/pathitem.h @@ -4,29 +4,33 @@ #include #include #include "markeritem.h" -#include "units.h" +#include "path.h" +class Map; + class PathItem : public QGraphicsObject { Q_OBJECT public: - PathItem(QGraphicsItem *parent = 0); + PathItem(const Path &path, Map *map, QGraphicsItem *parent = 0); QPainterPath shape() const {return _shape;} QRectF boundingRect() const {return _shape.boundingRect();} void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); - const QPainterPath &path() const {return _path;} - void showMarker(bool show) {_marker->setVisible(show);} - void setScale(qreal scale); - void setUnits(enum Units units); + const Path &path() const {return _path;} + + void setMap(Map *map); + void setColor(const QColor &color); void setWidth(int width); void setStyle(Qt::PenStyle style); + void showMarker(bool show) {_marker->setVisible(show);} + public slots: void moveMarker(qreal distance); @@ -34,22 +38,24 @@ signals: void selected(bool); protected: - void updateShape(); - - QVector _distance; - QPainterPath _path; + Path _path; MarkerItem *_marker; - Units _units; private: QPointF position(qreal distance) const; + void updatePainterPath(Map *map); + void updateShape(); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + Map *_map; + qreal _md; + int _width; QPen _pen; QPainterPath _shape; + QPainterPath _painterPath; }; #endif // PATHITEM_H diff --git a/src/pathview.cpp b/src/pathview.cpp index 29aa198f..36b3c3bb 100644 --- a/src/pathview.cpp +++ b/src/pathview.cpp @@ -1,12 +1,13 @@ #include #include #include +#include #include "opengl.h" -#include "rd.h" -#include "wgs84.h" +#include "misc.h" #include "poi.h" #include "data.h" #include "map.h" +#include "emptymap.h" #include "trackitem.h" #include "routeitem.h" #include "waypointitem.h" @@ -14,59 +15,9 @@ #include "pathview.h" -#define ZOOM_MAX 18 -#define ZOOM_MIN 3 #define MARGIN 10.0 #define SCALE_OFFSET 7 -static QPoint mercator2tile(const QPointF &m, int z) -{ - QPoint tile; - - tile.setX((int)(floor((m.x() + 180.0) / 360.0 * (1< ZOOM_MAX) - return ZOOM_MAX; - - return zoom; -} - -qreal mapScale(int zoom) -{ - return ((360.0/(qreal)(1<setZValue(2.0); - _zoom = ZOOM_MAX; - _res = 1.0; - _map = 0; - _poi = 0; + _map = map; + _poi = poi; + connect(_map, SIGNAL(loaded()), this, SLOT(redraw())); + connect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI())); _units = Metric; + _showMap = true; _showTracks = true; _showRoutes = true; _showWaypoints = true; @@ -124,7 +72,8 @@ PathView::PathView(QWidget *parent) _plot = false; - _scene->setSceneRect(scaled(mapBounds(), 1.0 / mapScale(_zoom))); + setSceneRect(_map->bounds()); + _res = _map->resolution(_scene->sceneRect().center()); } PathView::~PathView() @@ -140,19 +89,16 @@ PathItem *PathView::addTrack(const Track &track) return 0; } - TrackItem *ti = new TrackItem(track); + TrackItem *ti = new TrackItem(track, _map); _tracks.append(ti); _tr |= ti->path().boundingRect(); - _zoom = scale2zoom(contentsScale()); - ti->setScale(1.0/mapScale(_zoom)); ti->setColor(_palette.nextColor()); ti->setWidth(_trackWidth); ti->setStyle(_trackStyle); ti->setVisible(_showTracks); _scene->addItem(ti); - if (_poi) - addPOI(_poi->points(ti)); + addPOI(_poi->points(ti->path())); return ti; } @@ -164,11 +110,9 @@ PathItem *PathView::addRoute(const Route &route) return 0; } - RouteItem *ri = new RouteItem(route); + RouteItem *ri = new RouteItem(route, _map); _routes.append(ri); _rr |= ri->path().boundingRect(); - _zoom = scale2zoom(contentsScale()); - ri->setScale(1.0/mapScale(_zoom)); ri->setColor(_palette.nextColor()); ri->setWidth(_routeWidth); ri->setStyle(_routeStyle); @@ -177,61 +121,52 @@ PathItem *PathView::addRoute(const Route &route) ri->showWaypointLabels(_showWaypointLabels); _scene->addItem(ri); - if (_poi) - addPOI(_poi->points(ri)); + addPOI(_poi->points(ri->path())); return ri; } void PathView::addWaypoints(const QList &waypoints) { - qreal scale = mapScale(_zoom); - for (int i = 0; i < waypoints.count(); i++) { const Waypoint &w = waypoints.at(i); - WaypointItem *wi = new WaypointItem(w); + WaypointItem *wi = new WaypointItem(w, _map); _waypoints.append(wi); - updateWaypointsBoundingRect(wi->coordinates()); - wi->setScale(1.0/scale); + Coordinates c = wi->waypoint().coordinates(); + updateWaypointsBoundingRect(QPointF(c.lon(), c.lat())); wi->setZValue(1); wi->showLabel(_showWaypointLabels); wi->setVisible(_showWaypoints); _scene->addItem(wi); } - if (_poi) - addPOI(_poi->points(waypoints)); - - _zoom = scale2zoom(contentsScale()); + addPOI(_poi->points(waypoints)); } QList PathView::loadData(const Data &data) { QList paths; - int zoom = _zoom; - + qreal scale = _map->zoom(); for (int i = 0; i < data.tracks().count(); i++) paths.append(addTrack(*(data.tracks().at(i)))); - for (int i = 0; i < data.routes().count(); i++) paths.append(addRoute(*(data.routes().at(i)))); - addWaypoints(data.waypoints()); if (_tracks.empty() && _routes.empty() && _waypoints.empty()) return paths; - if (_zoom < zoom) - rescale(_zoom); + if (mapScale() != scale) + rescale(); else updatePOIVisibility(); - QPointF center = contentsCenter(); + QPointF center = contentCenter(); centerOn(center); - _res = zoom2resolution(_zoom, -(center.y() * mapScale(_zoom))); + _res = _map->resolution(center); _mapScale->setResolution(_res); if (_mapScale->scene() != _scene) _scene->addItem(_mapScale); @@ -252,33 +187,25 @@ void PathView::updateWaypointsBoundingRect(const QPointF &wp) unite(_wr, wp); } -qreal PathView::contentsScale() const +qreal PathView::mapScale() const { QRectF br = _tr | _rr | _wr; if (!br.isNull() && !_wp.isNull()) unite(br, _wp); - if (br.isNull()) - return mapScale(ZOOM_MAX); - - QPointF sc(br.width() / (viewport()->width() - MARGIN/2), - br.height() / (viewport()->height() - MARGIN/2)); - - return qMax(sc.x(), sc.y()); + return _map->zoomFit(viewport()->size() - QSize(MARGIN/2, MARGIN/2), br); } -QPointF PathView::contentsCenter() const +QPointF PathView::contentCenter() const { QRectF br = _tr | _rr | _wr; if (!br.isNull() && !_wp.isNull()) unite(br, _wp); - qreal scale = mapScale(_zoom); - if (br.isNull()) - return _wp / scale; + return _map->ll2xy(_wp); else - return scaled(br, 1.0/scale).center(); + return _map->ll2xy(br.center()); } void PathView::updatePOIVisibility() @@ -302,25 +229,20 @@ void PathView::updatePOIVisibility() } } -void PathView::rescale(int zoom) +void PathView::rescale() { - _zoom = zoom; - qreal scale = mapScale(zoom); - - _scene->setSceneRect(scaled(mapBounds(), 1.0 / scale)); + setSceneRect(_map->bounds()); for (int i = 0; i < _tracks.size(); i++) - _tracks.at(i)->setScale(1.0/scale); - + _tracks.at(i)->setMap(_map); for (int i = 0; i < _routes.size(); i++) - _routes.at(i)->setScale(1.0/scale); - + _routes.at(i)->setMap(_map); for (int i = 0; i < _waypoints.size(); i++) - _waypoints.at(i)->setScale(1.0/scale); + _waypoints.at(i)->setMap(_map); QHash::const_iterator it; for (it = _pois.constBegin(); it != _pois.constEnd(); it++) - it.value()->setScale(1.0/scale); + it.value()->setMap(_map); updatePOIVisibility(); } @@ -336,16 +258,41 @@ void PathView::setPalette(const Palette &palette) _routes.at(i)->setColor(_palette.nextColor()); } +void PathView::setMap(Map *map) +{ + _map->unload(); + disconnect(_map, SIGNAL(loaded()), this, SLOT(redraw())); + + _map = map; + _map->load(); + connect(_map, SIGNAL(loaded()), this, SLOT(redraw())); + + mapScale(); + setSceneRect(_map->bounds()); + + for (int i = 0; i < _tracks.size(); i++) + _tracks.at(i)->setMap(map); + for (int i = 0; i < _routes.size(); i++) + _routes.at(i)->setMap(map); + for (int i = 0; i < _waypoints.size(); i++) + _waypoints.at(i)->setMap(map); + + QPointF center = contentCenter(); + centerOn(center); + + _res = _map->resolution(center); + _mapScale->setResolution(_res); + + resetCachedContent(); +} + void PathView::setPOI(POI *poi) { - if (_poi) - disconnect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI())); + disconnect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI())); + connect(poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI())); _poi = poi; - if (_poi) - connect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI())); - updatePOI(); } @@ -359,13 +306,10 @@ void PathView::updatePOI() } _pois.clear(); - if (!_poi) - return; - for (int i = 0; i < _tracks.size(); i++) - addPOI(_poi->points(_tracks.at(i))); + addPOI(_poi->points(_tracks.at(i)->path())); for (int i = 0; i < _routes.size(); i++) - addPOI(_poi->points(_routes.at(i))); + addPOI(_poi->points(_routes.at(i)->path())); addPOI(_poi->points(_waypoints)); updatePOIVisibility(); @@ -373,16 +317,13 @@ void PathView::updatePOI() void PathView::addPOI(const QVector &waypoints) { - qreal scale = mapScale(_zoom); - for (int i = 0; i < waypoints.size(); i++) { const Waypoint &w = waypoints.at(i); if (_pois.contains(w)) continue; - WaypointItem *pi = new WaypointItem(w); - pi->setScale(1.0/scale); + WaypointItem *pi = new WaypointItem(w, _map); pi->setZValue(1); pi->showLabel(_showPOILabels); pi->setVisible(_showPOI); @@ -392,19 +333,6 @@ void PathView::addPOI(const QVector &waypoints) } } -void PathView::setMap(Map *map) -{ - if (_map) - disconnect(_map, SIGNAL(loaded()), this, SLOT(redraw())); - - _map = map; - - if (_map) - connect(_map, SIGNAL(loaded()), this, SLOT(redraw())); - - resetCachedContent(); -} - void PathView::setUnits(enum Units units) { _units = units; @@ -428,23 +356,16 @@ void PathView::redraw() resetCachedContent(); } -void PathView::zoom(int z, const QPoint &pos) +void PathView::zoom(const QPoint &pos, const Coordinates &c) { - if (_tracks.isEmpty() && _routes.isEmpty() && _waypoints.isEmpty()) - return; - QPoint offset = pos - viewport()->rect().center(); - QPointF spos = mapToScene(pos); - qreal os = mapScale(_zoom); - _zoom = z; + rescale(); - rescale(_zoom); - - QPointF center = (spos * (os/mapScale(_zoom))) - offset; + QPointF center = _map->ll2xy(c) - offset; centerOn(center); - _res = zoom2resolution(_zoom, -(center.y() * mapScale(_zoom))); + _res = _map->resolution(center); _mapScale->setResolution(_res); resetCachedContent(); @@ -452,36 +373,50 @@ void PathView::zoom(int z, const QPoint &pos) void PathView::wheelEvent(QWheelEvent *event) { - int z = (event->delta() > 0) ? - qMin(_zoom + 1, ZOOM_MAX) : qMax(_zoom - 1, ZOOM_MIN); + qreal os, ns; - zoom(z, event->pos()); + os = _map->zoom(); + Coordinates c = _map->xy2ll(mapToScene(event->pos())); + + ns = (event->delta() > 0) ? _map->zoomIn() : _map->zoomOut(); + if (ns != os) + zoom(event->pos(), c); } void PathView::mouseDoubleClickEvent(QMouseEvent *event) { + qreal os, ns; + if (event->button() != Qt::LeftButton && event->button() != Qt::RightButton) return; - int z = (event->button() == Qt::LeftButton) ? - qMin(_zoom + 1, ZOOM_MAX) : qMax(_zoom - 1, ZOOM_MIN); + os = _map->zoom(); + Coordinates c = _map->xy2ll(mapToScene(event->pos())); - zoom(z, event->pos()); + ns = (event->button() == Qt::LeftButton) ? _map->zoomIn() : _map->zoomOut(); + if (ns != os) + zoom(event->pos(), c); } void PathView::keyPressEvent(QKeyEvent *event) { - int z = -1; + qreal os, ns; + + os = _map->zoom(); + QPoint pos = QRect(QPoint(), viewport()->size()).center(); + Coordinates c = _map->xy2ll(mapToScene(pos)); if (event->matches(QKeySequence::ZoomIn)) - z = qMin(_zoom + 1, ZOOM_MAX); - if (event->matches(QKeySequence::ZoomOut)) - z = qMax(_zoom - 1, ZOOM_MIN); - - if (z >= 0) - zoom(z, QRect(QPoint(), size()).center()); - else + ns = _map->zoomIn(); + else if (event->matches(QKeySequence::ZoomOut)) + ns = _map->zoomOut(); + else { QWidget::keyPressEvent(event); + return; + } + + if (ns != os) + zoom(pos, c); } void PathView::plot(QPainter *painter, const QRectF &target) @@ -504,6 +439,7 @@ void PathView::plot(QPainter *painter, const QRectF &target) setUpdatesEnabled(false); _plot = true; + _map->setBlockingMode(true); QPointF pos = _mapScale->pos(); _mapScale->setPos(mapToScene(QPoint(adj.bottomRight() + QPoint( @@ -514,6 +450,7 @@ void PathView::plot(QPainter *painter, const QRectF &target) _mapScale->setPos(pos); + _map->setBlockingMode(false); _plot = false; setUpdatesEnabled(true); } @@ -530,12 +467,11 @@ void PathView::clear() _scene->clear(); _palette.reset(); - _zoom = ZOOM_MAX; - _res = 1.0; _tr = QRectF(); _rr = QRectF(); _wr = QRectF(); _wp = QPointF(); - _scene->setSceneRect(scaled(mapBounds(), 1.0 / mapScale(_zoom))); + setSceneRect(_map->bounds()); + _res = _map->resolution(_scene->sceneRect().center()); } void PathView::showTracks(bool show) @@ -581,6 +517,12 @@ void PathView::showRouteWaypoints(bool show) _routes.at(i)->showWaypoints(show); } +void PathView::showMap(bool show) +{ + _showMap = show; + resetCachedContent(); +} + void PathView::showPOI(bool show) { _showPOI = show; @@ -644,51 +586,24 @@ void PathView::setRouteStyle(Qt::PenStyle style) void PathView::drawBackground(QPainter *painter, const QRectF &rect) { - if ((_tracks.isEmpty() && _routes.isEmpty() && _waypoints.isEmpty()) - || !_map) { + if (_showMap) + _map->draw(painter, rect); + else painter->fillRect(rect, Qt::white); - return; - } - - qreal scale = mapScale(_zoom); - QRectF rr(rect.topLeft() * scale, rect.size()); - QPoint tile = mercator2tile(QPointF(rr.topLeft().x(), -rr.topLeft().y()), - _zoom); - QPointF tm = tile2mercator(tile, _zoom); - QPoint tl = QPoint((int)(tm.x() / scale), (int)(-tm.y() / scale)); - - - QList tiles; - QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y()); - for (int i = 0; i < ceil(s.width() / Tile::size()); i++) - for (int j = 0; j < ceil(s.height() / Tile::size()); j++) - tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom)); - - _map->loadTiles(tiles, _plot); - - for (int i = 0; i < tiles.count(); i++) { - Tile &t = tiles[i]; - QPoint tp(tl.x() + (t.xy().x() - tile.x()) * Tile::size(), - tl.y() + (t.xy().y() - tile.y()) * Tile::size()); - painter->drawPixmap(tp, t.pixmap()); - } } void PathView::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); - if (_tracks.isEmpty() && _routes.isEmpty() && _waypoints.isEmpty()) - return; + qreal scale = _map->zoom(); + if (mapScale() != scale) + rescale(); - int zoom = scale2zoom(contentsScale()); - if (zoom != _zoom) - rescale(zoom); - - QPointF center = contentsCenter(); + QPointF center = contentCenter(); centerOn(center); - _res = zoom2resolution(_zoom, -(center.y() * mapScale(_zoom))); + _res = _map->resolution(center); _mapScale->setResolution(_res); resetCachedContent(); @@ -710,7 +625,7 @@ void PathView::scrollContentsBy(int dx, int dy) QGraphicsView::scrollContentsBy(dx, dy); QPointF center = mapToScene(viewport()->rect().center()); - qreal res = zoom2resolution(_zoom, -(center.y() * mapScale(_zoom))); + qreal res = _map->resolution(center); if (qMax(res, _res) / qMin(res, _res) > 1.1) { _mapScale->setResolution(res); diff --git a/src/pathview.h b/src/pathview.h index b50e8e90..999741a9 100644 --- a/src/pathview.h +++ b/src/pathview.h @@ -25,7 +25,7 @@ class PathView : public QGraphicsView Q_OBJECT public: - PathView(QWidget *parent = 0); + PathView(Map *map, POI *poi, QWidget *parent = 0); ~PathView(); QList loadData(const Data &data); @@ -48,6 +48,7 @@ public: public slots: void redraw(); + void showMap(bool show); void showPOI(bool show); void setPOIOverlap(bool overlap); void showWaypointLabels(bool show); @@ -72,10 +73,10 @@ private: void loadPOI(); void clearPOI(); - qreal contentsScale() const; - QPointF contentsCenter() const; - void rescale(int zoom); - void zoom(int z, const QPoint &pos); + qreal mapScale() const; + QPointF contentCenter() const; + void rescale(); + void zoom(const QPoint &pos, const Coordinates &c); void updatePOIVisibility(); void updateWaypointsBoundingRect(const QPointF &wp); @@ -94,7 +95,6 @@ private: QList _waypoints; QHash _pois; - int _zoom; QRectF _tr, _rr, _wr; QPointF _wp; qreal _res; @@ -104,6 +104,7 @@ private: Palette _palette; Units _units; + bool _showMap; bool _showTracks; bool _showRoutes; bool _showWaypoints; diff --git a/src/poi.cpp b/src/poi.cpp index 557c019c..7ea9ff41 100644 --- a/src/poi.cpp +++ b/src/poi.cpp @@ -58,18 +58,15 @@ static bool cb(size_t data, void* context) return true; } -QVector POI::points(const PathItem *path) const +QVector POI::points(const Path &path) const { QVector ret; QSet set; qreal min[2], max[2]; - const QPainterPath &pp = path->path(); - for (int i = 0; i < pp.elementCount(); i++) { - const QPainterPath::Element &pe = pp.elementAt(i); - Coordinates p = Coordinates::fromMercator(QPointF(pe.x, -pe.y)); - - QPair br = p.boundingRect(_radius); + for (int i = 0; i < path.count(); i++) { + const Coordinates &c = path.at(i).coordinates(); + QPair br = c.boundingRect(_radius); min[0] = br.first.lon(); min[1] = br.first.lat(); max[0] = br.second.lon(); diff --git a/src/poi.h b/src/poi.h index a1a1c93a..211ff4a2 100644 --- a/src/poi.h +++ b/src/poi.h @@ -7,8 +7,8 @@ #include #include "waypoint.h" #include "rtree.h" +#include "path.h" -class PathItem; class WaypointItem; class POI : public QObject @@ -25,7 +25,7 @@ public: unsigned radius() const {return _radius;} void setRadius(unsigned radius); - QVector points(const PathItem *path) const; + QVector points(const Path &path) const; QVector points(const QList &list) const; QVector points(const QList &list) const; diff --git a/src/route.cpp b/src/route.cpp index cd052cb3..40d072f6 100644 --- a/src/route.cpp +++ b/src/route.cpp @@ -11,6 +11,16 @@ Route::Route(const RouteData &data) : _data(data) } } +Path Route::path() const +{ + Path ret; + + for (int i = 0; i < _data.size(); i++) + ret.append(PathPoint(_data.at(i).coordinates(), _distance.at(i))); + + return ret; +} + Graph Route::elevation() const { Graph graph; diff --git a/src/route.h b/src/route.h index c3b77037..e413fdb2 100644 --- a/src/route.h +++ b/src/route.h @@ -11,13 +11,17 @@ class Route public: Route(const RouteData &data); - const RouteData &routeData() const {return _data;} - const QVector &distanceData() const {return _distance;} + Path path() const; + + const QVector &waypoints() const {return _data;} Graph elevation() const; qreal distance() const; + const QString &name() const {return _data.name();} + const QString &description() const {return _data.description();} + bool isNull() const {return (_data.count() < 2);} private: diff --git a/src/routeitem.cpp b/src/routeitem.cpp index 93851126..46022663 100644 --- a/src/routeitem.cpp +++ b/src/routeitem.cpp @@ -3,10 +3,11 @@ #include "waypoint.h" #include "waypointitem.h" #include "tooltip.h" +#include "map.h" #include "routeitem.h" -QString RouteItem::toolTip() +QString RouteItem::toolTip(Units units) { ToolTip tt; @@ -14,58 +15,41 @@ QString RouteItem::toolTip() tt.insert(tr("Name"), _name); if (!_desc.isEmpty()) tt.insert(tr("Description"), _desc); - tt.insert(tr("Distance"), Format::distance(_distance.last(), _units)); + tt.insert(tr("Distance"), Format::distance(_path.last().distance(), units)); return tt.toString(); } -RouteItem::RouteItem(const Route &route, QGraphicsItem *parent) - : PathItem(parent) +RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent) + : PathItem(route.path(), map, parent) { - const RouteData &r = route.routeData(); - const QVector &d = route.distanceData(); - QPointF p; + const QVector &waypoints = route.waypoints(); + for (int i = 0; i < waypoints.size(); i++) + new WaypointItem(waypoints.at(i), map, this); - Q_ASSERT(r.count() >= 2); - Q_ASSERT(r.size() == d.size()); + _name = route.name(); + _desc = route.description(); - _name = r.name(); - _desc = r.description(); - - new WaypointItem(r.first(), this); - p = r.first().coordinates().toMercator(); - _path.moveTo(QPointF(p.x(), -p.y())); - _distance.append(d.first()); - for (int i = 1; i < r.size(); i++) { - if (r.at(i).coordinates() == r.at(i-1).coordinates()) - continue; - p = r.at(i).coordinates().toMercator(); - _path.lineTo(QPointF(p.x(), -p.y())); - _distance.append(d.at(i)); - new WaypointItem(r.at(i), this); - } - - updateShape(); - - _marker->setPos(_path.elementAt(0)); - - setToolTip(toolTip()); + setToolTip(toolTip(Metric)); } -void RouteItem::setScale(qreal scale) +void RouteItem::setMap(Map *map) { QList childs = childItems(); - for (int i = 0; i < childs.count(); i++) - childs.at(i)->setScale(1.0/scale); + for (int i = 0; i < childs.count(); i++) { + if (childs.at(i) != _marker) { + WaypointItem *wi = static_cast(childs.at(i)); + wi->setMap(map); + } + } - PathItem::setScale(scale); + PathItem::setMap(map); } void RouteItem::setUnits(enum Units units) { - PathItem::setUnits(units); - setToolTip(toolTip()); + setToolTip(toolTip(units)); } void RouteItem::showWaypoints(bool show) diff --git a/src/routeitem.h b/src/routeitem.h index 4ecab949..9b5cf2fd 100644 --- a/src/routeitem.h +++ b/src/routeitem.h @@ -3,23 +3,26 @@ #include "pathitem.h" #include "route.h" +#include "units.h" +class Map; class RouteItem : public PathItem { Q_OBJECT public: - RouteItem(const Route &route, QGraphicsItem *parent = 0); + RouteItem(const Route &route, Map *map, QGraphicsItem *parent = 0); - void setUnits(enum Units units); - void setScale(qreal scale); + //void setScale(qreal scale); + void setMap(Map *map); + void setUnits(Units units); void showWaypoints(bool show); void showWaypointLabels(bool show); private: - QString toolTip(); + QString toolTip(Units units); QString _name; QString _desc; diff --git a/src/tcxparser.cpp b/src/tcxparser.cpp index 9b36724e..b68297ac 100644 --- a/src/tcxparser.cpp +++ b/src/tcxparser.cpp @@ -4,7 +4,7 @@ void TCXParser::warning(const char *text) const { const QFile *file = static_cast(_reader.device()); - fprintf(stderr, "%s:%lld: %s\n", qPrintable(file->fileName()), + qWarning("%s:%lld: %s\n", qPrintable(file->fileName()), _reader.lineNumber(), text); } diff --git a/src/track.h b/src/track.h index 6f07973d..72103e72 100644 --- a/src/track.h +++ b/src/track.h @@ -15,6 +15,7 @@ public: Track(const TrackData &data); Path path() const; + Graph elevation() const; Graph speed() const; Graph heartRate() const; diff --git a/src/trackitem.cpp b/src/trackitem.cpp index a84cf8b5..19c22bc2 100644 --- a/src/trackitem.cpp +++ b/src/trackitem.cpp @@ -1,10 +1,11 @@ #include #include "format.h" #include "tooltip.h" +#include "map.h" #include "trackitem.h" -QString TrackItem::toolTip() +QString TrackItem::toolTip(Units units) { ToolTip tt; @@ -12,7 +13,7 @@ QString TrackItem::toolTip() tt.insert(tr("Name"), _name); if (!_desc.isEmpty()) tt.insert(tr("Description"), _desc); - tt.insert(tr("Distance"), Format::distance(_distance.last(), _units)); + tt.insert(tr("Distance"), Format::distance(_path.last().distance(), units)); if (_time > 0) tt.insert(tr("Total time"), Format::timeSpan(_time)); if (_movingTime > 0) @@ -23,39 +24,19 @@ QString TrackItem::toolTip() return tt.toString(); } -TrackItem::TrackItem(const Track &track, QGraphicsItem *parent) - : PathItem(parent) +TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent) + : PathItem(track.path(), map, parent) { - QPointF p; - const Path &path = track.path(); - Q_ASSERT(path.count() >= 2); - - p = path.first().coordinates().toMercator(); - _path.moveTo(QPointF(p.x(), -p.y())); - _distance.append(path.first().distance()); - for (int i = 1; i < path.size(); i++) { - if (path.at(i).coordinates() == path.at(i-1).coordinates()) - continue; - p = path.at(i).coordinates().toMercator(); - _path.lineTo(QPointF(p.x(), -p.y())); - _distance.append(path.at(i).distance()); - } - - updateShape(); - _name = track.name(); _desc = track.description(); _date = track.date(); _time = track.time(); _movingTime = track.movingTime(); - _marker->setPos(_path.elementAt(0)); - - setToolTip(toolTip()); + setToolTip(toolTip(Metric)); } -void TrackItem::setUnits(enum Units units) +void TrackItem::setUnits(Units units) { - PathItem::setUnits(units); - setToolTip(toolTip()); + setToolTip(toolTip(units)); } diff --git a/src/trackitem.h b/src/trackitem.h index 8756ac07..6774dda5 100644 --- a/src/trackitem.h +++ b/src/trackitem.h @@ -5,19 +5,21 @@ #include #include "track.h" #include "pathitem.h" +#include "units.h" +class Map; class TrackItem : public PathItem { Q_OBJECT public: - TrackItem(const Track &track, QGraphicsItem *parent = 0); + TrackItem(const Track &track, Map *map, QGraphicsItem *parent = 0); - void setUnits(enum Units units); + void setUnits(Units units); private: - QString toolTip(); + QString toolTip(Units units); QString _name; QString _desc; diff --git a/src/waypointitem.cpp b/src/waypointitem.cpp index 9ce3cd2b..c283fb00 100644 --- a/src/waypointitem.cpp +++ b/src/waypointitem.cpp @@ -3,23 +3,24 @@ #include "config.h" #include "format.h" #include "tooltip.h" +#include "map.h" #include "waypointitem.h" #define POINT_SIZE 8 #define HOVER_SIZE 10 -QString WaypointItem::toolTip() +QString WaypointItem::toolTip(Units units) { ToolTip tt; - if (!_waypoint.name().isEmpty() && !_showLabel) + if (!_waypoint.name().isEmpty()) tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name()); tt.insert(qApp->translate("WaypointItem", "Coordinates"), Format::coordinates(_waypoint.coordinates())); if (!std::isnan(_waypoint.elevation())) tt.insert(qApp->translate("WaypointItem", "Elevation"), - Format::elevation(_waypoint.elevation(), _units)); + Format::elevation(_waypoint.elevation(), units)); if (!_waypoint.timestamp().isNull()) tt.insert(qApp->translate("WaypointItem", "Date"), _waypoint.timestamp().toString(Qt::SystemLocaleShortDate)); @@ -30,25 +31,26 @@ QString WaypointItem::toolTip() return tt.toString(); } -WaypointItem::WaypointItem(const Waypoint &waypoint, QGraphicsItem *parent) - : QGraphicsItem(parent) +WaypointItem::WaypointItem(const Waypoint &waypoint, Map *map, + QGraphicsItem *parent) : QGraphicsItem(parent) { - _units = Metric; + _waypoint = waypoint; _showLabel = true; _hover = false; - _waypoint = waypoint; - QPointF p = waypoint.coordinates().toMercator(); - _coordinates = QPointF(p.x(), -p.y()); - updateShape(); - setPos(_coordinates); - setToolTip(toolTip()); + setPos(map->ll2xy(waypoint.coordinates())); + setToolTip(toolTip(Metric)); setCursor(Qt::ArrowCursor); setAcceptHoverEvents(true); } +void WaypointItem::setMap(Map *map) +{ + setPos(map->ll2xy(_waypoint.coordinates())); +} + void WaypointItem::updateShape() { QPainterPath p; @@ -104,15 +106,17 @@ void WaypointItem::paint(QPainter *painter, */ } +/* void WaypointItem::setScale(qreal scale) { - setPos(_coordinates * scale); + QPointF p = _map->ll2xy(_waypoint.coordinates()); + setPos(QPointF(p.x(), -p.y()) * scale); } +*/ void WaypointItem::setUnits(enum Units units) { - _units = units; - setToolTip(toolTip()); + setToolTip(toolTip(units)); } void WaypointItem::showLabel(bool show) @@ -120,7 +124,6 @@ void WaypointItem::showLabel(bool show) prepareGeometryChange(); _showLabel = show; updateShape(); - setToolTip(toolTip()); } void WaypointItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) diff --git a/src/waypointitem.h b/src/waypointitem.h index 021793f1..cd4c0260 100644 --- a/src/waypointitem.h +++ b/src/waypointitem.h @@ -5,16 +5,17 @@ #include "waypoint.h" #include "units.h" +class Map; + class WaypointItem : public QGraphicsItem { public: - WaypointItem(const Waypoint &waypoint, QGraphicsItem *parent = 0); + WaypointItem(const Waypoint &waypoint, Map *map, QGraphicsItem *parent = 0); const Waypoint &waypoint() const {return _waypoint;} - const QPointF &coordinates() const {return _coordinates;} - void setUnits(enum Units units); - void setScale(qreal scale); + void setMap(Map *map); + void setUnits(Units units); void showLabel(bool show); QPainterPath shape() const {return _shape;} @@ -27,12 +28,10 @@ private: void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); void updateShape(); - QString toolTip(); + QString toolTip(Units units); QPainterPath _shape; - QPointF _coordinates; Waypoint _waypoint; - Units _units; bool _hover; bool _showLabel;