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

Added support for downloading DEM tiles

This commit is contained in:
Martin Tůma 2021-08-30 20:31:33 +02:00
parent 94a0158243
commit 2232b011a1
24 changed files with 437 additions and 87 deletions

View File

@ -1,4 +1,4 @@
version: 9.5.{build} version: 9.6.{build}
configuration: configuration:
- Release - Release

View File

@ -3,7 +3,7 @@ unix:!macx {
} else { } else {
TARGET = GPXSee TARGET = GPXSee
} }
VERSION = 9.5 VERSION = 9.6
QT += core \ QT += core \
gui \ gui \
@ -18,10 +18,12 @@ greaterThan(QT_MAJOR_VERSION, 5) {QT += openglwidgets}
CONFIG += object_parallel_to_source CONFIG += object_parallel_to_source
INCLUDEPATH += ./src INCLUDEPATH += ./src
HEADERS += src/common/config.h \ HEADERS += src/common/config.h \
src/GUI/authenticationwidget.h \
src/GUI/axislabelitem.h \ src/GUI/axislabelitem.h \
src/GUI/dirselectwidget.h \ src/GUI/dirselectwidget.h \
src/GUI/flowlayout.h \ src/GUI/flowlayout.h \
src/GUI/graphicsscene.h \ src/GUI/graphicsscene.h \
src/GUI/infolabel.h \
src/GUI/mapaction.h \ src/GUI/mapaction.h \
src/GUI/mapitem.h \ src/GUI/mapitem.h \
src/GUI/marginswidget.h \ src/GUI/marginswidget.h \
@ -194,6 +196,7 @@ HEADERS += src/common/config.h \
src/data/locparser.h \ src/data/locparser.h \
src/data/slfparser.h \ src/data/slfparser.h \
src/data/dem.h \ src/data/dem.h \
src/data/demloader.h \
src/common/polygon.h \ src/common/polygon.h \
src/data/area.h \ src/data/area.h \
src/map/obliquestereographic.h \ src/map/obliquestereographic.h \
@ -231,9 +234,11 @@ HEADERS += src/common/config.h \
src/map/worldfilemap.h src/map/worldfilemap.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/GUI/authenticationwidget.cpp \
src/GUI/axislabelitem.cpp \ src/GUI/axislabelitem.cpp \
src/GUI/dirselectwidget.cpp \ src/GUI/dirselectwidget.cpp \
src/GUI/flowlayout.cpp \ src/GUI/flowlayout.cpp \
src/GUI/infolabel.cpp \
src/GUI/mapitem.cpp \ src/GUI/mapitem.cpp \
src/GUI/marginswidget.cpp \ src/GUI/marginswidget.cpp \
src/GUI/markerinfoitem.cpp \ src/GUI/markerinfoitem.cpp \
@ -377,6 +382,7 @@ SOURCES += src/main.cpp \
src/data/locparser.cpp \ src/data/locparser.cpp \
src/data/slfparser.cpp \ src/data/slfparser.cpp \
src/data/dem.cpp \ src/data/dem.cpp \
src/data/demloader.cpp \
src/map/obliquestereographic.cpp \ src/map/obliquestereographic.cpp \
src/GUI/coordinatesitem.cpp \ src/GUI/coordinatesitem.cpp \
src/map/rmap.cpp \ src/map/rmap.cpp \

View File

@ -43,6 +43,8 @@
<file alias="view-filter@2x.png">icons/GUI/view-filter@2x.png</file> <file alias="view-filter@2x.png">icons/GUI/view-filter@2x.png</file>
<file alias="applications-internet_32.png">icons/GUI/applications-internet_32.png</file> <file alias="applications-internet_32.png">icons/GUI/applications-internet_32.png</file>
<file alias="applications-internet_32@2x.png">icons/GUI/applications-internet_32@2x.png</file> <file alias="applications-internet_32@2x.png">icons/GUI/applications-internet_32@2x.png</file>
<file alias="view-grid.png">icons/GUI/view-grid.png</file>
<file alias="view-grid@2x.png">icons/GUI/view-grid@2x.png</file>
</qresource> </qresource>
<!-- POI icons for default IMG map style --> <!-- POI icons for default IMG map style -->

BIN
icons/GUI/view-grid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
icons/GUI/view-grid@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

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

View File

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

View File

@ -0,0 +1,14 @@
#include <QFormLayout>
#include "authenticationwidget.h"
AuthenticationWidget::AuthenticationWidget(QWidget *parent) : QWidget(parent)
{
_username = new QLineEdit();
_password = new QLineEdit();
QFormLayout *layout = new QFormLayout();
layout->addRow(tr("Username:"), _username);
layout->addRow(tr("Password:"), _password);
setLayout(layout);
}

View File

@ -0,0 +1,24 @@
#ifndef AUTHENTICATIONWIDGET_H
#define AUTHENTICATIONWIDGET_H
#include <QWidget>
#include <QLineEdit>
class AuthenticationWidget : public QWidget
{
Q_OBJECT
public:
AuthenticationWidget(QWidget *parent = 0);
QString username() const {return _username->text();}
QString password() const {return _password->text();}
void setUsername(const QString &username) {_username->setText(username);}
void setPassword(const QString &password) {_password->setText(password);}
private:
QLineEdit *_username, *_password;
};
#endif // AUTHENTICATIONWIDGET_H

View File

@ -29,6 +29,7 @@
#include "common/downloader.h" #include "common/downloader.h"
#include "data/data.h" #include "data/data.h"
#include "data/poi.h" #include "data/poi.h"
#include "data/demloader.h"
#include "map/maplist.h" #include "map/maplist.h"
#include "map/emptymap.h" #include "map/emptymap.h"
#include "map/crs.h" #include "map/crs.h"
@ -62,6 +63,8 @@ GUI::GUI()
TreeNode<POIAction*> poiActions; TreeNode<POIAction*> poiActions;
_poi = new POI(this); _poi = new POI(this);
_dem = new DEMLoader(ProgramPaths::demDir(true), this);
connect(_dem, &DEMLoader::finished, this, &GUI::demLoaded);
createMapView(); createMapView();
createGraphTabs(); createGraphTabs();
@ -402,6 +405,12 @@ void GUI::createActions(TreeNode<MapAction*> &mapActions,
_showMarkerCoordinatesAction->setCheckable(true); _showMarkerCoordinatesAction->setCheckable(true);
_showMarkerCoordinatesAction->setActionGroup(markerInfoGroup); _showMarkerCoordinatesAction->setActionGroup(markerInfoGroup);
// DEM actions
_downloadDEMAction = new QAction(tr("Download DEM data"), this);
_downloadDEMAction->setMenuRole(QAction::NoRole);
_downloadDEMAction->setEnabled(false);
connect(_downloadDEMAction, &QAction::triggered, this, &GUI::downloadDEM);
// Graph actions // Graph actions
_showGraphsAction = new QAction(QIcon(SHOW_GRAPHS_ICON), tr("Show graphs"), _showGraphsAction = new QAction(QIcon(SHOW_GRAPHS_ICON), tr("Show graphs"),
this); this);
@ -585,18 +594,6 @@ void GUI::createMenus(const TreeNode<MapAction*> &mapActions,
graphMenu->addSeparator(); graphMenu->addSeparator();
graphMenu->addAction(_showGraphsAction); graphMenu->addAction(_showGraphsAction);
_poiMenu = menuBar()->addMenu(tr("&POI"));
createPOINodeMenu(poiActions, _poiMenu);
_poisEnd = _poiMenu->addSeparator();
_poiMenu->addAction(_openPOIAction);
_poiMenu->addAction(_selectAllPOIAction);
_poiMenu->addAction(_unselectAllPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOILabelsAction);
_poiMenu->addAction(_overlapPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOIAction);
QMenu *dataMenu = menuBar()->addMenu(tr("&Data")); QMenu *dataMenu = menuBar()->addMenu(tr("&Data"));
dataMenu->addAction(_showWaypointLabelsAction); dataMenu->addAction(_showWaypointLabelsAction);
dataMenu->addAction(_showRouteWaypointsAction); dataMenu->addAction(_showRouteWaypointsAction);
@ -612,6 +609,22 @@ void GUI::createMenus(const TreeNode<MapAction*> &mapActions,
dataMenu->addAction(_showAreasAction); dataMenu->addAction(_showAreasAction);
dataMenu->addAction(_showWaypointsAction); dataMenu->addAction(_showWaypointsAction);
_poiMenu = menuBar()->addMenu(tr("&POI"));
createPOINodeMenu(poiActions, _poiMenu);
_poisEnd = _poiMenu->addSeparator();
_poiMenu->addAction(_openPOIAction);
_poiMenu->addAction(_selectAllPOIAction);
_poiMenu->addAction(_unselectAllPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOILabelsAction);
_poiMenu->addAction(_overlapPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOIAction);
QMenu *demMenu = menuBar()->addMenu(tr("DEM"));
demMenu->addSeparator();
demMenu->addAction(_downloadDEMAction);
QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings")); QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
QMenu *timeMenu = settingsMenu->addMenu(tr("Time")); QMenu *timeMenu = settingsMenu->addMenu(tr("Time"));
timeMenu->addAction(_totalTimeAction); timeMenu->addAction(_totalTimeAction);
@ -1052,6 +1065,16 @@ void GUI::openOptions()
if (options.poiRadius != _options.poiRadius) if (options.poiRadius != _options.poiRadius)
_poi->setRadius(options.poiRadius); _poi->setRadius(options.poiRadius);
if (options.demURL != _options.demURL)
_dem->setUrl(options.demURL);
if (options.demAuthorization != _options.demAuthorization
|| options.demUsername != _options.demUsername
|| options.demPassword != _options.demPassword)
_dem->setAuthorization(options.demAuthorization
? Authorization(options.demUsername, options.demPassword)
: Authorization());
_downloadDEMAction->setEnabled(!options.demURL.isEmpty());
if (options.pixmapCache != _options.pixmapCache) if (options.pixmapCache != _options.pixmapCache)
QPixmapCache::setCacheLimit(options.pixmapCache * 1024); QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
@ -1688,6 +1711,26 @@ void GUI::clearMapCache()
_mapView->clearMapCache(); _mapView->clearMapCache();
} }
void GUI::downloadDEM()
{
_demRect = _mapView->boundingRect();
if (_dem->loadTiles(_demRect))
_downloadDEMAction->setEnabled(false);
else
demLoaded();
}
void GUI::demLoaded()
{
if (!_dem->checkTiles(_demRect))
QMessageBox::warning(this, APP_NAME,
tr("Could not download all required DEM files."));
reloadFiles();
_downloadDEMAction->setEnabled(!_options.demURL.isEmpty());
}
void GUI::updateStatusBarInfo() void GUI::updateStatusBarInfo()
{ {
if (_files.count() == 0) if (_files.count() == 0)
@ -2236,6 +2279,14 @@ void GUI::writeSettings()
settings.setValue(USE_SEGMENTS_SETTING, _options.useSegments); settings.setValue(USE_SEGMENTS_SETTING, _options.useSegments);
if (_options.poiRadius != POI_RADIUS_DEFAULT) if (_options.poiRadius != POI_RADIUS_DEFAULT)
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius); settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
if (_options.demURL != DEM_URL_DEFAULT)
settings.setValue(DEM_URL_SETTING, _options.demURL);
if (_options.demAuthorization != DEM_AUTH_DEFAULT)
settings.setValue(DEM_AUTH_SETTING, _options.demAuthorization);
if (_options.demUsername != DEM_USERNAME_DEFAULT)
settings.setValue(DEM_USERNAME_SETTING, _options.demUsername);
if (_options.demPassword != DEM_PASSWORD_DEFAULT)
settings.setValue(DEM_PASSWORD_SETTING, _options.demPassword);
if (_options.useOpenGL != USE_OPENGL_DEFAULT) if (_options.useOpenGL != USE_OPENGL_DEFAULT)
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL); settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
if (_options.enableHTTP2 != ENABLE_HTTP2_DEFAULT) if (_options.enableHTTP2 != ENABLE_HTTP2_DEFAULT)
@ -2547,6 +2598,13 @@ void GUI::readSettings()
PAUSE_INTERVAL_DEFAULT).toInt(); PAUSE_INTERVAL_DEFAULT).toInt();
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT) _options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
.toInt(); .toInt();
_options.demURL = settings.value(DEM_URL_SETTING, DEM_URL_DEFAULT).toString();
_options.demAuthorization = settings.value(DEM_AUTH_SETTING,
DEM_AUTH_DEFAULT).toBool();
_options.demUsername = settings.value(DEM_USERNAME_SETTING,
DEM_USERNAME_DEFAULT).toString();
_options.demPassword = settings.value(DEM_PASSWORD_SETTING,
DEM_PASSWORD_DEFAULT).toString();
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT) _options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
.toBool(); .toBool();
_options.enableHTTP2 = settings.value(ENABLE_HTTP2_SETTING, _options.enableHTTP2 = settings.value(ENABLE_HTTP2_SETTING,
@ -2641,6 +2699,13 @@ void GUI::readSettings()
_poi->setRadius(_options.poiRadius); _poi->setRadius(_options.poiRadius);
_dem->setUrl(_options.demURL);
if (_options.demAuthorization)
_dem->setAuthorization(Authorization(_options.demUsername,
_options.demPassword));
if (!_options.demURL.isEmpty())
_downloadDEMAction->setEnabled(true);
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024); QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
settings.endGroup(); settings.endGroup();

View File

@ -7,6 +7,7 @@
#include <QDate> #include <QDate>
#include <QPrinter> #include <QPrinter>
#include "common/treenode.h" #include "common/treenode.h"
#include "common/rectc.h"
#include "data/graph.h" #include "data/graph.h"
#include "units.h" #include "units.h"
#include "timetype.h" #include "timetype.h"
@ -32,6 +33,7 @@ class QScreen;
class MapAction; class MapAction;
class POIAction; class POIAction;
class Data; class Data;
class DEMLoader;
class GUI : public QMainWindow class GUI : public QMainWindow
{ {
@ -71,6 +73,7 @@ private slots:
void prevMap(); void prevMap();
void openOptions(); void openOptions();
void clearMapCache(); void clearMapCache();
void downloadDEM();
void mapChanged(QAction *action); void mapChanged(QAction *action);
void graphChanged(int); void graphChanged(int);
@ -102,6 +105,8 @@ private slots:
void mapLoadedDir(); void mapLoadedDir();
void mapInitialized(); void mapInitialized();
void demLoaded();
private: private:
typedef QPair<QDateTime, QDateTime> DateTimeRange; typedef QPair<QDateTime, QDateTime> DateTimeRange;
@ -224,6 +229,7 @@ private:
QAction *_showTicksAction; QAction *_showTicksAction;
QAction *_showCoordinatesAction; QAction *_showCoordinatesAction;
QAction *_openOptionsAction; QAction *_openOptionsAction;
QAction *_downloadDEMAction;
QAction *_mapsEnd; QAction *_mapsEnd;
QAction *_poisEnd; QAction *_poisEnd;
@ -239,6 +245,7 @@ private:
POI *_poi; POI *_poi;
Map *_map; Map *_map;
DEMLoader *_dem;
FileBrowser *_browser; FileBrowser *_browser;
QList<QString> _files; QList<QString> _files;
@ -260,6 +267,8 @@ private:
QString _dataDir, _mapDir, _poiDir; QString _dataDir, _mapDir, _poiDir;
Units _units; Units _units;
RectC _demRect;
}; };
#endif // GUI_H #endif // GUI_H

View File

@ -26,5 +26,6 @@
#define PRINT_EXPORT_ICON ":/document-print_32.png" #define PRINT_EXPORT_ICON ":/document-print_32.png"
#define DATA_ICON ":/view-filter.png" #define DATA_ICON ":/view-filter.png"
#define MAPS_ICON ":/applications-internet_32.png" #define MAPS_ICON ":/applications-internet_32.png"
#define DEM_ICON ":/view-grid.png"
#endif /* ICONS_H */ #endif /* ICONS_H */

10
src/GUI/infolabel.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "infolabel.h"
InfoLabel::InfoLabel(const QString &text, QWidget *parent)
: QLabel(text, parent)
{
QFont f(font());
f.setPointSize(f.pointSize() - 1);
setWordWrap(true);
setFont(f);
}

12
src/GUI/infolabel.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef INFOLABEL_H
#define INFOLABEL_H
#include <QLabel>
class InfoLabel : public QLabel
{
public:
InfoLabel(const QString &text, QWidget *parent = 0);
};
#endif // INFOLABEL_H

View File

@ -88,6 +88,8 @@ public:
void clearMapCache(); void clearMapCache();
void fitContentToSize(); void fitContentToSize();
RectC boundingRect() const {return _tr | _rr | _wr | _ar;}
public slots: public slots:
void showMap(bool show); void showMap(bool show);
void showPOI(bool show); void showPOI(bool show);

View File

@ -14,12 +14,14 @@
#include <QSysInfo> #include <QSysInfo>
#include <QButtonGroup> #include <QButtonGroup>
#include "icons.h" #include "icons.h"
#include "infolabel.h"
#include "colorbox.h" #include "colorbox.h"
#include "stylecombobox.h" #include "stylecombobox.h"
#include "oddspinbox.h" #include "oddspinbox.h"
#include "percentslider.h" #include "percentslider.h"
#include "projectioncombobox.h" #include "projectioncombobox.h"
#include "dirselectwidget.h" #include "dirselectwidget.h"
#include "authenticationwidget.h"
#include "optionsdialog.h" #include "optionsdialog.h"
@ -53,17 +55,11 @@ QWidget *OptionsDialog::createMapPage()
_inputProjection->setCurrentIndex(_inputProjection->findData( _inputProjection->setCurrentIndex(_inputProjection->findData(
_options.inputProjection)); _options.inputProjection));
QLabel *inInfo = new QLabel(tr("Select the proper projection of maps" InfoLabel *inInfo = new InfoLabel(tr("Select the proper projection of maps"
" without a projection definition (JNX, KMZ and world file maps).")); " without a projection definition (JNX, KMZ and world file maps)."));
QLabel *outInfo = new QLabel(tr("Select the desired projection of vector" InfoLabel *outInfo = new InfoLabel(tr("Select the desired projection of"
" maps (IMG and Mapsforge maps). The projection must be valid for" " vector maps (IMG and Mapsforge maps). The projection must be valid for"
" the whole map area.")); " the whole map area."));
QFont f = inInfo->font();
f.setPointSize(f.pointSize() - 1);
inInfo->setWordWrap(true);
outInfo->setWordWrap(true);
inInfo->setFont(f);
outInfo->setFont(f);
_hidpi = new QRadioButton(tr("High-resolution")); _hidpi = new QRadioButton(tr("High-resolution"));
_lodpi = new QRadioButton(tr("Standard")); _lodpi = new QRadioButton(tr("Standard"));
@ -71,14 +67,10 @@ QWidget *OptionsDialog::createMapPage()
_hidpi->setChecked(true); _hidpi->setChecked(true);
else else
_lodpi->setChecked(true); _lodpi->setChecked(true);
QLabel *lhi = new QLabel(tr("Non-HiDPI maps are loaded as HiDPI maps. " InfoLabel *lhi = new InfoLabel(tr("Non-HiDPI maps are loaded as HiDPI maps. "
"The map is sharp but map objects are small/hard to read.")); "The map is sharp but map objects are small/hard to read."));
QLabel *llo = new QLabel(tr("Non-HiDPI maps are loaded such as they are. " InfoLabel *llo = new InfoLabel(tr("Non-HiDPI maps are loaded such as they are. "
"Map objects have the expected size but the map is blurry.")); "Map objects have the expected size but the map is blurry."));
lhi->setWordWrap(true);
llo->setWordWrap(true);
lhi->setFont(f);
llo->setFont(f);
QVBoxLayout *inLayout = new QVBoxLayout(); QVBoxLayout *inLayout = new QVBoxLayout();
inLayout->addWidget(_inputProjection); inLayout->addWidget(_inputProjection);
@ -576,6 +568,42 @@ QWidget *OptionsDialog::createPOIPage()
return poiPage; return poiPage;
} }
QWidget *OptionsDialog::createDEMPage()
{
_demURL = new QLineEdit();
_demURL->setText(_options.demURL);
_demAuth = new AuthenticationWidget();
_demAuth->setUsername(_options.demUsername);
_demAuth->setPassword(_options.demPassword);
_demAuth->setEnabled(_options.demAuthorization);
QCheckBox *useAuth = new QCheckBox(tr("Use HTTP authentication"));
useAuth->setChecked(_demAuth->isEnabled());
connect(useAuth, &QRadioButton::toggled, _demAuth,
&AuthenticationWidget::setEnabled);
InfoLabel *info = new InfoLabel(tr("Use $lat and $lon for NYY/SYY and EXXX/WXXX in the URL."));
QFormLayout *urlLayout = new QFormLayout();
urlLayout->addRow(tr("URL:"), _demURL);
urlLayout->addRow(info);
QVBoxLayout *sourceLayout = new QVBoxLayout();
sourceLayout->addLayout(urlLayout);
sourceLayout->addSpacing(10);
sourceLayout->addWidget(useAuth);
sourceLayout->addWidget(_demAuth);
sourceLayout->addStretch();
QWidget *sourceTab = new QWidget();
sourceTab->setLayout(sourceLayout);
QTabWidget *demPage = new QTabWidget();
demPage->addTab(sourceTab, tr("Source"));
return demPage;
}
QWidget *OptionsDialog::createExportPage() QWidget *OptionsDialog::createExportPage()
{ {
_wysiwyg = new QRadioButton(tr("WYSIWYG")); _wysiwyg = new QRadioButton(tr("WYSIWYG"));
@ -584,17 +612,11 @@ QWidget *OptionsDialog::createExportPage()
_hires->setChecked(true); _hires->setChecked(true);
else else
_wysiwyg->setChecked(true); _wysiwyg->setChecked(true);
QLabel *lw = new QLabel(tr("The printed area is approximately the display" InfoLabel *lw = new InfoLabel(tr("The printed area is approximately the "
" area. The map zoom level does not change.")); "display area. The map zoom level does not change."));
QLabel *lh = new QLabel(tr("The zoom level will be changed so that" InfoLabel *lh = new InfoLabel(tr("The zoom level will be changed so that"
" the whole content (tracks/waypoints) fits to the printed area and" " the whole content (tracks/waypoints) fits to the printed area and"
" the map resolution is as close as possible to the print resolution.")); " the map resolution is as close as possible to the print resolution."));
QFont f = lw->font();
f.setPointSize(f.pointSize() - 1);
lw->setWordWrap(true);
lh->setWordWrap(true);
lw->setFont(f);
lh->setFont(f);
QVBoxLayout *modeTabLayout = new QVBoxLayout(); QVBoxLayout *modeTabLayout = new QVBoxLayout();
modeTabLayout->addWidget(_wysiwyg); modeTabLayout->addWidget(_wysiwyg);
@ -691,12 +713,8 @@ QWidget *OptionsDialog::createSystemPage()
_poiPath = new DirSelectWidget(); _poiPath = new DirSelectWidget();
_poiPath->setDir(_options.poiPath); _poiPath->setDir(_options.poiPath);
QLabel *info = new QLabel(tr("Select the initial paths of the file open" InfoLabel *info = new InfoLabel(tr("Select the initial paths of the file"
" dialogues. Leave the field empty for the system default.")); " open dialogues. Leave the field empty for the system default."));
QFont f = info->font();
f.setPointSize(f.pointSize() - 1);
info->setFont(f);
info->setWordWrap(true);
QFormLayout *pathsFormLayout = new QFormLayout(); QFormLayout *pathsFormLayout = new QFormLayout();
pathsFormLayout->addRow(tr("Data:"), _dataPath); pathsFormLayout->addRow(tr("Data:"), _dataPath);
@ -705,8 +723,8 @@ QWidget *OptionsDialog::createSystemPage()
QWidget *pathsTab = new QWidget(); QWidget *pathsTab = new QWidget();
QVBoxLayout *pathsTabLayout = new QVBoxLayout(); QVBoxLayout *pathsTabLayout = new QVBoxLayout();
pathsTabLayout->addWidget(info);
pathsTabLayout->addLayout(pathsFormLayout); pathsTabLayout->addLayout(pathsFormLayout);
pathsTabLayout->addWidget(info);
pathsTabLayout->addStretch(); pathsTabLayout->addStretch();
pathsTab->setLayout(pathsTabLayout); pathsTab->setLayout(pathsTabLayout);
@ -725,6 +743,7 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
pages->addWidget(createMapPage()); pages->addWidget(createMapPage());
pages->addWidget(createDataPage()); pages->addWidget(createDataPage());
pages->addWidget(createPOIPage()); pages->addWidget(createPOIPage());
pages->addWidget(createDEMPage());
pages->addWidget(createExportPage()); pages->addWidget(createExportPage());
pages->addWidget(createSystemPage()); pages->addWidget(createSystemPage());
@ -735,6 +754,7 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
new QListWidgetItem(QIcon(MAPS_ICON), tr("Maps"), menu); new QListWidgetItem(QIcon(MAPS_ICON), tr("Maps"), menu);
new QListWidgetItem(QIcon(DATA_ICON), tr("Data"), menu); new QListWidgetItem(QIcon(DATA_ICON), tr("Data"), menu);
new QListWidgetItem(QIcon(POI_ICON), tr("POI"), menu); new QListWidgetItem(QIcon(POI_ICON), tr("POI"), menu);
new QListWidgetItem(QIcon(DEM_ICON), tr("DEM"), menu);
new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"), new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"),
menu); menu);
new QListWidgetItem(QIcon(SYSTEM_ICON), tr("System"), menu); new QListWidgetItem(QIcon(SYSTEM_ICON), tr("System"), menu);
@ -831,6 +851,11 @@ void OptionsDialog::accept()
if (qAbs(poiRadius - _options.poiRadius) > 0.01) if (qAbs(poiRadius - _options.poiRadius) > 0.01)
_options.poiRadius = poiRadius; _options.poiRadius = poiRadius;
_options.demURL = _demURL->text();
_options.demAuthorization = _demAuth->isEnabled();
_options.demUsername = _demAuth->username();
_options.demPassword = _demAuth->password();
_options.useOpenGL = _useOpenGL->isChecked(); _options.useOpenGL = _useOpenGL->isChecked();
_options.enableHTTP2 = _enableHTTP2->isChecked(); _options.enableHTTP2 = _enableHTTP2->isChecked();
_options.pixmapCache = _pixmapCache->value(); _options.pixmapCache = _pixmapCache->value();

View File

@ -6,18 +6,19 @@
#include "units.h" #include "units.h"
#include "timezoneinfo.h" #include "timezoneinfo.h"
class ColorBox;
class StyleComboBox;
class OddSpinBox;
class QSpinBox; class QSpinBox;
class QDoubleSpinBox; class QDoubleSpinBox;
class QComboBox; class QComboBox;
class QCheckBox; class QCheckBox;
class QRadioButton; class QRadioButton;
class QLineEdit;
class ColorBox;
class StyleComboBox;
class OddSpinBox;
class PercentSlider; class PercentSlider;
class ProjectionComboBox; class ProjectionComboBox;
class DirSelectWidget; class DirSelectWidget;
class AuthenticationWidget;
struct Options { struct Options {
// Appearance // Appearance
@ -61,6 +62,11 @@ struct Options {
bool useSegments; bool useSegments;
// POI // POI
int poiRadius; int poiRadius;
// DEM
QString demURL;
QString demUsername;
QString demPassword;
bool demAuthorization;
// System // System
bool useOpenGL; bool useOpenGL;
bool enableHTTP2; bool enableHTTP2;
@ -100,6 +106,7 @@ private:
QWidget *createPOIPage(); QWidget *createPOIPage();
QWidget *createSystemPage(); QWidget *createSystemPage();
QWidget *createExportPage(); QWidget *createExportPage();
QWidget *createDEMPage();
Options &_options; Options &_options;
@ -153,6 +160,9 @@ private:
QCheckBox *_useSegments; QCheckBox *_useSegments;
// POI // POI
QDoubleSpinBox *_poiRadius; QDoubleSpinBox *_poiRadius;
// DEM
QLineEdit *_demURL;
AuthenticationWidget *_demAuth;
// System // System
QSpinBox *_pixmapCache; QSpinBox *_pixmapCache;
QSpinBox *_connectionTimeout; QSpinBox *_connectionTimeout;

View File

@ -173,6 +173,14 @@
#define USE_SEGMENTS_DEFAULT true #define USE_SEGMENTS_DEFAULT true
#define POI_RADIUS_SETTING "poiRadius" #define POI_RADIUS_SETTING "poiRadius"
#define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM) #define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM)
#define DEM_URL_SETTING "demURL"
#define DEM_URL_DEFAULT ""
#define DEM_AUTH_SETTING "demAuthentication"
#define DEM_AUTH_DEFAULT false
#define DEM_USERNAME_SETTING "demUsername"
#define DEM_USERNAME_DEFAULT ""
#define DEM_PASSWORD_SETTING "demPassword"
#define DEM_PASSWORD_DEFAULT ""
#define USE_OPENGL_SETTING "useOpenGL" #define USE_OPENGL_SETTING "useOpenGL"
#define USE_OPENGL_DEFAULT false #define USE_OPENGL_DEFAULT false
#define ENABLE_HTTP2_SETTING "enableHTTP2" #define ENABLE_HTTP2_SETTING "enableHTTP2"

View File

@ -77,7 +77,7 @@ QNetworkAccessManager *Downloader::_manager = 0;
int Downloader::_timeout = 30; int Downloader::_timeout = 30;
bool Downloader::_http2 = true; bool Downloader::_http2 = true;
bool Downloader::doDownload(const Download &dl, const QByteArray &authorization) bool Downloader::doDownload(const Download &dl, const Authorization &auth)
{ {
const QUrl &url = dl.url(); const QUrl &url = dl.url();
@ -98,8 +98,8 @@ bool Downloader::doDownload(const Download &dl, const QByteArray &authorization)
QNetworkRequest::NoLessSafeRedirectPolicy); QNetworkRequest::NoLessSafeRedirectPolicy);
request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2)); request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2));
request.setRawHeader("User-Agent", USER_AGENT); request.setRawHeader("User-Agent", USER_AGENT);
if (!authorization.isNull()) if (!auth.isNull())
request.setRawHeader("Authorization", authorization); request.setRawHeader("Authorization", auth.header());
QFile *file = new QFile(tmpName(dl.file())); QFile *file = new QFile(tmpName(dl.file()));
if (!file->open(QIODevice::WriteOnly)) { if (!file->open(QIODevice::WriteOnly)) {
@ -182,7 +182,7 @@ bool Downloader::get(const QList<Download> &list,
bool finishEmitted = false; bool finishEmitted = false;
for (int i = 0; i < list.count(); i++) for (int i = 0; i < list.count(); i++)
finishEmitted |= doDownload(list.at(i), authorization.header()); finishEmitted |= doDownload(list.at(i), authorization);
return finishEmitted; return finishEmitted;
} }

View File

@ -29,6 +29,7 @@ public:
Authorization() {} Authorization() {}
Authorization(const QString &username, const QString &password); Authorization(const QString &username, const QString &password);
bool isNull() const {return _header.isNull();}
const QByteArray &header() const {return _header;} const QByteArray &header() const {return _header;}
private: private:
@ -79,7 +80,7 @@ private:
class ReplyTimeout; class ReplyTimeout;
void insertError(const QUrl &url, QNetworkReply::NetworkError error); void insertError(const QUrl &url, QNetworkReply::NetworkError error);
bool doDownload(const Download &dl, const QByteArray &authorization); bool doDownload(const Download &dl, const Authorization &auth);
void downloadFinished(QNetworkReply *reply); void downloadFinished(QNetworkReply *reply);
void readData(QNetworkReply *reply); void readData(QNetworkReply *reply);

View File

@ -16,7 +16,7 @@
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <private/qzipreader_p.h> #include <private/qzipreader_p.h>
#include "common/coordinates.h" #include "common/rectc.h"
#include "dem.h" #include "dem.h"
@ -66,19 +66,26 @@ static qreal height(const Coordinates &c, const QByteArray *data)
} }
QString DEM::_dir; QString DEM::Tile::latStr() const
QCache<DEM::Key, QByteArray> DEM::_data;
QString DEM::baseName(const Key &key)
{ {
const char ns = (key.lat() >= 0) ? 'N' : 'S'; const char ns = (_lat >= 0) ? 'N' : 'S';
const char ew = (key.lon() >= 0) ? 'E' : 'W'; return QString("%1%2").arg(ns).arg(qAbs(_lat), 2, 10, QChar('0'));
return QString("%1%2%3%4.hgt").arg(ns)
.arg(qAbs(key.lat()), 2, 10, QChar('0')).arg(ew)
.arg(qAbs(key.lon()), 3, 10, QChar('0'));
} }
QString DEM::Tile::lonStr() const
{
const char ew = (_lon >= 0) ? 'E' : 'W';
return QString("%1%2").arg(ew).arg(qAbs(_lon), 3, 10, QChar('0'));
}
QString DEM::Tile::baseName() const
{
return QString("%1%2.hgt").arg(latStr(), lonStr());
}
QString DEM::_dir;
QCache<DEM::Tile, QByteArray> DEM::_data;
QString DEM::fileName(const QString &baseName) QString DEM::fileName(const QString &baseName)
{ {
return QDir(_dir).absoluteFilePath(baseName); return QDir(_dir).absoluteFilePath(baseName);
@ -87,6 +94,7 @@ QString DEM::fileName(const QString &baseName)
void DEM::setDir(const QString &path) void DEM::setDir(const QString &path)
{ {
_dir = path; _dir = path;
_data.clear();
} }
qreal DEM::elevation(const Coordinates &c) qreal DEM::elevation(const Coordinates &c)
@ -94,11 +102,11 @@ qreal DEM::elevation(const Coordinates &c)
if (_dir.isEmpty()) if (_dir.isEmpty())
return NAN; return NAN;
Key k(qFloor(c.lon()), qFloor(c.lat())); Tile tile(qFloor(c.lon()), qFloor(c.lat()));
QByteArray *ba = _data[k]; QByteArray *ba = _data[tile];
if (!ba) { if (!ba) {
QString bn(baseName(k)); QString bn(tile.baseName());
QString fn(fileName(bn)); QString fn(fileName(bn));
QString zn(fn + ".zip"); QString zn(fn + ".zip");
@ -106,22 +114,30 @@ qreal DEM::elevation(const Coordinates &c)
QZipReader zip(zn, QIODevice::ReadOnly); QZipReader zip(zn, QIODevice::ReadOnly);
ba = new QByteArray(zip.fileData(bn)); ba = new QByteArray(zip.fileData(bn));
qreal ele = height(c, ba); qreal ele = height(c, ba);
_data.insert(k, ba); _data.insert(tile, ba);
return ele; return ele;
} else { } else {
QFile file(fn); QFile file(fn);
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
qWarning("%s: %s", qPrintable(file.fileName()), qWarning("%s: %s", qPrintable(file.fileName()),
qPrintable(file.errorString())); qPrintable(file.errorString()));
_data.insert(k, new QByteArray()); _data.insert(tile, new QByteArray());
return NAN; return NAN;
} else { } else {
ba = new QByteArray(file.readAll()); ba = new QByteArray(file.readAll());
qreal ele = height(c, ba); qreal ele = height(c, ba);
_data.insert(k, ba); _data.insert(tile, ba);
return ele; return ele;
} }
} }
} else } else
return height(c, ba); return height(c, ba);
} }
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const DEM::Tile &tile)
{
dbg.nospace() << "Tile(" << tile.baseName() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

View File

@ -8,18 +8,23 @@
class QString; class QString;
class Coordinates; class Coordinates;
class RectC;
class DEM class DEM
{ {
private: public:
class Key { class Tile {
public: public:
Key(int lon, int lat) : _lon(lon), _lat(lat) {} Tile(int lon, int lat) : _lon(lon), _lat(lat) {}
int lon() const {return _lon;} int lon() const {return _lon;}
int lat() const {return _lat;} int lat() const {return _lat;}
bool operator==(const Key &other) const QString lonStr() const;
QString latStr() const;
QString baseName() const;
bool operator==(const Tile &other) const
{ {
return (_lon == other._lon && _lat == other._lat); return (_lon == other._lon && _lat == other._lat);
} }
@ -28,22 +33,23 @@ private:
int _lon, _lat; int _lon, _lat;
}; };
static QString baseName(const Key &key);
static QString fileName(const QString &baseName);
static QString _dir;
static QCache<Key, QByteArray> _data;
public:
static void setDir(const QString &path); static void setDir(const QString &path);
static qreal elevation(const Coordinates &c); static qreal elevation(const Coordinates &c);
friend HASH_T qHash(const Key &key); private:
static QString fileName(const QString &baseName);
static QString _dir;
static QCache<Tile, QByteArray> _data;
}; };
inline HASH_T qHash(const DEM::Key &key) inline HASH_T qHash(const DEM::Tile &tile)
{ {
return (qHash(key.lon()) ^ qHash(key.lat())); return (qHash(tile.lon()) ^ qHash(tile.lat()));
} }
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const DEM::Tile &tile);
#endif // QT_NO_DEBUG
#endif // DEM_H #endif // DEM_H

102
src/data/demloader.cpp Normal file
View File

@ -0,0 +1,102 @@
#include <QtMath>
#include <QDir>
#include <QFileInfo>
#include "common/rectc.h"
#include "demloader.h"
#define DOWNLOAD_LIMIT 32
static QList<DEM::Tile> tiles(const RectC &rect)
{
QList<DEM::Tile> list;
if (!rect.isValid())
return list;
for (int i = qFloor(rect.top()); i >= qFloor(rect.bottom()); i--)
for (int j = qFloor(rect.left()); j <= qFloor(rect.right()); j++)
list.append(DEM::Tile(j, i));
return list;
}
static bool isZip(const QUrl &url)
{
QFileInfo fi(url.fileName());
return (fi.suffix().toLower() == "zip");
}
DEMLoader::DEMLoader(const QString &dir, QObject *parent)
: QObject(parent), _dir(dir)
{
_downloader = new Downloader(this);
connect(_downloader, &Downloader::finished, this, &DEMLoader::finished);
}
bool DEMLoader::loadTiles(const RectC &rect)
{
QList<DEM::Tile> tl(tiles(rect));
QList<Download> dl;
/* Create the user DEM dir only when a download is requested as it will
override the global DEM dir. */
if (!QDir().mkpath(_dir)) {
qWarning("%s: %s", qPrintable(_dir), "Error creating DEM directory");
return false;
}
/* This also clears the DEM data cache which is necessary to use the data
from the downloaded DEM tiles. */
DEM::setDir(_dir);
for (int i = 0; i < tl.size(); i++) {
const DEM::Tile &t = tl.at(i);
QString fn(tileFile(t));
QString zn(fn + ".zip");
if (!(QFileInfo(zn).exists() || QFileInfo(fn).exists())) {
QUrl url(tileUrl(t));
dl.append(Download(url, isZip(url) ? zn : fn));
}
}
if (dl.size() > DOWNLOAD_LIMIT) {
qWarning("DEM download limit exceeded. Limit/requested: %u/%u.",
DOWNLOAD_LIMIT, dl.size());
return false;
}
return _downloader->get(dl, _authorization);
}
bool DEMLoader::checkTiles(const RectC &rect) const
{
QList<DEM::Tile> tl(tiles(rect));
for (int i = 0; i < tl.size(); i++) {
const DEM::Tile &t = tl.at(i);
QString fn(tileFile(t));
QString zn(fn + ".zip");
if (!(QFileInfo(zn).exists() || QFileInfo(fn).exists()))
return false;
}
return true;
}
QUrl DEMLoader::tileUrl(const DEM::Tile &tile) const
{
QString url(_url);
url.replace("$lon", tile.lonStr());
url.replace("$lat", tile.latStr());
return QUrl(url);
}
QString DEMLoader::tileFile(const DEM::Tile &tile) const
{
return _dir + QLatin1Char('/') + tile.baseName();
}

37
src/data/demloader.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef DEMLOADER_H
#define DEMLOADER_H
#include <QObject>
#include "common/downloader.h"
#include "dem.h"
class RectC;
class DEMLoader : public QObject
{
Q_OBJECT
public:
DEMLoader(const QString &dir, QObject *parent = 0);
void setUrl(const QString &url) {_url = url;}
void setAuthorization(const Authorization &authorization)
{_authorization = authorization;}
bool loadTiles(const RectC &rect);
bool checkTiles(const RectC &rect) const;
signals:
void finished();
private:
QUrl tileUrl(const DEM::Tile &tile) const;
QString tileFile(const DEM::Tile &tile) const;
Downloader *_downloader;
QString _url;
QString _dir;
Authorization _authorization;
};
#endif // DEMLOADER_H