1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-04 06:49:16 +02:00

Compare commits

...

332 Commits
7.17 ... 7.26

Author SHA1 Message Date
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
bf0dd1b24a Version++ 2020-03-04 19:49:11 +01:00
9b687bb830 Merge branch 'origin/master' into Weblate. 2020-03-04 19:48:15 +01:00
9859608115 Added missing support for URLs defined in OnlineResources 2020-03-04 19:47:23 +01:00
3d66b2fbb6 Merge branch 'origin/master' into Weblate. 2020-03-03 09:39:27 +01:00
9f62b7114e The service parameter is expected in the GetMap request by some servers
(The WMS specification is not 100% clear here)
2020-03-03 09:38:18 +01:00
c8f7e6f691 Merge branch 'origin/master' into Weblate. 2020-03-03 09:29:47 +01:00
c85f404d28 Enable specifiing of format parameters 2020-03-03 09:29:16 +01:00
273a0f0f27 Merge branch 'origin/master' into Weblate. 2020-03-01 14:39:44 +01:00
bb6d6a4044 Version++ 2020-03-01 14:39:40 +01:00
bd3a3b90bc Merge branch 'origin/master' into Weblate. 2020-03-01 13:59:42 +01:00
521369a6ec Make the WMS tile size configurable 2020-03-01 13:59:15 +01:00
440a5736f6 Merge branch 'origin/master' into Weblate. 2020-03-01 13:26:52 +01:00
45a6cdeda0 Strip the format parameters for format comparsion 2020-03-01 13:26:19 +01:00
f73c27c39c Merge branch 'origin/master' into Weblate. 2020-03-01 11:46:52 +01:00
12827edcb2 Removed obsolete include 2020-03-01 11:46:44 +01:00
3ec5c37fd5 Merge branch 'origin/master' into Weblate. 2020-03-01 11:43:41 +01:00
ee24bd54f1 Fixed tile cache reload issues 2020-03-01 11:43:08 +01:00
cc22df3bf2 Cosmetics 2020-03-01 10:30:00 +01:00
ef017edbf6 Merge branch 'origin/master' into Weblate. 2020-02-29 21:40:28 +01:00
d7f0cda4b2 Properly parse the ScaleHint tag 2020-02-29 21:40:13 +01:00
dc03ab91d6 Merge branch 'origin/master' into Weblate. 2020-02-29 20:12:26 +01:00
a898ff2807 Use 72dpi in the ScaleHint to scaleDenominator transformation 2020-02-29 20:11:49 +01:00
497017091f Merge branch 'origin/master' into Weblate. 2020-02-29 13:48:19 +01:00
9dd4e117f6 Added missing WMS 1.1 ScaleHint handling 2020-02-29 13:47:27 +01:00
86535021aa Merge branch 'origin/master' into Weblate. 2020-02-23 12:45:59 +01:00
92deaaaf2b Use the latest xmlns 2020-02-23 12:45:36 +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
015a9187a0 Fixed memory leak
(formal only, the data is allocated during the whole application life anyway)
2020-02-20 09:02:01 +01:00
1de9c6ef5d Version++ 2020-02-17 20:19:11 +01:00
54b6225c6c Fixed "rect inversion" problems 2020-02-17 19:23:36 +01:00
48c7299ba6 Improved railroad lines default style 2020-02-17 19:21:36 +01:00
c284b9fa7c Back to lon, lat order to not correspond with all the APIs 2020-02-17 19:20:18 +01:00
2c503a2406 Enable world basemap projection in web mercator projection 2020-02-17 09:47:47 +01:00
27edc4d6b5 Properly handle IMG basemaps (gmapbmap.img) 2020-02-17 09:19:15 +01:00
f333a76ef7 Some more regions/countries rendering improvement 2020-02-16 20:31:09 +01:00
2c114f43c5 Code cleanup 2020-02-16 15:59:51 +01:00
29e29591f8 Fixed typo 2020-02-16 13:59:19 +01:00
e4ac9fda0e Improved country names labels handling 2020-02-16 12:57:40 +01:00
26229e5871 Fixed tile bounds exceeding map bounds 2020-02-16 08:43:37 +01:00
64bee2f2f4 Use a beter default min zoom value 2020-02-15 21:58:29 +01:00
e4288ee95c Remove devel code 2020-02-15 21:51:47 +01:00
c9b3c2eedd Compute the min map zoom from the tiles 2020-02-15 21:49:00 +01:00
42e4b0769f Fixed broken tile/subdivs/polygon bounds/coordinates
+ do the coordinates left shift in a C++ standard defined way
2020-02-15 11:46:16 +01:00
ce043ef8fa Do not try to open the user style file when it is not set 2020-02-14 20:00:42 +01:00
8c7050e273 Fixed broken subdiv bounds 2020-02-14 09:20:10 +01:00
d670107a11 Fixed gap between the basemap and normal map tiles on some maps 2020-02-13 22:49:15 +01:00
7b03c4d852 Fixed error handling 2020-02-13 22:48:47 +01:00
2002b828dd Version++ 2020-02-12 20:35:59 +01:00
71f0e1d0ac Cleanup the data loading code 2020-02-12 20:33:23 +01:00
eb9767f2dd Cleanup the map loading code 2020-02-12 20:32:57 +01:00
8d06ab6208 Enable loading of GMAP maps when Garmin BaseCamp has associated them 2020-02-12 09:37:03 +01:00
187cb77858 Added missing Hungarian localization copy 2020-02-12 08:36:56 +01:00
8167a995f6 Added GMAP support info 2020-02-11 23:31:51 +01:00
b5aed7314e Merge branch 'origin/master' into Weblate. 2020-02-11 21:16:44 +01:00
464d4c5327 Removed forgotten devel code 2020-02-11 21:16:31 +01:00
11f4dc4b41 Merge branch 'origin/master' into Weblate. 2020-02-11 21:04:43 +01:00
2d3ad41d69 Use the GMAP basemaps rather than discarding them
+ some minor point rendering issues fixes
2020-02-11 21:03:55 +01:00
a7dcc57dd1 Translated using Weblate (Spanish)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/es/
2020-02-11 18:50:35 +01:00
2d5e11f001 Properly handle basemap entries with './xxx' paths 2020-02-10 19:40:39 +01:00
e86f89308b Decreased GMAP read block size to 4K 2020-02-10 19:40:04 +01:00
378fa8dc0e Make caching work for both IMG and GMAP maps 2020-02-10 19:38:45 +01:00
dde8903013 Merge branch 'origin/master' into Weblate. 2020-02-10 09:33:37 +01:00
37c4fe1eba Cosmetics 2020-02-10 09:33:31 +01:00
f9db0acb03 Merge branch 'origin/master' into Weblate. 2020-02-10 09:26:32 +01:00
1aa07a6a34 Removed duplicit code 2020-02-10 09:26:23 +01:00
c302a67299 Merge branch 'origin/master' into Weblate. 2020-02-10 09:11:05 +01:00
8b6d7acec5 Give the compiler more posibilities for optimization 2020-02-10 09:10:09 +01:00
ba70fd159d Merge branch 'origin/master' into Weblate. 2020-02-10 08:55:45 +01:00
1773a1ae0d Cosmetics 2020-02-10 08:55:34 +01:00
136f08aa76 Merge branch 'origin/master' into Weblate. 2020-02-09 23:27:36 +01:00
f70d92805b Version++ 2020-02-09 23:27:22 +01:00
69e66f1856 Merge branch 'origin/master' into Weblate. 2020-02-09 23:25:25 +01:00
911c63df0c Added support for GMAP maps 2020-02-09 23:24:48 +01:00
d16899530a Merge branch 'origin/master' into Weblate. 2020-02-08 16:17:00 +01:00
e2339c67cd Fixed bitstream4 flush 2020-02-08 16:16:24 +01:00
c809d2f17e Merge branch 'origin/master' into Weblate. 2020-02-08 16:02:00 +01:00
fac0bae006 Fixed crash on GMP tiles without LBL or NET parts 2020-02-08 16:01:22 +01:00
9bd03c1225 Merge branch 'origin/master' into Weblate. 2020-02-08 10:55:07 +01:00
d63c666997 Code cleanup 2020-02-08 10:54:59 +01:00
0cd20a1e57 Unions can not have non-POD members 2020-02-08 10:52:26 +01:00
2f70d46be8 Merge branch 'origin/master' into Weblate. 2020-02-07 22:11:25 +01:00
325e83569c Redesigned IMG caching
(Cache encoded data rather than raw data)
2020-02-07 22:10:06 +01:00
56b374ed30 Merge branch 'origin/master' into Weblate. 2020-02-04 23:04:08 +01:00
df1be4aeb9 Added missing reference 2020-02-04 23:03:50 +01:00
d79bdaef78 Merge branch 'origin/master' into Weblate. 2020-02-04 23:02:01 +01:00
fa0c09b30c Code cleanup 2020-02-04 23:01:47 +01:00
95c82c501a Merge branch 'origin/master' into Weblate. 2020-02-04 21:48:14 +01:00
3b16f37e66 Some micro-optimizatoins and code cleanup 2020-02-04 21:47:31 +01:00
661e26fbdb Merge branch 'origin/master' into Weblate. 2020-02-03 23:18:28 +01:00
25939cfa62 Fixed typo
(I'd rather not investigate how it could work...)
2020-02-03 23:18:00 +01:00
c59ea4e5cd Merge branch 'origin/master' into Weblate. 2020-02-03 22:58:35 +01:00
5de1bc7e7d Update gpxsee.desktop (#271) 2020-02-03 22:58:29 +01:00
7c3399575b Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2020-02-03 09:37:41 +01:00
7bedd17071 Translated using Weblate (Polish)
Currently translated at 98.3% (345 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pl/
2020-02-03 09:37:39 +01:00
06a84dcea2 Code cleanup 2020-02-02 09:03:35 +01:00
ca204626a1 Some hot path inlining 2020-02-01 19:21:41 +01:00
d16ef7b081 Translated using Weblate (Hungarian)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2020-01-30 09:01:52 +01:00
ef013dc036 Translated using Weblate (Hungarian)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2020-01-28 23:51:08 +01:00
633d52daca Added Hungarian localization 2020-01-28 22:02:16 +01:00
adbb5e5684 Translated using Weblate (Hungarian)
Currently translated at 98.0% (344 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/hu/
2020-01-28 14:34:01 +01:00
bd9b09df3d Merge branch 'origin/master' into Weblate. 2020-01-26 18:24:52 +01:00
ede2b6004f Added SML info 2020-01-26 18:24:48 +01:00
a516055dc2 Merge branch 'origin/master' into Weblate. 2020-01-26 18:06:26 +01:00
1ba3ada96e Added Hungarian localization stub 2020-01-26 18:05:50 +01:00
1667e0ca70 Merge branch 'origin/master' into Weblate. 2020-01-26 15:59:41 +01:00
cb03c8a903 Always run the redistributables installer and let it decide what to do
(instead of an own and broken registry check with magic numbers)
2020-01-26 15:58:20 +01:00
b50d5227a5 Merge branch 'origin/master' into Weblate. 2020-01-26 14:08:23 +01:00
eef6360643 Fixed MSVC redist checks 2020-01-26 14:07:49 +01:00
f38db3227f Merge branch 'origin/master' into Weblate. 2020-01-26 13:24:27 +01:00
ac73c04445 MSVC redistributables names have changed 2020-01-26 13:23:45 +01:00
c3aeb95660 Merge branch 'origin/master' into Weblate. 2020-01-26 13:14:09 +01:00
3299b41ec7 There is no Qt 5.14/VS2017 on appveyor... 2020-01-26 13:13:34 +01:00
6bdd97611d Merge branch 'origin/master' into Weblate. 2020-01-26 13:08:16 +01:00
58fd5022ca paths? 2020-01-26 13:07:55 +01:00
15e09539c7 Merge branch 'origin/master' into Weblate. 2020-01-26 13:02:48 +01:00
ebe1851abb ... 2020-01-26 13:02:29 +01:00
a225c6d308 Merge branch 'origin/master' into Weblate. 2020-01-26 12:57:56 +01:00
b3ddbd7b63 Removed duplicit configuration 2020-01-26 12:57:27 +01:00
a94056ac7e Merge branch 'origin/master' into Weblate. 2020-01-26 12:56:15 +01:00
e5042b11d7 Maybe there is a completely different error... 2020-01-26 12:55:46 +01:00
aea7bbdf09 Merge branch 'origin/master' into Weblate. 2020-01-26 12:51:40 +01:00
2e0eadd0e7 Try another path 2020-01-26 12:51:19 +01:00
1c9b31d7c9 Merge branch 'origin/master' into Weblate. 2020-01-26 12:50:07 +01:00
42e917efab Reduced test 2020-01-26 12:49:47 +01:00
7a9b26756a Merge branch 'origin/master' into Weblate. 2020-01-26 12:40:03 +01:00
07761ea335 Try it using conditions... 2020-01-26 12:39:38 +01:00
eaae965719 Merge branch 'origin/master' into Weblate. 2020-01-26 12:33:41 +01:00
f17e5c2ea1 try it without call 2020-01-26 12:33:15 +01:00
ca6e8638d8 Merge branch 'origin/master' into Weblate. 2020-01-26 12:17:38 +01:00
21bcaf9562 Some more try... 2020-01-26 12:17:15 +01:00
66a22fbfee Merge branch 'origin/master' into Weblate. 2020-01-26 12:08:32 +01:00
a160cb4eb3 Maybe this way... 2020-01-26 12:08:02 +01:00
2d3ca7c5f8 Merge branch 'origin/master' into Weblate. 2020-01-26 12:02:53 +01:00
f5ec18a13a Surrender 2020-01-26 12:02:30 +01:00
8c71d11fa6 Merge branch 'origin/master' into Weblate. 2020-01-26 11:59:36 +01:00
f6b15e1acc Yet another try... 2020-01-26 11:59:12 +01:00
857c5050f4 Merge branch 'origin/master' into Weblate. 2020-01-26 11:52:39 +01:00
652471ac90 ?! 2020-01-26 11:52:16 +01:00
44d1d27c93 Merge branch 'origin/master' into Weblate. 2020-01-26 11:50:38 +01:00
2da1f8bc70 @#$%^! windows shell 2020-01-26 11:49:42 +01:00
68e20cff5b Merge branch 'origin/master' into Weblate. 2020-01-26 11:43:41 +01:00
2d4d2721bf Added missing quotes 2020-01-26 11:43:17 +01:00
1c67f1cac9 Merge branch 'origin/master' into Weblate. 2020-01-26 11:41:12 +01:00
d74816883a AppVeyor build fix 2020-01-26 11:40:38 +01:00
a09d13594d Merge branch 'origin/master' into Weblate. 2020-01-26 11:28:42 +01:00
81b5631de4 Updated Windows build environment
(Qt 5.14, MSVC 2017, OpenSSL 1.1.1)
2020-01-26 11:27:48 +01:00
6916d0e6b4 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2020-01-25 22:21:36 +01:00
757ec98108 Translated using Weblate (Russian)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2020-01-25 22:21:35 +01:00
2ddb5dc28b Translated using Weblate (Finnish)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2020-01-25 22:21:35 +01:00
2e84901e43 Merge branch 'origin/master' into Weblate. 2020-01-24 19:45:01 +01:00
f61fb5657c Translated using Weblate (Spanish)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/es/
2020-01-24 19:45:01 +01:00
24e216cdfc Translated using Weblate (French)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fr/
2020-01-24 19:45:00 +01:00
09d6de00a6 Some more style/layout fiddeling
+ code cleanup
2020-01-24 19:44:19 +01:00
f7990ee2e6 Merge branch 'origin/master' into Weblate. 2020-01-23 23:22:56 +01:00
cb01c0b590 Cleanup the remaining parser that stores temporary data 2020-01-23 23:22:04 +01:00
21cf5ddaec Merge branch 'origin/master' into Weblate. 2020-01-23 23:21:01 +01:00
48d4686dd4 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pt_BR/
2020-01-23 23:21:01 +01:00
d035a307d8 Variables with underscores followed by a capital letter are prohibited by the C++ standard
Fixes #266
2020-01-23 23:19:32 +01:00
89e04f3678 Translated using Weblate (Swedish)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2020-01-23 08:04:58 +01:00
e664fc188c Translated using Weblate (German)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2020-01-23 08:04:58 +01:00
3597a07ed7 Translated using Weblate (Czech)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2020-01-23 08:04:58 +01:00
0472cc03f3 Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.0% (323 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2020-01-23 08:04:57 +01:00
d169a9f710 Translated using Weblate (Turkish)
Currently translated at 100.0% (351 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2020-01-23 08:04:57 +01:00
2ff2195116 Localization update 2020-01-22 22:42:26 +01:00
cdbf2db5fe Version++ 2020-01-22 22:39:14 +01:00
2bb635a120 Log coordinates in a better usable way 2020-01-22 22:36:58 +01:00
465b146001 Do not duplicate the area descriptions where (most) maps have POIs 2020-01-22 22:18:29 +01:00
ce6d6298bb Use the polygons labels for displaying rather than discarding them 2020-01-22 21:01:05 +01:00
ae2ff99be8 Do not fetch polygon labels as they are unused anyway 2020-01-22 07:46:58 +01:00
5b6cd24839 Merge branch 'master' of https://github.com/tumic0/GPXSee 2020-01-21 21:53:07 +01:00
cd22e6207a Added support for Huffman compressed RGN data 2020-01-21 21:50:13 +01:00
b826e497f3 Some more cosmetics 2020-01-19 21:00:11 +01:00
7cc39f25e1 Code cleanup 2020-01-19 18:08:03 +01:00
f61d9fcd55 Fixed crash on error introduced with the latest error handling changes
(+ ignore broken tiles rather than terminate parsing)
2020-01-19 17:40:14 +01:00
67a1f7e108 Includes cleanup 2020-01-19 17:05:26 +01:00
040de56a54 Fixed error handling + code cleanup 2020-01-19 13:23:20 +01:00
a486abb159 Added workaround for broken GPS IFD entries produced by NOKIA phones
Closes #260
2020-01-16 22:54:12 +01:00
37215959b8 Merge branch 'origin/master' into Weblate. 2020-01-16 19:32:17 +01:00
1ccce095a9 Updated appdata info 2020-01-16 19:31:40 +01:00
057180e431 Added OS X SML integration 2020-01-16 19:31:14 +01:00
60e62da6a2 Merge branch 'origin/master' into Weblate. 2020-01-15 23:24:58 +01:00
a3eafea60a Added missing icon file 2020-01-15 23:24:20 +01:00
962f8de160 Merge branch 'origin/master' into Weblate. 2020-01-15 23:11:01 +01:00
6525d34e08 Added SML desktop integration (Windows + Linux) 2020-01-15 23:10:14 +01:00
3c661ddf65 Merge branch 'origin/master' into Weblate. 2020-01-15 21:56:45 +01:00
42b4216d9c Added support for sensors data 2020-01-15 21:56:04 +01:00
4e7418111d Merge branch 'origin/master' into Weblate. 2020-01-14 23:21:58 +01:00
a89ef11d73 Added basic support for Suunto SML files 2020-01-14 23:21:05 +01:00
d8b54ac342 Version++ 2020-01-12 16:09:06 +01:00
293046590e Merge branch 'origin/master' into Weblate. 2020-01-12 16:08:19 +01:00
ea2f67fc09 Merge branch 'origin/master' into Weblate. 2020-01-12 16:01:50 +01:00
60b33064a7 Delete the popup immediately to prevent it "blocking" an error message 2020-01-12 16:01:38 +01:00
2efc6fb6d3 Optimization 2020-01-11 23:41:04 +01:00
eb744df9cc Merge branch 'origin/master' into Weblate. 2020-01-11 23:40:49 +01:00
8c9180190a Fixed buffer overflow 2020-01-05 00:50:02 +01:00
6c6d297a3c Merge branch 'origin/master' into Weblate. 2020-01-05 00:49:34 +01:00
6839119794 Updated 4Umaps URL 2019-12-23 17:05:38 +01:00
669715aa32 Merge branch 'origin/master' into Weblate. 2019-12-23 17:05:27 +01:00
fdc172ab38 Merge branch 'origin/master' into Weblate. 2019-12-23 16:16:26 +01:00
05f23de1ed Clear the popup on scene clear
Fixes #257
2019-12-23 16:16:15 +01:00
9a0344adac Switched OpenStreetMap to HTTPS 2019-12-23 15:48:58 +01:00
b9d5fa8772 Merge branch 'origin/master' into Weblate. 2019-12-23 15:48:57 +01:00
d4de0edca6 Merge branch 'origin/master' into Weblate. 2019-12-20 23:39:29 +01:00
fdeb24a196 Properly load non-ASCII map names 2019-12-20 23:39:20 +01:00
7f650f9d6a Merge branch 'origin/master' into Weblate. 2019-12-11 16:02:28 +01:00
a986293f20 Properly handle points with extended style IDs 2019-12-09 22:50:19 +01:00
ec0b0adba0 Merge branch 'master' of https://github.com/tumic0/GPXSee 2019-12-04 22:20:01 +01:00
52c6bb569e Merge branch 'origin/master' into Weblate. 2019-12-04 22:19:59 +01:00
0f670f9ddd A much nicer (and faster) 24b conversion 2019-12-04 22:19:00 +01:00
dba46b09a8 Merge branch 'origin/master' into Weblate. 2019-12-03 08:19:20 +01:00
ce59f13de7 Ukrainian localized comment for gpxsee.desktop entry (#259) 2019-12-03 08:19:14 +01:00
b8cf3872ce Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.0% (322 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2019-12-02 10:32:40 +01:00
b59112f74e Added missing error handling 2019-12-01 13:33:00 +01:00
17b3602fda Fixed/improved pause detection
Fixes #250
2019-11-26 23:15:06 +01:00
c0928097cc Merge branch 'origin/master' into Weblate. 2019-11-26 20:17:22 +01:00
e709fad764 French description in desktop (#253) 2019-11-26 20:17:18 +01:00
fb566ae163 Merge branch 'origin/master' into Weblate. 2019-11-26 20:16:51 +01:00
3d8a4cc6b7 append Comment[tr] (#254) 2019-11-26 20:16:47 +01:00
4eb44a53f7 Merge branch 'origin/master' into Weblate. 2019-11-26 08:15:11 +01:00
b61c39ea46 Code cleanup 2019-11-26 08:15:01 +01:00
4767be1972 Translated using Weblate (French)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fr/
2019-11-23 10:04:57 +01:00
ee9e17e31c Translated using Weblate (Ukrainian)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2019-11-20 13:24:45 +01:00
3721084d21 Made the code compile on QT5 < 5.9
+ propper error handling
2019-11-15 23:41:30 +01:00
6076c9d82e Added missing includes 2019-11-15 23:05:22 +01:00
2ef499001d Merge branch 'origin/master' into Weblate. 2019-11-15 22:11:18 +01:00
e4d8ab1feb Added support for multiple images 2019-11-15 22:10:55 +01:00
2fc82aceab Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pt_BR/
2019-11-15 14:04:32 +01:00
475eb6185a Added missing red light camera entry points 2019-11-14 22:55:06 +01:00
cdf3a48516 Improved layout on OS X 2019-11-13 20:03:19 +01:00
dcc3c0086a Merge branch 'origin/master' into Weblate. 2019-11-13 19:21:48 +01:00
0d6aeecebb Added missing speed 2019-11-13 19:21:41 +01:00
704c68443a Translated using Weblate (Finnish)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2019-11-13 19:13:01 +01:00
183b02a5e7 Translated using Weblate (Russian)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2019-11-13 19:13:01 +01:00
e91815150d Translated using Weblate (Turkish)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2019-11-13 19:13:00 +01:00
73c33450ee Translated using Weblate (Swedish)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2019-11-13 10:40:31 +01:00
e06d07c176 Translated using Weblate (German)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2019-11-13 09:52:37 +01:00
c73f30e21f Translated using Weblate (Czech)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2019-11-13 09:48:52 +01:00
bbf2044729 Fixed typo 2019-11-13 09:43:54 +01:00
4a86ab9d1e Merge remote-tracking branch 'weblate/master' 2019-11-13 09:41:25 +01:00
16c3fea8a8 Fixed weblate merge conflict 2019-11-13 09:26:42 +01:00
f4ec9b6ac4 A better option name 2019-11-13 09:13:05 +01:00
44155f9ef0 Translated using Weblate (Russian)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2019-11-13 08:54:30 +01:00
a5e809a0f3 Translated using Weblate (Finnish)
Currently translated at 100.0% (350 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2019-11-13 08:54:29 +01:00
3e70f9262b Translated using Weblate (Russian)
Currently translated at 99.7% (349 of 350 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2019-11-13 08:42:08 +01:00
3573a2edbe Localization update 2019-11-13 08:33:51 +01:00
82d9623c6d Merge branch 'origin/master' into Weblate. 2019-11-13 08:28:07 +01:00
258a9b0201 Added automatic pause detection 2019-11-13 08:27:54 +01:00
36d84f6c98 Merge branch 'origin/master' into Weblate. 2019-11-11 20:21:58 +01:00
15c79a7a13 Translated using Weblate (Norwegian Bokmål)
Currently translated at 92.0% (320 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2019-11-11 20:21:58 +01:00
34e0294815 Translated using Weblate (Finnish)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2019-11-11 20:21:58 +01:00
ad6a04b975 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2019-11-11 20:21:57 +01:00
e0f682fcd8 Translated using Weblate (Russian)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2019-11-11 20:21:57 +01:00
a801359eed Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pt_BR/
2019-11-11 20:21:57 +01:00
bb238f4c7e Missing GPI integration stuff 2019-11-11 20:21:14 +01:00
c1ebc8e74b Merge branch 'origin/master' into Weblate. 2019-11-10 19:48:36 +01:00
110c8c6bed Fixed broken coordinates decoding 2019-11-10 19:48:17 +01:00
e8fffe65bf Merge branch 'origin/master' into Weblate. 2019-11-10 19:20:38 +01:00
1731aa1890 Added propper error handling 2019-11-10 19:20:25 +01:00
c389889662 Merge branch 'origin/master' into Weblate. 2019-11-10 18:39:49 +01:00
e38e12f89b Allow arbitrary zoom levels
Closes #244
2019-11-10 18:38:47 +01:00
26f71f5d00 Merge branch 'origin/master' into Weblate. 2019-11-10 18:00:16 +01:00
65e04ce08b Fixed areas tooltip handling 2019-11-10 17:59:58 +01:00
d84d8e03e0 Merge branch 'origin/master' into Weblate. 2019-11-10 16:47:01 +01:00
475ad358ae Translated using Weblate (Turkish)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/tr/
2019-11-10 16:47:01 +01:00
db7e60bdfb Added support for GPI speed/red light cameras 2019-11-10 16:46:31 +01:00
444791fb4c Translated using Weblate (Swedish)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2019-11-10 07:00:23 +01:00
be3a882baa Merge branch 'origin/master' into Weblate. 2019-11-09 23:06:59 +01:00
6d6dc9f316 Code cleanup 2019-11-09 23:06:52 +01:00
d86e825c60 Merge branch 'origin/master' into Weblate. 2019-11-09 21:43:45 +01:00
6bb2af3da8 Translated using Weblate (Czech)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/cs/
2019-11-09 21:43:45 +01:00
d195526afb Translated using Weblate (German)
Currently translated at 100.0% (348 of 348 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/de/
2019-11-09 21:43:45 +01:00
9ec3956672 Code cleanup 2019-11-09 21:43:07 +01:00
3098d2ab95 Merge branch 'origin/master' into Weblate. 2019-11-09 21:27:06 +01:00
dccf9f7e1a Localization update 2019-11-09 21:26:52 +01:00
e55c29c33c Merge branch 'origin/master' into Weblate. 2019-11-09 21:25:57 +01:00
bd5728ffa0 Version++ 2019-11-09 21:25:45 +01:00
af525d4e3d Merge branch 'origin/master' into Weblate. 2019-11-08 21:01:37 +01:00
96e0b584a0 Added support for waypoint addresses 2019-11-08 21:00:59 +01:00
0d6e3bac17 Merge branch 'origin/master' into Weblate. 2019-11-08 01:04:43 +01:00
d292b5f533 Fixed Qt4 build 2019-11-08 01:04:17 +01:00
ec2cb21a8a Merge branch 'origin/master' into Weblate. 2019-11-07 23:24:25 +01:00
3632ed8816 Do not duplicate temporary images + improved error handling 2019-11-07 23:23:25 +01:00
58acea1a2d Translated using Weblate (French)
Currently translated at 100.0% (347 of 347 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fr/
2019-11-07 05:35:59 +01:00
9e63e3f47e Added support for GPI images
+ GPI parser fixes/improvements
2019-11-06 23:23:05 +01:00
7581c8a32a Trying to fix the OS X build 2019-11-06 08:50:46 +01:00
8715e0e37b Enabled Ukrainian localization 2019-11-05 21:59:01 +01:00
36b9813e1b Merge branch 'origin/master' into Weblate. 2019-11-05 19:41:53 +01:00
1a59e1cb24 Code cleanup 2019-11-05 19:31:52 +01:00
7f7d25a6fa Translated using Weblate (Finnish)
Currently translated at 100.0% (347 of 347 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fi/
2019-11-05 00:39:45 +01:00
87755952b6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (347 of 347 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/uk/
2019-11-04 10:19:52 +01:00
3821eb4d84 Merge branch 'origin/master' into Weblate. 2019-11-03 17:27:43 +01:00
15af82ee49 Improved pause detection 2019-11-03 17:27:15 +01:00
1f8a66108b Merge branch 'origin/master' into Weblate. 2019-11-02 23:26:16 +01:00
9309dd945b Added Ukrainian translations file stub 2019-11-02 23:25:41 +01:00
858741c9a5 Translated using Weblate (Russian)
Currently translated at 100.0% (347 of 347 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ru/
2019-11-02 19:23:59 +01:00
157 changed files with 12330 additions and 5714 deletions

View File

@ -1,22 +1,33 @@
version: 7.17.{build} version: 7.26.{build}
configuration: Release
platform: Any CPU configuration:
- Release
image:
- Visual Studio 2017
environment: environment:
NSISDIR: C:\Program Files (x86)\NSIS NSISDIR: C:\Program Files (x86)\NSIS
matrix: matrix:
- QTDIR: C:\Qt\5.11\msvc2015 - QTDIR: C:\Qt\5.13\msvc2017
PLATFORM: x86
NSI: gpxsee.nsi NSI: gpxsee.nsi
OPENSSLDIR: C:\OpenSSL-Win32\bin VCVARS: vcvars32.bat
- QTDIR: C:\Qt\5.11\msvc2015_64 OPENSSLDIR: C:\OpenSSL-v111-Win32\bin
PLATFORM: x86_amd64 LIBCRYPTO: libssl-1_1.dll
LIBSSL: libcrypto-1_1.dll
- QTDIR: C:\Qt\5.13\msvc2017_64
NSI: gpxsee64.nsi NSI: gpxsee64.nsi
OPENSSLDIR: C:\OpenSSL-Win64\bin VCVARS: vcvars64.bat
OPENSSLDIR: C:\OpenSSL-v111-Win64\bin
LIBCRYPTO: libssl-1_1-x64.dll
LIBSSL: libcrypto-1_1-x64.dll
install: install:
- cmd: >- - cmd: >-
set PATH=%QTDIR%\bin;%NSISDIR%;%PATH% set PATH=%QTDIR%\bin;%NSISDIR%;%PATH%
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PLATFORM% call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\"%VCVARS%
build_script: build_script:
- cmd: >- - cmd: >-
lrelease gpxsee.pro lrelease gpxsee.pro
@ -44,9 +55,9 @@ build_script:
copy licence.txt installer copy licence.txt installer
copy %OPENSSLDIR%\libeay32.dll installer copy %OPENSSLDIR%\%LIBCRYPTO% installer
copy %OPENSSLDIR%\ssleay32.dll installer copy %OPENSSLDIR%\%LIBSSL% installer
makensis.exe installer\%NSI% makensis.exe installer\%NSI%

View File

@ -6,6 +6,7 @@ os:
before_install: before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
install: install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install qt; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install qt; fi

View File

@ -2,9 +2,9 @@
GPXSee is a Qt-based GPS log file viewer and analyzer that supports all common GPS log file formats. GPXSee is a Qt-based GPS log file viewer and analyzer that supports all common GPS log file formats.
## Features ## Features
* Opens GPX, TCX, FIT, KML, NMEA, IGC, CUP, SLF, LOC, GeoJSON, OziExplorer (PLT, RTE, WPT), Garmin GPI&CSV and geotagged JPEG files. * Opens GPX, TCX, FIT, KML, NMEA, IGC, CUP, SIGMA SLF, Suunto SML, LOC, GeoJSON, OziExplorer (PLT, RTE, WPT), Garmin GPI&CSV and geotagged JPEG files.
* User-definable online maps (OpenStreetMap/Google tiles, WMTS, WMS, TMS, QuadTiles). * User-definable online maps (OpenStreetMap/Google tiles, WMTS, WMS, TMS, QuadTiles).
* Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases, Garmin IMG & JNX maps, TwoNav RMaps, GeoTIFF images). * Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases, Garmin IMG/GMAP & JNX maps, TwoNav RMaps, GeoTIFF images).
* Elevation, speed, heart rate, cadence, power, temperature and gear ratio/shifts graphs. * Elevation, speed, heart rate, cadence, power, temperature and gear ratio/shifts graphs.
* Support for DEM files (SRTM HGT). * Support for DEM files (SRTM HGT).
* Support for multiple tracks in one view. * Support for multiple tracks in one view.

View File

@ -3,7 +3,7 @@ unix:!macx {
} else { } else {
TARGET = GPXSee TARGET = GPXSee
} }
VERSION = 7.17 VERSION = 7.26
QT += core \ QT += core \
gui \ gui \
@ -20,7 +20,9 @@ equals(QT_MAJOR_VERSION, 5) : lessThan(QT_MINOR_VERSION, 4) {QT += opengl}
INCLUDEPATH += ./src INCLUDEPATH += ./src
HEADERS += src/common/config.h \ HEADERS += src/common/config.h \
src/GUI/graphicsscene.h \ src/GUI/graphicsscene.h \
src/GUI/mapaction.h \
src/GUI/popup.h \ src/GUI/popup.h \
src/common/garmin.h \
src/common/staticassert.h \ src/common/staticassert.h \
src/common/coordinates.h \ src/common/coordinates.h \
src/common/range.h \ src/common/range.h \
@ -88,6 +90,12 @@ HEADERS += src/common/config.h \
src/GUI/areaitem.h \ src/GUI/areaitem.h \
src/data/link.h \ src/data/link.h \
src/map/IMG/bitmapline.h \ src/map/IMG/bitmapline.h \
src/map/IMG/bitstream.h \
src/map/IMG/deltastream.h \
src/map/IMG/gmap.h \
src/map/IMG/huffmanstream.h \
src/map/IMG/huffmantable.h \
src/map/IMG/mapdata.h \
src/map/IMG/textpathitem.h \ src/map/IMG/textpathitem.h \
src/map/IMG/textpointitem.h \ src/map/IMG/textpointitem.h \
src/map/projection.h \ src/map/projection.h \
@ -178,7 +186,6 @@ HEADERS += src/common/config.h \
src/map/IMG/lblfile.h \ src/map/IMG/lblfile.h \
src/map/IMG/vectortile.h \ src/map/IMG/vectortile.h \
src/map/IMG/subdiv.h \ src/map/IMG/subdiv.h \
src/map/IMG/units.h \
src/map/IMG/style.h \ src/map/IMG/style.h \
src/map/IMG/netfile.h \ src/map/IMG/netfile.h \
src/GUI/limitedcombobox.h \ src/GUI/limitedcombobox.h \
@ -187,7 +194,9 @@ HEADERS += src/common/config.h \
src/map/IMG/label.h \ src/map/IMG/label.h \
src/data/csv.h \ src/data/csv.h \
src/data/cupparser.h \ src/data/cupparser.h \
src/data/gpiparser.h src/data/gpiparser.h \
src/data/address.h \
src/data/smlparser.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/GUI/popup.cpp \ src/GUI/popup.cpp \
src/common/coordinates.cpp \ src/common/coordinates.cpp \
@ -240,7 +249,14 @@ SOURCES += src/main.cpp \
src/GUI/gearratiographitem.cpp \ src/GUI/gearratiographitem.cpp \
src/GUI/mapview.cpp \ src/GUI/mapview.cpp \
src/GUI/areaitem.cpp \ src/GUI/areaitem.cpp \
src/data/waypoint.cpp \
src/map/IMG/bitmapline.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/mapdata.cpp \
src/map/IMG/textpathitem.cpp \ src/map/IMG/textpathitem.cpp \
src/map/IMG/textpointitem.cpp \ src/map/IMG/textpointitem.cpp \
src/map/maplist.cpp \ src/map/maplist.cpp \
@ -323,7 +339,8 @@ SOURCES += src/main.cpp \
src/data/csv.cpp \ src/data/csv.cpp \
src/data/cupparser.cpp \ src/data/cupparser.cpp \
src/GUI/graphicsscene.cpp \ src/GUI/graphicsscene.cpp \
src/data/gpiparser.cpp src/data/gpiparser.cpp \
src/data/smlparser.cpp
greaterThan(QT_MAJOR_VERSION, 4) { greaterThan(QT_MAJOR_VERSION, 4) {
HEADERS += src/data/geojsonparser.h HEADERS += src/data/geojsonparser.h
@ -347,7 +364,9 @@ TRANSLATIONS = lang/gpxsee_en.ts \
lang/gpxsee_da.ts \ lang/gpxsee_da.ts \
lang/gpxsee_tr.ts \ lang/gpxsee_tr.ts \
lang/gpxsee_es.ts \ lang/gpxsee_es.ts \
lang/gpxsee_pt_BR.ts lang/gpxsee_pt_BR.ts \
lang/gpxsee_uk.ts \
lang/gpxsee_hu.ts
macx { macx {
ICON = icons/gpxsee.icns ICON = icons/gpxsee.icns
@ -365,7 +384,9 @@ macx {
lang/gpxsee_da.qm \ lang/gpxsee_da.qm \
lang/gpxsee_tr.qm \ lang/gpxsee_tr.qm \
lang/gpxsee_es.qm \ lang/gpxsee_es.qm \
lang/gpxsee_pt_BR.qm lang/gpxsee_pt_BR.qm \
lang/gpxsee_uk.qm \
lang/gpxsee_hu.qm
csv.path = Contents/Resources csv.path = Contents/Resources
csv.files = pkg/csv csv.files = pkg/csv
maps.path = Contents/Resources maps.path = Contents/Resources
@ -384,7 +405,8 @@ macx {
icons/formats/slf.icns \ icons/formats/slf.icns \
icons/formats/json.icns \ icons/formats/json.icns \
icons/formats/cup.icns \ icons/formats/cup.icns \
icons/formats/gpi.icns icons/formats/gpi.icns \
icons/formats/sml.icns
QMAKE_BUNDLE_DATA += locale maps icons csv QMAKE_BUNDLE_DATA += locale maps icons csv
} }
@ -403,7 +425,8 @@ win32 {
icons/formats/slf.ico \ icons/formats/slf.ico \
icons/formats/json.ico \ icons/formats/json.ico \
icons/formats/cup.ico \ icons/formats/cup.ico \
icons/formats/gpi.ico icons/formats/gpi.ico \
icons/formats/sml.ico
DEFINES += _USE_MATH_DEFINES \ DEFINES += _USE_MATH_DEFINES \
NOGDI NOGDI
} }

BIN
icons/formats/sml.icns Normal file

Binary file not shown.

BIN
icons/formats/sml.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

View File

@ -12,3 +12,4 @@ loc:#556677
slf:#881199 slf:#881199
cup:#20a810 cup:#20a810
gpi:#fca314 gpi:#fca314
sml:#6434eb

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

1922
lang/gpxsee_hu.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

1924
lang/gpxsee_uk.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -243,6 +243,22 @@
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Viewer</string> <string>Viewer</string>
</dict> </dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>sml</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/sml+xml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>icons/sml.icns</string>
<key>CFBundleTypeName</key>
<string>Suunto Markup Language</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array> </array>
<key>UTImportedTypeDeclarations</key> <key>UTImportedTypeDeclarations</key>
@ -538,7 +554,28 @@
<string>gpi</string> <string>gpi</string>
</array> </array>
<key>public.mime-type</key> <key>public.mime-type</key>
<string>application/vnd.garmin.cup</string> <string>application/vnd.garmin.gpi</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.suunto.sml</string>
<key>UTTypeReferenceURL</key>
<string>https://www.suunto.com</string>
<key>UTTypeDescription</key>
<string>Suunto Markup Language</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>sml</string>
</array>
<key>public.mime-type</key>
<string>application/sml+xml</string>
</dict> </dict>
</dict> </dict>
</array> </array>

View File

@ -12,17 +12,18 @@
<p>Features:</p> <p>Features:</p>
<ul> <ul>
<li>Opens GPX, TCX, FIT, KML, IGC, NMEA, SLF, LOC, OziExplorer (PLT, <li>Opens GPX, TCX, FIT, KML, IGC, NMEA, SIGMA SLF, Suunto SML, LOC,
WPT, RTE), GeoJSON, SeeYou CUP, Garmin CSV and geotagged OziExplorer (PLT, WPT, RTE), GeoJSON, SeeYou CUP,
JPEG files.</li> Garmin GPI &amp; CSV and geotagged JPEG files.</li>
<li>User-definable online maps (OpenStreetMap/Google tiles, WMTS, <li>User-definable online maps (OpenStreetMap/Google tiles, WMTS,
WMS, TMS).</li> WMS, TMS, QuadTiles).</li>
<li>Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases, <li>Offline maps (MBTiles, OziExplorer maps, TrekBuddy maps/atlases,
TwoNav RMaps, Garmin IMG &amp; JNX, GeoTIFF images).</li> TwoNav RMaps, Garmin IMG &amp; JNX, GeoTIFF images).</li>
<li>Elevation, speed, heart rate, cadence, power and temperature <li>Elevation, speed, heart rate, cadence, power, temperature and
graphs.</li> gear ratio graphs.</li>
<li>Support for multiple tracks in one view.</li> <li>Support for multiple tracks in one view.</li>
<li>Support for POI files.</li> <li>Support for POI files.</li>
<li>Support for DEM files (SRTM HGT).</li>
<li>Print/export to PDF.</li> <li>Print/export to PDF.</li>
<li>Full-screen mode.</li> <li>Full-screen mode.</li>
<li>HiDPI/Retina displays &amp; maps support.</li> <li>HiDPI/Retina displays &amp; maps support.</li>
@ -69,5 +70,7 @@
<mimetype>application/slf+xml</mimetype> <mimetype>application/slf+xml</mimetype>
<mimetype>application/geo+json</mimetype> <mimetype>application/geo+json</mimetype>
<mimetype>application/vnd.naviter.seeyou.cup</mimetype> <mimetype>application/vnd.naviter.seeyou.cup</mimetype>
<mimetype>application/vnd.garmin.gpi</mimetype>
<mimetype>application/sml+xml</mimetype>
</mimetypes> </mimetypes>
</component> </component>

View File

@ -3,12 +3,16 @@ Name=GPXSee
Comment=GPS log file viewer and analyzer Comment=GPS log file viewer and analyzer
Comment[cz]=Prohlížeč a analyzátor GPS logů Comment[cz]=Prohlížeč a analyzátor GPS logů
Comment[fi]=Ohjelma GPS-lokien katseluun ja analysointiin Comment[fi]=Ohjelma GPS-lokien katseluun ja analysointiin
Comment[fr]=Visualisation et analyse de fichier GPS
Comment[nb]=GPS-loggfilleser og analysator
Comment[pl]=Przeglądarka i analizator plików dziennika GPS Comment[pl]=Przeglądarka i analizator plików dziennika GPS
Comment[ru]=Программа для просмотра и анализа GPS логов Comment[ru]=Программа для просмотра и анализа GPS логов
Comment[sv]=GPS-loggfilsläsare och analysator Comment[sv]=GPS-loggfilsläsare och analysator
Comment[tr]=GPS günlük dosyası görüntüleyici ve analizcisi
Comment[uk]=Переглядач та аналізатор GPS логів
Exec=gpxsee %F Exec=gpxsee %F
Icon=gpxsee Icon=gpxsee
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Graphics;Viewer;Education;Geography;Maps;Sports;Qt; Categories=Graphics;Viewer;Education;Geography;Maps;Sports;Qt;
MimeType=application/gpx+xml;application/tcx+xml;application/vnd.ant.fit;application/vnd.google-earth.kml+xml;application/vnd.fai.igc;application/vnd.nmea.nmea;application/vnd.oziexplorer.plt;application/vnd.oziexplorer.rte;application/vnd.oziexplorer.wpt;application/loc+xml;application/slf+xml;application/geo+json;application/vnd.naviter.seeyou.cup; MimeType=application/gpx+xml;application/tcx+xml;application/vnd.ant.fit;application/vnd.google-earth.kml+xml;application/vnd.fai.igc;application/vnd.nmea.nmea;application/vnd.oziexplorer.plt;application/vnd.oziexplorer.rte;application/vnd.oziexplorer.wpt;application/loc+xml;application/slf+xml;application/geo+json;application/vnd.naviter.seeyou.cup;application/vnd.garmin.gpi;application/sml+xml;

View File

@ -7,7 +7,7 @@
; The name of the installer ; The name of the installer
Name "GPXSee" Name "GPXSee"
; Program version ; Program version
!define VERSION "7.17" !define VERSION "7.26"
; The file to write ; The file to write
OutFile "GPXSee-${VERSION}.exe" OutFile "GPXSee-${VERSION}.exe"
@ -106,20 +106,21 @@ Section "GPXSee" SEC_APP
; Associate file formats ; Associate file formats
DetailPrint "Associating file types..." DetailPrint "Associating file types..."
!insertmacro FILE_ASSOCIATION_ADD "gpx" "GPS Exchange Format" 7 !insertmacro FILE_ASSOCIATION_ADD "gpx" "GPS Exchange Format" 8
!insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 8 !insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 9
!insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 9 !insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 10
!insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 10 !insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 11
!insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 11 !insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 12
!insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 12 !insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 13
!insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track Point File" 13 !insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track Point File" 14
!insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 14 !insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 15
!insertmacro FILE_ASSOCIATION_ADD "wpt" "OziExplorer Waypoint File" 1 !insertmacro FILE_ASSOCIATION_ADD "wpt" "OziExplorer Waypoint File" 1
!insertmacro FILE_ASSOCIATION_ADD "loc" "Geocaching.com Waypoint File" 2 !insertmacro FILE_ASSOCIATION_ADD "loc" "Geocaching.com Waypoint File" 2
!insertmacro FILE_ASSOCIATION_ADD "slf" "Sigma Log File" 3 !insertmacro FILE_ASSOCIATION_ADD "slf" "Sigma Log File" 3
!insertmacro FILE_ASSOCIATION_ADD "geojson" "GeoJSON" 4 !insertmacro FILE_ASSOCIATION_ADD "geojson" "GeoJSON" 4
!insertmacro FILE_ASSOCIATION_ADD "cup" "SeeYou CUP File" 5 !insertmacro FILE_ASSOCIATION_ADD "cup" "SeeYou CUP File" 5
!insertmacro FILE_ASSOCIATION_ADD "gpi" "Garmin POI File" 6 !insertmacro FILE_ASSOCIATION_ADD "gpi" "Garmin POI File" 6
!insertmacro FILE_ASSOCIATION_ADD "sml" "Suunto Markup Language" 7
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd SectionEnd
@ -147,30 +148,17 @@ Section "MSVC runtime" SEC_MSVC
SectionIn RO SectionIn RO
DetailPrint "Checking whether Visual C++ 2015 Redistributable is already installed..."
${If} ${RunningX64}
ReadRegDword $R0 HKLM "SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Installed"
${Else}
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Installed"
${EndIf}
StrCmp $R0 "1" 0 +3
DetailPrint "Visual C++ 2015 Redistributable is already installed, skipping install."
Goto done
DetailPrint "Installing Visual C++ 2015 Redistributable..."
SetOutPath $TEMP SetOutPath $TEMP
File "vcredist_x86.exe" File "vc_redist.x86.exe"
ExecWait '"$TEMP\vcredist_x86.exe" /install /quiet /norestart' ExecWait '"$TEMP\vc_redist.x86.exe" /install /quiet /norestart'
SetOutPath $INSTDIR SetOutPath $INSTDIR
done:
SectionEnd SectionEnd
Section "OpenSSL" SEC_OPENSSL Section "OpenSSL" SEC_OPENSSL
File "libeay32.dll" File "libcrypto-1_1.dll"
File "ssleay32.dll" File "libssl-1_1.dll"
SectionEnd SectionEnd
@ -188,6 +176,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
!insertmacro LOCALIZATION "Finnish" "fi" !insertmacro LOCALIZATION "Finnish" "fi"
!insertmacro LOCALIZATION "French" "fr" !insertmacro LOCALIZATION "French" "fr"
!insertmacro LOCALIZATION "German" "de" !insertmacro LOCALIZATION "German" "de"
!insertmacro LOCALIZATION "Hungarian" "hu"
!insertmacro LOCALIZATION "Norwegian" "nb" !insertmacro LOCALIZATION "Norwegian" "nb"
!insertmacro LOCALIZATION "Polish" "pl" !insertmacro LOCALIZATION "Polish" "pl"
!insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR" !insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR"
@ -195,6 +184,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
!insertmacro LOCALIZATION "Spanish" "es" !insertmacro LOCALIZATION "Spanish" "es"
!insertmacro LOCALIZATION "Swedish" "sv" !insertmacro LOCALIZATION "Swedish" "sv"
!insertmacro LOCALIZATION "Turkish" "tr" !insertmacro LOCALIZATION "Turkish" "tr"
!insertmacro LOCALIZATION "Ukrainian" "uk"
SectionGroupEnd SectionGroupEnd
;-------------------------------- ;--------------------------------
@ -231,6 +221,7 @@ Section "Uninstall"
!insertmacro FILE_ASSOCIATION_REMOVE "geojson" !insertmacro FILE_ASSOCIATION_REMOVE "geojson"
!insertmacro FILE_ASSOCIATION_REMOVE "cup" !insertmacro FILE_ASSOCIATION_REMOVE "cup"
!insertmacro FILE_ASSOCIATION_REMOVE "gpi" !insertmacro FILE_ASSOCIATION_REMOVE "gpi"
!insertmacro FILE_ASSOCIATION_REMOVE "sml"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd SectionEnd
@ -243,7 +234,7 @@ SectionEnd
LangString DESC_QT ${LANG_ENGLISH} \ LangString DESC_QT ${LANG_ENGLISH} \
"QT cross-platform application framework." "QT cross-platform application framework."
LangString DESC_MSVC ${LANG_ENGLISH} \ LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2015 runtime components. If already installed, will be skipped." "Microsoft Visual C++ 2017 runtime. If already installed, will be skipped."
LangString DESC_OPENSSL ${LANG_ENGLISH} \ LangString DESC_OPENSSL ${LANG_ENGLISH} \
"OpenSSL library. Required for HTTPS to work." "OpenSSL library. Required for HTTPS to work."
LangString DESC_ANGLE ${LANG_ENGLISH} \ LangString DESC_ANGLE ${LANG_ENGLISH} \

View File

@ -91,4 +91,18 @@
<generic-icon name="text-plain"/> <generic-icon name="text-plain"/>
<glob pattern="*.cup"/> <glob pattern="*.cup"/>
</mime-type> </mime-type>
<mime-type type="application/vnd.garmin.gpi">
<comment>Garmin POI File</comment>
<sub-class-of type="application/octet-stream"/>
<generic-icon name="application/octet-stream"/>
<glob pattern="*.gpi"/>
</mime-type>
<mime-type type="application/sml+xml">
<comment>Suunto Markup Language</comment>
<sub-class-of type="application/xml"/>
<generic-icon name="application-xml"/>
<glob pattern="*.sml"/>
</mime-type>
</mime-info> </mime-info>

View File

@ -7,7 +7,7 @@
; The name of the installer ; The name of the installer
Name "GPXSee" Name "GPXSee"
; Program version ; Program version
!define VERSION "7.17" !define VERSION "7.26"
; The file to write ; The file to write
OutFile "GPXSee-${VERSION}_x64.exe" OutFile "GPXSee-${VERSION}_x64.exe"
@ -113,20 +113,21 @@ Section "GPXSee" SEC_APP
; Associate file formats ; Associate file formats
DetailPrint "Associating file types..." DetailPrint "Associating file types..."
!insertmacro FILE_ASSOCIATION_ADD "gpx" "GPS Exchange Format" 7 !insertmacro FILE_ASSOCIATION_ADD "gpx" "GPS Exchange Format" 8
!insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 8 !insertmacro FILE_ASSOCIATION_ADD "tcx" "Training Center XML" 9
!insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 9 !insertmacro FILE_ASSOCIATION_ADD "kml" "Keyhole Markup Language" 10
!insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 10 !insertmacro FILE_ASSOCIATION_ADD "fit" "Flexible and Interoperable Data Transfer" 11
!insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 11 !insertmacro FILE_ASSOCIATION_ADD "igc" "Flight Recorder Data Format" 12
!insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 12 !insertmacro FILE_ASSOCIATION_ADD "nmea" "NMEA 0183 Data" 13
!insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track Point File" 13 !insertmacro FILE_ASSOCIATION_ADD "plt" "OziExplorer Track Point File" 14
!insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 14 !insertmacro FILE_ASSOCIATION_ADD "rte" "OziExplorer Route File" 15
!insertmacro FILE_ASSOCIATION_ADD "wpt" "OziExplorer Waypoint File" 1 !insertmacro FILE_ASSOCIATION_ADD "wpt" "OziExplorer Waypoint File" 1
!insertmacro FILE_ASSOCIATION_ADD "loc" "Geocaching.com Waypoint File" 2 !insertmacro FILE_ASSOCIATION_ADD "loc" "Geocaching.com Waypoint File" 2
!insertmacro FILE_ASSOCIATION_ADD "slf" "Sigma Log File" 3 !insertmacro FILE_ASSOCIATION_ADD "slf" "Sigma Log File" 3
!insertmacro FILE_ASSOCIATION_ADD "geojson" "GeoJSON" 4 !insertmacro FILE_ASSOCIATION_ADD "geojson" "GeoJSON" 4
!insertmacro FILE_ASSOCIATION_ADD "cup" "SeeYou CUP File" 5 !insertmacro FILE_ASSOCIATION_ADD "cup" "SeeYou CUP File" 5
!insertmacro FILE_ASSOCIATION_ADD "gpi" "Garmin POI File" 6 !insertmacro FILE_ASSOCIATION_ADD "gpi" "Garmin POI File" 6
!insertmacro FILE_ASSOCIATION_ADD "sml" "Suunto Markup Language" 7
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd SectionEnd
@ -154,25 +155,17 @@ Section "MSVC runtime" SEC_MSVC
SectionIn RO SectionIn RO
DetailPrint "Checking whether Visual C++ 2015 Redistributable is already installed..."
ReadRegDword $R0 HKLM "SOFTWARE\Wow6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed"
StrCmp $R0 "1" 0 +3
DetailPrint "Visual C++ 2015 Redistributable is already installed, skipping install."
Goto done
DetailPrint "Installing Visual C++ 2015 Redistributable..."
SetOutPath $TEMP SetOutPath $TEMP
File "vcredist_x64.exe" File "vc_redist.x64.exe"
ExecWait '"$TEMP\vcredist_x64.exe" /install /quiet /norestart' ExecWait '"$TEMP\vc_redist.x64.exe" /install /quiet /norestart'
SetOutPath $INSTDIR SetOutPath $INSTDIR
done:
SectionEnd SectionEnd
Section "OpenSSL" SEC_OPENSSL Section "OpenSSL" SEC_OPENSSL
File "libeay32.dll" File "libcrypto-1_1-x64.dll"
File "ssleay32.dll" File "libssl-1_1-x64.dll"
SectionEnd SectionEnd
@ -190,6 +183,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
!insertmacro LOCALIZATION "Finnish" "fi" !insertmacro LOCALIZATION "Finnish" "fi"
!insertmacro LOCALIZATION "French" "fr" !insertmacro LOCALIZATION "French" "fr"
!insertmacro LOCALIZATION "German" "de" !insertmacro LOCALIZATION "German" "de"
!insertmacro LOCALIZATION "Hungarian" "hu"
!insertmacro LOCALIZATION "Norwegian" "nb" !insertmacro LOCALIZATION "Norwegian" "nb"
!insertmacro LOCALIZATION "Polish" "pl" !insertmacro LOCALIZATION "Polish" "pl"
!insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR" !insertmacro LOCALIZATION "Portuguese (Brazil)" "pt_BR"
@ -197,6 +191,7 @@ SectionGroup "Localization" SEC_LOCALIZATION
!insertmacro LOCALIZATION "Spanish" "es" !insertmacro LOCALIZATION "Spanish" "es"
!insertmacro LOCALIZATION "Swedish" "sv" !insertmacro LOCALIZATION "Swedish" "sv"
!insertmacro LOCALIZATION "Turkish" "tr" !insertmacro LOCALIZATION "Turkish" "tr"
!insertmacro LOCALIZATION "Ukrainian" "uk"
SectionGroupEnd SectionGroupEnd
;-------------------------------- ;--------------------------------
@ -234,6 +229,7 @@ Section "Uninstall"
!insertmacro FILE_ASSOCIATION_REMOVE "geojson" !insertmacro FILE_ASSOCIATION_REMOVE "geojson"
!insertmacro FILE_ASSOCIATION_REMOVE "cup" !insertmacro FILE_ASSOCIATION_REMOVE "cup"
!insertmacro FILE_ASSOCIATION_REMOVE "gpi" !insertmacro FILE_ASSOCIATION_REMOVE "gpi"
!insertmacro FILE_ASSOCIATION_REMOVE "sml"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)' System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd SectionEnd
@ -246,7 +242,7 @@ SectionEnd
LangString DESC_QT ${LANG_ENGLISH} \ LangString DESC_QT ${LANG_ENGLISH} \
"QT cross-platform application framework." "QT cross-platform application framework."
LangString DESC_MSVC ${LANG_ENGLISH} \ LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2015 runtime components. If already installed, will be skipped." "Microsoft Visual C++ 2017 runtime. If already installed, will be skipped."
LangString DESC_OPENSSL ${LANG_ENGLISH} \ LangString DESC_OPENSSL ${LANG_ENGLISH} \
"OpenSSL library. Required for HTTPS to work." "OpenSSL library. Required for HTTPS to work."
LangString DESC_ANGLE ${LANG_ENGLISH} \ LangString DESC_ANGLE ${LANG_ENGLISH} \

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3"> <map xmlns="http://www.gpxsee.org/map/1.4">
<name>4UMaps</name> <name>4UMaps</name>
<url>https://4umaps.com/$z/$x/$y.png</url> <url>https://tileserver.4umaps.com/$z/$x/$y.png</url>
<zoom min="2" max="15"/> <zoom min="2" max="15"/>
<bounds bottom="-65"/> <bounds bottom="-65"/>
<copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © 4UMaps.eu</copyright> <copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © 4UMaps.eu</copyright>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3" type="WMTS"> <map xmlns="http://www.gpxsee.org/map/1.4" type="WMTS">
<name>Antarctica</name> <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://gis.ngdc.noaa.gov/arcgis/rest/services/antarctic/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> <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>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3"> <map xmlns="http://www.gpxsee.org/map/1.4">
<name>Open Street Map</name> <name>Open Street Map</name>
<url>http://tile.openstreetmap.org/$z/$x/$y.png</url> <url>https://tile.openstreetmap.org/$z/$x/$y.png</url>
<copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © OpenStreetMap (CC-BY-SA)</copyright> <copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © OpenStreetMap (CC-BY-SA)</copyright>
</map> </map>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3"> <map xmlns="http://www.gpxsee.org/map/1.4">
<name>Open Topo Map</name> <name>Open Topo Map</name>
<url>https://a.tile.opentopomap.org/$z/$x/$y.png</url> <url>https://a.tile.opentopomap.org/$z/$x/$y.png</url>
<zoom max="17"/> <zoom max="17"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3"> <map xmlns="http://www.gpxsee.org/map/1.4">
<name>USGS Imagery</name> <name>USGS Imagery</name>
<url>https://basemap.nationalmap.gov/ArcGIS/rest/services/USGSImageryOnly/MapServer/tile/$z/$y/$x</url> <url>https://basemap.nationalmap.gov/ArcGIS/rest/services/USGSImageryOnly/MapServer/tile/$z/$y/$x</url>
<zoom min="2" max="15"/> <zoom min="2" max="15"/>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3"> <map xmlns="http://www.gpxsee.org/map/1.4">
<name>USGS Topo</name> <name>USGS Topo</name>
<url>https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/$z/$y/$x</url> <url>https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/$z/$y/$x</url>
<zoom min="2" max="15"/> <zoom min="2" max="15"/>

View File

@ -8,7 +8,7 @@
#include "areaitem.h" #include "areaitem.h"
ToolTip AreaItem::toolTip() const QString AreaItem::info() const
{ {
ToolTip tt; ToolTip tt;
@ -18,11 +18,11 @@ ToolTip AreaItem::toolTip() const
tt.insert(qApp->translate("PolygonItem", "Description"), tt.insert(qApp->translate("PolygonItem", "Description"),
_area.description()); _area.description());
return tt; return tt.toString();
} }
AreaItem::AreaItem(const Area &area, Map *map, QGraphicsItem *parent) AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent)
: QGraphicsItem(parent), _area(area) : GraphicsItem(parent), _area(area)
{ {
_map = map; _map = map;
_digitalZoom = 0; _digitalZoom = 0;
@ -173,6 +173,6 @@ void AreaItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
void AreaItem::mousePressEvent(QGraphicsSceneMouseEvent *event) void AreaItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ {
Popup::show(event->screenPos(), toolTip().toString(), event->widget()); Popup::show(event->screenPos(), info(), event->widget());
QGraphicsItem::mousePressEvent(event); QGraphicsItem::mousePressEvent(event);
} }

View File

@ -1,16 +1,16 @@
#ifndef AREAITEM_H #ifndef AREAITEM_H
#define AREAITEM_H #define AREAITEM_H
#include <QGraphicsItem>
#include "data/area.h" #include "data/area.h"
#include "graphicsscene.h"
#include "tooltip.h" #include "tooltip.h"
class Map; class Map;
class AreaItem : public QGraphicsItem class AreaItem : public GraphicsItem
{ {
public: public:
AreaItem(const Area &area, Map *map, QGraphicsItem *parent = 0); AreaItem(const Area &area, Map *map, GraphicsItem *parent = 0);
QPainterPath shape() const {return _painterPath;} QPainterPath shape() const {return _painterPath;}
QRectF boundingRect() const {return _painterPath.boundingRect();} QRectF boundingRect() const {return _painterPath.boundingRect();}
@ -27,6 +27,8 @@ public:
void setStyle(Qt::PenStyle style); void setStyle(Qt::PenStyle style);
void setDigitalZoom(int zoom); void setDigitalZoom(int zoom);
virtual QString info() const;
protected: protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);

View File

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

View File

@ -21,7 +21,7 @@ public:
void showRoutes(bool show); void showRoutes(bool show);
private: private:
enum Type {Track, Route}; enum PathType {TrackPath, RoutePath};
qreal max() const; qreal max() const;
qreal min() const; qreal min() const;
@ -31,7 +31,8 @@ private:
void setYUnits(Units units); void setYUnits(Units units);
void setInfo(); 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); void showItems(const QList<ElevationGraphItem *> &list, bool show);
qreal _trackAscent, _trackDescent; qreal _trackAscent, _trackDescent;

View File

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

View File

@ -8,8 +8,10 @@ class ElevationGraphItem : public GraphItem
Q_OBJECT Q_OBJECT
public: public:
enum DataType {GPS, DEM};
ElevationGraphItem(const Graph &graph, GraphType type, int width, 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 ascent() const {return _ascent;}
qreal descent() const {return _descent;} qreal descent() const {return _descent;}

View File

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

View File

@ -46,3 +46,9 @@ void GraphicsScene::helpEvent(QGraphicsSceneHelpEvent *event)
/* No need to process QGraphicsScene::helpEvent() */ /* No need to process QGraphicsScene::helpEvent() */
} }
void GraphicsScene::clear()
{
Popup::clear();
QGraphicsScene::clear();
}

View File

@ -18,6 +18,9 @@ class GraphicsScene : public QGraphicsScene
public: public:
GraphicsScene(QObject *parent = 0) : QGraphicsScene(parent) {} GraphicsScene(QObject *parent = 0) : QGraphicsScene(parent) {}
public slots:
void clear();
protected: protected:
void helpEvent(QGraphicsSceneHelpEvent *event); void helpEvent(QGraphicsSceneHelpEvent *event);

View File

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

View File

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

View File

@ -274,8 +274,8 @@ void GraphView::redraw(const QSizeF &size)
rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale); rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
+ _yOffset); + _yOffset);
if (ry.size() < _minYRange) if (ry.size() < _minYRange * _yScale)
ry.resize(_minYRange); ry.resize(_minYRange * _yScale);
_xAxis->setRange(rx); _xAxis->setRange(rx);
_yAxis->setRange(ry); _yAxis->setRange(ry);
@ -419,14 +419,16 @@ void GraphView::updateSliderInfo()
{ {
QLocale l(QLocale::system()); QLocale l(QLocale::system());
qreal r = 0, y = 0; qreal r = 0, y = 0;
GraphItem *cardinal = (_graphs.count() == 1 || (_graphs.count() == 2
&& _graphs.first()->secondaryGraph())) ? _graphs.first() : 0;
if (_graphs.count() == 1) { if (cardinal) {
QRectF br(_graphs.first()->bounds()); QRectF br(_bounds);
if (br.height() < _minYRange) if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0, br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2); _minYRange/2 - br.height()/2);
y = _graphs.first()->yAtX(_sliderPos); y = cardinal->yAtX(_sliderPos);
r = (y - br.bottom()) / br.height(); r = (y - br.bottom()) / br.height();
} }
@ -436,11 +438,17 @@ void GraphView::updateSliderInfo()
_sliderInfo->setSide(s); _sliderInfo->setSide(s);
_sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r)); _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) bounds().width() > 3600) : l.toString(_sliderPos * _xScale, 'f', 1)
+ UNIT_SPACE + _xUnits, (_graphs.count() > 1) ? QString() + UNIT_SPACE + _xUnits);
: l.toString(-y * _yScale + _yOffset, 'f', _precision) + UNIT_SPACE QString yText((!cardinal) ? QString() : l.toString(-y * _yScale + _yOffset,
+ _yUnits); '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) void GraphView::emitSliderPositionChanged(const QPointF &pos)

View File

@ -51,6 +51,7 @@
#include "graphtab.h" #include "graphtab.h"
#include "graphitem.h" #include "graphitem.h"
#include "pathitem.h" #include "pathitem.h"
#include "mapaction.h"
#include "gui.h" #include "gui.h"
@ -58,7 +59,6 @@
GUI::GUI() GUI::GUI()
{ {
loadMaps();
loadPOIs(); loadPOIs();
createMapView(); createMapView();
@ -106,24 +106,13 @@ GUI::GUI()
updateStatusBarInfo(); updateStatusBarInfo();
} }
void GUI::loadMaps()
{
_ml = new MapList(this);
QString mapDir(ProgramPaths::mapDir());
if (!mapDir.isNull() && !_ml->loadDir(mapDir))
qWarning("%s", qPrintable(_ml->errorString()));
_map = new EmptyMap(this);
}
void GUI::loadPOIs() void GUI::loadPOIs()
{ {
_poi = new POI(this); _poi = new POI(this);
QString poiDir(ProgramPaths::poiDir());
if (!poiDir.isNull() && !_poi->loadDir(poiDir)) QString poiDir(ProgramPaths::poiDir());
qWarning("%s", qPrintable(_poi->errorString())); if (!poiDir.isNull())
_poi->loadDir(poiDir);
} }
void GUI::createBrowser() void GUI::createBrowser()
@ -134,40 +123,56 @@ void GUI::createBrowser()
void GUI::createMapActions() void GUI::createMapActions()
{ {
_mapsSignalMapper = new QSignalMapper(this);
_mapsActionGroup = new QActionGroup(this); _mapsActionGroup = new QActionGroup(this);
_mapsActionGroup->setExclusive(true); _mapsActionGroup->setExclusive(true);
for (int i = 0; i < _ml->maps().count(); i++) QString mapDir(ProgramPaths::mapDir());
createMapAction(_ml->maps().at(i)); if (mapDir.isNull())
return;
connect(_mapsSignalMapper, SIGNAL(mapped(int)), this, QString unused;
SLOT(mapChanged(int))); 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->setMenuRole(QAction::NoRole);
a->setCheckable(true); a->setCheckable(true);
a->setActionGroup(_mapsActionGroup); a->setActionGroup(_mapsActionGroup);
connect(a, SIGNAL(triggered()), this, SLOT(mapChanged()));
_mapActions.append(a);
_mapsSignalMapper->setMapping(a, _mapActions.size() - 1);
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
return a; 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() void GUI::createPOIFilesActions()
{ {
_poiFilesSignalMapper = new QSignalMapper(this); _poiFilesSignalMapper = new QSignalMapper(this);
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
for (int i = 0; i < _poi->files().count(); i++) for (int i = 0; i < _poi->files().count(); i++)
createPOIFileAction(_poi->files().at(i)); createPOIFileAction(_poi->files().at(i));
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
} }
QAction *GUI::createPOIFileAction(const QString &fileName) QAction *GUI::createPOIFileAction(const QString &fileName)
@ -281,8 +286,10 @@ void GUI::createActions()
createPOIFilesActions(); createPOIFilesActions();
// Map actions // Map actions
createMapActions();
_showMapAction = new QAction(QIcon(SHOW_MAP_ICON), tr("Show map"), _showMapAction = new QAction(QIcon(SHOW_MAP_ICON), tr("Show map"),
this); this);
_showMapAction->setEnabled(false);
_showMapAction->setMenuRole(QAction::NoRole); _showMapAction->setMenuRole(QAction::NoRole);
_showMapAction->setCheckable(true); _showMapAction->setCheckable(true);
_showMapAction->setShortcut(SHOW_MAP_SHORTCUT); _showMapAction->setShortcut(SHOW_MAP_SHORTCUT);
@ -294,10 +301,10 @@ void GUI::createActions()
_loadMapAction->setMenuRole(QAction::NoRole); _loadMapAction->setMenuRole(QAction::NoRole);
connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap())); connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap()));
_clearMapCacheAction = new QAction(tr("Clear tile cache"), this); _clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
_clearMapCacheAction->setEnabled(false);
_clearMapCacheAction->setMenuRole(QAction::NoRole); _clearMapCacheAction->setMenuRole(QAction::NoRole);
connect(_clearMapCacheAction, SIGNAL(triggered()), _mapView, connect(_clearMapCacheAction, SIGNAL(triggered()), _mapView,
SLOT(clearMapCache())); SLOT(clearMapCache()));
createMapActions();
_nextMapAction = new QAction(tr("Next map"), this); _nextMapAction = new QAction(tr("Next map"), this);
_nextMapAction->setMenuRole(QAction::NoRole); _nextMapAction->setMenuRole(QAction::NoRole);
_nextMapAction->setShortcut(NEXT_MAP_SHORTCUT); _nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
@ -308,10 +315,6 @@ void GUI::createActions()
_prevMapAction->setShortcut(PREV_MAP_SHORTCUT); _prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap())); connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap()));
addAction(_prevMapAction); addAction(_prevMapAction);
if (_ml->maps().isEmpty()) {
_showMapAction->setEnabled(false);
_clearMapCacheAction->setEnabled(false);
}
_showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this); _showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this);
_showCoordinatesAction->setMenuRole(QAction::NoRole); _showCoordinatesAction->setMenuRole(QAction::NoRole);
_showCoordinatesAction->setCheckable(true); _showCoordinatesAction->setCheckable(true);
@ -451,11 +454,11 @@ void GUI::createActions()
_degreesMinutesAction->setActionGroup(ag); _degreesMinutesAction->setActionGroup(ag);
connect(_degreesMinutesAction, SIGNAL(triggered()), this, connect(_degreesMinutesAction, SIGNAL(triggered()), this,
SLOT(setDegreesMinutes())); SLOT(setDegreesMinutes()));
_DMSAction = new QAction(tr("Degrees, minutes, seconds (DMS)"), this); _dmsAction = new QAction(tr("Degrees, minutes, seconds (DMS)"), this);
_DMSAction->setMenuRole(QAction::NoRole); _dmsAction->setMenuRole(QAction::NoRole);
_DMSAction->setCheckable(true); _dmsAction->setCheckable(true);
_DMSAction->setActionGroup(ag); _dmsAction->setActionGroup(ag);
connect(_DMSAction, SIGNAL(triggered()), this, SLOT(setDMS())); connect(_dmsAction, SIGNAL(triggered()), this, SLOT(setDMS()));
_fullscreenAction = new QAction(QIcon(FULLSCREEN_ICON), _fullscreenAction = new QAction(QIcon(FULLSCREEN_ICON),
tr("Fullscreen mode"), this); tr("Fullscreen mode"), this);
_fullscreenAction->setMenuRole(QAction::NoRole); _fullscreenAction->setMenuRole(QAction::NoRole);
@ -506,7 +509,7 @@ void GUI::createMenus()
#endif // Q_OS_MAC #endif // Q_OS_MAC
_mapMenu = menuBar()->addMenu(tr("&Map")); _mapMenu = menuBar()->addMenu(tr("&Map"));
_mapMenu->addActions(_mapActions); _mapMenu->addActions(_mapsActionGroup->actions());
_mapsEnd = _mapMenu->addSeparator(); _mapsEnd = _mapMenu->addSeparator();
_mapMenu->addAction(_loadMapAction); _mapMenu->addAction(_loadMapAction);
_mapMenu->addAction(_clearMapCacheAction); _mapMenu->addAction(_clearMapCacheAction);
@ -559,7 +562,7 @@ void GUI::createMenus()
QMenu *coordinatesMenu = settingsMenu->addMenu(tr("Coordinates format")); QMenu *coordinatesMenu = settingsMenu->addMenu(tr("Coordinates format"));
coordinatesMenu->addAction(_decimalDegreesAction); coordinatesMenu->addAction(_decimalDegreesAction);
coordinatesMenu->addAction(_degreesMinutesAction); coordinatesMenu->addAction(_degreesMinutesAction);
coordinatesMenu->addAction(_DMSAction); coordinatesMenu->addAction(_dmsAction);
settingsMenu->addSeparator(); settingsMenu->addSeparator();
settingsMenu->addAction(_showToolbarsAction); settingsMenu->addAction(_showToolbarsAction);
settingsMenu->addAction(_fullscreenAction); settingsMenu->addAction(_fullscreenAction);
@ -608,6 +611,7 @@ void GUI::createToolBars()
void GUI::createMapView() void GUI::createMapView()
{ {
_map = new EmptyMap(this);
_mapView = new MapView(_map, _poi, this); _mapView = new MapView(_map, _poi, this);
_mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, _mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Expanding)); QSizePolicy::Expanding));
@ -905,9 +909,14 @@ void GUI::openOptions()
Track::action(options.option); \ Track::action(options.option); \
reload = true; \ reload = true; \
} }
#define SET_DATA_OPTION(option, action) \ #define SET_ROUTE_OPTION(option, action) \
if (options.option != _options.option) { \ 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; \ reload = true; \
} }
@ -949,16 +958,22 @@ void GUI::openOptions()
SET_TRACK_OPTION(cadenceFilter, setCadenceFilter); SET_TRACK_OPTION(cadenceFilter, setCadenceFilter);
SET_TRACK_OPTION(powerFilter, setPowerFilter); SET_TRACK_OPTION(powerFilter, setPowerFilter);
SET_TRACK_OPTION(outlierEliminate, setOutlierElimination); SET_TRACK_OPTION(outlierEliminate, setOutlierElimination);
SET_TRACK_OPTION(automaticPause, setAutomaticPause);
SET_TRACK_OPTION(pauseSpeed, setPauseSpeed); SET_TRACK_OPTION(pauseSpeed, setPauseSpeed);
SET_TRACK_OPTION(pauseInterval, setPauseInterval); SET_TRACK_OPTION(pauseInterval, setPauseInterval);
SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed); 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) if (options.poiRadius != _options.poiRadius)
_poi->setRadius(options.poiRadius); _poi->setRadius(options.poiRadius);
if (options.poiUseDEM != _options.poiUseDEM)
_poi->useDEM(options.poiUseDEM);
if (options.pixmapCache != _options.pixmapCache) if (options.pixmapCache != _options.pixmapCache)
QPixmapCache::setCacheLimit(options.pixmapCache * 1024); QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
@ -1321,22 +1336,49 @@ void GUI::loadMap()
bool GUI::loadMap(const QString &fileName) bool GUI::loadMap(const QString &fileName)
{ {
// On OS X fileName may be a directory!
if (fileName.isEmpty()) if (fileName.isEmpty())
return false; return false;
if (_ml->loadFile(fileName)) { QString error;
QAction *a = createMapAction(_ml->maps().last()); 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;
}
for (int i = 0; i < maps.size(); i++) {
Map *map = maps.at(i);
MapAction *a = createMapAction(map);
_mapMenu->insertAction(_mapsEnd, a); _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); _showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true); _clearMapCacheAction->setEnabled(true);
a->trigger();
return true;
} else { } else {
QString error = tr("Error loading map:") + "\n\n" 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); QMessageBox::critical(this, APP_NAME, error);
action->deleteLater();
return false;
} }
} }
@ -1378,31 +1420,42 @@ void GUI::updateWindowTitle()
setWindowTitle(APP_NAME); 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); _mapView->setMap(_map);
} }
void GUI::nextMap() void GUI::nextMap()
{ {
if (_ml->maps().count() < 2) QAction *checked = _mapsActionGroup->checkedAction();
if (!checked)
return; return;
int next = (_ml->maps().indexOf(_map) + 1) % _ml->maps().count(); QList<QAction*> maps = _mapsActionGroup->actions();
_mapActions.at(next)->setChecked(true); for (int i = 1; i < maps.size(); i++) {
mapChanged(next); int next = (maps.indexOf(checked) + i) % maps.count();
if (maps.at(next)->isEnabled()) {
maps.at(next)->trigger();
break;
}
}
} }
void GUI::prevMap() void GUI::prevMap()
{ {
if (_ml->maps().count() < 2) QAction *checked = _mapsActionGroup->checkedAction();
if (!checked)
return; return;
int prev = (_ml->maps().indexOf(_map) + _ml->maps().count() - 1) QList<QAction*> maps = _mapsActionGroup->actions();
% _ml->maps().count(); for (int i = 1; i < maps.size(); i++) {
_mapActions.at(prev)->setChecked(true); int prev = (maps.indexOf(checked) + maps.count() - i) % maps.count();
mapChanged(prev); if (maps.at(prev)->isEnabled()) {
maps.at(prev)->trigger();
break;
}
}
} }
void GUI::poiFileChecked(int index) void GUI::poiFileChecked(int index)
@ -1654,7 +1707,7 @@ void GUI::writeSettings()
: _nauticalUnitsAction->isChecked() ? Nautical : Metric; : _nauticalUnitsAction->isChecked() ? Nautical : Metric;
if (units != UNITS_DEFAULT) if (units != UNITS_DEFAULT)
settings.setValue(UNITS_SETTING, units); settings.setValue(UNITS_SETTING, units);
CoordinatesFormat format = _DMSAction->isChecked() ? DMS CoordinatesFormat format = _dmsAction->isChecked() ? DMS
: _degreesMinutesAction->isChecked() ? DegreesMinutes : DecimalDegrees; : _degreesMinutesAction->isChecked() ? DegreesMinutes : DecimalDegrees;
if (format != COORDINATES_DEFAULT) if (format != COORDINATES_DEFAULT)
settings.setValue(COORDINATES_SETTING, format); settings.setValue(COORDINATES_SETTING, format);
@ -1798,6 +1851,8 @@ void GUI::writeSettings()
settings.setValue(POWER_FILTER_SETTING, _options.powerFilter); settings.setValue(POWER_FILTER_SETTING, _options.powerFilter);
if (_options.outlierEliminate != OUTLIER_ELIMINATE_DEFAULT) if (_options.outlierEliminate != OUTLIER_ELIMINATE_DEFAULT)
settings.setValue(OUTLIER_ELIMINATE_SETTING, _options.outlierEliminate); settings.setValue(OUTLIER_ELIMINATE_SETTING, _options.outlierEliminate);
if (_options.automaticPause != AUTOMATIC_PAUSE_DEFAULT)
settings.setValue(AUTOMATIC_PAUSE_SETTING, _options.automaticPause);
if (_options.pauseSpeed != PAUSE_SPEED_DEFAULT) if (_options.pauseSpeed != PAUSE_SPEED_DEFAULT)
settings.setValue(PAUSE_SPEED_SETTING, _options.pauseSpeed); settings.setValue(PAUSE_SPEED_SETTING, _options.pauseSpeed);
if (_options.pauseInterval != PAUSE_INTERVAL_DEFAULT) if (_options.pauseInterval != PAUSE_INTERVAL_DEFAULT)
@ -1806,10 +1861,14 @@ void GUI::writeSettings()
settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed); settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed);
if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT) if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT)
settings.setValue(DATA_USE_DEM_SETTING, _options.dataUseDEM); 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);
if (_options.poiRadius != POI_RADIUS_DEFAULT) if (_options.poiRadius != POI_RADIUS_DEFAULT)
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius); settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
if (_options.poiUseDEM != POI_USE_DEM_DEFAULT)
settings.setValue(POI_USE_DEM_SETTING, _options.poiUseDEM);
if (_options.useOpenGL != USE_OPENGL_DEFAULT) if (_options.useOpenGL != USE_OPENGL_DEFAULT)
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL); settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2
@ -1876,7 +1935,7 @@ void GUI::readSettings()
value = settings.value(COORDINATES_SETTING, COORDINATES_DEFAULT).toInt(); value = settings.value(COORDINATES_SETTING, COORDINATES_DEFAULT).toInt();
if (value == DMS) if (value == DMS)
_DMSAction->trigger(); _dmsAction->trigger();
else if (value == DegreesMinutes) else if (value == DegreesMinutes)
_degreesMinutesAction->trigger(); _degreesMinutesAction->trigger();
else else
@ -1893,9 +1952,11 @@ void GUI::readSettings()
_showMapAction->setChecked(true); _showMapAction->setChecked(true);
else else
_mapView->showMap(false); _mapView->showMap(false);
if (_ml->maps().count()) { QAction *ma = mapAction(settings.value(CURRENT_MAP_SETTING).toString());
int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString()); if (ma) {
_mapActions.at(index)->trigger(); ma->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} }
if (settings.value(SHOW_COORDINATES_SETTING, SHOW_COORDINATES_DEFAULT) if (settings.value(SHOW_COORDINATES_SETTING, SHOW_COORDINATES_DEFAULT)
.toBool()) { .toBool()) {
@ -2072,12 +2133,18 @@ void GUI::readSettings()
USE_REPORTED_SPEED_DEFAULT).toBool(); USE_REPORTED_SPEED_DEFAULT).toBool();
_options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING, _options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING,
DATA_USE_DEM_DEFAULT).toBool(); 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();
_options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING,
AUTOMATIC_PAUSE_DEFAULT).toBool();
_options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING, _options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING,
PAUSE_INTERVAL_DEFAULT).toInt(); PAUSE_INTERVAL_DEFAULT).toInt();
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT) _options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
.toInt(); .toInt();
_options.poiUseDEM = settings.value(POI_USE_DEM_SETTING,
POI_USE_DEM_DEFAULT).toBool();
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT) _options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
.toBool(); .toBool();
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2
@ -2153,24 +2220,42 @@ void GUI::readSettings()
Track::setCadenceFilter(_options.cadenceFilter); Track::setCadenceFilter(_options.cadenceFilter);
Track::setPowerFilter(_options.powerFilter); Track::setPowerFilter(_options.powerFilter);
Track::setOutlierElimination(_options.outlierEliminate); Track::setOutlierElimination(_options.outlierEliminate);
Track::setAutomaticPause(_options.automaticPause);
Track::setPauseSpeed(_options.pauseSpeed); Track::setPauseSpeed(_options.pauseSpeed);
Track::setPauseInterval(_options.pauseInterval); Track::setPauseInterval(_options.pauseInterval);
Track::useReportedSpeed(_options.useReportedSpeed); 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->setRadius(_options.poiRadius);
_poi->useDEM(_options.poiUseDEM);
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024); QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
settings.endGroup(); settings.endGroup();
} }
int GUI::mapIndex(const QString &name) QAction *GUI::mapAction(const QString &name)
{ {
for (int i = 0; i < _ml->maps().count(); i++) QList<QAction *> maps = _mapsActionGroup->actions();
if (_ml->maps().at(i)->name() == name)
return i; // 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; return 0;
} }

View File

@ -26,9 +26,9 @@ class FileBrowser;
class GraphTab; class GraphTab;
class MapView; class MapView;
class Map; class Map;
class MapList;
class POI; class POI;
class QScreen; class QScreen;
class MapAction;
class GUI : public QMainWindow class GUI : public QMainWindow
{ {
@ -64,7 +64,7 @@ private slots:
void prevMap(); void prevMap();
void openOptions(); void openOptions();
void mapChanged(int); void mapChanged();
void graphChanged(int); void graphChanged(int);
void poiFileChecked(int); void poiFileChecked(int);
@ -88,16 +88,18 @@ private slots:
void screenChanged(QScreen *screen); void screenChanged(QScreen *screen);
void logicalDotsPerInchChanged(qreal dpi); void logicalDotsPerInchChanged(qreal dpi);
void mapLoaded();
void mapInitialized();
private: private:
typedef QPair<QDate, QDate> DateRange; typedef QPair<QDate, QDate> DateRange;
void loadMaps();
void loadPOIs(); void loadPOIs();
void closeFiles(); void closeFiles();
void plot(QPrinter *printer); void plot(QPrinter *printer);
QAction *createPOIFileAction(const QString &fileName); QAction *createPOIFileAction(const QString &fileName);
QAction *createMapAction(const Map *map); MapAction *createMapAction(Map *map);
void createPOIFilesActions(); void createPOIFilesActions();
void createMapActions(); void createMapActions();
void createActions(); void createActions();
@ -127,7 +129,7 @@ private:
qreal distance() const; qreal distance() const;
qreal time() const; qreal time() const;
qreal movingTime() const; qreal movingTime() const;
int mapIndex(const QString &name); QAction *mapAction(const QString &name);
void readSettings(); void readSettings();
void writeSettings(); void writeSettings();
@ -180,7 +182,7 @@ private:
QAction *_nauticalUnitsAction; QAction *_nauticalUnitsAction;
QAction *_decimalDegreesAction; QAction *_decimalDegreesAction;
QAction *_degreesMinutesAction; QAction *_degreesMinutesAction;
QAction *_DMSAction; QAction *_dmsAction;
QAction *_totalTimeAction; QAction *_totalTimeAction;
QAction *_movingTimeAction; QAction *_movingTimeAction;
QAction *_nextMapAction; QAction *_nextMapAction;
@ -196,11 +198,9 @@ private:
QAction *_showCoordinatesAction; QAction *_showCoordinatesAction;
QAction *_openOptionsAction; QAction *_openOptionsAction;
QAction *_mapsEnd; QAction *_mapsEnd;
QList<QAction*> _mapActions;
QList<QAction*> _poiFilesActions;
QList<QAction*> _poiFilesActions;
QSignalMapper *_poiFilesSignalMapper; QSignalMapper *_poiFilesSignalMapper;
QSignalMapper *_mapsSignalMapper;
QLabel *_fileNameLabel; QLabel *_fileNameLabel;
QLabel *_distanceLabel; QLabel *_distanceLabel;
@ -212,7 +212,6 @@ private:
QList<GraphTab*> _tabs; QList<GraphTab*> _tabs;
POI *_poi; POI *_poi;
MapList *_ml;
Map *_map; Map *_map;
FileBrowser *_browser; FileBrowser *_browser;

View File

@ -5,7 +5,7 @@
HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type, HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type,
int width, const QColor &color, QGraphicsItem *parent) 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->isValid());
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

@ -2,7 +2,6 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QWheelEvent> #include <QWheelEvent>
#include <QApplication> #include <QApplication>
#include <QPixmapCache>
#include <QScrollBar> #include <QScrollBar>
#include "data/poi.h" #include "data/poi.h"
#include "data/data.h" #include "data/data.h"
@ -55,7 +54,7 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent)
_map = map; _map = map;
_map->load(); _map->load();
_map->setProjection(_projection); _map->setProjection(_projection);
connect(_map, SIGNAL(loaded()), this, SLOT(reloadMap())); connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
_poi = poi; _poi = poi;
connect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI())); connect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI()));
@ -317,7 +316,7 @@ void MapView::setMap(Map *map)
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight())); RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
_map->unload(); _map->unload();
disconnect(_map, SIGNAL(loaded()), this, SLOT(reloadMap())); disconnect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
_map = map; _map = map;
_map->load(); _map->load();
@ -325,7 +324,7 @@ void MapView::setMap(Map *map)
#ifdef ENABLE_HIDPI #ifdef ENABLE_HIDPI
_map->setDevicePixelRatio(_deviceRatio, _mapRatio); _map->setDevicePixelRatio(_deviceRatio, _mapRatio);
#endif // ENABLE_HIDPI #endif // ENABLE_HIDPI
connect(_map, SIGNAL(loaded()), this, SLOT(reloadMap())); connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
digitalZoom(0); digitalZoom(0);
@ -351,7 +350,6 @@ void MapView::setMap(Map *map)
centerOn(nc); centerOn(nc);
reloadMap(); reloadMap();
QPixmapCache::clear();
} }
void MapView::setPOI(POI *poi) void MapView::setPOI(POI *poi)
@ -453,10 +451,7 @@ void MapView::setCoordinatesFormat(CoordinatesFormat format)
void MapView::clearMapCache() void MapView::clearMapCache()
{ {
_map->clearCache(); _map->clearCache();
reloadMap();
fitMapZoom();
rescale();
centerOn(contentCenter());
} }
void MapView::digitalZoom(int zoom) void MapView::digitalZoom(int zoom)
@ -982,7 +977,6 @@ void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
_deviceRatio = deviceRatio; _deviceRatio = deviceRatio;
_mapRatio = mapRatio; _mapRatio = mapRatio;
QPixmapCache::clear();
QRectF vr(mapToScene(viewport()->rect()).boundingRect() QRectF vr(mapToScene(viewport()->rect()).boundingRect()
.intersected(_map->bounds())); .intersected(_map->bounds()));

View File

@ -12,6 +12,7 @@
#include <QRadioButton> #include <QRadioButton>
#include <QLabel> #include <QLabel>
#include <QSysInfo> #include <QSysInfo>
#include <QButtonGroup>
#include "map/pcs.h" #include "map/pcs.h"
#include "icons.h" #include "icons.h"
#include "colorbox.h" #include "colorbox.h"
@ -37,6 +38,12 @@ static QFrame *line()
#endif // Q_OS_MAC #endif // Q_OS_MAC
void OptionsDialog::automaticPauseDetectionSet(bool set)
{
_pauseInterval->setEnabled(!set);
_pauseSpeed->setEnabled(!set);
}
QWidget *OptionsDialog::createMapPage() QWidget *OptionsDialog::createMapPage()
{ {
_projection = new LimitedComboBox(200); _projection = new LimitedComboBox(200);
@ -343,10 +350,18 @@ QWidget *OptionsDialog::createDataPage()
filterTab->setLayout(filterTabLayout); filterTab->setLayout(filterTabLayout);
_automaticPause = new QRadioButton(tr("Automatic"));
_manualPause = new QRadioButton(tr("Custom"));
if (_options->automaticPause)
_automaticPause->setChecked(true);
else
_manualPause->setChecked(true);
_pauseSpeed = new QDoubleSpinBox(); _pauseSpeed = new QDoubleSpinBox();
_pauseSpeed->setDecimals(1); _pauseSpeed->setDecimals(1);
_pauseSpeed->setSingleStep(0.1); _pauseSpeed->setSingleStep(0.1);
_pauseSpeed->setMinimum(0.1); _pauseSpeed->setMinimum(0.1);
_pauseSpeed->setEnabled(_manualPause->isChecked());
if (_options->units == Imperial) { if (_options->units == Imperial) {
_pauseSpeed->setValue(_options->pauseSpeed * MS2MIH); _pauseSpeed->setValue(_options->pauseSpeed * MS2MIH);
_pauseSpeed->setSuffix(UNIT_SPACE + tr("mi/h")); _pauseSpeed->setSuffix(UNIT_SPACE + tr("mi/h"));
@ -361,10 +376,26 @@ QWidget *OptionsDialog::createDataPage()
_pauseInterval->setMinimum(1); _pauseInterval->setMinimum(1);
_pauseInterval->setSuffix(UNIT_SPACE + tr("s")); _pauseInterval->setSuffix(UNIT_SPACE + tr("s"));
_pauseInterval->setValue(_options->pauseInterval); _pauseInterval->setValue(_options->pauseInterval);
_pauseInterval->setEnabled(_manualPause->isChecked());
QFormLayout *pauseLayout = new QFormLayout(); connect(_automaticPause, SIGNAL(toggled(bool)), this,
pauseLayout->addRow(tr("Minimal speed:"), _pauseSpeed); SLOT(automaticPauseDetectionSet(bool)));
pauseLayout->addRow(tr("Minimal duration:"), _pauseInterval);
QHBoxLayout *pauseTypeLayout = new QHBoxLayout();
#ifdef Q_OS_MAC
pauseTypeLayout->addStretch();
#endif
pauseTypeLayout->addWidget(_automaticPause);
pauseTypeLayout->addWidget(_manualPause);
pauseTypeLayout->addStretch();
QFormLayout *pauseValuesLayout = new QFormLayout();
pauseValuesLayout->addRow(tr("Minimal speed:"), _pauseSpeed);
pauseValuesLayout->addRow(tr("Minimal duration:"), _pauseInterval);
QVBoxLayout *pauseLayout = new QVBoxLayout();
pauseLayout->addLayout(pauseTypeLayout);
pauseLayout->addLayout(pauseValuesLayout);
QWidget *pauseTab = new QWidget(); QWidget *pauseTab = new QWidget();
pauseTab->setLayout(pauseLayout); pauseTab->setLayout(pauseLayout);
@ -376,6 +407,8 @@ QWidget *OptionsDialog::createDataPage()
_reportedSpeed->setChecked(true); _reportedSpeed->setChecked(true);
else else
_computedSpeed->setChecked(true); _computedSpeed->setChecked(true);
_showSecondarySpeed = new QCheckBox(tr("Show secondary speed"));
_showSecondarySpeed->setChecked(_options->showSecondarySpeed);
_dataGPSElevation = new QRadioButton(tr("GPS data")); _dataGPSElevation = new QRadioButton(tr("GPS data"));
_dataDEMElevation = new QRadioButton(tr("DEM data")); _dataDEMElevation = new QRadioButton(tr("DEM data"));
@ -383,19 +416,28 @@ QWidget *OptionsDialog::createDataPage()
_dataDEMElevation->setChecked(true); _dataDEMElevation->setChecked(true);
else else
_dataGPSElevation->setChecked(true); _dataGPSElevation->setChecked(true);
_showSecondaryElevation = new QCheckBox(tr("Show secondary elevation"));
_showSecondaryElevation->setChecked(_options->showSecondaryElevation);
QWidget *sourceTab = new QWidget(); QWidget *sourceTab = new QWidget();
QVBoxLayout *sourceTabLayout = new QVBoxLayout(); QVBoxLayout *sourceTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QButtonGroup *speedGroup = new QButtonGroup(this);
speedGroup->addButton(_computedSpeed);
speedGroup->addButton(_reportedSpeed);
QVBoxLayout *speedOptions = new QVBoxLayout(); QVBoxLayout *speedOptions = new QVBoxLayout();
speedOptions->addWidget(_computedSpeed); speedOptions->addWidget(_computedSpeed);
speedOptions->addWidget(_reportedSpeed); speedOptions->addWidget(_reportedSpeed);
speedOptions->addWidget(_showSecondarySpeed);
QButtonGroup *elevationGroup = new QButtonGroup(this);
elevationGroup->addButton(_dataGPSElevation);
elevationGroup->addButton(_dataDEMElevation);
QVBoxLayout *elevationOptions = new QVBoxLayout(); QVBoxLayout *elevationOptions = new QVBoxLayout();
elevationOptions->addWidget(_dataGPSElevation); elevationOptions->addWidget(_dataGPSElevation);
elevationOptions->addWidget(_dataDEMElevation); elevationOptions->addWidget(_dataDEMElevation);
elevationOptions->addWidget(_showSecondaryElevation);
QFormLayout *formLayout = new QFormLayout(); QFormLayout *formLayout = new QFormLayout();
formLayout->addRow(tr("Speed:"), speedOptions); formLayout->addRow(tr("Speed:"), speedOptions);
@ -408,12 +450,14 @@ QWidget *OptionsDialog::createDataPage()
speedLayout->addWidget(_computedSpeed); speedLayout->addWidget(_computedSpeed);
speedLayout->addWidget(_reportedSpeed); speedLayout->addWidget(_reportedSpeed);
speedLayout->addWidget(_showSecondarySpeed);
QGroupBox *speedBox = new QGroupBox(tr("Speed")); QGroupBox *speedBox = new QGroupBox(tr("Speed"));
speedBox->setLayout(speedLayout); speedBox->setLayout(speedLayout);
elevationLayout->addWidget(_dataGPSElevation); elevationLayout->addWidget(_dataGPSElevation);
elevationLayout->addWidget(_dataDEMElevation); elevationLayout->addWidget(_dataDEMElevation);
elevationLayout->addWidget(_showSecondaryElevation);
QGroupBox *elevationBox = new QGroupBox(tr("Elevation")); QGroupBox *elevationBox = new QGroupBox(tr("Elevation"));
elevationBox->setLayout(elevationLayout); elevationBox->setLayout(elevationLayout);
@ -435,13 +479,6 @@ QWidget *OptionsDialog::createDataPage()
QWidget *OptionsDialog::createPOIPage() 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 = new QDoubleSpinBox();
_poiRadius->setSingleStep(1); _poiRadius->setSingleStep(1);
_poiRadius->setDecimals(1); _poiRadius->setDecimals(1);
@ -456,13 +493,8 @@ QWidget *OptionsDialog::createPOIPage()
_poiRadius->setSuffix(UNIT_SPACE + tr("km")); _poiRadius->setSuffix(UNIT_SPACE + tr("km"));
} }
QVBoxLayout *elevationLayout = new QVBoxLayout();
elevationLayout->addWidget(_poiGPSElevation);
elevationLayout->addWidget(_poiDEMElevation);
QFormLayout *poiLayout = new QFormLayout(); QFormLayout *poiLayout = new QFormLayout();
poiLayout->addRow(tr("Radius:"), _poiRadius); poiLayout->addRow(tr("Radius:"), _poiRadius);
poiLayout->addRow(tr("Elevation:"), elevationLayout);
QWidget *poiTab = new QWidget(); QWidget *poiTab = new QWidget();
poiTab->setLayout(poiLayout); poiTab->setLayout(poiLayout);
@ -679,6 +711,7 @@ void OptionsDialog::accept()
_options->cadenceFilter = _cadenceFilter->value(); _options->cadenceFilter = _cadenceFilter->value();
_options->powerFilter = _powerFilter->value(); _options->powerFilter = _powerFilter->value();
_options->outlierEliminate = _outlierEliminate->isChecked(); _options->outlierEliminate = _outlierEliminate->isChecked();
_options->automaticPause = _automaticPause->isChecked();
qreal pauseSpeed = (_options->units == Imperial) qreal pauseSpeed = (_options->units == Imperial)
? _pauseSpeed->value() / MS2MIH : (_options->units == Nautical) ? _pauseSpeed->value() / MS2MIH : (_options->units == Nautical)
? _pauseSpeed->value() / MS2KN : _pauseSpeed->value() / MS2KMH; ? _pauseSpeed->value() / MS2KN : _pauseSpeed->value() / MS2KMH;
@ -687,13 +720,14 @@ void OptionsDialog::accept()
_options->pauseInterval = _pauseInterval->value(); _options->pauseInterval = _pauseInterval->value();
_options->useReportedSpeed = _reportedSpeed->isChecked(); _options->useReportedSpeed = _reportedSpeed->isChecked();
_options->dataUseDEM = _dataDEMElevation->isChecked(); _options->dataUseDEM = _dataDEMElevation->isChecked();
_options->showSecondaryElevation = _showSecondaryElevation->isChecked();
_options->showSecondarySpeed = _showSecondarySpeed->isChecked();
qreal poiRadius = (_options->units == Imperial) qreal poiRadius = (_options->units == Imperial)
? _poiRadius->value() * MIINM : (_options->units == Nautical) ? _poiRadius->value() * MIINM : (_options->units == Nautical)
? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM; ? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM;
if (qAbs(poiRadius - _options->poiRadius) > 0.01) if (qAbs(poiRadius - _options->poiRadius) > 0.01)
_options->poiRadius = poiRadius; _options->poiRadius = poiRadius;
_options->poiUseDEM = _poiDEMElevation->isChecked();
_options->useOpenGL = _useOpenGL->isChecked(); _options->useOpenGL = _useOpenGL->isChecked();
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2

View File

@ -49,13 +49,15 @@ struct Options {
int cadenceFilter; int cadenceFilter;
int powerFilter; int powerFilter;
bool outlierEliminate; bool outlierEliminate;
bool automaticPause;
qreal pauseSpeed; qreal pauseSpeed;
int pauseInterval; int pauseInterval;
bool useReportedSpeed; bool useReportedSpeed;
bool dataUseDEM; bool dataUseDEM;
bool showSecondaryElevation;
bool showSecondarySpeed;
// POI // POI
int poiRadius; int poiRadius;
bool poiUseDEM;
// System // System
bool useOpenGL; bool useOpenGL;
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2
@ -86,6 +88,9 @@ public slots:
public: public:
OptionsDialog(Options *options, QWidget *parent = 0); OptionsDialog(Options *options, QWidget *parent = 0);
private slots:
void automaticPauseDetectionSet(bool set);
private: private:
QWidget *createMapPage(); QWidget *createMapPage();
QWidget *createAppearancePage(); QWidget *createAppearancePage();
@ -129,16 +134,19 @@ private:
OddSpinBox *_cadenceFilter; OddSpinBox *_cadenceFilter;
OddSpinBox *_powerFilter; OddSpinBox *_powerFilter;
QCheckBox *_outlierEliminate; QCheckBox *_outlierEliminate;
QRadioButton *_automaticPause;
QRadioButton *_manualPause;
QDoubleSpinBox *_pauseSpeed; QDoubleSpinBox *_pauseSpeed;
QSpinBox *_pauseInterval; QSpinBox *_pauseInterval;
QRadioButton *_computedSpeed; QRadioButton *_computedSpeed;
QRadioButton *_reportedSpeed; QRadioButton *_reportedSpeed;
QRadioButton *_dataGPSElevation; QRadioButton *_dataGPSElevation;
QRadioButton *_dataDEMElevation; QRadioButton *_dataDEMElevation;
QCheckBox *_showSecondaryElevation;
QCheckBox *_showSecondarySpeed;
// POI // POI
QDoubleSpinBox *_poiRadius; QDoubleSpinBox *_poiRadius;
QRadioButton *_poiGPSElevation;
QRadioButton *_poiDEMElevation;
// System // System
QSpinBox *_pixmapCache; QSpinBox *_pixmapCache;
QSpinBox *_connectionTimeout; QSpinBox *_connectionTimeout;

View File

@ -163,3 +163,9 @@ void Popup::show(const QPoint &pos, const QString &text, QWidget *w)
PopupLabel::_instance->place(pos, w); PopupLabel::_instance->place(pos, w);
PopupLabel::_instance->showNormal(); PopupLabel::_instance->showNormal();
} }
void Popup::clear()
{
if (PopupLabel::_instance)
delete PopupLabel::_instance;
}

View File

@ -9,6 +9,7 @@ class Popup
{ {
public: public:
static void show(const QPoint &pos, const QString &text, QWidget *w); static void show(const QPoint &pos, const QString &text, QWidget *w);
static void clear();
}; };
#endif // POPUP_H #endif // POPUP_H

View File

@ -5,7 +5,7 @@
PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width, PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, QGraphicsItem *parent) 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); tt.insert(tr("Name"), _name);
if (!_desc.isEmpty()) if (!_desc.isEmpty())
tt.insert(tr("Description"), _desc); 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(), tt.insert(tr("Distance"), Format::distance(path().last().last().distance(),
_units)); _units));
if (!_links.isEmpty()) { if (!_links.isEmpty()) {
@ -43,6 +45,7 @@ RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent)
_name = route.name(); _name = route.name();
_desc = route.description(); _desc = route.description();
_comment = route.comment();
_links = route.links(); _links = route.links();
_coordinatesFormat = DecimalDegrees; _coordinatesFormat = DecimalDegrees;
} }

View File

@ -29,6 +29,7 @@ public:
private: private:
QString _name; QString _name;
QString _desc; QString _desc;
QString _comment;
QVector<Link> _links; QVector<Link> _links;
CoordinatesFormat _coordinatesFormat; CoordinatesFormat _coordinatesFormat;

View File

@ -135,6 +135,8 @@
#define POWER_FILTER_DEFAULT 3 #define POWER_FILTER_DEFAULT 3
#define OUTLIER_ELIMINATE_SETTING "outlierEliminate" #define OUTLIER_ELIMINATE_SETTING "outlierEliminate"
#define OUTLIER_ELIMINATE_DEFAULT true #define OUTLIER_ELIMINATE_DEFAULT true
#define AUTOMATIC_PAUSE_SETTING "automaticPause"
#define AUTOMATIC_PAUSE_DEFAULT true
#define PAUSE_SPEED_SETTING "pauseSpeed" #define PAUSE_SPEED_SETTING "pauseSpeed"
#define PAUSE_SPEED_DEFAULT 0.5 /* m/s */ #define PAUSE_SPEED_DEFAULT 0.5 /* m/s */
#define PAUSE_INTERVAL_SETTING "pauseInterval" #define PAUSE_INTERVAL_SETTING "pauseInterval"
@ -143,10 +145,12 @@
#define USE_REPORTED_SPEED_DEFAULT false #define USE_REPORTED_SPEED_DEFAULT false
#define DATA_USE_DEM_SETTING "dataUseDEM" #define DATA_USE_DEM_SETTING "dataUseDEM"
#define DATA_USE_DEM_DEFAULT false #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 POI_RADIUS_SETTING "poiRadius" #define POI_RADIUS_SETTING "poiRadius"
#define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM) #define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM)
#define POI_USE_DEM_SETTING "poiUseDEM"
#define POI_USE_DEM_DEFAULT false
#define USE_OPENGL_SETTING "useOpenGL" #define USE_OPENGL_SETTING "useOpenGL"
#define USE_OPENGL_DEFAULT false #define USE_OPENGL_DEFAULT false
#define ENABLE_HTTP2_SETTING "enableHTTP2" #define ENABLE_HTTP2_SETTING "enableHTTP2"

View File

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

View File

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

View File

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

View File

@ -10,7 +10,8 @@ class SpeedGraphItem : public GraphItem
public: public:
SpeedGraphItem(const Graph &graph, GraphType type, int width, 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 avg() const {return _avg;}
qreal mavg() const {return _mavg;} qreal mavg() const {return _mavg;}

View File

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

View File

@ -1,10 +1,21 @@
#include <QImageReader>
#include <QLabel>
#include "popup.h" #include "popup.h"
#include "tooltip.h" #include "tooltip.h"
static QSize thumbnailSize(const ImageInfo &img, int limit)
{
int width, height;
if (img.size().width() > img.size().height()) {
width = qMin(img.size().width(), limit);
qreal ratio = img.size().width() / (qreal)img.size().height();
height = (int)(width / ratio);
} else {
height = qMin(img.size().height(), limit);
qreal ratio = img.size().height() / (qreal)img.size().width();
width = (int)(height / ratio);
}
#define THUMBNAIL_MAX_SIZE 240 return QSize(width, height);
}
void ToolTip::insert(const QString &key, const QString &value) void ToolTip::insert(const QString &key, const QString &value)
{ {
@ -15,22 +26,17 @@ QString ToolTip::toString() const
{ {
QString html; QString html;
if (_img.isValid()) { if (_images.size()) {
int width, height; html = "<div align=\"center\">";
if (_img.size().width() > _img.size().height()) { for (int i = 0; i < _images.size(); i++) {
width = qMin(_img.size().width(), THUMBNAIL_MAX_SIZE); const ImageInfo &img = _images.at(i);
qreal ratio = _img.size().width() / (qreal)_img.size().height(); QSize size(thumbnailSize(img, qMin(960/_images.size(), 240)));
height = (int)(width / ratio);
} else {
height = qMin(_img.size().height(), THUMBNAIL_MAX_SIZE);
qreal ratio = _img.size().height() / (qreal)_img.size().width();
width = (int)(height / ratio);
}
html += "<div align=\"center\">"; html += QString("<a href=\"file:%0\">"
html += QString("<a href=\"file:%0\">" "<img src=\"%0\" width=\"%1\" height=\"%2\"/></a>")
"<img src=\"%0\" width=\"%1\" height=\"%2\"/></a>") .arg(img.path(), QString::number(size.width()),
.arg(_img.path(), QString::number(width), QString::number(height)); QString::number(size.height()));
}
html += "</div>"; html += "</div>";
} }

View File

@ -3,6 +3,7 @@
#include <QString> #include <QString>
#include <QList> #include <QList>
#include <QVector>
#include "common/kv.h" #include "common/kv.h"
#include "data/imageinfo.h" #include "data/imageinfo.h"
@ -10,12 +11,12 @@ class ToolTip
{ {
public: public:
void insert(const QString &key, const QString &value); void insert(const QString &key, const QString &value);
void setImage(const ImageInfo &image) {_img = image;} void setImages(const QVector<ImageInfo> &images) {_images = images;}
QString toString() const; QString toString() const;
private: private:
QList<KV<QString, QString> > _list; QList<KV<QString, QString> > _list;
ImageInfo _img; QVector<ImageInfo> _images;
}; };
#endif // TOOLTIP_H #endif // TOOLTIP_H

View File

@ -13,6 +13,8 @@ QString TrackItem::info() const
tt.insert(tr("Name"), _name); tt.insert(tr("Name"), _name);
if (!_desc.isEmpty()) if (!_desc.isEmpty())
tt.insert(tr("Description"), _desc); 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(), tt.insert(tr("Distance"), Format::distance(path().last().last().distance(),
_units)); _units));
if (_time > 0) if (_time > 0)
@ -41,6 +43,7 @@ TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent)
{ {
_name = track.name(); _name = track.name();
_desc = track.description(); _desc = track.description();
_comment = track.comment();
_links = track.links(); _links = track.links();
_date = track.date(); _date = track.date();
_time = track.time(); _time = track.time();

View File

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

View File

@ -21,15 +21,36 @@ QString WaypointItem::info() const
tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name()); tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name());
tt.insert(qApp->translate("WaypointItem", "Coordinates"), tt.insert(qApp->translate("WaypointItem", "Coordinates"),
Format::coordinates(_waypoint.coordinates(), _format)); Format::coordinates(_waypoint.coordinates(), _format));
if (_waypoint.hasElevation()) if (!std::isnan(_waypoint.elevations().first)) {
tt.insert(qApp->translate("WaypointItem", "Elevation"), QString val = Format::elevation(_waypoint.elevations().first, _units);
Format::elevation(_waypoint.elevation(), _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()) if (_waypoint.timestamp().isValid())
tt.insert(qApp->translate("WaypointItem", "Date"), tt.insert(qApp->translate("WaypointItem", "Date"),
_waypoint.timestamp().toString(Qt::SystemLocaleShortDate)); _waypoint.timestamp().toString(Qt::SystemLocaleShortDate));
if (!_waypoint.description().isEmpty()) if (!_waypoint.description().isEmpty())
tt.insert(qApp->translate("WaypointItem", "Description"), tt.insert(qApp->translate("WaypointItem", "Description"),
_waypoint.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();
addr += "<br/>" + _waypoint.address().city();
if (!_waypoint.address().postalCode().isEmpty())
addr += "<br/>" + _waypoint.address().postalCode();
if (!_waypoint.address().state().isEmpty())
addr += "<br/>" + _waypoint.address().state();
if (!_waypoint.address().country().isEmpty())
addr += "<br/>" + _waypoint.address().country();
addr += "</address>";
tt.insert(qApp->translate("WaypointItem", "Address"), addr);
}
if (!_waypoint.links().isEmpty()) { if (!_waypoint.links().isEmpty()) {
QString links; QString links;
for (int i = 0; i < _waypoint.links().size(); i++) { for (int i = 0; i < _waypoint.links().size(); i++) {
@ -41,7 +62,7 @@ QString WaypointItem::info() const
} }
tt.insert(qApp->translate("WaypointItem", "Links"), links); tt.insert(qApp->translate("WaypointItem", "Links"), links);
} }
tt.setImage(_waypoint.image()); tt.setImages(_waypoint.images());
return tt.toString(); return tt.toString();
} }

View File

@ -14,7 +14,8 @@ double Coordinates::distanceTo(const Coordinates &c) const
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Coordinates &c) QDebug operator<<(QDebug dbg, const Coordinates &c)
{ {
dbg.nospace() << "Coordinates(" << c.lon() << ", " << c.lat() << ")"; dbg.nospace() << qSetRealNumberPrecision(10) << "Coordinates(" << c.lon()
<< ", " << c.lat() << ")";
return dbg.space(); return dbg.space();
} }
#endif // QT_NO_DEBUG #endif // QT_NO_DEBUG

18
src/common/garmin.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef GARMIN_H
#define GARMIN_H
#include <QtGlobal>
#define LS(val, bits) ((qint32)(((quint32)(val))<<(bits)))
inline double toWGS32(qint32 v)
{
return ((double)v / (double)(1U<<31)) * 180.0;
}
inline double toWGS24(qint32 v)
{
return toWGS32(LS(v, 8));
}
#endif // GARMIN_H

View File

@ -28,6 +28,9 @@ public:
double left() const {return _tl.lon();} double left() const {return _tl.lon();}
double right() const {return _br.lon();} double right() const {return _br.lon();}
double width() const {return (right() - left());}
double height() const {return (top() - bottom());}
void setLeft(double val) {_tl.rlon() = val;} void setLeft(double val) {_tl.rlon() = val;}
void setRight(double val) {_br.rlon() = val;} void setRight(double val) {_br.rlon() = val;}
void setTop(double val) {_tl.rlat() = val;} void setTop(double val) {_tl.rlat() = val;}

View File

@ -9,6 +9,7 @@
#define TIFF_SHORT 3 #define TIFF_SHORT 3
#define TIFF_LONG 4 #define TIFF_LONG 4
#define TIFF_RATIONAL 5 #define TIFF_RATIONAL 5
#define TIFF_SRATIONAL 10
#define TIFF_DOUBLE 12 #define TIFF_DOUBLE 12
class TIFFFile class TIFFFile

35
src/data/address.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef ADDRESS_H
#define ADDRESS_H
#include <QString>
class Address
{
public:
Address() {}
Address(const QString &street, const QString &city)
: _street(street), _city(city) {}
const QString &street() const {return _street;}
const QString &city() const {return _city;}
const QString &state() const {return _state;}
const QString &country() const {return _country;}
const QString &postalCode() const {return _postalCode;}
void setStreet(const QString &street) {_street = street;}
void setCity(const QString &city) {_city = city;}
void setState(const QString &state) {_state = state;}
void setCountry(const QString &country) {_country = country;}
void setPostalCode(const QString &postalCode) {_postalCode = postalCode;}
bool isValid() const {return !(_street.isEmpty() || _city.isEmpty());}
private:
QString _street;
QString _city;
QString _state;
QString _country;
QString _postalCode;
};
#endif // ADDRESS_H

View File

@ -19,7 +19,7 @@
#include "exifparser.h" #include "exifparser.h"
#include "cupparser.h" #include "cupparser.h"
#include "gpiparser.h" #include "gpiparser.h"
#include "dem.h" #include "smlparser.h"
#include "data.h" #include "data.h"
@ -41,78 +41,48 @@ static GeoJSONParser geojson;
static EXIFParser exif; static EXIFParser exif;
static CUPParser cup; static CUPParser cup;
static GPIParser gpi; static GPIParser gpi;
static SMLParser sml;
static QHash<QString, Parser*> parsers() static QMap<QString, Parser*> parsers()
{ {
QHash<QString, Parser*> hash; QMap<QString, Parser*> map;
hash.insert("gpx", &gpx); map.insert("gpx", &gpx);
hash.insert("tcx", &tcx); map.insert("tcx", &tcx);
hash.insert("kml", &kml); map.insert("kml", &kml);
hash.insert("fit", &fit); map.insert("fit", &fit);
hash.insert("csv", &csv); map.insert("csv", &csv);
hash.insert("igc", &igc); map.insert("igc", &igc);
hash.insert("nmea", &nmea); map.insert("nmea", &nmea);
hash.insert("plt", &plt); map.insert("plt", &plt);
hash.insert("wpt", &wpt); map.insert("wpt", &wpt);
hash.insert("rte", &rte); map.insert("rte", &rte);
hash.insert("loc", &loc); map.insert("loc", &loc);
hash.insert("slf", &slf); map.insert("slf", &slf);
#ifdef ENABLE_GEOJSON #ifdef ENABLE_GEOJSON
hash.insert("json", &geojson); map.insert("json", &geojson);
hash.insert("geojson", &geojson); map.insert("geojson", &geojson);
#endif // ENABLE_GEOJSON #endif // ENABLE_GEOJSON
hash.insert("jpeg", &exif); map.insert("jpeg", &exif);
hash.insert("jpg", &exif); map.insert("jpg", &exif);
hash.insert("cup", &cup); map.insert("cup", &cup);
hash.insert("gpi", &gpi); map.insert("gpi", &gpi);
map.insert("sml", &sml);
return hash; return map;
} }
QMap<QString, Parser*> Data::_parsers = parsers();
QHash<QString, Parser*> Data::_parsers = parsers();
bool Data::_useDEM = false;
void Data::processData(QList<TrackData> &trackData, QList<RouteData> &routeData) void Data::processData(QList<TrackData> &trackData, QList<RouteData> &routeData)
{ {
for (int i = 0; i < trackData.count(); i++) { 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);
}
}
}
_tracks.append(Track(trackData.at(i))); _tracks.append(Track(trackData.at(i)));
} for (int i = 0; i < routeData.count(); 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);
}
}
_routes.append(Route(routeData.at(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); QFile file(fileName);
QFileInfo fi(fileName); QFileInfo fi(fileName);
@ -127,12 +97,11 @@ Data::Data(const QString &fileName, bool poi)
return; return;
} }
QHash<QString, Parser*>::iterator it; QMap<QString, Parser*>::iterator it;
if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) { if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) {
if (it.value()->parse(&file, trackData, routeData, _polygons, if (it.value()->parse(&file, trackData, routeData, _polygons,
_waypoints)) { _waypoints)) {
if (!poi) processData(trackData, routeData);
processData(trackData, routeData);
_valid = true; _valid = true;
return; return;
} else { } else {
@ -143,8 +112,7 @@ Data::Data(const QString &fileName, bool poi)
for (it = _parsers.begin(); it != _parsers.end(); it++) { for (it = _parsers.begin(); it != _parsers.end(); it++) {
if (it.value()->parse(&file, trackData, routeData, _polygons, if (it.value()->parse(&file, trackData, routeData, _polygons,
_waypoints)) { _waypoints)) {
if (!poi) processData(trackData, routeData);
processData(trackData, routeData);
_valid = true; _valid = true;
return; return;
} }
@ -163,17 +131,8 @@ Data::Data(const QString &fileName, bool poi)
QString Data::formats() QString Data::formats()
{ {
QStringList l(filter());
qSort(l);
QString supported;
for (int i = 0; i < l.size(); i++) {
supported += l.at(i);
if (i != l.size() - 1)
supported += " ";
}
return return
qApp->translate("Data", "Supported files") + " (" + supported + ");;" qApp->translate("Data", "Supported files") + " (" + filter().join(" ") + ");;"
+ qApp->translate("Data", "CSV files") + " (*.csv);;" + qApp->translate("Data", "CSV files") + " (*.csv);;"
+ qApp->translate("Data", "CUP files") + " (*.cup);;" + qApp->translate("Data", "CUP files") + " (*.cup);;"
+ qApp->translate("Data", "FIT files") + " (*.fit);;" + qApp->translate("Data", "FIT files") + " (*.fit);;"
@ -189,6 +148,7 @@ QString Data::formats()
+ qApp->translate("Data", "NMEA files") + " (*.nmea);;" + qApp->translate("Data", "NMEA files") + " (*.nmea);;"
+ qApp->translate("Data", "OziExplorer files") + " (*.plt *.rte *.wpt);;" + qApp->translate("Data", "OziExplorer files") + " (*.plt *.rte *.wpt);;"
+ qApp->translate("Data", "SLF files") + " (*.slf);;" + qApp->translate("Data", "SLF files") + " (*.slf);;"
+ qApp->translate("Data", "SML files") + " (*.sml);;"
+ qApp->translate("Data", "TCX files") + " (*.tcx);;" + qApp->translate("Data", "TCX files") + " (*.tcx);;"
+ qApp->translate("Data", "All files") + " (*)"; + qApp->translate("Data", "All files") + " (*)";
} }
@ -196,15 +156,10 @@ QString Data::formats()
QStringList Data::filter() QStringList Data::filter()
{ {
QStringList filter; QStringList filter;
QHash<QString, Parser*>::iterator it;
for (it = _parsers.begin(); it != _parsers.end(); it++) for (QMap<QString, Parser*>::iterator it = _parsers.begin();
filter << QString("*.%1").arg(it.key()); it != _parsers.end(); it++)
filter << "*." + it.key();
return filter; return filter;
} }
void Data::useDEM(bool use)
{
_useDEM = use;
}

View File

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

View File

@ -82,7 +82,10 @@ double EXIFParser::altitude(TIFFFile &file, const IFDEntry &alt,
double EXIFParser::coordinate(TIFFFile &file, const IFDEntry &ll) const double EXIFParser::coordinate(TIFFFile &file, const IFDEntry &ll) const
{ {
if (!(ll.type == TIFF_RATIONAL && ll.count == 3)) // Some broken image creators like NOKIA phones use a wrong (SRATIONAL)
// data type
if (!((ll.type == TIFF_RATIONAL || ll.type == TIFF_SRATIONAL)
&& ll.count == 3))
return NAN; return NAN;
if (!file.seek(ll.offset)) if (!file.seek(ll.offset))
@ -209,7 +212,7 @@ bool EXIFParser::parseTIFF(QFile *file, QVector<Waypoint> &waypoints)
Waypoint wp(c); Waypoint wp(c);
wp.setName(QFileInfo(file->fileName()).baseName()); wp.setName(QFileInfo(file->fileName()).baseName());
wp.setImage(img); wp.addImage(img);
wp.setElevation(altitude(tiff, GPSIFD.value(GPSAltitude), wp.setElevation(altitude(tiff, GPSIFD.value(GPSAltitude),
GPSIFD.value(GPSAltitudeRef))); GPSIFD.value(GPSAltitudeRef)));
wp.setTimestamp(QDateTime(QDate::fromString(text(tiff, wp.setTimestamp(QDateTime(QDate::fromString(text(tiff,

View File

@ -1,8 +1,17 @@
#include <cstring>
#include <QDataStream> #include <QDataStream>
#include <QTextCodec> #include <QTextCodec>
#include <QtEndian> #include <QtEndian>
#include <QUrl> #include <QUrl>
#include <QIODevice>
#include <QApplication>
#include <QBuffer> #include <QBuffer>
#include <QImageReader>
#include <QCryptographicHash>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QTemporaryDir>
#endif // QT_VERSION >= 5
#include "common/garmin.h"
#include "gpiparser.h" #include "gpiparser.h"
@ -27,10 +36,25 @@ private:
QString _str; QString _str;
}; };
class CryptDevice : public QIODevice
{
public:
CryptDevice(QIODevice *device, quint32 key, quint32 blockSize,
QObject *parent = 0);
bool isSequential() const {return true;}
#define BLOCK_KEY 0xf870b5 protected:
qint64 readData(char *data, qint64 maxSize);
qint64 writeData(const char *, qint64) {return -1;}
void demangle(quint8 *data, quint32 size, quint32 key) private:
QIODevice *_device;
quint32 _key;
QByteArray _block;
int _available;
};
static void demangle(quint8 *data, quint32 size, quint32 key)
{ {
static const unsigned char shuf[] = { static const unsigned char shuf[] = {
0xb, 0xc, 0xa, 0x0, 0xb, 0xc, 0xa, 0x0,
@ -40,29 +64,77 @@ void demangle(quint8 *data, quint32 size, quint32 key)
}; };
int hiCnt = 0, loCnt; int hiCnt = 0, loCnt;
quint8 sum = shuf[(key >> 0x10) + key + (key >> 0x18) + (key >> 8) & 0xf]; quint8 sum = shuf[((key >> 24) + (key >> 16) + (key >> 8) + key) & 0xf];
for (quint32 i = 0; i < size; i++) { for (quint32 i = 0; i < size; i++) {
quint8 hiAdd = shuf[key >> (hiCnt << 2) & 0xf] + sum; quint8 hiAdd = shuf[key >> (hiCnt << 2) & 0xf] + sum;
loCnt = (hiCnt > 6) ? 0 : hiCnt + 1; loCnt = (hiCnt > 6) ? 0 : hiCnt + 1;
quint8 loAdd = shuf[key >> (loCnt << 2) & 0xf] + sum; quint8 loAdd = shuf[key >> (loCnt << 2) & 0xf] + sum;
quint8 hi = data[i] + hiAdd * 0xf0; quint8 hi = data[i] - (hiAdd << 4);
quint8 lo = data[i] - loAdd; quint8 lo = data[i] - loAdd;
data[i] = (hi & 0xf0) | (lo & 0x0f); data[i] = (hi & 0xf0) | (lo & 0x0f);
hiCnt = (loCnt > 6) ? 0 : loCnt + 1; hiCnt = (loCnt > 6) ? 0 : loCnt + 1;
} }
} }
static inline double toWGS(qint32 v) CryptDevice::CryptDevice(QIODevice *device, quint32 key, quint32 blockSize,
QObject *parent) : QIODevice(parent), _device(device), _key(key), _available(0)
{ {
return (double)(((double)v / (double)(1U<<31)) * (double)180); _block.resize(blockSize);
setOpenMode(_device->openMode());
}
qint64 CryptDevice::readData(char *data, qint64 maxSize)
{
qint64 rs, ts = 0;
int cs;
if (_available) {
cs = qMin(maxSize, (qint64)_available);
memcpy(data, _block.constData() + _block.size() - _available, cs);
_available -= cs;
maxSize -= cs;
ts = cs;
}
while (maxSize) {
if ((rs = _device->read(_block.data(), _block.size())) < 0)
return -1;
else if (!rs)
break;
_available = rs;
demangle((quint8*)_block.data(), _available, _key);
cs = qMin(maxSize, (qint64)_available);
memcpy(data + ts, _block.constData(), cs);
_available -= cs;
maxSize -= cs;
ts += cs;
}
return ts;
}
static qint32 readInt24(QDataStream &stream)
{
unsigned char data[3];
quint32 val;
stream.readRawData((char*)data, sizeof(data));
val = data[0] | ((quint32)data[1]) << 8 | ((quint32)data[2]) << 16;
return (val > 0x7FFFFF) ? (val & 0x7FFFFF) - 0x800000 : val;
} }
static quint16 nextHeaderType(QDataStream &stream) static quint16 nextHeaderType(QDataStream &stream)
{ {
quint16 type = 0; quint16 type = 0;
stream.device()->peek((char*)&type, sizeof(type));
return qFromLittleEndian(type); if (stream.device()->peek((char*)&type, sizeof(type))
< (qint64)sizeof(type)) {
stream.setStatus(QDataStream::ReadCorruptData);
return 0xFFFF;
} else
return qFromLittleEndian(type);
} }
static quint8 readRecordHeader(QDataStream &stream, RecordHeader &hdr) static quint8 readRecordHeader(QDataStream &stream, RecordHeader &hdr)
@ -82,18 +154,6 @@ static quint32 skipRecord(QDataStream &stream)
return rs + rh.size; return rs + rh.size;
} }
static quint32 readFprsRecord(QDataStream &stream)
{
RecordHeader rh;
quint16 s1;
quint8 rs, s2, s3, s4;
rs = readRecordHeader(stream, rh);
stream >> s1 >> s2 >> s3 >> s4;
return rs + 5;
}
static quint16 readString(QDataStream &stream, QTextCodec *codec, QString &str) static quint16 readString(QDataStream &stream, QTextCodec *codec, QString &str)
{ {
quint16 len; quint16 len;
@ -110,15 +170,16 @@ static quint32 readTranslatedObjects(QDataStream &stream, QTextCodec *codec,
QList<TranslatedString> &objects) QList<TranslatedString> &objects)
{ {
qint32 size = 0, ret; qint32 size = 0, ret;
char lang[2]; char lang[3];
memset(lang, 0, sizeof(lang));
objects.clear(); objects.clear();
stream >> size; stream >> size;
ret = size + 4; ret = size + 4;
while (size > 0) { while (stream.status() == QDataStream::Ok && size > 0) {
QString str; QString str;
stream.readRawData(lang, sizeof(lang)); stream.readRawData(lang, sizeof(lang) - 1);
size -= readString(stream, codec, str) + 2; size -= readString(stream, codec, str) + 2;
objects.append(TranslatedString(lang, str)); objects.append(TranslatedString(lang, str));
} }
@ -129,163 +190,16 @@ static quint32 readTranslatedObjects(QDataStream &stream, QTextCodec *codec,
return ret; return ret;
} }
static quint32 readDescription(QDataStream &stream, QTextCodec *codec, static quint32 readFprsRecord(QDataStream &stream)
Waypoint &waypoint)
{ {
RecordHeader rh; RecordHeader rh;
quint8 rs;
quint32 ds;
QList<TranslatedString> obj;
rs = readRecordHeader(stream, rh);
ds = readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
waypoint.setDescription(obj.first().str());
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readNotes(QDataStream &stream, QTextCodec *codec,
Waypoint &waypoint)
{
RecordHeader rh;
quint8 rs, s1;
quint32 ds = 1;
rs = readRecordHeader(stream, rh);
stream >> s1;
if (s1 & 0x1) {
QList<TranslatedString> obj;
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty() && waypoint.description().isNull())
waypoint.setDescription(obj.first().str());
}
if (s1 & 0x2) {
QString str;
ds += readString(stream, codec, str);
if (!str.isEmpty() && waypoint.description().isNull())
waypoint.setDescription(str);
}
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readContact(QDataStream &stream, QTextCodec *codec,
Waypoint &waypoint)
{
RecordHeader rh;
quint8 rs;
quint16 s1; quint16 s1;
quint32 ds = 2; quint8 rs, s2, s3, s4;
QString str;
QList<TranslatedString> obj;
rs = readRecordHeader(stream, rh); rs = readRecordHeader(stream, rh);
stream >> s1; stream >> s1 >> s2 >> s3 >> s4;
if (s1 & 0x1) // phone return rs + 5;
ds += readString(stream, codec, str);
if (s1 & 0x2) // phone2
ds += readString(stream, codec, str);
if (s1 & 0x4) // fax
ds += readString(stream, codec, str);
if (s1 & 0x8) // mail
ds += readString(stream, codec, str);
if (s1 & 0x10) { // web
ds += readString(stream, codec, str);
QUrl url(str);
waypoint.addLink(Link(url.scheme().isEmpty()
? "http://" + str : str, str));
}
if (s1 & 0x20) // unknown
ds += readTranslatedObjects(stream, codec, obj);
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readPOI(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints)
{
RecordHeader rh;
quint8 rs;
quint32 ds;
qint32 s1, s2;
quint16 s3;
QList<TranslatedString> obj;
rs = readRecordHeader(stream, rh);
stream >> s1 >> s2 >> s3;
stream.skipRawData(s3);
ds = 10 + s3;
ds += readTranslatedObjects(stream, codec, obj);
waypoints.append(Waypoint(Coordinates(toWGS(s2), toWGS(s1))));
if (!obj.isEmpty())
waypoints.last().setName(obj.first().str());
while (ds < rh.size) {
switch(nextHeaderType(stream)) {
case 10:
ds += readDescription(stream, codec, waypoints.last());
break;
case 12:
ds += readContact(stream, codec, waypoints.last());
break;
case 14:
ds += readNotes(stream, codec, waypoints.last());
break;
default:
ds += skipRecord(stream);
}
}
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readSpatialIndex(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints)
{
RecordHeader rh;
quint32 ds, s5;
qint32 top, right, bottom, left;
quint16 s6;
quint8 rs;
rs = readRecordHeader(stream, rh);
stream >> top >> right >> bottom >> left >> s5 >> s6;
stream.skipRawData(s6);
ds = 22 + s6;
if (rh.flags & 0x8) {
while (ds < rh.size) {
switch(nextHeaderType(stream)) {
case 2:
ds += readPOI(stream, codec, waypoints);
break;
case 8:
ds += readSpatialIndex(stream, codec, waypoints);
break;
default:
ds += skipRecord(stream);
}
}
}
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
} }
static quint32 readFileDataRecord(QDataStream &stream, QTextCodec *codec) static quint32 readFileDataRecord(QDataStream &stream, QTextCodec *codec)
@ -326,6 +240,29 @@ static quint32 readFileDataRecord(QDataStream &stream, QTextCodec *codec)
stream.skipRawData(ss1); stream.skipRawData(ss1);
ds += ss1 + 2; ds += ss1 + 2;
} }
// structure of higher fields not known
if (ds > rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
else if (ds < rh.size)
// skip remaining unknown fields
stream.skipRawData(rh.size - ds);
return rs + rh.size;
}
static quint32 readDescription(QDataStream &stream, QTextCodec *codec,
Waypoint &waypoint)
{
RecordHeader rh;
quint8 rs;
quint32 ds;
QList<TranslatedString> obj;
rs = readRecordHeader(stream, rh);
ds = readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
waypoint.setDescription(obj.first().str());
if (ds != rh.size) if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData); stream.setStatus(QDataStream::ReadCorruptData);
@ -333,33 +270,397 @@ static quint32 readFileDataRecord(QDataStream &stream, QTextCodec *codec)
return rs + rh.size; return rs + rh.size;
} }
bool GPIParser::readFileHeader(QDataStream &stream, quint32 &ebs) static quint32 readNotes(QDataStream &stream, QTextCodec *codec,
Waypoint &waypoint)
{ {
RecordHeader rh; RecordHeader rh;
quint32 ds, s7; quint8 rs, s1;
quint16 s10; quint32 ds = 1;
quint8 s5, s6, s8, s9;
char magic[6]; rs = readRecordHeader(stream, rh);
stream >> s1;
if (s1 & 0x1) {
QList<TranslatedString> obj;
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
waypoint.setComment(obj.first().str());
}
if (s1 & 0x2) {
QString str;
ds += readString(stream, codec, str);
if (!str.isEmpty())
waypoint.setComment(str);
}
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readContact(QDataStream &stream, QTextCodec *codec,
Waypoint &waypoint)
{
RecordHeader rh;
quint8 rs;
quint16 flags;
quint32 ds = 2;
QString str;
QList<TranslatedString> obj;
rs = readRecordHeader(stream, rh);
stream >> flags;
if (flags & 0x1) // phone
ds += readString(stream, codec, str);
if (flags & 0x2) // phone2
ds += readString(stream, codec, str);
if (flags & 0x4) // fax
ds += readString(stream, codec, str);
if (flags & 0x8) // mail
ds += readString(stream, codec, str);
if (flags & 0x10) { // web
ds += readString(stream, codec, str);
QUrl url(str);
waypoint.addLink(Link(url.scheme().isEmpty()
? "http://" + str : str, str));
}
if (flags & 0x20) // unknown
ds += readTranslatedObjects(stream, codec, obj);
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readAddress(QDataStream &stream, QTextCodec *codec,
Waypoint &waypoint)
{
RecordHeader rh;
quint8 rs;
quint16 flags;
quint32 ds = 2;
QList<TranslatedString> obj;
QString str;
Address addr;
rs = readRecordHeader(stream, rh);
stream >> flags;
if (flags & 0x1) {
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
addr.setCity(obj.first().str());
}
if (flags & 0x2) {
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
addr.setCountry(obj.first().str());
}
if (flags & 0x4) {
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
addr.setState(obj.first().str());
}
if (flags & 0x8) {
ds += readString(stream, codec, str);
addr.setPostalCode(str);
}
if (flags & 0x10) {
ds += readTranslatedObjects(stream, codec, obj);
if (!obj.isEmpty())
addr.setStreet(obj.first().str());
}
if (flags & 0x20) // unknown
ds += readString(stream, codec, str);
waypoint.setAddress(addr);
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
static const QTemporaryDir &tempDir()
{
static QTemporaryDir dir;
return dir;
}
static quint32 readImageInfo(QDataStream &stream, Waypoint &waypoint,
const QString &fileName, int &imgId)
{
RecordHeader rh;
quint8 rs, s1;
quint32 size;
rs = readRecordHeader(stream, rh);
stream >> s1 >> size;
QByteArray ba;
ba.resize(size);
stream.readRawData(ba.data(), ba.size());
if (tempDir().isValid()) {
QBuffer buf(&ba);
QImageReader ir(&buf);
QByteArray id(fileName.toUtf8() + QByteArray::number(imgId++));
QFile imgFile(tempDir().path() + "/" + QString("%0.%1").arg(
QCryptographicHash::hash(id, QCryptographicHash::Sha1).toHex(),
QString(ir.format())));
imgFile.open(QIODevice::WriteOnly);
imgFile.write(ba);
imgFile.close();
waypoint.addImage(ImageInfo(imgFile.fileName(), ir.size()));
}
if (size + 5 != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
#endif // QT_VERSION >= 5
static int speed(quint8 flags)
{
switch (flags >> 4) {
case 0x8:
return 40;
case 0x9:
return 30;
case 0xA:
return 50;
case 0xB:
return 70;
case 0xC:
return 80;
case 0xD:
return 90;
case 0xE:
return 100;
case 0xF:
return 120;
default:
return 0;
}
}
static quint32 readCamera(QDataStream &stream, QVector<Waypoint> &waypoints,
QList<Area> &polygons)
{
RecordHeader rh;
quint8 flags, type, s7, rs;
qint32 top, right, bottom, left, lat, lon;
quint32 ds = 15;
rs = readRecordHeader(stream, rh);
top = readInt24(stream);
right = readInt24(stream);
bottom = readInt24(stream);
left = readInt24(stream);
stream >> flags >> type >> s7;
if (s7) {
quint32 skip = s7 + 2 + s7/4;
stream.skipRawData(skip);
lat = readInt24(stream);
lon = readInt24(stream);
ds += skip + 6;
} else {
quint8 s8;
stream.skipRawData(9);
stream >> s8;
quint32 skip = 3 + s8 + s8/4;
stream.skipRawData(skip);
lat = readInt24(stream);
lon = readInt24(stream);
ds += skip + 16;
}
waypoints.append(Coordinates(toWGS24(lon), toWGS24(lat)));
Area area;
Polygon polygon;
QVector<Coordinates> v(4);
v[0] = Coordinates(toWGS24(left), toWGS24(top));
v[1] = Coordinates(toWGS24(right), toWGS24(top));
v[2] = Coordinates(toWGS24(right), toWGS24(bottom));
v[3] = Coordinates(toWGS24(left), toWGS24(bottom));
polygon.append(v);
area.append(polygon);
switch (type) {
case 8:
area.setDescription(QString("%1&nbsp;mi/h")
.arg(speed(flags)));
break;
case 9:
area.setDescription(QString("%1&nbsp;km/h")
.arg(speed(flags)));
break;
case 10:
case 11:
area.setDescription("Red light camera");
break;
}
polygons.append(area);
if (ds > rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
else if (ds < rh.size)
stream.skipRawData(rh.size - ds);
return rs + rh.size;
}
static quint32 readPOI(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints, const QString &fileName, int &imgId)
{
RecordHeader rh;
quint8 rs;
quint32 ds;
qint32 lat, lon;
quint16 s3;
QList<TranslatedString> obj;
rs = readRecordHeader(stream, rh);
stream >> lat >> lon >> s3;
stream.skipRawData(s3);
ds = 10 + s3;
ds += readTranslatedObjects(stream, codec, obj);
waypoints.append(Waypoint(Coordinates(toWGS32(lon), toWGS32(lat))));
if (!obj.isEmpty())
waypoints.last().setName(obj.first().str());
while (stream.status() == QDataStream::Ok && ds < rh.size) {
switch(nextHeaderType(stream)) {
case 10:
ds += readDescription(stream, codec, waypoints.last());
break;
case 11:
ds += readAddress(stream, codec, waypoints.last());
break;
case 12:
ds += readContact(stream, codec, waypoints.last());
break;
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
case 13:
ds += readImageInfo(stream, waypoints.last(), fileName, imgId);
break;
#endif // QT_VERSION >= 5
case 14:
ds += readNotes(stream, codec, waypoints.last());
break;
default:
ds += skipRecord(stream);
}
}
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static quint32 readSpatialIndex(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints, QList<Area> &polygons, const QString &fileName,
int &imgId)
{
RecordHeader rh;
quint32 ds, s5;
qint32 top, right, bottom, left;
quint16 s6;
quint8 rs;
rs = readRecordHeader(stream, rh);
stream >> top >> right >> bottom >> left >> s5 >> s6;
stream.skipRawData(s6);
ds = 22 + s6;
if (rh.flags & 0x8) {
while (stream.status() == QDataStream::Ok && ds < rh.size) {
switch(nextHeaderType(stream)) {
case 2:
ds += readPOI(stream, codec, waypoints, fileName, imgId);
break;
case 8:
ds += readSpatialIndex(stream, codec, waypoints, polygons,
fileName, imgId);
break;
case 19:
ds += readCamera(stream, waypoints, polygons);
break;
default:
ds += skipRecord(stream);
}
}
}
if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
return rs + rh.size;
}
static void readPOIDatabase(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints, QList<Area> &polygons, const QString &fileName,
int &imgId)
{
RecordHeader rh;
QList<TranslatedString> obj;
quint32 ds;
readRecordHeader(stream, rh); readRecordHeader(stream, rh);
stream.readRawData(magic, sizeof(magic)); ds = readTranslatedObjects(stream, codec, obj);
if (memcmp(magic, "GRMREC", sizeof(magic))) { ds += readSpatialIndex(stream, codec, waypoints, polygons, fileName, imgId);
_errorString = "Not a GPI file"; if (rh.flags & 0x8) {
return false; while (stream.status() == QDataStream::Ok && ds < rh.size) {
switch(nextHeaderType(stream)) {
case 5: // symbol
case 7: // category
default:
ds += skipRecord(stream);
}
}
} }
stream >> s5 >> s6 >> s7 >> s8 >> s9 >> s10;
stream.skipRawData(s10);
ds = sizeof(magic) + 10 + s10;
if (rh.flags & 8)
ds += readFprsRecord(stream);
ebs = (s8 & 0x4) ? s9 * 8 + 8 : 0; if (ds != rh.size)
stream.setStatus(QDataStream::ReadCorruptData);
}
if (stream.status() != QDataStream::Ok || ds != rh.size) { bool GPIParser::readData(QDataStream &stream, QTextCodec *codec,
_errorString = "Invalid file header"; QVector<Waypoint> &waypoints, QList<Area> &polygons, const QString &fileName)
return false; {
} else int imgId = 0;
return true;
while (stream.status() == QDataStream::Ok) {
switch (nextHeaderType(stream)) {
case 0x09: // POI database
readPOIDatabase(stream, codec, waypoints, polygons, fileName,
imgId);
break;
case 0xffff: // EOF
skipRecord(stream);
if (stream.status() == QDataStream::Ok)
return true;
break;
case 0x16: // route
case 0x15: // info header
default:
skipRecord(stream);
}
}
_errorString = "Invalid/corrupted GPI data";
return false;
} }
bool GPIParser::readGPIHeader(QDataStream &stream, QTextCodec **codec) bool GPIParser::readGPIHeader(QDataStream &stream, QTextCodec **codec)
@ -394,58 +695,33 @@ bool GPIParser::readGPIHeader(QDataStream &stream, QTextCodec **codec)
return true; return true;
} }
void GPIParser::readPOIDatabase(QDataStream &stream, QTextCodec *codec, bool GPIParser::readFileHeader(QDataStream &stream, quint32 &ebs)
QVector<Waypoint> &waypoints)
{ {
RecordHeader rh; RecordHeader rh;
QList<TranslatedString> obj; quint32 ds, s7;
quint32 ds; quint16 s10;
quint8 s5, s6, s8, s9;
char magic[6];
readRecordHeader(stream, rh); readRecordHeader(stream, rh);
ds = readTranslatedObjects(stream, codec, obj); stream.readRawData(magic, sizeof(magic));
ds += readSpatialIndex(stream, codec, waypoints); if (memcmp(magic, "GRMREC", sizeof(magic))) {
if (rh.flags & 0x8) { _errorString = "Not a GPI file";
while (ds < rh.size) { return false;
switch(nextHeaderType(stream)) {
case 5: // symbol
case 7: // category
default:
ds += skipRecord(stream);
}
}
} }
stream >> s5 >> s6 >> s7 >> s8 >> s9 >> s10;
stream.skipRawData(s10);
ds = sizeof(magic) + 10 + s10;
if (rh.flags & 8)
ds += readFprsRecord(stream);
if (ds != rh.size) ebs = (s8 & 0x4) ? s9 * 8 + 8 : 0;
stream.setStatus(QDataStream::ReadCorruptData);
}
bool GPIParser::readEntry(QDataStream &stream, QTextCodec *codec, if (stream.status() != QDataStream::Ok || ds != rh.size) {
QVector<Waypoint> &waypoints) _errorString = "Invalid file header";
{ return false;
switch (nextHeaderType(stream)) { } else
case 0x09: // POI database return true;
readPOIDatabase(stream, codec, waypoints);
break;
case 0xffff: // EOF
skipRecord(stream);
return false;
case 0x16: // route
case 0x15: // info header
default:
skipRecord(stream);
}
return true;
}
bool GPIParser::readData(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints)
{
while (stream.status() == QDataStream::Ok)
if (!readEntry(stream, codec, waypoints))
return stream.atEnd();
return false;
} }
bool GPIParser::parse(QFile *file, QList<TrackData> &tracks, bool GPIParser::parse(QFile *file, QList<TrackData> &tracks,
@ -453,11 +729,9 @@ bool GPIParser::parse(QFile *file, QList<TrackData> &tracks,
{ {
Q_UNUSED(tracks); Q_UNUSED(tracks);
Q_UNUSED(routes); Q_UNUSED(routes);
Q_UNUSED(polygons);
QDataStream stream(file); QDataStream stream(file);
QTextCodec *codec = 0; QTextCodec *codec = 0;
quint32 ebs; quint32 ebs;
bool ret;
stream.setByteOrder(QDataStream::LittleEndian); stream.setByteOrder(QDataStream::LittleEndian);
@ -465,24 +739,11 @@ bool GPIParser::parse(QFile *file, QList<TrackData> &tracks,
return false; return false;
if (ebs) { if (ebs) {
QByteArray ba(stream.device()->readAll()); CryptDevice dev(stream.device(), 0xf870b5, ebs);
for (int i = 0; i < (ba.size() / (int)ebs); i++) QDataStream cryptStream(&dev);
demangle((quint8*)(ba.data() + i * ebs), ebs, BLOCK_KEY); cryptStream.setByteOrder(QDataStream::LittleEndian);
demangle((quint8*)(ba.data() + (ba.size() / (int)ebs) * ebs), return readData(cryptStream, codec, waypoints, polygons,
ba.size() - ((ba.size() / (int)ebs) * ebs), BLOCK_KEY); file->fileName());
QBuffer buffer(&ba);
buffer.open(QIODevice::ReadOnly);
QDataStream memStream(&buffer);
memStream.setByteOrder(QDataStream::LittleEndian);
ret = readData(memStream, codec, waypoints);
} else } else
ret = readData(stream, codec, waypoints); return readData(stream, codec, waypoints, polygons, file->fileName());
if (!ret) {
_errorString = "Invalid/corrupted GPI data";
return false;
} else
return true;
} }

View File

@ -18,11 +18,8 @@ private:
bool readFileHeader(QDataStream &stream, quint32 &ebs); bool readFileHeader(QDataStream &stream, quint32 &ebs);
bool readGPIHeader(QDataStream &stream, QTextCodec **codec); bool readGPIHeader(QDataStream &stream, QTextCodec **codec);
bool readData(QDataStream &stream, QTextCodec *codec, bool readData(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints); QVector<Waypoint> &waypoints, QList<Area> &polygons,
bool readEntry(QDataStream &stream, QTextCodec *codec, const QString &fileName);
QVector<Waypoint> &waypoints);
void readPOIDatabase(QDataStream &stream, QTextCodec *codec,
QVector<Waypoint> &waypoints);
QString _errorString; QString _errorString;
}; };

View File

@ -69,8 +69,10 @@ Coordinates GPXParser::coordinates()
void GPXParser::rpExtension(SegmentData *autoRoute) void GPXParser::rpExtension(SegmentData *autoRoute)
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("rpt")) if (_reader.name() == QLatin1String("rpt")) {
autoRoute->append(Trackpoint(coordinates())); if (autoRoute)
autoRoute->append(Trackpoint(coordinates()));
}
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
} }
@ -91,11 +93,45 @@ void GPXParser::tpExtension(Trackpoint &trackpoint)
} }
} }
void GPXParser::rteptExtensions(SegmentData *autoRoute) void GPXParser::address(Waypoint &waypoint)
{
Address addr;
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("StreetAddress"))
addr.setStreet(_reader.readElementText());
else if (_reader.name() == QLatin1String("City"))
addr.setCity(_reader.readElementText());
else if (_reader.name() == QLatin1String("PostalCode"))
addr.setPostalCode(_reader.readElementText());
else if (_reader.name() == QLatin1String("State"))
addr.setState(_reader.readElementText());
else if (_reader.name() == QLatin1String("Country"))
addr.setCountry(_reader.readElementText());
else
_reader.skipCurrentElement();
}
waypoint.setAddress(addr);
}
void GPXParser::wpExtension(Waypoint &waypoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Address"))
address(waypoint);
else
_reader.skipCurrentElement();
}
}
void GPXParser::waypointExtensions(Waypoint &waypoint, SegmentData *autoRoute)
{ {
while (_reader.readNextStartElement()) { while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("RoutePointExtension")) if (_reader.name() == QLatin1String("RoutePointExtension"))
rpExtension(autoRoute); rpExtension(autoRoute);
else if (_reader.name() == QLatin1String("WaypointExtension"))
wpExtension(waypoint);
else else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
@ -156,6 +192,8 @@ void GPXParser::waypointData(Waypoint &waypoint, SegmentData *autoRoute)
waypoint.setName(_reader.readElementText()); waypoint.setName(_reader.readElementText());
else if (_reader.name() == QLatin1String("desc")) else if (_reader.name() == QLatin1String("desc"))
waypoint.setDescription(_reader.readElementText()); waypoint.setDescription(_reader.readElementText());
else if (_reader.name() == QLatin1String("cmt"))
waypoint.setComment(_reader.readElementText());
else if (_reader.name() == QLatin1String("ele")) else if (_reader.name() == QLatin1String("ele"))
waypoint.setElevation(number()); waypoint.setElevation(number());
else if (_reader.name() == QLatin1String("geoidheight")) else if (_reader.name() == QLatin1String("geoidheight"))
@ -170,8 +208,8 @@ void GPXParser::waypointData(Waypoint &waypoint, SegmentData *autoRoute)
link10.setURL(_reader.readElementText()); link10.setURL(_reader.readElementText());
else if (_reader.name() == QLatin1String("urlname")) else if (_reader.name() == QLatin1String("urlname"))
link10.setText(_reader.readElementText()); link10.setText(_reader.readElementText());
else if (autoRoute && _reader.name() == QLatin1String("extensions")) else if (_reader.name() == QLatin1String("extensions"))
rteptExtensions(autoRoute); waypointExtensions(waypoint, autoRoute);
else else
_reader.skipCurrentElement(); _reader.skipCurrentElement();
} }
@ -208,6 +246,8 @@ void GPXParser::routepoints(RouteData &route, QList<TrackData> &tracks)
route.setName(_reader.readElementText()); route.setName(_reader.readElementText());
else if (_reader.name() == QLatin1String("desc")) else if (_reader.name() == QLatin1String("desc"))
route.setDescription(_reader.readElementText()); route.setDescription(_reader.readElementText());
else if (_reader.name() == QLatin1String("cmt"))
route.setComment(_reader.readElementText());
else if (_reader.name() == QLatin1String("link")) { else if (_reader.name() == QLatin1String("link")) {
Link l(link()); Link l(link());
if (!l.URL().isEmpty()) if (!l.URL().isEmpty())
@ -242,6 +282,8 @@ void GPXParser::track(TrackData &track)
track.setName(_reader.readElementText()); track.setName(_reader.readElementText());
else if (_reader.name() == QLatin1String("desc")) else if (_reader.name() == QLatin1String("desc"))
track.setDescription(_reader.readElementText()); track.setDescription(_reader.readElementText());
else if (_reader.name() == QLatin1String("cmt"))
track.setComment(_reader.readElementText());
else if (_reader.name() == QLatin1String("link")) { else if (_reader.name() == QLatin1String("link")) {
Link l(link()); Link l(link());
if (!l.URL().isEmpty()) if (!l.URL().isEmpty())

View File

@ -21,11 +21,13 @@ private:
void rpExtension(SegmentData *autoRoute); void rpExtension(SegmentData *autoRoute);
void tpExtension(Trackpoint &trackpoint); void tpExtension(Trackpoint &trackpoint);
void trkptExtensions(Trackpoint &trackpoint); void trkptExtensions(Trackpoint &trackpoint);
void rteptExtensions(SegmentData *autoRoute); void wpExtension(Waypoint &waypoint);
void waypointExtensions(Waypoint &waypoint, SegmentData *autoRoute);
void area(Area &area); void area(Area &area);
void gpxExtensions(QList<Area> &areas); void gpxExtensions(QList<Area> &areas);
void trackpointData(Trackpoint &trackpoint); void trackpointData(Trackpoint &trackpoint);
void waypointData(Waypoint &waypoint, SegmentData *autoRoute = 0); void waypointData(Waypoint &waypoint, SegmentData *autoRoute = 0);
void address(Waypoint &waypoint);
qreal number(); qreal number();
QDateTime time(); QDateTime time();
Coordinates coordinates(); Coordinates coordinates();

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 #endif // GRAPH_H

View File

@ -87,16 +87,18 @@ static bool readTimestamp(const char *data, QTime &time)
static bool readARecord(const char *line, qint64 len) 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; return false;
for (int i = 1; i < 7; i++) for (int i = 1; i < 6; i++)
if (!::isprint(line[i])) if (!::isprint(line[i]))
return false; return false;
return true; return true;
} }
bool IGCParser::readHRecord(const char *line, int len) bool IGCParser::readHRecord(CTX &ctx, const char *line, int len)
{ {
if (len < 11 || ::strncmp(line, "HFDTE", 5)) if (len < 11 || ::strncmp(line, "HFDTE", 5))
return true; return true;
@ -112,9 +114,9 @@ bool IGCParser::readHRecord(const char *line, int len)
return false; return false;
} }
_date = QDate(y + 2000 <= QDate::currentDate().year() ? 2000 + y : 1900 + y, ctx.date = QDate(y + 2000 <= QDate::currentDate().year()
m, d); ? 2000 + y : 1900 + y, m, d);
if (!_date.isValid()) { if (!ctx.date.isValid()) {
_errorString = "Invalid date"; _errorString = "Invalid date";
return false; return false;
} }
@ -122,8 +124,8 @@ bool IGCParser::readHRecord(const char *line, int len)
return true; return true;
} }
bool IGCParser::readBRecord(SegmentData &segment, const char *line, bool IGCParser::readBRecord(CTX &ctx, const char *line, int len,
int len) SegmentData &segment)
{ {
qreal lat, lon, ele; qreal lat, lon, ele;
QTime time; QTime time;
@ -152,20 +154,20 @@ bool IGCParser::readBRecord(SegmentData &segment, const char *line,
return false; return false;
} }
if (time < _time && !segment.isEmpty() if (time < ctx.time && !segment.isEmpty()
&& _date == segment.last().timestamp().date()) && ctx.date == segment.last().timestamp().date())
_date = _date.addDays(1); ctx.date = ctx.date.addDays(1);
_time = time; ctx.time = time;
Trackpoint t(Coordinates(lon, lat)); Trackpoint t(Coordinates(lon, lat));
t.setTimestamp(QDateTime(_date, _time, Qt::UTC)); t.setTimestamp(QDateTime(ctx.date, ctx.time, Qt::UTC));
t.setElevation(ele); t.setElevation(ele);
segment.append(t); segment.append(t);
return true; return true;
} }
bool IGCParser::readCRecord(RouteData &route, const char *line, int len) bool IGCParser::readCRecord(const char *line, int len, RouteData &route)
{ {
qreal lat, lon; qreal lat, lon;
@ -202,6 +204,7 @@ bool IGCParser::parse(QFile *file, QList<TrackData> &tracks,
qint64 len; qint64 len;
char line[76 + 2 + 1 + 1]; char line[76 + 2 + 1 + 1];
bool route = false, track = false; bool route = false, track = false;
CTX ctx;
_errorLine = 1; _errorLine = 1;
@ -225,28 +228,28 @@ bool IGCParser::parse(QFile *file, QList<TrackData> &tracks,
} }
} else { } else {
if (line[0] == 'H') { if (line[0] == 'H') {
if (!readHRecord(line, len)) if (!readHRecord(ctx, line, len))
return false; return false;
} else if (line[0] == 'C') { } else if (line[0] == 'C') {
if (route) { if (route) {
if (!readCRecord(routes.last() ,line, len)) if (!readCRecord(line, len, routes.last()))
return false; return false;
} else { } else {
route = true; route = true;
routes.append(RouteData()); routes.append(RouteData());
} }
} else if (line[0] == 'B') { } else if (line[0] == 'B') {
if (_date.isNull()) { if (ctx.date.isNull()) {
_errorString = "Missing date header"; _errorString = "Missing date header";
return false; return false;
} }
if (!track) { if (!track) {
tracks.append(TrackData()); tracks.append(TrackData());
tracks.last().append(SegmentData()); tracks.last().append(SegmentData());
_time = QTime(0, 0); ctx.time = QTime(0, 0);
track = true; track = true;
} }
if (!readBRecord(tracks.last().last(), line, len)) if (!readBRecord(ctx, line, len, tracks.last().last()))
return false; return false;
} }
} }

View File

@ -17,15 +17,17 @@ public:
int errorLine() const {return _errorLine;} int errorLine() const {return _errorLine;}
private: private:
bool readHRecord(const char *line, int len); struct CTX {
bool readBRecord(SegmentData &segment, const char *line, int len); QDate date;
bool readCRecord(RouteData &route, const char *line, int len); QTime time;
};
bool readHRecord(CTX &ctx, const char *line, int len);
bool readBRecord(CTX &ctx, const char *line, int len, SegmentData &segment);
bool readCRecord(const char *line, int len, RouteData &route);
int _errorLine; int _errorLine;
QString _errorString; QString _errorString;
QDate _date;
QTime _time;
}; };
#endif // IGCPARSER_H #endif // IGCPARSER_H

View File

@ -7,15 +7,15 @@ class Link {
public: public:
Link() {} Link() {}
Link(const QString &URL, const QString &text = QString()) Link(const QString &URL, const QString &text = QString())
: _URL(URL), _text(text) {} : _url(URL), _text(text) {}
void setURL(const QString &URL) {_URL = URL;} void setURL(const QString &URL) {_url = URL;}
void setText(const QString &text) {_text = text;} void setText(const QString &text) {_text = text;}
const QString &URL() const {return _URL;} const QString &URL() const {return _url;}
const QString &text() const {return _text;} const QString &text() const {return _text;}
private: private:
QString _URL; QString _url;
QString _text; QString _text;
}; };

View File

@ -227,7 +227,8 @@ bool NMEAParser::readEW(const char *data, int len, qreal &lon)
return true; return true;
} }
bool NMEAParser::readRMC(SegmentData &segment, const char *line, int len) bool NMEAParser::readRMC(CTX &ctx, const char *line, int len,
SegmentData &segment)
{ {
int col = 1; int col = 1;
const char *vp = line; const char *vp = line;
@ -280,23 +281,24 @@ bool NMEAParser::readRMC(SegmentData &segment, const char *line, int len)
} }
if (!date.isNull()) { if (!date.isNull()) {
if (_date.isNull() && !_time.isNull() && !segment.isEmpty()) if (ctx.date.isNull() && !ctx.time.isNull() && !segment.isEmpty())
segment.last().setTimestamp(QDateTime(date, _time, Qt::UTC)); segment.last().setTimestamp(QDateTime(date, ctx.time, Qt::UTC));
_date = date; ctx.date = date;
} }
Coordinates c(lon, lat); Coordinates c(lon, lat);
if (valid && !_GGA && c.isValid()) { if (valid && !ctx.GGA && c.isValid()) {
Trackpoint t(c); Trackpoint t(c);
if (!_date.isNull() && !time.isNull()) if (!ctx.date.isNull() && !time.isNull())
t.setTimestamp(QDateTime(_date, time, Qt::UTC)); t.setTimestamp(QDateTime(ctx.date, time, Qt::UTC));
segment.append(t); segment.append(t);
} }
return true; return true;
} }
bool NMEAParser::readGGA(SegmentData &segment, const char *line, int len) bool NMEAParser::readGGA(CTX &ctx, const char *line, int len,
SegmentData &segment)
{ {
int col = 1; int col = 1;
const char *vp = line; const char *vp = line;
@ -306,7 +308,7 @@ bool NMEAParser::readGGA(SegmentData &segment, const char *line, int len)
if (*lp == ',' || *lp == '*') { if (*lp == ',' || *lp == '*') {
switch (col) { switch (col) {
case 1: case 1:
if (!readTime(vp, lp - vp, _time)) if (!readTime(vp, lp - vp, ctx.time))
return false; return false;
break; break;
case 2: case 2:
@ -360,19 +362,19 @@ bool NMEAParser::readGGA(SegmentData &segment, const char *line, int len)
Coordinates c(lon, lat); Coordinates c(lon, lat);
if (c.isValid()) { if (c.isValid()) {
Trackpoint t(c); Trackpoint t(c);
if (!(_time.isNull() || _date.isNull())) if (!(ctx.time.isNull() || ctx.date.isNull()))
t.setTimestamp(QDateTime(_date, _time, Qt::UTC)); t.setTimestamp(QDateTime(ctx.date, ctx.time, Qt::UTC));
if (!std::isnan(ele)) if (!std::isnan(ele))
t.setElevation(ele - gh); t.setElevation(ele - gh);
segment.append(t); segment.append(t);
_GGA = true; ctx.GGA = true;
} }
return true; return true;
} }
bool NMEAParser::readWPL(QVector<Waypoint> &waypoints, const char *line, int len) bool NMEAParser::readWPL(const char *line, int len, QVector<Waypoint> &waypoints)
{ {
int col = 1; int col = 1;
const char *vp = line; const char *vp = line;
@ -423,7 +425,7 @@ bool NMEAParser::readWPL(QVector<Waypoint> &waypoints, const char *line, int len
return true; return true;
} }
bool NMEAParser::readZDA(const char *line, int len) bool NMEAParser::readZDA(CTX &ctx, const char *line, int len)
{ {
int col = 1; int col = 1;
const char *vp = line; const char *vp = line;
@ -468,8 +470,8 @@ bool NMEAParser::readZDA(const char *line, int len)
return false; return false;
} }
_date = QDate(y, m, d); ctx.date = QDate(y, m, d);
if (!_date.isValid()) { if (!ctx.date.isValid()) {
_errorString = "Invalid date"; _errorString = "Invalid date";
return false; return false;
} }
@ -486,13 +488,11 @@ bool NMEAParser::parse(QFile *file, QList<TrackData> &tracks,
qint64 len; qint64 len;
char line[80 + 2 + 1 + 1]; char line[80 + 2 + 1 + 1];
SegmentData segment; SegmentData segment;
CTX ctx;
_errorLine = 1; _errorLine = 1;
_errorString.clear(); _errorString.clear();
_date = QDate();
_time = QTime();
_GGA = false;
while (!file->atEnd()) { while (!file->atEnd()) {
len = file->readLine(line, sizeof(line)); len = file->readLine(line, sizeof(line));
@ -507,16 +507,16 @@ bool NMEAParser::parse(QFile *file, QList<TrackData> &tracks,
if (validSentence(line, len)) { if (validSentence(line, len)) {
if (!memcmp(line + 3, "RMC,", 4)) { if (!memcmp(line + 3, "RMC,", 4)) {
if (!readRMC(segment, line + 7, len - 7)) if (!readRMC(ctx, line + 7, len - 7, segment))
return false; return false;
} else if (!memcmp(line + 3, "GGA,", 4)) { } else if (!memcmp(line + 3, "GGA,", 4)) {
if (!readGGA(segment, line + 7, len - 7)) if (!readGGA(ctx, line + 7, len - 7, segment))
return false; return false;
} else if (!memcmp(line + 3, "WPL,", 4)) { } else if (!memcmp(line + 3, "WPL,", 4)) {
if (!readWPL(waypoints, line + 7, len - 7)) if (!readWPL(line + 7, len - 7, waypoints))
return false; return false;
} else if (!memcmp(line + 3, "ZDA,", 4)) { } else if (!memcmp(line + 3, "ZDA,", 4)) {
if (!readZDA(line + 7, len - 7)) if (!readZDA(ctx, line + 7, len - 7))
return false; return false;
} }
} }

View File

@ -8,7 +8,7 @@
class NMEAParser : public Parser class NMEAParser : public Parser
{ {
public: public:
NMEAParser() : _errorLine(0), _GGA(false) {} NMEAParser() : _errorLine(0) {}
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes, bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Area> &polygons, QVector<Waypoint> &waypoints); QList<Area> &polygons, QVector<Waypoint> &waypoints);
@ -16,6 +16,14 @@ public:
int errorLine() const {return _errorLine;} int errorLine() const {return _errorLine;}
private: private:
struct CTX {
CTX() : GGA(false) {}
QDate date;
QTime time;
bool GGA;
};
bool readEW(const char *data, int len, qreal &lon); bool readEW(const char *data, int len, qreal &lon);
bool readLon(const char *data, int len, qreal &lon); bool readLon(const char *data, int len, qreal &lon);
bool readNS(const char *data, int len, qreal &lat); bool readNS(const char *data, int len, qreal &lat);
@ -25,17 +33,13 @@ private:
bool readAltitude(const char *data, int len, qreal &ele); bool readAltitude(const char *data, int len, qreal &ele);
bool readGeoidHeight(const char *data, int len, qreal &gh); bool readGeoidHeight(const char *data, int len, qreal &gh);
bool readRMC(SegmentData &segment, const char *line, int len); bool readRMC(CTX &ctx, const char *line, int len, SegmentData &segment);
bool readGGA(SegmentData &segment, const char *line, int len); bool readGGA(CTX &ctx, const char *line, int len, SegmentData &segment);
bool readWPL(QVector<Waypoint> &waypoints, const char *line, int len); bool readWPL(const char *line, int len, QVector<Waypoint> &waypoints);
bool readZDA(const char *line, int len); bool readZDA(CTX &ctx, const char *line, int len);
int _errorLine; int _errorLine;
QString _errorString; QString _errorString;
QDate _date;
QTime _time;
bool _GGA;
}; };
#endif // NMEAPARSER_H #endif // NMEAPARSER_H

View File

@ -14,28 +14,19 @@ POI::POI(QObject *parent) : QObject(parent)
{ {
_errorLine = 0; _errorLine = 0;
_radius = 1000; _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; FileIndex index;
index.enabled = true; index.enabled = true;
index.start = _data.size(); index.start = _data.size();
if (!data.isValid()) { if (!data.isValid()) {
if (dir) { _errorString = data.errorString();
if (data.errorLine()) _errorLine = 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();
}
return false; return false;
} }
@ -59,37 +50,22 @@ bool POI::loadFile(const QString &path, bool dir)
return true; return true;
} }
bool POI::loadFile(const QString &path) void POI::loadDir(const QString &path)
{
_errorString.clear();
_errorLine = 0;
return loadFile(path, false);
}
bool POI::loadDir(const QString &path)
{ {
QDir md(path); QDir md(path);
md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
QFileInfoList fl = md.entryInfoList(); QFileInfoList fl = md.entryInfoList();
bool ret = true;
_errorString.clear();
_errorLine = 0;
for (int i = 0; i < fl.size(); i++) { for (int i = 0; i < fl.size(); i++) {
const QFileInfo &fi = fl.at(i); const QFileInfo &fi = fl.at(i);
if (fi.isDir()) { if (fi.isDir())
if (!loadDir(fi.absoluteFilePath())) loadDir(fi.absoluteFilePath());
ret = false; else
} else { if (!loadFile(fi.absoluteFilePath()))
if (!loadFile(fi.absoluteFilePath(), true)) qWarning(qPrintable(fi.absoluteFilePath() + ": "
ret = false; + _errorString));
}
} }
return ret;
} }
static bool cb(size_t data, void* context) 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); _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> POI::points(const Path &path) const
{ {
QList<Waypoint> ret; QList<Waypoint> ret;
@ -158,8 +123,6 @@ QList<Waypoint> POI::points(const Path &path) const
for (it = set.constBegin(); it != set.constEnd(); ++it) for (it = set.constBegin(); it != set.constEnd(); ++it)
ret.append(_data.at(*it)); ret.append(_data.at(*it));
appendElevation(ret);
return ret; return ret;
} }
@ -181,8 +144,6 @@ QList<Waypoint> POI::points(const Waypoint &point) const
for (it = set.constBegin(); it != set.constEnd(); ++it) for (it = set.constBegin(); it != set.constEnd(); ++it)
ret.append(_data.at(*it)); ret.append(_data.at(*it));
appendElevation(ret);
return ret; return ret;
} }
@ -206,8 +167,6 @@ QList<Waypoint> POI::points(const Area &area) const
for (it = set.constBegin(); it != set.constEnd(); ++it) for (it = set.constBegin(); it != set.constEnd(); ++it)
ret.append(_data.at(*it)); ret.append(_data.at(*it));
appendElevation(ret);
return ret; return ret;
} }
@ -253,10 +212,3 @@ void POI::setRadius(unsigned radius)
emit pointsChanged(); emit pointsChanged();
} }
void POI::useDEM(bool use)
{
_useDEM = use;
emit pointsChanged();
}

View File

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

View File

@ -1,5 +1,8 @@
#include "dem.h"
#include "route.h" #include "route.h"
bool Route::_useDEM = false;
bool Route::_show2ndElevation = false;
Route::Route(const RouteData &data) : _data(data) Route::Route(const RouteData &data) : _data(data)
{ {
@ -25,7 +28,7 @@ Path Route::path() const
return ret; return ret;
} }
Graph Route::elevation() const Graph Route::gpsElevation() const
{ {
Graph graph; Graph graph;
graph.append(GraphSegment()); graph.append(GraphSegment());
@ -38,6 +41,38 @@ Graph Route::elevation() const
return graph; 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 qreal Route::distance() const
{ {
return (_distance.isEmpty()) ? 0 : _distance.last(); return (_distance.isEmpty()) ? 0 : _distance.last();

View File

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

View File

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

163
src/data/smlparser.cpp Normal file
View File

@ -0,0 +1,163 @@
#include "smlparser.h"
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const SMLParser::Sensors &sensors)
{
dbg.nospace() << "Sensors(" << sensors.cadence << ", "
<< sensors.temperature << ", " << sensors.hr << "," << sensors.power
<< ", " << sensors.speed << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG
void SMLParser::sample(SegmentData &segment, QMap<QDateTime, Sensors> &map)
{
QDateTime timestamp;
Sensors sensors;
qreal lat = NAN, lon = NAN, altitude = NAN;
bool ok, periodic = false;
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Latitude")) {
lat = _reader.readElementText().toDouble(&ok);
if (!ok || lat < -90 || lon > 90) {
_reader.raiseError("Invalid Latitude");
return;
}
} else if (_reader.name() == QLatin1String("Longitude")) {
lon = _reader.readElementText().toDouble(&ok);
if (!ok || lat < -180 || lon > 180) {
_reader.raiseError("Invalid Longitude");
return;
}
} else if (_reader.name() == QLatin1String("UTC")) {
timestamp = QDateTime::fromString(_reader.readElementText(),
Qt::ISODate);
if (!timestamp.isValid()) {
_reader.raiseError("Invalid timestamp");
return;
}
} else if (_reader.name() == QLatin1String("GPSAltitude")) {
altitude = _reader.readElementText().toDouble(&ok);
if (!ok) {
_reader.raiseError("Invalid GPS altitude");
return;
}
} else if (_reader.name() == QLatin1String("SampleType")) {
if (_reader.readElementText() == "periodic")
periodic = true;
} else if (_reader.name() == QLatin1String("Cadence")) {
sensors.cadence = _reader.readElementText().toDouble(&ok);
if (!ok || sensors.cadence < 0) {
_reader.raiseError("Invalid Cadence");
return;
}
} else if (_reader.name() == QLatin1String("Temperature")) {
sensors.temperature = _reader.readElementText().toDouble(&ok);
// Temperature is in Kelvin units
if (!ok || sensors.temperature < 0) {
_reader.raiseError("Invalid Temperature");
return;
}
} else if (_reader.name() == QLatin1String("HR")) {
sensors.hr = _reader.readElementText().toDouble(&ok);
if (!ok || sensors.hr < 0) {
_reader.raiseError("Invalid HR");
return;
}
} else if (_reader.name() == QLatin1String("BikePower")) {
sensors.power = _reader.readElementText().toDouble(&ok);
if (!ok || sensors.power < 0) {
_reader.raiseError("Invalid BikePower");
return;
}
} else if (_reader.name() == QLatin1String("Speed")) {
sensors.speed = _reader.readElementText().toDouble(&ok);
if (!ok || sensors.speed < 0) {
_reader.raiseError("Invalid Speed");
return;
}
} else
_reader.skipCurrentElement();
}
if (periodic && timestamp.isValid())
map.insert(timestamp, sensors);
else if (!(std::isnan(lon) || std::isnan(lat))) {
Trackpoint t(Coordinates(rad2deg(lon), rad2deg(lat)));
t.setTimestamp(timestamp);
t.setElevation(altitude);
segment.append(t);
}
}
void SMLParser::samples(SegmentData &segment)
{
QMap<QDateTime, Sensors> sensors;
QMap<QDateTime, Sensors>::const_iterator it;
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Sample")) {
sample(segment, sensors);
} else
_reader.skipCurrentElement();
}
for (int i = 0; i < segment.size(); i++) {
Trackpoint &t = segment[i];
if ((it = sensors.lowerBound(t.timestamp())) != sensors.constEnd()) {
t.setCadence(it->cadence * 60);
t.setTemperature(it->temperature - 273.15);
t.setHeartRate(it->hr * 60);
t.setPower(it->power);
t.setSpeed(it->speed);
}
}
}
void SMLParser::deviceLog(TrackData &track)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("Samples")) {
track.append(SegmentData());
samples(track.last());
} else
_reader.skipCurrentElement();
}
}
void SMLParser::sml(QList<TrackData> &tracks)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("DeviceLog")) {
tracks.append(TrackData());
deviceLog(tracks.last());
} else
_reader.skipCurrentElement();
}
}
bool SMLParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Area> &polygons,
QVector<Waypoint> &waypoints)
{
Q_UNUSED(routes);
Q_UNUSED(polygons);
Q_UNUSED(waypoints);
_reader.clear();
_reader.setDevice(file);
if (_reader.readNextStartElement()) {
if (_reader.name() == QLatin1String("sml"))
sml(tracks);
else
_reader.raiseError("Not a SML file");
}
return !_reader.error();
}

38
src/data/smlparser.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef SMLPARSER_H
#define SMLPARSER_H
#include <QXmlStreamReader>
#include <QMap>
#include <QDebug>
#include "parser.h"
class SMLParser : public Parser
{
public:
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Area> &polygons, QVector<Waypoint> &waypoints);
QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();}
private:
struct Sensors
{
Sensors()
: cadence(NAN), temperature(NAN), hr(NAN), power(NAN), speed(NAN) {}
qreal cadence, temperature, hr, power, speed;
};
void sml(QList<TrackData> &tracks);
void deviceLog(TrackData &track);
void samples(SegmentData &segment);
void sample(SegmentData &segment, QMap<QDateTime, Sensors> &map);
#ifndef QT_NO_DEBUG
friend QDebug operator<<(QDebug dbg, const Sensors &sensors);
#endif // QT_NO_DEBUG
QXmlStreamReader _reader;
};
#endif // SMLPARSER_H

View File

@ -1,3 +1,4 @@
#include "dem.h"
#include "track.h" #include "track.h"
@ -7,13 +8,27 @@ int Track::_heartRateWindow = 3;
int Track::_cadenceWindow = 3; int Track::_cadenceWindow = 3;
int Track::_powerWindow = 3; int Track::_powerWindow = 3;
bool Track::_automaticPause = true;
qreal Track::_pauseSpeed = 0.5; qreal Track::_pauseSpeed = 0.5;
int Track::_pauseInterval = 10; int Track::_pauseInterval = 10;
bool Track::_outlierEliminate = true; bool Track::_outlierEliminate = true;
bool Track::_useReportedSpeed = false; bool Track::_useReportedSpeed = false;
bool Track::_useDEM = false;
bool Track::_show2ndElevation = false;
bool Track::_show2ndSpeed = false;
static qreal avg(const QVector<qreal> &v)
{
qreal sum = 0;
for (int i = 0; i < v.size(); i++)
sum += v.at(i);
return sum/v.size();
}
static qreal median(QVector<qreal> &v) static qreal median(QVector<qreal> &v)
{ {
qSort(v.begin(), v.end()); qSort(v.begin(), v.end());
@ -24,8 +39,7 @@ static qreal MAD(QVector<qreal> &v, qreal m)
{ {
for (int i = 0; i < v.size(); i++) for (int i = 0; i < v.size(); i++)
v[i] = qAbs(v.at(i) - m); v[i] = qAbs(v.at(i) - m);
qSort(v.begin(), v.end()); return median(v);
return v.at(v.size() / 2);
} }
static QSet<int> eliminate(const QVector<qreal> &v) static QSet<int> eliminate(const QVector<qreal> &v)
@ -37,7 +51,7 @@ static QSet<int> eliminate(const QVector<qreal> &v)
qreal M = MAD(w, m); qreal M = MAD(w, m);
for (int i = 0; i < v.size(); i++) 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) > 3.5)
rm.insert(i); rm.insert(i);
return rm; return rm;
@ -132,17 +146,39 @@ Track::Track(const TrackData &data) : _data(data), _pause(0)
} }
} }
if (!hasTime)
continue;
// get stop-points + pause duration // get stop-points + pause duration
int pauseInterval;
qreal pauseSpeed;
if (_automaticPause) {
pauseSpeed = (avg(seg.speed) > 2.8) ? 0.40 : 0.15;
pauseInterval = 10;
} else {
pauseSpeed = _pauseSpeed;
pauseInterval = _pauseInterval;
}
int ss = 0, la = 0;
for (int j = 1; j < seg.time.size(); j++) { for (int j = 1; j < seg.time.size(); j++) {
if (seg.time.at(j) > seg.time.at(j-1) + _pauseInterval if (seg.speed.at(j) > pauseSpeed)
&& seg.speed.at(j) < _pauseSpeed) { ss = -1;
_pause += seg.time.at(j) - seg.time.at(j-1); else if (ss < 0)
seg.stop.insert(j-1); ss = j-1;
seg.stop.insert(j);
if (ss >= 0 && seg.time.at(j) > seg.time.at(ss) + pauseInterval) {
int l = qMax(ss, la);
_pause += seg.time.at(j) - seg.time.at(l);
for (int k = l; k <= j; k++)
seg.stop.insert(k);
la = j;
} }
} }
if (!_outlierEliminate || !hasTime) if (!_outlierEliminate)
continue; continue;
@ -181,7 +217,7 @@ Track::Track(const TrackData &data) : _data(data), _pause(0)
} }
} }
Graph Track::elevation() const Graph Track::gpsElevation() const
{ {
Graph ret; Graph ret;
@ -205,7 +241,48 @@ Graph Track::elevation() const
return ret; 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; Graph ret;
@ -219,14 +296,10 @@ Graph Track::speed() const
qreal v; qreal v;
for (int j = 0; j < sd.size(); j++) { for (int j = 0; j < sd.size(); j++) {
if (seg.stop.contains(j) && (!std::isnan(seg.speed.at(j)) if (seg.stop.contains(j) && !std::isnan(seg.speed.at(j))) {
|| sd.at(j).hasSpeed())) {
v = 0; v = 0;
stop.append(gs.size()); stop.append(gs.size());
} else if (_useReportedSpeed && sd.at(j).hasSpeed() } else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
&& !seg.outliers.contains(j))
v = sd.at(j).speed();
else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
v = seg.speed.at(j); v = seg.speed.at(j);
else else
continue; continue;
@ -244,6 +317,60 @@ Graph Track::speed() const
return ret; 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 Track::heartRate() const
{ {
Graph ret; Graph ret;

View File

@ -17,8 +17,8 @@ public:
Path path() const; Path path() const;
Graph elevation() const; GraphPair elevation() const;
Graph speed() const; GraphPair speed() const;
Graph heartRate() const; Graph heartRate() const;
Graph temperature() const; Graph temperature() const;
Graph cadence() const; Graph cadence() const;
@ -32,6 +32,7 @@ public:
const QString &name() const {return _data.name();} const QString &name() const {return _data.name();}
const QString &description() const {return _data.description();} const QString &description() const {return _data.description();}
const QString &comment() const {return _data.comment();}
const QVector<Link> &links() const {return _data.links();} const QVector<Link> &links() const {return _data.links();}
bool isValid() const; bool isValid() const;
@ -41,11 +42,17 @@ public:
static void setHeartRateFilter(int window) {_heartRateWindow = window;} static void setHeartRateFilter(int window) {_heartRateWindow = window;}
static void setCadenceFilter(int window) {_cadenceWindow = window;} static void setCadenceFilter(int window) {_cadenceWindow = window;}
static void setPowerFilter(int window) {_powerWindow = window;} static void setPowerFilter(int window) {_powerWindow = window;}
static void setAutomaticPause(bool set) {_automaticPause = set;}
static void setPauseSpeed(qreal speed) {_pauseSpeed = speed;} static void setPauseSpeed(qreal speed) {_pauseSpeed = speed;}
static void setPauseInterval(int interval) {_pauseInterval = interval;} static void setPauseInterval(int interval) {_pauseInterval = interval;}
static void setOutlierElimination(bool eliminate) static void setOutlierElimination(bool eliminate)
{_outlierEliminate = eliminate;} {_outlierEliminate = eliminate;}
static void useReportedSpeed(bool use) {_useReportedSpeed = use;} 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: private:
struct Segment { struct Segment {
@ -58,6 +65,11 @@ private:
bool discardStopPoint(const Segment &seg, int i) const; bool discardStopPoint(const Segment &seg, int i) const;
Graph demElevation() const;
Graph gpsElevation() const;
Graph reportedSpeed() const;
Graph computedSpeed() const;
TrackData _data; TrackData _data;
QList<Segment> _segments; QList<Segment> _segments;
qreal _pause; qreal _pause;
@ -68,9 +80,13 @@ private:
static int _heartRateWindow; static int _heartRateWindow;
static int _cadenceWindow; static int _cadenceWindow;
static int _powerWindow; static int _powerWindow;
static bool _automaticPause;
static qreal _pauseSpeed; static qreal _pauseSpeed;
static int _pauseInterval; static int _pauseInterval;
static bool _useReportedSpeed; static bool _useReportedSpeed;
static bool _useDEM;
static bool _show2ndElevation;
static bool _show2ndSpeed;
}; };
#endif // TRACK_H #endif // TRACK_H

View File

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

@ -4,34 +4,42 @@
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
#include <QHash> #include <QHash>
#include <QVector>
#include <QDebug> #include <QDebug>
#include "common/coordinates.h" #include "common/coordinates.h"
#include "imageinfo.h" #include "imageinfo.h"
#include "link.h" #include "link.h"
#include "address.h"
class Waypoint class Waypoint
{ {
public: public:
Waypoint() {_elevation = NAN;} Waypoint() : _elevation(NAN) {}
Waypoint(const Coordinates &coordinates) : _coordinates(coordinates) Waypoint(const Coordinates &coordinates)
{_elevation = NAN;} : _coordinates(coordinates), _elevation(NAN) {}
const Coordinates &coordinates() const {return _coordinates;} const Coordinates &coordinates() const {return _coordinates;}
const QString &name() const {return _name;} const QString &name() const {return _name;}
const QString &description() const {return _description;} const QString &description() const {return _description;}
const ImageInfo &image() const {return _image;} 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 QVector<Link> &links() const {return _links;}
const QDateTime &timestamp() const {return _timestamp;} const QDateTime &timestamp() const {return _timestamp;}
qreal elevation() const {return _elevation;} qreal elevation() const {return _elevation;}
QPair<qreal, qreal> elevations() const;
void setCoordinates(const Coordinates &coordinates) void setCoordinates(const Coordinates &coordinates)
{_coordinates = coordinates;} {_coordinates = coordinates;}
void setName(const QString &name) {_name = name;} void setName(const QString &name) {_name = name;}
void setDescription(const QString &description) void setDescription(const QString &description)
{_description = 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 setTimestamp(const QDateTime &timestamp) {_timestamp = timestamp;}
void setElevation(qreal elevation) {_elevation = elevation;} void setElevation(qreal elevation) {_elevation = elevation;}
void setImage(const ImageInfo &image) {_image = image;} void addImage(const ImageInfo &image) {_images.append(image);}
void addLink(const Link &link) {_links.append(link);} void addLink(const Link &link) {_links.append(link);}
bool hasElevation() const {return !std::isnan(_elevation);} bool hasElevation() const {return !std::isnan(_elevation);}
@ -40,14 +48,23 @@ public:
{return this->_name == other._name {return this->_name == other._name
&& this->_coordinates == other._coordinates;} && this->_coordinates == other._coordinates;}
static void useDEM(bool use) {_useDEM = use;}
static void showSecondaryElevation(bool show)
{_show2ndElevation = show;}
private: private:
Coordinates _coordinates; Coordinates _coordinates;
QString _name; QString _name;
QString _description; QString _description;
ImageInfo _image; QString _comment;
Address _address;
QVector<ImageInfo> _images;
QVector<Link> _links; QVector<Link> _links;
QDateTime _timestamp; QDateTime _timestamp;
qreal _elevation; qreal _elevation;
static bool _useDEM;
static bool _show2ndElevation;
}; };
inline uint qHash(const Waypoint &key) inline uint qHash(const Waypoint &key)
@ -59,11 +76,9 @@ inline uint qHash(const Waypoint &key)
inline QDebug operator<<(QDebug dbg, const Waypoint &waypoint) inline QDebug operator<<(QDebug dbg, const Waypoint &waypoint)
{ {
dbg.nospace() << "Waypoint(" << waypoint.coordinates() << ", " dbg.nospace() << "Waypoint(" << waypoint.coordinates() << ", "
<< waypoint.name() << ", " << waypoint.description() << ")"; << waypoint.name() << ")";
return dbg.space(); return dbg.space();
} }
#endif // QT_NO_DEBUG #endif // QT_NO_DEBUG
Q_DECLARE_TYPEINFO(Waypoint, Q_MOVABLE_TYPE);
#endif // WAYPOINT_H #endif // WAYPOINT_H

View File

@ -1,7 +1,13 @@
#include <QPainter> #include <QPainter>
#include <QImage> #include <QImage>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#include <QtCore/qmath.h>
#else // QT5
#include <QtMath>
#endif // QT5
#include "bitmapline.h" #include "bitmapline.h"
static QImage img2line(const QImage &img, int width) static QImage img2line(const QImage &img, int width)
{ {
Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied); Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied);
@ -32,8 +38,8 @@ void BitmapLine::draw(QPainter *painter, const QPolygonF &line,
painter->save(); painter->save();
painter->translate(segment.p1()); painter->translate(segment.p1());
painter->rotate(-segment.angle()); painter->rotate(-segment.angle());
painter->drawImage(0, -img.height()/2, img2line(img, segment.length())); painter->drawImage(0.0, -img.height()/2.0, img2line(img,
qCeil(segment.length())));
painter->restore(); painter->restore();
} }
} }

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