1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-10 01:04:27 +02:00

Compare commits

...

59 Commits
9.4 ... 9.6

Author SHA1 Message Date
0c434106af Merge remote-tracking branch 'weblate/master' 2021-09-04 10:03:35 +02:00
b938e24d65 Do not check for insane numbers of DEM tiles 2021-09-04 10:02:24 +02:00
1d65e32335 Translated using Weblate (Esperanto)
Currently translated at 94.9% (395 of 416 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/eo/
2021-09-03 11:37:11 +02:00
6be33a1fc1 Translated using Weblate (Russian)
Currently translated at 100.0% (416 of 416 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2021-09-03 11:37:11 +02:00
660fdde8c2 Translated using Weblate (Finnish)
Currently translated at 98.5% (410 of 416 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2021-09-03 11:37:11 +02:00
d50ab8607e Added some more shortcuts 2021-09-02 12:55:32 +02:00
3709a3feb5 Do not show the "Print" button in the toolbar on OS X
(As we are using buttons with text on OS X, the space is very limited)
2021-09-02 12:47:29 +02:00
a073c93e0d Removed debug stuff 2021-09-02 12:30:07 +02:00
f934df59e4 Some more options dialog polishing on OS X 2021-09-02 12:27:20 +02:00
394fcf6d4d Merge remote-tracking branch 'weblate/master' 2021-09-02 04:18:35 +02:00
0e1584ea24 Translated using Weblate (German)
Currently translated at 100.0% (416 of 416 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2021-09-02 04:17:13 +02:00
c592be7cfd Use the static exists() functions that are faster according to the docs 2021-09-01 20:14:06 +02:00
c84d7871fb Merge remote-tracking branch 'weblate/master' 2021-09-01 13:27:51 +02:00
34669b68f5 Code cleanup 2021-09-01 13:27:30 +02:00
849eaa57cc Merge branch 'origin/master' into Weblate. 2021-09-01 13:11:29 +02:00
7f6ac2e4c3 Improved DEM downloads handling logic 2021-09-01 13:08:34 +02:00
5407fe35e6 Merge branch 'origin/master' into Weblate. 2021-08-31 23:40:53 +02:00
133aac3bd4 Windows/Linux optionsdialog polishing 2021-08-31 22:55:32 +02:00
a5cd05233c Merge branch 'origin/master' into Weblate. 2021-08-31 18:45:31 +02:00
22b691252f Fixed widget margins 2021-08-31 18:43:55 +02:00
988ea7d952 Czech translation update 2021-08-31 18:43:32 +02:00
c253669017 Merge branch 'origin/master' into Weblate. 2021-08-31 18:28:00 +02:00
37d8aadfed Translated using Weblate (Hungarian)
Currently translated at 100.0% (417 of 417 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2021-08-31 18:27:59 +02:00
ae204e9ddf Translated using Weblate (Turkish)
Currently translated at 100.0% (417 of 417 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2021-08-31 18:27:59 +02:00
07410ad7d5 Translated using Weblate (Russian)
Currently translated at 98.8% (412 of 417 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2021-08-31 18:27:59 +02:00
98b56485bd Localization update 2021-08-31 18:27:42 +02:00
302fe4d8e1 Improved Options UI on OS X 2021-08-31 18:25:30 +02:00
045dab6cdd Silenced QT6 warning 2021-08-31 18:23:10 +02:00
da522cd2ba Use the latest OS X image 2021-08-31 18:10:26 +02:00
38322f09a6 Translated using Weblate (Swedish)
Currently translated at 100.0% (417 of 417 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2021-08-31 07:28:09 +02:00
7803340c0e Localization update 2021-08-30 20:33:39 +02:00
2232b011a1 Added support for downloading DEM tiles 2021-08-30 20:31:33 +02:00
94a0158243 Properly handle connection timeouts
(count timeout from last received data chunk, not for the whole download)
2021-08-29 20:28:08 +02:00
018d0ba085 Redesigned HTTP downloader
- Save the data as they come rather than at once
- + some related refactoring
2021-08-26 22:22:18 +02:00
d5a472ddc0 Cosmetics 2021-08-25 00:34:31 +02:00
09a6d8655e Code cleanup 2021-08-23 22:27:36 +02:00
1d2b93466f Fixed shifted file association 2021-08-22 19:07:38 +02:00
9979a8b233 Added ONmove OMD/GHP support info 2021-08-22 17:29:42 +02:00
ef6f3a0516 Translated using Weblate (Ukrainian)
Currently translated at 95.3% (388 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2021-08-22 16:58:45 +02:00
4f81e120b7 Translated using Weblate (German)
Currently translated at 100.0% (407 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2021-08-22 16:58:45 +02:00
ed68cbd891 Translated using Weblate (Czech)
Currently translated at 100.0% (407 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2021-08-22 15:24:16 +02:00
a4c7449772 Translated using Weblate (Esperanto)
Currently translated at 95.5% (389 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/eo/
2021-08-21 17:35:10 +02:00
60d82c9b7b Translated using Weblate (Russian)
Currently translated at 100.0% (407 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2021-08-21 17:35:09 +02:00
fe288a4fea Translated using Weblate (Finnish)
Currently translated at 99.2% (404 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2021-08-21 17:35:09 +02:00
9f95ded407 Translated using Weblate (Hungarian)
Currently translated at 100.0% (407 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2021-08-21 10:35:40 +02:00
1241b71475 Translated using Weblate (Turkish)
Currently translated at 100.0% (407 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2021-08-21 10:35:40 +02:00
48a7ecb83e Translated using Weblate (Swedish)
Currently translated at 100.0% (407 of 407 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2021-08-21 10:35:40 +02:00
62a60723be Added GHP OS X desktop integration 2021-08-19 23:41:01 +02:00
b7ee1ac660 Localization update 2021-08-19 19:47:26 +02:00
9f6ced0342 Added GHP Linux & Windows desktop integration 2021-08-19 19:45:53 +02:00
814eceb82c Added support for ONmove 7xx series GHP files 2021-08-19 19:35:26 +02:00
445598cd52 Localization update 2021-08-19 00:01:29 +02:00
eab43332ee Version++ 2021-08-18 23:30:38 +02:00
94571ccfc6 OMD OS X desktop integration 2021-08-18 23:28:11 +02:00
accea5d9da OMD Windows & Linux desktop integration 2021-08-18 22:30:02 +02:00
8d8a31eef9 Cosmetics 2021-08-18 21:33:07 +02:00
221d1b3fdb Added support for OnMove OMD files 2021-08-18 21:29:28 +02:00
ab062cc3ff Improved handling of labels with separators 2021-08-16 09:00:36 +02:00
78e8b03d66 Code cleanup 2021-08-10 20:44:16 +02:00
74 changed files with 7216 additions and 5273 deletions

View File

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

View File

@ -5,7 +5,7 @@ os:
- osx
dist: focal
osx_image: xcode12
osx_image: xcode12.5
cache:
directories:

View File

@ -2,7 +2,7 @@
GPXSee is a Qt-based GPS log file viewer and analyzer that supports all common 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 and geotagged JPEG files.
* 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 and geotagged JPEG files.
* User-definable online maps (OpenStreetMap/Google tiles, WMTS, WMS, TMS, QuadTiles).
* Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases, Garmin IMG/GMAP & JNX maps, TwoNav RMaps, GeoTIFF images, BSB charts, KMZ maps, AlpineQuest maps, Locus/OsmAnd/RMaps SQLite maps, Mapsforge vector maps, ESRI World-File georeferenced images).
* Elevation, speed, heart rate, cadence, power, temperature and gear ratio/shifts graphs.

View File

@ -3,7 +3,7 @@ unix:!macx {
} else {
TARGET = GPXSee
}
VERSION = 9.4
VERSION = 9.6
QT += core \
gui \
@ -18,10 +18,12 @@ greaterThan(QT_MAJOR_VERSION, 5) {QT += openglwidgets}
CONFIG += object_parallel_to_source
INCLUDEPATH += ./src
HEADERS += src/common/config.h \
src/GUI/authenticationwidget.h \
src/GUI/axislabelitem.h \
src/GUI/dirselectwidget.h \
src/GUI/flowlayout.h \
src/GUI/graphicsscene.h \
src/GUI/infolabel.h \
src/GUI/mapaction.h \
src/GUI/mapitem.h \
src/GUI/marginswidget.h \
@ -43,6 +45,7 @@ HEADERS += src/common/config.h \
src/common/greatcircle.h \
src/common/programpaths.h \
src/common/tifffile.h \
src/common/downloader.h \
src/GUI/app.h \
src/GUI/icons.h \
src/GUI/gui.h \
@ -95,6 +98,7 @@ HEADERS += src/common/config.h \
src/GUI/areaitem.h \
src/data/itnparser.h \
src/data/link.h \
src/data/onmoveparsers.h \
src/data/ov2parser.h \
src/map/IMG/bitmapline.h \
src/map/IMG/bitstream.h \
@ -134,7 +138,6 @@ HEADERS += src/common/config.h \
src/map/map.h \
src/map/maplist.h \
src/map/onlinemap.h \
src/map/downloader.h \
src/map/tile.h \
src/map/emptymap.h \
src/map/ozimap.h \
@ -193,6 +196,7 @@ HEADERS += src/common/config.h \
src/data/locparser.h \
src/data/slfparser.h \
src/data/dem.h \
src/data/demloader.h \
src/common/polygon.h \
src/data/area.h \
src/map/obliquestereographic.h \
@ -230,9 +234,11 @@ HEADERS += src/common/config.h \
src/map/worldfilemap.h
SOURCES += src/main.cpp \
src/GUI/authenticationwidget.cpp \
src/GUI/axislabelitem.cpp \
src/GUI/dirselectwidget.cpp \
src/GUI/flowlayout.cpp \
src/GUI/infolabel.cpp \
src/GUI/mapitem.cpp \
src/GUI/marginswidget.cpp \
src/GUI/markerinfoitem.cpp \
@ -246,6 +252,7 @@ SOURCES += src/main.cpp \
src/common/greatcircle.cpp \
src/common/programpaths.cpp \
src/common/tifffile.cpp \
src/common/downloader.cpp \
src/GUI/app.cpp \
src/GUI/gui.cpp \
src/GUI/axisitem.cpp \
@ -289,6 +296,7 @@ SOURCES += src/main.cpp \
src/GUI/areaitem.cpp \
src/data/address.cpp \
src/data/itnparser.cpp \
src/data/onmoveparsers.cpp \
src/data/ov2parser.cpp \
src/data/waypoint.cpp \
src/map/IMG/bitmapline.cpp \
@ -313,7 +321,6 @@ SOURCES += src/main.cpp \
src/map/kmzmap.cpp \
src/map/maplist.cpp \
src/map/onlinemap.cpp \
src/map/downloader.cpp \
src/map/emptymap.cpp \
src/map/ozimap.cpp \
src/map/polyconic.cpp \
@ -375,6 +382,7 @@ SOURCES += src/main.cpp \
src/data/locparser.cpp \
src/data/slfparser.cpp \
src/data/dem.cpp \
src/data/demloader.cpp \
src/map/obliquestereographic.cpp \
src/GUI/coordinatesitem.cpp \
src/map/rmap.cpp \
@ -476,7 +484,9 @@ macx {
icons/formats/sqlt.icns \
icons/formats/ov2.icns \
icons/formats/itn.icns \
icons/formats/wld.icns
icons/formats/wld.icns \
icons/formats/omd.icns \
icons/formats/ghp.icns
QMAKE_BUNDLE_DATA += locale maps icons csv
}
@ -508,7 +518,9 @@ win32 {
icons/formats/sqlt.ico \
icons/formats/ov2.ico \
icons/formats/itn.ico \
icons/formats/wld.ico
icons/formats/wld.ico \
icons/formats/omd.ico \
icons/formats/ghp.ico
DEFINES += _USE_MATH_DEFINES \
NOGDI
}

View File

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
icons/formats/ghp.icns Normal file

Binary file not shown.

BIN
icons/formats/ghp.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

BIN
icons/formats/omd.icns Normal file

Binary file not shown.

BIN
icons/formats/omd.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

View File

@ -25,3 +25,5 @@ sqlt:#303030
ov2:#a8c920
itn:#b8540d
wld:#c74c8f
omd:#ed09cb
ghp:#ed09cb

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

@ -546,6 +546,38 @@
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>omd</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.onmove.omd</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icons/omd.icns</string>
<key>CFBundleTypeName</key>
<string>ONmove Log File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ghp</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.onmove.ghp</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icons/ghp.icns</string>
<key>CFBundleTypeName</key>
<string>ONmove Log File</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
@ -1232,6 +1264,48 @@
<string>application/vnd.esri.wld</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.geonaute.omd</string>
<key>UTTypeReferenceURL</key>
<string>https://github.com/ColinPitrat/kalenji-gps-watch-reader</string>
<key>UTTypeDescription</key>
<string>ONmove Log File</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>omd</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.onmove.omd</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.geonaute.ghp</string>
<key>UTTypeReferenceURL</key>
<string>https://github.com/ColinPitrat/kalenji-gps-watch-reader</string>
<key>UTTypeDescription</key>
<string>ONmove Log File</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>ghp</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.onmove.ghp</string>
</dict>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>

View File

@ -14,8 +14,8 @@
<ul>
<li>Opens GPX, TCX, FIT, KML, IGC, NMEA, SIGMA SLF, Suunto SML, LOC,
OziExplorer (PLT, WPT, RTE), GeoJSON, SeeYou CUP,
Garmin GPI &amp; CSV, TomTom OV2 &amp; ITN and geotagged JPEG
files.</li>
Garmin GPI &amp; CSV, TomTom OV2 &amp; ITN, ONmove OMD/GHP
and geotagged JPEG files.</li>
<li>User-definable online maps (OpenStreetMap/Google tiles, WMTS,
WMS, TMS, QuadTiles).</li>
<li>Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases,
@ -95,5 +95,7 @@
<mimetype>application/vnd.tomtom.ov2</mimetype>
<mimetype>application/vnd.tomtom.itn</mimetype>
<mimetype>application/vnd.esri.wld</mimetype>
<mimetype>application/vnd.onmove.omd</mimetype>
<mimetype>application/vnd.onmove.ghp</mimetype>
</mimetypes>
</component>

View File

@ -9,7 +9,7 @@ Unicode true
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "9.4"
!define VERSION "9.6"
; The file to write
OutFile "GPXSee-${VERSION}.exe"
@ -137,15 +137,17 @@ Section "GPXSee" SEC_APP
!insertmacro FILE_ASSOCIATION_ADD "jgw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "gfw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "pgw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "tfw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 21
!insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 22
!insertmacro FILE_ASSOCIATION_ADD "kmz" "KML geographic compressed data" 22
!insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 23
!insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 24
!insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 25
!insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track File" 26
!insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 27
!insertmacro FILE_ASSOCIATION_ADD "tfw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "omd" "ONmove Log File" 21
!insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 22
!insertmacro FILE_ASSOCIATION_ADD "ghp" "ONmove Log File" 23
!insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 24
!insertmacro FILE_ASSOCIATION_ADD "kmz" "KML geographic compressed data" 24
!insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 25
!insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 26
!insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 27
!insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track File" 28
!insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 29
WriteRegStr HKCR "Applications\GPXSee.exe\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".gpx\OpenWithList" "GPXSee.exe" ""
@ -188,7 +190,9 @@ Section "GPXSee" SEC_APP
WriteRegStr HKCR ".jgw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".gfw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".pgw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".tfw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".tfw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".omd\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".ghp\OpenWithList" "GPXSee.exe" ""
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
@ -313,6 +317,8 @@ Section "Uninstall"
!insertmacro FILE_ASSOCIATION_REMOVE "gfw"
!insertmacro FILE_ASSOCIATION_REMOVE "pgw"
!insertmacro FILE_ASSOCIATION_REMOVE "tfw"
!insertmacro FILE_ASSOCIATION_REMOVE "omd"
!insertmacro FILE_ASSOCIATION_REMOVE "ghp"
DeleteRegValue HKCR ".gpx\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".tcx\OpenWithList" "GPXSee.exe"
@ -355,6 +361,8 @@ Section "Uninstall"
DeleteRegValue HKCR ".gfw\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".pgw\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".tfw\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".omd\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".ghp\OpenWithList" "GPXSee.exe"
DeleteRegKey HKCR "Applications\GPXSee.exe"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
@ -387,4 +395,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

View File

@ -142,6 +142,20 @@
<glob pattern="*.itn"/>
</mime-type>
<mime-type type="application/vnd.onmove.omd">
<comment>ONmove Log File</comment>
<sub-class-of type="application/octet-stream"/>
<generic-icon name="application/octet-stream"/>
<glob pattern="*.omd"/>
</mime-type>
<mime-type type="application/vnd.onmove.ghp">
<comment>ONmove Log File</comment>
<sub-class-of type="application/octet-stream"/>
<generic-icon name="application/octet-stream"/>
<glob pattern="*.ghp"/>
</mime-type>
<!-- Maps -->
<mime-type type="application/vnd.garmin.img">

View File

@ -9,7 +9,7 @@ Unicode true
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "9.4"
!define VERSION "9.6"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"
@ -144,15 +144,17 @@ Section "GPXSee" SEC_APP
!insertmacro FILE_ASSOCIATION_ADD "jgw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "gfw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "pgw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "tfw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 21
!insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 22
!insertmacro FILE_ASSOCIATION_ADD "kmz" "KML geographic compressed data" 22
!insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 23
!insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 24
!insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 25
!insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track File" 26
!insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 27
!insertmacro FILE_ASSOCIATION_ADD "tfw" "ESRI World File" 20
!insertmacro FILE_ASSOCIATION_ADD "omd" "ONmove Log File" 21
!insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 22
!insertmacro FILE_ASSOCIATION_ADD "ghp" "ONmove Log File" 23
!insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 24
!insertmacro FILE_ASSOCIATION_ADD "kmz" "KML geographic compressed data" 24
!insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 25
!insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 26
!insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 27
!insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track File" 28
!insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 29
WriteRegStr HKCR "Applications\GPXSee.exe\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".gpx\OpenWithList" "GPXSee.exe" ""
@ -196,6 +198,8 @@ Section "GPXSee" SEC_APP
WriteRegStr HKCR ".gfw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".pgw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".tfw\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".omd\OpenWithList" "GPXSee.exe" ""
WriteRegStr HKCR ".ghp\OpenWithList" "GPXSee.exe" ""
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
@ -336,6 +340,8 @@ Section "Uninstall"
!insertmacro FILE_ASSOCIATION_REMOVE "gfw"
!insertmacro FILE_ASSOCIATION_REMOVE "pgw"
!insertmacro FILE_ASSOCIATION_REMOVE "tfw"
!insertmacro FILE_ASSOCIATION_REMOVE "omd"
!insertmacro FILE_ASSOCIATION_REMOVE "ghp"
DeleteRegValue HKCR ".gpx\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".tcx\OpenWithList" "GPXSee.exe"
@ -378,6 +384,8 @@ Section "Uninstall"
DeleteRegValue HKCR ".gfw\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".pgw\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".tfw\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".omd\OpenWithList" "GPXSee.exe"
DeleteRegValue HKCR ".ghp\OpenWithList" "GPXSee.exe"
DeleteRegKey HKCR "Applications\GPXSee.exe"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
@ -410,4 +418,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

View File

@ -9,7 +9,7 @@
#include <QSurfaceFormat>
#include "common/programpaths.h"
#include "common/config.h"
#include "map/downloader.h"
#include "common/downloader.h"
#include "map/ellipsoid.h"
#include "map/gcs.h"
#include "map/pcs.h"

View File

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

View File

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

View File

@ -26,11 +26,12 @@
#include <QStyle>
#include <QTabBar>
#include "common/programpaths.h"
#include "common/downloader.h"
#include "data/data.h"
#include "data/poi.h"
#include "data/demloader.h"
#include "map/maplist.h"
#include "map/emptymap.h"
#include "map/downloader.h"
#include "map/crs.h"
#include "icons.h"
#include "keys.h"
@ -62,6 +63,8 @@ GUI::GUI()
TreeNode<POIAction*> poiActions;
_poi = new POI(this);
_dem = new DEMLoader(ProgramPaths::demDir(true), this);
connect(_dem, &DEMLoader::finished, this, &GUI::demLoaded);
createMapView();
createGraphTabs();
@ -352,6 +355,7 @@ void GUI::createActions(TreeNode<MapAction*> &mapActions,
_showTracksAction = new QAction(tr("Show tracks"), this);
_showTracksAction->setMenuRole(QAction::NoRole);
_showTracksAction->setCheckable(true);
_showTracksAction->setShortcut(SHOW_TRACKS_SHORTCUT);
connect(_showTracksAction, &QAction::triggered, this, &GUI::showTracks);
_showRoutesAction = new QAction(tr("Show routes"), this);
_showRoutesAction->setMenuRole(QAction::NoRole);
@ -360,13 +364,12 @@ void GUI::createActions(TreeNode<MapAction*> &mapActions,
_showWaypointsAction = new QAction(tr("Show waypoints"), this);
_showWaypointsAction->setMenuRole(QAction::NoRole);
_showWaypointsAction->setCheckable(true);
connect(_showWaypointsAction, &QAction::triggered, _mapView,
&MapView::showWaypoints);
connect(_showWaypointsAction, &QAction::triggered, this,
&GUI::showWaypoints);
_showAreasAction = new QAction(tr("Show areas"), this);
_showAreasAction->setMenuRole(QAction::NoRole);
_showAreasAction->setCheckable(true);
connect(_showAreasAction, &QAction::triggered, _mapView,
&MapView::showAreas);
connect(_showAreasAction, &QAction::triggered, this, &GUI::showAreas);
_showWaypointLabelsAction = new QAction(tr("Waypoint labels"), this);
_showWaypointLabelsAction->setMenuRole(QAction::NoRole);
_showWaypointLabelsAction->setCheckable(true);
@ -402,6 +405,13 @@ void GUI::createActions(TreeNode<MapAction*> &mapActions,
_showMarkerCoordinatesAction->setCheckable(true);
_showMarkerCoordinatesAction->setActionGroup(markerInfoGroup);
// DEM actions
_downloadDEMAction = new QAction(tr("Download DEM data"), this);
_downloadDEMAction->setMenuRole(QAction::NoRole);
_downloadDEMAction->setEnabled(false);
_downloadDEMAction->setShortcut(DOWNLOAD_DEM_SHORTCUT);
connect(_downloadDEMAction, &QAction::triggered, this, &GUI::downloadDEM);
// Graph actions
_showGraphsAction = new QAction(QIcon(SHOW_GRAPHS_ICON), tr("Show graphs"),
this);
@ -585,18 +595,6 @@ void GUI::createMenus(const TreeNode<MapAction*> &mapActions,
graphMenu->addSeparator();
graphMenu->addAction(_showGraphsAction);
_poiMenu = menuBar()->addMenu(tr("&POI"));
createPOINodeMenu(poiActions, _poiMenu);
_poisEnd = _poiMenu->addSeparator();
_poiMenu->addAction(_openPOIAction);
_poiMenu->addAction(_selectAllPOIAction);
_poiMenu->addAction(_unselectAllPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOILabelsAction);
_poiMenu->addAction(_overlapPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOIAction);
QMenu *dataMenu = menuBar()->addMenu(tr("&Data"));
dataMenu->addAction(_showWaypointLabelsAction);
dataMenu->addAction(_showRouteWaypointsAction);
@ -612,6 +610,22 @@ void GUI::createMenus(const TreeNode<MapAction*> &mapActions,
dataMenu->addAction(_showAreasAction);
dataMenu->addAction(_showWaypointsAction);
_poiMenu = menuBar()->addMenu(tr("&POI"));
createPOINodeMenu(poiActions, _poiMenu);
_poisEnd = _poiMenu->addSeparator();
_poiMenu->addAction(_openPOIAction);
_poiMenu->addAction(_selectAllPOIAction);
_poiMenu->addAction(_unselectAllPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOILabelsAction);
_poiMenu->addAction(_overlapPOIAction);
_poiMenu->addSeparator();
_poiMenu->addAction(_showPOIAction);
QMenu *demMenu = menuBar()->addMenu(tr("DEM"));
demMenu->addSeparator();
demMenu->addAction(_downloadDEMAction);
QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
QMenu *timeMenu = settingsMenu->addMenu(tr("Time"));
timeMenu->addAction(_totalTimeAction);
@ -652,7 +666,9 @@ void GUI::createToolBars()
_fileToolBar->addAction(_openFileAction);
_fileToolBar->addAction(_reloadFileAction);
_fileToolBar->addAction(_closeFileAction);
#ifndef Q_OS_MAC
_fileToolBar->addAction(_printFileAction);
#endif // Q_OS_MAC
_showToolBar = addToolBar(tr("Show"));
_showToolBar->setObjectName("Show");
@ -854,6 +870,7 @@ bool GUI::loadFile(const QString &fileName, bool silent)
updateStatusBarInfo();
updateWindowTitle();
updateGraphTabs();
updateDEMDownloadAction();
QString error = tr("Error loading data file:") + "\n\n"
+ fileName + "\n\n" + data.errorString();
@ -919,6 +936,8 @@ void GUI::loadData(const Data &data)
pi->setMarkerPosition(gt->sliderPosition());
}
}
updateDEMDownloadAction();
}
void GUI::openPOIFile()
@ -1052,6 +1071,15 @@ void GUI::openOptions()
if (options.poiRadius != _options.poiRadius)
_poi->setRadius(options.poiRadius);
if (options.demURL != _options.demURL)
_dem->setUrl(options.demURL);
if (options.demAuthorization != _options.demAuthorization
|| options.demUsername != _options.demUsername
|| options.demPassword != _options.demPassword)
_dem->setAuthorization(options.demAuthorization
? Authorization(options.demUsername, options.demPassword)
: Authorization());
if (options.pixmapCache != _options.pixmapCache)
QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
@ -1071,6 +1099,8 @@ void GUI::openOptions()
reloadFiles();
_options = options;
updateDEMDownloadAction();
}
void GUI::printFile()
@ -1364,6 +1394,7 @@ void GUI::reloadFiles()
_fileActionGroup->setEnabled(false);
else
_browser->setCurrent(_files.last());
updateDEMDownloadAction();
}
void GUI::closeFiles()
@ -1396,6 +1427,7 @@ void GUI::closeAll()
updateStatusBarInfo();
updateWindowTitle();
updateGraphTabs();
updateDEMDownloadAction();
}
void GUI::showGraphs(bool show)
@ -1455,6 +1487,7 @@ void GUI::showTracks(bool show)
updateStatusBarInfo();
updateGraphTabs();
updateDEMDownloadAction();
}
void GUI::showRoutes(bool show)
@ -1466,6 +1499,19 @@ void GUI::showRoutes(bool show)
updateStatusBarInfo();
updateGraphTabs();
updateDEMDownloadAction();
}
void GUI::showWaypoints(bool show)
{
_mapView->showWaypoints(show);
updateDEMDownloadAction();
}
void GUI::showAreas(bool show)
{
_mapView->showAreas(show);
updateDEMDownloadAction();
}
void GUI::showGraphGrids(bool show)
@ -1688,6 +1734,30 @@ void GUI::clearMapCache()
_mapView->clearMapCache();
}
void GUI::downloadDEM()
{
RectC br(_mapView->boundingRect());
_demRects.append(br);
if (!_dem->loadTiles(br) && _demRects.size() == 1)
demLoaded();
}
void GUI::demLoaded()
{
for (int i = 0; i < _demRects.size(); i++) {
if (!_dem->checkTiles(_demRects.at(i))) {
QMessageBox::warning(this, APP_NAME,
tr("Could not download all required DEM files."));
break;
}
}
DEM::clearCache();
_demRects.clear();
reloadFiles();
}
void GUI::updateStatusBarInfo()
{
if (_files.count() == 0)
@ -1843,6 +1913,12 @@ bool GUI::updateGraphTabs()
return (hidden != _graphTabWidget->isHidden());
}
void GUI::updateDEMDownloadAction()
{
_downloadDEMAction->setEnabled(!_dem->url().isEmpty()
&& !_dem->checkTiles(_mapView->boundingRect()));
}
void GUI::setTimeType(TimeType type)
{
for (int i = 0; i <_tabs.count(); i++)
@ -2236,6 +2312,14 @@ void GUI::writeSettings()
settings.setValue(USE_SEGMENTS_SETTING, _options.useSegments);
if (_options.poiRadius != POI_RADIUS_DEFAULT)
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
if (_options.demURL != DEM_URL_DEFAULT)
settings.setValue(DEM_URL_SETTING, _options.demURL);
if (_options.demAuthorization != DEM_AUTH_DEFAULT)
settings.setValue(DEM_AUTH_SETTING, _options.demAuthorization);
if (_options.demUsername != DEM_USERNAME_DEFAULT)
settings.setValue(DEM_USERNAME_SETTING, _options.demUsername);
if (_options.demPassword != DEM_PASSWORD_DEFAULT)
settings.setValue(DEM_PASSWORD_SETTING, _options.demPassword);
if (_options.useOpenGL != USE_OPENGL_DEFAULT)
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
if (_options.enableHTTP2 != ENABLE_HTTP2_DEFAULT)
@ -2547,6 +2631,13 @@ void GUI::readSettings()
PAUSE_INTERVAL_DEFAULT).toInt();
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
.toInt();
_options.demURL = settings.value(DEM_URL_SETTING, DEM_URL_DEFAULT).toString();
_options.demAuthorization = settings.value(DEM_AUTH_SETTING,
DEM_AUTH_DEFAULT).toBool();
_options.demUsername = settings.value(DEM_USERNAME_SETTING,
DEM_USERNAME_DEFAULT).toString();
_options.demPassword = settings.value(DEM_PASSWORD_SETTING,
DEM_PASSWORD_DEFAULT).toString();
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
.toBool();
_options.enableHTTP2 = settings.value(ENABLE_HTTP2_SETTING,
@ -2641,6 +2732,11 @@ void GUI::readSettings()
_poi->setRadius(_options.poiRadius);
_dem->setUrl(_options.demURL);
if (_options.demAuthorization)
_dem->setAuthorization(Authorization(_options.demUsername,
_options.demPassword));
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
settings.endGroup();

View File

@ -7,6 +7,7 @@
#include <QDate>
#include <QPrinter>
#include "common/treenode.h"
#include "common/rectc.h"
#include "data/graph.h"
#include "units.h"
#include "timetype.h"
@ -32,6 +33,7 @@ class QScreen;
class MapAction;
class POIAction;
class Data;
class DEMLoader;
class GUI : public QMainWindow
{
@ -65,12 +67,15 @@ private slots:
void showFullscreen(bool show);
void showTracks(bool show);
void showRoutes(bool show);
void showAreas(bool show);
void showWaypoints(bool show);
void loadMap();
void loadMapDir();
void nextMap();
void prevMap();
void openOptions();
void clearMapCache();
void downloadDEM();
void mapChanged(QAction *action);
void graphChanged(int);
@ -102,6 +107,8 @@ private slots:
void mapLoadedDir();
void mapInitialized();
void demLoaded();
private:
typedef QPair<QDateTime, QDateTime> DateTimeRange;
@ -138,6 +145,7 @@ private:
void updateStatusBarInfo();
void updateWindowTitle();
bool updateGraphTabs();
void updateDEMDownloadAction();
TimeType timeType() const;
Units units() const;
@ -224,6 +232,7 @@ private:
QAction *_showTicksAction;
QAction *_showCoordinatesAction;
QAction *_openOptionsAction;
QAction *_downloadDEMAction;
QAction *_mapsEnd;
QAction *_poisEnd;
@ -239,6 +248,7 @@ private:
POI *_poi;
Map *_map;
DEMLoader *_dem;
FileBrowser *_browser;
QList<QString> _files;
@ -260,6 +270,8 @@ private:
QString _dataDir, _mapDir, _poiDir;
Units _units;
QList<RectC> _demRects;
};
#endif // GUI_H

View File

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

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

@ -0,0 +1,15 @@
#include <QtGlobal>
#include "infolabel.h"
InfoLabel::InfoLabel(const QString &text, QWidget *parent)
: QLabel(text, parent)
{
QFont f(font());
#ifdef Q_OS_MAC
f.setPointSize(qMax(10, f.pointSize() - 2));
#else // Q_OS_MAC
f.setPointSize(f.pointSize() - 1);
#endif // Q_OS_MAC
setWordWrap(true);
setFont(f);
}

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

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

View File

@ -28,6 +28,8 @@
#define PREV_MAP_SHORTCUT QKeySequence(QKeySequence::Back)
#define SHOW_GRAPHS_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_G)
#define STATISTICS_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_S)
#define DOWNLOAD_DEM_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_D)
#define SHOW_TRACKS_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_T)
#define FULLSCREEN_SHORTCUT (QKeySequence(QKeySequence::FullScreen).isEmpty() \
? QKeySequence(Qt::Key_F11) \
: QKeySequence(QKeySequence::FullScreen))

View File

@ -1147,3 +1147,19 @@ void MapView::fitContentToSize()
centerOn(contentCenter());
}
RectC MapView::boundingRect() const
{
RectC rect;
if (_showTracks)
rect |= _tr;
if (_showRoutes)
rect |= _rr;
if (_showWaypoints)
rect |= _wr;
if (_showAreas)
rect |= _ar;
return rect;
}

View File

@ -88,6 +88,8 @@ public:
void clearMapCache();
void fitContentToSize();
RectC boundingRect() const;
public slots:
void showMap(bool show);
void showPOI(bool show);

View File

@ -14,12 +14,14 @@
#include <QSysInfo>
#include <QButtonGroup>
#include "icons.h"
#include "infolabel.h"
#include "colorbox.h"
#include "stylecombobox.h"
#include "oddspinbox.h"
#include "percentslider.h"
#include "projectioncombobox.h"
#include "dirselectwidget.h"
#include "authenticationwidget.h"
#include "optionsdialog.h"
@ -53,17 +55,11 @@ QWidget *OptionsDialog::createMapPage()
_inputProjection->setCurrentIndex(_inputProjection->findData(
_options.inputProjection));
QLabel *inInfo = new QLabel(tr("Select the proper projection of maps"
InfoLabel *inInfo = new InfoLabel(tr("Select the proper projection of maps"
" without a projection definition (JNX, KMZ and world file maps)."));
QLabel *outInfo = new QLabel(tr("Select the desired projection of vector"
" maps (IMG and Mapsforge maps). The projection must be valid for"
InfoLabel *outInfo = new InfoLabel(tr("Select the desired projection of"
" vector maps (IMG and Mapsforge maps). The projection must be valid for"
" the whole map area."));
QFont f = inInfo->font();
f.setPointSize(f.pointSize() - 1);
inInfo->setWordWrap(true);
outInfo->setWordWrap(true);
inInfo->setFont(f);
outInfo->setFont(f);
_hidpi = new QRadioButton(tr("High-resolution"));
_lodpi = new QRadioButton(tr("Standard"));
@ -71,14 +67,10 @@ QWidget *OptionsDialog::createMapPage()
_hidpi->setChecked(true);
else
_lodpi->setChecked(true);
QLabel *lhi = new QLabel(tr("Non-HiDPI maps are loaded as HiDPI maps. "
InfoLabel *lhi = new InfoLabel(tr("Non-HiDPI maps are loaded as HiDPI maps. "
"The map is sharp but map objects are small/hard to read."));
QLabel *llo = new QLabel(tr("Non-HiDPI maps are loaded such as they are. "
InfoLabel *llo = new InfoLabel(tr("Non-HiDPI maps are loaded such as they are. "
"Map objects have the expected size but the map is blurry."));
lhi->setWordWrap(true);
llo->setWordWrap(true);
lhi->setFont(f);
llo->setFont(f);
QVBoxLayout *inLayout = new QVBoxLayout();
inLayout->addWidget(_inputProjection);
@ -134,34 +126,12 @@ QWidget *OptionsDialog::createAppearancePage()
_trackWidth->setMinimum(1);
_trackStyle = new StyleComboBox();
_trackStyle->setValue(_options.trackStyle);
QFormLayout *trackLayout = new QFormLayout();
#ifdef Q_OS_MAC
trackLayout->addRow(tr("Track width:"), _trackWidth);
trackLayout->addRow(tr("Track style:"), _trackStyle);
#else // Q_OS_MAC
trackLayout->addRow(tr("Width:"), _trackWidth);
trackLayout->addRow(tr("Style:"), _trackStyle);
QGroupBox *trackBox = new QGroupBox(tr("Tracks"));
trackBox->setLayout(trackLayout);
#endif // Q_OS_MAC
// Routes
_routeWidth = new QSpinBox();
_routeWidth->setValue(_options.routeWidth);
_routeWidth->setMinimum(1);
_routeStyle = new StyleComboBox();
_routeStyle->setValue(_options.routeStyle);
QFormLayout *routeLayout = new QFormLayout();
#ifdef Q_OS_MAC
routeLayout->addRow(tr("Route width:"), _routeWidth);
routeLayout->addRow(tr("Route style:"), _routeStyle);
#else // Q_OS_MAC
routeLayout->addRow(tr("Width:"), _routeWidth);
routeLayout->addRow(tr("Style:"), _routeStyle);
QGroupBox *routeBox = new QGroupBox(tr("Routes"));
routeBox->setLayout(routeLayout);
#endif // Q_OS_MAC
// Areas
_areaWidth = new QSpinBox();
_areaWidth->setValue(_options.areaWidth);
@ -169,52 +139,64 @@ QWidget *OptionsDialog::createAppearancePage()
_areaStyle->setValue(_options.areaStyle);
_areaOpacity = new PercentSlider();
_areaOpacity->setValue(_options.areaOpacity);
QFormLayout *areaLayout = new QFormLayout();
#ifdef Q_OS_MAC
areaLayout->addRow(tr("Area border width:"), _areaWidth);
areaLayout->addRow(tr("Area border style:"), _areaStyle);
areaLayout->addRow(tr("Area fill opacity:"), _areaOpacity);
#else // Q_OS_MAC
areaLayout->addRow(tr("Width:"), _areaWidth);
areaLayout->addRow(tr("Style:"), _areaStyle);
areaLayout->addRow(tr("Fill opacity:"), _areaOpacity);
QGroupBox *areaBox = new QGroupBox(tr("Areas"));
areaBox->setLayout(areaLayout);
#endif // Q_OS_MAC
// Palette & antialiasing
_baseColor = new ColorBox();
_baseColor->setColor(_options.palette.color());
_colorOffset = new PercentSlider();
_colorOffset->setValue(_options.palette.shift() * 100);
_pathAA = new QCheckBox(tr("Use anti-aliasing"));
_pathAA->setChecked(_options.pathAntiAliasing);
#ifdef Q_OS_MAC
QWidget *pathTab = new QWidget();
QFormLayout *pathTabLayout = new QFormLayout();
pathTabLayout->addRow(tr("Track width:"), _trackWidth);
pathTabLayout->addRow(tr("Track style:"), _trackStyle);
pathTabLayout->addRow(line());
pathTabLayout->addRow(tr("Route width:"), _routeWidth);
pathTabLayout->addRow(tr("Route style:"), _routeStyle);
pathTabLayout->addRow(line());
pathTabLayout->addRow(tr("Area border width:"), _areaWidth);
pathTabLayout->addRow(tr("Area border style:"), _areaStyle);
pathTabLayout->addRow(tr("Area fill opacity:"), _areaOpacity);
pathTabLayout->addRow(line());
pathTabLayout->addRow(tr("Base color:"), _baseColor);
pathTabLayout->addRow(tr("Palette shift:"), _colorOffset);
pathTabLayout->addRow(line());
pathTabLayout->addWidget(_pathAA);
pathTab->setLayout(pathTabLayout);
#else // Q_OS_MAC
QFormLayout *trackLayout = new QFormLayout();
trackLayout->addRow(tr("Width:"), _trackWidth);
trackLayout->addRow(tr("Style:"), _trackStyle);
QGroupBox *trackBox = new QGroupBox(tr("Tracks"));
trackBox->setLayout(trackLayout);
QFormLayout *routeLayout = new QFormLayout();
routeLayout->addRow(tr("Width:"), _routeWidth);
routeLayout->addRow(tr("Style:"), _routeStyle);
QGroupBox *routeBox = new QGroupBox(tr("Routes"));
routeBox->setLayout(routeLayout);
QFormLayout *areaLayout = new QFormLayout();
areaLayout->addRow(tr("Width:"), _areaWidth);
areaLayout->addRow(tr("Style:"), _areaStyle);
areaLayout->addRow(tr("Fill opacity:"), _areaOpacity);
QGroupBox *areaBox = new QGroupBox(tr("Areas"));
areaBox->setLayout(areaLayout);
QFormLayout *paletteLayout = new QFormLayout();
paletteLayout->addRow(tr("Base color:"), _baseColor);
paletteLayout->addRow(tr("Palette shift:"), _colorOffset);
_pathAA = new QCheckBox(tr("Use anti-aliasing"));
_pathAA->setChecked(_options.pathAntiAliasing);
QFormLayout *pathAALayout = new QFormLayout();
pathAALayout->addWidget(_pathAA);
QWidget *pathTab = new QWidget();
QVBoxLayout *pathTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
pathTabLayout->addLayout(trackLayout);
pathTabLayout->addWidget(line());
pathTabLayout->addLayout(routeLayout);
pathTabLayout->addWidget(line());
pathTabLayout->addLayout(areaLayout);
pathTabLayout->addWidget(line());
#else // Q_OS_MAC
pathTabLayout->addWidget(trackBox);
pathTabLayout->addWidget(routeBox);
pathTabLayout->addWidget(areaBox);
#endif // Q_OS_MAC
pathTabLayout->addLayout(paletteLayout);
pathTabLayout->addLayout(pathAALayout);
pathTabLayout->addStretch();
pathTab->setLayout(pathTabLayout);
#endif // Q_OS_MAC
// Waypoints
_waypointSize = new QSpinBox();
@ -222,46 +204,40 @@ QWidget *OptionsDialog::createAppearancePage()
_waypointSize->setValue(_options.waypointSize);
_waypointColor = new ColorBox();
_waypointColor->setColor(_options.waypointColor);
QFormLayout *waypointLayout = new QFormLayout();
#ifdef Q_OS_MAC
waypointLayout->addRow(tr("Waypoint color:"), _waypointColor);
waypointLayout->addRow(tr("Waypoint size:"), _waypointSize);
#else // Q_OS_MAC
waypointLayout->addRow(tr("Color:"), _waypointColor);
waypointLayout->addRow(tr("Size:"), _waypointSize);
QGroupBox *waypointBox = new QGroupBox(tr("Waypoints"));
waypointBox->setLayout(waypointLayout);
#endif // Q_OS_MAC
// POI
_poiSize = new QSpinBox();
_poiSize->setMinimum(1);
_poiSize->setValue(_options.poiSize);
_poiColor = new ColorBox();
_poiColor->setColor(_options.poiColor);
QFormLayout *poiLayout = new QFormLayout();
#ifdef Q_OS_MAC
poiLayout->addRow(tr("POI color:"), _poiColor);
poiLayout->addRow(tr("POI size:"), _poiSize);
QWidget *pointTab = new QWidget();
QFormLayout *pointTabLayout = new QFormLayout();
pointTabLayout->addRow(tr("Waypoint color:"), _waypointColor);
pointTabLayout->addRow(tr("Waypoint size:"), _waypointSize);
pointTabLayout->addRow(line());
pointTabLayout->addRow(tr("POI color:"), _poiColor);
pointTabLayout->addRow(tr("POI size:"), _poiSize);
pointTab->setLayout(pointTabLayout);
#else // Q_OS_MAC
QFormLayout *waypointLayout = new QFormLayout();
waypointLayout->addRow(tr("Color:"), _waypointColor);
waypointLayout->addRow(tr("Size:"), _waypointSize);
QGroupBox *waypointBox = new QGroupBox(tr("Waypoints"));
waypointBox->setLayout(waypointLayout);
QFormLayout *poiLayout = new QFormLayout();
poiLayout->addRow(tr("Color:"), _poiColor);
poiLayout->addRow(tr("Size:"), _poiSize);
QGroupBox *poiBox = new QGroupBox(tr("POIs"));
poiBox->setLayout(poiLayout);
#endif // Q_OS_MAC
QWidget *pointTab = new QWidget();
QVBoxLayout *pointTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
pointTabLayout->addLayout(waypointLayout);
pointTabLayout->addWidget(line());
pointTabLayout->addLayout(poiLayout);
#else // Q_OS_MAC
pointTabLayout->addWidget(waypointBox);
pointTabLayout->addWidget(poiBox);
#endif // Q_OS_MAC
pointTabLayout->addStretch();
pointTab->setLayout(pointTabLayout);
#endif // Q_OS_MAC
// Graphs
_sliderColor = new ColorBox();
@ -269,23 +245,29 @@ QWidget *OptionsDialog::createAppearancePage()
_graphWidth = new QSpinBox();
_graphWidth->setValue(_options.graphWidth);
_graphWidth->setMinimum(1);
_graphAA = new QCheckBox(tr("Use anti-aliasing"));
_graphAA->setChecked(_options.graphAntiAliasing);
#ifdef Q_OS_MAC
QWidget *graphTab = new QWidget();
QFormLayout *graphTabLayout = new QFormLayout();
graphTabLayout->addRow(tr("Line width:"), _graphWidth);
graphTabLayout->addRow(tr("Slider color:"), _sliderColor);
graphTabLayout->addWidget(_graphAA);
graphTab->setLayout(graphTabLayout);
#else // Q_OS_MAC
QFormLayout *graphLayout = new QFormLayout();
graphLayout->addRow(tr("Line width:"), _graphWidth);
graphLayout->addRow(tr("Slider color:"), _sliderColor);
_graphAA = new QCheckBox(tr("Use anti-aliasing"));
_graphAA->setChecked(_options.graphAntiAliasing);
QFormLayout *graphAALayout = new QFormLayout();
graphAALayout->addWidget(_graphAA);
QWidget *graphTab = new QWidget();
QVBoxLayout *graphTabLayout = new QVBoxLayout();
graphTabLayout->addLayout(graphLayout);
graphTabLayout->addLayout(graphAALayout);
graphTabLayout->addStretch();
graphTab->setLayout(graphTabLayout);
#endif // Q_OS_MAC
// Map
_mapOpacity = new PercentSlider();
@ -297,14 +279,12 @@ QWidget *OptionsDialog::createAppearancePage()
QFormLayout *mapLayout = new QFormLayout();
mapLayout->addRow(tr("Background color:"), _backgroundColor);
mapLayout->addRow(tr("Map opacity:"), _mapOpacity);
QWidget *mapTab = new QWidget();
QVBoxLayout *mapTabLayout = new QVBoxLayout();
mapTabLayout->addLayout(mapLayout);
mapTabLayout->addStretch();
mapTab->setLayout(mapTabLayout);
QTabWidget *appearancePage = new QTabWidget();
appearancePage->addTab(pathTab, tr("Paths"));
appearancePage->addTab(pointTab, tr("Points"));
@ -334,36 +314,39 @@ QWidget *OptionsDialog::createDataPage()
_powerFilter->setValue(_options.powerFilter);
_powerFilter->setToolTip(filterToolTip);
_outlierEliminate = new QCheckBox(tr("Eliminate GPS outliers"));
_outlierEliminate->setChecked(_options.outlierEliminate);
#ifdef Q_OS_MAC
QWidget *filterTab = new QWidget();
QFormLayout *filterTabLayout = new QFormLayout();
filterTabLayout->addWidget(new QLabel(tr("Smoothing")));
filterTabLayout->addRow(tr("Elevation:"), _elevationFilter);
filterTabLayout->addRow(tr("Speed:"), _speedFilter);
filterTabLayout->addRow(tr("Heart rate:"), _heartRateFilter);
filterTabLayout->addRow(tr("Cadence:"), _cadenceFilter);
filterTabLayout->addRow(tr("Power:"), _powerFilter);
filterTabLayout->addWidget(new QWidget());
filterTabLayout->addWidget(_outlierEliminate);
filterTab->setLayout(filterTabLayout);
#else // Q_OS_MAC
QFormLayout *smoothLayout = new QFormLayout();
smoothLayout->addRow(tr("Elevation:"), _elevationFilter);
smoothLayout->addRow(tr("Speed:"), _speedFilter);
smoothLayout->addRow(tr("Heart rate:"), _heartRateFilter);
smoothLayout->addRow(tr("Cadence:"), _cadenceFilter);
smoothLayout->addRow(tr("Power:"), _powerFilter);
#ifndef Q_OS_MAC
QGroupBox *smoothBox = new QGroupBox(tr("Smoothing"));
smoothBox->setLayout(smoothLayout);
#endif // Q_OS_MAC
_outlierEliminate = new QCheckBox(tr("Eliminate GPS outliers"));
_outlierEliminate->setChecked(_options.outlierEliminate);
QFormLayout *outlierLayout = new QFormLayout();
outlierLayout->addWidget(_outlierEliminate);
QWidget *filterTab = new QWidget();
QVBoxLayout *filterTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
filterTabLayout->addWidget(new QLabel(tr("Smoothing:")));
filterTabLayout->addLayout(smoothLayout);
filterTabLayout->addWidget(line());
#else // Q_OS_MAC
QGroupBox *smoothBox = new QGroupBox(tr("Smoothing"));
smoothBox->setLayout(smoothLayout);
QFormLayout *outlierLayout = new QFormLayout();
outlierLayout->addWidget(_outlierEliminate);
filterTabLayout->addWidget(smoothBox);
#endif // Q_OS_MAC
filterTabLayout->addLayout(outlierLayout);
filterTabLayout->addStretch();
filterTab->setLayout(filterTabLayout);
#endif // Q_OS_MAC
_automaticPause = new QRadioButton(tr("Automatic"));
_manualPause = new QRadioButton(tr("Custom"));
@ -396,26 +379,6 @@ QWidget *OptionsDialog::createDataPage()
connect(_automaticPause, &QRadioButton::toggled, this,
&OptionsDialog::automaticPauseDetectionSet);
QHBoxLayout *pauseTypeLayout = new QHBoxLayout();
#ifdef Q_OS_MAC
pauseTypeLayout->addStretch();
#endif
pauseTypeLayout->addWidget(_automaticPause);
pauseTypeLayout->addWidget(_manualPause);
pauseTypeLayout->addStretch();
QFormLayout *pauseValuesLayout = new QFormLayout();
pauseValuesLayout->addRow(tr("Minimal speed:"), _pauseSpeed);
pauseValuesLayout->addRow(tr("Minimal duration:"), _pauseInterval);
QVBoxLayout *pauseLayout = new QVBoxLayout();
pauseLayout->addLayout(pauseTypeLayout);
pauseLayout->addLayout(pauseValuesLayout);
QWidget *pauseTab = new QWidget();
pauseTab->setLayout(pauseLayout);
_computedSpeed = new QRadioButton(tr("Computed from distance/time"));
_reportedSpeed = new QRadioButton(tr("Recorded by device"));
if (_options.useReportedSpeed)
@ -458,10 +421,9 @@ QWidget *OptionsDialog::createDataPage()
_useSegments = new QCheckBox(tr("Use segments"));
_useSegments->setChecked(_options.useSegments);
QWidget *sourceTab = new QWidget();
QVBoxLayout *sourceTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
QWidget *sourceTab = new QWidget();
QFormLayout *sourceTabLayout = new QFormLayout();
QButtonGroup *speedGroup = new QButtonGroup(this);
speedGroup->addButton(_computedSpeed);
speedGroup->addButton(_reportedSpeed);
@ -469,7 +431,6 @@ QWidget *OptionsDialog::createDataPage()
speedOptions->addWidget(_computedSpeed);
speedOptions->addWidget(_reportedSpeed);
speedOptions->addWidget(_showSecondarySpeed);
QButtonGroup *elevationGroup = new QButtonGroup(this);
elevationGroup->addButton(_dataGPSElevation);
elevationGroup->addButton(_dataDEMElevation);
@ -477,7 +438,6 @@ QWidget *OptionsDialog::createDataPage()
elevationOptions->addWidget(_dataGPSElevation);
elevationOptions->addWidget(_dataDEMElevation);
elevationOptions->addWidget(_showSecondaryElevation);
QButtonGroup *timeZoneGroup = new QButtonGroup(this);
timeZoneGroup->addButton(_utcZone);
timeZoneGroup->addButton(_systemZone);
@ -487,62 +447,68 @@ QWidget *OptionsDialog::createDataPage()
zoneOptions->addWidget(_systemZone);
zoneOptions->addWidget(_customZone);
zoneOptions->addItem(customZoneLayout);
QFormLayout *formLayout = new QFormLayout();
formLayout->addRow(tr("Speed:"), speedOptions);
formLayout->addRow(tr("Elevation:"), elevationOptions);
formLayout->addRow(tr("Time zone:"), zoneOptions);
QFormLayout *segmentsLayout = new QFormLayout();
segmentsLayout->addWidget(_useSegments);
sourceTabLayout->addLayout(formLayout);
sourceTabLayout->addWidget(line());
sourceTabLayout->addLayout(segmentsLayout);
sourceTabLayout->addRow(tr("Speed:"), speedOptions);
sourceTabLayout->addRow(tr("Elevation:"), elevationOptions);
sourceTabLayout->addRow(tr("Time zone:"), zoneOptions);
sourceTabLayout->addRow(line());
sourceTabLayout->addWidget(_useSegments);
sourceTab->setLayout(sourceTabLayout);
#else // Q_OS_MAC
QWidget *sourceTab = new QWidget();
QVBoxLayout *sourceTabLayout = new QVBoxLayout();
QFormLayout *speedLayout = new QFormLayout();
QFormLayout *elevationLayout = new QFormLayout();
QFormLayout *timeZoneLayout = new QFormLayout();
QFormLayout *segmentsLayout = new QFormLayout();
speedLayout->addWidget(_computedSpeed);
speedLayout->addWidget(_reportedSpeed);
speedLayout->addWidget(_showSecondarySpeed);
QGroupBox *speedBox = new QGroupBox(tr("Speed"));
speedBox->setLayout(speedLayout);
elevationLayout->addWidget(_dataGPSElevation);
elevationLayout->addWidget(_dataDEMElevation);
elevationLayout->addWidget(_showSecondaryElevation);
QGroupBox *elevationBox = new QGroupBox(tr("Elevation"));
elevationBox->setLayout(elevationLayout);
timeZoneLayout->addWidget(_utcZone);
timeZoneLayout->addWidget(_systemZone);
timeZoneLayout->addWidget(_customZone);
timeZoneLayout->addItem(customZoneLayout);
QGroupBox *timeZoneBox = new QGroupBox(tr("Time zone"));
timeZoneBox->setLayout(timeZoneLayout);
segmentsLayout->addWidget(_useSegments);
sourceTabLayout->addWidget(speedBox);
sourceTabLayout->addWidget(elevationBox);
sourceTabLayout->addWidget(timeZoneBox);
sourceTabLayout->addLayout(segmentsLayout);
#endif // Q_OS_MAC
sourceTabLayout->addStretch();
sourceTab->setLayout(sourceTabLayout);
#endif // Q_OS_MAC
QHBoxLayout *pauseTypeLayout = new QHBoxLayout();
#ifdef Q_OS_MAC
pauseTypeLayout->addStretch();
#endif
pauseTypeLayout->addWidget(_automaticPause);
pauseTypeLayout->addWidget(_manualPause);
pauseTypeLayout->addStretch();
QFormLayout *pauseValuesLayout = new QFormLayout();
pauseValuesLayout->addRow(tr("Minimal speed:"), _pauseSpeed);
pauseValuesLayout->addRow(tr("Minimal duration:"), _pauseInterval);
QVBoxLayout *pauseLayout = new QVBoxLayout();
pauseLayout->addLayout(pauseTypeLayout);
pauseLayout->addLayout(pauseValuesLayout);
QWidget *pauseTab = new QWidget();
pauseTab->setLayout(pauseLayout);
QTabWidget *dataPage = new QTabWidget();
dataPage->addTab(filterTab, tr("Filtering"));
dataPage->addTab(sourceTab, tr("Sources"));
dataPage->addTab(filterTab, tr("Filtering"));
dataPage->addTab(pauseTab, tr("Pause detection"));
return dataPage;
@ -576,6 +542,57 @@ QWidget *OptionsDialog::createPOIPage()
return poiPage;
}
QWidget *OptionsDialog::createDEMPage()
{
_demURL = new QLineEdit();
_demURL->setMinimumWidth(300);
_demURL->setText(_options.demURL);
_demAuth = new AuthenticationWidget();
_demAuth->setUsername(_options.demUsername);
_demAuth->setPassword(_options.demPassword);
_demAuth->setEnabled(_options.demAuthorization);
QCheckBox *useAuth = new QCheckBox(tr("Use HTTP authentication"));
useAuth->setChecked(_demAuth->isEnabled());
connect(useAuth, &QRadioButton::toggled, _demAuth,
&AuthenticationWidget::setEnabled);
InfoLabel *info = new InfoLabel(
tr("Use $lat and $lon for NYY/SYY and EXXX/WXXX in the URL."));
info->setMinimumWidth(_demURL->minimumWidth());
#ifdef Q_OS_MAC
QFormLayout *sourceLayout = new QFormLayout();
sourceLayout->addRow(tr("URL:"), _demURL);
sourceLayout->addWidget(info);
sourceLayout->addWidget(new QWidget());
sourceLayout->addWidget(useAuth);
sourceLayout->addWidget(_demAuth);
sourceLayout->setAlignment(_demAuth, Qt::AlignLeft);
QWidget *sourceTab = new QWidget();
sourceTab->setLayout(sourceLayout);
#else // Q_OS_MAC
QVBoxLayout *urlValueLayout = new QVBoxLayout();
urlValueLayout->addWidget(_demURL);
urlValueLayout->addWidget(info);
QFormLayout *urlLayout = new QFormLayout();
urlLayout->addRow(tr("URL:"), urlValueLayout);
QVBoxLayout *sourceLayout = new QVBoxLayout();
sourceLayout->addLayout(urlLayout);
sourceLayout->addSpacing(10);
sourceLayout->addWidget(useAuth);
sourceLayout->addWidget(_demAuth);
sourceLayout->addStretch();
QWidget *sourceTab = new QWidget();
sourceTab->setLayout(sourceLayout);
#endif // Q_OS_MAC
QTabWidget *demPage = new QTabWidget();
demPage->addTab(sourceTab, tr("Source"));
return demPage;
}
QWidget *OptionsDialog::createExportPage()
{
_wysiwyg = new QRadioButton(tr("WYSIWYG"));
@ -584,17 +601,11 @@ QWidget *OptionsDialog::createExportPage()
_hires->setChecked(true);
else
_wysiwyg->setChecked(true);
QLabel *lw = new QLabel(tr("The printed area is approximately the display"
" area. The map zoom level does not change."));
QLabel *lh = new QLabel(tr("The zoom level will be changed so that"
InfoLabel *lw = new InfoLabel(tr("The printed area is approximately the "
"display area. The map zoom level does not change."));
InfoLabel *lh = new InfoLabel(tr("The zoom level will be changed so that"
" the whole content (tracks/waypoints) fits to the printed area and"
" the map resolution is as close as possible to the print resolution."));
QFont f = lw->font();
f.setPointSize(f.pointSize() - 1);
lw->setWordWrap(true);
lh->setWordWrap(true);
lw->setFont(f);
lh->setFont(f);
QVBoxLayout *modeTabLayout = new QVBoxLayout();
modeTabLayout->addWidget(_wysiwyg);
@ -669,20 +680,28 @@ QWidget *OptionsDialog::createSystemPage()
_connectionTimeout->setSuffix(UNIT_SPACE + tr("s"));
_connectionTimeout->setValue(_options.connectionTimeout);
#ifdef Q_OS_MAC
QWidget *systemTab = new QWidget();
QFormLayout *systemTabLayout = new QFormLayout();
systemTabLayout->addRow(tr("Image cache size:"), _pixmapCache);
systemTabLayout->addRow(tr("Connection timeout:"), _connectionTimeout);
systemTabLayout->addWidget(_enableHTTP2);
systemTabLayout->addWidget(_useOpenGL);
systemTab->setLayout(systemTabLayout);
#else // Q_OS_MAC
QFormLayout *formLayout = new QFormLayout();
formLayout->addRow(tr("Image cache size:"), _pixmapCache);
formLayout->addRow(tr("Connection timeout:"), _connectionTimeout);
QFormLayout *checkboxLayout = new QFormLayout();
checkboxLayout->addWidget(_enableHTTP2);
checkboxLayout->addWidget(_useOpenGL);
QWidget *systemTab = new QWidget();
QVBoxLayout *systemTabLayout = new QVBoxLayout();
systemTabLayout->addLayout(formLayout);
systemTabLayout->addLayout(checkboxLayout);
systemTabLayout->addStretch();
systemTab->setLayout(systemTabLayout);
#endif // Q_OS_MAC
_dataPath = new DirSelectWidget();
_dataPath->setDir(_options.dataPath);
@ -691,12 +710,8 @@ QWidget *OptionsDialog::createSystemPage()
_poiPath = new DirSelectWidget();
_poiPath->setDir(_options.poiPath);
QLabel *info = new QLabel(tr("Select the initial paths of the file open"
" dialogues. Leave the field empty for the system default."));
QFont f = info->font();
f.setPointSize(f.pointSize() - 1);
info->setFont(f);
info->setWordWrap(true);
InfoLabel *info = new InfoLabel(tr("Select the initial paths of the file"
" open dialogues. Leave the field empty for the system default."));
QFormLayout *pathsFormLayout = new QFormLayout();
pathsFormLayout->addRow(tr("Data:"), _dataPath);
@ -705,8 +720,8 @@ QWidget *OptionsDialog::createSystemPage()
QWidget *pathsTab = new QWidget();
QVBoxLayout *pathsTabLayout = new QVBoxLayout();
pathsTabLayout->addWidget(info);
pathsTabLayout->addLayout(pathsFormLayout);
pathsTabLayout->addWidget(info);
pathsTabLayout->addStretch();
pathsTab->setLayout(pathsTabLayout);
@ -725,6 +740,7 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
pages->addWidget(createMapPage());
pages->addWidget(createDataPage());
pages->addWidget(createPOIPage());
pages->addWidget(createDEMPage());
pages->addWidget(createExportPage());
pages->addWidget(createSystemPage());
@ -735,6 +751,7 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
new QListWidgetItem(QIcon(MAPS_ICON), tr("Maps"), menu);
new QListWidgetItem(QIcon(DATA_ICON), tr("Data"), menu);
new QListWidgetItem(QIcon(POI_ICON), tr("POI"), menu);
new QListWidgetItem(QIcon(DEM_ICON), tr("DEM"), menu);
new QListWidgetItem(QIcon(PRINT_EXPORT_ICON), tr("Print & Export"),
menu);
new QListWidgetItem(QIcon(SYSTEM_ICON), tr("System"), menu);
@ -764,6 +781,9 @@ OptionsDialog::OptionsDialog(Options &options, Units units, QWidget *parent)
QVBoxLayout *layout = new QVBoxLayout;
layout->addLayout(contentLayout);
layout->addWidget(buttonBox);
#ifdef Q_OS_MAC
layout->setSizeConstraint(QLayout::SetFixedSize);
#endif // Q_OS_MAC
setLayout(layout);
setWindowTitle(tr("Options"));
@ -831,6 +851,11 @@ void OptionsDialog::accept()
if (qAbs(poiRadius - _options.poiRadius) > 0.01)
_options.poiRadius = poiRadius;
_options.demURL = _demURL->text();
_options.demAuthorization = _demAuth->isEnabled();
_options.demUsername = _demAuth->username();
_options.demPassword = _demAuth->password();
_options.useOpenGL = _useOpenGL->isChecked();
_options.enableHTTP2 = _enableHTTP2->isChecked();
_options.pixmapCache = _pixmapCache->value();

View File

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

View File

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

204
src/common/downloader.cpp Normal file
View File

@ -0,0 +1,204 @@
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
#include <QDir>
#include <QTimerEvent>
#include "common/config.h"
#include "downloader.h"
#if defined(Q_OS_LINUX)
#define PLATFORM_STR "Linux"
#elif defined(Q_OS_WIN32)
#define PLATFORM_STR "Windows"
#elif defined(Q_OS_MAC)
#define PLATFORM_STR "OS X"
#else
#define PLATFORM_STR "Unknown"
#endif
#define USER_AGENT \
APP_NAME "/" APP_VERSION " (" PLATFORM_STR "; Qt " QT_VERSION_STR ")"
#define MAX_REDIRECT_LEVEL 5
#define RETRIES 3
#define ATTR_REDIRECT_POLICY QNetworkRequest::RedirectPolicyAttribute
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
#define ATTR_HTTP2_ALLOWED QNetworkRequest::HTTP2AllowedAttribute
#else // QT 5.15
#define ATTR_HTTP2_ALLOWED QNetworkRequest::Http2AllowedAttribute
#endif // QT 5.15
#define TMP_SUFFIX ".download"
static QString tmpName(const QString &origName)
{
return origName + TMP_SUFFIX;
}
static QString origName(const QString &tmpName)
{
return tmpName.left(tmpName.size() - (sizeof(TMP_SUFFIX) - 1));
}
Authorization::Authorization(const QString &username, const QString &password)
{
QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64();
_header = "Basic " + data;
}
NetworkTimeout::NetworkTimeout(int timeout, QNetworkReply *reply)
: QObject(reply), _timeout(timeout)
{
connect(reply, &QIODevice::readyRead, this, &NetworkTimeout::reset);
_timer.start(timeout * 1000, this);
}
void NetworkTimeout::reset()
{
_timer.start(_timeout * 1000, this);
}
void NetworkTimeout::timerEvent(QTimerEvent *ev)
{
if (!_timer.isActive() || ev->timerId() != _timer.timerId())
return;
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning())
reply->close();
_timer.stop();
}
QNetworkAccessManager *Downloader::_manager = 0;
int Downloader::_timeout = 30;
bool Downloader::_http2 = true;
bool Downloader::doDownload(const Download &dl, const Authorization &auth)
{
const QUrl &url = dl.url();
if (!url.isValid() || !(url.scheme() == QLatin1String("http")
|| url.scheme() == QLatin1String("https"))) {
qWarning("%s: Invalid URL", qPrintable(url.toString()));
return false;
}
if (_errorDownloads.value(url) >= RETRIES)
return false;
if (_currentDownloads.contains(url))
return false;
QNetworkRequest request(url);
request.setMaximumRedirectsAllowed(MAX_REDIRECT_LEVEL);
request.setAttribute(ATTR_REDIRECT_POLICY,
QNetworkRequest::NoLessSafeRedirectPolicy);
request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2));
request.setRawHeader("User-Agent", USER_AGENT);
if (!auth.isNull())
request.setRawHeader("Authorization", auth.header());
QFile *file = new QFile(tmpName(dl.file()));
if (!file->open(QIODevice::WriteOnly)) {
qWarning("%s: %s", qPrintable(file->fileName()),
qPrintable(file->errorString()));
_errorDownloads.insert(url, RETRIES);
return false;
}
Q_ASSERT(_manager);
QNetworkReply *reply = _manager->get(request);
file->setParent(reply);
_currentDownloads.insert(url, file);
if (reply->isRunning()) {
/* Starting with Qt 5.15 this can be replaced by
QNetworkRequest::setTransferTimeout() */
new NetworkTimeout(_timeout, reply);
connect(reply, &QIODevice::readyRead, this, &Downloader::emitReadReady);
connect(reply, &QNetworkReply::finished, this, &Downloader::emitFinished);
} else {
readData(reply);
downloadFinished(reply);
}
return true;
}
void Downloader::emitFinished()
{
downloadFinished(static_cast<QNetworkReply*>(sender()));
}
void Downloader::emitReadReady()
{
readData(static_cast<QNetworkReply*>(sender()));
}
void Downloader::insertError(const QUrl &url, QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError)
_errorDownloads.insert(url, _errorDownloads.value(url) + 1);
else
_errorDownloads.insert(url, RETRIES);
}
void Downloader::readData(QNetworkReply *reply)
{
QFile *file = _currentDownloads.value(reply->request().url());
Q_ASSERT(file);
file->write(reply->readAll());
}
void Downloader::downloadFinished(QNetworkReply *reply)
{
QUrl url(reply->request().url());
QNetworkReply::NetworkError error = reply->error();
QFile *file = _currentDownloads.value(reply->request().url());
if (error) {
insertError(url, error);
qWarning("%s: %s", url.toEncoded().constData(),
qPrintable(reply->errorString()));
file->remove();
} else {
file->close();
file->rename(origName(file->fileName()));
}
_currentDownloads.remove(url);
reply->deleteLater();
if (_currentDownloads.isEmpty())
emit finished();
}
bool Downloader::get(const QList<Download> &list,
const Authorization &authorization)
{
bool finishEmitted = false;
for (int i = 0; i < list.count(); i++)
finishEmitted |= doDownload(list.at(i), authorization);
return finishEmitted;
}
void Downloader::enableHTTP2(bool enable)
{
Q_ASSERT(_manager);
_http2 = enable;
_manager->clearConnectionCache();
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Download &download)
{
dbg.nospace() << "Download(" << download.url() << "," << download.file()
<< ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

View File

@ -3,11 +3,12 @@
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBasicTimer>
#include <QUrl>
#include <QList>
#include <QSet>
#include <QHash>
class QFile;
class Download
{
@ -28,12 +29,30 @@ public:
Authorization() {}
Authorization(const QString &username, const QString &password);
bool isNull() const {return _header.isNull();}
const QByteArray &header() const {return _header;}
private:
QByteArray _header;
};
class NetworkTimeout : public QObject
{
Q_OBJECT
public:
NetworkTimeout(int timeout, QNetworkReply *reply);
private slots:
void reset();
private:
void timerEvent(QTimerEvent *ev);
QBasicTimer _timer;
int _timeout;
};
class Downloader : public QObject
{
Q_OBJECT
@ -55,18 +74,17 @@ signals:
private slots:
void emitFinished();
void downloadFinished(QNetworkReply *reply);
void emitReadReady();
private:
class Redirect;
class ReplyTimeout;
void insertError(const QUrl &url, QNetworkReply::NetworkError error);
bool doDownload(const Download &dl, const QByteArray &authorization,
const Redirect *redirect = 0);
bool saveToDisk(const QString &filename, QIODevice *data);
bool doDownload(const Download &dl, const Authorization &auth);
void downloadFinished(QNetworkReply *reply);
void readData(QNetworkReply *reply);
QSet<QUrl> _currentDownloads;
QHash<QUrl, QFile*> _currentDownloads;
QHash<QUrl, int> _errorDownloads;
static QNetworkAccessManager *_manager;
@ -74,4 +92,8 @@ private:
static bool _http2;
};
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Download &download);
#endif // QT_NO_DEBUG
#endif // DOWNLOADER_H

View File

@ -19,6 +19,7 @@
#include "smlparser.h"
#include "ov2parser.h"
#include "itnparser.h"
#include "onmoveparsers.h"
#include "data.h"
@ -41,6 +42,8 @@ static GPIParser gpi;
static SMLParser sml;
static OV2Parser ov2;
static ITNParser itn;
static OMDParser omd;
static GHPParser ghp;
static QMap<QString, Parser*> parsers()
{
@ -67,6 +70,8 @@ static QMap<QString, Parser*> parsers()
map.insert("sml", &sml);
map.insert("ov2", &ov2);
map.insert("itn", &itn);
map.insert("omd", &omd);
map.insert("ghp", &ghp);
return map;
}
@ -144,6 +149,7 @@ QString Data::formats()
+ qApp->translate("Data", "KML files") + " (*.kml);;"
+ qApp->translate("Data", "LOC files") + " (*.loc);;"
+ qApp->translate("Data", "NMEA files") + " (*.nmea);;"
+ qApp->translate("Data", "ONmove files") + " (*.omd *.ghp);;"
+ qApp->translate("Data", "OV2 files") + " (*.ov2);;"
+ qApp->translate("Data", "OziExplorer files") + " (*.plt *.rte *.wpt);;"
+ qApp->translate("Data", "SLF files") + " (*.slf);;"

View File

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

View File

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

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

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

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

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

278
src/data/onmoveparsers.cpp Normal file
View File

@ -0,0 +1,278 @@
#include <QFileInfo>
#include <QDir>
#include <QtEndian>
#include "onmoveparsers.h"
static inline quint16 u16(const char *buffer)
{
return qFromLittleEndian<quint16>(buffer);
}
static inline qint16 s16(const char *buffer)
{
return qFromLittleEndian<qint16>(buffer);
}
static inline qint32 s32(const char *buffer)
{
return qFromLittleEndian<qint32>(buffer);
}
bool OMDParser::readHeaderFile(const QString &omdPath, Header &hdr)
{
QFileInfo fi(omdPath);
QString path = fi.absoluteDir().filePath(fi.baseName() + ".OMH");
QFile file(path);
char buffer[60];
if (!file.open(QIODevice::ReadOnly)) {
qWarning("%s: %s", qPrintable(path), qPrintable(file.errorString()));
return false;
}
if (file.read(buffer, sizeof(buffer)) != sizeof(buffer)) {
qWarning("%s: invalid OMH file", qPrintable(path));
return false;
}
quint8 Y = buffer[14];
quint8 M = buffer[15];
quint8 D = buffer[16];
quint8 h = buffer[17];
quint8 m = buffer[18];
quint16 ascent = u16(buffer + 22);
quint16 descent = u16(buffer + 24);
quint8 avgHr = buffer[12];
quint8 maxHr = buffer[13];
QDateTime date(QDate(Y + 2000, M, D), QTime(h, m), Qt::UTC);
if (!date.isValid()) {
qWarning("%s: invalid date", qPrintable(path));
return false;
}
hdr.date = date;
hdr.hr = avgHr || maxHr;
hdr.elevation = ascent || descent;
return true;
}
bool OMDParser::readF1(const char *chunk, const Header &hdr, Sequence &seq,
SegmentData &segment)
{
if (seq.cnt > 1) {
_errorString = "invalid chunk sequence";
return false;
}
qint32 lat = s32(chunk);
qint32 lon = s32(chunk + 4);
quint16 sec = u16(chunk + 12);
quint8 fia = chunk[14];
qint16 alt = s16(chunk + 15);
if (fia == 3) {
Trackpoint t(Coordinates(lon / 1000000.0, lat / 1000000.0));
if (!t.coordinates().isValid()) {
_errorString = "invalid coordinates";
return false;
}
t.setTimestamp(QDateTime(hdr.date.date(),
hdr.date.time().addSecs(sec), Qt::UTC));
if (hdr.elevation)
t.setElevation(alt);
seq.idx[seq.cnt] = segment.size();
segment.append(t);
} else
seq.idx[seq.cnt] = -1;
seq.cnt++;
return true;
}
bool OMDParser::readF2(const char *chunk, const Header &hdr, Sequence &seq,
SegmentData &segment)
{
if (!seq.cnt) {
_errorString = "invalid chunk sequence";
return false;
}
quint16 speed1 = u16(chunk + 2);
quint8 hr1 = chunk[6];
quint16 speed2 = u16(chunk + 12);
quint8 hr2 = chunk[16];
if (seq.idx[0] >= 0) {
Trackpoint &p0 = segment[seq.idx[0]];
if (hdr.hr)
p0.setHeartRate(hr1);
p0.setSpeed(speed1 / 360.0);
}
if (seq.idx[1] >= 0) {
Trackpoint &p1 = segment[seq.idx[1]];
if (hdr.hr)
p1.setHeartRate(hr2);
p1.setSpeed(speed2 / 360.0);
}
seq.idx[0] = -1;
seq.idx[1] = -1;
seq.cnt = 0;
return true;
}
bool OMDParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Area> &polygons, QVector<Waypoint> &waypoints)
{
Q_UNUSED(routes);
Q_UNUSED(waypoints);
Q_UNUSED(polygons);
SegmentData segment;
Header hdr;
Sequence seq;
char chunk[20];
qint64 size;
// If no header file is found or it is invalid, continue with the default
// header values. The track will have a fictional date and possibly some
// zero-graphs, but it will be still usable.
readHeaderFile(file->fileName(), hdr);
while ((size = file->read(chunk, sizeof(chunk))) == sizeof(chunk)) {
switch ((quint8)chunk[19]) {
case 0xF1:
if (!readF1(chunk, hdr, seq, segment))
return false;
break;
case 0xF2:
if (!readF2(chunk, hdr, seq, segment))
return false;
break;
default:
_errorString = "invalid chunk type";
return false;
}
}
if (size < 0) {
_errorString = "I/O error";
return false;
} else if (size) {
_errorString = "unexpected end of file";
return false;
}
tracks.append(TrackData());
tracks.last().append(segment);
return true;
}
bool GHPParser::readHeaderFile(const QString &ghpPath, Header &hdr)
{
QFileInfo fi(ghpPath);
QString path = fi.absoluteDir().filePath(fi.baseName() + ".GHT");
QFile file(path);
char buffer[96];
if (!file.open(QIODevice::ReadOnly)) {
qWarning("%s: %s", qPrintable(path), qPrintable(file.errorString()));
return false;
}
if (file.read(buffer, sizeof(buffer)) != sizeof(buffer)) {
qWarning("%s: invalid GHT file", qPrintable(path));
return false;
}
quint8 Y = buffer[0];
quint8 M = buffer[1];
quint8 D = buffer[2];
quint8 h = buffer[3];
quint8 m = buffer[4];
quint8 s = buffer[5];
quint8 avgHr = buffer[61];
quint8 maxHr = buffer[60];
QDateTime date(QDate(Y + 2000, M, D), QTime(h, m, s), Qt::UTC);
if (!date.isValid()) {
qWarning("%s: invalid date", qPrintable(path));
return false;
}
hdr.date = date;
hdr.hr = avgHr || maxHr;
return true;
}
bool GHPParser::readF0(const char *chunk, const Header &hdr, int &time,
SegmentData &segment)
{
qint32 lat = s32(chunk);
qint32 lon = s32(chunk + 4);
qint16 alt = s16(chunk + 8);
quint16 speed = u16(chunk + 10);
quint8 hr = chunk[12];
quint8 fia = chunk[13];
quint8 ms = chunk[16];
if (fia == 3) {
Trackpoint t(Coordinates(lon / 1000000.0, lat / 1000000.0));
if (!t.coordinates().isValid()) {
_errorString = "invalid coordinates";
return false;
}
t.setTimestamp(QDateTime(hdr.date.date(),
hdr.date.time().addMSecs(time * 100), Qt::UTC));
t.setSpeed(speed / 360.0);
t.setElevation(alt);
if (hdr.hr)
t.setHeartRate(hr);
segment.append(t);
}
time += ms;
return true;
}
bool GHPParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Area> &polygons, QVector<Waypoint> &waypoints)
{
Q_UNUSED(routes);
Q_UNUSED(waypoints);
Q_UNUSED(polygons);
SegmentData segment;
Header hdr;
int time = 0;
char chunk[20];
qint64 size;
// see OMD
readHeaderFile(file->fileName(), hdr);
while ((size = file->read(chunk, sizeof(chunk))) == sizeof(chunk))
if (!readF0(chunk, hdr, time, segment))
return false;
if (size < 0) {
_errorString = "I/O error";
return false;
} else if (size) {
_errorString = "unexpected end of file";
return false;
}
tracks.append(TrackData());
tracks.last().append(segment);
return true;
}

67
src/data/onmoveparsers.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef ONMOVEPARSERS_H
#define ONMOVEPARSERS_H
#include "parser.h"
class OMDParser : public Parser
{
public:
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 0;}
private:
struct Header
{
Header() : date(QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC)),
elevation(true), hr(true) {}
QDateTime date;
bool elevation;
bool hr;
};
struct Sequence
{
Sequence() : cnt(0), idx{-1, -1} {}
unsigned cnt;
int idx[2];
};
bool readHeaderFile(const QString &omdPath, Header &hdr);
bool readF1(const char *chunk, const Header &hdr, Sequence &seq,
SegmentData &segment);
bool readF2(const char *chunk, const Header &hdr, Sequence &seq,
SegmentData &segment);
QString _errorString;
};
class GHPParser : public Parser
{
public:
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 0;}
private:
struct Header
{
Header() : date(QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC)),
hr(true) {}
QDateTime date;
bool hr;
};
bool readHeaderFile(const QString &ghpPath, Header &hdr);
bool readF0(const char *chunk, const Header &hdr, int &time,
SegmentData &segment);
QString _errorString;
};
#endif // ONMOVEPARSERS_H

View File

@ -6,8 +6,6 @@
using namespace IMG;
typedef QMap<QByteArray, VectorTile*> TileMap;
static SubFile::Type tileType(const char str[3])
{
if (!memcmp(str, "TRE", 3))
@ -26,25 +24,25 @@ static SubFile::Type tileType(const char str[3])
return SubFile::Unknown;
}
IMGData::IMGData(const QString &fileName) : _fileName(fileName)
bool IMGData::readSubFileBlocks(QFile &file, quint64 offset, SubFile *subFile)
{
#define CHECK(condition) \
if (!(condition)) { \
_errorString = "Unsupported or invalid IMG file"; \
qDeleteAll(tileMap); \
return; \
quint16 block;
if (!file.seek(offset + 0x20))
return false;
for (int i = 0; i < 240; i++) {
if (!readValue(file, block))
return false;
if (block == 0xFFFF)
break;
subFile->addBlock(block);
}
QFile file(fileName);
TileMap tileMap;
QByteArray typFile;
return true;
}
if (!file.open(QFile::ReadOnly)) {
_errorString = file.errorString();
return;
}
// Read IMG header
bool IMGData::readIMGHeader(QFile &file)
{
char signature[7], identifier[7];
file.read((char*)&_key, 1) && file.seek(0x10)
&& read(file, signature, sizeof(signature)) && file.seek(0x41)
@ -52,24 +50,35 @@ IMGData::IMGData(const QString &fileName) : _fileName(fileName)
if (memcmp(signature, "DSKIMG", sizeof(signature))
|| memcmp(identifier, "GARMIN", sizeof(identifier))) {
_errorString = "Not a Garmin IMG file";
return;
return false;
}
char d1[20], d2[31];
quint8 e1, e2;
CHECK(file.seek(0x49) && read(file, d1, sizeof(d1)) && file.seek(0x61)
if (!(file.seek(0x49) && read(file, d1, sizeof(d1)) && file.seek(0x61)
&& readValue(file, e1) && readValue(file, e2) && file.seek(0x65)
&& read(file, d2, sizeof(d2)));
&& read(file, d2, sizeof(d2)))) {
_errorString = "Error reading IMG header";
return false;
}
QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2)));
_name = QString::fromLatin1(nba.constData(), nba.size()-1).trimmed();
_blockBits = e1 + e2;
// Read the FAT table
return true;
}
bool IMGData::readFAT(QFile &file, TileMap &tileMap)
{
QByteArray typFile;
quint8 flag;
quint64 offset = 0x200;
// Skip unused FAT blocks if any
while (true) {
CHECK(file.seek(offset) && readValue(file, flag));
if (!(file.seek(offset) && readValue(file, flag)))
return false;
if (flag)
break;
offset += 512;
@ -79,17 +88,18 @@ IMGData::IMGData(const QString &fileName) : _fileName(fileName)
char name[8], type[3];
quint32 size;
quint16 part;
CHECK(file.seek(offset + 12) && readValue(file, size));
if (!(file.seek(offset + 12) && readValue(file, size)))
return false;
offset += 512;
int cnt = (size - offset) / 512;
// Read FAT blocks describing the IMG sub-files
for (int i = 0; i < cnt; i++) {
quint16 block;
CHECK(file.seek(offset) && readValue(file, flag)
if (!(file.seek(offset) && readValue(file, flag)
&& read(file, name, sizeof(name))
&& read(file, type, sizeof(type)) && readValue(file, size)
&& readValue(file, part));
&& readValue(file, part)))
return false;
SubFile::Type tt = tileType(type);
QByteArray fn(name, sizeof(name));
@ -104,15 +114,8 @@ IMGData::IMGData(const QString &fileName) : _fileName(fileName)
SubFile *subFile = part ? tile->file(tt)
: tile->addFile(this, tt);
CHECK(subFile);
CHECK(file.seek(offset + 0x20));
for (int i = 0; i < 240; i++) {
CHECK(readValue(file, block));
if (block == 0xFFFF)
break;
subFile->addBlock(block);
}
if (!(subFile && readSubFileBlocks(file, offset, subFile)))
return false;
} else if (tt == SubFile::TYP) {
SubFile *typ = 0;
if (typFile.isNull()) {
@ -122,28 +125,26 @@ IMGData::IMGData(const QString &fileName) : _fileName(fileName)
} else if (fn == typFile)
typ = _typ;
if (typ) {
CHECK(file.seek(offset + 0x20));
for (int i = 0; i < 240; i++) {
CHECK(readValue(file, block));
if (block == 0xFFFF)
break;
typ->addBlock(block);
}
}
if (typ && !readSubFileBlocks(file, offset, typ))
return false;
}
offset += 512;
}
// Create tile tree
return true;
}
bool IMGData::createTileTree(const TileMap &tileMap)
{
int minMapZoom = 24;
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
if (!tile->init()) {
qWarning("%s: %s: Invalid map tile", qPrintable(file.fileName()),
qWarning("%s: %s: Invalid map tile", qPrintable(_fileName),
qPrintable(it.key()));
delete tile;
continue;
@ -177,10 +178,32 @@ IMGData::IMGData(const QString &fileName) : _fileName(fileName)
if (!_baseMap)
_zooms.setMin(_zooms.min() - 2);
if (!_tileTree.Count())
return (_tileTree.Count() > 0);
}
IMGData::IMGData(const QString &fileName) : _fileName(fileName)
{
QFile file(fileName);
TileMap tileMap;
if (!file.open(QFile::ReadOnly)) {
_errorString = file.errorString();
return;
}
if (!readIMGHeader(file))
return;
if (!readFAT(file, tileMap)) {
_errorString = "Error reading FAT data";
qDeleteAll(tileMap);
return;
}
if (!createTileTree(tileMap)) {
_errorString = "No usable map tile found";
else
_valid = true;
return;
}
_valid = true;
}
qint64 IMGData::read(QFile &file, char *data, qint64 maxSize) const

View File

@ -18,8 +18,14 @@ public:
bool readBlock(QFile &file, int blockNum, char *data) const;
private:
typedef QMap<QByteArray, VectorTile*> TileMap;
qint64 read(QFile &file, char *data, qint64 maxSize) const;
template<class T> bool readValue(QFile &file, T &val) const;
bool readSubFileBlocks(QFile &file, quint64 offset, SubFile *subFile);
bool readFAT(QFile &file, TileMap &tileMap);
bool readIMGHeader(QFile &file);
bool createTileTree(const TileMap &tileMap);
QString _fileName;
quint8 _key;

View File

@ -57,6 +57,12 @@ static QString capitalized(const QString &str)
return ret;
}
static QByteArray ft2m(const QByteArray &str)
{
bool ok;
int number = str.toInt(&ok);
return ok ? QByteArray::number(qRound(number * 0.3048)) : str;
}
LBLFile::~LBLFile()
{
@ -130,13 +136,15 @@ void LBLFile::clear()
_rasters = 0;
}
Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize,
bool convert) const
{
Shield::Type shieldType = Shield::None;
QByteArray label, shieldLabel;
QByteArray *bap = &label;
Charset curCharSet = Normal;
quint8 b1, b2, b3;
int split = -1;
if (!seek(hdl, offset))
return Label();
@ -149,6 +157,10 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
for (int cpt = 0; cpt < 4; cpt++) {
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
if (split >= 0)
label = label.left(split) + ft2m(label.mid(split));
else if (convert)
label = ft2m(label);
QString text(QString::fromLatin1(label));
return Label(capitalize && isAllUpperCase(text)
? capitalized(text) : text, Shield(shieldType, shieldLabel));
@ -159,7 +171,16 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
curCharSet = Symbol;
else if (c[cpt] == 0x1b)
curCharSet = Special;
else if (c[cpt] >= 0x2a && c[cpt] <= 0x2f) {
else if (c[cpt] >= 0x1e && c[cpt] <= 0x1f) {
if (bap == &shieldLabel)
bap = &label;
else {
if (!bap->isEmpty())
bap->append('\n');
if (c[cpt] == 0x1f && split < 0)
split = bap->size();
}
} else if (c[cpt] >= 0x2a && c[cpt] <= 0x2f) {
shieldType = static_cast<Shield::Type>(c[cpt] - 0x29);
bap = &shieldLabel;
} else if (bap == &shieldLabel
@ -181,11 +202,13 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
}
}
Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize) const
Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize,
bool convert) const
{
Shield::Type shieldType = Shield::None;
QByteArray label, shieldLabel;
QByteArray *bap = &label;
int split = -1;
for (int i = 0; i < str.size(); i++) {
const quint8 &c = str.at(i);
@ -198,8 +221,12 @@ Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize) const
else if ((c >= 0x1e && c <= 0x1f)) {
if (bap == &shieldLabel)
bap = &label;
else
bap->append(' ');
else {
if (!bap->isEmpty())
bap->append('\n');
if (c == 0x1f && split < 0)
split = bap->size();
}
} else if (c < 0x07) {
shieldType = static_cast<Shield::Type>(c);
bap = &shieldLabel;
@ -209,13 +236,17 @@ Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize) const
bap->append(c);
}
if (split >= 0)
label = label.left(split) + ft2m(label.mid(split));
else if (convert)
label = ft2m(label);
QString text(_codec.toString(label));
return Label(capitalize && isAllUpperCase(text) ? capitalized(text) : text,
Shield(shieldType, _codec.toString(shieldLabel)));
}
Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize) const
Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize,
bool convert) const
{
QVector<quint8> str;
quint8 c;
@ -229,10 +260,11 @@ Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize) const
str.append(c);
} while (c);
return str2label(str, capitalize);
return str2label(str, capitalize, convert);
}
Label LBLFile::labelHuffman(Handle &hdl, quint32 offset, bool capitalize) const
Label LBLFile::labelHuffman(Handle &hdl, quint32 offset, bool capitalize,
bool convert) const
{
QVector<quint8> str;
@ -241,7 +273,7 @@ Label LBLFile::labelHuffman(Handle &hdl, quint32 offset, bool capitalize) const
if (!_huffmanText->decode(this, hdl, str))
return Label();
if (!_table)
return str2label(str, capitalize);
return str2label(str, capitalize, convert);
QVector<quint8> str2;
@ -268,10 +300,11 @@ Label LBLFile::labelHuffman(Handle &hdl, quint32 offset, bool capitalize) const
}
}
return str2label(str2, capitalize);
return str2label(str2, capitalize, convert);
}
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize) const
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize,
bool convert) const
{
quint32 labelOffset;
if (poi) {
@ -289,12 +322,12 @@ Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize) con
switch (_encoding) {
case 6:
return label6b(hdl, labelOffset, capitalize);
return label6b(hdl, labelOffset, capitalize, convert);
case 9:
case 10:
return label8b(hdl, labelOffset, capitalize);
return label8b(hdl, labelOffset, capitalize, convert);
case 11:
return labelHuffman(hdl, labelOffset, capitalize);
return labelHuffman(hdl, labelOffset, capitalize, convert);
default:
return Label();
}

View File

@ -32,7 +32,7 @@ public:
void clear();
Label label(Handle &hdl, quint32 offset, bool poi = false,
bool capitalize = true) const;
bool capitalize = true, bool convert = false) const;
quint8 imageIdSize() const {return _imgOffsetIdSize;}
QPixmap image(Handle &hdl, quint32 id) const;
@ -43,10 +43,14 @@ private:
quint32 size;
};
Label str2label(const QVector<quint8> &str, bool capitalize) const;
Label label6b(Handle &hdl, quint32 offset, bool capitalize) const;
Label label8b(Handle &hdl, quint32 offset, bool capitalize) const;
Label labelHuffman(Handle &hdl, quint32 offset, bool capitalize) const;
Label str2label(const QVector<quint8> &str, bool capitalize,
bool convert) const;
Label label6b(Handle &hdl, quint32 offset, bool capitalize,
bool convert) const;
Label label8b(Handle &hdl, quint32 offset, bool capitalize,
bool convert) const;
Label labelHuffman(Handle &hdl, quint32 offset, bool capitalize,
bool convert) const;
bool loadRasterTable(Handle &hdl, quint32 offset, quint32 size,
quint32 recordSize);

View File

@ -180,9 +180,9 @@ static bool readShape(const NODFile *nod, SubFile::Handle &nodHdl,
return false;
flags |= (v2 << 8);
bool hasCoordinatesAdjustBit = flags & (1 << (v2b + 7));
bool useEosBit = flags & (1 << (v2b + 5));
bool hasAdjustBit = flags & (1 << (v2b + 7));
bool startWithStream = flags & (1 << (v2b + 6));
bool useEosBit = flags & (1 << (v2b + 5));
quint32 extraBits;
int lonSign, latSign;
@ -257,7 +257,7 @@ static bool readShape(const NODFile *nod, SubFile::Handle &nodHdl,
if (!stream.readNext(lonDelta, latDelta))
break;
if (hasCoordinatesAdjustBit && !stream.read(1, adjustBit))
if (hasAdjustBit && !stream.read(1, adjustBit))
return false;
stepsCnt++;
@ -433,9 +433,9 @@ bool NETFile::link(const SubDiv *subdiv, quint32 shift, Handle &hdl,
quint16 mask = 0;
quint32 size;
bool firstIsShape = (linkInfo.flags >> 0x12) & 1;
bool singleTopology = (linkInfo.flags >> 0x11) & 1;
bool hasLevels = (linkInfo.flags >> 0x13) & 1;
bool firstIsShape = (linkInfo.flags >> 10) & 1;
bool singleTopology = (linkInfo.flags >> 9) & 1;
bool hasLevels = (linkInfo.flags >> 11) & 1;
if (!singleTopology || hasLevels) {
if (!bs.readVUInt32(size))

View File

@ -156,14 +156,12 @@ bool NODFile::linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
if (!seek(hdl, infoOffset))
return false;
quint32 unused, flags;
quint32 padding;
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
if (!(bs.read(skip, unused) && bs.read(0xc, flags)))
if (!(bs.read(skip, padding) && bs.read(0xc, linkInfo.flags)))
return false;
linkInfo.flags = ((flags << 8) & 0xf0000) | (flags & 0xff);
if (!(flags << 8 & 0x10000)) {
if (!(linkInfo.flags & 0x100)) {
if (!bs.read(s1, linkInfo.linkOffset))
return false;
} else {
@ -221,10 +219,9 @@ bool NODFile::nodeInfo(Handle &hdl, const BlockInfo &blockInfo,
pos.setX(pos.x() | extraLon << 4); pos.setY(pos.y() | extraLat << 4);
nodeInfo.bytes++;
}
// TODO?: extra bits
// TODO?: check and adjust (shift) coordinates
nodeInfo.pos = pos;
nodeInfo.flags &= 0xf8;
return true;
}
@ -311,8 +308,9 @@ bool NODFile::absAdjInfo(Handle &hdl, AdjacencyInfo &adj) const
if (firstLoop) {
skip >>= (flags >> 5) & 1;
flags |= 0x20;
firstLoop = false;
}
firstLoop = false;
quint32 f4 = flags & 0x10;
quint32 f4sn = (f4 >> 4) ^ 1;
quint32 m1 = (flags >> 5) & f4sn;
@ -416,8 +414,8 @@ bool NODFile::relAdjInfo(Handle &hdl, AdjacencyInfo &adj) const
if (firstLoop) {
skip >>= (flags >> 5) & 1;
flags = ((flags >> 1) & 0x20) | (flags & 0xffffffdf);
firstLoop = false;
}
firstLoop = false;
flagsBits >>= (flags >> 3) & 1;
quint32 m = (((flags & 0x70) == 0x30) << 1) | ((flags >> 6) & 1);
if (!m) {

View File

@ -21,13 +21,6 @@ static const QColor shieldBgColor1("#dd3e3e");
static const QColor shieldBgColor2("#379947");
static const QColor shieldBgColor3("#4a7fc1");
static QString convertUnits(const QString &str)
{
bool ok;
int number = str.toInt(&ok);
return ok ? QString::number(qRound(number * 0.3048)) : str;
}
static QFont pixelSizeFont(int pixelSize)
{
QFont f;
@ -350,9 +343,6 @@ void RasterTile::processStreetNames(const QRect &tileRect,
|| style.textFontSize() == Style::None)
continue;
if (Style::isContourLine(poly.type))
poly.label.setText(convertUnits(poly.label.text()));
const QFont *fnt = font(style.textFontSize(), Style::Small);
const QColor *color = style.textColor().isValid()
? &style.textColor() : 0;
@ -450,14 +440,6 @@ void RasterTile::processPoints(QList<TextItem*> &textItems)
if ((!label || !fnt) && !img)
continue;
if (Style::isSpot(point.type))
point.label.setText(convertUnits(point.label.text()));
if (Style::isSummit(point.type) && !point.label.text().isEmpty()) {
QStringList list = point.label.text().split(" ");
list.last() = convertUnits(list.last());
point.label = list.join(" ");
}
TextPointItem *item = new TextPointItem(QPoint(point.coordinates.lon(),
point.coordinates.lat()), label, fnt, img, color, &haloColor);
if (item->isValid() && !item->collides(textItems))

View File

@ -237,9 +237,11 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
quint32 lblOff;
if (net && net->lblOffset(netHdl, labelPtr & 0x3FFFFF, lblOff)
&& lblOff)
poly.label = lbl->label(lblHdl, lblOff);
poly.label = lbl->label(lblHdl, lblOff, false, true,
Style::isContourLine(poly.type));
} else
poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, false,
true, Style::isContourLine(poly.type));
}
polys->append(poly);
@ -351,7 +353,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
return false;
if (lbl && (labelPtr & 0x3FFFFF))
poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, false, true,
Style::isContourLine(poly.type));
polys->append(poly);
}
@ -396,7 +399,7 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF,
labelPtr & 0x400000, !(Style::isCountry(point.type)
|| Style::isState(point.type)));
|| Style::isState(point.type)), Style::isSpot(point.type));
points->append(point);
}
@ -446,7 +449,7 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv,
point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, false);
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
points->append(point);
}

View File

@ -111,8 +111,6 @@ public:
{return (type == TYPE(0x16) || type == 0x10a03);}
static bool isSpot(quint32 type)
{return (type == TYPE(0x62) || type == TYPE(0x63));}
static bool isSummit(quint32 type)
{return (type == 0x6616);}
static bool isMajorRoad(quint32 type)
{return (type <= TYPE(0x04));}
static bool isCountry(quint32 type)

View File

@ -1,240 +0,0 @@
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
#include <QBasicTimer>
#include <QDir>
#include <QTimerEvent>
#include "common/config.h"
#include "downloader.h"
#if defined(Q_OS_LINUX)
#define PLATFORM_STR "Linux"
#elif defined(Q_OS_WIN32)
#define PLATFORM_STR "Windows"
#elif defined(Q_OS_MAC)
#define PLATFORM_STR "OS X"
#else
#define PLATFORM_STR "Unknown"
#endif
#define USER_AGENT \
APP_NAME "/" APP_VERSION " (" PLATFORM_STR "; Qt " QT_VERSION_STR ")"
#define ATTR_REDIRECT QNetworkRequest::RedirectionTargetAttribute
#define ATTR_FILE QNetworkRequest::User
#define ATTR_ORIGIN \
static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 1)
#define ATTR_LEVEL \
static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User + 2)
#define MAX_REDIRECT_LEVEL 5
#define RETRIES 3
Authorization::Authorization(const QString &username, const QString &password)
{
QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64();
_header = "Basic " + data;
}
class Downloader::ReplyTimeout : public QObject
{
public:
static void setTimeout(QNetworkReply *reply, int timeout)
{
Q_ASSERT(reply);
new ReplyTimeout(reply, timeout);
}
private:
ReplyTimeout(QNetworkReply *reply, int timeout) : QObject(reply)
{
_timer.start(timeout * 1000, this);
}
void timerEvent(QTimerEvent *ev)
{
if (!_timer.isActive() || ev->timerId() != _timer.timerId())
return;
QNetworkReply *reply = static_cast<QNetworkReply*>(parent());
if (reply->isRunning())
reply->close();
_timer.stop();
}
QBasicTimer _timer;
};
class Downloader::Redirect
{
public:
Redirect() : _level(0) {}
Redirect(const QUrl &origin, int level) :
_origin(origin), _level(level) {}
const QUrl &origin() const {return _origin;}
int level() const {return _level;}
private:
QUrl _origin;
int _level;
};
QNetworkAccessManager *Downloader::_manager = 0;
int Downloader::_timeout = 30;
bool Downloader::_http2 = true;
bool Downloader::doDownload(const Download &dl,
const QByteArray &authorization, const Redirect *redirect)
{
const QUrl &url = dl.url();
if (!url.isValid() || !(url.scheme() == QLatin1String("http")
|| url.scheme() == QLatin1String("https"))) {
qWarning("%s: Invalid URL", qPrintable(url.toString()));
if (redirect)
_errorDownloads.insert(redirect->origin(), RETRIES);
return false;
}
if (_errorDownloads.value(url) >= RETRIES)
return false;
if (_currentDownloads.contains(url) && !redirect)
return false;
QNetworkRequest request(url);
request.setAttribute(ATTR_FILE, QVariant(dl.file()));
if (redirect) {
request.setAttribute(ATTR_ORIGIN, QVariant(redirect->origin()));
request.setAttribute(ATTR_LEVEL, QVariant(redirect->level()));
}
request.setRawHeader("User-Agent", USER_AGENT);
if (!authorization.isNull())
request.setRawHeader("Authorization", authorization);
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute,
QVariant(_http2));
#else // QT 5.15
request.setAttribute(QNetworkRequest::Http2AllowedAttribute,
QVariant(_http2));
#endif // QT 5.15
Q_ASSERT(_manager);
QNetworkReply *reply = _manager->get(request);
if (reply && reply->isRunning()) {
_currentDownloads.insert(url);
ReplyTimeout::setTimeout(reply, _timeout);
connect(reply, &QNetworkReply::finished, this, &Downloader::emitFinished);
} else if (reply)
downloadFinished(reply);
else
return false;
return true;
}
void Downloader::emitFinished()
{
downloadFinished(static_cast<QNetworkReply*>(sender()));
}
bool Downloader::saveToDisk(const QString &filename, QIODevice *data)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
qWarning("Error writing file: %s: %s",
qPrintable(filename), qPrintable(file.errorString()));
return false;
}
file.write(data->readAll());
file.close();
return true;
}
void Downloader::insertError(const QUrl &url, QNetworkReply::NetworkError error)
{
if (error == QNetworkReply::OperationCanceledError)
_errorDownloads.insert(url, _errorDownloads.value(url) + 1);
else
_errorDownloads.insert(url, RETRIES);
}
void Downloader::downloadFinished(QNetworkReply *reply)
{
QUrl url(reply->request().url());
QNetworkReply::NetworkError error = reply->error();
if (error) {
QUrl origin(reply->request().attribute(ATTR_ORIGIN).toUrl());
if (origin.isEmpty()) {
insertError(url, error);
qWarning("Error downloading file: %s: %s",
url.toEncoded().constData(), qPrintable(reply->errorString()));
} else {
insertError(origin, error);
qWarning("Error downloading file: %s -> %s: %s",
origin.toEncoded().constData(), url.toEncoded().constData(),
qPrintable(reply->errorString()));
}
} else {
QUrl location(reply->attribute(ATTR_REDIRECT).toUrl());
QString filename(reply->request().attribute(ATTR_FILE).toString());
if (!location.isEmpty()) {
QUrl origin(reply->request().attribute(ATTR_ORIGIN).toUrl());
int level = reply->request().attribute(ATTR_LEVEL).toInt();
if (level >= MAX_REDIRECT_LEVEL) {
_errorDownloads.insert(origin, RETRIES);
qWarning("Error downloading file: %s: "
"redirect level limit reached (redirect loop?)",
origin.toEncoded().constData());
} else {
QUrl redirectUrl;
if (location.isRelative()) {
QString path = QDir::isAbsolutePath(location.path())
? location.path() : "/" + location.path();
redirectUrl = QUrl(url.scheme() + "://" + url.host() + path);
} else
redirectUrl = location;
Redirect redirect(origin.isEmpty() ? url : origin, level + 1);
Download dl(redirectUrl, filename);
doDownload(dl, reply->request().rawHeader("Authorization"),
&redirect);
}
} else {
if (!saveToDisk(filename, reply))
_errorDownloads.insert(url, RETRIES);
}
}
_currentDownloads.remove(url);
reply->deleteLater();
if (_currentDownloads.isEmpty())
emit finished();
}
bool Downloader::get(const QList<Download> &list,
const Authorization &authorization)
{
bool finishEmitted = false;
for (int i = 0; i < list.count(); i++)
finishEmitted |= doDownload(list.at(i), authorization.header());
return finishEmitted;
}
void Downloader::enableHTTP2(bool enable)
{
Q_ASSERT(_manager);
_http2 = enable;
_manager->clearConnectionCache();
}

View File

@ -5,7 +5,7 @@
#include "common/range.h"
#include "common/rectc.h"
#include "common/kv.h"
#include "downloader.h"
#include "common/downloader.h"
#include "coordinatesystem.h"
class Map;

View File

@ -3,7 +3,7 @@
#include <QDir>
#include "common/rectc.h"
#include "common/programpaths.h"
#include "downloader.h"
#include "common/downloader.h"
#include "osm.h"
#include "onlinemap.h"

View File

@ -3,8 +3,8 @@
#include <QObject>
#include <QString>
#include "common/downloader.h"
#include "tile.h"
#include "downloader.h"
class TileLoader : public QObject
{
@ -38,4 +38,4 @@ private:
bool _quadTiles;
};
#endif // TILELOADER_Honlinemap
#endif // TILELOADER_H

View File

@ -3,7 +3,6 @@
#include <QEventLoop>
#include <QXmlStreamReader>
#include <QStringList>
#include "downloader.h"
#include "crs.h"
#include "wms.h"
@ -320,20 +319,6 @@ bool WMS::parseCapabilities()
return true;
}
bool WMS::downloadCapabilities(const QString &url)
{
if (!_downloader) {
_downloader = new Downloader(this);
connect(_downloader, &Downloader::finished, this,
&WMS::capabilitiesReady);
}
QList<Download> dl;
dl.append(Download(url, _path));
return _downloader->get(dl, _setup.authorization());
}
void WMS::capabilitiesReady()
{
if (!QFileInfo(_path).exists()) {
@ -348,15 +333,20 @@ void WMS::capabilitiesReady()
}
WMS::WMS(const QString &file, const WMS::Setup &setup, QObject *parent)
: QObject(parent), _setup(setup), _path(file), _downloader(0), _valid(false),
_ready(false)
: QObject(parent), _setup(setup), _path(file), _valid(false), _ready(false)
{
QString url = QString("%1%2service=WMS&request=GetCapabilities")
.arg(setup.url(), setup.url().contains('?') ? "&" : "?");
if (!QFileInfo(file).exists())
_valid = downloadCapabilities(url);
else {
if (!QFileInfo(file).exists()) {
Downloader *downloader = new Downloader(this);
connect(downloader, &Downloader::finished, this,
&WMS::capabilitiesReady);
QList<Download> dl;
dl.append(Download(url, _path));
_valid = downloader->get(dl, _setup.authorization());
} else {
_ready = true;
_valid = parseCapabilities();
}

View File

@ -6,8 +6,8 @@
#include "common/range.h"
#include "common/rectc.h"
#include "common/kv.h"
#include "common/downloader.h"
#include "projection.h"
#include "downloader.h"
#include "coordinatesystem.h"
class QXmlStreamReader;
@ -109,11 +109,9 @@ private:
void capability(QXmlStreamReader &reader, CTX &ctx);
void capabilities(QXmlStreamReader &reader, CTX &ctx);
bool parseCapabilities();
bool downloadCapabilities(const QString &url);
WMS::Setup _setup;
QString _path;
Downloader *_downloader;
Projection _projection;
RangeF _scaleDenominator;
RectC _bbox;

View File

@ -4,7 +4,6 @@
#include "common/wgs84.h"
#include "common/rectc.h"
#include "common/programpaths.h"
#include "downloader.h"
#include "tileloader.h"
#include "wmsmap.h"

View File

@ -6,7 +6,6 @@
#include <QStringList>
#include <QtAlgorithms>
#include <QXmlStreamReader>
#include "downloader.h"
#include "pcs.h"
#include "crs.h"
#include "wmts.h"
@ -302,20 +301,6 @@ bool WMTS::parseCapabilities(CTX &ctx)
return true;
}
bool WMTS::downloadCapabilities(const QString &url)
{
if (!_downloader) {
_downloader = new Downloader(this);
connect(_downloader, &Downloader::finished, this,
&WMTS::capabilitiesReady);
}
QList<Download> dl;
dl.append(Download(url, _path));
return _downloader->get(dl, _setup.authorization());
}
void WMTS::capabilitiesReady()
{
if (!QFileInfo(_path).exists()) {
@ -363,7 +348,7 @@ bool WMTS::init()
}
WMTS::WMTS(const QString &file, const WMTS::Setup &setup, QObject *parent)
: QObject(parent), _setup(setup), _downloader(0), _valid(false), _ready(false)
: QObject(parent), _setup(setup), _valid(false), _ready(false)
{
QUrl url(setup.rest() ? setup.url() : QString(
"%1%2service=WMTS&Version=1.0.0&request=GetCapabilities").arg(setup.url(),
@ -371,9 +356,15 @@ WMTS::WMTS(const QString &file, const WMTS::Setup &setup, QObject *parent)
_path = url.isLocalFile() ? url.toLocalFile() : file;
if (!url.isLocalFile() && !QFileInfo(file).exists())
_valid = downloadCapabilities(url.toString());
else {
if (!url.isLocalFile() && !QFileInfo(file).exists()) {
Downloader *downloader = new Downloader(this);
connect(downloader, &Downloader::finished, this,
&WMTS::capabilitiesReady);
QList<Download> dl;
dl.append(Download(url.toString(), _path));
_valid = downloader->get(dl, _setup.authorization());
} else {
_ready = true;
_valid = init();
}

View File

@ -9,8 +9,8 @@
#include "common/config.h"
#include "common/rectc.h"
#include "common/kv.h"
#include "common/downloader.h"
#include "projection.h"
#include "downloader.h"
#include "coordinatesystem.h"
class QXmlStreamReader;
@ -154,13 +154,11 @@ private:
void contents(QXmlStreamReader &reader, CTX &ctx);
void capabilities(QXmlStreamReader &reader, CTX &ctx);
bool parseCapabilities(CTX &ctx);
bool downloadCapabilities(const QString &url);
void createZooms(const CTX &ctx);
bool init();
WMTS::Setup _setup;
QString _path;
Downloader *_downloader;
RectC _bbox;
QList<Zoom> _zooms;
Projection _projection;