1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-17 04:14:24 +02:00

Compare commits

..

135 Commits
7.25 ... 7.31

Author SHA1 Message Date
14f4dead76 Merge branch 'origin/master' into Weblate. 2020-06-29 23:46:00 +02:00
96bb3bbdbb Removed obsolete stuff 2020-06-29 23:46:06 +02:00
22d18b6d4e Merge branch 'origin/master' into Weblate. 2020-06-28 21:02:09 +02:00
c1b79217a9 Update gpxsee_en.ts (#296) 2020-06-28 21:02:05 +02:00
e67a14b072 Merge branch 'origin/master' into Weblate. 2020-06-28 19:52:37 +02:00
473d03cf1f Fixed broken extended objects segment fetching 2020-06-28 19:51:59 +02:00
a339706293 Version++ 2020-06-27 22:51:53 +02:00
39c414ca73 Merge branch 'origin/master' into Weblate. 2020-06-27 22:51:44 +02:00
c59d60faed Merge branch 'origin/master' into Weblate. 2020-06-27 22:50:15 +02:00
32d3eab10e Initial (and partial) IMG links support
+ various IMG fixes (RGN parsing, IMG parsing)
2020-06-27 22:46:26 +02:00
e7729e8745 Added missing Italian localization stuff 2020-06-27 18:05:50 +02:00
bf145c9eb5 Merge branch 'origin/master' into Weblate. 2020-06-27 18:05:48 +02:00
de0a6b0397 Merge branch 'origin/master' into Weblate. 2020-06-27 18:00:07 +02:00
8cf89a580f Removed Arabic translation stub as noone is evidently gona use it despite
the Weblate request...
2020-06-27 17:59:15 +02:00
152e2a8a09 Merge branch 'origin/master' into Weblate. 2020-06-27 17:57:23 +02:00
6b860fe18c added italian translation (#295)
Added italian translation
2020-06-27 17:57:18 +02:00
95f138f5f0 Translated using Weblate (German)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2020-06-11 13:41:42 +02:00
1fc4dbbb73 Cosmetics 2020-05-30 17:10:13 +02:00
0999cdcba2 Fixed Qt version with time zones support
+ reverted broken Qt version check for opengl
2020-05-30 17:06:06 +02:00
cc16c9e79b Merge branch 'origin/master' into Weblate. 2020-05-30 14:20:58 +02:00
1990c85fd7 Updated Antarctica map URL 2020-05-30 14:20:20 +02:00
58f70fa833 Merge branch 'origin/master' into Weblate. 2020-05-30 14:00:01 +02:00
0f6c50d588 Added missing support for nested KML Documents 2020-05-30 13:59:34 +02:00
89dce5152e Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pt_BR/
2020-05-29 01:41:40 +02:00
8bce6a44ed Translated using Weblate (French)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fr/
2020-05-29 01:41:40 +02:00
59ecd3fdf0 Translated using Weblate (Turkish)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2020-05-26 12:41:37 +02:00
a10c729e52 Merge branch 'origin/master' into Weblate. 2020-05-21 20:07:40 +02:00
369601f102 Translated using Weblate (Hungarian)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2020-05-21 20:07:39 +02:00
47d0feeb46 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2020-05-21 20:07:39 +02:00
58a0acc718 Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.4% (357 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2020-05-21 20:07:39 +02:00
c466527625 Translated using Weblate (Russian)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2020-05-21 20:07:38 +02:00
9cd00075c7 Translated using Weblate (Finnish)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2020-05-21 20:07:38 +02:00
6f72d46d6c Merge branch 'master' of github.com:tumic0/GPXSee 2020-05-21 20:07:29 +02:00
54467e6d45 Fixed build with Qt < 5.2 2020-05-21 20:06:49 +02:00
3d2e33361d Merge branch 'origin/master' into Weblate. 2020-05-20 23:56:01 +02:00
f91df0d026 Translated using Weblate (Swedish)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2020-05-20 23:56:01 +02:00
9bd004359d Fixed OS X build 2020-05-20 23:45:06 +02:00
e170f92e79 Translated using Weblate (Czech)
Currently translated at 100.0% (359 of 359 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2020-05-20 21:13:48 +02:00
0fb5d8dae6 Czech translation 2020-05-20 21:10:32 +02:00
5ff931bb5e Localization update 2020-05-20 21:02:33 +02:00
5bd744a8ed Added time zone settings 2020-05-20 21:00:36 +02:00
035883aab2 Added github FUNDING.yml file 2020-05-03 11:26:43 +02:00
571ed087e3 Back to the original modified Z-score treshold (5) 2020-05-02 20:44:24 +02:00
c461b2e549 Added support for non-standard 8-parts CRSs 2020-05-02 09:48:30 +02:00
26b5411465 Version++ 2020-04-30 21:48:07 +02:00
8965f450ce 32 layers ought to be enough for anybody 2020-04-30 21:46:41 +02:00
a958544667 Added support for IMG maps overlays 2020-04-26 15:46:42 +02:00
ddf865834a Remove unused consructor 2020-04-26 02:01:25 +02:00
a4abed8f1f Code cleanup 2020-04-26 01:17:54 +02:00
56061c93cb Merge branch 'origin/master' into Weblate. 2020-04-22 22:31:14 +02:00
7385b08262 Added Arabic translations file stub 2020-04-22 22:30:45 +02:00
9d79bd9a9d Merge branch 'origin/master' into Weblate. 2020-04-22 00:47:40 +02:00
159e5aeae9 Fixed error handling 2020-04-22 00:47:12 +02:00
d8beaed876 Merge branch 'origin/master' into Weblate. 2020-04-21 23:28:08 +02:00
c1584f30d2 Limit the map bounds properly based on projection, not a magic height 2020-04-21 23:26:35 +02:00
efcefe8fec Merge branch 'origin/master' into Weblate. 2020-04-19 14:52:45 +02:00
cbe312d9c8 Version++ 2020-04-19 14:52:40 +02:00
5322ee96c8 Merge branch 'origin/master' into Weblate. 2020-04-19 11:36:41 +02:00
08334d7fde Move the world maps bounds limit hack to the propper place 2020-04-19 11:36:17 +02:00
51d4e04343 Merge branch 'origin/master' into Weblate. 2020-04-18 00:01:28 +02:00
33bbd6a592 Yet another special case 2020-04-18 00:00:48 +02:00
0f96bc602c Merge branch 'origin/master' into Weblate. 2020-04-15 22:48:54 +02:00
7811527239 Rather show less road shields than more 2020-04-15 22:48:28 +02:00
31da4e1906 Some more default IMG style tweaking 2020-04-15 22:48:02 +02:00
cb6a82a10a Merge branch 'origin/master' into Weblate. 2020-04-09 10:17:58 +02:00
652cbd7c11 Fixed Qt4 build 2020-04-09 10:17:30 +02:00
ff0711c620 Merge branch 'origin/master' into Weblate. 2020-04-08 22:28:54 +02:00
eb0ff84379 Code cleanup 2020-04-08 22:28:35 +02:00
74775b2c62 Merge branch 'origin/master' into Weblate. 2020-04-08 00:55:02 +02:00
6ee3a8ea8d Added support for FIT course points 2020-04-08 00:54:35 +02:00
ee3d43e249 A slightly darker white 2020-04-08 00:00:43 +02:00
a6fbae38b8 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pt_BR/
2020-04-07 15:09:42 +02:00
242babb741 Improved default IMG style
("less green")
2020-04-07 00:54:31 +02:00
e26d1abd5a Version++ 2020-04-05 10:50:24 +02:00
e80d16bec5 Merge branch 'origin/master' into Weblate. 2020-04-05 10:39:16 +02:00
412ae74bfa Fixed broken map enable condition
(falsly enabled map can crash)
2020-04-05 10:38:16 +02:00
1c472e47b9 Translated using Weblate (Hungarian)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2020-04-04 20:09:37 +02:00
4a725375e2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2020-04-02 12:09:36 +02:00
383a196414 Translated using Weblate (Russian)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2020-04-02 12:09:36 +02:00
f05b51efa6 Translated using Weblate (Finnish)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2020-04-02 12:09:36 +02:00
a56c02953f Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.4% (354 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2020-03-29 20:05:00 +02:00
00d3849e4f Translated using Weblate (French)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fr/
2020-03-29 20:05:00 +02:00
c9244c0684 Fixed broken graph color change when secondary graphs present 2020-03-28 23:28:39 +01:00
d94938261a Version++ 2020-03-28 19:50:39 +01:00
d5fc06d9d1 Fixed remaining qWarning() format warning 2020-03-28 19:15:03 +01:00
9e7ebe930e Do not rescale the map on tile cache reload
(we do not reload the map parameters any more)
2020-03-28 16:12:15 +01:00
19bc509043 Merge remote-tracking branch 'weblate/master' 2020-03-27 23:30:44 +01:00
335794ee21 Translated using Weblate (German)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2020-03-27 23:10:10 +01:00
2404107d87 Translated using Weblate (German)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2020-03-27 23:10:09 +01:00
9447addd19 German translation 2020-03-27 23:09:43 +01:00
b1647d944c Silenced clang warnings 2020-03-27 23:09:13 +01:00
77ac919b83 Fixed broken error path reporting 2020-03-27 23:03:11 +01:00
4d652aeaff Merge branch 'origin/master' into Weblate. 2020-03-27 20:48:18 +01:00
3ef2361523 Removed obsolete stuff 2020-03-27 20:47:45 +01:00
e2b1c2c778 Merge branch 'origin/master' into Weblate. 2020-03-27 00:12:15 +01:00
1f5ecdfc38 Use a unicode character constant that works on all OSs (Windows) 2020-03-27 00:11:12 +01:00
c55b4f1217 Translated using Weblate (Swedish)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2020-03-26 18:47:50 +01:00
fd71a4c7ce Translated using Weblate (Czech)
Currently translated at 100.0% (356 of 356 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2020-03-26 18:47:49 +01:00
0b6b09f226 Merge branch 'origin/master' into Weblate. 2020-03-25 23:33:35 +01:00
517ca89814 Localization update 2020-03-25 23:33:12 +01:00
455ec3a54b Merge branch 'origin/master' into Weblate. 2020-03-25 23:20:03 +01:00
8cb8d97ee2 Use the standard value of 3.5 in the outlier test 2020-03-25 23:19:30 +01:00
40e520d3bf Merge branch 'origin/master' into Weblate. 2020-03-25 23:12:37 +01:00
6b75442312 Removed obsolete code 2020-03-25 23:12:21 +01:00
cbc5b2466e Merge branch 'origin/master' into Weblate. 2020-03-25 23:10:04 +01:00
19a847c7d4 Enable simultaneous display of GPS and DEM data 2020-03-25 23:08:26 +01:00
f9e5cb206f Merge branch 'origin/master' into Weblate. 2020-03-24 22:06:34 +01:00
441c738d0f Allow IGC files with an A header of size 6 2020-03-24 22:06:03 +01:00
0bf6d41de6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (354 of 354 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pt_BR/
2020-03-23 04:48:54 +01:00
b7869e985d Translated using Weblate (Russian)
Currently translated at 100.0% (354 of 354 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2020-03-18 16:45:24 +01:00
5152d5eb0b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (354 of 354 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2020-03-18 16:45:23 +01:00
c0653ab0a8 Translated using Weblate (Finnish)
Currently translated at 100.0% (354 of 354 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2020-03-18 16:45:22 +01:00
41d27cabe2 Translated using Weblate (Swedish)
Currently translated at 100.0% (354 of 354 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2020-03-18 16:45:12 +01:00
e2f2e9700f Localization update 2020-03-17 21:13:21 +01:00
654bfcd058 Version++ 2020-03-17 21:10:45 +01:00
82c0c1f8a7 Asynchronous WMS/WMTS map loading
(also fixes crash on OS X)
2020-03-17 21:06:51 +01:00
9ce6e16b60 Fixed graph axis ticks when range < min range and units != m
Fixes #278
2020-03-14 23:55:57 +01:00
98cd3c3922 Merge branch 'master' of https://github.com/tumic0/GPXSee 2020-03-12 09:37:55 +01:00
a776f1d30e Added missing button group on OS X
fixes #276
2020-03-12 09:36:23 +01:00
aea17c9fed Added support for GPX comments (cmt tag)
Closes #272
2020-03-09 20:04:13 +01:00
23c18d4acd Optimization 2020-03-07 19:24:39 +01:00
eb8fc7b540 Merge branch 'origin/master' into Weblate. 2020-03-04 19:49:27 +01:00
9b687bb830 Merge branch 'origin/master' into Weblate. 2020-03-04 19:48:15 +01:00
3d66b2fbb6 Merge branch 'origin/master' into Weblate. 2020-03-03 09:39:27 +01:00
c8f7e6f691 Merge branch 'origin/master' into Weblate. 2020-03-03 09:29:47 +01:00
273a0f0f27 Merge branch 'origin/master' into Weblate. 2020-03-01 14:39:44 +01:00
bd3a3b90bc Merge branch 'origin/master' into Weblate. 2020-03-01 13:59:42 +01:00
440a5736f6 Merge branch 'origin/master' into Weblate. 2020-03-01 13:26:52 +01:00
f73c27c39c Merge branch 'origin/master' into Weblate. 2020-03-01 11:46:52 +01:00
3ec5c37fd5 Merge branch 'origin/master' into Weblate. 2020-03-01 11:43:41 +01:00
ef017edbf6 Merge branch 'origin/master' into Weblate. 2020-02-29 21:40:28 +01:00
dc03ab91d6 Merge branch 'origin/master' into Weblate. 2020-02-29 20:12:26 +01:00
497017091f Merge branch 'origin/master' into Weblate. 2020-02-29 13:48:19 +01:00
86535021aa Merge branch 'origin/master' into Weblate. 2020-02-23 12:45:59 +01:00
86a943d143 Translated using Weblate (Polish)
Currently translated at 98.5% (346 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pl/
2020-02-20 12:42:26 +01:00
145 changed files with 9657 additions and 5593 deletions

View File

@ -1,4 +1,4 @@
version: 7.25.{build}
version: 7.31.{build}
configuration:
- Release

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: tumic0

View File

@ -3,7 +3,7 @@ unix:!macx {
} else {
TARGET = GPXSee
}
VERSION = 7.25
VERSION = 7.31
QT += core \
gui \
@ -20,6 +20,7 @@ equals(QT_MAJOR_VERSION, 5) : lessThan(QT_MINOR_VERSION, 4) {QT += opengl}
INCLUDEPATH += ./src
HEADERS += src/common/config.h \
src/GUI/graphicsscene.h \
src/GUI/mapaction.h \
src/GUI/popup.h \
src/common/garmin.h \
src/common/staticassert.h \
@ -94,7 +95,9 @@ HEADERS += src/common/config.h \
src/map/IMG/gmap.h \
src/map/IMG/huffmanstream.h \
src/map/IMG/huffmantable.h \
src/map/IMG/nodfile.h \
src/map/IMG/mapdata.h \
src/map/IMG/rastertile.h \
src/map/IMG/textpathitem.h \
src/map/IMG/textpointitem.h \
src/map/projection.h \
@ -248,13 +251,16 @@ SOURCES += src/main.cpp \
src/GUI/gearratiographitem.cpp \
src/GUI/mapview.cpp \
src/GUI/areaitem.cpp \
src/data/waypoint.cpp \
src/map/IMG/bitmapline.cpp \
src/map/IMG/bitstream.cpp \
src/map/IMG/deltastream.cpp \
src/map/IMG/gmap.cpp \
src/map/IMG/huffmanstream.cpp \
src/map/IMG/huffmantable.cpp \
src/map/IMG/nodfile.cpp \
src/map/IMG/mapdata.cpp \
src/map/IMG/rastertile.cpp \
src/map/IMG/textpathitem.cpp \
src/map/IMG/textpointitem.cpp \
src/map/maplist.cpp \
@ -344,6 +350,9 @@ greaterThan(QT_MAJOR_VERSION, 4) {
HEADERS += src/data/geojsonparser.h
SOURCES += src/data/geojsonparser.cpp
}
equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 4) {
HEADERS += src/GUI/timezoneinfo.h
}
DEFINES += APP_VERSION=\\\"$$VERSION\\\" \
QT_NO_DEPRECATED_WARNINGS
@ -364,7 +373,8 @@ TRANSLATIONS = lang/gpxsee_en.ts \
lang/gpxsee_es.ts \
lang/gpxsee_pt_BR.ts \
lang/gpxsee_uk.ts \
lang/gpxsee_hu.ts
lang/gpxsee_hu.ts \
lang/gpxsee_it.ts
macx {
ICON = icons/gpxsee.icns
@ -384,7 +394,8 @@ macx {
lang/gpxsee_es.qm \
lang/gpxsee_pt_BR.qm \
lang/gpxsee_uk.qm \
lang/gpxsee_hu.qm
lang/gpxsee_hu.qm \
lang/gpxsee_it.qm
csv.path = Contents/Resources
csv.files = pkg/csv
maps.path = Contents/Resources

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

@ -1,4 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>GUI</name>
<message numerus="yes">
<location filename="../src/GUI/gui.cpp" line="1392"/>
<source>%n files</source>
<translation>
<numerusform>%n file</numerusform>
<numerusform>%n files</numerusform>
</translation>
</message>
</context>
</TS>

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

1940
lang/gpxsee_it.ts Normal file

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

@ -7,7 +7,7 @@
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "7.25"
!define VERSION "7.31"
; The file to write
OutFile "GPXSee-${VERSION}.exe"
@ -177,6 +177,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
!insertmacro LOCALIZATION "French" "fr"
!insertmacro LOCALIZATION "German" "de"
!insertmacro LOCALIZATION "Hungarian" "hu"
!insertmacro LOCALIZATION "Italian" "it"
!insertmacro LOCALIZATION "Norwegian" "nb"
!insertmacro LOCALIZATION "Polish" "pl"
!insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR"

View File

@ -7,7 +7,7 @@
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "7.25"
!define VERSION "7.31"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"
@ -184,6 +184,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
!insertmacro LOCALIZATION "French" "fr"
!insertmacro LOCALIZATION "German" "de"
!insertmacro LOCALIZATION "Hungarian" "hu"
!insertmacro LOCALIZATION "Italian" "it"
!insertmacro LOCALIZATION "Norwegian" "nb"
!insertmacro LOCALIZATION "Polish" "pl"
!insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR"

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.4" type="WMTS">
<name>Antarctica</name>
<url type="REST">https://gis.ngdc.noaa.gov/arcgis/rest/services/antarctic/antarctic_basemap/MapServer/WMTS/1.0.0/WMTSCapabilities.xml</url>
<url type="REST">https://tiles.arcgis.com/tiles/C8EMgrsFcRFL6LrL/arcgis/rest/services/Antarctic_Basemap/MapServer/WMTS/1.0.0/WMTSCapabilities.xml</url>
<copyright>NOAA National Centers for Environmental Information (NCEI); International Bathymetric Chart of the Southern Ocean (IBCSO); General Bathymetric Chart of the Oceans (GEBCO); Natural Earth</copyright>
<layer>antarctic_antarctic_basemap</layer>
<layer>Antarctic_Basemap</layer>
<set>default028mm</set>
</map>

View File

@ -5,7 +5,7 @@
CadenceGraphItem::CadenceGraphItem(const Graph &graph, GraphType type,
int width, const QColor &color, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
{
}

View File

@ -65,35 +65,38 @@ void ElevationGraph::setInfo()
}
}
GraphItem *ElevationGraph::loadGraph(const Graph &graph, Type type)
GraphItem *ElevationGraph::loadGraph(const Graph &graph, PathType type,
const QColor &color, bool primary)
{
if (!graph.isValid()) {
_palette.nextColor();
if (!graph.isValid())
return 0;
}
ElevationGraphItem *gi = new ElevationGraphItem(graph, _graphType, _width,
_palette.nextColor());
color, primary ? Qt::SolidLine : Qt::DashLine);
gi->setUnits(_units);
if (type == Track) {
if (type == TrackPath) {
_tracks.append(gi);
if (_showTracks)
addGraph(gi);
_trackAscent += gi->ascent();
_trackDescent += gi->descent();
_trackMax = nMax(_trackMax, gi->max());
_trackMin = nMin(_trackMin, gi->min());
if (primary) {
_trackAscent += gi->ascent();
_trackDescent += gi->descent();
_trackMax = nMax(_trackMax, gi->max());
_trackMin = nMin(_trackMin, gi->min());
}
} else {
_routes.append(gi);
if (_showRoutes)
addGraph(gi);
_routeAscent += gi->ascent();
_routeDescent += gi->descent();
_routeMax = nMax(_routeMax, gi->max());
_routeMin = nMin(_routeMin, gi->min());
if (primary) {
_routeAscent += gi->ascent();
_routeDescent += gi->descent();
_routeMax = nMax(_routeMax, gi->max());
_routeMin = nMin(_routeMin, gi->min());
}
}
return gi;
@ -102,11 +105,32 @@ GraphItem *ElevationGraph::loadGraph(const Graph &graph, Type type)
QList<GraphItem*> ElevationGraph::loadData(const Data &data)
{
QList<GraphItem*> graphs;
GraphItem *primary, *secondary;
for (int i = 0; i < data.tracks().count(); i++)
graphs.append(loadGraph(data.tracks().at(i).elevation(), Track));
for (int i = 0; i < data.routes().count(); i++)
graphs.append(loadGraph(data.routes().at(i).elevation(), Route));
for (int i = 0; i < data.tracks().count(); i++) {
QColor color(_palette.nextColor());
const GraphPair &gp = data.tracks().at(i).elevation();
primary = loadGraph(gp.primary(), TrackPath, color, true);
secondary = primary
? loadGraph(gp.secondary(), TrackPath, color, false) : 0;
if (primary && secondary)
primary->setSecondaryGraph(secondary);
graphs.append(primary);
}
for (int i = 0; i < data.routes().count(); i++) {
QColor color(_palette.nextColor());
const GraphPair &gp = data.routes().at(i).elevation();
primary = loadGraph(gp.primary(), RoutePath, color, true);
secondary = primary
? loadGraph(gp.secondary(), RoutePath, color, false) : 0;
if (primary && secondary)
primary->setSecondaryGraph(secondary);
graphs.append(primary);
}
for (int i = 0; i < data.areas().count(); i++)
_palette.nextColor();

View File

@ -21,7 +21,7 @@ public:
void showRoutes(bool show);
private:
enum Type {Track, Route};
enum PathType {TrackPath, RoutePath};
qreal max() const;
qreal min() const;
@ -31,7 +31,8 @@ private:
void setYUnits(Units units);
void setInfo();
GraphItem *loadGraph(const Graph &graph, Type type);
GraphItem *loadGraph(const Graph &graph, PathType type, const QColor &color,
bool primary);
void showItems(const QList<ElevationGraphItem *> &list, bool show);
qreal _trackAscent, _trackDescent;

View File

@ -4,8 +4,8 @@
ElevationGraphItem::ElevationGraphItem(const Graph &graph, GraphType type,
int width, const QColor &color, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
int width, const QColor &color, Qt::PenStyle style, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, style, parent)
{
_min = GraphItem::min();
_max = GraphItem::max();
@ -42,5 +42,6 @@ QString ElevationGraphItem::info() const
tt.insert(tr("Minimum"), l.toString(min() * scale, 'f', 0)
+ UNIT_SPACE + su);
return tt.toString();
}

View File

@ -8,8 +8,10 @@ class ElevationGraphItem : public GraphItem
Q_OBJECT
public:
enum DataType {GPS, DEM};
ElevationGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent = 0);
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0);
qreal ascent() const {return _ascent;}
qreal descent() const {return _descent;}

View File

@ -6,7 +6,7 @@
GearRatioGraphItem::GearRatioGraphItem(const Graph &graph, GraphType type,
int width, const QColor &color, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
{
for (int i = 0; i < graph.size(); i++) {
const GraphSegment &segment = graph.at(i);

View File

@ -5,13 +5,13 @@
GraphItem::GraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent)
: GraphicsItem(parent), _graph(graph), _type(type)
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent)
: GraphicsItem(parent), _graph(graph), _type(type), _secondaryGraph(0)
{
Q_ASSERT(_graph.isValid());
_units = Metric;
_pen = QPen(color, width);
_pen = QPen(color, width, style);
_sx = 0; _sy = 0;
_time = _graph.hasTime();
setZValue(2.0);

View File

@ -12,8 +12,8 @@ class GraphItem : public QObject, public GraphicsItem
Q_OBJECT
public:
GraphItem(const Graph &graph, GraphType type, int width, const QColor &color,
QGraphicsItem *parent = 0);
GraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0);
virtual ~GraphItem() {}
virtual QString info() const = 0;
@ -35,6 +35,9 @@ public:
void setWidth(int width);
void setUnits(Units units) {_units = units;}
GraphItem *secondaryGraph() const {return _secondaryGraph;}
void setSecondaryGraph(GraphItem *graph) {_secondaryGraph = graph;}
qreal yAtX(qreal x);
qreal distanceAtTime(qreal time);
@ -69,6 +72,8 @@ private:
qreal _sx, _sy;
QPen _pen;
bool _time;
GraphItem *_secondaryGraph;
};
#endif // GRAPHITEM_H

View File

@ -1,3 +1,4 @@
#include <QSet>
#include <QGraphicsScene>
#include <QEvent>
#include <QMouseEvent>
@ -274,8 +275,8 @@ void GraphView::redraw(const QSizeF &size)
rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
+ _yOffset);
if (ry.size() < _minYRange)
ry.resize(_minYRange);
if (ry.size() < _minYRange * _yScale)
ry.resize(_minYRange * _yScale);
_xAxis->setRange(rx);
_yAxis->setRange(ry);
@ -419,14 +420,16 @@ void GraphView::updateSliderInfo()
{
QLocale l(QLocale::system());
qreal r = 0, y = 0;
GraphItem *cardinal = (_graphs.count() == 1 || (_graphs.count() == 2
&& _graphs.first()->secondaryGraph())) ? _graphs.first() : 0;
if (_graphs.count() == 1) {
QRectF br(_graphs.first()->bounds());
if (cardinal) {
QRectF br(_bounds);
if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2);
y = _graphs.first()->yAtX(_sliderPos);
y = cardinal->yAtX(_sliderPos);
r = (y - br.bottom()) / br.height();
}
@ -436,11 +439,17 @@ void GraphView::updateSliderInfo()
_sliderInfo->setSide(s);
_sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r));
_sliderInfo->setText(_graphType == Time ? Format::timeSpan(_sliderPos,
QString xText(_graphType == Time ? Format::timeSpan(_sliderPos,
bounds().width() > 3600) : l.toString(_sliderPos * _xScale, 'f', 1)
+ UNIT_SPACE + _xUnits, (_graphs.count() > 1) ? QString()
: l.toString(-y * _yScale + _yOffset, 'f', _precision) + UNIT_SPACE
+ _yUnits);
+ UNIT_SPACE + _xUnits);
QString yText((!cardinal) ? QString() : l.toString(-y * _yScale + _yOffset,
'f', _precision) + UNIT_SPACE + _yUnits);
if (cardinal && cardinal->secondaryGraph()) {
qreal delta = y - cardinal->secondaryGraph()->yAtX(_sliderPos);
yText += " " + QChar(0x0394) + l.toString(-delta * _yScale + _yOffset,
'f', _precision) + UNIT_SPACE + _yUnits;
}
_sliderInfo->setText(xText, yText);
}
void GraphView::emitSliderPositionChanged(const QPointF &pos)
@ -486,8 +495,23 @@ void GraphView::setPalette(const Palette &palette)
_palette = palette;
_palette.reset();
for (int i = 0; i < _graphs.count(); i++)
_graphs.at(i)->setColor(_palette.nextColor());
QSet<GraphItem*> secondary;
for (int i = 0; i < _graphs.count(); i++) {
GraphItem *g = _graphs[i];
if (g->secondaryGraph())
secondary.insert(g->secondaryGraph());
}
for (int i = 0; i < _graphs.count(); i++) {
GraphItem *g = _graphs[i];
if (secondary.contains(g))
continue;
QColor color(_palette.nextColor());
g->setColor(color);
if (g->secondaryGraph())
g->secondaryGraph()->setColor(color);
}
}
void GraphView::setGraphWidth(int width)

View File

@ -3,7 +3,6 @@
#include <QGraphicsView>
#include <QList>
#include <QSet>
#include "data/graph.h"
#include "palette.h"
#include "units.h"

View File

@ -51,6 +51,7 @@
#include "graphtab.h"
#include "graphitem.h"
#include "pathitem.h"
#include "mapaction.h"
#include "gui.h"
@ -58,7 +59,6 @@
GUI::GUI()
{
loadMaps();
loadPOIs();
createMapView();
@ -106,24 +106,13 @@ GUI::GUI()
updateStatusBarInfo();
}
void GUI::loadMaps()
{
_ml = new MapList(this);
QString mapDir(ProgramPaths::mapDir());
if (!mapDir.isNull() && !_ml->loadDir(mapDir))
qWarning("%s", qPrintable(_ml->errorPath() + ": " + _ml->errorString()));
_map = new EmptyMap(this);
}
void GUI::loadPOIs()
{
_poi = new POI(this);
QString poiDir(ProgramPaths::poiDir());
if (!poiDir.isNull() && !_poi->loadDir(poiDir))
qWarning("%s", qPrintable(_poi->errorString()));
QString poiDir(ProgramPaths::poiDir());
if (!poiDir.isNull())
_poi->loadDir(poiDir);
}
void GUI::createBrowser()
@ -134,40 +123,56 @@ void GUI::createBrowser()
void GUI::createMapActions()
{
_mapsSignalMapper = new QSignalMapper(this);
_mapsActionGroup = new QActionGroup(this);
_mapsActionGroup->setExclusive(true);
for (int i = 0; i < _ml->maps().count(); i++)
createMapAction(_ml->maps().at(i));
QString mapDir(ProgramPaths::mapDir());
if (mapDir.isNull())
return;
connect(_mapsSignalMapper, SIGNAL(mapped(int)), this,
SLOT(mapChanged(int)));
QString unused;
QList<Map*> maps(MapList::loadMaps(mapDir, unused));
for (int i = 0; i < maps.count(); i++) {
MapAction *a = createMapAction(maps.at(i));
connect(a, SIGNAL(loaded()), this, SLOT(mapInitialized()));
}
}
QAction *GUI::createMapAction(const Map *map)
MapAction *GUI::createMapAction(Map *map)
{
QAction *a = new QAction(map->name(), this);
MapAction *a = new MapAction(map);
a->setMenuRole(QAction::NoRole);
a->setCheckable(true);
a->setActionGroup(_mapsActionGroup);
_mapActions.append(a);
_mapsSignalMapper->setMapping(a, _mapActions.size() - 1);
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
connect(a, SIGNAL(triggered()), this, SLOT(mapChanged()));
return a;
}
void GUI::mapInitialized()
{
MapAction *action = static_cast<MapAction*>(QObject::sender());
Map *map = action->data().value<Map*>();
if (map->isValid()) {
if (!_mapsActionGroup->checkedAction())
action->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} else {
qWarning("%s: %s", qPrintable(map->name()), qPrintable(map->errorString()));
action->deleteLater();
}
}
void GUI::createPOIFilesActions()
{
_poiFilesSignalMapper = new QSignalMapper(this);
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
for (int i = 0; i < _poi->files().count(); i++)
createPOIFileAction(_poi->files().at(i));
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
}
QAction *GUI::createPOIFileAction(const QString &fileName)
@ -243,7 +248,7 @@ void GUI::createActions()
_reloadFileAction->setMenuRole(QAction::NoRole);
_reloadFileAction->setShortcut(RELOAD_SHORTCUT);
_reloadFileAction->setActionGroup(_fileActionGroup);
connect(_reloadFileAction, SIGNAL(triggered()), this, SLOT(reloadFile()));
connect(_reloadFileAction, SIGNAL(triggered()), this, SLOT(reloadFiles()));
addAction(_reloadFileAction);
_statisticsAction = new QAction(tr("Statistics..."), this);
_statisticsAction->setMenuRole(QAction::NoRole);
@ -281,8 +286,10 @@ void GUI::createActions()
createPOIFilesActions();
// Map actions
createMapActions();
_showMapAction = new QAction(QIcon(SHOW_MAP_ICON), tr("Show map"),
this);
_showMapAction->setEnabled(false);
_showMapAction->setMenuRole(QAction::NoRole);
_showMapAction->setCheckable(true);
_showMapAction->setShortcut(SHOW_MAP_SHORTCUT);
@ -294,10 +301,10 @@ void GUI::createActions()
_loadMapAction->setMenuRole(QAction::NoRole);
connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap()));
_clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
_clearMapCacheAction->setEnabled(false);
_clearMapCacheAction->setMenuRole(QAction::NoRole);
connect(_clearMapCacheAction, SIGNAL(triggered()), _mapView,
SLOT(clearMapCache()));
createMapActions();
_nextMapAction = new QAction(tr("Next map"), this);
_nextMapAction->setMenuRole(QAction::NoRole);
_nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
@ -308,10 +315,6 @@ void GUI::createActions()
_prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap()));
addAction(_prevMapAction);
if (_ml->maps().isEmpty()) {
_showMapAction->setEnabled(false);
_clearMapCacheAction->setEnabled(false);
}
_showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this);
_showCoordinatesAction->setMenuRole(QAction::NoRole);
_showCoordinatesAction->setCheckable(true);
@ -506,7 +509,7 @@ void GUI::createMenus()
#endif // Q_OS_MAC
_mapMenu = menuBar()->addMenu(tr("&Map"));
_mapMenu->addActions(_mapActions);
_mapMenu->addActions(_mapsActionGroup->actions());
_mapsEnd = _mapMenu->addSeparator();
_mapMenu->addAction(_loadMapAction);
_mapMenu->addAction(_clearMapCacheAction);
@ -608,6 +611,7 @@ void GUI::createToolBars()
void GUI::createMapView()
{
_map = new EmptyMap(this);
_mapView = new MapView(_map, _poi, this);
_mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Expanding));
@ -787,7 +791,12 @@ bool GUI::loadFile(const QString &fileName)
_trackDistance += track.distance();
_time += track.time();
_movingTime += track.movingTime();
const QDate &date = track.date().date();
#ifdef ENABLE_TIMEZONES
const QDateTime date = track.date().toTimeZone(
_options.timeZone.zone());
#else // ENABLE_TIMEZONES
const QDateTime &date = track.date();
#endif // ENABLE_TIMEZONES
if (_dateRange.first.isNull() || _dateRange.first > date)
_dateRange.first = date;
if (_dateRange.second.isNull() || _dateRange.second < date)
@ -905,9 +914,14 @@ void GUI::openOptions()
Track::action(options.option); \
reload = true; \
}
#define SET_DATA_OPTION(option, action) \
#define SET_ROUTE_OPTION(option, action) \
if (options.option != _options.option) { \
Data::action(options.option); \
Route::action(options.option); \
reload = true; \
}
#define SET_WAYPOINT_OPTION(option, action) \
if (options.option != _options.option) { \
Waypoint::action(options.option); \
reload = true; \
}
@ -953,13 +967,18 @@ void GUI::openOptions()
SET_TRACK_OPTION(pauseSpeed, setPauseSpeed);
SET_TRACK_OPTION(pauseInterval, setPauseInterval);
SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed);
SET_TRACK_OPTION(dataUseDEM, useDEM);
SET_TRACK_OPTION(showSecondaryElevation, showSecondaryElevation);
SET_TRACK_OPTION(showSecondarySpeed, showSecondarySpeed);
SET_DATA_OPTION(dataUseDEM, useDEM);
SET_ROUTE_OPTION(dataUseDEM, useDEM);
SET_ROUTE_OPTION(showSecondaryElevation, showSecondaryElevation);
SET_WAYPOINT_OPTION(dataUseDEM, useDEM);
SET_WAYPOINT_OPTION(showSecondaryElevation, showSecondaryElevation);
if (options.poiRadius != _options.poiRadius)
_poi->setRadius(options.poiRadius);
if (options.poiUseDEM != _options.poiUseDEM)
_poi->useDEM(options.poiUseDEM);
if (options.pixmapCache != _options.pixmapCache)
QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
@ -976,9 +995,16 @@ void GUI::openOptions()
_mapView->setDevicePixelRatio(devicePixelRatioF(),
options.hidpiMap ? devicePixelRatioF() : 1.0);
#endif // ENABLE_HIDPI
#ifdef ENABLE_TIMEZONES
if (options.timeZone != _options.timeZone) {
_mapView->setTimeZone(options.timeZone.zone());
_dateRange.first = _dateRange.first.toTimeZone(options.timeZone.zone());
_dateRange.second = _dateRange.second.toTimeZone(options.timeZone.zone());
}
#endif // ENABLE_TIMEZONES
if (reload)
reloadFile();
reloadFiles();
_options = options;
}
@ -1171,7 +1197,7 @@ void GUI::plot(QPrinter *printer)
}
}
void GUI::reloadFile()
void GUI::reloadFiles()
{
_trackCount = 0;
_routeCount = 0;
@ -1181,7 +1207,7 @@ void GUI::reloadFile()
_routeDistance = 0;
_time = 0;
_movingTime = 0;
_dateRange = DateRange(QDate(), QDate());
_dateRange = DateTimeRange(QDateTime(), QDateTime());
_pathName = QString();
for (int i = 0; i < _tabs.count(); i++)
@ -1215,7 +1241,7 @@ void GUI::closeFiles()
_routeDistance = 0;
_time = 0;
_movingTime = 0;
_dateRange = DateRange(QDate(), QDate());
_dateRange = DateTimeRange(QDateTime(), QDateTime());
_pathName = QString();
_sliderPos = 0;
@ -1322,25 +1348,49 @@ void GUI::loadMap()
bool GUI::loadMap(const QString &fileName)
{
// On OS X fileName may be a directory!
if (fileName.isEmpty())
return false;
QFileInfo fi(fileName);
bool res = fi.isDir() ? _ml->loadDir(fileName) : _ml->loadFile(fileName);
QString error;
QList<Map*> maps = MapList::loadMaps(fileName, error);
if (maps.isEmpty()) {
error = tr("Error loading map:") + "\n\n"
+ fileName + "\n\n" + error;
QMessageBox::critical(this, APP_NAME, error);
return false;
}
if (res) {
QAction *a = createMapAction(_ml->maps().last());
for (int i = 0; i < maps.size(); i++) {
Map *map = maps.at(i);
MapAction *a = createMapAction(map);
_mapMenu->insertAction(_mapsEnd, a);
if (map->isReady()) {
a->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} else
connect(a, SIGNAL(loaded()), this, SLOT(mapLoaded()));
}
return true;
}
void GUI::mapLoaded()
{
MapAction *action = static_cast<MapAction*>(QObject::sender());
Map *map = action->data().value<Map*>();
if (map->isValid()) {
action->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
a->trigger();
return true;
} else {
QString error = tr("Error loading map:") + "\n\n"
+ fileName + "\n\n" + _ml->errorString();
+ map->name() + "\n\n" + map->errorString();
QMessageBox::critical(this, APP_NAME, error);
return false;
action->deleteLater();
}
}
@ -1382,31 +1432,42 @@ void GUI::updateWindowTitle()
setWindowTitle(APP_NAME);
}
void GUI::mapChanged(int index)
void GUI::mapChanged()
{
_map = _ml->maps().at(index);
_map = _mapsActionGroup->checkedAction()->data().value<Map*>();
_mapView->setMap(_map);
}
void GUI::nextMap()
{
if (_ml->maps().count() < 2)
QAction *checked = _mapsActionGroup->checkedAction();
if (!checked)
return;
int next = (_ml->maps().indexOf(_map) + 1) % _ml->maps().count();
_mapActions.at(next)->setChecked(true);
mapChanged(next);
QList<QAction*> maps = _mapsActionGroup->actions();
for (int i = 1; i < maps.size(); i++) {
int next = (maps.indexOf(checked) + i) % maps.count();
if (maps.at(next)->isEnabled()) {
maps.at(next)->trigger();
break;
}
}
}
void GUI::prevMap()
{
if (_ml->maps().count() < 2)
QAction *checked = _mapsActionGroup->checkedAction();
if (!checked)
return;
int prev = (_ml->maps().indexOf(_map) + _ml->maps().count() - 1)
% _ml->maps().count();
_mapActions.at(prev)->setChecked(true);
mapChanged(prev);
QList<QAction*> maps = _mapsActionGroup->actions();
for (int i = 1; i < maps.size(); i++) {
int prev = (maps.indexOf(checked) + maps.count() - i) % maps.count();
if (maps.at(prev)->isEnabled()) {
maps.at(prev)->trigger();
break;
}
}
}
void GUI::poiFileChecked(int index)
@ -1812,10 +1873,19 @@ void GUI::writeSettings()
settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed);
if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT)
settings.setValue(DATA_USE_DEM_SETTING, _options.dataUseDEM);
if (_options.showSecondaryElevation != SHOW_SECONDARY_ELEVATION_DEFAULT)
settings.setValue(SHOW_SECONDARY_ELEVATION_SETTING,
_options.showSecondaryElevation);
if (_options.showSecondarySpeed != SHOW_SECONDARY_SPEED_DEFAULT)
settings.setValue(SHOW_SECONDARY_SPEED_SETTING,
_options.showSecondarySpeed);
#ifdef ENABLE_TIMEZONES
if (_options.timeZone != TimeZoneInfo())
settings.setValue(TIME_ZONE_SETTING, QVariant::fromValue(
_options.timeZone));
#endif // ENABLE_TIMEZONES
if (_options.poiRadius != POI_RADIUS_DEFAULT)
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
if (_options.poiUseDEM != POI_USE_DEM_DEFAULT)
settings.setValue(POI_USE_DEM_SETTING, _options.poiUseDEM);
if (_options.useOpenGL != USE_OPENGL_DEFAULT)
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
#ifdef ENABLE_HTTP2
@ -1899,9 +1969,11 @@ void GUI::readSettings()
_showMapAction->setChecked(true);
else
_mapView->showMap(false);
if (_ml->maps().count()) {
int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString());
_mapActions.at(index)->trigger();
QAction *ma = mapAction(settings.value(CURRENT_MAP_SETTING).toString());
if (ma) {
ma->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
}
if (settings.value(SHOW_COORDINATES_SETTING, SHOW_COORDINATES_DEFAULT)
.toBool()) {
@ -2078,14 +2150,21 @@ void GUI::readSettings()
USE_REPORTED_SPEED_DEFAULT).toBool();
_options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING,
DATA_USE_DEM_DEFAULT).toBool();
_options.showSecondaryElevation = settings.value(
SHOW_SECONDARY_ELEVATION_SETTING,
SHOW_SECONDARY_ELEVATION_DEFAULT).toBool();
_options.showSecondarySpeed = settings.value(
SHOW_SECONDARY_SPEED_SETTING,
SHOW_SECONDARY_SPEED_DEFAULT).toBool();
#ifdef ENABLE_TIMEZONES
_options.timeZone = settings.value(TIME_ZONE_SETTING).value<TimeZoneInfo>();
#endif // ENABLE_TIMEZONES
_options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING,
AUTOMATIC_PAUSE_DEFAULT).toBool();
_options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING,
PAUSE_INTERVAL_DEFAULT).toInt();
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
.toInt();
_options.poiUseDEM = settings.value(POI_USE_DEM_SETTING,
POI_USE_DEM_DEFAULT).toBool();
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
.toBool();
#ifdef ENABLE_HTTP2
@ -2144,6 +2223,9 @@ void GUI::readSettings()
_options.hidpiMap ? devicePixelRatioF() : 1.0);
#endif // ENABLE_HIDPI
_mapView->setProjection(_options.projection);
#ifdef ENABLE_TIMEZONES
_mapView->setTimeZone(_options.timeZone.zone());
#endif // ENABLE_TIMEZONES
for (int i = 0; i < _tabs.count(); i++) {
_tabs.at(i)->setPalette(_options.palette);
@ -2165,21 +2247,38 @@ void GUI::readSettings()
Track::setPauseSpeed(_options.pauseSpeed);
Track::setPauseInterval(_options.pauseInterval);
Track::useReportedSpeed(_options.useReportedSpeed);
Data::useDEM(_options.dataUseDEM);
Track::useDEM(_options.dataUseDEM);
Track::showSecondaryElevation(_options.showSecondaryElevation);
Track::showSecondarySpeed(_options.showSecondarySpeed);
Route::useDEM(_options.dataUseDEM);
Route::showSecondaryElevation(_options.showSecondaryElevation);
Waypoint::useDEM(_options.dataUseDEM);
Waypoint::showSecondaryElevation(_options.showSecondaryElevation);
_poi->setRadius(_options.poiRadius);
_poi->useDEM(_options.poiUseDEM);
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
settings.endGroup();
}
int GUI::mapIndex(const QString &name)
QAction *GUI::mapAction(const QString &name)
{
for (int i = 0; i < _ml->maps().count(); i++)
if (_ml->maps().at(i)->name() == name)
return i;
QList<QAction *> maps = _mapsActionGroup->actions();
// Last map
for (int i = 0; i < maps.count(); i++) {
Map *map = maps.at(i)->data().value<Map*>();
if (map->name() == name && map->isReady())
return maps.at(i);
}
// Any usable map
for (int i = 0; i < maps.count(); i++) {
Map *map = maps.at(i)->data().value<Map*>();
if (map->isReady())
return maps.at(i);
}
return 0;
}

View File

@ -26,9 +26,9 @@ class FileBrowser;
class GraphTab;
class MapView;
class Map;
class MapList;
class POI;
class QScreen;
class MapAction;
class GUI : public QMainWindow
{
@ -48,7 +48,7 @@ private slots:
void exportFile();
void openFile();
void closeAll();
void reloadFile();
void reloadFiles();
void statistics();
void openPOIFile();
void closePOIFiles();
@ -64,7 +64,7 @@ private slots:
void prevMap();
void openOptions();
void mapChanged(int);
void mapChanged();
void graphChanged(int);
void poiFileChecked(int);
@ -88,16 +88,18 @@ private slots:
void screenChanged(QScreen *screen);
void logicalDotsPerInchChanged(qreal dpi);
private:
typedef QPair<QDate, QDate> DateRange;
void mapLoaded();
void mapInitialized();
private:
typedef QPair<QDateTime, QDateTime> DateTimeRange;
void loadMaps();
void loadPOIs();
void closeFiles();
void plot(QPrinter *printer);
QAction *createPOIFileAction(const QString &fileName);
QAction *createMapAction(const Map *map);
MapAction *createMapAction(Map *map);
void createPOIFilesActions();
void createMapActions();
void createActions();
@ -127,7 +129,7 @@ private:
qreal distance() const;
qreal time() const;
qreal movingTime() const;
int mapIndex(const QString &name);
QAction *mapAction(const QString &name);
void readSettings();
void writeSettings();
@ -196,11 +198,9 @@ private:
QAction *_showCoordinatesAction;
QAction *_openOptionsAction;
QAction *_mapsEnd;
QList<QAction*> _mapActions;
QList<QAction*> _poiFilesActions;
QList<QAction*> _poiFilesActions;
QSignalMapper *_poiFilesSignalMapper;
QSignalMapper *_mapsSignalMapper;
QLabel *_fileNameLabel;
QLabel *_distanceLabel;
@ -212,7 +212,6 @@ private:
QList<GraphTab*> _tabs;
POI *_poi;
MapList *_ml;
Map *_map;
FileBrowser *_browser;
@ -221,7 +220,7 @@ private:
int _trackCount, _routeCount, _areaCount, _waypointCount;
qreal _trackDistance, _routeDistance;
qreal _time, _movingTime;
DateRange _dateRange;
DateTimeRange _dateRange;
QString _pathName;
qreal _sliderPos;

View File

@ -5,7 +5,7 @@
HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type,
int width, const QColor &color, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
{
}

32
src/GUI/mapaction.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef MAPACTION_H
#define MAPACTION_H
#include <QAction>
#include "map/map.h"
class MapAction : public QAction
{
Q_OBJECT
public:
MapAction(Map *map, QObject *parent = 0) : QAction(map->name(), parent)
{
map->setParent(this);
setData(QVariant::fromValue(map));
setEnabled(map->isReady());
connect(map, SIGNAL(mapLoaded()), this, SLOT(mapLoaded()));
}
signals:
void loaded();
private slots:
void mapLoaded()
{
Map *map = data().value<Map*>();
setEnabled(map->isValid());
emit loaded();
}
};
#endif // MAPACTION_H

View File

@ -54,13 +54,11 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent)
_map = map;
_map->load();
_map->setProjection(_projection);
connect(_map, SIGNAL(loaded()), this, SLOT(reloadMap()));
connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
_poi = poi;
connect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI()));
_units = Metric;
_coordinatesFormat = DecimalDegrees;
_mapOpacity = 1.0;
_backgroundColor = Qt::white;
_markerColor = Qt::red;
@ -122,7 +120,6 @@ PathItem *MapView::addTrack(const Track &track)
ti->setColor(_palette.nextColor());
ti->setWidth(_trackWidth);
ti->setStyle(_trackStyle);
ti->setUnits(_units);
ti->setVisible(_showTracks);
ti->setDigitalZoom(_digitalZoom);
ti->setMarkerColor(_markerColor);
@ -149,8 +146,6 @@ PathItem *MapView::addRoute(const Route &route)
ri->setColor(_palette.nextColor());
ri->setWidth(_routeWidth);
ri->setStyle(_routeStyle);
ri->setUnits(_units);
ri->setCoordinatesFormat(_coordinatesFormat);
ri->setVisible(_showRoutes);
ri->showWaypoints(_showRouteWaypoints);
ri->showWaypointLabels(_showWaypointLabels);
@ -200,7 +195,6 @@ void MapView::addWaypoints(const QVector<Waypoint> &waypoints)
wi->setSize(_waypointSize);
wi->setColor(_waypointColor);
wi->showLabel(_showWaypointLabels);
wi->setToolTipFormat(_units, _coordinatesFormat);
wi->setVisible(_showWaypoints);
wi->setDigitalZoom(_digitalZoom);
_scene->addItem(wi);
@ -316,7 +310,7 @@ void MapView::setMap(Map *map)
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
_map->unload();
disconnect(_map, SIGNAL(loaded()), this, SLOT(reloadMap()));
disconnect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
_map = map;
_map->load();
@ -324,7 +318,7 @@ void MapView::setMap(Map *map)
#ifdef ENABLE_HIDPI
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
#endif // ENABLE_HIDPI
connect(_map, SIGNAL(loaded()), this, SLOT(reloadMap()));
connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
digitalZoom(0);
@ -401,7 +395,6 @@ void MapView::addPOI(const QList<Waypoint> &waypoints)
pi->showLabel(_showPOILabels);
pi->setVisible(_showPOI);
pi->setDigitalZoom(_digitalZoom);
pi->setToolTipFormat(_units, _coordinatesFormat);
_scene->addItem(pi);
_pois.insert(SearchPointer<Waypoint>(&(pi->waypoint())), pi);
@ -410,51 +403,38 @@ void MapView::addPOI(const QList<Waypoint> &waypoints)
void MapView::setUnits(Units units)
{
if (_units == units)
return;
_units = units;
_mapScale->setUnits(_units);
WaypointItem::setUnits(units);
PathItem::setUnits(units);
for (int i = 0; i < _tracks.count(); i++)
_tracks[i]->setUnits(_units);
_tracks[i]->updateTicks();
for (int i = 0; i < _routes.count(); i++)
_routes[i]->setUnits(_units);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setToolTipFormat(_units, _coordinatesFormat);
_routes[i]->updateTicks();
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setToolTipFormat(_units, _coordinatesFormat);
_mapScale->setUnits(units);
}
void MapView::setCoordinatesFormat(CoordinatesFormat format)
{
if (_coordinatesFormat == format)
return;
WaypointItem::setCoordinatesFormat(format);
_coordinatesFormat = format;
_coordinates->setFormat(format);
}
_coordinates->setFormat(_coordinatesFormat);
for (int i = 0; i < _waypoints.count(); i++)
_waypoints.at(i)->setToolTipFormat(_units, _coordinatesFormat);
for (int i = 0; i < _routes.count(); i++)
_routes[i]->setCoordinatesFormat(_coordinatesFormat);
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setToolTipFormat(_units, _coordinatesFormat);
void MapView::setTimeZone(const QTimeZone &zone)
{
#ifdef ENABLE_TIMEZONES
WaypointItem::setTimeZone(zone);
PathItem::setTimeZone(zone);
#else // ENABLE_TIMEZONES
Q_UNUSED(zone);
#endif // ENABLE_TIMEZONES
}
void MapView::clearMapCache()
{
_map->clearCache();
fitMapZoom();
rescale();
centerOn(contentCenter());
reloadMap();
}
void MapView::digitalZoom(int zoom)

View File

@ -31,6 +31,7 @@ class GraphItem;
class AreaItem;
class Area;
class GraphicsScene;
class QTimeZone;
class MapView : public QGraphicsView
{
@ -83,6 +84,7 @@ public slots:
void showTicks(bool show);
void clearMapCache();
void setCoordinatesFormat(CoordinatesFormat format);
void setTimeZone(const QTimeZone &zone);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
void setProjection(int id);
@ -137,8 +139,6 @@ private:
POI *_poi;
Palette _palette;
Units _units;
CoordinatesFormat _coordinatesFormat;
qreal _mapOpacity;
Projection _projection;

View File

@ -12,6 +12,7 @@
#include <QRadioButton>
#include <QLabel>
#include <QSysInfo>
#include <QButtonGroup>
#include "map/pcs.h"
#include "icons.h"
#include "colorbox.h"
@ -166,11 +167,8 @@ QWidget *OptionsDialog::createAppearancePage()
// Palette & antialiasing
_baseColor = new ColorBox();
_baseColor->setColor(_options->palette.color());
_colorOffset = new QDoubleSpinBox();
_colorOffset->setMinimum(0);
_colorOffset->setMaximum(1.0);
_colorOffset->setSingleStep(0.01);
_colorOffset->setValue(_options->palette.shift());
_colorOffset = new PercentSlider();
_colorOffset->setValue(_options->palette.shift() * 100);
QFormLayout *paletteLayout = new QFormLayout();
paletteLayout->addRow(tr("Base color:"), _baseColor);
paletteLayout->addRow(tr("Palette shift:"), _colorOffset);
@ -406,6 +404,8 @@ QWidget *OptionsDialog::createDataPage()
_reportedSpeed->setChecked(true);
else
_computedSpeed->setChecked(true);
_showSecondarySpeed = new QCheckBox(tr("Show secondary speed"));
_showSecondarySpeed->setChecked(_options->showSecondarySpeed);
_dataGPSElevation = new QRadioButton(tr("GPS data"));
_dataDEMElevation = new QRadioButton(tr("DEM data"));
@ -413,43 +413,108 @@ QWidget *OptionsDialog::createDataPage()
_dataDEMElevation->setChecked(true);
else
_dataGPSElevation->setChecked(true);
_showSecondaryElevation = new QCheckBox(tr("Show secondary elevation"));
_showSecondaryElevation->setChecked(_options->showSecondaryElevation);
#ifdef ENABLE_TIMEZONES
_utcZone = new QRadioButton(tr("UTC"));
_systemZone = new QRadioButton(tr("System"));
_customZone = new QRadioButton(tr("Custom"));
if (_options->timeZone.type() == TimeZoneInfo::UTC)
_utcZone->setChecked(true);
else if (_options->timeZone.type() == TimeZoneInfo::System)
_systemZone->setChecked(true);
else
_customZone->setChecked(true);
_timeZone = new QComboBox();
_timeZone->setEnabled(_customZone->isChecked());
QList<QByteArray> zones = QTimeZone::availableTimeZoneIds();
for (int i = 0; i < zones.size(); i++)
_timeZone->addItem(zones.at(i));
_timeZone->setCurrentText(_options->timeZone.customZone().id());
connect(_customZone, SIGNAL(toggled(bool)), _timeZone,
SLOT(setEnabled(bool)));
QHBoxLayout *customZoneLayout = new QHBoxLayout();
customZoneLayout->addSpacing(20);
customZoneLayout->addWidget(_timeZone);
#endif // ENABLE_TIMEZONES
QWidget *sourceTab = new QWidget();
QVBoxLayout *sourceTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
QButtonGroup *speedGroup = new QButtonGroup(this);
speedGroup->addButton(_computedSpeed);
speedGroup->addButton(_reportedSpeed);
QVBoxLayout *speedOptions = new QVBoxLayout();
speedOptions->addWidget(_computedSpeed);
speedOptions->addWidget(_reportedSpeed);
speedOptions->addWidget(_showSecondarySpeed);
QButtonGroup *elevationGroup = new QButtonGroup(this);
elevationGroup->addButton(_dataGPSElevation);
elevationGroup->addButton(_dataDEMElevation);
QVBoxLayout *elevationOptions = new QVBoxLayout();
elevationOptions->addWidget(_dataGPSElevation);
elevationOptions->addWidget(_dataDEMElevation);
elevationOptions->addWidget(_showSecondaryElevation);
#ifdef ENABLE_TIMEZONES
QButtonGroup *timeZoneGroup = new QButtonGroup(this);
timeZoneGroup->addButton(_utcZone);
timeZoneGroup->addButton(_systemZone);
timeZoneGroup->addButton(_customZone);
QVBoxLayout *zoneOptions = new QVBoxLayout();
zoneOptions->addWidget(_utcZone);
zoneOptions->addWidget(_systemZone);
zoneOptions->addWidget(_customZone);
zoneOptions->addItem(customZoneLayout);
#endif // ENABLE_TIMEZONES
QFormLayout *formLayout = new QFormLayout();
formLayout->addRow(tr("Speed:"), speedOptions);
formLayout->addRow(tr("Elevation:"), elevationOptions);
#ifdef ENABLE_TIMEZONES
formLayout->addRow(tr("Time zone:"), zoneOptions);
#endif // ENABLE_TIMEZONES
sourceTabLayout->addLayout(formLayout);
#else // Q_OS_MAC
QFormLayout *speedLayout = new QFormLayout();
QFormLayout *elevationLayout = new QFormLayout();
#ifdef ENABLE_TIMEZONES
QFormLayout *timeZoneLayout = new QFormLayout();
#endif // ENABLE_TIMEZONES
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);
#ifdef ENABLE_TIMEZONES
timeZoneLayout->addWidget(_utcZone);
timeZoneLayout->addWidget(_systemZone);
timeZoneLayout->addWidget(_customZone);
timeZoneLayout->addItem(customZoneLayout);
QGroupBox *timeZoneBox = new QGroupBox(tr("Time zone"));
timeZoneBox->setLayout(timeZoneLayout);
#endif // ENABLE_TIMEZONES
sourceTabLayout->addWidget(speedBox);
sourceTabLayout->addWidget(elevationBox);
#ifdef ENABLE_TIMEZONES
sourceTabLayout->addWidget(timeZoneBox);
#endif // ENABLE_TIMEZONES
#endif // Q_OS_MAC
sourceTabLayout->addStretch();
sourceTab->setLayout(sourceTabLayout);
@ -465,13 +530,6 @@ QWidget *OptionsDialog::createDataPage()
QWidget *OptionsDialog::createPOIPage()
{
_poiGPSElevation = new QRadioButton(tr("GPS data"));
_poiDEMElevation = new QRadioButton(tr("DEM data"));
if (_options->poiUseDEM)
_poiDEMElevation->setChecked(true);
else
_poiGPSElevation->setChecked(true);
_poiRadius = new QDoubleSpinBox();
_poiRadius->setSingleStep(1);
_poiRadius->setDecimals(1);
@ -486,13 +544,8 @@ QWidget *OptionsDialog::createPOIPage()
_poiRadius->setSuffix(UNIT_SPACE + tr("km"));
}
QVBoxLayout *elevationLayout = new QVBoxLayout();
elevationLayout->addWidget(_poiGPSElevation);
elevationLayout->addWidget(_poiDEMElevation);
QFormLayout *poiLayout = new QFormLayout();
poiLayout->addRow(tr("Radius:"), _poiRadius);
poiLayout->addRow(tr("Elevation:"), elevationLayout);
QWidget *poiTab = new QWidget();
poiTab->setLayout(poiLayout);
@ -675,7 +728,7 @@ OptionsDialog::OptionsDialog(Options *options, QWidget *parent)
void OptionsDialog::accept()
{
_options->palette.setColor(_baseColor->color());
_options->palette.setShift(_colorOffset->value());
_options->palette.setShift(_colorOffset->value() / 100.0);
_options->mapOpacity = _mapOpacity->value();
_options->backgroundColor = _backgroundColor->color();
_options->trackWidth = _trackWidth->value();
@ -718,13 +771,21 @@ void OptionsDialog::accept()
_options->pauseInterval = _pauseInterval->value();
_options->useReportedSpeed = _reportedSpeed->isChecked();
_options->dataUseDEM = _dataDEMElevation->isChecked();
_options->showSecondaryElevation = _showSecondaryElevation->isChecked();
_options->showSecondarySpeed = _showSecondarySpeed->isChecked();
#ifdef ENABLE_TIMEZONES
_options->timeZone.setType(_utcZone->isChecked()
? TimeZoneInfo::UTC : _systemZone->isChecked()
? TimeZoneInfo::System : TimeZoneInfo::Custom);
_options->timeZone.setCustomZone(QTimeZone(_timeZone->currentText()
.toLatin1()));
#endif // ENABLE_TIMEZONES
qreal poiRadius = (_options->units == Imperial)
? _poiRadius->value() * MIINM : (_options->units == Nautical)
? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM;
if (qAbs(poiRadius - _options->poiRadius) > 0.01)
_options->poiRadius = poiRadius;
_options->poiUseDEM = _poiDEMElevation->isChecked();
_options->useOpenGL = _useOpenGL->isChecked();
#ifdef ENABLE_HTTP2

View File

@ -5,6 +5,9 @@
#include "common/config.h"
#include "palette.h"
#include "units.h"
#ifdef ENABLE_TIMEZONES
#include "timezoneinfo.h"
#endif // ENABLE_TIMEZONES
class ColorBox;
class StyleComboBox;
@ -17,6 +20,7 @@ class QRadioButton;
class PercentSlider;
class LimitedComboBox;
struct Options {
// Appearance
Palette palette;
@ -54,9 +58,13 @@ struct Options {
int pauseInterval;
bool useReportedSpeed;
bool dataUseDEM;
bool showSecondaryElevation;
bool showSecondarySpeed;
#ifdef ENABLE_TIMEZONES
TimeZoneInfo timeZone;
#endif // ENABLE_TIMEZONES
// POI
int poiRadius;
bool poiUseDEM;
// System
bool useOpenGL;
#ifdef ENABLE_HTTP2
@ -102,7 +110,7 @@ private:
// Appearance
ColorBox *_baseColor;
QDoubleSpinBox *_colorOffset;
PercentSlider *_colorOffset;
PercentSlider *_mapOpacity;
ColorBox *_backgroundColor;
QSpinBox *_trackWidth;
@ -133,7 +141,6 @@ private:
OddSpinBox *_cadenceFilter;
OddSpinBox *_powerFilter;
QCheckBox *_outlierEliminate;
QRadioButton *_automaticPause;
QRadioButton *_manualPause;
QDoubleSpinBox *_pauseSpeed;
@ -142,10 +149,16 @@ private:
QRadioButton *_reportedSpeed;
QRadioButton *_dataGPSElevation;
QRadioButton *_dataDEMElevation;
QCheckBox *_showSecondaryElevation;
QCheckBox *_showSecondarySpeed;
#ifdef ENABLE_TIMEZONES
QRadioButton *_utcZone;
QRadioButton *_systemZone;
QRadioButton *_customZone;
QComboBox *_timeZone;
#endif // ENABLE_TIMEZONES
// POI
QDoubleSpinBox *_poiRadius;
QRadioButton *_poiGPSElevation;
QRadioButton *_poiDEMElevation;
// System
QSpinBox *_pixmapCache;
QSpinBox *_connectionTimeout;

View File

@ -21,12 +21,16 @@ static inline unsigned segments(qreal distance)
return ceil(distance / GEOGRAPHICAL_MILE);
}
Units PathItem::_units = Metric;
#ifdef ENABLE_TIMEZONES
QTimeZone PathItem::_timeZone = QTimeZone::utc();
#endif // ENABLE_TIMEZONES
PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent)
: GraphicsItem(parent), _path(path), _map(map)
{
Q_ASSERT(_path.isValid());
_units = Metric;
_digitalZoom = 0;
_width = 3;
QBrush brush(Qt::SolidPattern);
@ -352,16 +356,6 @@ void PathItem::showTicks(bool show)
updateTicks();
}
void PathItem::setUnits(Units units)
{
if (_units == units)
return;
prepareGeometryChange();
_units = units;
updateTicks();
}
void PathItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);

View File

@ -1,8 +1,12 @@
#ifndef PATHITEM_H
#define PATHITEM_H
#include "common/config.h"
#include <QGraphicsObject>
#include <QPen>
#ifdef ENABLE_TIMEZONES
#include <QTimeZone>
#endif // ENABLE_TIMEZONES
#include "data/path.h"
#include "markeritem.h"
#include "units.h"
@ -28,7 +32,6 @@ public:
void setMap(Map *map);
void setUnits(Units units);
void setColor(const QColor &color);
void setWidth(qreal width);
void setStyle(Qt::PenStyle style);
@ -37,6 +40,13 @@ public:
void showMarker(bool show);
void showTicks(bool show);
void updateTicks();
static void setUnits(Units units) {_units = units;}
#ifdef ENABLE_TIMEZONES
static void setTimeZone(const QTimeZone &zone) {_timeZone = zone;}
#endif // ENABLE_TIMEZONES
public slots:
void moveMarker(qreal distance);
void hover(bool hover);
@ -49,7 +59,10 @@ protected:
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
Units _units;
static Units _units;
#ifdef ENABLE_TIMEZONES
static QTimeZone _timeZone;
#endif // ENABLE_TIMEZONES
private:
const PathSegment *segment(qreal x) const;
@ -60,7 +73,6 @@ private:
qreal xInM() const;
unsigned tickSize() const;
void updateTicks();
Path _path;
Map *_map;

View File

@ -5,7 +5,7 @@
PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
{
}

View File

@ -15,6 +15,8 @@ QString RouteItem::info() const
tt.insert(tr("Name"), _name);
if (!_desc.isEmpty())
tt.insert(tr("Description"), _desc);
if (!_comment.isEmpty() && _comment != _desc)
tt.insert(tr("Comment"), _comment);
tt.insert(tr("Distance"), Format::distance(path().last().last().distance(),
_units));
if (!_links.isEmpty()) {
@ -43,8 +45,8 @@ RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent)
_name = route.name();
_desc = route.description();
_comment = route.comment();
_links = route.links();
_coordinatesFormat = DecimalDegrees;
}
void RouteItem::setMap(Map *map)
@ -55,28 +57,6 @@ void RouteItem::setMap(Map *map)
PathItem::setMap(map);
}
void RouteItem::setUnits(Units u)
{
if (_units == u)
return;
for (int i = 0; i < _waypoints.count(); i++)
_waypoints[i]->setToolTipFormat(u, _coordinatesFormat);
PathItem::setUnits(u);
}
void RouteItem::setCoordinatesFormat(CoordinatesFormat format)
{
if (_coordinatesFormat == format)
return;
_coordinatesFormat = format;
for (int i = 0; i < _waypoints.count(); i++)
_waypoints[i]->setToolTipFormat(_units, _coordinatesFormat);
}
void RouteItem::showWaypoints(bool show)
{
for (int i = 0; i < _waypoints.count(); i++)

View File

@ -19,8 +19,6 @@ public:
void setMap(Map *map);
void setUnits(Units u);
void setCoordinatesFormat(CoordinatesFormat format);
void showWaypoints(bool show);
void showWaypointLabels(bool show);
@ -29,8 +27,8 @@ public:
private:
QString _name;
QString _desc;
QString _comment;
QVector<Link> _links;
CoordinatesFormat _coordinatesFormat;
QVector<WaypointItem*> _waypoints;
};

View File

@ -145,10 +145,13 @@
#define USE_REPORTED_SPEED_DEFAULT false
#define DATA_USE_DEM_SETTING "dataUseDEM"
#define DATA_USE_DEM_DEFAULT false
#define SHOW_SECONDARY_ELEVATION_SETTING "showSecondaryElevation"
#define SHOW_SECONDARY_ELEVATION_DEFAULT false
#define SHOW_SECONDARY_SPEED_SETTING "showSecondarySpeed"
#define SHOW_SECONDARY_SPEED_DEFAULT false
#define TIME_ZONE_SETTING "timeZone"
#define POI_RADIUS_SETTING "poiRadius"
#define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM)
#define POI_USE_DEM_SETTING "poiUseDEM"
#define POI_USE_DEM_DEFAULT false
#define USE_OPENGL_SETTING "useOpenGL"
#define USE_OPENGL_DEFAULT false
#define ENABLE_HTTP2_SETTING "enableHTTP2"

View File

@ -40,31 +40,46 @@ void SpeedGraph::setInfo()
clearInfo();
}
GraphItem *SpeedGraph::loadGraph(const Graph &graph, const Track &track,
const QColor &color, bool primary)
{
if (!graph.isValid())
return 0;
SpeedGraphItem *gi = new SpeedGraphItem(graph, _graphType, _width,
color, primary ? Qt::SolidLine : Qt::DashLine, track.movingTime());
gi->setTimeType(_timeType);
gi->setUnits(_units);
_tracks.append(gi);
if (_showTracks)
addGraph(gi);
if (primary) {
_avg.append(QPointF(track.distance(), gi->avg()));
_mavg.append(QPointF(track.distance(), gi->mavg()));
}
return gi;
}
QList<GraphItem*> SpeedGraph::loadData(const Data &data)
{
QList<GraphItem*> graphs;
for (int i = 0; i < data.tracks().count(); i++) {
GraphItem *primary, *secondary;
QColor color(_palette.nextColor());
const Track &track = data.tracks().at(i);
const Graph &graph = track.speed();
const GraphPair &gp = track.speed();
if (!graph.isValid()) {
_palette.nextColor();
graphs.append(0);
} else {
SpeedGraphItem *gi = new SpeedGraphItem(graph, _graphType, _width,
_palette.nextColor(), track.movingTime());
gi->setTimeType(_timeType);
gi->setUnits(_units);
primary = loadGraph(gp.primary(), track, color, true);
secondary = primary
? loadGraph(gp.secondary(), track, color, false) : 0;
if (primary && secondary)
primary->setSecondaryGraph(secondary);
_tracks.append(gi);
if (_showTracks)
addGraph(gi);
_avg.append(QPointF(track.distance(), gi->avg()));
_mavg.append(QPointF(track.distance(), gi->mavg()));
graphs.append(gi);
}
graphs.append(primary);
}
for (int i = 0; i < data.routes().count(); i++) {

View File

@ -5,6 +5,7 @@
#include "graphtab.h"
class SpeedGraphItem;
class Track;
class SpeedGraph : public GraphTab
{
@ -22,6 +23,8 @@ public:
void showTracks(bool show);
private:
GraphItem *loadGraph(const Graph &graph, const Track &track,
const QColor &color, bool primary);
qreal avg() const;
qreal max() const {return bounds().bottom();}
void setYUnits();

View File

@ -5,8 +5,8 @@
SpeedGraphItem::SpeedGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, qreal movingTime, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
const QColor &color, Qt::PenStyle style, qreal movingTime,
QGraphicsItem *parent) : GraphItem(graph, type, width, color, style, parent)
{
_timeType = Total;

View File

@ -10,7 +10,8 @@ class SpeedGraphItem : public GraphItem
public:
SpeedGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, qreal movingTime, QGraphicsItem *parent = 0);
const QColor &color, Qt::PenStyle style, qreal movingTime,
QGraphicsItem *parent = 0);
qreal avg() const {return _avg;}
qreal mavg() const {return _mavg;}

View File

@ -5,7 +5,7 @@
TemperatureGraphItem::TemperatureGraphItem(const Graph &graph, GraphType type,
int width, const QColor &color, QGraphicsItem *parent)
: GraphItem(graph, type, width, color, parent)
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
{
_min = GraphItem::min();
_max = GraphItem::max();

69
src/GUI/timezoneinfo.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef TIMEZONEINFO_H
#define TIMEZONEINFO_H
#include <QTimeZone>
#include <QDataStream>
class TimeZoneInfo
{
public:
enum Type {
UTC,
System,
Custom
};
TimeZoneInfo() : _type(UTC), _customZone(QTimeZone::systemTimeZone()) {}
Type type() const {return _type;}
const QTimeZone &customZone() const {return _customZone;}
QTimeZone zone() const
{
if (_type == UTC)
return QTimeZone::utc();
else if (_type == System)
return QTimeZone::systemTimeZone();
else
return _customZone;
}
void setType(Type type) {_type = type;}
void setCustomZone(const QTimeZone &zone) {_customZone = zone;}
bool operator==(const TimeZoneInfo &other) const
{
if (_type == UTC || _type == System)
return _type == other._type;
else
return (other._type == Custom && _customZone == other._customZone);
}
bool operator!=(const TimeZoneInfo &other) {return !(*this == other);}
private:
friend QDataStream& operator<<(QDataStream &out, const TimeZoneInfo &info);
friend QDataStream& operator>>(QDataStream &in, TimeZoneInfo &info);
Type _type;
QTimeZone _customZone;
};
Q_DECLARE_METATYPE(TimeZoneInfo)
inline QDataStream &operator<<(QDataStream &out, const TimeZoneInfo &info)
{
out << static_cast<int>(info._type) << info._customZone;
return out;
}
inline QDataStream &operator>>(QDataStream &in, TimeZoneInfo &info)
{
int t;
in >> t;
info._type = static_cast<TimeZoneInfo::Type>(t);
in >> info._customZone;
return in;
}
#endif // TIMEZONEINFO_H

View File

@ -13,6 +13,8 @@ QString TrackItem::info() const
tt.insert(tr("Name"), _name);
if (!_desc.isEmpty())
tt.insert(tr("Description"), _desc);
if (!_comment.isEmpty() && _comment != _desc)
tt.insert(tr("Comment"), _comment);
tt.insert(tr("Distance"), Format::distance(path().last().last().distance(),
_units));
if (_time > 0)
@ -20,7 +22,13 @@ QString TrackItem::info() const
if (_movingTime > 0)
tt.insert(tr("Moving time"), Format::timeSpan(_movingTime));
if (!_date.isNull())
tt.insert(tr("Date"), _date.toString(Qt::SystemLocaleShortDate));
tt.insert(tr("Date"),
#ifdef ENABLE_TIMEZONES
_date.toTimeZone(_timeZone)
#else // ENABLE_TIMEZONES
_date
#endif // ENABLE_TIMEZONES
.toString(Qt::SystemLocaleShortDate));
if (!_links.isEmpty()) {
QString links;
for (int i = 0; i < _links.size(); i++) {
@ -41,6 +49,7 @@ TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent)
{
_name = track.name();
_desc = track.description();
_comment = track.comment();
_links = track.links();
_date = track.date();
_time = track.time();

View File

@ -22,6 +22,7 @@ public:
private:
QString _name;
QString _desc;
QString _comment;
QVector<Link> _links;
QDateTime _date;
qreal _time;

View File

@ -13,6 +13,13 @@
#define FS(size) \
((int)((qreal)size * 1.41))
Units WaypointItem::_units = Metric;
CoordinatesFormat WaypointItem::_format = DecimalDegrees;
#ifdef ENABLE_TIMEZONES
QTimeZone WaypointItem::_timeZone = QTimeZone::utc();
#endif // ENABLE_TIMEZONES
QString WaypointItem::info() const
{
ToolTip tt;
@ -21,15 +28,28 @@ QString WaypointItem::info() const
tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name());
tt.insert(qApp->translate("WaypointItem", "Coordinates"),
Format::coordinates(_waypoint.coordinates(), _format));
if (_waypoint.hasElevation())
tt.insert(qApp->translate("WaypointItem", "Elevation"),
Format::elevation(_waypoint.elevation(), _units));
if (!std::isnan(_waypoint.elevations().first)) {
QString val = Format::elevation(_waypoint.elevations().first, _units);
if (!std::isnan(_waypoint.elevations().second))
val += " (" + Format::elevation(_waypoint.elevations().second,
_units) + ")";
tt.insert(qApp->translate("WaypointItem", "Elevation"), val);
}
if (_waypoint.timestamp().isValid())
tt.insert(qApp->translate("WaypointItem", "Date"),
_waypoint.timestamp().toString(Qt::SystemLocaleShortDate));
#ifdef ENABLE_TIMEZONES
_waypoint.timestamp().toTimeZone(_timeZone)
#else // ENABLE_TIMEZONES
_waypoint.timestamp()
#endif // ENABLE_TIMEZONES
.toString(Qt::SystemLocaleShortDate));
if (!_waypoint.description().isEmpty())
tt.insert(qApp->translate("WaypointItem", "Description"),
_waypoint.description());
if (!_waypoint.comment().isEmpty()
&& _waypoint.comment() != _waypoint.description())
tt.insert(qApp->translate("WaypointItem", "Comment"),
_waypoint.comment());
if (_waypoint.address().isValid()) {
QString addr("<address>");
addr += _waypoint.address().street();
@ -70,9 +90,6 @@ WaypointItem::WaypointItem(const Waypoint &waypoint, Map *map,
_font.setPixelSize(FS(_size));
_font.setFamily(FONT_FAMILY);
_units = Metric;
_format = DecimalDegrees;
updateCache();
setPos(map->ll2xy(waypoint.coordinates()));
@ -143,12 +160,6 @@ void WaypointItem::setColor(const QColor &color)
update();
}
void WaypointItem::setToolTipFormat(Units units, CoordinatesFormat format)
{
_units = units;
_format = format;
}
void WaypointItem::showLabel(bool show)
{
if (_showLabel == show)

View File

@ -1,9 +1,13 @@
#ifndef WAYPOINTITEM_H
#define WAYPOINTITEM_H
#include "common/config.h"
#include <cmath>
#include <QGraphicsItem>
#include <QFont>
#ifdef ENABLE_TIMEZONES
#include <QTimeZone>
#endif // ENABLE_TIMEZONES
#include "data/waypoint.h"
#include "map/map.h"
#include "units.h"
@ -23,7 +27,6 @@ public:
void setColor(const QColor &color);
void showLabel(bool show);
void setDigitalZoom(int zoom) {setScale(pow(2, -zoom));}
void setToolTipFormat(Units units, CoordinatesFormat format);
QPainterPath shape() const {return _shape;}
QRectF boundingRect() const {return _shape.boundingRect();}
@ -32,6 +35,13 @@ public:
QString info() const;
static void setUnits(Units units) {_units = units;}
static void setCoordinatesFormat(CoordinatesFormat format)
{_format = format;}
#ifdef ENABLE_TIMEZONES
static void setTimeZone(const QTimeZone &zone) {_timeZone = zone;}
#endif // ENABLE_TIMEZONES
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
@ -48,8 +58,11 @@ private:
QFont _font;
QRect _labelBB;
Units _units;
CoordinatesFormat _format;
static Units _units;
static CoordinatesFormat _format;
#ifdef ENABLE_TIMEZONES
static QTimeZone _timeZone;
#endif // ENABLE_TIMEZONES
};
#endif // WAYPOINTITEM_H

View File

@ -18,4 +18,8 @@
#define ENABLE_GEOJSON
#endif // QT >= 5.0
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
#define ENABLE_TIMEZONES
#endif // QT >= 5.5
#endif /* CONFIG_H */

View File

@ -20,7 +20,6 @@
#include "cupparser.h"
#include "gpiparser.h"
#include "smlparser.h"
#include "dem.h"
#include "data.h"
@ -73,49 +72,17 @@ static QMap<QString, Parser*> parsers()
return map;
}
QMap<QString, Parser*> Data::_parsers = parsers();
bool Data::_useDEM = false;
void Data::processData(QList<TrackData> &trackData, QList<RouteData> &routeData)
{
for (int i = 0; i < trackData.count(); i++) {
TrackData &track = trackData[i];
for (int j = 0; j < track.size(); j++) {
SegmentData &segment = track[j];
for (int k = 0; k < segment.size(); k++) {
Trackpoint &t = segment[k];
if (!t.hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(t.coordinates());
if (!std::isnan(elevation))
t.setElevation(elevation);
}
}
}
for (int i = 0; i < trackData.count(); i++)
_tracks.append(Track(trackData.at(i)));
}
for (int i = 0; i < routeData.count(); i++) {
RouteData &route = routeData[i];
for (int j = 0; j < route.size(); j++) {
Waypoint &w = route[j];
if (!w.hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(w.coordinates());
if (!std::isnan(elevation))
w.setElevation(elevation);
}
}
for (int i = 0; i < routeData.count(); i++)
_routes.append(Route(routeData.at(i)));
}
for (int i = 0; i < _waypoints.size(); i++) {
if (!_waypoints.at(i).hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(_waypoints.at(i).coordinates());
if (!std::isnan(elevation))
_waypoints[i].setElevation(elevation);
}
}
}
Data::Data(const QString &fileName, bool poi)
Data::Data(const QString &fileName)
{
QFile file(fileName);
QFileInfo fi(fileName);
@ -134,8 +101,7 @@ Data::Data(const QString &fileName, bool poi)
if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) {
if (it.value()->parse(&file, trackData, routeData, _polygons,
_waypoints)) {
if (!poi)
processData(trackData, routeData);
processData(trackData, routeData);
_valid = true;
return;
} else {
@ -146,8 +112,7 @@ Data::Data(const QString &fileName, bool poi)
for (it = _parsers.begin(); it != _parsers.end(); it++) {
if (it.value()->parse(&file, trackData, routeData, _polygons,
_waypoints)) {
if (!poi)
processData(trackData, routeData);
processData(trackData, routeData);
_valid = true;
return;
}
@ -198,8 +163,3 @@ QStringList Data::filter()
return filter;
}
void Data::useDEM(bool use)
{
_useDEM = use;
}

View File

@ -14,7 +14,7 @@
class Data
{
public:
Data(const QString &fileName, bool poi = false);
Data(const QString &fileName);
bool isValid() const {return _valid;}
const QString &errorString() const {return _errorString;}
@ -28,8 +28,6 @@ public:
static QString formats();
static QStringList filter();
static void useDEM(bool use);
private:
void processData(QList<TrackData> &trackData, QList<RouteData> &routeData);
@ -43,7 +41,6 @@ private:
QVector<Waypoint> _waypoints;
static QMap<QString, Parser*> _parsers;
static bool _useDEM;
};
#endif // DATA_H

View File

@ -7,6 +7,7 @@
#define RECORD_MESSAGE 20
#define EVENT_MESSAGE 21
#define COURSE_POINT 32
#define TIMESTAMP_FIELD 253
class Event {
@ -48,10 +49,12 @@ public:
class FITParser::CTX {
public:
CTX(QFile *file) : file(file), len(0), endian(0), timestamp(0),
lastWrite(0), ratio(NAN) {}
CTX(QFile *file, QVector<Waypoint> &waypoints)
: file(file), waypoints(waypoints), len(0), endian(0), timestamp(0),
lastWrite(0), ratio(NAN) {}
QFile *file;
QVector<Waypoint> &waypoints;
quint32 len;
quint8 endian;
quint32 timestamp, lastWrite;
@ -61,6 +64,41 @@ public:
SegmentData segment;
};
static QMap<int, QString> coursePointDescInit()
{
QMap<int, QString> map;
map.insert(1, "Summit");
map.insert(2, "Valley");
map.insert(3, "Water");
map.insert(4, "Food");
map.insert(5, "Danger");
map.insert(6, "Left");
map.insert(7, "Right");
map.insert(8, "Straight");
map.insert(9, "First aid");
map.insert(10, "Fourth category");
map.insert(11, "Third category");
map.insert(12, "Second category");
map.insert(13, "First category");
map.insert(14, "Hors category");
map.insert(15, "Sprint");
map.insert(16, "Left fork");
map.insert(17, "Right fork");
map.insert(18, "Middle fork");
map.insert(19, "Slight left");
map.insert(20, "Sharp left");
map.insert(21, "Slight right");
map.insert(22, "Sharp right");
map.insert(23, "U-Turn");
map.insert(24, "Segment start");
map.insert(25, "Segment end");
return map;
}
static QMap<int, QString> coursePointDesc = coursePointDescInit();
bool FITParser::readData(QFile *file, char *data, size_t size)
{
@ -80,17 +118,12 @@ bool FITParser::readData(QFile *file, char *data, size_t size)
template<class T> bool FITParser::readValue(CTX &ctx, T &val)
{
T data;
if (!readData(ctx.file, (char*)&data, sizeof(T)))
if (!readData(ctx.file, (char*)&val, sizeof(T)))
return false;
ctx.len -= sizeof(T);
if (ctx.endian)
val = qFromBigEndian(data);
else
val = qFromLittleEndian(data);
if (sizeof(T) > 1)
val = (ctx.endian) ? qFromBigEndian(val) : qFromLittleEndian(val);
return true;
}
@ -167,41 +200,51 @@ bool FITParser::parseDefinitionMessage(CTX &ctx, quint8 header)
return true;
}
bool FITParser::readField(CTX &ctx, Field *field, quint32 &val)
bool FITParser::readField(CTX &ctx, Field *field, QVariant &val, bool &valid)
{
quint8 v8 = (quint8)-1;
quint16 v16 = (quint16)-1;
bool ret;
val = (quint32)-1;
#define VAL(type, inval) \
{type var; \
if (field->size == sizeof(var)) { \
ret = readValue(ctx, var); \
val = var; \
valid = (var != (inval)); \
} else { \
ret = skipValue(ctx, field->size); \
valid = false; \
}}
switch (field->type) {
case 0: // enum
case 1: // sint8
VAL(qint8, 0x7fU);
break;
case 2: // uint8
if (field->size == 1) {
ret = readValue(ctx, v8);
val = v8;
} else
ret = skipValue(ctx, field->size);
case 0: // enum
VAL(quint8, 0xffU);
break;
case 7: // UTF8 nul terminated string
{QByteArray ba(ctx.file->read(field->size));
ctx.len -= field->size;
ret = (ba.size() == field->size);
val = ret ? ba : QString();
valid = !ba.isEmpty();}
break;
case 0x83: // sint16
VAL(qint16, 0x7fffU);
break;
case 0x84: // uint16
if (field->size == 2) {
ret = readValue(ctx, v16);
val = v16;
} else
ret = skipValue(ctx, field->size);
VAL(quint16, 0xffffU);
break;
case 0x85: // sint32
VAL(qint32, 0x7fffffffU);
break;
case 0x86: // uint32
if (field->size == 4)
ret = readValue(ctx, val);
else
ret = skipValue(ctx, field->size);
VAL(quint32, 0xffffffffU);
break;
default:
ret = skipValue(ctx, field->size);
valid = false;
break;
}
@ -211,8 +254,10 @@ bool FITParser::readField(CTX &ctx, Field *field, quint32 &val)
bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
{
Field *field;
QVariant val;
bool valid;
Event event;
quint32 val;
Waypoint waypoint;
if (!def->fields && !def->devFields) {
@ -224,69 +269,79 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
for (int i = 0; i < def->numFields; i++) {
field = &def->fields[i];
if (!readField(ctx, field, val))
if (!readField(ctx, field, val, valid))
return false;
if (!valid)
continue;
if (field->id == TIMESTAMP_FIELD)
ctx.timestamp = val;
ctx.timestamp = val.toUInt();
else if (def->globalId == RECORD_MESSAGE) {
switch (field->id) {
case 0:
if (val != 0x7fffffff)
ctx.trackpoint.rcoordinates().setLat(
((qint32)val / (double)0x7fffffff) * 180);
ctx.trackpoint.rcoordinates().setLat(
(val.toInt() / (double)0x7fffffff) * 180);
break;
case 1:
if (val != 0x7fffffff)
ctx.trackpoint.rcoordinates().setLon(
((qint32)val / (double)0x7fffffff) * 180);
ctx.trackpoint.rcoordinates().setLon(
(val.toInt() / (double)0x7fffffff) * 180);
break;
case 2:
if (val != 0xffff)
ctx.trackpoint.setElevation((val / 5.0) - 500);
ctx.trackpoint.setElevation((val.toUInt() / 5.0) - 500);
break;
case 3:
if (val != 0xff)
ctx.trackpoint.setHeartRate(val);
ctx.trackpoint.setHeartRate(val.toUInt());
break;
case 4:
if (val != 0xff)
ctx.trackpoint.setCadence(val);
ctx.trackpoint.setCadence(val.toUInt());
break;
case 6:
if (val != 0xffff)
ctx.trackpoint.setSpeed(val / 1000.0f);
ctx.trackpoint.setSpeed(val.toUInt() / 1000.0f);
break;
case 7:
if (val != 0xffff)
ctx.trackpoint.setPower(val);
ctx.trackpoint.setPower(val.toUInt());
break;
case 13:
if (val != 0x7f)
ctx.trackpoint.setTemperature((qint8)val);
ctx.trackpoint.setTemperature(val.toInt());
break;
case 73:
if (val != 0xffffffff)
ctx.trackpoint.setSpeed(val / 1000.0f);
ctx.trackpoint.setSpeed(val.toUInt() / 1000.0f);
break;
case 78:
if (val != 0xffffffff)
ctx.trackpoint.setElevation((val / 5.0) - 500);
ctx.trackpoint.setElevation((val.toUInt() / 5.0) - 500);
break;
default:
break;
}
} else if (def->globalId == EVENT_MESSAGE) {
switch (field->id) {
case 0:
event.id = val;
event.id = val.toUInt();
break;
case 1:
event.type = val;
event.type = val.toUInt();
break;
case 3:
event.data = val;
event.data = val.toUInt();
break;
}
} else if (def->globalId == COURSE_POINT) {
switch (field->id) {
case 1:
waypoint.setTimestamp(QDateTime::fromTime_t(val.toUInt()
+ 631065600));
break;
case 2:
waypoint.rcoordinates().setLat(
(val.toInt() / (double)0x7fffffff) * 180);
break;
case 3:
waypoint.rcoordinates().setLon(
(val.toInt() / (double)0x7fffffff) * 180);
break;
case 5:
waypoint.setDescription(coursePointDesc.value(val.toUInt()));
break;
case 6:
waypoint.setName(val.toString());
break;
}
}
@ -294,7 +349,7 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
for (int i = 0; i < def->numDevFields; i++) {
field = &def->devFields[i];
if (!readField(ctx, field, val))
if (!readField(ctx, field, val, valid))
return false;
}
@ -315,7 +370,9 @@ bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
ctx.trackpoint = Trackpoint();
ctx.lastWrite = ctx.timestamp;
}
}
} else if (def->globalId == COURSE_POINT)
if (waypoint.coordinates().isValid())
ctx.waypoints.append(waypoint);
return true;
}
@ -381,9 +438,8 @@ bool FITParser::parse(QFile *file, QList<TrackData> &tracks,
QList<Area> &polygons, QVector<Waypoint> &waypoints)
{
Q_UNUSED(routes);
Q_UNUSED(waypoints);
Q_UNUSED(polygons);
CTX ctx(file);
CTX ctx(file, waypoints);
if (!parseHeader(ctx))

View File

@ -21,7 +21,7 @@ private:
bool readData(QFile *file, char *data, size_t size);
template<class T> bool readValue(CTX &ctx, T &val);
bool skipValue(CTX &ctx, quint8 size);
bool readField(CTX &ctx, Field *field, quint32 &val);
bool readField(CTX &ctx, Field *field, QVariant &val, bool &valid);
bool parseHeader(CTX &ctx);
bool parseRecord(CTX &ctx);

View File

@ -282,14 +282,14 @@ static quint32 readNotes(QDataStream &stream, QTextCodec *codec,
if (s1 & 0x1) {
QList<TranslatedString> obj;
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty() && waypoint.description().isNull())
waypoint.setDescription(obj.first().str());
if (!obj.isEmpty())
waypoint.setComment(obj.first().str());
}
if (s1 & 0x2) {
QString str;
ds += readString(stream, codec, str);
if (!str.isEmpty() && waypoint.description().isNull())
waypoint.setDescription(str);
if (!str.isEmpty())
waypoint.setComment(str);
}
if (ds != rh.size)

View File

@ -192,6 +192,8 @@ void GPXParser::waypointData(Waypoint &waypoint, SegmentData *autoRoute)
waypoint.setName(_reader.readElementText());
else if (_reader.name() == QLatin1String("desc"))
waypoint.setDescription(_reader.readElementText());
else if (_reader.name() == QLatin1String("cmt"))
waypoint.setComment(_reader.readElementText());
else if (_reader.name() == QLatin1String("ele"))
waypoint.setElevation(number());
else if (_reader.name() == QLatin1String("geoidheight"))
@ -244,6 +246,8 @@ void GPXParser::routepoints(RouteData &route, QList<TrackData> &tracks)
route.setName(_reader.readElementText());
else if (_reader.name() == QLatin1String("desc"))
route.setDescription(_reader.readElementText());
else if (_reader.name() == QLatin1String("cmt"))
route.setComment(_reader.readElementText());
else if (_reader.name() == QLatin1String("link")) {
Link l(link());
if (!l.URL().isEmpty())
@ -278,6 +282,8 @@ void GPXParser::track(TrackData &track)
track.setName(_reader.readElementText());
else if (_reader.name() == QLatin1String("desc"))
track.setDescription(_reader.readElementText());
else if (_reader.name() == QLatin1String("cmt"))
track.setComment(_reader.readElementText());
else if (_reader.name() == QLatin1String("link")) {
Link l(link());
if (!l.URL().isEmpty())

View File

@ -67,4 +67,17 @@ public:
}
};
class GraphPair
{
public:
GraphPair(const Graph &primary, const Graph &secondary)
: _primary(primary), _secondary(secondary) {}
const Graph &primary() const {return _primary;}
const Graph &secondary() const {return _secondary;}
private:
Graph _primary, _secondary;
};
#endif // GRAPH_H

View File

@ -87,10 +87,12 @@ static bool readTimestamp(const char *data, QTime &time)
static bool readARecord(const char *line, qint64 len)
{
if (len < 7 || line[0] != 'A')
/* The minimal A record length should be 7 according to the specification,
but records with length of 6 exist in the wild */
if (len < 6 || line[0] != 'A')
return false;
for (int i = 1; i < 7; i++)
for (int i = 1; i < 6; i++)
if (!::isprint(line[i]))
return false;
return true;

View File

@ -585,7 +585,9 @@ void KMLParser::document(QList<TrackData> &tracks, QList<Area> &areas,
QVector<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Placemark"))
if (_reader.name() == QLatin1String("Document"))
document(tracks, areas, waypoints);
else if (_reader.name() == QLatin1String("Placemark"))
placemark(tracks, areas, waypoints);
else if (_reader.name() == QLatin1String("Folder"))
folder(tracks, areas, waypoints);

View File

@ -14,28 +14,19 @@ POI::POI(QObject *parent) : QObject(parent)
{
_errorLine = 0;
_radius = 1000;
_useDEM = false;
}
bool POI::loadFile(const QString &path, bool dir)
bool POI::loadFile(const QString &path)
{
Data data(path, true);
Data data(path);
FileIndex index;
index.enabled = true;
index.start = _data.size();
if (!data.isValid()) {
if (dir) {
if (data.errorLine())
_errorString += QString("%1:%2: %3\n").arg(path)
.arg(data.errorLine()).arg(data.errorString());
else
_errorString += path + ": " + data.errorString() + "\n";
} else {
_errorString = data.errorString();
_errorLine = data.errorLine();
}
_errorString = data.errorString();
_errorLine = data.errorLine();
return false;
}
@ -59,37 +50,22 @@ bool POI::loadFile(const QString &path, bool dir)
return true;
}
bool POI::loadFile(const QString &path)
{
_errorString.clear();
_errorLine = 0;
return loadFile(path, false);
}
bool POI::loadDir(const QString &path)
void POI::loadDir(const QString &path)
{
QDir md(path);
md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
QFileInfoList fl = md.entryInfoList();
bool ret = true;
_errorString.clear();
_errorLine = 0;
for (int i = 0; i < fl.size(); i++) {
const QFileInfo &fi = fl.at(i);
if (fi.isDir()) {
if (!loadDir(fi.absoluteFilePath()))
ret = false;
} else {
if (!loadFile(fi.absoluteFilePath(), true))
ret = false;
}
if (fi.isDir())
loadDir(fi.absoluteFilePath());
else
if (!loadFile(fi.absoluteFilePath()))
qWarning("%s: %s", qPrintable(fi.absoluteFilePath()),
qPrintable(_errorString));
}
return ret;
}
static bool cb(size_t data, void* context)
@ -112,17 +88,6 @@ void POI::search(const RectC &rect, QSet<int> &set) const
_tree.Search(min, max, cb, &set);
}
void POI::appendElevation(QList<Waypoint> &points) const
{
for (int i = 0; i < points.size(); i++) {
if (!points.at(i).hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(points.at(i).coordinates());
if (!std::isnan(elevation))
points[i].setElevation(elevation);
}
}
}
QList<Waypoint> POI::points(const Path &path) const
{
QList<Waypoint> ret;
@ -158,8 +123,6 @@ QList<Waypoint> POI::points(const Path &path) const
for (it = set.constBegin(); it != set.constEnd(); ++it)
ret.append(_data.at(*it));
appendElevation(ret);
return ret;
}
@ -181,8 +144,6 @@ QList<Waypoint> POI::points(const Waypoint &point) const
for (it = set.constBegin(); it != set.constEnd(); ++it)
ret.append(_data.at(*it));
appendElevation(ret);
return ret;
}
@ -206,8 +167,6 @@ QList<Waypoint> POI::points(const Area &area) const
for (it = set.constBegin(); it != set.constEnd(); ++it)
ret.append(_data.at(*it));
appendElevation(ret);
return ret;
}
@ -253,10 +212,3 @@ void POI::setRadius(unsigned radius)
emit pointsChanged();
}
void POI::useDEM(bool use)
{
_useDEM = use;
emit pointsChanged();
}

View File

@ -20,13 +20,12 @@ public:
POI(QObject *parent = 0);
bool loadFile(const QString &path);
bool loadDir(const QString &path);
void loadDir(const QString &path);
const QString &errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
unsigned radius() const {return _radius;}
void setRadius(unsigned radius);
void useDEM(bool use);
QList<Waypoint> points(const Path &path) const;
QList<Waypoint> points(const Waypoint &point) const;
@ -49,7 +48,6 @@ private:
bool loadFile(const QString &path, bool dir);
void search(const RectC &rect, QSet<int> &set) const;
void appendElevation(QList<Waypoint> &points) const;
POITree _tree;
QVector<Waypoint> _data;
@ -57,7 +55,6 @@ private:
QList<FileIndex> _indexes;
unsigned _radius;
bool _useDEM;
QString _errorString;
int _errorLine;

View File

@ -1,5 +1,8 @@
#include "dem.h"
#include "route.h"
bool Route::_useDEM = false;
bool Route::_show2ndElevation = false;
Route::Route(const RouteData &data) : _data(data)
{
@ -25,7 +28,7 @@ Path Route::path() const
return ret;
}
Graph Route::elevation() const
Graph Route::gpsElevation() const
{
Graph graph;
graph.append(GraphSegment());
@ -38,6 +41,38 @@ Graph Route::elevation() const
return graph;
}
Graph Route::demElevation() const
{
Graph graph;
graph.append(GraphSegment());
GraphSegment &gs = graph.last();
for (int i = 0; i < _data.size(); i++) {
qreal dem = DEM::elevation(_data.at(i).coordinates());
if (!std::isnan(dem))
gs.append(GraphPoint(_distance.at(i), NAN, dem));
}
return graph;
}
GraphPair Route::elevation() const
{
if (_useDEM) {
Graph dem(demElevation());
if (dem.isValid())
return GraphPair(dem, _show2ndElevation ? gpsElevation() : Graph());
else
return GraphPair(gpsElevation(), Graph());
} else {
Graph gps(gpsElevation());
if (gps.isValid())
return GraphPair(gps, _show2ndElevation ? demElevation() : Graph());
else
return GraphPair(demElevation(), Graph());
}
}
qreal Route::distance() const
{
return (_distance.isEmpty()) ? 0 : _distance.last();

View File

@ -11,23 +11,31 @@ class Route
public:
Route(const RouteData &data);
Path path() const;
const RouteData &data() const {return _data;}
Graph elevation() const;
Path path() const;
GraphPair elevation() const;
qreal distance() const;
const QString &name() const {return _data.name();}
const QString &description() const {return _data.description();}
const QString &comment() const {return _data.comment();}
const QVector<Link> &links() const {return _data.links();}
bool isValid() const {return _data.size() >= 2;}
static void useDEM(bool use) {_useDEM = use;}
static void showSecondaryElevation(bool show)
{_show2ndElevation = show;}
private:
Graph gpsElevation() const;
Graph demElevation() const;
RouteData _data;
QVector<qreal> _distance;
static bool _useDEM;
static bool _show2ndElevation;
};
#endif // ROUTE_H

View File

@ -11,15 +11,18 @@ class RouteData : public QVector<Waypoint>
public:
const QString &name() const {return _name;}
const QString &description() const {return _desc;}
const QString &comment() const {return _comment;}
const QVector<Link> &links() const {return _links;}
void setName(const QString &name) {_name = name;}
void setDescription(const QString &desc) {_desc = desc;}
void setComment(const QString &comment) {_comment = comment;}
void addLink(const Link &link) {_links.append(link);}
private:
QString _name;
QString _desc;
QString _comment;
QVector<Link> _links;
};

View File

@ -1,3 +1,4 @@
#include "dem.h"
#include "track.h"
@ -13,6 +14,9 @@ int Track::_pauseInterval = 10;
bool Track::_outlierEliminate = true;
bool Track::_useReportedSpeed = false;
bool Track::_useDEM = false;
bool Track::_show2ndElevation = false;
bool Track::_show2ndSpeed = false;
static qreal avg(const QVector<qreal> &v)
@ -38,6 +42,12 @@ static qreal MAD(QVector<qreal> &v, qreal m)
return median(v);
}
/*
Modified Z-score (Iglewicz and Hoaglin)
The acceleration data distribution has usualy a (much) higher kurtosis than
the normal distribution thus a higher comparsion value than the usual 3.5 is
required.
*/
static QSet<int> eliminate(const QVector<qreal> &v)
{
QSet<int> rm;
@ -47,7 +57,7 @@ static QSet<int> eliminate(const QVector<qreal> &v)
qreal M = MAD(w, m);
for (int i = 0; i < v.size(); i++)
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 5)
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 5.0)
rm.insert(i);
return rm;
@ -213,7 +223,7 @@ Track::Track(const TrackData &data) : _data(data), _pause(0)
}
}
Graph Track::elevation() const
Graph Track::gpsElevation() const
{
Graph ret;
@ -237,7 +247,48 @@ Graph Track::elevation() const
return ret;
}
Graph Track::speed() const
Graph Track::demElevation() const
{
Graph ret;
for (int i = 0; i < _data.size(); i++) {
const SegmentData &sd = _data.at(i);
if (sd.size() < 2)
continue;
const Segment &seg = _segments.at(i);
GraphSegment gs;
for (int j = 0; j < sd.size(); j++) {
qreal dem = DEM::elevation(sd.at(j).coordinates());
if (std::isnan(dem) || seg.outliers.contains(j))
continue;
gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), dem));
}
ret.append(filter(gs, _elevationWindow));
}
return ret;
}
GraphPair Track::elevation() const
{
if (_useDEM) {
Graph dem(demElevation());
if (dem.isValid())
return GraphPair(dem, _show2ndElevation ? gpsElevation() : Graph());
else
return GraphPair(gpsElevation(), Graph());
} else {
Graph gps(gpsElevation());
if (gps.isValid())
return GraphPair(gps, _show2ndElevation ? demElevation() : Graph());
else
return GraphPair(demElevation(), Graph());
}
}
Graph Track::computedSpeed() const
{
Graph ret;
@ -251,14 +302,10 @@ Graph Track::speed() const
qreal v;
for (int j = 0; j < sd.size(); j++) {
if (seg.stop.contains(j) && (!std::isnan(seg.speed.at(j))
|| sd.at(j).hasSpeed())) {
if (seg.stop.contains(j) && !std::isnan(seg.speed.at(j))) {
v = 0;
stop.append(gs.size());
} else if (_useReportedSpeed && sd.at(j).hasSpeed()
&& !seg.outliers.contains(j))
v = sd.at(j).speed();
else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
} else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
v = seg.speed.at(j);
else
continue;
@ -276,6 +323,60 @@ Graph Track::speed() const
return ret;
}
Graph Track::reportedSpeed() const
{
Graph ret;
for (int i = 0; i < _data.size(); i++) {
const SegmentData &sd = _data.at(i);
if (sd.size() < 2)
continue;
const Segment &seg = _segments.at(i);
GraphSegment gs;
QList<int> stop;
qreal v;
for (int j = 0; j < sd.size(); j++) {
if (seg.stop.contains(j) && sd.at(j).hasSpeed()) {
v = 0;
stop.append(gs.size());
} else if (sd.at(j).hasSpeed() && !seg.outliers.contains(j))
v = sd.at(j).speed();
else
continue;
gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), v));
}
ret.append(filter(gs, _speedWindow));
GraphSegment &filtered = ret.last();
for (int j = 0; j < stop.size(); j++)
filtered[stop.at(j)].setY(0);
}
return ret;
}
GraphPair Track::speed() const
{
if (_useReportedSpeed) {
Graph reported(reportedSpeed());
if (reported.isValid())
return GraphPair(reported, _show2ndSpeed ? computedSpeed()
: Graph());
else
return GraphPair(computedSpeed(), Graph());
} else {
Graph computed(computedSpeed());
if (computed.isValid())
return GraphPair(computed, _show2ndSpeed ? reportedSpeed()
: Graph());
else
return GraphPair(reportedSpeed(), Graph());
}
}
Graph Track::heartRate() const
{
Graph ret;

View File

@ -17,8 +17,8 @@ public:
Path path() const;
Graph elevation() const;
Graph speed() const;
GraphPair elevation() const;
GraphPair speed() const;
Graph heartRate() const;
Graph temperature() const;
Graph cadence() const;
@ -32,6 +32,7 @@ public:
const QString &name() const {return _data.name();}
const QString &description() const {return _data.description();}
const QString &comment() const {return _data.comment();}
const QVector<Link> &links() const {return _data.links();}
bool isValid() const;
@ -47,6 +48,11 @@ public:
static void setOutlierElimination(bool eliminate)
{_outlierEliminate = eliminate;}
static void useReportedSpeed(bool use) {_useReportedSpeed = use;}
static void useDEM(bool use) {_useDEM = use;}
static void showSecondaryElevation(bool show)
{_show2ndElevation = show;}
static void showSecondarySpeed(bool show)
{_show2ndSpeed = show;}
private:
struct Segment {
@ -59,6 +65,11 @@ private:
bool discardStopPoint(const Segment &seg, int i) const;
Graph demElevation() const;
Graph gpsElevation() const;
Graph reportedSpeed() const;
Graph computedSpeed() const;
TrackData _data;
QList<Segment> _segments;
qreal _pause;
@ -73,6 +84,9 @@ private:
static qreal _pauseSpeed;
static int _pauseInterval;
static bool _useReportedSpeed;
static bool _useDEM;
static bool _show2ndElevation;
static bool _show2ndSpeed;
};
#endif // TRACK_H

View File

@ -14,15 +14,18 @@ class TrackData : public QList<SegmentData>
public:
const QString &name() const {return _name;}
const QString &description() const {return _desc;}
const QString &comment() const {return _comment;}
const QVector<Link> &links() const {return _links;}
void setName(const QString &name) {_name = name;}
void setDescription(const QString &desc) {_desc = desc;}
void setComment(const QString &comment) {_comment = comment;}
void addLink(const Link &link) {_links.append(link);}
private:
QString _name;
QString _desc;
QString _comment;
QVector<Link> _links;
};

23
src/data/waypoint.cpp Normal file
View File

@ -0,0 +1,23 @@
#include "dem.h"
#include "waypoint.h"
bool Waypoint::_useDEM = false;
bool Waypoint::_show2ndElevation = false;
QPair<qreal, qreal> Waypoint::elevations() const
{
if (_useDEM) {
qreal dem = DEM::elevation(coordinates());
if (!std::isnan(dem))
return QPair<qreal, qreal>(dem, _show2ndElevation ? elevation()
: NAN);
else
return QPair<qreal, qreal>(elevation(), NAN);
} else {
if (hasElevation())
return QPair<qreal, qreal>(elevation(), _show2ndElevation
? DEM::elevation(coordinates()) : NAN);
else
return QPair<qreal, qreal>(DEM::elevation(coordinates()), NAN);
}
}

View File

@ -14,24 +14,29 @@
class Waypoint
{
public:
Waypoint() {_elevation = NAN;}
Waypoint(const Coordinates &coordinates) : _coordinates(coordinates)
{_elevation = NAN;}
Waypoint() : _elevation(NAN) {}
Waypoint(const Coordinates &coordinates)
: _coordinates(coordinates), _elevation(NAN) {}
const Coordinates &coordinates() const {return _coordinates;}
Coordinates &rcoordinates() {return _coordinates;}
const QString &name() const {return _name;}
const QString &description() const {return _description;}
const QString &comment() const {return _comment;}
const Address &address() const {return _address;}
const QVector<ImageInfo> &images() const {return _images;}
const QVector<Link> &links() const {return _links;}
const QDateTime &timestamp() const {return _timestamp;}
qreal elevation() const {return _elevation;}
QPair<qreal, qreal> elevations() const;
void setCoordinates(const Coordinates &coordinates)
{_coordinates = coordinates;}
void setName(const QString &name) {_name = name;}
void setDescription(const QString &description)
{_description = description;}
void setComment(const QString &comment) {_comment = comment;}
void setAddress(const Address &address) {_address = address;}
void setTimestamp(const QDateTime &timestamp) {_timestamp = timestamp;}
void setElevation(qreal elevation) {_elevation = elevation;}
@ -44,15 +49,23 @@ public:
{return this->_name == other._name
&& this->_coordinates == other._coordinates;}
static void useDEM(bool use) {_useDEM = use;}
static void showSecondaryElevation(bool show)
{_show2ndElevation = show;}
private:
Coordinates _coordinates;
QString _name;
QString _description;
QString _comment;
Address _address;
QVector<ImageInfo> _images;
QVector<Link> _links;
QDateTime _timestamp;
qreal _elevation;
static bool _useDEM;
static bool _show2ndElevation;
};
inline uint qHash(const Waypoint &key)
@ -64,11 +77,9 @@ inline uint qHash(const Waypoint &key)
inline QDebug operator<<(QDebug dbg, const Waypoint &waypoint)
{
dbg.nospace() << "Waypoint(" << waypoint.coordinates() << ", "
<< waypoint.name() << ", " << waypoint.description() << ")";
<< waypoint.name() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG
Q_DECLARE_TYPEINFO(Waypoint, Q_MOVABLE_TYPE);
#endif // WAYPOINT_H

View File

@ -1,4 +1,7 @@
#include "common/config.h"
#ifdef ENABLE_TIMEZONES
#include "GUI/timezoneinfo.h"
#endif // ENABLE_TIMEZONES
#include "GUI/app.h"
int main(int argc, char *argv[])
@ -7,6 +10,9 @@ int main(int argc, char *argv[])
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif // ENABLE_HIDPI
#ifdef ENABLE_TIMEZONES
qRegisterMetaTypeStreamOperators<TimeZoneInfo>("TimeZoneInfo");
#endif // ENABLE_TIMEZONES
App app(argc, argv);
return app.run();

View File

@ -41,8 +41,19 @@ bool BitStream1::flush()
return true;
}
bool BitStream4::flush()
{
if (_length && !_file.seek(_hdl, _hdl.pos() + _length))
return false;
bool BitStream4::read(int bits, quint32 &val)
_length = 0;
_used = 32;
_unused = 0;
return true;
}
bool BitStream4F::read(int bits, quint32 &val)
{
if (bits <= 32 - (int)(_used + _unused)) {
val = bits ? (_data << _used) >> (32 - bits) : 0;
@ -50,6 +61,8 @@ bool BitStream4::read(int bits, quint32 &val)
return true;
}
if (_unused)
return false;
quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0;
quint32 bytes = qMin(_length, 4U);
@ -66,14 +79,123 @@ bool BitStream4::read(int bits, quint32 &val)
return true;
}
bool BitStream4::flush()
BitStream4R::BitStream4R(const SubFile &file, SubFile::Handle &hdl,
quint32 length) : BitStream4(file, hdl, length)
{
if (_length && !_file.seek(_hdl, _hdl.pos() + _length))
_file.seek(_hdl, _hdl.pos() - 4);
}
bool BitStream4R::readBytes(int bytes, quint32 &val)
{
quint32 bits = _used % 8;
quint32 b;
if (bits) {
if (!read(8 - bits, b))
return false;
Q_ASSERT(!b);
}
return read(bytes * 8, val);
}
bool BitStream4R::readVUInt32(quint32 &val)
{
quint32 b;
quint8 bytes, shift;
if (!readBytes(1, b))
return false;
_length = 0;
_used = 32;
_unused = 0;
if ((b & 1) == 0) {
if ((b & 2) == 0) {
bytes = ((b >> 2) & 1) ^ 3;
shift = 5;
} else {
shift = 6;
bytes = 1;
}
} else {
shift = 7;
bytes = 0;
}
val = b >> (8 - shift);
if (bytes) {
if (!readBytes(bytes, b))
return false;
val = val | (b << shift);
}
return true;
}
bool BitStream4R::readVuint32SM(quint32 &val1, quint32 &val2, quint32 &val2Bits)
{
quint32 b, eb;
if (!readBytes(1, b))
return false;
if (!(b & 1)) {
val1 = b >> 3 & 0x1f;
val2 = b >> 1 & 3;
val2Bits = 2;
} else {
eb = b & 2;
val2 = b >> 2 & 0x3f;
val2Bits = eb * 2 + 6;
if (!readBytes((eb >> 1 | 2) - 1, b))
return false;
if (eb) {
val2 = val2 | (b & 0xf) << 6;
b = b >> 4 & 0xfff;
}
val1 = b;
}
return true;
}
bool BitStream4R::skip(quint32 bytes)
{
if (bytes * 8 > bitsAvailable())
return false;
quint32 ab = (32 - (_used + _unused))/8;
if (bytes <= ab)
_used += bytes * 8;
else {
quint32 seek = ((bytes - ab)/4)*4;
quint32 read = (bytes - ab)%4;
if (seek && !_file.seek(_hdl, _hdl.pos() - seek))
return false;
_length -= seek;
if (read) {
quint32 rb = qMin(_length, 4U);
if (!_file.readUInt32(_hdl, _data))
return false;
if (!_file.seek(_hdl, _hdl.pos() - 8))
return false;
_length -= rb;
_unused = (4 - rb) * 8;
_used = read * 8;
} else
_used = 32;
}
return true;
}
void BitStream4R::resize(quint32 length)
{
quint32 ab = (32 - _used)/8;
if (ab < length)
_length = length - ab;
else {
_length = 0;
_used += length * 8;
}
}

View File

@ -10,7 +10,7 @@ public:
bool read(int bits, quint32 &val);
bool flush();
quint32 bitsAvailable() const {return _length * 8 + _remaining;}
quint64 bitsAvailable() const {return (quint64)_length * 8 + _remaining;}
private:
const SubFile &_file;
@ -25,15 +25,64 @@ public:
: _file(file), _hdl(hdl), _length(length), _used(32), _unused(0),
_data(0) {}
bool read(int bits, quint32 &val);
bool flush();
quint32 bitsAvailable() const {return _length * 8 + (32 - _used) - _unused;}
quint64 bitsAvailable() const
{return (quint64)_length * 8 + (32 - _used) - _unused;}
private:
protected:
const SubFile &_file;
SubFile::Handle &_hdl;
quint32 _length, _used, _unused;
quint32 _data;
};
class BitStream4F : public BitStream4 {
public:
BitStream4F(const SubFile &file, SubFile::Handle &hdl, quint32 length)
: BitStream4(file, hdl, length) {}
bool read(int bits, quint32 &val);
};
class BitStream4R : public BitStream4 {
public:
BitStream4R(const SubFile &file, SubFile::Handle &hdl, quint32 length);
template<typename T> bool read(int bits, T &val);
bool readBytes(int bytes, quint32 &val);
bool readVUInt32(quint32 &val);
bool readVuint32SM(quint32 &val1, quint32 &val2, quint32 &val2Bits);
bool skip(quint32 bytes);
void resize(quint32 length);
};
template<typename T>
bool BitStream4R::read(int bits, T &val)
{
if (bits <= 32 - (int)(_used + _unused)) {
val = bits ? (_data << _used) >> (32 - bits) : 0;
_used += bits;
return true;
}
if (_unused)
return false;
quint32 old = (_used < 32) ? (_data << _used) >> (32 - bits) : 0;
quint32 bytes = qMin(_length, 4U);
if (!_file.readUInt32(_hdl, _data))
return false;
if (!_file.seek(_hdl, _hdl.pos() - 8))
return false;
_length -= bytes;
_used -= 32 - bits;
_unused = (4 - bytes) * 8;
val = _data >> (32 - _used) | old;
return true;
}
#endif // BITSTREAM_H

View File

@ -1,6 +1,5 @@
#include <QXmlStreamReader>
#include <QDir>
#include "map/osm.h"
#include "vectortile.h"
#include "gmap.h"
@ -104,11 +103,6 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
if (tile->zooms().min() < _zooms.min())
_zooms.setMin(tile->zooms().min());
// Limit world maps bounds so that the maps can be projected using
// the default Web Mercator projection
if (_bounds.height() > 120)
_bounds &= OSM::BOUNDS;
return true;
}

View File

@ -1,74 +1,30 @@
#include "huffmanstream.h"
HuffmanStream::HuffmanStream(const SubFile &file, SubFile::Handle &hdl,
quint32 length, const HuffmanTable &table, bool line)
: BitStream4(file, hdl, length), _table(table), _symbolDataSize(0),
_symbolData(0)
bool HuffmanStreamF::init(bool line)
{
if (line) {
if (!(sign(_lonSign) && sign(_latSign)))
return;
return false;
} else {
_lonSign = 0;
_latSign = 0;
}
quint32 eb;
if (!read(1, eb))
return;
if (!_bs.read(1, eb))
return false;
if (eb) {
qWarning("Extended polygon/lines not supported");
flush();
}
}
bool HuffmanStream::sign(int &val)
{
quint32 bit;
val = 0;
if (!read(1, bit))
qWarning() << "Extended lines/polygons not supported";
return false;
if (bit) {
if (!read(1, bit))
return false;
val = bit ? -1 : 1;
}
return true;
}
bool HuffmanStream::readDelta(int sign, qint32 &symbol)
bool HuffmanStreamR::init()
{
quint8 size;
quint32 next;
quint8 nextSize = qMin((quint32)(32 - _symbolDataSize), bitsAvailable());
if (!read(nextSize, next))
if (!(sign(_lonSign) && sign(_latSign)))
return false;
_symbolData = (_symbolData << nextSize) | next;
_symbolDataSize += nextSize;
symbol = _table.symbol(_symbolData << (32 - _symbolDataSize), size);
if (size <= _symbolDataSize)
_symbolDataSize -= size;
else
return false;
if (symbol && !sign) {
if (!_symbolDataSize)
return false;
else {
sign = ((1U << (_symbolDataSize - 1)) & _symbolData) ? -1 : 1;
_symbolDataSize--;
}
}
symbol = sign * symbol;
return true;
}

View File

@ -4,10 +4,12 @@
#include "bitstream.h"
#include "huffmantable.h"
class HuffmanStream : public BitStream4 {
template <class BitStream>
class HuffmanStream {
public:
HuffmanStream(const SubFile &file, SubFile::Handle &hdl, quint32 length,
const HuffmanTable &table, bool line);
HuffmanStream(BitStream &bitstream, const HuffmanTable &table)
: _bs(bitstream), _table(table), _symbolDataSize(0), _symbolData(0),
_lonSign(0), _latSign(0) {}
bool readNext(qint32 &lonDelta, qint32 &latDelta)
{
@ -17,19 +19,87 @@ public:
return (lonDelta || latDelta);
}
bool readOffset(qint32 &lonDelta, qint32 &latDelta)
{return (readDelta(1, lonDelta) && readDelta(1, latDelta));}
bool atEnd() const
{return _symbolDataSize + bitsAvailable() < _table.maxSymbolSize();}
{return _symbolDataSize + _bs.bitsAvailable() < _table.maxSymbolSize();}
bool flush() {return _bs.flush();}
private:
protected:
bool sign(int &val);
bool readDelta(int sign, qint32 &delta);
BitStream &_bs;
const HuffmanTable &_table;
quint32 _symbolDataSize;
quint32 _symbolData;
int _lonSign, _latSign;
};
template <class BitStream>
bool HuffmanStream<BitStream>::sign(int &val)
{
quint32 bit;
val = 0;
if (!_bs.read(1, bit))
return false;
if (bit) {
if (!_bs.read(1, bit))
return false;
val = bit ? -1 : 1;
}
return true;
}
template <class BitStream>
bool HuffmanStream<BitStream>::readDelta(int sign, qint32 &symbol)
{
quint8 size;
quint32 next;
quint8 nextSize = qMin((quint64)(32 - _symbolDataSize), _bs.bitsAvailable());
if (!_bs.read(nextSize, next))
return false;
_symbolData = (_symbolData << nextSize) | next;
_symbolDataSize += nextSize;
symbol = _table.symbol(_symbolData << (32 - _symbolDataSize), size);
if (size <= _symbolDataSize)
_symbolDataSize -= size;
else
return false;
if (symbol && !sign) {
if (!_symbolDataSize)
return false;
else {
sign = ((1U << (_symbolDataSize - 1)) & _symbolData) ? -1 : 1;
_symbolDataSize--;
}
}
symbol = sign * symbol;
return true;
}
class HuffmanStreamF : public HuffmanStream<BitStream4F> {
public:
HuffmanStreamF(BitStream4F &bitstream, const HuffmanTable &table)
: HuffmanStream(bitstream, table) {}
bool init(bool line);
bool readOffset(qint32 &lonDelta, qint32 &latDelta)
{return (readDelta(1, lonDelta) && readDelta(1, latDelta));}
};
class HuffmanStreamR : public HuffmanStream<BitStream4R> {
public:
HuffmanStreamR(BitStream4R &bitstream, const HuffmanTable &table)
: HuffmanStream(bitstream, table) {}
bool init();
};
#endif // HUFFMANSTREAM_H

View File

@ -42,6 +42,8 @@ bool HuffmanTable::load(const SubFile &file, SubFile::Handle &hdl,
_s10 = _s14 + _s1c * _s1d;
_s18 = _s10 + (_s1 << _s0);
_id = id;
return true;
}

View File

@ -13,6 +13,8 @@ public:
quint8 maxSymbolSize() const {return _s2;}
quint32 symbol(quint32 data, quint8 &size) const;
quint8 id() const {return _id;}
private:
bool getBuffer(const SubFile &file, SubFile::Handle &hdl, quint32 offset,
quint32 size, quint8 id);
@ -22,6 +24,8 @@ private:
quint8 *_s10, *_s14, *_s18;
quint8 _s1c, _s1d, _s1e, _s1f, _s20;
quint16 _s22;
quint8 _id;
};
#endif // HUFFMANTABLE_H

View File

@ -1,6 +1,5 @@
#include <QMap>
#include <QtEndian>
#include "map/osm.h"
#include "vectortile.h"
#include "img.h"
@ -60,7 +59,7 @@ IMG::IMG(const QString &fileName) : _file(fileName)
QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2)));
_name = QString::fromLatin1(nba.constData(), nba.size()-1).trimmed();
_blockSize = 1 << (e1 + e2);
_blockBits = e1 + e2;
// Read the FAT table
quint8 flag;
@ -133,7 +132,6 @@ IMG::IMG(const QString &fileName) : _file(fileName)
}
// Create tile tree
int minMapZoom = 24;
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
@ -160,19 +158,19 @@ IMG::IMG(const QString &fileName) : _file(fileName)
minMapZoom = tile->zooms().min();
}
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
// Detect and mark basemap
TileTree::Iterator it;
for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it)) {
VectorTile *tile = _tileTree.GetAt(it);
if (tile->zooms().min() > minMapZoom)
_baseMap = true;
if (tile->zooms().min() == minMapZoom)
tile->markAsBasemap();
}
// Limit world maps bounds so that the maps can be projected using
// the default Web Mercator projection
if (_bounds.height() > 120)
_bounds &= OSM::BOUNDS;
// Allow some extra zoom out on maps without basemaps, but not too much as
// this would kill the rendering performance
if (!_baseMap)
_zooms.setMin(_zooms.min() - 2);
if (!_tileTree.Count())
_errorString = "No usable map tile found";
@ -203,9 +201,9 @@ template<class T> bool IMG::readValue(T &val)
bool IMG::readBlock(int blockNum, char *data)
{
if (!_file.seek((qint64)blockNum * (qint64)_blockSize))
if (!_file.seek((quint64)blockNum << _blockBits))
return false;
if (read(data, _blockSize) < _blockSize)
if (read(data, 1U<<_blockBits) < 1U<<_blockBits)
return false;
return true;

View File

@ -14,14 +14,14 @@ public:
private:
friend class SubFile;
int blockSize() const {return _blockSize;}
unsigned blockBits() const {return _blockBits;}
bool readBlock(int blockNum, char *data);
qint64 read(char *data, qint64 maxSize);
template<class T> bool readValue(T &val);
QFile _file;
quint8 _key;
int _blockSize;
unsigned _blockBits;
};
#endif // IMG_H

View File

@ -151,7 +151,9 @@ Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize) const
if (!c || c == 0x1d)
break;
if ((c >= 0x1e && c <= 0x1f)) {
if (c == 0x1c)
capitalize = false;
else if ((c >= 0x1e && c <= 0x1f)) {
if (bap == &shieldLabel)
bap = &label;
else

View File

@ -54,7 +54,7 @@ inline bool pointCb(VectorTile *tile, void *context)
}
MapData::MapData() : _typ(0), _style(0), _zooms(15, 28), _baseMap(false),
MapData::MapData() : _typ(0), _style(0), _zooms(24, 28), _baseMap(false),
_valid(false)
{
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);

View File

@ -1,5 +1,89 @@
#include "bitstream.h"
#include "huffmanstream.h"
#include "subdiv.h"
#include "nodfile.h"
#include "netfile.h"
bool adjCnts(BitStream4R &bs, QVector<quint16> &cnts, quint16 &mask)
{
quint32 val, cnt, bits;
if (!bs.read(4, val))
return false;
cnt = ((val >> 2) & 3) + 2;
bits = ((val * 2) & 6) + 4;
mask = 1<<(3 + ((val * 2) & 6));
if (cnt == 5) {
if (!bs.read(8, cnt))
return false;
Q_ASSERT(cnt > 4);
}
if (cnt < 2)
return false;
cnts.resize(cnt - 1);
for (int i = 0; i < cnts.size(); i++)
if (!bs.read(bits, cnts[i]))
return false;
return true;
}
bool skipNodes(BitStream4R &bs, const QVector<quint16> &cnts, quint16 mask)
{
for (int i = 0; i < cnts.size(); i++) {
if (cnts.at(i) & mask) {
quint32 v1, v2, v2b;
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
if (!bs.skip(v1))
return false;
}
}
return true;
}
bool seekToLevel(BitStream4R &bs, quint8 level)
{
quint32 v1, v2, v2b;
for (quint8 i = 1; i < level; ) {
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
if (!bs.skip(v1))
return false;
Q_ASSERT(!(v2 & 2));
if (v2 & 2)
return false;
if (v2 & 1)
i++;
};
return true;
}
bool seekToLine(BitStream4R &bs, quint8 line)
{
quint32 v1, v2, v2b;
for (quint8 i = 0; i < line; i++) {
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
if (!bs.skip(v1))
return false;
Q_ASSERT(!(v2 & 2));
if (v2 & 2)
return false;
}
return true;
}
bool NETFile::init(Handle &hdl)
{
quint8 multiplier;
@ -10,11 +94,121 @@ bool NETFile::init(Handle &hdl)
&& readUInt32(hdl, _size) && readUInt8(hdl, multiplier)))
return false;
if (hdrLen >= 0x47) {
quint32 info;
if (!(seek(hdl, _gmpOffset + 0x37) && readUInt32(hdl, info)))
return false;
_tableId = ((info >> 2) & 0xF);
if (!(seek(hdl, _gmpOffset + 0x43) && readUInt32(hdl, _linksOffset)
&& readUInt32(hdl, _linksSize) && readUInt8(hdl, _linksShift)))
return false;
}
_multiplier = 1<<multiplier;
return true;
}
bool NETFile::link(const SubDiv *subdiv, Handle &hdl, NODFile *nod,
Handle &nodHdl, const NODFile::BlockInfo blockInfo, quint8 linkId,
quint8 lineId, const HuffmanTable &table, QList<IMG::Poly> *lines)
{
if (!_multiplier && !init(hdl))
return false;
// TODO
if (!subdiv->level())
return false;
NODFile::LinkInfo linkInfo;
if (!nod->linkInfo(nodHdl, blockInfo, linkId, linkInfo))
return false;
quint32 linkOffset = _linksOffset + (linkInfo.linkOffset << _linksShift);
Q_ASSERT(linkOffset <= _linksOffset + _linksSize);
quint8 s68 = (linkInfo.flags >> 0x12) & 1;
quint8 s69 = (linkInfo.flags >> 0x11) & 1;
quint8 s6a = (linkInfo.flags >> 0x13) & 1;
if (s6a == 1) {
QVector<quint16> ca;
quint16 mask;
if (!seek(hdl, linkOffset))
return false;
BitStream4R bs(*this, hdl, linkOffset - _linksOffset);
quint32 size;
if (!bs.readVUInt32(size))
return false;
if (s69 == 0) {
if (!adjCnts(bs, ca, mask))
return false;
}
if (s68 == 1) {
quint32 v1, v2, v2b;
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
Q_ASSERT(v1);
if (!bs.skip(v1))
return false;
}
if (!skipNodes(bs, ca, mask))
return false;
if (!seekToLevel(bs, subdiv->level()))
return false;
if (!seekToLine(bs, lineId))
return false;
quint32 v1, v2, v2b;
if (!bs.readVuint32SM(v1, v2, v2b))
return false;
bs.resize(v1);
quint32 lon, lat;
if (!(bs.read(0x12 - v2b, lon) && bs.read(16, lat)))
return false;
if (2 < v2b)
lon |= (v2 >> 2) << (0x12U - v2b);
QPoint pos = QPoint(LS(subdiv->lon(), 8) + LS((qint16)lon,
32-subdiv->bits()), LS(subdiv->lat(), 8) + LS((qint16)lat,
32-subdiv->bits()));
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
IMG::Poly poly;
if (!nod->linkType(nodHdl, blockInfo, linkId, poly.type))
return false;
poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat()));
Q_ASSERT(_tableId == table.id());
HuffmanStreamR stream(bs, table);
if (!stream.init())
return false;
qint32 lonDelta, latDelta;
while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += LS(lonDelta, 32-subdiv->bits());
if (pos.rx() < 0 && subdiv->lon() >= 0)
pos.rx() = 0x7fffffff;
pos.ry() += LS(latDelta, 32-subdiv->bits());
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat()));
poly.boundingRect = poly.boundingRect.united(c);
}
lines->append(poly);
}
return true;
}
bool NETFile::lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset)
{
if (!_multiplier && !init(hdl))

View File

@ -1,25 +1,37 @@
#ifndef NETFILE_H
#define NETFILE_H
#include "img.h"
#include "subfile.h"
#include "nodfile.h"
class NODFile;
class LBLFile;
class SubDiv;
class HuffmanTable;
class NETFile : public SubFile
{
public:
NETFile(IMG *img)
: SubFile(img), _offset(0), _size(0), _multiplier(0) {}
NETFile(const QString &path)
: SubFile(path), _offset(0), _size(0), _multiplier(0) {}
NETFile(SubFile *gmp, quint32 offset)
: SubFile(gmp, offset), _offset(0), _size(0), _multiplier(0) {}
NETFile(IMG *img) : SubFile(img), _offset(0), _size(0), _linksOffset(0),
_linksSize(0), _multiplier(0), _linksShift(0) {}
NETFile(const QString &path) : SubFile(path), _offset(0), _size(0),
_linksOffset(0), _linksSize(0), _multiplier(0), _linksShift(0) {}
NETFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset),
_offset(0), _size(0), _linksOffset(0), _linksSize(0), _multiplier(0),
_linksShift(0) {}
bool lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset);
bool link(const SubDiv *subdiv, Handle &hdl, NODFile *nod, Handle &nodHdl,
const NODFile::BlockInfo blockInfo, quint8 linkId, quint8 lineId,
const HuffmanTable &table, QList<IMG::Poly> *lines);
private:
bool init(Handle &hdl);
quint32 _offset, _size;
quint8 _multiplier;
quint32 _offset, _size, _linksOffset, _linksSize;
quint8 _multiplier, _linksShift;
quint8 _tableId;
};
#endif // NETFILE_H

148
src/map/IMG/nodfile.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "bitstream.h"
#include "nodfile.h"
bool NODFile::init(Handle &hdl)
{
quint16 hdrLen;
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)))
return false;
if (hdrLen >= 0x7b) {
if (!(seek(hdl, _gmpOffset + 0x21) && readUInt8(hdl, _blockShift)
&& readUInt8(hdl, _nodeShift)))
return false;
if (!(seek(hdl, _gmpOffset + 0x67) && readUInt32(hdl, _blockOffset)
&& readUInt32(hdl, _blockSize) && readUInt16(hdl, _blockRecordSize)
&& readUInt32(hdl, _indexOffset) && readUInt32(hdl, _indexSize)
&& readUInt16(hdl, _indexRecordSize) && readUInt32(hdl, _indexFlags)))
return false;
}
return true;
}
quint32 NODFile::indexIdSize(Handle &hdl)
{
if (!_indexRecordSize && !init(hdl))
return 0;
quint32 indexCount = _indexSize / _indexRecordSize;
if (indexCount <= 0x100)
return 1;
else if (indexCount <= 0x1000)
return 2;
else if (indexCount <= 0x1000000)
return 3;
else
return 0;
}
bool NODFile::blockInfo(Handle &hdl, quint32 blockIndexId,
BlockInfo &blockInfo) const
{
quint32 blockOffset;
quint32 offset = _indexRecordSize * blockIndexId + _indexOffset;
quint32 offsetSize = (_indexFlags & 3) + 1;
Q_ASSERT(offset <= _indexOffset + _indexSize);
if (!(seek(hdl, offset) && readVUInt32(hdl, offsetSize, blockOffset)))
return false;
blockInfo.offset = (blockOffset << _blockShift) + _blockOffset;
if (!(seek(hdl, blockInfo.offset) && readUInt16(hdl, blockInfo.h0)
&& readUInt32(hdl, blockInfo.h2) && readUInt32(hdl, blockInfo.h6)
&& readUInt32(hdl, blockInfo.ha) && readUInt16(hdl, blockInfo.he)
&& readUInt8(hdl, blockInfo.h10) && readUInt8(hdl, blockInfo.h11)
&& readUInt8(hdl, blockInfo.h12)))
return false;
return true;
}
bool NODFile::linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
LinkInfo &linkInfo) const
{
Q_ASSERT(linkId < blockInfo.h10);
quint32 infoOffset = ((blockInfo.he * linkId) >> 3) + 0x13
+ ((blockInfo.h0 >> 0xb) & 1) + blockInfo.offset;
quint32 s1 = ((blockInfo.h0 >> 2) & 0x1f) + 8;
quint32 s2 = (blockInfo.h0 >> 7) & 0xf;
quint32 skip = (blockInfo.he * linkId) & 7;
Q_ASSERT(infoOffset <= _blockOffset + _blockSize);
if (!seek(hdl, infoOffset))
return false;
quint32 unused, flags;
BitStream1 bs(*this, hdl, _blockOffset + _blockSize - infoOffset);
if (!(bs.read(skip, unused) && bs.read(0xc, flags)))
return false;
linkInfo.flags = ((flags << 8) & 0xf0000) | (flags & 0xff);
if (!(flags << 8 & 0x10000)) {
if (!bs.read(s1, linkInfo.linkOffset))
return false;
} else {
if (!bs.read(s1 - s2, linkInfo.linkOffset))
return false;
linkInfo.linkOffset += blockInfo.ha;
}
return true;
}
bool NODFile::linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
quint32 &type) const
{
quint32 offset = ((blockInfo.h10 * blockInfo.he + 7) >> 3) + 0x13 +
blockInfo.offset + ((blockInfo.h0 >> 0xb) & 1) + (quint32)blockInfo.h11
* 3;
quint32 low = 0;
quint32 high = blockInfo.h12 - 1;
quint32 pos;
quint16 val;
if (high > 1) {
do {
pos = (low + high) / 2;
if (!seek(hdl, offset + _blockRecordSize * pos))
return false;
if (!readUInt16(hdl, val))
return false;
quint32 tmp = pos;
if ((val >> 8) <= linkId) {
low = pos;
tmp = high;
}
high = tmp;
} while (low + 1 < high);
}
if (!seek(hdl, offset + _blockRecordSize * low))
return false;
if (!readUInt16(hdl, val))
return false;
type = val & 0x3f;
if ((low < high) && (pos != high)) {
if (!seek(hdl, offset + _blockRecordSize * high))
return false;
if (!readUInt16(hdl, val))
return false;
if ((val >> 8) <= linkId) {
type = (val & 0x3f);
}
}
type *= 256;
return true;
}

54
src/map/IMG/nodfile.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef NODFILE_H
#define NODFILE_H
#include "img.h"
#include "subfile.h"
class NODFile : public SubFile
{
public:
struct BlockInfo {
quint32 offset;
quint16 h0;
quint32 h2;
quint32 h6;
quint32 ha;
quint16 he;
quint8 h10; // links count
quint8 h11;
quint8 h12;
};
struct LinkInfo {
quint32 linkOffset;
quint32 flags;
};
NODFile(IMG *img) : SubFile(img), _indexOffset(0), _indexSize(0),
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
_blockRecordSize(0), _blockShift(0), _nodeShift(0) {}
NODFile(const QString &path) : SubFile(path), _indexOffset(0), _indexSize(0),
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
_blockRecordSize(0), _blockShift(0), _nodeShift(0) {}
NODFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset),
_indexOffset(0), _indexSize(0),_indexFlags(0), _blockOffset(0),
_blockSize(0), _indexRecordSize(0), _blockRecordSize(0), _blockShift(0),
_nodeShift(0) {}
quint32 indexIdSize(Handle &hdl);
bool blockInfo(Handle &hdl, quint32 blockIndexId,
BlockInfo &blockInfo) const;
bool linkInfo(Handle &hdl, const BlockInfo &blockInfo, quint32 linkId,
LinkInfo &linkInfo) const;
bool linkType(Handle &hdl, const BlockInfo &blockInfo, quint8 linkId,
quint32 &type) const;
private:
bool init(Handle &hdl);
quint32 _indexOffset, _indexSize, _indexFlags, _blockOffset, _blockSize;
quint16 _indexRecordSize, _blockRecordSize;
quint8 _blockShift, _nodeShift;
};
#endif // NETFILE_H

406
src/map/IMG/rastertile.cpp Normal file
View File

@ -0,0 +1,406 @@
#include <QFont>
#include <QPainter>
#include "textpathitem.h"
#include "textpointitem.h"
#include "bitmapline.h"
#include "style.h"
#include "rastertile.h"
#define AREA(rect) \
(rect.size().width() * rect.size().height())
static const QColor shieldColor(Qt::white);
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 int minPOIZoom(Style::POIClass cl)
{
switch (cl) {
case Style::Food:
case Style::Shopping:
case Style::Services:
return 27;
case Style::Accommodation:
case Style::Recreation:
return 25;
case Style::ManmadePlaces:
case Style::NaturePlaces:
case Style::Transport:
case Style::Community:
case Style::Elementary:
return 23;
default:
return 0;
}
}
static QFont pixelSizeFont(int pixelSize)
{
QFont f;
f.setPixelSize(pixelSize);
return f;
}
static QFont *font(Style::FontSize size, Style::FontSize defaultSize
= Style::Normal)
{
/* The fonts must be initialized on first usage (after the QGuiApplication
instance is created) */
static QFont large = pixelSizeFont(16);
static QFont normal = pixelSizeFont(14);
static QFont small = pixelSizeFont(12);
static QFont extraSmall = pixelSizeFont(10);
switch (size) {
case Style::None:
return 0;
case Style::Large:
return &large;
case Style::Normal:
return &normal;
case Style::Small:
return &small;
case Style::ExtraSmall:
return &extraSmall;
default:
return font(defaultSize);
}
}
static QFont *poiFont(Style::FontSize size = Style::Normal)
{
static QFont poi = pixelSizeFont(10);
switch (size) {
case Style::None:
return 0;
default:
return &poi;
}
}
static const QColor *shieldBgColor(Label::Shield::Type type)
{
switch (type) {
case Label::Shield::USInterstate:
case Label::Shield::Hbox:
return &shieldBgColor1;
case Label::Shield::USShield:
case Label::Shield::Box:
return &shieldBgColor2;
case Label::Shield::USRound:
case Label::Shield::Oval:
return &shieldBgColor3;
default:
return 0;
}
}
static int minShieldZoom(Label::Shield::Type type)
{
switch (type) {
case Label::Shield::USInterstate:
case Label::Shield::Hbox:
return 17;
case Label::Shield::USShield:
case Label::Shield::Box:
return 19;
case Label::Shield::USRound:
case Label::Shield::Oval:
return 20;
default:
return 0;
}
}
static qreal area(const QVector<QPointF> &polygon)
{
qreal area = 0;
for (int i = 0; i < polygon.size(); i++) {
int j = (i + 1) % polygon.size();
area += polygon.at(i).x() * polygon.at(j).y();
area -= polygon.at(i).y() * polygon.at(j).x();
}
area /= 2.0;
return area;
}
static QPointF centroid(const QVector<QPointF> &polygon)
{
qreal cx = 0, cy = 0;
qreal factor = 1.0 / (6.0 * area(polygon));
for (int i = 0; i < polygon.size(); i++) {
int j = (i + 1) % polygon.size();
qreal f = (polygon.at(i).x() * polygon.at(j).y() - polygon.at(j).x()
* polygon.at(i).y());
cx += (polygon.at(i).x() + polygon.at(j).x()) * f;
cy += (polygon.at(i).y() + polygon.at(j).y()) * f;
}
return QPointF(cx * factor, cy * factor);
}
static bool rectNearPolygon(const QPolygonF &polygon, const QRectF &rect)
{
return (polygon.boundingRect().contains(rect)
&& (polygon.containsPoint(rect.topLeft(), Qt::OddEvenFill)
|| polygon.containsPoint(rect.topRight(), Qt::OddEvenFill)
|| polygon.containsPoint(rect.bottomLeft(), Qt::OddEvenFill)
|| polygon.containsPoint(rect.bottomRight(), Qt::OddEvenFill)));
}
void RasterTile::render()
{
QList<TextItem*> textItems;
processPoints(textItems);
processPolygons(textItems);
processLines(textItems);
_img.fill(Qt::transparent);
QPainter painter(&_img);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(-_xy.x(), -_xy.y());
drawPolygons(&painter);
drawLines(&painter);
drawTextItems(&painter, textItems);
//painter.setPen(Qt::red);
//painter.drawRect(QRect(_xy, _img.size()));
qDeleteAll(textItems);
}
void RasterTile::drawPolygons(QPainter *painter)
{
for (int n = 0; n < _style->drawOrder().size(); n++) {
for (int i = 0; i < _polygons.size(); i++) {
const MapData::Poly &poly = _polygons.at(i);
if (poly.type != _style->drawOrder().at(n))
continue;
const Style::Polygon &style = _style->polygon(poly.type);
painter->setPen(style.pen());
painter->setBrush(style.brush());
painter->drawPolygon(poly.points);
}
}
}
void RasterTile::drawLines(QPainter *painter)
{
painter->setBrush(Qt::NoBrush);
for (int i = 0; i < _lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i);
const Style::Line &style = _style->line(poly.type);
if (style.background() == Qt::NoPen)
continue;
painter->setPen(style.background());
painter->drawPolyline(poly.points);
}
for (int i = 0; i < _lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i);
const Style::Line &style = _style->line(poly.type);
if (!style.img().isNull())
BitmapLine::draw(painter, poly.points, style.img());
else if (style.foreground() != Qt::NoPen) {
painter->setPen(style.foreground());
painter->drawPolyline(poly.points);
}
}
}
void RasterTile::drawTextItems(QPainter *painter,
const QList<TextItem*> &textItems)
{
for (int i = 0; i < textItems.size(); i++)
textItems.at(i)->paint(painter);
}
void RasterTile::processPolygons(QList<TextItem*> &textItems)
{
for (int i = 0; i < _polygons.size(); i++) {
MapData::Poly &poly = _polygons[i];
if (poly.label.text().isEmpty())
continue;
if (_zoom <= 23 && (Style::isWaterArea(poly.type)
|| Style::isMilitaryArea(poly.type)
|| Style::isNatureReserve(poly.type))) {
const Style::Polygon &style = _style->polygon(poly.type);
TextPointItem *item = new TextPointItem(
centroid(poly.points).toPoint(), &poly.label.text(),
poiFont(), 0, &style.brush().color());
if (item->isValid() && !item->collides(textItems)
&& rectNearPolygon(poly.points, item->boundingRect()))
textItems.append(item);
else
delete item;
}
}
}
void RasterTile::processLines(QList<TextItem*> &textItems)
{
QRect tileRect(_xy, _img.size());
qStableSort(_lines);
if (_zoom >= 22)
processStreetNames(tileRect, textItems);
processShields(tileRect, textItems);
}
void RasterTile::processStreetNames(const QRect &tileRect,
QList<TextItem*> &textItems)
{
for (int i = 0; i < _lines.size(); i++) {
MapData::Poly &poly = _lines[i];
const Style::Line &style = _style->line(poly.type);
if (style.img().isNull() && style.foreground() == Qt::NoPen)
continue;
if (poly.label.text().isEmpty()
|| 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;
TextPathItem *item = new TextPathItem(poly.points,
&poly.label.text(), tileRect, fnt, color);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
}
void RasterTile::processShields(const QRect &tileRect,
QList<TextItem*> &textItems)
{
for (int type = FIRST_SHIELD; type <= LAST_SHIELD; type++) {
if (minShieldZoom(static_cast<Label::Shield::Type>(type)) > _zoom)
continue;
QHash<Label::Shield, QPolygonF> shields;
QHash<Label::Shield, const Label::Shield*> sp;
for (int i = 0; i < _lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i);
const Label::Shield &shield = poly.label.shield();
if (!shield.isValid() || shield.type() != type
|| !Style::isMajorRoad(poly.type))
continue;
QPolygonF &p = shields[shield];
for (int j = 0; j < poly.points.size(); j++)
p.append(poly.points.at(j));
sp.insert(shield, &shield);
}
for (QHash<Label::Shield, QPolygonF>::const_iterator it
= shields.constBegin(); it != shields.constEnd(); ++it) {
const QPolygonF &p = it.value();
QRectF rect(p.boundingRect() & tileRect);
if (AREA(rect) < AREA(QRect(0, 0, _img.width()/4, _img.width()/4)))
continue;
QMap<qreal, int> map;
QPointF center = rect.center();
for (int j = 0; j < p.size(); j++) {
QLineF l(p.at(j), center);
map.insert(l.length(), j);
}
QMap<qreal, int>::const_iterator jt = map.constBegin();
TextPointItem *item = new TextPointItem(
p.at(jt.value()).toPoint(), &(sp.value(it.key())->text()),
poiFont(), 0, &shieldColor, shieldBgColor(it.key().type()));
bool valid = false;
while (true) {
if (!item->collides(textItems)
&& tileRect.contains(item->boundingRect().toRect())) {
valid = true;
break;
}
if (++jt == map.constEnd())
break;
item->setPos(p.at(jt.value()).toPoint());
}
if (valid)
textItems.append(item);
else
delete item;
}
}
}
void RasterTile::processPoints(QList<TextItem*> &textItems)
{
qSort(_points);
for (int i = 0; i < _points.size(); i++) {
MapData::Point &point = _points[i];
const Style::Point &style = _style->point(point.type);
if (point.poi && _zoom < minPOIZoom(Style::poiClass(point.type)))
continue;
const QString *label = point.label.text().isEmpty()
? 0 : &(point.label.text());
const QImage *img = style.img().isNull() ? 0 : &style.img();
const QFont *fnt = point.poi
? poiFont(style.textFontSize()) : font(style.textFontSize());
const QColor *color = style.textColor().isValid()
? &style.textColor() : 0;
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);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
}

48
src/map/IMG/rastertile.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef RASTERTILE_H
#define RASTERTILE_H
#include <QImage>
#include "mapdata.h"
class QPainter;
class TextItem;
class Style;
class RasterTile
{
public:
RasterTile(const Style *style, int zoom, const QRect &rect,
const QString &key, const QList<MapData::Poly> &polygons,
const QList<MapData::Poly> &lines, QList<MapData::Point> &points)
: _style(style), _zoom(zoom), _xy(rect.topLeft()),
_key(key), _img(rect.size(), QImage::Format_ARGB32_Premultiplied),
_polygons(polygons), _lines(lines), _points(points) {}
const QString &key() const {return _key;}
const QPoint &xy() const {return _xy;}
const QImage &img() const {return _img;}
void render();
private:
void drawPolygons(QPainter *painter);
void drawLines(QPainter *painter);
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
void processPolygons(QList<TextItem *> &textItems);
void processLines(QList<TextItem*> &textItems);
void processPoints(QList<TextItem*> &textItems);
void processShields(const QRect &tileRect, QList<TextItem*> &textItems);
void processStreetNames(const QRect &tileRect, QList<TextItem*> &textItems);
const Style *_style;
int _zoom;
QPoint _xy;
QString _key;
QImage _img;
QList<MapData::Poly> _polygons;
QList<MapData::Poly> _lines;
QList<MapData::Point> _points;
};
#endif // RASTERTILE_H

View File

@ -5,9 +5,12 @@
#include "huffmanstream.h"
#include "lblfile.h"
#include "netfile.h"
#include "nodfile.h"
#include "rgnfile.h"
#define MASK(bits) ((2U << ((bits) - 1U)) - 1U)
static quint64 pointId(const QPoint &pos, quint32 type, quint32 labelPtr)
{
quint64 id;
@ -52,8 +55,7 @@ bool RGNFile::skipClassFields(Handle &hdl) const
return seek(hdl, hdl.pos() + rs);
}
bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3],
SegmentType type) const
bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3]) const
{
quint32 bitfield = 0xFFFFFFFF;
@ -61,29 +63,40 @@ bool RGNFile::skipLclFields(Handle &hdl, const quint32 flags[3],
if (!readVBitfield32(hdl, bitfield))
return false;
for (int i = 0; i < 29; i++) {
for (int i = 0, j = 0; i < 29; i++) {
if ((flags[0] >> i) & 1) {
if (bitfield & 1) {
quint32 m = flags[(i >> 4) + 1] >> ((i * 2) & 0x1e) & 3;
switch (i) {
case 5:
if (m == 1 && type == Point) {
quint16 u16;
if (!readUInt16(hdl, u16))
return false;
}
break;
default:
break;
}
quint32 m = flags[(j >> 4) + 1] >> ((j * 2) & 0x1e) & 3;
quint32 skip = 0;
if (m == 3) {
if (!readVUInt32(hdl, skip))
return false;
} else
skip = m + 1;
if (!seek(hdl, hdl.pos() + skip))
return false;
}
bitfield >>= 1;
j++;
}
}
return true;
}
bool RGNFile::skipGblFields(Handle &hdl, quint32 flags) const
{
int cnt = 0;
do {
cnt = cnt + (flags & 3);
flags = flags >> 2;
} while (flags != 0);
return seek(hdl, hdl.pos() + cnt);
}
void RGNFile::clearFlags()
{
memset(_polygonsFlags, 0, sizeof(_polygonsFlags));
@ -102,14 +115,17 @@ bool RGNFile::init(Handle &hdl)
if (hdrLen >= 0x68) {
if (!(readUInt32(hdl, _polygonsOffset) && readUInt32(hdl, _polygonsSize)
&& seek(hdl, _gmpOffset + 0x2D) && readUInt32(hdl, _polygonsFlags[0])
&& readUInt32(hdl, _polygonsFlags[1]) && readUInt32(hdl, _polygonsFlags[2])
&& seek(hdl, _gmpOffset + 0x29) && readUInt32(hdl, _polygonGblFlags)
&& readUInt32(hdl, _polygonsFlags[0]) && readUInt32(hdl, _polygonsFlags[1])
&& readUInt32(hdl, _polygonsFlags[2])
&& readUInt32(hdl, _linesOffset) && readUInt32(hdl, _linesSize)
&& seek(hdl, _gmpOffset + 0x49) && readUInt32(hdl, _linesFlags[0])
&& readUInt32(hdl, _linesFlags[1]) && readUInt32(hdl, _linesFlags[2])
&& seek(hdl, _gmpOffset + 0x45) && readUInt32(hdl, _linesGblFlags)
&& readUInt32(hdl, _linesFlags[0]) && readUInt32(hdl, _linesFlags[1])
&& readUInt32(hdl, _linesFlags[2])
&& readUInt32(hdl, _pointsOffset) && readUInt32(hdl, _pointsSize)
&& seek(hdl, _gmpOffset + 0x65) && readUInt32(hdl, _pointsFlags[0])
&& readUInt32(hdl, _pointsFlags[1]) && readUInt32(hdl, _pointsFlags[2])))
&& seek(hdl, _gmpOffset + 0x61) && readUInt32(hdl, _pointsGblFlags)
&& readUInt32(hdl, _pointsFlags[0]) && readUInt32(hdl, _pointsFlags[1])
&& readUInt32(hdl, _pointsFlags[2])))
return false;
}
@ -230,6 +246,7 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
&& readInt16(hdl, lon) && readInt16(hdl, lat)
&& readVUInt32(hdl, len)))
return false;
Q_ASSERT(hdl.pos() + len <= segment.end());
poly.type = 0x10000 | (quint16(type)<<8) | (subtype & 0x1F);
labelPtr = 0;
@ -239,8 +256,10 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
qint32 lonDelta, latDelta;
HuffmanStream stream(*this, hdl, len, _huffmanTable,
segmentType == Line);
BitStream4F bs(*this, hdl, len);
HuffmanStreamF stream(bs, _huffmanTable);
if (!stream.init(segmentType == Line))
return false;
if (shift) {
if (!stream.readOffset(lonDelta, latDelta))
@ -298,7 +317,11 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
if (subtype & 0x80 && !skipClassFields(hdl))
return false;
if (subtype & 0x40 && !skipLclFields(hdl, segmentType == Line
? _linesFlags : _polygonsFlags, segmentType))
? _linesFlags : _polygonsFlags))
return false;
quint32 gblFlags = (segmentType == Line)
? _linesGblFlags : _polygonGblFlags;
if (gblFlags && !skipGblFields(hdl, gblFlags))
return false;
if (lbl && (labelPtr & 0x3FFFFF))
@ -317,6 +340,7 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
const SubDiv::Segment &segment = (segmentType == IndexedPoint)
? subdiv->idxPoints() : subdiv->points();
if (!segment.isValid())
return true;
if (!seek(hdl, segment.offset()))
@ -379,7 +403,9 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
return false;
if (subtype & 0x80 && !skipClassFields(hdl))
return false;
if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags, Point))
if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags))
return false;
if (_pointsGblFlags && !skipGblFields(hdl, _pointsGblFlags))
return false;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
@ -403,6 +429,86 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
return true;
}
bool RGNFile::links(Handle &hdl, const SubDiv *subdiv, NETFile *net,
Handle &netHdl, NODFile *nod, Handle &nodHdl, QList<IMG::Poly> *lines) const
{
quint32 size, blockIndexIdSize, blockIndexId;
quint8 flags;
const SubDiv::Segment &segment = subdiv->roadReferences();
if (!segment.isValid())
return true;
if (!seek(hdl, segment.offset()))
return false;
if (!net || !nod)
return false;
if (!(blockIndexIdSize = nod->indexIdSize(nodHdl)))
return false;
while (hdl.pos() < (int)segment.end()) {
if (!readVUInt32(hdl, size))
return false;
int pos = hdl.pos();
if (!(readUInt8(hdl, flags) && readVUInt32(hdl, blockIndexIdSize,
blockIndexId)))
return false;
quint8 bits[3];
for (int i = 0; i < 3; i++)
bits[i] = 0x4000a08 >> (((flags >> (2*i) & 3) << 3) ^ 0x10);
quint8 byteSize = ((bits[0] + bits[1] + bits[2]) + 7) >> 3;
quint32 counts;
if (!readVUInt32(hdl, byteSize, counts))
return false;
quint16 b8 = bits[0] ? (MASK(bits[0]) & counts) + 1 : 0;
quint16 b10 = bits[1] ? (MASK(bits[1]) & (counts >> bits[0])) + 1 : 0;
quint16 b16 = bits[2] ? (MASK(bits[2]) & (counts >> (bits[0] + bits[1])))
+ 1 : 0;
NODFile::BlockInfo blockInfo;
if (!nod->blockInfo(nodHdl, blockIndexId, blockInfo))
return false;
quint8 linkId, lineId;
for (int i = 0; i < b8 + b10 + b16; i++) {
if (!b8 || b8 <= i) {
quint16 v16;
if (!readUInt16(hdl, v16))
return false;
if (!b16 || b8 + b16 <= i) {
int shift = ((i - (b8 + b16)) * 10) % 8;
linkId = (quint8)(v16 >> shift);
lineId = (((v16 >> shift) >> 8) & 3) + 1;
if (shift < 6 && i < b8 + b10 + b16 - 1)
seek(hdl, hdl.pos() - 1);
} else {
linkId = (quint8)v16;
lineId = v16 >> 8;
Q_ASSERT(lineId > 4);
}
} else {
if (!readUInt8(hdl, linkId))
return false;
lineId = 0;
}
net->link(subdiv, netHdl, nod, nodHdl, blockInfo, linkId, lineId,
_huffmanTable, lines);
}
Q_ASSERT(pos + (int)size == hdl.pos());
}
return true;
}
QMap<RGNFile::SegmentType, SubDiv::Segment> RGNFile::segments(Handle &hdl,
SubDiv *subdiv) const
{

View File

@ -8,6 +8,7 @@
class LBLFile;
class NETFile;
class NODFile;
class RGNFile : public SubFile
{
@ -46,6 +47,8 @@ public:
QList<IMG::Poly> *polys) const;
bool extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
Handle &lblHdl, QList<IMG::Point> *points) const;
bool links(Handle &hdl, const SubDiv *subdiv, NETFile *net, Handle &netHdl,
NODFile *nod, Handle &nodHdl, QList<IMG::Poly> *lines) const;
bool subdivInit(Handle &hdl, SubDiv *subdiv) const;
@ -54,8 +57,9 @@ private:
const;
void clearFlags();
bool skipClassFields(Handle &hdl) const;
bool skipLclFields(Handle &hdl, const quint32 flags[3], SegmentType type)
bool skipLclFields(Handle &hdl, const quint32 flags[3])
const;
bool skipGblFields(Handle &hdl, quint32 flags) const;
quint32 _offset;
quint32 _size;
@ -69,6 +73,9 @@ private:
quint32 _pointsOffset;
quint32 _pointsSize;
quint32 _pointsFlags[3];
quint32 _polygonGblFlags;
quint32 _linesGblFlags;
quint32 _pointsGblFlags;
HuffmanTable _huffmanTable;

View File

@ -25,12 +25,12 @@ void Style::defaultPolygonStyle()
_polygons[TYPE(0x12)] = Polygon(QBrush("#e6e2d9"));
_polygons[TYPE(0x13)] = Polygon(QBrush("#dbd0b6"),
QPen(QColor("#cdccc4"), 1));
_polygons[TYPE(0x14)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x15)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x14)] = Polygon(QBrush("#cadfaf"));
_polygons[TYPE(0x15)] = Polygon(QBrush("#cadfaf"));
_polygons[TYPE(0x16)] = Polygon(QBrush(QColor("#9ac269"),
Qt::BDiagPattern));
_polygons[TYPE(0x17)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x18)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x17)] = Polygon(QBrush("#e4efcf"));
_polygons[TYPE(0x18)] = Polygon(QBrush("#e3edc6"));
_polygons[TYPE(0x19)] = Polygon(QBrush("#e3edc6"), QPen("#c9d3a5"));
_polygons[TYPE(0x1a)] = Polygon(QBrush("#000000", Qt::Dense6Pattern),
QPen(QColor("#cdccc4"), 1));
@ -60,23 +60,23 @@ void Style::defaultPolygonStyle()
_polygons[TYPE(0x4a)] = Polygon(QBrush("#f1f0e5"), QPen("#f1f0e5"));
_polygons[TYPE(0x4c)] = Polygon(QBrush("#9fc4e1", Qt::Dense6Pattern));
_polygons[TYPE(0x4d)] = Polygon(QBrush("#ddf1fd"));
_polygons[TYPE(0x4e)] = Polygon(QBrush("#e3edc1"));
_polygons[TYPE(0x4f)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x50)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x4e)] = Polygon(QBrush("#f8f8f8"));
_polygons[TYPE(0x4f)] = Polygon(QBrush("#e4efcf"));
_polygons[TYPE(0x50)] = Polygon(QBrush("#cadfaf"));
_polygons[TYPE(0x51)] = Polygon(QBrush("#9fc4e1", Qt::Dense4Pattern));
_polygons[TYPE(0x52)] = Polygon(QBrush("#d4ebb8"));
_polygons[TYPE(0x52)] = Polygon(QBrush("#cadfaf"));
_drawOrder << TYPE(0x4b) << TYPE(0x4a) << TYPE(0x01) << TYPE(0x02)
<< TYPE(0x03) << TYPE(0x17) << TYPE(0x18) << TYPE(0x19) << TYPE(0x1a)
<< TYPE(0x28) << TYPE(0x29) << TYPE(0x32) << TYPE(0x3b) << TYPE(0x3c)
<< TYPE(0x3d) << TYPE(0x3e) << TYPE(0x3f) << TYPE(0x40) << TYPE(0x41)
<< TYPE(0x42) << TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46)
<< TYPE(0x47) << TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d)
<< TYPE(0x4e) << TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52)
<< TYPE(0x14) << TYPE(0x15) << TYPE(0x16) << TYPE(0x1e) << TYPE(0x1f)
<< TYPE(0x04) << TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08)
<< TYPE(0x09) << TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d)
<< TYPE(0x0e) << TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12)
<< TYPE(0x03) << TYPE(0x17) << TYPE(0x18) << TYPE(0x1a) << TYPE(0x28)
<< TYPE(0x29) << TYPE(0x32) << TYPE(0x3b) << TYPE(0x3c) << TYPE(0x3d)
<< TYPE(0x3e) << TYPE(0x3f) << TYPE(0x40) << TYPE(0x41) << TYPE(0x42)
<< TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46) << TYPE(0x47)
<< TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d) << TYPE(0x4e)
<< TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52) << TYPE(0x14)
<< TYPE(0x15) << TYPE(0x16) << TYPE(0x1e) << TYPE(0x1f) << TYPE(0x04)
<< TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08) << TYPE(0x09)
<< TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d) << TYPE(0x0e)
<< TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12) << TYPE(0x19)
<< TYPE(0x13);
}

View File

@ -21,8 +21,9 @@ public:
quint32 _offset, _end;
};
SubDiv(quint32 offset, qint32 lon, qint32 lat, int bits, quint8 objects)
: _lon(lon), _lat(lat), _bits(bits), _init(false)
SubDiv(quint32 offset, qint32 lon, qint32 lat, quint8 level, quint8 bits,
quint8 objects) : _lon(lon), _lat(lat), _level(level), _bits(bits),
_init(false)
{
_tre.objects = objects;
_tre.offset = offset;
@ -78,6 +79,7 @@ public:
qint32 lon() const {return _lon;}
qint32 lat() const {return _lat;}
quint8 bits() const {return _bits;}
quint8 level() const {return _level;}
// Valid only after initialization
Segment points() const
@ -94,6 +96,8 @@ public:
{return Segment(_rgn.extLinesOffset, _rgn.extLinesEnd);}
Segment extPolygons() const
{return Segment(_rgn.extPolygonsOffset, _rgn.extPolygonsEnd);}
Segment roadReferences() const
{return Segment(_rgn.roadReferencesOffset, _rgn.roadReferencesEnd);}
// Valid only until initialization
quint8 objects() const {return _tre.objects;}
@ -142,6 +146,7 @@ private:
};
qint32 _lon, _lat;
quint8 _level;
quint8 _bits;
bool _init;
union {

View File

@ -3,26 +3,28 @@
#include "subfile.h"
#define mod2n(x, m) ((x) & ((m) - 1));
bool SubFile::seek(Handle &handle, quint32 pos) const
{
if (handle._file) {
int blockNum = pos / BLOCK_SIZE;
int blockNum = pos >> BLOCK_BITS;
if (handle._blockNum != blockNum) {
if (!handle._file->seek((qint64)blockNum * BLOCK_SIZE))
if (!handle._file->seek((quint64)blockNum << BLOCK_BITS))
return false;
if (handle._file->read(handle._data.data(), BLOCK_SIZE) < 0)
if (handle._file->read(handle._data.data(), (1<<BLOCK_BITS)) < 0)
return false;
handle._blockNum = blockNum;
}
handle._blockPos = pos % BLOCK_SIZE;
handle._blockPos = mod2n(pos, 1U<<BLOCK_BITS);
handle._pos = pos;
return true;
} else {
quint32 blockSize = _img->blockSize();
int blockNum = pos / blockSize;
quint32 blockBits = _img->blockBits();
int blockNum = pos >> blockBits;
if (handle._blockNum != blockNum) {
if (blockNum >= _blocks->size())
@ -32,7 +34,7 @@ bool SubFile::seek(Handle &handle, quint32 pos) const
handle._blockNum = blockNum;
}
handle._blockPos = pos % blockSize;
handle._blockPos = mod2n(pos, 1U<<blockBits);
handle._pos = pos;
return true;
@ -70,6 +72,22 @@ bool SubFile::readVUInt32(Handle &hdl, quint32 &val) const
return true;
}
bool SubFile::readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const
{
switch (bytes) {
case 1:
return readUInt8(hdl, val);
case 2:
return readUInt16(hdl, val);
case 3:
return readUInt24(hdl, val);
case 4:
return readUInt32(hdl, val);
default:
return false;
}
}
bool SubFile::readVBitfield32(Handle &hdl, quint32 &bitfield) const
{
quint8 bits;

View File

@ -6,12 +6,12 @@
#include "img.h"
#define BLOCK_SIZE 4096
#define BLOCK_BITS 12 /* 4096 bytes */
class SubFile
{
public:
enum Type {Unknown, TRE, RGN, LBL, NET, TYP, GMP};
enum Type {Unknown, TRE, RGN, LBL, NET, NOD, TYP, GMP};
class Handle
{
@ -22,9 +22,9 @@ public:
if (subFile && subFile->_path) {
_file = new QFile(*(subFile->_path));
_file->open(QIODevice::ReadOnly);
_data.resize(BLOCK_SIZE);
_data.resize(1U<<BLOCK_BITS);
} else if (subFile)
_data.resize(subFile->_img->blockSize());
_data.resize(1U<<subFile->_img->blockBits());
}
~Handle() {delete _file;}
@ -132,6 +132,7 @@ public:
}
bool readVUInt32(Handle &hdl, quint32 &val) const;
bool readVUInt32(Handle &hdl, quint32 bytes, quint32 &val) const;
bool readVBitfield32(Handle &hdl, quint32 &bitfield) const;
QString fileName() const {return _path ? *_path : _img->fileName();}
@ -142,7 +143,7 @@ protected:
private:
bool readByte(Handle &handle, quint8 &val) const
{
int blockSize = _img ? _img->blockSize() : BLOCK_SIZE;
int blockSize = _img ? 1U<<_img->blockBits() : 1U<<BLOCK_BITS;
val = handle._data.at(handle._blockPos++);
handle._pos++;
return (handle._blockPos >= blockSize)

Some files were not shown because too many files have changed in this diff Show More