mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-07-01 21:39:15 +02:00
Compare commits
88 Commits
Author | SHA1 | Date | |
---|---|---|---|
083dd39bef | |||
bd64ca4f57 | |||
b382a5e674 | |||
20687a1df7 | |||
bb82750e9b | |||
51e6058960 | |||
db3f111815 | |||
219311577f | |||
26d4770f47 | |||
db0d9ceffb | |||
fc9b01480c | |||
cd8f415615 | |||
824393879b | |||
f79f555d6f | |||
13e9a50d59 | |||
f1667e70b5 | |||
1ce347230b | |||
d4f3e293df | |||
7492b74aed | |||
71dbb176bb | |||
301107add1 | |||
60ae4c0268 | |||
bd76e508ae | |||
e9f7642cde | |||
5c73cb55cb | |||
c39298000d | |||
e17d7d4a8f | |||
65b74b146d | |||
b937b9f2cb | |||
d7fe0fa9bf | |||
9657104f50 | |||
5a692c71a8 | |||
e8ede272ae | |||
39a8a144dd | |||
02700a485d | |||
99ea19e35a | |||
a6bb0f1520 | |||
0a74684713 | |||
332a4d9393 | |||
db98f381b5 | |||
31a600638f | |||
2ff18bc373 | |||
c2e301f4e8 | |||
a25303fe98 | |||
2bee8656a4 | |||
4a31f6f76d | |||
eaef588443 | |||
87a808265e | |||
a39cef2abd | |||
960827e92b | |||
7de1b84f77 | |||
4b1fa13429 | |||
f10857eddc | |||
781fc22113 | |||
9ced0fd3a7 | |||
3e71d1b785 | |||
491bf5614b | |||
fc873719e1 | |||
99c3edddfd | |||
fde8ad620b | |||
7855c69729 | |||
8749be9103 | |||
6f9f49a435 | |||
cfdb12c4ae | |||
9436f98023 | |||
905f80b1ce | |||
4ef1fa77f8 | |||
d483cd35cd | |||
c2b09df118 | |||
7e537d819a | |||
b68567f000 | |||
ca8ff6c7ee | |||
e9c15ef956 | |||
e39f0881ab | |||
6f05d38d31 | |||
9cb91a35ef | |||
bb0073d7e7 | |||
97d646bb19 | |||
3fcf04daf7 | |||
3daab92b84 | |||
fc555fd7d0 | |||
57741f8c2d | |||
6eb3a4f7de | |||
295a89b3cc | |||
dc3fdc4c3b | |||
3867b723a9 | |||
d7fc400d73 | |||
f8f6859e7d |
@ -1,4 +1,4 @@
|
||||
version: 5.18.{build}
|
||||
version: 6.0.{build}
|
||||
configuration: Release
|
||||
platform: Any CPU
|
||||
environment:
|
||||
|
12
README.md
12
README.md
@ -3,7 +3,7 @@ GPXSee is a Qt-based GPS log file viewer and analyzer that supports GPX, TCX,
|
||||
KML, FIT, IGC, NMEA, SLF, LOC and OziExplorer files.
|
||||
|
||||
## Features
|
||||
* [User-definable online maps](https://github.com/tumic0/GPXSee-maps) (OSM/Google tiles, WMTS, WMS).
|
||||
* User-definable online maps (OSM/Google tiles, WMTS, WMS).
|
||||
* Offline maps (OziExplorer maps, TrekBuddy maps/atlases, Garmin JNX maps, GeoTIFF images).
|
||||
* Elevation, speed, heart rate, cadence, power, temperature and gear ratio/shifts graphs.
|
||||
* Support for multiple tracks in one view.
|
||||
@ -17,10 +17,15 @@ KML, FIT, IGC, NMEA, SLF, LOC and OziExplorer files.
|
||||

|
||||
|
||||
## Build
|
||||
Build requirements:
|
||||
* Qt 4.8 or QT 5.x (Qt >= 5.10.1 recommended for all features)
|
||||
* C++03 compiler (tested: msvc2015, gcc >= 4.8, clang/Apple LLVM version 8.1.0)
|
||||
|
||||
Build steps:
|
||||
```shell
|
||||
lrelease gpxsee.pro
|
||||
qmake gpxsee.pro
|
||||
make
|
||||
make # nmake on windows
|
||||
```
|
||||
|
||||
## Download
|
||||
@ -33,5 +38,8 @@ make
|
||||
## Homepage
|
||||
http://www.gpxsee.org
|
||||
|
||||
## Maps
|
||||
[GPXSee maps repository](https://github.com/tumic0/GPXSee-maps)
|
||||
|
||||
## Translations
|
||||
GPXSee uses [Weblate](https://hosted.weblate.org/projects/gpxsee) for translations.
|
||||
|
19
gpxsee.pro
19
gpxsee.pro
@ -1,9 +1,10 @@
|
||||
TARGET = GPXSee
|
||||
VERSION = 5.18
|
||||
VERSION = 6.0
|
||||
|
||||
QT += core \
|
||||
gui \
|
||||
network
|
||||
network \
|
||||
sql
|
||||
greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
QT += widgets
|
||||
QT += printsupport
|
||||
@ -142,7 +143,9 @@ HEADERS += src/config.h \
|
||||
src/data/slfparser.h \
|
||||
src/map/geotiffmap.h \
|
||||
src/map/image.h \
|
||||
src/common/greatcircle.h
|
||||
src/common/greatcircle.h \
|
||||
src/map/mbtilesmap.h \
|
||||
src/map/osm.h
|
||||
SOURCES += src/main.cpp \
|
||||
src/common/coordinates.cpp \
|
||||
src/common/rectc.cpp \
|
||||
@ -248,10 +251,13 @@ SOURCES += src/main.cpp \
|
||||
src/data/slfparser.cpp \
|
||||
src/map/geotiffmap.cpp \
|
||||
src/map/image.cpp \
|
||||
src/common/greatcircle.cpp
|
||||
src/common/greatcircle.cpp \
|
||||
src/map/mbtilesmap.cpp \
|
||||
src/map/osm.cpp
|
||||
|
||||
RESOURCES += gpxsee.qrc
|
||||
TRANSLATIONS = lang/gpxsee_cs.ts \
|
||||
TRANSLATIONS = lang/gpxsee_en.ts \
|
||||
lang/gpxsee_cs.ts \
|
||||
lang/gpxsee_sv.ts \
|
||||
lang/gpxsee_de.ts \
|
||||
lang/gpxsee_ru.ts \
|
||||
@ -263,7 +269,8 @@ macx {
|
||||
ICON = icons/gpxsee.icns
|
||||
QMAKE_INFO_PLIST = pkg/Info.plist
|
||||
LOCALE.path = Contents/Resources/translations
|
||||
LOCALE.files = lang/gpxsee_cs.qm \
|
||||
LOCALE.files = lang/gpxsee_en.qm \
|
||||
lang/gpxsee_cs.qm \
|
||||
lang/gpxsee_de.qm \
|
||||
lang/gpxsee_fi.qm \
|
||||
lang/gpxsee_fr.qm \
|
||||
|
@ -958,32 +958,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Podporované soubory</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation>MBTiles mapy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation>Garmin JNX mapy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>OziExplorer mapy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>TrekBuddy mapy/atlasy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>GeoTIFF obrázky</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Online mapové zdroje</translation>
|
||||
</message>
|
||||
|
1747
lang/gpxsee_da.ts
Normal file
1747
lang/gpxsee_da.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -957,32 +957,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Unterstütze Dateien</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation>MBTiles Karten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation>Garmin JNX Karten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>OziExplorer Karten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>TrekBuddy Karten/Atlanten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>GeoTIFF Bilder</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Online-Kartenquellen</translation>
|
||||
</message>
|
||||
@ -1084,22 +1089,22 @@
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="45"/>
|
||||
<source>High-resolution</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Hohe Auflösung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="46"/>
|
||||
<source>Standard</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Standardmäßig</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="51"/>
|
||||
<source>Non-HiDPI maps are loaded as HiDPI maps. The map is sharp but map objects are small/hard to read.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Nicht-HiDPI-Karten werden als HiDPI-Karten geladen. Die Karte ist scharf, aber die Kartenobjekte sind klein / schwer zu lesen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="53"/>
|
||||
<source>Non-HiDPI maps are loaded such as they are. Map objects have the expected size but the map is blurry.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Nicht-HiDPI-Karten werden so geladen, wie sie sind. Kartenobjekte haben die erwartete Größe, aber die Karte ist verschwommen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="86"/>
|
||||
@ -1109,7 +1114,7 @@
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="88"/>
|
||||
<source>HiDPI display mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>HiDPI-Anzeigemodus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="122"/>
|
||||
@ -1411,7 +1416,7 @@
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="532"/>
|
||||
<source>Maps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Karten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/optionsdialog.cpp" line="533"/>
|
||||
|
14
lang/gpxsee_en.ts
Normal file
14
lang/gpxsee_en.ts
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="en_US">
|
||||
<context>
|
||||
<name>GUI</name>
|
||||
<message numerus="yes">
|
||||
<source>%n files</source>
|
||||
<translation>
|
||||
<numerusform>%n file</numerusform>
|
||||
<numerusform>%n files</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
@ -957,32 +957,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Tuetut tiedostot</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation>MBTiles -kartat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation>Garmin JNX -kartat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>OziExplorer -kartat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>TrekBuddy -kartat/kartastot</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>GeoTIFF -kuvat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Online-karttojen lähteet</translation>
|
||||
</message>
|
||||
|
@ -957,32 +957,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Formats pris en charge</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>Cartes OziExplorer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>Cartes ou atlas TrekBuddy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>Images GeoTIFF</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Cartes en ligne</translation>
|
||||
</message>
|
||||
|
1747
lang/gpxsee_no.ts
Normal file
1747
lang/gpxsee_no.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -958,32 +958,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Obsługiwane pliki</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>Mapy OziExplorer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>Mapy/atlasy TrekBuddy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>Obrazy GeoTIFF</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Źródła map online</translation>
|
||||
</message>
|
||||
|
@ -958,32 +958,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Все поддерживаемые файлы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation>MBTiles карты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation>Garmin JNX карты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>OziExplorer карты</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>TrekBuddy карты/атласы</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>GeoTIFF изображения</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Источники онлайн карт</translation>
|
||||
</message>
|
||||
|
@ -655,9 +655,11 @@
|
||||
<message numerus="yes">
|
||||
<location filename="../src/GUI/gui.cpp" line="1326"/>
|
||||
<source>%n files</source>
|
||||
<translation><numerusform>%n fil</numerusform>
|
||||
<numerusform>%n filer</numerusform>
|
||||
</translation></message>
|
||||
<translation>
|
||||
<numerusform>%n fil</numerusform>
|
||||
<numerusform>%n filer</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/GUI/gui.cpp" line="1002"/>
|
||||
<location filename="../src/GUI/gui.cpp" line="1006"/>
|
||||
@ -955,32 +957,37 @@
|
||||
<context>
|
||||
<name>MapList</name>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="114"/>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<source>Supported files</source>
|
||||
<translation>Filer som stöds</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="115"/>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<source>MBTiles maps</source>
|
||||
<translation>MBTiles-kartor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="120"/>
|
||||
<source>Garmin JNX maps</source>
|
||||
<translation>Garmin JNX-kartor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="116"/>
|
||||
<location filename="../src/map/maplist.cpp" line="121"/>
|
||||
<source>OziExplorer maps</source>
|
||||
<translation>OziExplorer-kartor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="117"/>
|
||||
<location filename="../src/map/maplist.cpp" line="122"/>
|
||||
<source>TrekBuddy maps/atlases</source>
|
||||
<translation>TrekBuddy-kartor/-atlaser</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="118"/>
|
||||
<location filename="../src/map/maplist.cpp" line="123"/>
|
||||
<source>GeoTIFF images</source>
|
||||
<translation>GeoTIFF-bilder</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/map/maplist.cpp" line="119"/>
|
||||
<location filename="../src/map/maplist.cpp" line="124"/>
|
||||
<source>Online map sources</source>
|
||||
<translation>Online-kartkällor</translation>
|
||||
</message>
|
||||
|
@ -30,7 +30,7 @@
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "5.18"
|
||||
!define VERSION "6.0"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}.exe"
|
||||
@ -153,10 +153,12 @@ Section "QT framework" SEC_QT
|
||||
File "Qt5Widgets.dll"
|
||||
File "Qt5PrintSupport.dll"
|
||||
File "Qt5Network.dll"
|
||||
File "Qt5Sql.dll"
|
||||
File /r "platforms"
|
||||
File /r "imageformats"
|
||||
File /r "printsupport"
|
||||
File /r "styles"
|
||||
File /r "sqldrivers"
|
||||
|
||||
SectionEnd
|
||||
|
||||
@ -270,4 +272,4 @@ LangString DESC_LOCALIZATION ${LANG_ENGLISH} \
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_MSVC} $(DESC_MSVC)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APP} $(DESC_APP)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_LOCALIZATION} $(DESC_LOCALIZATION)
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
@ -30,7 +30,7 @@
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "5.18"
|
||||
!define VERSION "6.0"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}_x64.exe"
|
||||
@ -160,10 +160,12 @@ Section "QT framework" SEC_QT
|
||||
File "Qt5Widgets.dll"
|
||||
File "Qt5PrintSupport.dll"
|
||||
File "Qt5Network.dll"
|
||||
File "Qt5Sql.dll"
|
||||
File /r "platforms"
|
||||
File /r "imageformats"
|
||||
File /r "printsupport"
|
||||
File /r "styles"
|
||||
File /r "sqldrivers"
|
||||
|
||||
SectionEnd
|
||||
|
||||
@ -273,4 +275,4 @@ LangString DESC_LOCALIZATION ${LANG_ENGLISH} \
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_MSVC} $(DESC_MSVC)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APP} $(DESC_APP)
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_LOCALIZATION} $(DESC_LOCALIZATION)
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
@ -49,9 +49,26 @@ void PathItem::updateShape()
|
||||
|
||||
void PathItem::addSegment(const Coordinates &c1, const Coordinates &c2)
|
||||
{
|
||||
if (fabs(c1.lon() - c2.lon()) > 180.0)
|
||||
_painterPath.moveTo(_map->ll2xy(c2));
|
||||
else
|
||||
if (fabs(c1.lon() - c2.lon()) > 180.0) {
|
||||
QPointF p;
|
||||
|
||||
if (c2.lon() < 0) {
|
||||
QLineF l(QPointF(c1.lon(), c1.lat()), QPointF(c2.lon() + 360,
|
||||
c2.lat()));
|
||||
QLineF dl(QPointF(180, -90), QPointF(180, 90));
|
||||
l.intersect(dl, &p);
|
||||
_painterPath.lineTo(_map->ll2xy(Coordinates(180, p.y())));
|
||||
_painterPath.moveTo(_map->ll2xy(Coordinates(-180, p.y())));
|
||||
} else {
|
||||
QLineF l(QPointF(c1.lon(), c1.lat()), QPointF(c2.lon() - 360,
|
||||
c2.lat()));
|
||||
QLineF dl(QPointF(-180, -90), QPointF(-180, 90));
|
||||
l.intersect(dl, &p);
|
||||
_painterPath.lineTo(_map->ll2xy(Coordinates(-180, p.y())));
|
||||
_painterPath.moveTo(_map->ll2xy(Coordinates(180, p.y())));
|
||||
}
|
||||
_painterPath.lineTo(_map->ll2xy(c2));
|
||||
} else
|
||||
_painterPath.lineTo(_map->ll2xy(c2));
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@ public:
|
||||
void setMin(int min) {_min = min;}
|
||||
void setMax(int max) {_max = max;}
|
||||
|
||||
bool contains(int val) const {return (val >= _min && val <= _max);}
|
||||
|
||||
private:
|
||||
int _min, _max;
|
||||
};
|
||||
|
@ -23,6 +23,11 @@ public:
|
||||
{return Coordinates((_tl.lon() + _br.lon()) / 2.0,
|
||||
(_tl.lat() + _br.lat()) / 2.0);}
|
||||
|
||||
double top() const {return _tl.lat();}
|
||||
double bottom() const {return _br.lat();}
|
||||
double left() const {return _tl.lon();}
|
||||
double right() const {return _br.lon();}
|
||||
|
||||
RectC operator|(const RectC &r) const;
|
||||
RectC &operator|=(const RectC &r) {*this = *this | r; return *this;}
|
||||
RectC operator&(const RectC &r) const;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "jnxmap.h"
|
||||
#include "geotiffmap.h"
|
||||
#include "mapsource.h"
|
||||
#include "mbtilesmap.h"
|
||||
#include "maplist.h"
|
||||
|
||||
|
||||
@ -56,6 +57,8 @@ bool MapList::loadFile(const QString &path, bool *atlas, bool dir)
|
||||
return loadMap(new JNXMap(path, this), path, dir);
|
||||
else if (suffix == "tif" || suffix == "tiff")
|
||||
return loadMap(new GeoTIFFMap(path, this), path, dir);
|
||||
else if (suffix == "mbtiles")
|
||||
return loadMap(new MBTilesMap(path, this), path, dir);
|
||||
else
|
||||
return loadMap(new OziMap(path, this), path, dir);
|
||||
}
|
||||
@ -111,7 +114,9 @@ void MapList::clear()
|
||||
QString MapList::formats()
|
||||
{
|
||||
return
|
||||
tr("Supported files") + " (*.jnx *.map *.tar *.tba *.tif *.tiff *.xml);;"
|
||||
tr("Supported files")
|
||||
+ " (*.jnx *.map *.mbtiles *.tar *.tba *.tif *.tiff *.xml);;"
|
||||
+ tr("MBTiles maps") + " (*.mbtiles);;"
|
||||
+ tr("Garmin JNX maps") + " (*.jnx);;"
|
||||
+ tr("OziExplorer maps") + " (*.map);;"
|
||||
+ tr("TrekBuddy maps/atlases") + " (*.tar *.tba);;"
|
||||
@ -123,6 +128,6 @@ QStringList MapList::filter()
|
||||
{
|
||||
QStringList filter;
|
||||
filter << "*.jnx" << "*.map" << "*.tba" << "*.tar" << "*.xml" << "*.tif"
|
||||
<< "*.tiff";
|
||||
<< "*.tiff" << "*.mbtiles";
|
||||
return filter;
|
||||
}
|
||||
|
@ -4,19 +4,12 @@
|
||||
#include "onlinemap.h"
|
||||
#include "wmtsmap.h"
|
||||
#include "wmsmap.h"
|
||||
#include "osm.h"
|
||||
#include "mapsource.h"
|
||||
|
||||
#define ZOOM_MAX 19
|
||||
#define ZOOM_MIN 0
|
||||
#define BOUNDS_LEFT -180
|
||||
#define BOUNDS_TOP 85.0511
|
||||
#define BOUNDS_RIGHT 180
|
||||
#define BOUNDS_BOTTOM -85.0511
|
||||
|
||||
|
||||
MapSource::Config::Config() : type(OSM), zooms(ZOOM_MIN, ZOOM_MAX),
|
||||
bounds(Coordinates(BOUNDS_LEFT, BOUNDS_TOP), Coordinates(BOUNDS_RIGHT,
|
||||
BOUNDS_BOTTOM)), format("image/png"), rest(false), tileRatio(1.0) {}
|
||||
MapSource::Config::Config() : type(OSM), zooms(osm::zooms), bounds(osm::bounds),
|
||||
format("image/png"), rest(false), tileRatio(1.0) {}
|
||||
|
||||
|
||||
static CoordinateSystem coordinateSystem(QXmlStreamReader &reader)
|
||||
@ -38,21 +31,21 @@ Range MapSource::zooms(QXmlStreamReader &reader)
|
||||
|
||||
if (attr.hasAttribute("min")) {
|
||||
min = attr.value("min").toString().toInt(&res);
|
||||
if (!res || (min < ZOOM_MIN || min > ZOOM_MAX)) {
|
||||
if (!res || !osm::zooms.contains(min)) {
|
||||
reader.raiseError("Invalid minimal zoom level");
|
||||
return Range();
|
||||
}
|
||||
} else
|
||||
min = ZOOM_MIN;
|
||||
min = osm::zooms.min();
|
||||
|
||||
if (attr.hasAttribute("max")) {
|
||||
max = attr.value("max").toString().toInt(&res);
|
||||
if (!res || (max < ZOOM_MIN || max > ZOOM_MAX)) {
|
||||
if (!res || !osm::zooms.contains(max)) {
|
||||
reader.raiseError("Invalid maximal zoom level");
|
||||
return Range();
|
||||
}
|
||||
} else
|
||||
max = ZOOM_MAX;
|
||||
max = osm::zooms.max();
|
||||
|
||||
if (min > max) {
|
||||
reader.raiseError("Invalid maximal/minimal zoom level combination");
|
||||
@ -70,39 +63,41 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
||||
|
||||
if (attr.hasAttribute("top")) {
|
||||
top = attr.value("top").toString().toDouble(&res);
|
||||
if (!res || (top < BOUNDS_BOTTOM || top > BOUNDS_TOP)) {
|
||||
if (!res || (top < osm::bounds.bottom() || top > osm::bounds.top())) {
|
||||
reader.raiseError("Invalid bounds top value");
|
||||
return RectC();
|
||||
}
|
||||
} else
|
||||
top = BOUNDS_TOP;
|
||||
top = osm::bounds.top();
|
||||
|
||||
if (attr.hasAttribute("bottom")) {
|
||||
bottom = attr.value("bottom").toString().toDouble(&res);
|
||||
if (!res || (bottom < BOUNDS_BOTTOM || bottom > BOUNDS_TOP)) {
|
||||
if (!res || (bottom < osm::bounds.bottom()
|
||||
|| bottom > osm::bounds.top())) {
|
||||
reader.raiseError("Invalid bounds bottom value");
|
||||
return RectC();
|
||||
}
|
||||
} else
|
||||
bottom = BOUNDS_BOTTOM;
|
||||
bottom = osm::bounds.bottom();
|
||||
|
||||
if (attr.hasAttribute("left")) {
|
||||
left = attr.value("left").toString().toDouble(&res);
|
||||
if (!res || (left < BOUNDS_LEFT || left > BOUNDS_RIGHT)) {
|
||||
if (!res || (left < osm::bounds.left() || left > osm::bounds.right())) {
|
||||
reader.raiseError("Invalid bounds left value");
|
||||
return RectC();
|
||||
}
|
||||
} else
|
||||
left = BOUNDS_LEFT;
|
||||
left = osm::bounds.left();
|
||||
|
||||
if (attr.hasAttribute("right")) {
|
||||
right = attr.value("right").toString().toDouble(&res);
|
||||
if (!res || (right < BOUNDS_LEFT || right > BOUNDS_RIGHT)) {
|
||||
if (!res || (right < osm::bounds.left()
|
||||
|| right > osm::bounds.right())) {
|
||||
reader.raiseError("Invalid bounds right value");
|
||||
return RectC();
|
||||
}
|
||||
} else
|
||||
right = BOUNDS_RIGHT;
|
||||
right = osm::bounds.right();
|
||||
|
||||
if (bottom >= top) {
|
||||
reader.raiseError("Invalid bottom/top bounds combination");
|
||||
@ -119,8 +114,20 @@ RectC MapSource::bounds(QXmlStreamReader &reader)
|
||||
void MapSource::map(QXmlStreamReader &reader, Config &config)
|
||||
{
|
||||
const QXmlStreamAttributes &attr = reader.attributes();
|
||||
config.type = (attr.value("type") == "WMTS") ? WMTS
|
||||
: (attr.value("type") == "WMS") ? WMS : OSM;
|
||||
QStringRef type = attr.value("type");
|
||||
|
||||
if (type == "WMTS")
|
||||
config.type = WMTS;
|
||||
else if (type == "WMS")
|
||||
config.type = WMS;
|
||||
else if (type == "TMS")
|
||||
config.type = TMS;
|
||||
else if (type == "OSM" || type.isEmpty())
|
||||
config.type = OSM;
|
||||
else {
|
||||
reader.raiseError("Invalid map type");
|
||||
return;
|
||||
}
|
||||
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "name")
|
||||
@ -231,16 +238,23 @@ Map *MapSource::loadMap(const QString &path, QString &errorString)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.type == WMTS)
|
||||
return new WMTSMap(config.name, WMTS::Setup(config.url, config.layer,
|
||||
config.set, config.style, config.format, config.rest,
|
||||
config.coordinateSystem, config.dimensions, config.authorization),
|
||||
config.tileRatio);
|
||||
else if (config.type == WMS)
|
||||
return new WMSMap(config.name, WMS::Setup(config.url, config.layer,
|
||||
config.style, config.format, config.crs, config.coordinateSystem,
|
||||
config.dimensions, config.authorization));
|
||||
else
|
||||
return new OnlineMap(config.name, config.url, config.zooms,
|
||||
config.bounds, config.tileRatio, config.authorization);
|
||||
switch (config.type) {
|
||||
case WMTS:
|
||||
return new WMTSMap(config.name, WMTS::Setup(config.url, config.layer,
|
||||
config.set, config.style, config.format, config.rest,
|
||||
config.coordinateSystem, config.dimensions, config.authorization),
|
||||
config.tileRatio);
|
||||
case WMS:
|
||||
return new WMSMap(config.name, WMS::Setup(config.url, config.layer,
|
||||
config.style, config.format, config.crs, config.coordinateSystem,
|
||||
config.dimensions, config.authorization));
|
||||
case TMS:
|
||||
return new OnlineMap(config.name, config.url, config.zooms,
|
||||
config.bounds, config.tileRatio, config.authorization, true);
|
||||
case OSM:
|
||||
return new OnlineMap(config.name, config.url, config.zooms,
|
||||
config.bounds, config.tileRatio, config.authorization, false);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ private:
|
||||
enum Type {
|
||||
OSM,
|
||||
WMTS,
|
||||
WMS
|
||||
WMS,
|
||||
TMS
|
||||
};
|
||||
|
||||
struct Config {
|
||||
|
264
src/map/mbtilesmap.cpp
Normal file
264
src/map/mbtilesmap.cpp
Normal file
@ -0,0 +1,264 @@
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlRecord>
|
||||
#include <QSqlField>
|
||||
#include <QFileInfo>
|
||||
#include <QPainter>
|
||||
#include <QPixmapCache>
|
||||
#include "common/rectc.h"
|
||||
#include "common/wgs84.h"
|
||||
#include "osm.h"
|
||||
#include "config.h"
|
||||
#include "mbtilesmap.h"
|
||||
|
||||
|
||||
#define META_TYPE(type) static_cast<QMetaType::Type>(type)
|
||||
|
||||
static double index2mercator(int index, int zoom)
|
||||
{
|
||||
return rad2deg(-M_PI + 2 * M_PI * ((double)index / (1<<zoom)));
|
||||
}
|
||||
|
||||
MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent)
|
||||
: Map(parent), _fileName(fileName), _deviceRatio(1.0), _tileRatio(1.0),
|
||||
_valid(false)
|
||||
{
|
||||
_db = QSqlDatabase::addDatabase("QSQLITE", fileName);
|
||||
_db.setDatabaseName(fileName);
|
||||
|
||||
if (!_db.open()) {
|
||||
_errorString = fileName + ": Error opening database file";
|
||||
return;
|
||||
}
|
||||
|
||||
QSqlRecord r = _db.record("tiles");
|
||||
if (r.isEmpty()
|
||||
|| r.field(0).name() != "zoom_level"
|
||||
|| META_TYPE(r.field(0).type()) != QMetaType::Int
|
||||
|| r.field(1).name() != "tile_column"
|
||||
|| META_TYPE(r.field(1).type()) != QMetaType::Int
|
||||
|| r.field(2).name() != "tile_row"
|
||||
|| META_TYPE(r.field(2).type()) != QMetaType::Int
|
||||
|| r.field(3).name() != "tile_data"
|
||||
|| META_TYPE(r.field(3).type()) != QMetaType::QByteArray) {
|
||||
_errorString = "Invalid table format";
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QSqlQuery query("SELECT min(zoom_level), max(zoom_level) FROM tiles",
|
||||
_db);
|
||||
if (!query.first()) {
|
||||
_errorString = "Empty tile set";
|
||||
return;
|
||||
}
|
||||
_zooms = Range(query.value(0).toInt(), query.value(1).toInt());
|
||||
if (_zooms.min() < 0 || !_zooms.isValid()) {
|
||||
_errorString = "Invalid zoom levels";
|
||||
return;
|
||||
}
|
||||
}
|
||||
_zoom = _zooms.max();
|
||||
|
||||
{
|
||||
QString sql = QString("SELECT min(tile_column), min(tile_row), "
|
||||
"max(tile_column), max(tile_row) FROM tiles WHERE zoom_level = %1")
|
||||
.arg(_zooms.min());
|
||||
QSqlQuery query(sql, _db);
|
||||
query.first();
|
||||
|
||||
double minX = index2mercator(qMin((1<<_zooms.min()) - 1,
|
||||
qMax(0, query.value(0).toInt())), _zooms.min());
|
||||
double minY = index2mercator(qMin((1<<_zooms.min()) - 1,
|
||||
qMax(0, query.value(1).toInt())), _zooms.min());
|
||||
double maxX = index2mercator(qMin((1<<_zooms.min()) - 1,
|
||||
qMax(0, query.value(2).toInt())) + 1, _zooms.min());
|
||||
double maxY = index2mercator(qMin((1<<_zooms.min()) - 1,
|
||||
qMax(0, query.value(3).toInt())) + 1, _zooms.min());
|
||||
Coordinates tl(osm::m2ll(QPointF(minX, maxY)));
|
||||
Coordinates br(osm::m2ll(QPointF(maxX, minY)));
|
||||
// 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();
|
||||
QImage tile = QImage::fromData(query.value(0).toByteArray());
|
||||
if (tile.isNull() || tile.size().width() != tile.size().height()) {
|
||||
_errorString = "Unsupported/invalid tile images";
|
||||
return;
|
||||
}
|
||||
_tileSize = tile.size().width();
|
||||
}
|
||||
|
||||
{
|
||||
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 = QFileInfo(_fileName).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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_db.close();
|
||||
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
void MBTilesMap::load()
|
||||
{
|
||||
_db.open();
|
||||
}
|
||||
|
||||
void MBTilesMap::unload()
|
||||
{
|
||||
_db.close();
|
||||
}
|
||||
|
||||
QRectF MBTilesMap::bounds()
|
||||
{
|
||||
return QRectF(ll2xy(_bounds.topLeft()), ll2xy(_bounds.bottomRight()));
|
||||
}
|
||||
|
||||
int MBTilesMap::limitZoom(int zoom) const
|
||||
{
|
||||
if (zoom < _zooms.min())
|
||||
return _zooms.min();
|
||||
if (zoom > _zooms.max())
|
||||
return _zooms.max();
|
||||
|
||||
return zoom;
|
||||
}
|
||||
|
||||
int MBTilesMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
{
|
||||
if (!rect.isValid())
|
||||
_zoom = _zooms.max();
|
||||
else {
|
||||
QRectF tbr(osm::ll2m(rect.topLeft()), osm::ll2m(rect.bottomRight()));
|
||||
QPointF sc(tbr.width() / size.width(), tbr.height() / size.height());
|
||||
_zoom = limitZoom(osm::scale2zoom(qMax(sc.x(), -sc.y())
|
||||
/ coordinatesRatio(), _tileSize));
|
||||
}
|
||||
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
qreal MBTilesMap::resolution(const QRectF &rect)
|
||||
{
|
||||
qreal scale = osm::zoom2scale(_zoom, _tileSize);
|
||||
|
||||
return (WGS84_RADIUS * 2.0 * M_PI * scale / 360.0
|
||||
* cos(2.0 * atan(exp(deg2rad(-rect.center().y() * scale))) - M_PI/2));
|
||||
}
|
||||
|
||||
int MBTilesMap::zoomIn()
|
||||
{
|
||||
_zoom = qMin(_zoom + 1, _zooms.max());
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
int MBTilesMap::zoomOut()
|
||||
{
|
||||
_zoom = qMax(_zoom - 1, _zooms.min());
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
qreal MBTilesMap::coordinatesRatio() const
|
||||
{
|
||||
return _deviceRatio > 1.0 ? _deviceRatio / _tileRatio : 1.0;
|
||||
}
|
||||
|
||||
qreal MBTilesMap::imageRatio() const
|
||||
{
|
||||
return _deviceRatio > 1.0 ? _deviceRatio : _tileRatio;
|
||||
}
|
||||
|
||||
qreal MBTilesMap::tileSize() const
|
||||
{
|
||||
return (_tileSize / coordinatesRatio());
|
||||
}
|
||||
|
||||
QByteArray MBTilesMap::tileData(int zoom, const QPoint &tile) const
|
||||
{
|
||||
QSqlQuery query(_db);
|
||||
query.prepare("SELECT tile_data FROM tiles "
|
||||
"WHERE zoom_level=:zoom AND tile_column=:x AND tile_row=:y");
|
||||
query.bindValue(":zoom", zoom);
|
||||
query.bindValue(":x", tile.x());
|
||||
query.bindValue(":y", (1<<zoom) - tile.y() - 1);
|
||||
query.exec();
|
||||
|
||||
if (query.first())
|
||||
return query.value(0).toByteArray();
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
void MBTilesMap::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) * 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()));
|
||||
for (int i = 0; i < ceil(s.width() / tileSize()); i++) {
|
||||
for (int j = 0; j < ceil(s.height() / tileSize()); j++) {
|
||||
QPixmap pm;
|
||||
QPoint t(tile.x() + i, tile.y() + j);
|
||||
QString key = _fileName + "-" + QString::number(_zoom) + "_"
|
||||
+ QString::number(t.x()) + "_" + QString::number(t.y());
|
||||
|
||||
if (!QPixmapCache::find(key, &pm))
|
||||
if (pm.loadFromData(tileData(_zoom, t)))
|
||||
QPixmapCache::insert(key, pm);
|
||||
|
||||
QPointF tp(qMax(tl.x(), b.left()) + (t.x() - tile.x()) * tileSize(),
|
||||
qMax(tl.y(), b.top()) + (t.y() - tile.y()) * tileSize());
|
||||
if (!pm.isNull()) {
|
||||
#ifdef ENABLE_HIDPI
|
||||
pm.setDevicePixelRatio(imageRatio());
|
||||
#endif // ENABLE_HIDPI
|
||||
painter->drawPixmap(tp, pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPointF MBTilesMap::ll2xy(const Coordinates &c)
|
||||
{
|
||||
qreal scale = osm::zoom2scale(_zoom, _tileSize);
|
||||
QPointF m = osm::ll2m(c);
|
||||
return QPointF(m.x() / scale, m.y() / -scale) / coordinatesRatio();
|
||||
}
|
||||
|
||||
Coordinates MBTilesMap::xy2ll(const QPointF &p)
|
||||
{
|
||||
qreal scale = osm::zoom2scale(_zoom, _tileSize);
|
||||
return osm::m2ll(QPointF(p.x() * scale, -p.y() * scale)
|
||||
* coordinatesRatio());
|
||||
}
|
57
src/map/mbtilesmap.h
Normal file
57
src/map/mbtilesmap.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef MBTILESMAP_H
|
||||
#define MBTILESMAP_H
|
||||
|
||||
#include <QSqlDatabase>
|
||||
#include <QByteArray>
|
||||
#include "common/range.h"
|
||||
#include "map.h"
|
||||
|
||||
class MBTilesMap : public Map
|
||||
{
|
||||
public:
|
||||
MBTilesMap(const QString &fileName, QObject *parent = 0);
|
||||
|
||||
QString name() const {return _name;}
|
||||
|
||||
QRectF bounds();
|
||||
qreal resolution(const QRectF &rect);
|
||||
|
||||
int zoom() const {return _zoom;}
|
||||
void setZoom(int zoom) {_zoom = zoom;}
|
||||
int zoomFit(const QSize &size, const RectC &rect);
|
||||
int zoomIn();
|
||||
int zoomOut();
|
||||
|
||||
QPointF ll2xy(const Coordinates &c);
|
||||
Coordinates xy2ll(const QPointF &p);
|
||||
|
||||
void draw(QPainter *painter, const QRectF &rect, Flags flags);
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
void setDevicePixelRatio(qreal ratio) {_deviceRatio = ratio;}
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
QString errorString() const {return _errorString;}
|
||||
|
||||
private:
|
||||
int limitZoom(int zoom) const;
|
||||
qreal tileSize() const;
|
||||
qreal coordinatesRatio() const;
|
||||
qreal imageRatio() const;
|
||||
QByteArray tileData(int zoom, const QPoint &tile) const;
|
||||
|
||||
QSqlDatabase _db;
|
||||
|
||||
QString _fileName, _name;
|
||||
RectC _bounds;
|
||||
Range _zooms;
|
||||
int _zoom;
|
||||
int _tileSize;
|
||||
qreal _deviceRatio, _tileRatio;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
};
|
||||
|
||||
#endif // MBTILESMAP_H
|
@ -3,49 +3,19 @@
|
||||
#include "common/rectc.h"
|
||||
#include "common/wgs84.h"
|
||||
#include "downloader.h"
|
||||
#include "osm.h"
|
||||
#include "config.h"
|
||||
#include "onlinemap.h"
|
||||
|
||||
|
||||
#define TILE_SIZE 256
|
||||
#define EPSILON 1e-6
|
||||
|
||||
static QPointF ll2m(const Coordinates &c)
|
||||
{
|
||||
return QPointF(c.lon(), rad2deg(log(tan(M_PI_4 + deg2rad(c.lat())/2.0))));
|
||||
}
|
||||
|
||||
static Coordinates m2ll(const QPointF &p)
|
||||
{
|
||||
return Coordinates(p.x(), rad2deg(2.0 * atan(exp(deg2rad(p.y()))) - M_PI_2));
|
||||
}
|
||||
|
||||
static QPoint mercator2tile(const QPointF &m, int z)
|
||||
{
|
||||
QPoint tile;
|
||||
|
||||
tile.setX((int)(floor((m.x() + 180.0) / 360.0 * (1<<z))));
|
||||
tile.setY((int)(floor((1.0 - (m.y() / 180.0)) / 2.0 * (1<<z))));
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
static qreal zoom2scale(int zoom)
|
||||
{
|
||||
return (360.0/(qreal)((1<<zoom) * TILE_SIZE));
|
||||
}
|
||||
|
||||
static int scale2zoom(qreal scale)
|
||||
{
|
||||
return (int)(log2(360.0/(scale * (qreal)TILE_SIZE)) + EPSILON);
|
||||
}
|
||||
|
||||
|
||||
OnlineMap::OnlineMap(const QString &name, const QString &url,
|
||||
const Range &zooms, const RectC &bounds, qreal tileRatio,
|
||||
const Authorization &authorization, QObject *parent)
|
||||
const Authorization &authorization, bool invertY, QObject *parent)
|
||||
: Map(parent), _name(name), _zooms(zooms), _bounds(bounds),
|
||||
_zoom(_zooms.max()), _deviceRatio(1.0), _tileRatio(tileRatio), _valid(false)
|
||||
_zoom(_zooms.max()), _deviceRatio(1.0), _tileRatio(tileRatio),
|
||||
_invertY(invertY), _valid(false)
|
||||
{
|
||||
QString dir(TILES_DIR + "/" + _name);
|
||||
|
||||
@ -83,9 +53,10 @@ int OnlineMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
if (!rect.isValid())
|
||||
_zoom = _zooms.max();
|
||||
else {
|
||||
QRectF tbr(ll2m(rect.topLeft()), ll2m(rect.bottomRight()));
|
||||
QRectF tbr(osm::ll2m(rect.topLeft()), osm::ll2m(rect.bottomRight()));
|
||||
QPointF sc(tbr.width() / size.width(), tbr.height() / size.height());
|
||||
_zoom = limitZoom(scale2zoom(qMax(sc.x(), -sc.y()) / coordinatesRatio()));
|
||||
_zoom = limitZoom(osm::scale2zoom(qMax(sc.x(), -sc.y())
|
||||
/ coordinatesRatio(), TILE_SIZE));
|
||||
}
|
||||
|
||||
return _zoom;
|
||||
@ -93,7 +64,7 @@ int OnlineMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
|
||||
qreal OnlineMap::resolution(const QRectF &rect)
|
||||
{
|
||||
qreal scale = zoom2scale(_zoom);
|
||||
qreal scale = osm::zoom2scale(_zoom, TILE_SIZE);
|
||||
|
||||
return (WGS84_RADIUS * 2.0 * M_PI * scale / 360.0
|
||||
* cos(2.0 * atan(exp(deg2rad(-rect.center().y() * scale))) - M_PI/2));
|
||||
@ -128,10 +99,10 @@ qreal OnlineMap::tileSize() const
|
||||
|
||||
void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
{
|
||||
qreal scale = zoom2scale(_zoom);
|
||||
qreal scale = osm::zoom2scale(_zoom, TILE_SIZE);
|
||||
QRectF b(bounds());
|
||||
|
||||
QPoint tile = mercator2tile(QPointF(rect.topLeft().x() * scale,
|
||||
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());
|
||||
@ -141,7 +112,8 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
qMin(rect.bottom() - tl.y(), b.height()));
|
||||
for (int i = 0; i < ceil(s.width() / tileSize()); i++)
|
||||
for (int j = 0; j < ceil(s.height() / tileSize()); j++)
|
||||
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom));
|
||||
tiles.append(Tile(QPoint(tile.x() + i,
|
||||
_invertY ? (1<<_zoom) - (tile.y() + j) - 1 : tile.y() + j), _zoom));
|
||||
|
||||
if (flags & Map::Block)
|
||||
_tileLoader->loadTilesSync(tiles);
|
||||
@ -151,7 +123,8 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
for (int i = 0; i < tiles.count(); i++) {
|
||||
Tile &t = tiles[i];
|
||||
QPointF tp(qMax(tl.x(), b.left()) + (t.xy().x() - tile.x()) * tileSize(),
|
||||
qMax(tl.y(), b.top()) + (t.xy().y() - tile.y()) * tileSize());
|
||||
qMax(tl.y(), b.top()) + ((_invertY ? (1<<_zoom) - t.xy().y() - 1 :
|
||||
t.xy().y()) - tile.y()) * tileSize());
|
||||
if (!t.pixmap().isNull()) {
|
||||
#ifdef ENABLE_HIDPI
|
||||
t.pixmap().setDevicePixelRatio(imageRatio());
|
||||
@ -163,13 +136,14 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
|
||||
QPointF OnlineMap::ll2xy(const Coordinates &c)
|
||||
{
|
||||
qreal scale = zoom2scale(_zoom);
|
||||
QPointF m = ll2m(c);
|
||||
qreal scale = osm::zoom2scale(_zoom, TILE_SIZE);
|
||||
QPointF m = osm::ll2m(c);
|
||||
return QPointF(m.x() / scale, m.y() / -scale) / coordinatesRatio();
|
||||
}
|
||||
|
||||
Coordinates OnlineMap::xy2ll(const QPointF &p)
|
||||
{
|
||||
qreal scale = zoom2scale(_zoom);
|
||||
return m2ll(QPointF(p.x() * scale, -p.y() * scale) * coordinatesRatio());
|
||||
qreal scale = osm::zoom2scale(_zoom, TILE_SIZE);
|
||||
return osm::m2ll(QPointF(p.x() * scale, -p.y() * scale)
|
||||
* coordinatesRatio());
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class OnlineMap : public Map
|
||||
public:
|
||||
OnlineMap(const QString &name, const QString &url, const Range &zooms,
|
||||
const RectC &bounds, qreal tileRatio, const Authorization &authorization,
|
||||
QObject *parent = 0);
|
||||
bool invertY, QObject *parent = 0);
|
||||
|
||||
QString name() const {return _name;}
|
||||
|
||||
@ -49,6 +49,7 @@ private:
|
||||
RectC _bounds;
|
||||
int _zoom;
|
||||
qreal _deviceRatio, _tileRatio;
|
||||
bool _invertY;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
|
29
src/map/osm.cpp
Normal file
29
src/map/osm.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "osm.h"
|
||||
|
||||
#define EPSILON 1e-6
|
||||
|
||||
QPointF osm::ll2m(const Coordinates &c)
|
||||
{
|
||||
return QPointF(c.lon(), rad2deg(log(tan(M_PI_4 + deg2rad(c.lat())/2.0))));
|
||||
}
|
||||
|
||||
Coordinates osm::m2ll(const QPointF &p)
|
||||
{
|
||||
return Coordinates(p.x(), rad2deg(2.0 * atan(exp(deg2rad(p.y()))) - M_PI_2));
|
||||
}
|
||||
|
||||
QPoint osm::mercator2tile(const QPointF &m, int z)
|
||||
{
|
||||
return QPoint((int)(floor((m.x() + 180.0) / 360.0 * (1<<z))),
|
||||
(int)(floor((1.0 - (m.y() / 180.0)) / 2.0 * (1<<z))));
|
||||
}
|
||||
|
||||
qreal osm::zoom2scale(int zoom, int tileSize)
|
||||
{
|
||||
return (360.0/(qreal)((1<<zoom) * tileSize));
|
||||
}
|
||||
|
||||
int osm::scale2zoom(qreal scale, int tileSize)
|
||||
{
|
||||
return (int)(log2(360.0/(scale * (qreal)tileSize)) + EPSILON);
|
||||
}
|
22
src/map/osm.h
Normal file
22
src/map/osm.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef OSM_H
|
||||
#define OSM_H
|
||||
|
||||
#include <QPointF>
|
||||
#include <common/coordinates.h>
|
||||
#include <common/rectc.h>
|
||||
#include <common/range.h>
|
||||
|
||||
namespace osm
|
||||
{
|
||||
static const RectC bounds(Coordinates(-180, 85.0511),
|
||||
Coordinates(180, -85.0511));
|
||||
static const Range zooms(0, 19);
|
||||
|
||||
QPointF ll2m(const Coordinates &c);
|
||||
Coordinates m2ll(const QPointF &p);
|
||||
QPoint mercator2tile(const QPointF &m, int z);
|
||||
qreal zoom2scale(int zoom, int tileSize);
|
||||
int scale2zoom(qreal scale, int tileSize);
|
||||
}
|
||||
|
||||
#endif // OSM_H
|
@ -26,13 +26,16 @@ void TileLoader::loadTilesAsync(QList<Tile> &list)
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
QString file = tileFile(t);
|
||||
QString file(tileFile(t));
|
||||
QFileInfo fi(file);
|
||||
QUrl url(tileUrl(t));
|
||||
|
||||
if (!fi.exists())
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
else
|
||||
if (url.isLocalFile())
|
||||
loadTileFile(t, url.toLocalFile());
|
||||
else if (fi.exists())
|
||||
loadTileFile(t, file);
|
||||
else
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
}
|
||||
|
||||
if (!dl.empty())
|
||||
@ -45,13 +48,16 @@ void TileLoader::loadTilesSync(QList<Tile> &list)
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Tile &t = list[i];
|
||||
QString file = tileFile(t);
|
||||
QString file(tileFile(t));
|
||||
QFileInfo fi(file);
|
||||
QUrl url(tileUrl(t));
|
||||
|
||||
if (!fi.exists())
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
else
|
||||
if (url.isLocalFile())
|
||||
loadTileFile(t, url.toLocalFile());
|
||||
else if (fi.exists())
|
||||
loadTileFile(t, file);
|
||||
else
|
||||
dl.append(Download(tileUrl(t), file));
|
||||
}
|
||||
|
||||
if (dl.empty())
|
||||
@ -84,7 +90,7 @@ void TileLoader::clearCache()
|
||||
_downloader->clearErrors();
|
||||
}
|
||||
|
||||
QString TileLoader::tileUrl(const Tile &tile) const
|
||||
QUrl TileLoader::tileUrl(const Tile &tile) const
|
||||
{
|
||||
QString url(_url);
|
||||
|
||||
@ -101,7 +107,7 @@ QString TileLoader::tileUrl(const Tile &tile) const
|
||||
url.replace("$y", QString::number(tile.xy().y()));
|
||||
}
|
||||
|
||||
return url;
|
||||
return QUrl(url);
|
||||
}
|
||||
|
||||
QString TileLoader::tileFile(const Tile &tile) const
|
||||
|
@ -26,7 +26,7 @@ signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
QString tileUrl(const Tile &tile) const;
|
||||
QUrl tileUrl(const Tile &tile) const;
|
||||
QString tileFile(const Tile &tile) const;
|
||||
|
||||
Downloader *_downloader;
|
||||
|
@ -274,8 +274,8 @@ bool WMS::getCapabilities(const QString &url, const QString &file,
|
||||
|
||||
WMS::WMS(const QString &file, const WMS::Setup &setup) : _valid(false)
|
||||
{
|
||||
QString capaUrl = QString("%1?service=WMS&request=GetCapabilities")
|
||||
.arg(setup.url());
|
||||
QString capaUrl = QString("%1%2service=WMS&request=GetCapabilities")
|
||||
.arg(setup.url(), setup.url().contains('?') ? "&" : "?");
|
||||
|
||||
if (!QFileInfo(file).exists())
|
||||
if (!getCapabilities(capaUrl, file, setup.authorization()))
|
||||
|
@ -20,11 +20,11 @@ QString WMSMap::tileUrl(const QString &version) const
|
||||
{
|
||||
QString url;
|
||||
|
||||
url = QString("%1?version=%2&request=GetMap&bbox=$bbox"
|
||||
"&width=%3&height=%4&layers=%5&styles=%6&format=%7&transparent=true")
|
||||
.arg(_setup.url(), version, QString::number(TILE_SIZE),
|
||||
QString::number(TILE_SIZE), _setup.layer(), _setup.style(),
|
||||
_setup.format());
|
||||
url = QString("%1%2version=%3&request=GetMap&bbox=$bbox"
|
||||
"&width=%4&height=%5&layers=%6&styles=%7&format=%8&transparent=true")
|
||||
.arg(_setup.url(), _setup.url().contains('?') ? "&" : "?", version,
|
||||
QString::number(TILE_SIZE), QString::number(TILE_SIZE), _setup.layer(),
|
||||
_setup.style(), _setup.format());
|
||||
|
||||
if (version >= "1.3.0")
|
||||
url.append(QString("&CRS=%1").arg(_setup.crs()));
|
||||
|
@ -277,7 +277,7 @@ bool WMTS::parseCapabilities(const QString &path, const Setup &setup)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMTS::getCapabilities(const QString &url, const QString &file,
|
||||
bool WMTS::downloadCapabilities(const QString &url, const QString &file,
|
||||
const Authorization &authorization)
|
||||
{
|
||||
Downloader d;
|
||||
@ -300,21 +300,22 @@ bool WMTS::getCapabilities(const QString &url, const QString &file,
|
||||
|
||||
WMTS::WMTS(const QString &file, const WMTS::Setup &setup) : _valid(false)
|
||||
{
|
||||
QString capaUrl = setup.rest() ? setup.url() :
|
||||
QString("%1?service=WMTS&Version=1.0.0&request=GetCapabilities")
|
||||
.arg(setup.url());
|
||||
QUrl url(setup.rest() ? setup.url() : QString(
|
||||
"%1%2service=WMTS&Version=1.0.0&request=GetCapabilities").arg(setup.url(),
|
||||
setup.url().contains('?') ? "&" : "?"));
|
||||
|
||||
if (!QFileInfo(file).exists())
|
||||
if (!getCapabilities(capaUrl, file, setup.authorization()))
|
||||
if (!url.isLocalFile() && !QFileInfo(file).exists())
|
||||
if (!downloadCapabilities(url.toString(), file, setup.authorization()))
|
||||
return;
|
||||
if (!parseCapabilities(file, setup))
|
||||
if (!parseCapabilities(url.isLocalFile() ? url.toLocalFile() : file, setup))
|
||||
return;
|
||||
|
||||
QString style = setup.style().isEmpty() ? "default" : setup.style();
|
||||
if (!setup.rest()) {
|
||||
_tileUrl = QString("%1?service=WMTS&Version=1.0.0&request=GetTile"
|
||||
"&Format=%2&Layer=%3&Style=%4&TileMatrixSet=%5&TileMatrix=$z"
|
||||
"&TileRow=$y&TileCol=$x").arg(setup.url(), setup.format(),
|
||||
_tileUrl = QString("%1%2service=WMTS&Version=1.0.0&request=GetTile"
|
||||
"&Format=%3&Layer=%4&Style=%5&TileMatrixSet=%6&TileMatrix=$z"
|
||||
"&TileRow=$y&TileCol=$x").arg(setup.url(),
|
||||
setup.url().contains('?') ? "&" : "?" , setup.format(),
|
||||
setup.layer(), style, setup.set());
|
||||
for (int i = 0; i < setup.dimensions().size(); i++) {
|
||||
const QPair<QString, QString> &dim = setup.dimensions().at(i);
|
||||
|
@ -139,7 +139,7 @@ private:
|
||||
void contents(QXmlStreamReader &reader, CTX &ctx);
|
||||
void capabilities(QXmlStreamReader &reader, CTX &ctx);
|
||||
bool parseCapabilities(const QString &path, const Setup &setup);
|
||||
bool getCapabilities(const QString &url, const QString &file,
|
||||
bool downloadCapabilities(const QString &url, const QString &file,
|
||||
const Authorization &authorization);
|
||||
|
||||
QSet<TileMatrix> _matrixes;
|
||||
|
Reference in New Issue
Block a user