1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-13 18:35:11 +02:00

Compare commits

...

41 Commits
13.10 ... 13.11

Author SHA1 Message Date
941b8e25bd Synchronized the ts files with the sources 2023-11-19 11:05:14 +01:00
8ad2d57ca1 Added GPSDump files support info 2023-11-19 10:30:34 +01:00
dbc18227ff Merge branch 'origin/master' into Weblate. 2023-11-18 23:55:45 +01:00
95d354f10c Fixed map bounds limiting 2023-11-18 23:55:58 +01:00
aedbc99a6c Improved MBTiles map loading performance when metadata table present
+ Fixed zoom limiting
2023-11-18 23:29:18 +01:00
04f9457085 Fixed bounds limiting 2023-11-18 21:32:30 +01:00
2adf0d4089 Translated using Weblate (Turkish)
Currently translated at 100.0% (471 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2023-11-18 19:01:04 +01:00
f572d68420 Merge branch 'origin/master' into Weblate. 2023-11-17 17:40:58 +01:00
bbc03ae59f Render MBTiles maps asynchronous if they include vector tiles 2023-11-17 17:39:33 +01:00
4d40cd6458 Translated using Weblate (German)
Currently translated at 100.0% (471 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2023-11-17 16:28:46 +01:00
69e1198efa Merge remote-tracking branch 'weblate/master' 2023-11-17 16:18:44 +01:00
059ed3aa14 Dothe QImage -> QPixmap transformation in parallel 2023-11-17 16:17:53 +01:00
7eba9ef39b Merge branch 'origin/master' into Weblate. 2023-11-17 12:41:56 +00:00
1d494d26aa Translated using Weblate (Spanish)
Currently translated at 100.0% (471 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/es/
2023-11-17 12:41:55 +00:00
f258ca1a3e Prevent broken Qt heuristics to put submenus on wrong places on Mac 2023-11-17 13:41:01 +01:00
58d2d84555 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (471 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/zh_Hans/
2023-11-16 22:26:31 +01:00
dcf9d625ac Translated using Weblate (Hungarian)
Currently translated at 100.0% (471 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2023-11-16 22:26:31 +01:00
47664c8cbd Translated using Weblate (Spanish)
Currently translated at 99.7% (470 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/es/
2023-11-16 22:26:31 +01:00
055b068379 Translated using Weblate (Swedish)
Currently translated at 99.5% (469 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2023-11-16 07:28:22 +01:00
b1655c6348 Merge remote-tracking branch 'weblate/master' 2023-11-15 22:56:35 +01:00
132ccb7696 Sosme more symbol icons 2023-11-15 22:56:06 +01:00
ea3257b7d0 Translated using Weblate (Czech)
Currently translated at 100.0% (471 of 471 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2023-11-15 22:28:54 +01:00
8419dee824 Localization update 2023-11-15 22:26:04 +01:00
22821796f2 Merge branch 'origin/master' into Weblate. 2023-11-15 22:25:49 +01:00
816b18b8cc Merge branch 'origin/master' into Weblate. 2023-11-15 02:48:36 +01:00
8ebd73115f Added "Open recent" menu 2023-11-15 01:24:39 +01:00
720c0f94bd Merge branch 'origin/master' into Weblate. 2023-11-13 22:21:56 +01:00
e450e62d8d Added missing symbol icons 2023-11-13 22:21:47 +01:00
a33ec51c9b Translated using Weblate (Swedish)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2023-11-12 11:01:36 +01:00
57e3bb293c Translated using Weblate (Turkish)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2023-11-11 10:24:30 +01:00
c6aca7940b Translated using Weblate (Swedish)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2023-11-11 10:24:30 +01:00
10bce1e974 Translated using Weblate (Hungarian)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2023-11-10 17:34:56 +01:00
raf
21a5f53759 Translated using Weblate (Catalan)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ca/
2023-11-10 16:33:52 +01:00
a589d4ee9e Translated using Weblate (Hungarian)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2023-11-10 16:33:52 +01:00
97bc9164a2 Translated using Weblate (Spanish)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/es/
2023-11-10 16:33:52 +01:00
8e5b4a8007 Translated using Weblate (Czech)
Currently translated at 100.0% (469 of 469 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2023-11-08 23:34:50 +01:00
148de41eb7 Localization update 2023-11-08 23:01:36 +01:00
75a757cc0c Added support for GPSDump WPT files 2023-11-08 22:36:05 +01:00
2c804780c4 Updated screenshot URL 2023-11-04 14:53:24 +01:00
b27981fe0f Fixed build with Qt versions < 5.12 2023-10-28 14:20:06 +02:00
2374a7d1dd Version++ 2023-10-28 14:19:50 +02:00
51 changed files with 5555 additions and 4736 deletions

View File

@ -1,4 +1,4 @@
version: 13.10.{build}
version: 13.11.{build}
configuration:
- Release

View File

@ -5,7 +5,7 @@ GPS log file formats.
## Features
* Opens GPX, TCX, FIT, KML, NMEA, IGC, CUP, SIGMA SLF, Suunto SML, LOC, GeoJSON,
OziExplorer (PLT, RTE, WPT), Garmin GPI&CSV, TomTom OV2&ITN, ONmove OMD/GHP,
TwoNav (TRK, RTE, WPT) and geotagged JPEG files.
TwoNav (TRK, RTE, WPT), GPSDump and geotagged JPEG files.
* User-definable online maps (OpenStreetMap/Google tiles, WMTS, WMS, TMS,
QuadTiles).
* Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases,
@ -24,7 +24,7 @@ GPS log file formats.
* Real-time GPS position.
* Windows, macOS, Linux and Android builds.
![GPXSee - Linux](https://a.fsdn.com/con/app/proj/gpxsee/screenshots/linux2.png)
![GPXSee - Linux](https://www.gpxsee.org/gallery/linux.png)
## Build
Build requirements:
@ -39,8 +39,6 @@ qmake gpxsee.pro
make # nmake on windows
```
## Download
* [Windows & OS X builds](https://sourceforge.net/projects/gpxsee)
* [Linux packages](https://software.opensuse.org/download.html?project=home%3Atumic%3AGPXSee&package=gpxsee)
@ -75,4 +73,5 @@ different, GPL compatible, licenses:
[Transverse Mercator](src/map/proj/transversemercator.cpp) projections - NIMA
Source Code Disclaimer
* [Projection parameters CSV files](data/CRS) - BSD/EPSG/Public domain
* [Mapsforge render theme](data/mapsforge/default.xml) and its [icons](icons/map/mapsforge) - LGPLv3
* [Mapsforge render theme](data/mapsforge/default.xml) and its
[icons](icons/map/mapsforge) - LGPLv3

View File

@ -3,7 +3,7 @@ unix:!macx:!android {
} else {
TARGET = GPXSee
}
VERSION = 13.10
VERSION = 13.11
QT += core \
@ -118,6 +118,7 @@ HEADERS += src/common/config.h \
src/GUI/pngexportdialog.h \
src/GUI/timezoneinfo.h \
src/GUI/passwordedit.h \
src/data/gpsdumpparser.h \
src/data/style.h \
src/data/twonavparser.h \
src/map/ENC/attributes.h \
@ -338,6 +339,7 @@ SOURCES += src/main.cpp \
src/GUI/pngexportdialog.cpp \
src/GUI/projectioncombobox.cpp \
src/GUI/passwordedit.cpp \
src/data/gpsdumpparser.cpp \
src/data/twonavparser.cpp \
src/map/ENC/atlasdata.cpp \
src/map/ENC/mapdata.cpp \

View File

@ -0,0 +1,4 @@
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path fill="#ff9900" d="M 17.417969 5.5 L 10.082031 5.5 L 8.25 3.667969 L 3.667969 3.667969 C 2.660156 3.667969 1.832031 4.492188 1.832031 5.5 L 1.832031 16.5 C 1.832031 17.507812 2.660156 18.332031 3.667969 18.332031 L 17.875 18.332031 C 18.652344 18.332031 19.25 17.738281 19.25 16.957031 L 19.25 7.332031 C 19.25 6.324219 18.425781 5.5 17.417969 5.5 Z M 17.417969 5.5 "/>
<path fill="#ffcc00" d="M 19.339844 8.25 L 7.011719 8.25 C 6.140625 8.25 5.363281 8.890625 5.226562 9.761719 L 3.667969 18.332031 L 18.195312 18.332031 C 19.066406 18.332031 19.847656 17.691406 19.984375 16.820312 L 21.128906 10.402344 C 21.359375 9.304688 20.488281 8.25 19.339844 8.25 Z M 19.339844 8.25 "/>
</svg>

After

Width:  |  Height:  |  Size: 773 B

View File

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" version="1.1">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#444444; } .ColorScheme-Highlight { color:#4285f4; } .ColorScheme-NeutralText { color:#ff9800; } .ColorScheme-PositiveText { color:#4caf50; } .ColorScheme-NegativeText { color:#f44336; }
</style>
</defs>
<g transform="translate(3,3)">
<path style="fill:currentColor;fill-rule:evenodd" class="ColorScheme-Text" d="M 7,4 H 9 V 7.5 L 11,9.5 9.5,11 7,8.5 Z M 8,1 A 7,7 0 0 0 1,8 7,7 0 0 0 8,15 7,7 0 0 0 15,8 7,7 0 0 0 8,1 Z M 8,3 A 5,5 0 0 1 13,8 5,5 0 0 1 8,13 5,5 0 0 1 3,8 5,5 0 0 1 8,3 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 678 B

BIN
icons/symbols/Ferry.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/symbols/Funicular.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/symbols/Lodge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/symbols/Pizza.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
icons/symbols/Railway.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
icons/symbols/Shower.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,4 +16,4 @@ Icon=gpxsee
Terminal=false
Type=Application
Categories=Graphics;Viewer;Education;Geography;Maps;Sports;Qt
MimeType=application/gpx+xml;application/vnd.garmin.tcx+xml;application/vnd.ant.fit;application/vnd.google-earth.kml+xml;application/vnd.fai.igc;application/vnd.nmea.nmea;application/vnd.oziexplorer.plt;application/vnd.oziexplorer.rte;application/vnd.oziexplorer.wpt;application/vnd.groundspeak.loc+xml;application/vnd.sigma.slf+xml;application/geo+json;application/vnd.naviter.seeyou.cup;application/vnd.garmin.gpi;application/vnd.suunto.sml+xml;image/jpeg;text/csv;application/vnd.garmin.img;application/vnd.garmin.jnx;application/vnd.garmin.gmap+xml;image/vnd.maptech.kap;application/vnd.oziexplorer.map;application/vnd.mapbox.mbtiles;application/vnd.twonav.rmap;application/vnd.trekbuddy.tba;application/vnd.gpxsee.map+xml;application/x-tar;image/tiff;application/vnd.google-earth.kmz;application/vnd.alpinequest.aqm;application/vnd.cgtk.gemf;application/vnd.rmaps.sqlite;application/vnd.osmdroid.sqlite;application/vnd.mapsforge.map;application/vnd.tomtom.ov2;application/vnd.tomtom.itn;application/vnd.esri.wld;application/vnd.onmove.omd;application/vnd.onmove.ghp;application/vnd.memory-map.qct;application/vnd.twonav.trk;application/vnd.twonav.rte;application/vnd.twonav.wpt;application/vnd.orux.map+xml;application/vnd.iho.s57-data;application/vnd.iho.s57-catalogue
MimeType=application/gpx+xml;application/vnd.garmin.tcx+xml;application/vnd.ant.fit;application/vnd.google-earth.kml+xml;application/vnd.fai.igc;application/vnd.nmea.nmea;application/vnd.oziexplorer.plt;application/vnd.oziexplorer.rte;application/vnd.oziexplorer.wpt;application/vnd.groundspeak.loc+xml;application/vnd.sigma.slf+xml;application/geo+json;application/vnd.naviter.seeyou.cup;application/vnd.garmin.gpi;application/vnd.suunto.sml+xml;image/jpeg;text/csv;application/vnd.garmin.img;application/vnd.garmin.jnx;application/vnd.garmin.gmap+xml;image/vnd.maptech.kap;application/vnd.oziexplorer.map;application/vnd.mapbox.mbtiles;application/vnd.twonav.rmap;application/vnd.trekbuddy.tba;application/vnd.gpxsee.map+xml;application/x-tar;image/tiff;application/vnd.google-earth.kmz;application/vnd.alpinequest.aqm;application/vnd.cgtk.gemf;application/vnd.rmaps.sqlite;application/vnd.osmdroid.sqlite;application/vnd.mapsforge.map;application/vnd.tomtom.ov2;application/vnd.tomtom.itn;application/vnd.esri.wld;application/vnd.onmove.omd;application/vnd.onmove.ghp;application/vnd.memory-map.qct;application/vnd.twonav.trk;application/vnd.twonav.rte;application/vnd.twonav.wpt;application/vnd.orux.map+xml;application/vnd.iho.s57-data;application/vnd.iho.s57-catalogue;application/vnd.gpsdump.wpt

View File

@ -177,6 +177,16 @@
<glob pattern="*.wpt"/>
</mime-type>
<mime-type type="application/vnd.gpsdump.wpt">
<comment>GPSDump Waypoint File</comment>
<sub-class-of type="text/plain"/>
<generic-icon name="text/plain"/>
<magic>
<match type="string" offset="0" value="$FormatGEO"/>
<match type="string" offset="0" value="$FormatUTM"/>
</magic>
<glob pattern="*.wpt"/>
</mime-type>
<!-- Maps -->

View File

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

View File

@ -70,7 +70,9 @@ App::App(int &argc, char **argv) : QApplication(argc, argv)
loadPCSs();
Waypoint::loadSymbolIcons(ProgramPaths::symbolsDir());
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
QIcon::setFallbackThemeName(APP_NAME);
#endif // QT 5.12
_gui = new GUI();

View File

@ -59,12 +59,13 @@
#include "gui.h"
#define MAX_RECENT_FILES 10
#define TOOLBAR_ICON_SIZE 22
GUI::GUI()
{
QString activeMap;
QStringList disabledPOIs;
QStringList disabledPOIs, recentFiles;
_poi = new POI(this);
_dem = new DEMLoader(ProgramPaths::demDir(true), this);
@ -108,10 +109,13 @@ GUI::GUI()
_movingTime = 0;
_lastTab = 0;
readSettings(activeMap, disabledPOIs);
readSettings(activeMap, disabledPOIs, recentFiles);
loadInitialMaps(activeMap);
loadInitialPOIs(disabledPOIs);
#ifndef Q_OS_ANDROID
loadRecentFiles(recentFiles);
#endif // Q_OS_ANDROID
updateGraphTabs();
updateStatusBarInfo();
@ -261,6 +265,15 @@ void GUI::createActions()
_statisticsAction->setActionGroup(_fileActionGroup);
connect(_statisticsAction, &QAction::triggered, this, &GUI::statistics);
addAction(_statisticsAction);
#ifndef Q_OS_ANDROID
_recentFilesActionGroup = new QActionGroup(this);
connect(_recentFilesActionGroup, &QActionGroup::triggered, this,
&GUI::recentFileSelected);
_clearRecentFilesAction = new QAction(tr("Clear list"), this);
_clearRecentFilesAction->setMenuRole(QAction::NoRole);
connect(_clearRecentFilesAction, &QAction::triggered, this,
&GUI::clearRecentFiles);
#endif // Q_OS_ANDROID
// POI actions
_poisActionGroup = new QActionGroup(this);
@ -614,6 +627,15 @@ void GUI::createMenus()
{
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(_openFileAction);
#ifndef Q_OS_ANDROID
_recentFilesMenu = fileMenu->addMenu(tr("Open recent"));
_recentFilesMenu->setIcon(QIcon::fromTheme(OPEN_RECENT_NAME,
QIcon(OPEN_RECENT_ICON)));
_recentFilesMenu->menuAction()->setMenuRole(QAction::NoRole);
_recentFilesMenu->setEnabled(false);
_recentFilesEnd = _recentFilesMenu->addSeparator();
_recentFilesMenu->addAction(_clearRecentFilesAction);
#endif // Q_OS_ANDROID
fileMenu->addAction(_openDirAction);
fileMenu->addSeparator();
#ifndef Q_OS_ANDROID
@ -659,6 +681,7 @@ void GUI::createMenus()
dataMenu->addAction(_showRouteWaypointsAction);
dataMenu->addAction(_showTicksAction);
QMenu *markerMenu = dataMenu->addMenu(tr("Position info"));
markerMenu->menuAction()->setMenuRole(QAction::NoRole);
markerMenu->addAction(_hideMarkersAction);
markerMenu->addAction(_showMarkersAction);
markerMenu->addAction(_showMarkerDateAction);
@ -696,13 +719,16 @@ void GUI::createMenus()
QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
QMenu *timeMenu = settingsMenu->addMenu(tr("Time"));
timeMenu->menuAction()->setMenuRole(QAction::NoRole);
timeMenu->addAction(_totalTimeAction);
timeMenu->addAction(_movingTimeAction);
QMenu *unitsMenu = settingsMenu->addMenu(tr("Units"));
unitsMenu->menuAction()->setMenuRole(QAction::NoRole);
unitsMenu->addAction(_metricUnitsAction);
unitsMenu->addAction(_imperialUnitsAction);
unitsMenu->addAction(_nauticalUnitsAction);
QMenu *coordinatesMenu = settingsMenu->addMenu(tr("Coordinates format"));
coordinatesMenu->menuAction()->setMenuRole(QAction::NoRole);
coordinatesMenu->addAction(_decimalDegreesAction);
coordinatesMenu->addAction(_degreesMinutesAction);
coordinatesMenu->addAction(_dmsAction);
@ -1018,6 +1044,9 @@ bool GUI::openFile(const QString &fileName, bool tryUnknown, int &showError)
updateNavigationActions();
updateStatusBarInfo();
updateWindowTitle();
#ifndef Q_OS_ANDROID
updateRecentFiles(fileName);
#endif // Q_OS_ANDROID
return true;
}
@ -1992,6 +2021,48 @@ void GUI::updateWindowTitle()
setWindowTitle(APP_NAME);
}
#ifndef Q_OS_ANDROID
void GUI::updateRecentFiles(const QString &fileName)
{
QAction *a = 0;
QList<QAction *> actions(_recentFilesActionGroup->actions());
for (int i = 0; i < actions.size(); i++) {
if (actions.at(i)->text() == fileName) {
a = actions.at(i);
break;
}
}
if (a)
delete a;
else if (actions.size() == MAX_RECENT_FILES)
delete actions.last();
actions = _recentFilesActionGroup->actions();
QAction *before = actions.size() ? actions.last() : _recentFilesEnd;
_recentFilesMenu->insertAction(before,
new QAction(fileName, _recentFilesActionGroup));
_recentFilesMenu->setEnabled(true);
}
void GUI::clearRecentFiles()
{
QList<QAction *> actions(_recentFilesActionGroup->actions());
for (int i = 0; i < actions.size(); i++)
delete actions.at(i);
_recentFilesMenu->setEnabled(false);
}
void GUI::recentFileSelected(QAction *action)
{
int showError = 1;
openFile(action->text(), true, showError);
}
#endif // Q_OS_ANDROID
void GUI::mapChanged(QAction *action)
{
_map = action->data().value<Map*>();
@ -2353,6 +2424,18 @@ void GUI::writeSettings()
#endif // Q_OS_ANDROID
settings.endGroup();
/* File */
#ifndef Q_OS_ANDROID
QList<QAction*> recentActions(_recentFilesActionGroup->actions());
QStringList recent;
for (int i = 0; i < recentActions.size(); i++)
recent.append(recentActions.at(i)->text());
settings.beginGroup(SETTINGS_FILE);
WRITE(recentDataFiles, recent);
settings.endGroup();
#endif // Q_OS_ANDROID
/* Map */
settings.beginGroup(SETTINGS_MAP);
WRITE(activeMap, _map->name());
@ -2372,11 +2455,11 @@ void GUI::writeSettings()
settings.endGroup();
/* POI */
QList<QAction*> actions(_poisActionGroup->actions());
QList<QAction*> disabledActions(_poisActionGroup->actions());
QStringList disabled;
for (int i = 0; i < actions.size(); i++)
if (!actions.at(i)->isChecked())
disabled.append(actions.at(i)->data().toString());
for (int i = 0; i < disabledActions.size(); i++)
if (!disabledActions.at(i)->isChecked())
disabled.append(disabledActions.at(i)->data().toString());
settings.beginGroup(SETTINGS_POI);
WRITE(showPoi, _showPOIAction->isChecked());
@ -2511,7 +2594,8 @@ void GUI::writeSettings()
settings.endGroup();
}
void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs)
void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs,
QStringList &recentFiles)
{
#define READ(name) \
(Settings::name.read(settings))
@ -2560,6 +2644,15 @@ void GUI::readSettings(QString &activeMap, QStringList &disabledPOIs)
#endif // Q_OS_ANDROID
settings.endGroup();
/* File */
#ifndef Q_OS_ANDROID
settings.beginGroup(SETTINGS_FILE);
recentFiles = READ(recentDataFiles);
settings.endGroup();
#else // Q_OS_ANDROID
Q_UNUSED(recentFiles);
#endif // Q_OS_ANDROID
/* Map */
settings.beginGroup(SETTINGS_MAP);
if (READ(showMap).toBool()) {
@ -3052,6 +3145,22 @@ void GUI::loadInitialPOIs(const QStringList &disabled)
_unselectAllPOIAction->setEnabled(!poiActions.isEmpty());
}
#ifndef Q_OS_ANDROID
void GUI::loadRecentFiles(const QStringList &files)
{
QAction *before = _recentFilesEnd;
for (int i = 0; i < files.size(); i++) {
QAction *a = new QAction(files.at(i), _recentFilesActionGroup);
_recentFilesMenu->insertAction(before, a);
before = a;
}
if (!files.isEmpty())
_recentFilesMenu->setEnabled(true);
}
#endif // Q_OS_ANDROID
QAction *GUI::mapAction(const QString &name)
{
QList<QAction *> maps(_mapsActionGroup->actions());

View File

@ -92,6 +92,10 @@ private slots:
void poiFileChecked(QAction *action);
void selectAllPOIs();
void unselectAllPOIs();
#ifndef Q_OS_ANDROID
void recentFileSelected(QAction *action);
void clearRecentFiles();
#endif // Q_OS_ANDROID
void next();
void prev();
@ -161,6 +165,9 @@ private:
void updateWindowTitle();
bool updateGraphTabs();
void updateDEMDownloadAction();
#ifndef Q_OS_ANDROID
void updateRecentFiles(const QString &fileName);
#endif // Q_OS_ANDROID
TimeType timeType() const;
Units units() const;
@ -174,10 +181,14 @@ private:
qreal movingTime() const;
QAction *mapAction(const QString &name);
QGeoPositionInfoSource *positionSource(const Options &options);
void readSettings(QString &activeMap, QStringList &disabledPOIs);
void readSettings(QString &activeMap, QStringList &disabledPOIs,
QStringList &recentFiles);
void loadInitialMaps(const QString &selected);
void loadInitialPOIs(const QStringList &disabled);
#ifndef Q_OS_ANDROID
void loadRecentFiles(const QStringList &files);
#endif // Q_OS_ANDROID
void loadOptions();
void updateOptions(const Options &options);
@ -198,11 +209,17 @@ private:
#endif // Q_OS_ANDROID
QMenu *_poiMenu;
QMenu *_mapMenu;
#ifndef Q_OS_ANDROID
QMenu *_recentFilesMenu;
#endif // Q_OS_ANDROID
QActionGroup *_fileActionGroup;
QActionGroup *_navigationActionGroup;
QActionGroup *_mapsActionGroup;
QActionGroup *_poisActionGroup;
#ifndef Q_OS_ANDROID
QActionGroup *_recentFilesActionGroup;
#endif // Q_OS_ANDROID
#if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
QAction *_exitAction;
#endif // Q_OS_MAC + Q_OS_ANDROID
@ -276,6 +293,10 @@ private:
QAction *_showDEMTilesAction;
QAction *_mapsEnd;
QAction *_poisEnd;
#ifndef Q_OS_ANDROID
QAction *_clearRecentFilesAction;
QAction *_recentFilesEnd;
#endif // Q_OS_ANDROID
QLabel *_fileNameLabel;
QLabel *_distanceLabel;

View File

@ -7,6 +7,7 @@
// Toolbar/menu icons
#define OPEN_FILE_ICON ":/icons/" APP_NAME "/actions/22x22/document-open.svg"
#define OPEN_RECENT_ICON ":/icons/" APP_NAME "/actions/22x22/document-open-recent.svg"
#define OPEN_DIR_ICON ":/icons/" APP_NAME "/actions/22x22/document-open-folder.svg"
#define EXPORT_FILE_ICON ":/icons/" APP_NAME "/actions/22x22/document-export.svg"
#define PRINT_FILE_ICON ":/icons/" APP_NAME "/actions/22x22/document-print.svg"
@ -40,6 +41,7 @@
// Desktop theme names
#define OPEN_FILE_NAME "document-open"
#define OPEN_RECENT_NAME "document-open-recent"
#define OPEN_DIR_NAME "document-open-folder"
#define EXPORT_FILE_NAME "document-export"
#define PRINT_FILE_NAME "document-print"

View File

@ -268,3 +268,8 @@ const Settings::SettingMap Settings::positionPluginParameters
const Settings::SettingList Settings::disabledPoiFiles
= Settings::SettingList("disabled", "file");
#ifndef Q_OS_ANDROID
const Settings::SettingList Settings::recentDataFiles
= Settings::SettingList("recent", "file");
#endif // Q_OS_ANDROID

View File

@ -7,6 +7,7 @@
#define SETTINGS_WINDOW "Window"
#define SETTINGS_SETTINGS "Settings"
#define SETTINGS_FILE "File"
#define SETTINGS_MAP "Map"
#define SETTINGS_GRAPH "Graph"
#define SETTINGS_POI "POI"
@ -84,6 +85,11 @@ public:
static const Setting showToolbars;
#endif // Q_OS_ANDROID
/* File */
#ifndef Q_OS_ANDROID
static const SettingList recentDataFiles;
#endif // Q_OS_ANDROID
/* Map */
static const Setting activeMap;
static const Setting showMap;

View File

@ -21,6 +21,7 @@
#include "itnparser.h"
#include "onmoveparsers.h"
#include "twonavparser.h"
#include "gpsdumpparser.h"
#include "data.h"
@ -46,6 +47,7 @@ static ITNParser itn;
static OMDParser omd;
static GHPParser ghp;
static TwoNavParser twonav;
static GPSDumpParser gpsdump;
static QMultiMap<QString, Parser*> parsers()
{
@ -78,6 +80,7 @@ static QMultiMap<QString, Parser*> parsers()
map.insert("trk", &twonav);
map.insert("rte", &twonav);
map.insert("wpt", &twonav);
map.insert("wpt", &gpsdump);
return map;
}
@ -174,6 +177,7 @@ QString Data::formats()
+ qApp->translate("Data", "SML files") + " (*.sml);;"
+ qApp->translate("Data", "TCX files") + " (*.tcx);;"
+ qApp->translate("Data", "TwoNav files") + " (*.rte *.trk *.wpt);;"
+ qApp->translate("Data", "GPSDump files") + " (*.wpt);;"
+ qApp->translate("Data", "All files") + " (*)";
}

161
src/data/gpsdumpparser.cpp Normal file
View File

@ -0,0 +1,161 @@
#include <QRegularExpression>
#include "map/pcs.h"
#include "map/gcs.h"
#include "map/utm.h"
#include "gpsdumpparser.h"
static double dms2dd(const QStringList &dms)
{
bool ok;
int deg = dms.at(1).toInt(&ok);
if (!ok)
return NAN;
int min = dms.at(2).toInt(&ok);
if (!ok)
return NAN;
double sec = dms.at(3).toDouble(&ok);
if (!ok)
return NAN;
return deg + min/60.0 + sec/3600.0;
}
static double parseLon(const QString &str)
{
QStringList dms(str.split(' '));
if (dms.size() < 4)
return NAN;
double dd = dms2dd(dms);
if (std::isnan(dd))
return NAN;
if (dms.at(0) == 'W')
return -dd;
else if (dms.at(0) == 'E')
return dd;
else
return NAN;
}
static double parseLat(const QString &str)
{
QStringList dms(str.split(' '));
if (dms.size() < 4)
return NAN;
double dd = dms2dd(dms);
if (std::isnan(dd))
return NAN;
if (dms.at(0) == 'S')
return -dd;
else if (dms.at(0) == 'N')
return dd;
else
return NAN;
}
static Coordinates parseGEO(const QString &lat, const QString &lon)
{
return Coordinates(parseLon(lon), parseLat(lat));
}
static Coordinates parseUTM(const QString &zone, const QString &easting,
const QString &northing)
{
bool ok;
int z = zone.left(zone.size() - 1).toInt(&ok);
if (!ok)
return Coordinates();
if (zone.right(1) < 'N')
z = -z;
int x = easting.toInt(&ok);
if (!ok)
return Coordinates();
int y = northing.toInt(&ok);
if (!ok)
return Coordinates();
Projection proj(PCS(GCS::WGS84(), Conversion(9807, UTM::setup(z), 9001)));
return proj.xy2ll(PointD(x, y));
}
bool GPSDumpParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Area> &polygons, QVector<Waypoint> &waypoints)
{
Q_UNUSED(tracks);
Q_UNUSED(routes);
Q_UNUSED(polygons);
_errorLine = 1;
_errorString.clear();
Type type = Unknown;
QRegularExpression dm("[ ]{2,}");
while (!file->atEnd()) {
QByteArray ba(file->readLine(4096).trimmed());
if (_errorLine == 1) {
if (ba == "$FormatGEO")
type = GEO;
else if (ba == "$FormatUTM")
type = UTM;
else {
_errorString = "Not a GPSDump waypoint file";
return false;
}
} else if (!ba.isEmpty()) {
QString line(ba);
QStringList fields(line.split(dm));
Coordinates c;
double ele = NAN;
QString desc;
bool ok;
if (type == UTM) {
if (fields.size() < 5) {
_errorString = "Parse error";
return false;
}
c = parseUTM(fields.at(1), fields.at(2), fields.at(3));
ele = fields.at(4).toDouble(&ok);
if (fields.size() > 5)
desc = fields.at(5);
} else {
if (fields.size() < 4) {
_errorString = "Parse error";
return false;
}
c = parseGEO(fields.at(1), fields.at(2));
ele = fields.at(3).toDouble(&ok);
if (fields.size() > 4)
desc = fields.at(4);
}
if (!c.isValid()) {
_errorString = "Invalid coordinates";
return false;
}
Waypoint w(c);
w.setName(fields.at(0));
if (ok)
w.setElevation(ele);
if (!desc.isEmpty())
w.setDescription(desc);
waypoints.append(w);
}
_errorLine++;
}
return true;
}

27
src/data/gpsdumpparser.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef GPSDUMPPARSER_H
#define GPSDUMPPARSER_H
#include "parser.h"
class GPSDumpParser : public Parser
{
public:
GPSDumpParser() : _errorLine(0) {}
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Area> &polygons, QVector<Waypoint> &waypoints);
QString errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
private:
enum Type {
Unknown,
GEO,
UTM
};
int _errorLine;
QString _errorString;
};
#endif // GPSDUMPPARSER_H

View File

@ -347,20 +347,14 @@ void AQMMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
Q_UNUSED(flags);
const Zoom &z = _zooms.at(_zoom);
qreal scale = OSM::zoom2scale(z.zoom, z.tileSize);
QRectF b(bounds());
QPoint tile = OSM::mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale) * _mapRatio, z.zoom);
QPointF tl(floor(rect.left() / tileSize())
* tileSize(), floor(rect.top() / tileSize()) * tileSize());
QSizeF s(qMin(rect.right() - tl.x(), b.width()),
qMin(rect.bottom() - tl.y(), b.height()));
Coordinates ctl(OSM::tile2ll(tile, z.zoom));
QPointF tl(ll2xy(Coordinates(ctl.lon(), -ctl.lat())));
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize());
int height = ceil(s.height() / tileSize());
QList<RenderTile> tiles;
for (int i = 0; i < width; i++) {
@ -371,13 +365,11 @@ void AQMMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
+ QString::number(t.x()) + "_" + QString::number(t.y());
if (QPixmapCache::find(key, &pm)) {
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (t.x() - tile.x()) * tileSize(),
tl.y() + (t.y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
} else {
} else
tiles.append(RenderTile(t, tileData(t), key));
}
}
}
@ -392,9 +384,8 @@ void AQMMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QPixmapCache::insert(mt.key(), pm);
QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize(),
tl.y() + (mt.xy().y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
}
}

View File

@ -243,20 +243,14 @@ void GEMFMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
Q_UNUSED(flags);
const Zoom &z = _zooms.at(_zi);
qreal scale = OSM::zoom2scale(z.level, _tileSize);
QRectF b(bounds());
QPoint tile = OSM::mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale) * _mapRatio, z.level);
QPointF tl(floor(rect.left() / tileSize())
* tileSize(), floor(rect.top() / tileSize()) * tileSize());
QSizeF s(qMin(rect.right() - tl.x(), b.width()),
qMin(rect.bottom() - tl.y(), b.height()));
Coordinates ctl(OSM::tile2ll(tile, z.level));
QPointF tl(ll2xy(Coordinates(ctl.lon(), -ctl.lat())));
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize());
int height = ceil(s.height() / tileSize());
QList<RenderTile> tiles;
for (int i = 0; i < width; i++) {
@ -267,13 +261,11 @@ void GEMFMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
+ QString::number(t.x()) + "_" + QString::number(t.y());
if (QPixmapCache::find(key, &pm)) {
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (t.x() - tile.x()) * tileSize(),
tl.y() + (t.y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
} else {
} else
tiles.append(RenderTile(t, tileData(t), key));
}
}
}
@ -288,9 +280,8 @@ void GEMFMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QPixmapCache::insert(mt.key(), pm);
QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize(),
tl.y() + (mt.xy().y() - tile.y())* tileSize());
drawTile(painter, pm, tp);
}
}

View File

@ -4,8 +4,6 @@
#include <QSqlError>
#include <QPainter>
#include <QPixmapCache>
#include <QImageReader>
#include <QBuffer>
#include <QtConcurrent>
#include "common/util.h"
#include "osm.h"
@ -14,36 +12,169 @@
#define META_TYPE(type) static_cast<QMetaType::Type>(type)
class MBTile
static RectC str2bounds(const QString &str)
{
public:
MBTile(int zoom, int scaledSize, const QPoint &xy, const QByteArray &data,
const QString &key) : _zoom(zoom), _scaledSize(scaledSize), _xy(xy),
_data(data), _key(key) {}
QStringList list(str.split(','));
if (list.size() != 4)
return RectC();
const QPoint &xy() const {return _xy;}
const QString &key() const {return _key;}
QPixmap pixmap() const {return QPixmap::fromImage(_image);}
bool lok, rok, bok, tok;
double left = list.at(0).toDouble(&lok);
double bottom = list.at(1).toDouble(&bok);
double right = list.at(2).toDouble(&rok);
double top = list.at(3).toDouble(&tok);
void load() {
QByteArray z(QString::number(_zoom).toLatin1());
return (lok && rok && bok && tok)
? RectC(Coordinates(left, top), Coordinates(right, bottom))
: RectC();
}
QBuffer buffer(&_data);
QImageReader reader(&buffer, z);
if (_scaledSize)
reader.setScaledSize(QSize(_scaledSize, _scaledSize));
reader.read(&_image);
bool MBTilesMap::getMinZoom(int &zoom)
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'minzoom'", _db);
if (query.first()) {
bool ok;
zoom = query.value(0).toString().toInt(&ok);
if (!ok || zoom < 0) {
_errorString = "Invalid minzoom metadata";
return false;
}
} else {
qWarning("%s: missing minzoom metadata", qPrintable(path()));
zoom = 0;
}
private:
int _zoom;
int _scaledSize;
QPoint _xy;
QByteArray _data;
QString _key;
QImage _image;
};
return true;
}
bool MBTilesMap::getMaxZoom(int &zoom)
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'maxzoom'", _db);
if (query.first()) {
bool ok;
zoom = query.value(0).toString().toInt(&ok);
if (!ok && zoom < 0) {
_errorString = "Invalid maxzoom metadata";
return false;
}
} else {
qWarning("%s: missing maxzoom metadata", qPrintable(path()));
zoom = 20;
}
return true;
}
bool MBTilesMap::getZooms()
{
int minZoom, maxZoom;
if (!(getMinZoom(minZoom) && getMaxZoom(maxZoom)))
return false;
for (int i = minZoom; i <= maxZoom; i++) {
QString sql = QString("SELECT zoom_level FROM tiles"
" WHERE zoom_level = %1 LIMIT 1").arg(i);
QSqlQuery query(sql, _db);
if (query.first())
_zooms.append(i);
}
_zi = _zooms.size() - 1;
return true;
}
bool MBTilesMap::getBounds()
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'bounds'", _db);
if (query.first()) {
RectC b(str2bounds(query.value(0).toString()));
if (!b.isValid()) {
_errorString = "Invalid bounds metadata";
return false;
}
_bounds = b;
} else {
qWarning("%s: missing bounds metadata", qPrintable(path()));
int z = _zooms.first();
QString sql = QString("SELECT min(tile_column), min(tile_row), "
"max(tile_column), max(tile_row) FROM tiles WHERE zoom_level = %1")
.arg(z);
QSqlQuery query(sql, _db);
query.first();
int minX = qMin((1<<z) - 1, qMax(0, query.value(0).toInt()));
int minY = qMin((1<<z) - 1, qMax(0, query.value(1).toInt()));
int maxX = qMin((1<<z) - 1, qMax(0, query.value(2).toInt())) + 1;
int maxY = qMin((1<<z) - 1, qMax(0, query.value(3).toInt())) + 1;
Coordinates tl(OSM::tile2ll(QPoint(minX, maxY), z));
Coordinates br(OSM::tile2ll(QPoint(maxX, minY), z));
// Workaround of broken zoom levels 0 and 1 due to numerical instability
tl.rlat() = qMin(tl.lat(), OSM::BOUNDS.top());
br.rlat() = qMax(br.lat(), OSM::BOUNDS.bottom());
_bounds = RectC(tl, br);
}
return true;
}
bool MBTilesMap::getTileSize()
{
QString sql("SELECT tile_data FROM tiles LIMIT 1");
QSqlQuery query(sql, _db);
query.first();
QByteArray data = query.value(0).toByteArray();
QBuffer buffer(&data);
QImageReader reader(&buffer);
QSize tileSize(reader.size());
if (!tileSize.isValid() || tileSize.width() != tileSize.height()) {
_errorString = "Unsupported/invalid tile images";
return false;
}
_tileSize = tileSize.width();
return true;
}
void MBTilesMap::getTileFormat()
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'format'", _db);
if (query.first()) {
if (query.value(0).toString() == "pbf")
_scalable = true;
} else
qWarning("%s: missing tiles format metadata", qPrintable(path()));
}
void MBTilesMap::getTilePixelRatio()
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'tilepixelratio'",
_db);
if (query.first()) {
bool ok;
double ratio = query.value(0).toString().toDouble(&ok);
if (ok)
_tileRatio = ratio;
}
}
void MBTilesMap::getName()
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'name'", _db);
if (query.first())
_name = query.value(0).toString();
else {
qWarning("%s: missing map name", qPrintable(path()));
_name = Util::file2name(path());
}
}
MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _mapRatio(1.0), _tileRatio(1.0), _scalable(false),
@ -75,90 +206,15 @@ MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent)
return;
}
{
QSqlQuery query("SELECT DISTINCT zoom_level FROM tiles"
" ORDER BY zoom_level", _db);
while (query.next())
_zooms.append(query.value(0).toInt());
if (_zooms.isEmpty()) {
_errorString = "Empty tile set";
return;
}
if (_zooms.first() < 0) {
_errorString = "Invalid zoom levels";
return;
}
}
_zi = _zooms.size() - 1;
{
int z = _zooms.first();
QString sql = QString("SELECT min(tile_column), min(tile_row), "
"max(tile_column), max(tile_row) FROM tiles WHERE zoom_level = %1")
.arg(z);
QSqlQuery query(sql, _db);
query.first();
int minX = qMin((1<<z) - 1, qMax(0, query.value(0).toInt()));
int minY = qMin((1<<z) - 1, qMax(0, query.value(1).toInt()));
int maxX = qMin((1<<z) - 1, qMax(0, query.value(2).toInt())) + 1;
int maxY = qMin((1<<z) - 1, qMax(0, query.value(3).toInt())) + 1;
Coordinates tl(OSM::tile2ll(QPoint(minX, maxY), z));
Coordinates br(OSM::tile2ll(QPoint(maxX, minY), z));
// Workaround of broken zoom levels 0 and 1 due to numerical instability
tl.rlat() = qMin(tl.lat(), OSM::BOUNDS.top());
br.rlat() = qMax(br.lat(), OSM::BOUNDS.bottom());
_bounds = RectC(tl, br);
}
{
QString sql = QString("SELECT tile_data FROM tiles LIMIT 1");
QSqlQuery query(sql, _db);
query.first();
QByteArray data = query.value(0).toByteArray();
QBuffer buffer(&data);
QImageReader reader(&buffer);
QSize tileSize(reader.size());
if (!tileSize.isValid() || tileSize.width() != tileSize.height()) {
_errorString = "Unsupported/invalid tile images";
return;
}
_tileSize = tileSize.width();
}
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'format'", _db);
if (query.first()) {
if (query.value(0).toString() == "pbf")
_scalable = true;
} else
qWarning("%s: missing tiles format", qPrintable(fileName));
}
{
QSqlQuery query("SELECT value FROM metadata WHERE name = 'name'", _db);
if (query.first())
_name = query.value(0).toString();
else {
qWarning("%s: missing map name", qPrintable(fileName));
_name = Util::file2name(fileName);
}
}
{
QSqlQuery query(
"SELECT value FROM metadata WHERE name = 'tilepixelratio'", _db);
if (query.first()) {
bool ok;
_tileRatio = query.value(0).toString().toDouble(&ok);
if (!ok) {
_errorString = "Invalid tile pixel ratio";
return;
}
}
}
if (!getZooms())
return;
if (!getBounds())
return;
if (!getTileSize())
return;
getTileFormat();
getTilePixelRatio();
getName();
_db.close();
@ -183,6 +239,7 @@ void MBTilesMap::load(const Projection &in, const Projection &out,
void MBTilesMap::unload()
{
cancelJobs(true);
_db.close();
}
@ -219,12 +276,16 @@ qreal MBTilesMap::resolution(const QRectF &rect)
int MBTilesMap::zoomIn()
{
cancelJobs(false);
_zi = qMin(_zi + 1, _zooms.size() - 1);
return _zi;
}
int MBTilesMap::zoomOut()
{
cancelJobs(false);
_zi = qMax(_zi - 1, 0);
return _zi;
}
@ -260,25 +321,65 @@ QByteArray MBTilesMap::tileData(int zoom, const QPoint &tile) const
return QByteArray();
}
bool MBTilesMap::isRunning(const QString &key) const
{
for (int i = 0; i < _jobs.size(); i++) {
const QList<MBTile> &tiles = _jobs.at(i)->tiles();
for (int j = 0; j < tiles.size(); j++)
if (tiles.at(j).key() == key)
return true;
}
return false;
}
void MBTilesMap::runJob(MBTilesMapJob *job)
{
_jobs.append(job);
connect(job, &MBTilesMapJob::finished, this, &MBTilesMap::jobFinished);
job->run();
}
void MBTilesMap::removeJob(MBTilesMapJob *job)
{
_jobs.removeOne(job);
job->deleteLater();
}
void MBTilesMap::jobFinished(MBTilesMapJob *job)
{
const QList<MBTile> &tiles = job->tiles();
for (int i = 0; i < tiles.size(); i++) {
const MBTile &mt = tiles.at(i);
if (!mt.pixmap().isNull())
QPixmapCache::insert(mt.key(), mt.pixmap());
}
removeJob(job);
emit tilesLoaded();
}
void MBTilesMap::cancelJobs(bool wait)
{
for (int i = 0; i < _jobs.size(); i++)
_jobs.at(i)->cancel(wait);
}
void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
Q_UNUSED(flags);
int zoom = _zooms.at(_zi);
qreal scale = OSM::zoom2scale(zoom, _tileSize);
QRectF b(bounds());
QPoint tile = OSM::mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale) * coordinatesRatio(), zoom);
QPointF tl(floor(rect.left() / tileSize())
* tileSize(), floor(rect.top() / tileSize()) * tileSize());
QSizeF s(qMin(rect.right() - tl.x(), b.width()),
qMin(rect.bottom() - tl.y(), b.height()));
Coordinates ctl(OSM::tile2ll(tile, zoom));
QPointF tl(ll2xy(Coordinates(ctl.lon(), -ctl.lat())));
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize());
int height = ceil(s.height() / tileSize());
QList<MBTile> tiles;
for (int i = 0; i < width; i++) {
@ -288,10 +389,12 @@ void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QString key = path() + "-" + QString::number(zoom) + "_"
+ QString::number(t.x()) + "_" + QString::number(t.y());
if (isRunning(key))
continue;
if (QPixmapCache::find(key, &pm)) {
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (t.x() - tile.x()) * tileSize(),
tl.y() + (t.y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
} else {
tiles.append(MBTile(zoom, _scaledSize, t, tileData(zoom, t),
@ -300,21 +403,25 @@ void MBTilesMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
}
}
QFuture<void> future = QtConcurrent::map(tiles, &MBTile::load);
future.waitForFinished();
if (!tiles.isEmpty()) {
if (flags & Map::Block || !_scalable) {
QFuture<void> future = QtConcurrent::map(tiles, &MBTile::load);
future.waitForFinished();
for (int i = 0; i < tiles.size(); i++) {
const MBTile &mt = tiles.at(i);
QPixmap pm(mt.pixmap());
if (pm.isNull())
continue;
for (int i = 0; i < tiles.size(); i++) {
const MBTile &mt = tiles.at(i);
QPixmap pm(mt.pixmap());
if (pm.isNull())
continue;
QPixmapCache::insert(mt.key(), pm);
QPixmapCache::insert(mt.key(), pm);
QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y())
* tileSize());
drawTile(painter, pm, tp);
QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize(),
tl.y() + (mt.xy().y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
}
} else
runJob(new MBTilesMapJob(tiles));
}
}

View File

@ -3,10 +3,80 @@
#include <QSqlDatabase>
#include <QVector>
#include <QImageReader>
#include <QBuffer>
#include <QPixmap>
#include <QtConcurrent>
#include "map.h"
class MBTile
{
public:
MBTile(int zoom, int scaledSize, const QPoint &xy, const QByteArray &data,
const QString &key) : _zoom(zoom), _scaledSize(scaledSize), _xy(xy),
_data(data), _key(key) {}
const QPoint &xy() const {return _xy;}
const QString &key() const {return _key;}
const QPixmap &pixmap() const {return _pixmap;}
void load() {
QByteArray z(QString::number(_zoom).toLatin1());
QBuffer buffer(&_data);
QImageReader reader(&buffer, z);
if (_scaledSize)
reader.setScaledSize(QSize(_scaledSize, _scaledSize));
_pixmap = QPixmap::fromImage(reader.read());
}
private:
int _zoom;
int _scaledSize;
QPoint _xy;
QByteArray _data;
QString _key;
QPixmap _pixmap;
};
class MBTilesMapJob : public QObject
{
Q_OBJECT
public:
MBTilesMapJob(const QList<MBTile> &tiles) : _tiles(tiles) {}
void run()
{
connect(&_watcher, &QFutureWatcher<void>::finished, this,
&MBTilesMapJob::handleFinished);
_future = QtConcurrent::map(_tiles, &MBTile::load);
_watcher.setFuture(_future);
}
void cancel(bool wait)
{
_future.cancel();
if (wait)
_future.waitForFinished();
}
const QList<MBTile> &tiles() const {return _tiles;}
signals:
void finished(MBTilesMapJob *job);
private slots:
void handleFinished() {emit finished(this);}
private:
QFutureWatcher<void> _watcher;
QFuture<void> _future;
QList<MBTile> _tiles;
};
class MBTilesMap : public Map
{
Q_OBJECT
public:
MBTilesMap(const QString &fileName, QObject *parent = 0);
@ -36,12 +106,27 @@ public:
static Map *create(const QString &path, const Projection &proj, bool *isDir);
private slots:
void jobFinished(MBTilesMapJob *job);
private:
bool getMinZoom(int &zoom);
bool getMaxZoom(int &zoom);
bool getZooms();
bool getBounds();
bool getTileSize();
void getTileFormat();
void getTilePixelRatio();
void getName();
qreal tileSize() const;
qreal coordinatesRatio() const;
qreal imageRatio() const;
QByteArray tileData(int zoom, const QPoint &tile) const;
void drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp);
bool isRunning(const QString &key) const;
void runJob(MBTilesMapJob *job);
void removeJob(MBTilesMapJob *job);
void cancelJobs(bool wait);
QSqlDatabase _db;
@ -54,6 +139,8 @@ private:
bool _scalable;
int _scaledSize;
QList<MBTilesMapJob*> _jobs;
bool _valid;
QString _errorString;
};

View File

@ -1,4 +1,3 @@
#include <QtCore>
#include <QPainter>
#include <QDir>
#include "common/rectc.h"
@ -101,15 +100,11 @@ qreal OnlineMap::tileSize() const
void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
qreal scale = OSM::zoom2scale(_zoom, _tileSize);
QRectF b(bounds());
QPoint tile = OSM::mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale) * coordinatesRatio(), _zoom);
QPointF tl(floor(rect.left() / tileSize())
* tileSize(), floor(rect.top() / tileSize()) * tileSize());
QSizeF s(qMin(rect.right() - tl.x(), b.width()),
qMin(rect.bottom() - tl.y(), b.height()));
Coordinates ctl(OSM::tile2ll(tile, _zoom));
QPointF tl(ll2xy(Coordinates(ctl.lon(), -ctl.lat())));
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize());
int height = ceil(s.height() / tileSize());
@ -127,9 +122,10 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
for (int i = 0; i < tiles.count(); i++) {
FetchTile &t = tiles[i];
QPointF tp(qMax(tl.x(), b.left()) + (t.xy().x() - tile.x()) * tileSize(),
qMax(tl.y(), b.top()) + ((_invertY ? (1<<_zoom) - t.xy().y() - 1 :
t.xy().y()) - tile.y()) * tileSize());
QPointF tp(tl.x() + (t.xy().x() - tile.x()) * tileSize(),
tl.y() + ((_invertY
? (1<<_zoom) - t.xy().y() - 1
: t.xy().y()) - tile.y()) * tileSize());
if (!t.pixmap().isNull()) {
t.pixmap().setDevicePixelRatio(imageRatio());

View File

@ -238,20 +238,14 @@ void OsmdroidMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
Q_UNUSED(flags);
qreal scale = OSM::zoom2scale(_zoom, _tileSize);
QRectF b(bounds());
QPoint tile = OSM::mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale) * _mapRatio, _zoom);
QPointF tl(floor(rect.left() / tileSize())
* tileSize(), floor(rect.top() / tileSize()) * tileSize());
QSizeF s(qMin(rect.right() - tl.x(), b.width()),
qMin(rect.bottom() - tl.y(), b.height()));
Coordinates ctl(OSM::tile2ll(tile, _zoom));
QPointF tl(ll2xy(Coordinates(ctl.lon(), -ctl.lat())));
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize());
int height = ceil(s.height() / tileSize());
QList<RenderTile> tiles;
for (int i = 0; i < width; i++) {
@ -262,13 +256,11 @@ void OsmdroidMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
+ QString::number(t.x()) + "_" + QString::number(t.y());
if (QPixmapCache::find(key, &pm)) {
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (t.x() - tile.x()) * tileSize(),
tl.y() + (t.y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
} else {
} else
tiles.append(RenderTile(t, tileData(_zoom, t), key));
}
}
}
@ -283,9 +275,8 @@ void OsmdroidMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QPixmapCache::insert(mt.key(), pm);
QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize(),
tl.y() + (mt.xy().y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
}
}

View File

@ -185,20 +185,14 @@ void SqliteMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
Q_UNUSED(flags);
qreal scale = OSM::zoom2scale(_zoom, _tileSize);
QRectF b(bounds());
QPoint tile = OSM::mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale) * _mapRatio, _zoom);
QPointF tl(floor(rect.left() / tileSize())
* tileSize(), floor(rect.top() / tileSize()) * tileSize());
QSizeF s(qMin(rect.right() - tl.x(), b.width()),
qMin(rect.bottom() - tl.y(), b.height()));
Coordinates ctl(OSM::tile2ll(tile, _zoom));
QPointF tl(ll2xy(Coordinates(ctl.lon(), -ctl.lat())));
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / tileSize());
int height = ceil(s.height() / tileSize());
QList<RenderTile> tiles;
for (int i = 0; i < width; i++) {
@ -209,13 +203,11 @@ void SqliteMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
+ QString::number(t.x()) + "_" + QString::number(t.y());
if (QPixmapCache::find(key, &pm)) {
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (t.y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (t.x() - tile.x()) * tileSize(),
tl.y() + (t.y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
} else {
} else
tiles.append(RenderTile(t, tileData(_zoom, t), key));
}
}
}
@ -230,9 +222,8 @@ void SqliteMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QPixmapCache::insert(mt.key(), pm);
QPointF tp(qMax(tl.x(), b.left()) + (mt.xy().x() - tile.x())
* tileSize(), qMax(tl.y(), b.top()) + (mt.xy().y() - tile.y())
* tileSize());
QPointF tp(tl.x() + (mt.xy().x() - tile.x()) * tileSize(),
tl.y() + (mt.xy().y() - tile.y()) * tileSize());
drawTile(painter, pm, tp);
}
}

View File

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/icons/GPXSee">
<file alias="actions/22x22/document-open.svg">icons/GUI/FlatColor/actions/22x22/document-open.svg</file>
<file alias="actions/22x22/document-open-recent.svg">icons/GUI/FlatColor/actions/22x22/document-open-recent.svg</file>
<file alias="actions/22x22/document-open-folder.svg">icons/GUI/FlatColor/actions/22x22/document-open-folder.svg</file>
<file alias="actions/22x22/document-export.svg">icons/GUI/FlatColor/actions/22x22/document-export.svg</file>
<file alias="actions/22x22/document-print.svg">icons/GUI/FlatColor/actions/22x22/document-print.svg</file>

View File

@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/icons/GPXSee">
<file alias="actions/22x22/document-open.svg">icons/GUI/Papirus/actions/22x22/document-open.svg</file>
<file alias="actions/22x22/document-open-recent.svg">icons/GUI/Papirus/actions/22x22/document-open-recent.svg</file>
<file alias="actions/22x22/document-open-folder.svg">icons/GUI/Papirus/actions/22x22/document-open-folder.svg</file>
<file alias="actions/22x22/document-export.svg">icons/GUI/Papirus/actions/22x22/document-export.svg</file>
<file alias="actions/22x22/document-print.svg">icons/GUI/Papirus/actions/22x22/document-print.svg</file>