1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-01 21:39:15 +02:00

Compare commits

...

446 Commits
2.5 ... 4.5

Author SHA1 Message Date
699e4f32d5 Version++ 2017-04-23 18:15:08 +02:00
992c8aaaf9 Improved error reporting 2017-04-23 13:37:17 +02:00
68a72c5809 Added proper OZF files detection 2017-04-23 12:49:40 +02:00
eace308774 Properly open all selected files (tar content/atlas structure is however still case sensitive) 2017-04-23 12:26:01 +02:00
e4e3f7d143 Added missing atlas maps unloading on atlas unload 2017-04-22 10:19:02 +02:00
acc49d015e Use only the "Map" word for maps/atlases in the GUI 2017-04-21 21:57:29 +02:00
b48652cb78 Localization update 2017-04-21 21:25:56 +02:00
0808f6679e Added "runtime" offline map loading
Offline map loading & error handling cleanup
2017-04-21 21:15:58 +02:00
610ac3d73f Fixed atlas zoom fitting algorithm 2017-04-19 00:07:01 +02:00
1d719fdcc8 Localization files update 2017-04-17 22:13:30 +02:00
1746e1cf08 Merge pull request #25 from mkroehnert/german-translation
add german translation
2017-04-17 21:58:05 +02:00
5d5a5365cd add german translation 2017-04-17 20:56:56 +02:00
7dfdeac1da Version++ 2017-04-17 19:57:52 +02:00
1ff13ee9f8 Added datum files to packages 2017-04-17 19:56:29 +02:00
34b1fb6b5d Cosmetics 2017-04-17 19:37:03 +02:00
02bf85780a Improved OZF files handling. 2017-04-17 19:34:44 +02:00
47199348d8 Improved error reporting 2017-04-17 19:15:20 +02:00
3b55bc0efc Added missing Lat/Lon based calibration points conversion to WGS84 2017-04-17 19:01:01 +02:00
4de3ddf71b Switched from hardcoded values to CSV files for datum definitions.
Non-WGS84 maps now fit correctly when defined using map grid.
2017-04-17 18:03:04 +02:00
e26fa92ce6 Code cleanup 2017-04-15 13:13:26 +02:00
dfb69b2755 Yet another error handling fix. 2017-04-15 11:47:30 +02:00
c2a30738cb Update README.md 2017-04-15 09:11:16 +02:00
fd53e89ea5 Improved error handling 2017-04-15 08:59:31 +02:00
ccf91bb29f Added support for ozf2 files 2017-04-14 22:39:33 +02:00
75bd388be0 Try to load maps with incorrect (windows absolute) image paths. 2017-04-13 20:00:27 +02:00
aef0357204 Version++ 2017-04-12 18:19:03 +02:00
d073d606d0 Added missing cache reset (causing huge redraw rects causing system memory exhaustion) 2017-04-12 18:11:53 +02:00
8822bf7e5f Code cleanup 2017-04-09 10:26:09 +02:00
051a3ed303 Replaced wrong NAD27 ellipsoid with the correct one 2017-04-07 18:19:56 +02:00
96437ae6ab Cosmetics 2017-04-07 10:16:14 +02:00
89c25bbc3a Update README.md 2017-04-07 10:02:41 +02:00
322792ea04 Improved digital zoom/fullscreen interaction 2017-04-06 19:54:50 +02:00
ce1c76a315 translation cleanup 2017-04-06 09:11:36 +02:00
804e9594ad Merge pull request #21 from eson57/patch-4
Update gpxsee_sv.ts
2017-04-06 09:10:13 +02:00
651973c524 Merge branch 'master' into patch-4 2017-04-06 09:09:51 +02:00
533af66080 Fixed typo 2017-04-06 09:08:25 +02:00
bab5750cf7 Update gpxsee_sv.ts 2017-04-06 06:36:18 +02:00
0d6f3ea162 Version++ 2017-04-05 22:56:37 +02:00
b915302e2f Localization update 2017-04-05 22:53:44 +02:00
03ef7a9147 Added digital zoom 2017-04-05 22:53:25 +02:00
33cb944e36 Code cleanup/optimization 2017-04-04 22:03:42 +02:00
8bc575aef2 Fixed map fitting algorithm 2017-04-04 01:19:17 +02:00
f0b7abdb72 Added SAD69 ellipsoid 2017-04-03 20:59:17 +02:00
6ce14734cd Use ellipsoids defined by the map file for the projection computation 2017-04-03 20:29:35 +02:00
925a0e2951 version++ 2017-04-02 22:19:24 +02:00
e46bba18f2 Optimized projection computation 2017-04-02 22:18:03 +02:00
a56aa4e706 Added support for Lambert Conformal Conic projection 2017-04-02 22:17:16 +02:00
c4b3a81b0b Make coverity happy 2017-04-02 09:32:40 +02:00
957cc6f4f3 Code cleanup 2017-04-02 00:39:12 +02:00
ebad0832ee Update README.md 2017-04-01 19:48:39 +02:00
186c135ad5 Update README.md 2017-04-01 19:47:14 +02:00
72b5b1ea97 Update README.md 2017-04-01 19:46:11 +02:00
106d4c6e0b Installer polishing 2017-04-01 18:02:34 +02:00
8f815a6af0 Missing file changes... 2017-04-01 16:55:46 +02:00
60f107d7cd Added possibility to compute the UTM zone from lat/lon reference points 2017-04-01 16:25:33 +02:00
e87fff4bf8 Added UTM projection support
Fixed broken map fitting
2017-04-01 15:58:32 +02:00
e6547d92f0 Fixed zoom speed issue on touchpads 2017-04-01 06:56:50 +02:00
bfcc577f64 Added missing destructor 2017-04-01 06:10:36 +02:00
a56ad8a933 Code cleanup/optimizations 2017-04-01 05:59:55 +02:00
b1748c848b Fixed broken atlas maps fitting in non-orthogonal projections. 2017-03-31 19:18:01 +02:00
3763d44662 Added Latitude/Longitude projection 2017-03-29 22:51:32 +02:00
b3e8081942 Added support for Transversal Mercator projection 2017-03-29 00:17:47 +02:00
b3dc886afc Localization update 2017-03-27 23:59:26 +02:00
431002fd62 Added offline map info to data sources info page 2017-03-27 23:58:36 +02:00
63c3a50ca6 Fixed scene (map) bounds not updating issue
Code cleanup
2017-03-27 23:52:24 +02:00
7fd30bbda3 Improved error handling 2017-03-27 10:31:41 +02:00
c1844f9557 Atlas loading optimization 2017-03-27 02:41:30 +02:00
0c7601c831 Fixed TAR parser 2017-03-26 18:08:37 +02:00
19eb3fba7f Merge branch 'master' of https://github.com/tumic0/GPXSee 2017-03-26 15:33:49 +02:00
bde3c8cc3d Added support for Trekbuddy atlases 2017-03-26 15:32:55 +02:00
b0ce471ea8 Markup syntax fix 2017-03-24 22:53:26 +01:00
bd23120c2c Improved image/tiles info fetching 2017-03-21 20:51:23 +01:00
ac4ef0631e Do not depend on a specific tile name when gathering tile info. 2017-03-21 19:02:29 +01:00
60d9a172b6 Refactoring 2017-03-21 09:27:44 +01:00
b8eede21c0 Improved tared maps scrolling performance 2017-03-21 09:01:30 +01:00
70ddbe192f Added support for tared maps 2017-03-21 01:15:29 +01:00
d8477571cc Added missing coordinates transformation 2017-03-20 22:52:39 +01:00
92545acba0 Added support for tiled offline maps 2017-03-20 10:05:07 +01:00
2bf66ea912 Tile loading code cleanup 2017-03-20 09:25:05 +01:00
b21c2267cf Added support for OziExplorer offline maps 2017-03-18 01:30:31 +01:00
278aa904c1 Human-friendly links 2017-03-05 10:39:50 +01:00
b369985f0a Updated Linux packages download URL 2017-03-05 10:35:19 +01:00
6a5781771f Updated program screenshot 2017-02-28 11:53:25 +01:00
1b616f3c81 Added installer executable description 2017-02-16 18:59:37 +01:00
99dfbc1d2f Added Windows installer code sign certificate 2017-02-13 23:58:19 +01:00
f83e3376b4 Version++ 2017-02-13 20:17:05 +01:00
311aeecb27 Fixed broken OpenSSL/ANGLE install when MSVC runtime is installed. 2017-02-13 20:13:13 +01:00
338ace6dff Added missing inicialization 2017-02-13 20:12:48 +01:00
51e9ef4416 Merge pull request #20 from eson57/patch-3
Update gpxsee_sv.ts
2017-02-13 10:19:29 +01:00
ebefe54510 Update gpxsee_sv.ts 2017-02-13 04:19:23 +01:00
1ec732c78d Version++ 2017-02-12 20:13:04 +01:00
ff733b2705 Indicate moving time in status bar 2017-02-12 20:11:36 +01:00
99e32b1a15 Made the moving time switch affect all related values
More standardized time type switching (menu)
2017-02-12 19:57:55 +01:00
f4a992a66f Added missing background color definition (OpenGL issue fix) 2017-02-12 17:49:54 +01:00
f52fa9a9ef Removed obsolete stuff 2017-02-12 17:38:20 +01:00
61f3a1c932 Added OpenSSL to Windows builds
Skipped Windows XP support in Windows builds
2017-02-12 17:34:13 +01:00
93313da01d Fixed QNetworkManager related crashes on program termination. 2017-02-07 23:36:06 +01:00
a253ac1796 Localization update 2017-02-05 16:10:42 +01:00
e6a0eeefcc Better Time/Moving Time display approach. 2017-02-05 16:01:54 +01:00
214d7c40dc Added missing coordinates bounding rectangle normalization
(Fixes issue #19)
2017-02-03 00:54:57 +01:00
cd89706d74 Do not use hardcoded control keys info 2017-02-02 20:06:17 +01:00
961eac9324 Added moving time info.
Added special track data processing for "pause states".
2017-01-31 09:37:01 +01:00
ddacac8d2e Added Open Topo Map to default map sources 2017-01-26 09:46:36 +01:00
bd9b5dbc6a Merge pull request #17 from eson57/patch-2
Update gpxsee_sv.ts
2017-01-24 23:57:29 +01:00
8b813b5879 Update gpxsee_sv.ts 2017-01-24 21:32:49 +01:00
b79de29464 Added missing swedish string 2017-01-24 18:30:00 +01:00
ae2765528d Updated obsolete strings 2017-01-24 18:23:56 +01:00
0356917790 Version++ 2017-01-24 18:12:08 +01:00
7352b24473 Added missing item update on content change 2017-01-24 18:11:08 +01:00
3a9ec6247c Removed Czech strings from Swedish translation 2017-01-23 10:01:27 +01:00
e74ac78138 Added missing swedish translation resource 2017-01-23 00:08:10 +01:00
5fa91be4ac Localization update 2017-01-23 00:03:48 +01:00
536b4fd121 Added swedish translation 2017-01-23 00:02:16 +01:00
0556ae0f58 Merge pull request #16 from eson57/patch-1
Swedish translation
2017-01-22 23:51:41 +01:00
e38fdb26d7 Version 3.7 2017-01-22 22:54:42 +01:00
e420602c69 Fixed scene bounding rect reset 2017-01-22 22:53:38 +01:00
b2a49eaa23 Version++ 2017-01-22 22:14:59 +01:00
211a4e4cef Create gpxsee_sv.ts 2017-01-21 09:49:51 +01:00
8ff8d4bf16 Code cleanup 2017-01-20 01:17:22 +01:00
46598a85fc Fixed redirect loop handling logic 2017-01-17 10:37:02 +01:00
32cbd33c91 Fixed tile area computation 2017-01-16 21:45:50 +01:00
0e356d0222 Added redirect loop check 2017-01-16 21:45:27 +01:00
1a29ab6304 Enabled free map scroll. 2017-01-16 09:54:12 +01:00
5581cff55b Fixed download error handling in case of redirects
Do not download tiles multiple times.
2017-01-16 09:53:01 +01:00
a458b82e37 Code cleanup 2017-01-11 22:14:01 +01:00
bd946fb477 Fixed bounding rect computation in special case (rect + 1 waypoint) 2017-01-09 00:20:06 +01:00
7e6ed0933c Code cleanup 2017-01-08 20:02:51 +01:00
1586a5e912 Optimization 2017-01-07 22:20:04 +01:00
3e340ab941 Improved error reporting.
Cosmetics.
2017-01-02 23:01:50 +01:00
ea178d1acb Fixed benign memory leak 2017-01-02 09:42:34 +01:00
f61488fcfa Version++ 2016-12-20 08:39:40 +01:00
b68ca92add OpenGL code cleanup 2016-12-20 00:11:30 +01:00
0448ae5eea Added basic print/export formating options 2016-12-16 02:30:58 +01:00
2bc112c7b4 Fixed broken latitude handling 2016-12-13 18:54:58 +01:00
ccd92edb8d More code cleanup 2016-12-10 11:52:18 +01:00
0cbf79870b Code cleanup 2016-12-10 11:50:53 +01:00
7e8530555d Unified AA samples count for QGLFormat and QSurfaceFormat. 2016-12-10 11:45:16 +01:00
2fd16e5e31 Version++ 2016-12-10 01:20:17 +01:00
a5033c8b19 Fixed QOpenGLWidget usage (wrong minimal QT version) 2016-12-10 01:18:31 +01:00
b0c6176ddd Fixed switched default values 2016-12-08 00:28:44 +01:00
6edd7a8c61 Code cleanup 2016-12-07 22:01:45 +01:00
8a299be65b Translations 2016-12-07 22:01:33 +01:00
cde4c65c53 GUI polishing 2016-12-07 21:38:36 +01:00
b24136a580 Use OpenGL also for graph views when selected 2016-12-07 02:02:39 +01:00
da1b2bb90a Use a reasonable number of samples per pixel 2016-12-07 00:54:14 +01:00
30e198cf46 Cache the graph grid 2016-12-07 00:44:32 +01:00
0f2deca4fa Fixed broken graph width setting 2016-12-07 00:03:36 +01:00
1055c4fd98 Added missing OpenGL related libs to Windows package.
Version++.
2016-12-06 22:05:26 +01:00
18c501b610 Removed path item caching (made obsolete by OpenGL rendering) 2016-12-06 21:28:45 +01:00
71310116e3 Cosmetics 2016-12-06 21:03:19 +01:00
34e48199b2 Fixed options/settings handling isses. 2016-12-06 21:02:44 +01:00
4cf027df58 Fixed widget rendering issues 2016-12-06 21:01:06 +01:00
a9668ca86e Added options dialogue 2016-12-06 01:48:26 +01:00
181f60ed40 README update 2016-11-23 19:54:46 +01:00
307405d661 Improved track date handling 2016-11-23 18:45:06 +01:00
1242423ca8 Fixed possible invalid memory access 2016-11-23 18:44:22 +01:00
2bdab0f449 NMEA parser improvements 2016-11-22 00:13:41 +01:00
4e23df3a66 Fixed broken date handling 2016-11-21 21:59:21 +01:00
23f5a317d0 Fixed graph bounds issue 2016-11-18 18:02:40 +01:00
80bc9f1f01 Fixed bounds check 2016-11-18 09:23:43 +01:00
dec3f10df3 Localization update 2016-11-18 08:10:23 +01:00
296d4c6c9d Added NMEA icons 2016-11-18 08:03:04 +01:00
668558cf2e Improved error reporting 2016-11-17 23:31:09 +01:00
a6053a4d7b Made the NMEA parser more robust 2016-11-17 19:43:02 +01:00
535361dada Added support for NMEA files 2016-11-16 23:54:15 +01:00
e9e7660beb Fixed path/distances size difference issue 2016-11-15 18:08:44 +01:00
eb5692a0ab Fixed path marker inaccuracy issue. 2016-11-14 22:12:43 +01:00
367427b26a Code cleanup 2016-11-13 09:49:31 +01:00
371fa13bc6 Resize optimization 2016-11-12 09:09:42 +01:00
5a8250ed63 Fixed special cases scene rect computation 2016-11-11 23:48:38 +01:00
c8a39a607b Fixed error checking 2016-11-11 22:04:26 +01:00
215cf03b68 Fixed error handling 2016-11-11 21:53:23 +01:00
9cadb8d0a5 Make coverity happy 2016-11-11 21:37:35 +01:00
20c82deda7 README update 2016-11-11 20:15:11 +01:00
7c9768e83a Added missing IGC integration.
Version 3.1.
2016-11-11 19:54:53 +01:00
736566b12c Fixed path view clipping 2016-11-11 18:35:22 +01:00
540339bf22 Fixed zoom computation.
Code cleanup.
2016-11-11 17:58:18 +01:00
81a9743064 Added support for IGC routes (C records) 2016-11-11 17:57:49 +01:00
818fa11fd3 Added IGC icons 2016-11-10 08:56:38 +01:00
dd80d34e58 Code cleanup 2016-11-10 08:46:59 +01:00
17c791d753 Added support for IGC files 2016-11-10 00:08:11 +01:00
9f06b042ca Added support for MultiGeometry objects in KML 2016-11-08 20:50:42 +01:00
d201101587 Updated README 2016-11-08 08:47:09 +01:00
da842bd21d Fixed FIT MIME type 2016-11-08 08:41:48 +01:00
8a90a736d2 Added cadence and power graphs 2016-11-06 03:28:08 +01:00
aac9bf024b Debug stuff 2016-11-05 20:00:14 +01:00
585ded6b1c Code cleanup 2016-11-05 17:37:04 +01:00
b9098c0dc5 Redesigned track data filtering 2016-11-05 17:33:29 +01:00
584245785e Added struct size padding asserts to FIT parser 2016-11-05 17:32:15 +01:00
66ce91c237 Enlarged icons 2016-11-03 18:34:47 +01:00
d134938ff8 Added missing icon files to Mac bundle 2016-11-03 18:34:23 +01:00
9e583a9dab Added support for FIT files with developer data fields 2016-11-03 08:02:28 +01:00
62ff4bb7c4 Refactoring 2016-11-03 07:37:17 +01:00
4b66aaa78b Fixed error handling 2016-11-03 01:09:41 +01:00
c97aa9c25d Updated OS X file associations 2016-11-02 20:13:53 +01:00
f1c6db0b46 Added TCX, KML and FIT file association 2016-11-02 19:39:32 +01:00
9bd70ec96e Localization update 2016-11-02 17:36:59 +01:00
984d3bb6c5 Refactoring 2016-11-02 17:35:26 +01:00
87a4398131 Improved FIT parser error handling 2016-11-02 17:33:54 +01:00
324ac2e0f4 Code cleanup 2016-11-02 17:33:06 +01:00
8591d3f34c Localization update 2016-11-01 08:42:16 +01:00
1a53968bd3 Fixed FIT parser 2016-11-01 08:24:05 +01:00
82cf7b49ca Fixed coordinates conversion 2016-11-01 00:14:44 +01:00
9789982626 Added support for Garmin FIT file format. 2016-10-31 22:59:08 +01:00
eb03fe6ead Added zooming on double clicks
Fixed "zoom under mouse" algorithm
2016-10-29 21:22:26 +02:00
0d1b416c4f Code cleanup 2016-10-29 18:01:25 +02:00
c89f8dbb47 Improved error reporting 2016-10-29 12:22:28 +02:00
e4d777bace Increased marker size(width) 2016-10-29 10:49:28 +02:00
640829d89d Some more error handling fixes. 2016-10-29 10:40:30 +02:00
78fb5c1547 Code cleanup 2016-10-28 21:44:53 +02:00
6a25ad6379 Improved error handling 2016-10-28 19:29:42 +02:00
5693375a3f Added support for KML gx:Track 2016-10-28 19:12:40 +02:00
10b903c129 Added support for track/route names and descriptions 2016-10-28 14:33:36 +02:00
9a0d304e8d Version 3.0 2016-10-28 01:37:59 +02:00
0e5eb7287d Use LineStrings as Tracks rather than routes in KML files 2016-10-28 01:33:44 +02:00
7827509a4a Optimization 2016-10-28 00:48:49 +02:00
974290ce8b Treat TCX courses as tracks not as routes 2016-10-28 00:48:04 +02:00
09c097cc68 KML parser code cleanup 2016-10-27 22:33:35 +02:00
7f12c0ca95 TCX parser code cleanup 2016-10-27 09:15:05 +02:00
a773921da0 Some more GPX parser code cleanup 2016-10-27 08:27:25 +02:00
b67428065f Code cleanup 2016-10-27 08:09:12 +02:00
5bbf117f64 Added missing GPX parser error handling
Refactoring
2016-10-27 00:20:00 +02:00
1744764025 Improved file loading performance
Better error reporting
2016-10-26 20:17:05 +02:00
c425b3868d Added support for KML files without document tags 2016-10-26 09:01:35 +02:00
84e61c657e Added support for sub-folders in KML 2016-10-26 08:23:18 +02:00
721ee2aaa9 Improved KML parser 2016-10-25 22:50:11 +02:00
6cc22afcdc Added file changes missing in last commit... 2016-10-25 19:46:44 +02:00
b99def1b30 Added support for KML files
Fixed some minor parser issues
2016-10-24 20:00:29 +02:00
1c5a19a33a Fixed TCX parser 2016-10-24 00:51:19 +02:00
27194d3d36 Added coordinates checking to file parsers
Refactoring
2016-10-24 00:21:40 +02:00
375ef20592 Fixed scale item print issue 2016-10-23 14:58:24 +02:00
e86c19635e Improved graph grid print output 2016-10-23 14:36:50 +02:00
d7fd40d9d2 Added support for TCX and CSV files 2016-10-23 11:09:20 +02:00
94fc5e17d0 Made the graph view resizeable 2016-10-19 23:38:28 +02:00
425c75416a Added proper plural handling 2016-10-19 23:15:42 +02:00
1160c6d385 Added graph grid option
Code cleanup
2016-10-17 23:14:07 +02:00
a6b2a477a1 Version 2.20 2016-10-15 23:43:26 +02:00
0ca264b176 GUI plishing 2016-10-12 22:37:40 +02:00
be3c101c07 Fixed graph line distortion in PDF output 2016-10-12 20:38:18 +02:00
cb52ad8bc5 Better file select button 2016-10-11 03:17:30 +02:00
12d5dcc78c Improved error handling.
Code cleanup.
2016-10-11 00:19:42 +02:00
afd87c6fa2 Fixed POI loading
Better map signal handling
2016-10-09 23:58:24 +02:00
c7d68f924f POI code refactoring 2016-10-09 23:46:30 +02:00
89304c0d5c Fixed QT4 compile issue 2016-10-08 21:16:56 +02:00
c57a0f4061 Improved POI files open dialogue 2016-10-08 21:10:13 +02:00
f02ff1fa01 POI loading optimization 2016-10-08 14:53:10 +02:00
434df521fb Added missing drag&drop enable 2016-10-05 18:23:16 +02:00
9d90b97ba3 Added support for drag&drop 2016-10-04 10:16:46 +02:00
09631de5a2 Code cleanup 2016-10-02 23:26:48 +02:00
6b8c0231ea Fixed crash on GPX files with empty tracks/routes 2016-09-27 10:19:39 +02:00
eae55b14bb Fixed slider step size in time graphs 2016-09-27 01:48:45 +02:00
29fe054d57 Added proper check for empty graph view. 2016-09-27 01:29:35 +02:00
b95fb80210 Fixed compile issue on Windows.
Some more refactoring.
2016-09-27 01:27:38 +02:00
70f4010f55 Code cleanup 2016-09-26 21:01:58 +02:00
014e260042 Fixed some more display issues 2016-09-26 10:09:56 +02:00
80bf57abfc Back to a correct slider size in PDF output... 2016-09-25 18:57:17 +02:00
473b92e8f7 Fixed route marker display issues.
Code cleanup.
2016-09-25 18:44:33 +02:00
1634b0715c Fixed PDF output on QT4 2016-09-25 18:08:39 +02:00
1a07360d91 Version++ 2016-09-22 21:01:05 +02:00
7e365cb990 Added graph highlighting on track select 2016-09-22 20:59:13 +02:00
19c44f56d6 Added suppport for proxy connections (using system proxy settings) 2016-09-22 20:57:58 +02:00
799079392c Fixed graph slider handling issue 2016-09-21 23:48:11 +02:00
1957a51570 Fixed broken graph handling 2016-09-19 23:35:04 +02:00
e9b32fb582 Fixed Z-order issue 2016-09-19 01:45:28 +02:00
3e421c6aba Added support for time graphs 2016-09-19 00:56:10 +02:00
aa461a04b1 Added support for time graphs 2016-09-19 00:55:03 +02:00
b369ffacef Some more performance improvement magic 2016-09-14 22:37:12 +02:00
6034685fa0 Code cleanup 2016-09-12 23:53:35 +02:00
420e96a2f4 Fixed rendering issues 2016-09-12 23:53:14 +02:00
a5f11cb77b Cosmetics 2016-09-12 02:27:30 +02:00
f9fcbf5de4 Fixed item Z order 2016-09-12 02:25:57 +02:00
058a09a426 Fixed cache issues 2016-09-12 02:01:13 +02:00
ebf3c3bc14 Drawing performance improvement 2016-09-11 17:15:23 +02:00
eeed5ceba3 Better Z-value handling 2016-09-11 13:43:33 +02:00
1827787fc2 Added item shape for better collision detection/hover handling 2016-09-11 13:42:22 +02:00
3074ba9957 Added item hovering 2016-09-10 13:11:46 +02:00
8624b42e0b Various route-releated fixes & improvements 2016-08-30 21:26:28 +02:00
c00ebdeefd version++ 2016-08-30 08:42:32 +02:00
05f1536285 Better handling of empty graphs 2016-08-30 08:39:14 +02:00
bf69ef58ba Fixed some more display issues 2016-08-20 11:28:08 +02:00
adcd603eec Fixed typo 2016-08-19 21:10:19 +02:00
0ee0bd882e Some more graph handling improvements 2016-08-19 19:48:44 +02:00
d48a2aac93 Fixed slider issues 2016-08-16 10:25:08 +02:00
7993e4dcb3 Added initial support for track/route graphs differentiation 2016-08-16 00:27:54 +02:00
d20d94ef05 code cleanup 2016-08-15 08:20:27 +02:00
9f0582cbea Fixed broken track/route handling in some corner cases 2016-08-10 22:16:39 +02:00
1e6925da75 GUI polishing 2016-08-10 21:17:12 +02:00
7cfc05c101 Splited route and track info in status bar 2016-08-10 20:36:09 +02:00
392b829733 Redesigned obscure position marker handling 2016-08-10 20:35:39 +02:00
36083d2fa1 PDF output polishing 2016-08-10 08:20:24 +02:00
63fd7f239c Improved translation 2016-08-10 08:19:55 +02:00
20b107581e Added missing routes related stuff
Refactoring/optimization/cleanup
2016-08-09 23:08:49 +02:00
aacc04520f Added some more data display options 2016-08-09 10:47:49 +02:00
dad85e46a7 Added support for GPX routes 2016-08-09 01:16:19 +02:00
5912506292 Fixed PDF export/print 2016-08-02 23:56:50 +02:00
e3c23d0ffc Error handling fix 2016-08-02 23:05:52 +02:00
efdb31ddc5 version++ 2016-08-02 20:48:46 +02:00
ee2f471ca0 Remaining tool-tip related changes
Improved/fixed POI handling
2016-08-02 20:46:22 +02:00
dafadbab60 Some more tool tips related changes 2016-08-02 00:28:56 +02:00
7de08d116a Fixed track tooltip area handling
Added some more waypoint info to waypoint tooltips.
Refactoring & optimization.
2016-07-28 00:23:22 +02:00
dac06b9537 Added tooltip info to tracks/waypoints 2016-07-25 19:32:36 +02:00
33b6e8954c Added track zoom in/out keyboard controll. 2016-07-21 22:39:53 +02:00
2695745d82 Code cleanup 2016-06-27 20:30:45 +02:00
fc67f6f19a Fixed broken check 2016-06-27 20:29:59 +02:00
07cd536867 Indent cleanup 2016-06-25 11:02:09 +02:00
c3d6294b7a Code cleanup 2016-06-24 00:55:44 +02:00
68671ed994 Refactoring 2016-06-21 00:12:34 +02:00
46989bd7a0 Code cleanup 2016-06-20 23:56:42 +02:00
0ce2f02178 version++ 2016-06-16 20:58:57 +02:00
1289762365 Added missing changes :-) 2016-06-16 20:47:32 +02:00
f0c3f9b8c8 Added temperature graphs
Fixed handling of GPX files with inconsistent trackpoint entries
2016-06-16 20:33:56 +02:00
31f6eeac26 Fixed infinite download loop on broken tile images
Added support for HTTP redirects
2016-06-16 20:32:11 +02:00
40803d68c1 Fixed slider/marker item inconsistency on file reload. 2016-06-06 20:38:10 +02:00
35dea229b4 Fixed printing on Windows XP 2016-05-29 19:35:38 +02:00
330b6547c4 Some more PDF export dialog polishing 2016-05-28 07:39:40 +02:00
498d33b77d PDF export dialog polishing 2016-05-27 22:45:58 +02:00
3592d9e68f Some more PDF info 2016-05-26 00:57:27 +02:00
7d38b55f60 Remove the PDF export dialog window superfluous "Help" button on Windows 2016-05-26 00:24:19 +02:00
82df1bec0e Improved PDF export dialog 2016-05-25 23:27:07 +02:00
82cbbbb52a Added basic PDF export settings 2016-05-24 03:01:22 +02:00
b8e404b789 Plots are now paper size agnostic 2016-05-22 15:47:23 +02:00
2ad94947d7 Make the file info and graph info having the same text size in plots. 2016-05-22 11:54:27 +02:00
b713da0012 Fixed QT4 compile issue 2016-05-21 16:18:48 +02:00
daf4ef894c Yet another bunch of print/PDF export improvements 2016-05-21 16:10:24 +02:00
eae8e23c6c Yet another print/pdf export fix 2016-05-21 00:28:06 +02:00
b9f7af33d7 Fixed graph shrinking on export/print 2016-05-20 23:47:37 +02:00
f334901db3 Print/pdf export related cosmetics 2016-05-20 22:44:03 +02:00
ffcba53b91 Added proper tile download for PDF exports/printing 2016-05-19 01:10:40 +02:00
f326c7e002 Added missing string localization
Code cleanup
2016-05-18 22:25:55 +02:00
15d4bd05f9 Keep the toolbar small 2016-05-17 00:45:49 +02:00
43bc7dd682 System defined toolbar style does't seem to be a that good idea... 2016-05-15 22:50:44 +02:00
a8cc02c1f9 Version++ 2016-05-15 22:40:49 +02:00
511deada2e Added printing support
"Save" is now "Export"
2016-05-15 22:38:15 +02:00
36937d8f2d Added support for maps in PDF export 2016-05-15 13:19:07 +02:00
969d87ec51 Improved toolbar style handling 2016-05-14 09:28:56 +02:00
07255aa8ae Some more code cleanup 2016-05-13 18:48:42 +02:00
e73f9448e9 Fixed plot region 2016-05-12 09:09:14 +02:00
ee9a8df51f Code cleanup 2016-05-12 09:03:05 +02:00
dcdd250858 Improved packaging scripts 2016-05-05 22:28:53 +02:00
6f73740da6 Program version (and some other stuff) now added by qmake. 2016-05-04 18:25:52 +02:00
6518fe3278 Added GPX file association 2016-05-03 10:18:51 +02:00
97e2215f0b File association stuff 2016-05-02 22:21:57 +02:00
21e7c00f66 Code cleanup 2016-04-30 10:54:07 +02:00
5daacc0a22 Program version now defined in the project file 2016-04-30 09:44:28 +02:00
1323f6ead8 Project structure refactoring 2016-04-29 22:49:37 +02:00
798f63aaff Fixed file open event handling on OS X
Installer files separation
2016-04-29 21:13:38 +02:00
b0abc976a5 Added remaining permanent settings 2016-04-28 08:49:06 +02:00
4c61e208ed More permanent settings 2016-04-26 20:55:29 +02:00
c997694e10 Merge branch 'master' of https://github.com/tumic0/GPXSee 2016-04-26 09:40:28 +02:00
a8d671556e Added permanent settings 2016-04-26 09:39:16 +02:00
2e4aeb57a8 Fixed broken file path handling on non-ASCII file systems. 2016-04-22 01:31:44 +02:00
e186a33340 Added screenshot + some more info 2016-04-20 09:27:57 +02:00
1b5bdc0f07 Better window title 2016-04-19 08:51:11 +02:00
5feec6ac0c Fixed broken registry handling in 64b uninstaller 2016-04-15 18:19:35 +02:00
5997ba21f2 Fixed OS X fullscreen shortcut (F11 collides with a system shortcut) 2016-04-13 22:04:14 +02:00
37d9bf1b9b Missing changes from previous commit
Yet another GUI polishing
2016-04-12 23:15:17 +02:00
2bf93f891a Redesigned about dialogs
64b Windows build stuff
2016-04-12 19:16:52 +02:00
11a9d75d8f Yet another graph tabs polishing
Code cleanup
2016-04-11 22:08:00 +02:00
23576cd52c Cosmetics 2016-04-09 09:14:10 +02:00
47bcd0cfaf Added map change shortcuts 2016-04-08 22:33:19 +02:00
043d1f49a6 Uh. That's hideous. 2016-04-05 19:45:30 +02:00
339144f7f8 Added fullscreen mode 2016-04-05 09:10:19 +02:00
27ff3a9ba8 version++ 2016-04-01 23:31:55 +02:00
8f4d31ac24 Fixed C++11 compile issues 2016-04-01 23:14:57 +02:00
34fcbfb60f Proper use of prepareGeometryChange() 2016-04-01 21:20:23 +02:00
803c4aecce Removed debug code 2016-04-01 19:35:57 +02:00
f247c2fa22 Added support for tiles cache clearing 2016-04-01 19:25:34 +02:00
9fa031ca4d Code cleanup 2016-04-01 19:20:55 +02:00
d3908f1561 Code cleanup 2016-04-01 09:41:52 +02:00
f41b13a5a2 Yet another small fix... 2016-03-30 23:07:54 +02:00
284f7d0a36 Fixed slider position handling 2016-03-30 20:50:51 +02:00
f469b4f600 Fixed slider info displaying on right graph border 2016-03-30 01:48:48 +02:00
eec797125b Fixed error handling 2016-03-29 20:58:33 +02:00
9dce08bce4 Added missing units init 2016-03-28 19:04:58 +02:00
06248457a0 Improved handlig of small-distance tracks 2016-03-27 15:04:58 +02:00
c9b5e0f2cb Code cleanup 2016-03-27 13:23:00 +02:00
b7f3a64577 Fixed broken elevation graph start value 2016-03-27 09:45:07 +02:00
e05069d653 Filter parameters adjustment 2016-03-25 13:11:26 +01:00
1928b579ab Replace global data sources with user data sources rather than merging them 2016-03-25 11:27:42 +01:00
501431207f Fixed graphs display configuration issue 2016-03-25 10:59:47 +01:00
0d41341086 Fixed broken file action group handling on file load error 2016-03-25 10:49:08 +01:00
13f383ab7a Yet another GUI polishing 2016-03-25 09:50:19 +01:00
5253698602 Yet another Graph tab display behaviour change 2016-03-24 20:50:11 +01:00
6bcb6ceb8d Now using a file on the filesystem for default maps rather than the executable 2016-03-24 19:34:46 +01:00
9da82a978c Fixed broken app close action 2016-03-24 19:16:26 +01:00
64a343dd2e Fixed tab logic 2016-03-23 20:56:39 +01:00
ac93ccba29 Fixed track color skew on empty tracks 2016-03-23 20:49:40 +01:00
7cd005873d Added missing size check 2016-03-23 20:48:22 +01:00
9c16c3498c Fixed redraw issue 2016-03-23 09:47:02 +01:00
a2eaacc017 Better graph tabs handling 2016-03-23 09:38:22 +01:00
58bed99d14 Fixed broken slider position setting
Refactoring
2016-03-23 09:22:26 +01:00
de71aa0d0c version++ 2016-03-22 19:49:48 +01:00
14290bf1f0 Enabled loading multiple POI files at once 2016-03-22 19:47:11 +01:00
e0fd77bbfd Fixed graph slider position computation 2016-03-22 19:19:59 +01:00
b1b8d68610 Fixed Y legend plotting issue 2016-03-22 06:48:49 +01:00
a0b64b4227 Added support for Garmin "TrackPointExtension" extension (heart rate) 2016-03-21 23:13:46 +01:00
93670d3026 Added heart rate graph
Improved graphs loading performance
Fixed various corner case behaviour bugs
2016-03-21 22:37:55 +01:00
b212ccf594 version++ 2016-03-20 21:08:13 +01:00
a80de92691 Code cleanup 2016-03-19 17:24:53 +01:00
17ab241a6d Refactoring 2016-03-19 09:06:43 +01:00
816e1d1768 Fixed POI loading logic 2016-03-19 09:05:40 +01:00
50c768e12d Header cleanup 2016-03-17 21:14:48 +01:00
72dda5fdd6 Added searching for POIs near waypoints
Refactoring
2016-03-17 20:56:40 +01:00
ee3a6adf2b Fixed graph display issues on constant graphs
Fixed elevation graph data handling
2016-03-17 00:50:20 +01:00
56f15da550 Added support for displaying waypoints 2016-03-15 01:20:24 +01:00
61c82a0836 Fixed (centred) waypoint position markers
Fixed waypoint "radius" computation
2016-03-13 17:44:25 +01:00
201b3f6dae Version++ 2016-03-05 22:51:39 +01:00
04f2a08b78 Code cleanup 2016-03-05 22:42:22 +01:00
a736e3204f Unit spacer fix 2016-03-05 22:09:53 +01:00
08f503249d Localization update 2016-03-05 18:06:57 +01:00
f05e6ce29f Extended POI file control possibilities 2016-03-05 18:01:13 +01:00
209f58412a Fixed broken palette handling 2016-03-03 19:45:04 +01:00
919d53ade9 Improved track loading logic 2016-03-03 09:15:56 +01:00
4cba2ddefd Fixed broken loading of GPX files with empty tracks 2016-03-03 09:15:15 +01:00
e52a163529 Refactoring 2016-03-02 09:34:39 +01:00
70238062f2 Added missing initializations 2016-03-02 09:15:03 +01:00
7d749dfe5b Fixed broken track count when reloading files 2016-02-29 09:04:36 +01:00
64e65679fe Now using antialiasing in track view 2016-02-28 10:58:25 +01:00
7a6e60b83d Merge branch 'master' of https://github.com/tumic0/GPXSee 2016-02-28 09:33:40 +01:00
a1ac09f615 Refactoring 2016-02-28 09:32:54 +01:00
236d045492 Added OBS download link 2016-02-19 23:27:27 +01:00
ba4ac9fe51 Yet another refactoring 2016-02-19 21:42:54 +01:00
59d8b3cc77 Code cleanup 2016-02-19 21:34:55 +01:00
2f80e612b5 WindowsXP font issue workaround 2016-02-13 12:13:56 +01:00
047801685a One Thunderforest is enaught 2016-02-13 11:11:54 +01:00
973298f36e Switched to VS2015 builds wih global installs 2016-02-13 11:09:51 +01:00
932f817496 Improved error handling
Added file filters to open dialogs
2016-02-12 20:12:37 +01:00
580289e533 Code cleanup 2016-02-12 10:29:18 +01:00
30891482a1 More includes cleanup 2016-02-12 10:23:14 +01:00
47a753610a Includes cleanup 2016-02-12 10:09:17 +01:00
e06a1bc148 Added support for POI files in GPX format 2016-02-11 20:58:52 +01:00
635293ec1b Extended default map list 2016-02-11 20:58:22 +01:00
2a987abec3 Cosmetics 2016-02-08 21:08:29 +01:00
aac3de5707 Localization update 2016-02-08 19:19:40 +01:00
923a00479a Added support for GPX files with multiple tracks 2016-02-08 17:53:09 +01:00
82660caccb Version++ 2016-02-07 18:29:07 +01:00
ddebaaa62c Fixed MSVC 2015 compile issues 2016-02-07 18:27:50 +01:00
7723392c01 Added default maps 2016-02-07 18:12:03 +01:00
30e78591c5 Some more shortcuts 2016-02-02 23:13:08 +01:00
a26b1257b2 Version 2.6 2016-02-02 23:02:03 +01:00
97a9dadd39 Scale artefacts fix 2016-02-02 22:56:38 +01:00
6acbf0d89f Fixed scale flicker issue on OS X
Performance improvements
2016-02-02 20:12:56 +01:00
9941faa218 Added proper initialization to silence analysis tools 2016-02-02 09:07:04 +01:00
e7edd5c3c3 Improved PDF export 2016-02-02 01:10:05 +01:00
16cce5abd0 Code cleanup 2016-01-19 09:04:54 +01:00
c2391929d3 Added track scale info 2016-01-14 00:37:51 +01:00
204 changed files with 17753 additions and 2551 deletions

251
Info.plist Normal file
View File

@ -0,0 +1,251 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleIconFile</key>
<string>@ICON@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>@SHORT_VERSION@</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
<string>cz.wz.tumic.GPXSee</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>gpx</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/gpx+xml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>gpx.icns</string>
<key>CFBundleTypeName</key>
<string>GPS Exchange Format</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>tcx</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/tcx+xml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>tcx.icns</string>
<key>CFBundleTypeName</key>
<string>Training Center XML</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>kml</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.google-earth.kml+xml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>kml.icns</string>
<key>CFBundleTypeName</key>
<string>Keyhole Markup Language</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>fit</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.ant.fit</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>fit.icns</string>
<key>CFBundleTypeName</key>
<string>Flexible and Interoperable Data Transfer</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>igc</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.fai.igc</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>igc.icns</string>
<key>CFBundleTypeName</key>
<string>Flight Recorder Data Format</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>nmea</string>
</array>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/vnd.nmea.nmea</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>nmea.icns</string>
<key>CFBundleTypeName</key>
<string>NMEA 0183 data</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>com.topografix.gpx</string>
<key>UTTypeReferenceURL</key>
<string>http://www.topografix.com/GPX/1/1</string>
<key>UTTypeDescription</key>
<string>GPS Exchange Format</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>gpx</string>
</array>
<key>public.mime-type</key>
<string>application/gpx+xml</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.garmin.tcx</string>
<key>UTTypeReferenceURL</key>
<string>http://www8.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd</string>
<key>UTTypeDescription</key>
<string>Training Center XML</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>tcx</string>
</array>
<key>public.mime-type</key>
<string>application/tcx+xml</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.google.kml</string>
<key>UTTypeReferenceURL</key>
<string>https://developers.google.com/kml/documentation/kmlreference</string>
<key>UTTypeDescription</key>
<string>Keyhole Markup Language</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.xml</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>kml</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.google-earth.kml+xml</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>com.thisisant.fit</string>
<key>UTTypeReferenceURL</key>
<string>https://www.thisisant.com/resources/fit</string>
<key>UTTypeDescription</key>
<string>Flexible and Interoperable Data Transfer</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>fit</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.ant.fit</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.fai.igc</string>
<key>UTTypeReferenceURL</key>
<string>http://www.fai.org/gnss-recording-devices/igc-approved-flight-recorders</string>
<key>UTTypeDescription</key>
<string>Flight Recorder Data Format</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>igc</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.fai.igc</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>org.nmea.nmea</string>
<key>UTTypeReferenceURL</key>
<string>http://www.nmea.org/content/nmea_standards/nmea_0183_v_410.asp</string>
<key>UTTypeDescription</key>
<string>NMEA 0183 data</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>nmea</string>
</array>
<key>public.mime-type</key>
<string>application/vnd.nmea.nmea</string>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@ -1,29 +1,33 @@
# GPXSee
GPX viewer and analyzer.
GPXSee is a Qt-based GPS log file viewer and analyzer that supports GPX, TCX,
KML, FIT, IGC and NMEA files.
* User-definable map sources.
* Track and elevation/speed graphs.
## Features
* User-definable online maps.
* Offline maps (OziExplorer maps and TrekBuddy maps/atlases).
* Elevation, speed, heart rate, cadence, power and temperature graphs.
* Support for multiple tracks in one view.
* Support for POI files (Garmin CSV format).
* Export to PDF.
* Support for POI files.
* Print/export to PDF.
* Full-screen mode.
* Native GUI for Windows, Mac OS X and Linux.
* Opens GPX, TCX, FIT, KML, IGC, NMEA and Garmin CSV files.
![GPXSee - Linux](https://a.fsdn.com/con/app/proj/gpxsee/screenshots/linux2.png)
## Build
### Linux/OS X
```shell
lrelease gpxsee.pro
qmake gpxsee.pro
make
```
### Windows
```shell
lrelease gpxsee.pro
qmake gpxsee.pro
nmake release
```
## Binaries
Available at Sourceforge: http://sourceforge.net/projects/gpxsee
## Download
* [Windows & OS X builds](http://sourceforge.net/projects/gpxsee)
* [Linux packages](http://software.opensuse.org/download.html?project=home%3Atumic%3AGPXSee&package=gpxsee)
## Changelog
[Changelog](https://build.opensuse.org/package/view_file/home:tumic:GPXSee/gpxsee/gpxsee.changes)
## Homepage
GPXSee homepage: http://tumic.wz.cz/gpxsee

View File

@ -1,138 +0,0 @@
;Include Modern UI
!include "MUI2.nsh"
; The name of the installer
Name "GPXSee"
; The file to write
OutFile "install.exe"
RequestExecutionLevel user
; The default installation directory
InstallDir "$LOCALAPPDATA\GPXSee"
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically)
InstallDirRegKey HKCU "Software\GPXSee" "Install_Dir"
; Registry key for uninstaller
!define REGENTRY "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPXSee"
; Start menu page configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\GPXSee"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "GPXSee"
Var StartMenuFolder
;--------------------------------
; Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "licence.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
; Languages
!insertmacro MUI_LANGUAGE "English"
; The stuff to install
Section "GPXSee (required)" SEC_APP
SectionIn RO
; Set output path to the installation directory.
SetOutPath $INSTDIR
; Put file there
File "gpxsee.exe"
; Write the installation path into the registry
WriteRegStr HKCU SOFTWARE\GPXSee "Install_Dir" "$INSTDIR"
; Write the uninstall keys for Windows
WriteRegStr HKCU "${REGENTRY}" "DisplayName" "GPXSee"
WriteRegStr HKCU "${REGENTRY}" "Publisher" "Martin Tuma"
WriteRegStr HKCU "${REGENTRY}" "DisplayVersion" "2.5"
WriteRegStr HKCU "${REGENTRY}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKCU "${REGENTRY}" "NoModify" 1
WriteRegDWORD HKCU "${REGENTRY}" "NoRepair" 1
WriteUninstaller "$INSTDIR\uninstall.exe"
; Create start menu entry and add links
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GPXSee.lnk" "$INSTDIR\gpxsee.exe"
!insertmacro MUI_STARTMENU_WRITE_END
SectionEnd
Section "QT libs" SEC_QT
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Widgets.dll"
File "Qt5PrintSupport.dll"
File "Qt5Network.dll"
File "libGLESv2.dll"
File /r "platforms"
File /r "imageformats"
SectionEnd
Section "MSVC runtime" SEC_MSVC
File "msvcr100.dll"
File "msvcp100.dll"
SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
; Remove registry keys
DeleteRegKey HKCU "${REGENTRY}"
DeleteRegKey HKCU SOFTWARE\GPXSee
; Remove directories used
RMDir /r "$INSTDIR"
; Remove Start menu entries
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder
Delete "$SMPROGRAMS\$StartMenuFolder\*.*"
RMDir "$SMPROGRAMS\$StartMenuFolder"
SectionEnd
;-------------------------------
;Descriptions
;Language strings
LangString DESC_QT ${LANG_ENGLISH} \
"QT Library. Unselct only if you have QT already installed!"
LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2010 runtime components. Unselct only if you have the runtime already installed!"
LangString DESC_APP ${LANG_ENGLISH} \
"GPXSee application"
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QT} $(DESC_QT)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_MSVC} $(DESC_MSVC)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APP} $(DESC_APP)
!insertmacro MUI_FUNCTION_DESCRIPTION_END

View File

@ -1,22 +1,18 @@
TARGET = GPXSee
VERSION = 4.5
QT += core \
gui \
network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
greaterThan(QT_MAJOR_VERSION, 4): QT += printsupport
lessThan(QT_VERSION, 5.4): QT += opengl
macx: QT += opengl
HEADERS += src/config.h \
src/icons.h \
src/gui.h \
src/gpx.h \
src/graph.h \
src/track.h \
src/parser.h \
src/poi.h \
src/rtree.h \
src/ll.h \
src/axisitem.h \
src/poiitem.h \
src/colorshop.h \
src/keys.h \
src/slideritem.h \
src/markeritem.h \
@ -26,20 +22,81 @@ HEADERS += src/config.h \
src/sliderinfoitem.h \
src/filebrowser.h \
src/map.h \
src/maplist.h \
src/onlinemap.h \
src/downloader.h \
src/units.h
src/units.h \
src/scaleitem.h \
src/waypoint.h \
src/track.h \
src/graphview.h \
src/trackpoint.h \
src/waypointitem.h \
src/palette.h \
src/heartrategraph.h \
src/range.h \
src/cpuarch.h \
src/settings.h \
src/app.h \
src/trackinfo.h \
src/exportdialog.h \
src/fileselectwidget.h \
src/margins.h \
src/temperaturegraph.h \
src/graphtab.h \
src/misc.h \
src/trackitem.h \
src/tooltip.h \
src/route.h \
src/routeitem.h \
src/graphitem.h \
src/graph.h \
src/pathitem.h \
src/pathview.h \
src/griditem.h \
src/data.h \
src/gpxparser.h \
src/tcxparser.h \
src/parser.h \
src/csvparser.h \
src/coordinates.h \
src/tile.h \
src/rd.h \
src/wgs84.h \
src/kmlparser.h \
src/trackdata.h \
src/routedata.h \
src/fitparser.h \
src/format.h \
src/path.h \
src/assert.h \
src/cadencegraph.h \
src/powergraph.h \
src/igcparser.h \
src/nmeaparser.h \
src/optionsdialog.h \
src/colorbox.h \
src/stylecombobox.h \
src/opengl.h \
src/timetype.h \
src/emptymap.h \
src/offlinemap.h \
src/matrix.h \
src/tar.h \
src/atlas.h \
src/projection.h \
src/mercator.h \
src/transversemercator.h \
src/latlon.h \
src/utm.h \
src/lambertconic.h \
src/ellipsoid.h \
src/ozf.h \
src/datum.h \
src/maplist.h
SOURCES += src/main.cpp \
src/gui.cpp \
src/gpx.cpp \
src/graph.cpp \
src/track.cpp \
src/parser.cpp \
src/poi.cpp \
src/ll.cpp \
src/axisitem.cpp \
src/poiitem.cpp \
src/colorshop.cpp \
src/slideritem.cpp \
src/markeritem.cpp \
src/infoitem.cpp \
@ -47,10 +104,87 @@ SOURCES += src/main.cpp \
src/speedgraph.cpp \
src/sliderinfoitem.cpp \
src/filebrowser.cpp \
src/map.cpp \
src/maplist.cpp \
src/downloader.cpp
src/onlinemap.cpp \
src/downloader.cpp \
src/scaleitem.cpp \
src/track.cpp \
src/graphview.cpp \
src/waypointitem.cpp \
src/palette.cpp \
src/heartrategraph.cpp \
src/range.cpp \
src/app.cpp \
src/trackinfo.cpp \
src/exportdialog.cpp \
src/fileselectwidget.cpp \
src/temperaturegraph.cpp \
src/trackpoint.cpp \
src/misc.cpp \
src/waypoint.cpp \
src/trackitem.cpp \
src/tooltip.cpp \
src/route.cpp \
src/routeitem.cpp \
src/graphitem.cpp \
src/pathitem.cpp \
src/pathview.cpp \
src/griditem.cpp \
src/data.cpp \
src/gpxparser.cpp \
src/tcxparser.cpp \
src/csvparser.cpp \
src/coordinates.cpp \
src/kmlparser.cpp \
src/fitparser.cpp \
src/format.cpp \
src/graph.cpp \
src/cadencegraph.cpp \
src/powergraph.cpp \
src/igcparser.cpp \
src/path.cpp \
src/nmeaparser.cpp \
src/optionsdialog.cpp \
src/colorbox.cpp \
src/stylecombobox.cpp \
src/emptymap.cpp \
src/offlinemap.cpp \
src/matrix.cpp \
src/tar.cpp \
src/atlas.cpp \
src/mercator.cpp \
src/transversemercator.cpp \
src/utm.cpp \
src/lambertconic.cpp \
src/ellipsoid.cpp \
src/ozf.cpp \
src/datum.cpp \
src/maplist.cpp
RESOURCES += gpxsee.qrc
TRANSLATIONS = lang/gpxsee_cs.ts
macx:ICON = icons/gpxsee.icns
win32:RC_FILE = gpxsee.rc
TRANSLATIONS = lang/gpxsee_cs.ts \
lang/gpxsee_sv.ts \
lang/gpxsee_de.ts
macx {
ICON = icons/gpxsee.icns
QMAKE_INFO_PLIST = Info.plist
APP_RESOURCES.files = icons/gpx.icns \
icons/tcx.icns \
icons/kml.icns \
icons/fit.icns \
icons/igc.icns \
icons/nmea.icns \
pkg/maps.txt \
pkg/ellipsoids.csv \
pkg/datums.csv
APP_RESOURCES.path = Contents/Resources
QMAKE_BUNDLE_DATA += APP_RESOURCES
}
win32 {
RC_ICONS = icons/gpxsee.ico \
icons/gpx.ico \
icons/tcx.ico \
icons/kml.ico \
icons/fit.ico \
icons/igc.ico \
icons/nmea.ico
}
DEFINES += APP_VERSION=\\\"$$VERSION\\\"

View File

@ -2,8 +2,8 @@
<qresource prefix="/">
<file>icons/dialog-close.png</file>
<file>icons/document-open.png</file>
<file>icons/document-save-as.png</file>
<file>icons/document-save.png</file>
<file>icons/document-print.png</file>
<file>icons/document-export.png</file>
<file>icons/flag.png</file>
<file>icons/gpxsee.png</file>
<file>icons/application-exit.png</file>
@ -13,6 +13,14 @@
<file>icons/arrow-right.png</file>
<file>icons/arrow-left-double.png</file>
<file>icons/arrow-right-double.png</file>
<file>icons/view-fullscreen.png</file>
<file>icons/office-chart-line-stacked.png</file>
<file>icons/preferences-desktop-display.png</file>
<file>icons/flag_48.png</file>
<file>icons/system-run.png</file>
<file>icons/document-print-preview.png</file>
<file>lang/gpxsee_cs.qm</file>
<file>lang/gpxsee_sv.qm</file>
<file>lang/gpxsee_de.qm</file>
</qresource>
</RCC>

View File

@ -1 +0,0 @@
IDI_ICON1 ICON DISCARDABLE "icons/gpxsee.ico"

BIN
icons/document-export.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
icons/document-print.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

BIN
icons/fit.icns Normal file

Binary file not shown.

BIN
icons/fit.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

171
icons/fit.svg Normal file
View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg3390"
height="185"
width="185"
inkscape:version="0.91 r13725"
sodipodi:docname="fit.svg">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1440"
inkscape:window-height="815"
id="namedview3427"
showgrid="false"
inkscape:zoom="3.1351351"
inkscape:cx="92.5"
inkscape:cy="92.5"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3390" />
<metadata
id="metadata3404">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3402" />
<path
style="fill:#ffffff;stroke:#999999;stroke-width:2.18774867"
id="path3392"
d="m 128.23996,2.2548915 -95.377018,0 0,181.0580085 134.394868,0 0,-141.313567 z m 0,0 0,39.7444415 39.01785,0" />
<rect
y="124.9782"
x="16.573463"
height="49.84631"
width="120.3215"
id="rect3426"
style="fill:#006600;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
id="g3828"
transform="matrix(0.89093793,0,0,0.84489692,3.5017807,-51.565424)">
<g
id="g3715">
<circle
style="fill:#000000"
cx="113"
cy="90.875"
id="ellipse3717"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-linejoin:round"
cx="113"
cy="90.875"
id="ellipse3719"
r="7.0209999" />
</g>
<polyline
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4"
points="62.3563,178.566 73.5,125.854 96,151.875 113,90.875 136.5,172.375 148.831,160.03 "
id="polyline3721" />
<g
id="g3723">
<circle
style="fill:#000000"
cx="73.5"
cy="125.854"
id="ellipse3725"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="73.5"
cy="125.854"
id="ellipse3727"
r="7.0209999" />
</g>
<g
id="g3729">
<circle
style="fill:#000000"
cx="136.5"
cy="172.375"
id="ellipse3731"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="136.5"
cy="172.375"
id="ellipse3733"
r="7.0209999" />
</g>
<g
id="g3735">
<circle
style="fill:#000000"
cx="60.700001"
cy="186.39999"
id="ellipse3737"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="60.700001"
cy="186.39999"
id="ellipse3739"
r="7.0209999" />
</g>
<g
id="g3741">
<circle
style="fill:#000000"
cx="154.5"
cy="154.354"
id="ellipse3743"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="154.5"
cy="154.354"
id="ellipse3745"
r="7.0209999" />
</g>
<g
id="g3747">
<circle
style="fill:#000000"
cx="96"
cy="151.875"
id="ellipse3749"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="96"
cy="151.875"
id="ellipse3751"
r="7.0209999" />
</g>
</g>
<text
transform="scale(0.9437456,1.0596076)"
id="text3921"
y="155.86783"
x="44.388157"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
sodipodi:linespacing="100%"><tspan
sodipodi:role="line"
id="tspan3429"
x="44.388157"
y="155.86783">FIT</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
icons/flag_48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
icons/gpx.icns Normal file

Binary file not shown.

BIN
icons/gpx.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

146
icons/gpx.svg Normal file
View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg3390"
height="185"
width="185">
<metadata
id="metadata3404">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3402" />
<path
style="fill:#ffffff;stroke:#999999;stroke-width:2.18774867"
id="path3392"
d="m 128.23996,2.2548915 -95.377018,0 0,181.0580085 134.394868,0 0,-141.313567 z m 0,0 0,39.7444415 39.01785,0" />
<rect
y="124.9782"
x="16.573463"
height="49.84631"
width="120.3215"
id="rect3426"
style="fill:#003399;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
id="g3828"
transform="matrix(0.89093793,0,0,0.84489692,3.5017807,-51.565424)">
<g
id="g3715">
<circle
style="fill:#000000"
cx="113"
cy="90.875"
id="ellipse3717"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-linejoin:round"
cx="113"
cy="90.875"
id="ellipse3719"
r="7.0209999" />
</g>
<polyline
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4"
points="62.3563,178.566 73.5,125.854 96,151.875 113,90.875 136.5,172.375 148.831,160.03 "
id="polyline3721" />
<g
id="g3723">
<circle
style="fill:#000000"
cx="73.5"
cy="125.854"
id="ellipse3725"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="73.5"
cy="125.854"
id="ellipse3727"
r="7.0209999" />
</g>
<g
id="g3729">
<circle
style="fill:#000000"
cx="136.5"
cy="172.375"
id="ellipse3731"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="136.5"
cy="172.375"
id="ellipse3733"
r="7.0209999" />
</g>
<g
id="g3735">
<circle
style="fill:#000000"
cx="60.700001"
cy="186.39999"
id="ellipse3737"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="60.700001"
cy="186.39999"
id="ellipse3739"
r="7.0209999" />
</g>
<g
id="g3741">
<circle
style="fill:#000000"
cx="154.5"
cy="154.354"
id="ellipse3743"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="154.5"
cy="154.354"
id="ellipse3745"
r="7.0209999" />
</g>
<g
id="g3747">
<circle
style="fill:#000000"
cx="96"
cy="151.875"
id="ellipse3749"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="96"
cy="151.875"
id="ellipse3751"
r="7.0209999" />
</g>
</g>
<text
transform="scale(0.9437456,1.0596076)"
id="text3921"
y="155.86783"
x="31.672857"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="155.86783"
x="31.672857"
id="tspan3923"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;writing-mode:lr-tb;text-anchor:start">GPX</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
icons/igc.icns Normal file

Binary file not shown.

BIN
icons/igc.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

145
icons/igc.svg Normal file
View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="185"
height="185"
id="svg3390"
version="1.1">
<metadata
id="metadata3404">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3402" />
<path
d="m 128.23996,2.2548915 -95.377018,0 0,181.0580085 134.394868,0 0,-141.313567 z m 0,0 0,39.7444415 39.01785,0"
id="path3392"
style="fill:#ffffff;stroke:#999999;stroke-width:2.18774867" />
<rect
style="fill:#ff3300;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect3426"
width="120.3215"
height="49.84631"
x="16.573463"
y="124.9782" />
<g
transform="matrix(0.89093793,0,0,0.84489692,3.5017807,-51.565424)"
id="g3828">
<g
id="g3715">
<circle
r="7.0209999"
id="ellipse3717"
cy="90.875"
cx="113"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3719"
cy="90.875"
cx="113"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-linejoin:round" />
</g>
<polyline
id="polyline3721"
points="62.3563,178.566 73.5,125.854 96,151.875 113,90.875 136.5,172.375 148.831,160.03 "
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4" />
<g
id="g3723">
<circle
r="7.0209999"
id="ellipse3725"
cy="125.854"
cx="73.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3727"
cy="125.854"
cx="73.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3729">
<circle
r="7.0209999"
id="ellipse3731"
cy="172.375"
cx="136.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3733"
cy="172.375"
cx="136.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3735">
<circle
r="7.0209999"
id="ellipse3737"
cy="186.39999"
cx="60.700001"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3739"
cy="186.39999"
cx="60.700001"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3741">
<circle
r="7.0209999"
id="ellipse3743"
cy="154.354"
cx="154.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3745"
cy="154.354"
cx="154.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3747">
<circle
r="7.0209999"
id="ellipse3749"
cy="151.875"
cx="96"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3751"
cy="151.875"
cx="96"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="40.149723"
y="155.86783"
id="text3921"
transform="scale(0.9437456,1.0596076)"><tspan
y="155.86783"
x="40.149723"
id="tspan3448">IGC</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
icons/kml.icns Normal file

Binary file not shown.

BIN
icons/kml.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

143
icons/kml.svg Normal file
View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="185"
height="185"
id="svg3390"
version="1.1">
<metadata
id="metadata3404">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3402" />
<path
d="m 128.23996,2.2548915 -95.377018,0 0,181.0580085 134.394868,0 0,-141.313567 z m 0,0 0,39.7444415 39.01785,0"
id="path3392"
style="fill:#ffffff;stroke:#999999;stroke-width:2.18774867" />
<rect
style="fill:#990000;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect3426"
width="120.3215"
height="49.84631"
x="16.573463"
y="124.9782" />
<g
transform="matrix(0.89093793,0,0,0.84489692,3.5017807,-51.565424)"
id="g3828">
<g
id="g3715">
<circle
r="7.0209999"
id="ellipse3717"
cy="90.875"
cx="113"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3719"
cy="90.875"
cx="113"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-linejoin:round" />
</g>
<polyline
id="polyline3721"
points="62.3563,178.566 73.5,125.854 96,151.875 113,90.875 136.5,172.375 148.831,160.03 "
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4" />
<g
id="g3723">
<circle
r="7.0209999"
id="ellipse3725"
cy="125.854"
cx="73.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3727"
cy="125.854"
cx="73.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3729">
<circle
r="7.0209999"
id="ellipse3731"
cy="172.375"
cx="136.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3733"
cy="172.375"
cx="136.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3735">
<circle
r="7.0209999"
id="ellipse3737"
cy="186.39999"
cx="60.700001"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3739"
cy="186.39999"
cx="60.700001"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3741">
<circle
r="7.0209999"
id="ellipse3743"
cy="154.354"
cx="154.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3745"
cy="154.354"
cx="154.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3747">
<circle
r="7.0209999"
id="ellipse3749"
cy="151.875"
cx="96"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3751"
cy="151.875"
cx="96"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
x="31.672857"
y="155.86783"
id="text3921"
transform="scale(0.9437456,1.0596076)"><tspan
id="tspan3429">KML</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
icons/nmea.icns Normal file

Binary file not shown.

BIN
icons/nmea.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

145
icons/nmea.svg Normal file
View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg3390"
height="185"
width="185">
<metadata
id="metadata3404">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3402" />
<path
style="fill:#ffffff;stroke:#999999;stroke-width:2.18774867"
id="path3392"
d="m 128.23996,2.2548915 -95.377018,0 0,181.0580085 134.394868,0 0,-141.313567 z m 0,0 0,39.7444415 39.01785,0" />
<rect
y="124.9782"
x="16.573463"
height="49.84631"
width="131.6837"
id="rect3426"
style="fill:#0083d7;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<g
id="g3828"
transform="matrix(0.89093793,0,0,0.84489692,3.5017807,-51.565424)">
<g
id="g3715">
<circle
style="fill:#000000"
cx="113"
cy="90.875"
id="ellipse3717"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-linejoin:round"
cx="113"
cy="90.875"
id="ellipse3719"
r="7.0209999" />
</g>
<polyline
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4"
points="62.3563,178.566 73.5,125.854 96,151.875 113,90.875 136.5,172.375 148.831,160.03 "
id="polyline3721" />
<g
id="g3723">
<circle
style="fill:#000000"
cx="73.5"
cy="125.854"
id="ellipse3725"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="73.5"
cy="125.854"
id="ellipse3727"
r="7.0209999" />
</g>
<g
id="g3729">
<circle
style="fill:#000000"
cx="136.5"
cy="172.375"
id="ellipse3731"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="136.5"
cy="172.375"
id="ellipse3733"
r="7.0209999" />
</g>
<g
id="g3735">
<circle
style="fill:#000000"
cx="60.700001"
cy="186.39999"
id="ellipse3737"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="60.700001"
cy="186.39999"
id="ellipse3739"
r="7.0209999" />
</g>
<g
id="g3741">
<circle
style="fill:#000000"
cx="154.5"
cy="154.354"
id="ellipse3743"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="154.5"
cy="154.354"
id="ellipse3745"
r="7.0209999" />
</g>
<g
id="g3747">
<circle
style="fill:#000000"
cx="96"
cy="151.875"
id="ellipse3749"
r="7.0209999" />
<circle
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2"
cx="96"
cy="151.875"
id="ellipse3751"
r="7.0209999" />
</g>
</g>
<text
transform="scale(0.9437456,1.0596076)"
id="text3921"
y="155.86783"
x="21.076782"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="155.86783"
x="21.076782"
id="tspan3429">NMEA</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
icons/system-run.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
icons/tcx.icns Normal file

Binary file not shown.

BIN
icons/tcx.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

145
icons/tcx.svg Normal file
View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
width="185"
height="185"
id="svg3390"
version="1.1">
<metadata
id="metadata3404">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3402" />
<path
d="m 128.23996,2.2548915 -95.377018,0 0,181.0580085 134.394868,0 0,-141.313567 z m 0,0 0,39.7444415 39.01785,0"
id="path3392"
style="fill:#ffffff;stroke:#999999;stroke-width:2.18774867" />
<rect
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect3426"
width="120.3215"
height="49.84631"
x="16.573463"
y="124.9782" />
<g
transform="matrix(0.89093793,0,0,0.84489692,3.5017807,-51.565424)"
id="g3828">
<g
id="g3715">
<circle
r="7.0209999"
id="ellipse3717"
cy="90.875"
cx="113"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3719"
cy="90.875"
cx="113"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-linejoin:round" />
</g>
<polyline
id="polyline3721"
points="62.3563,178.566 73.5,125.854 96,151.875 113,90.875 136.5,172.375 148.831,160.03 "
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:4" />
<g
id="g3723">
<circle
r="7.0209999"
id="ellipse3725"
cy="125.854"
cx="73.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3727"
cy="125.854"
cx="73.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3729">
<circle
r="7.0209999"
id="ellipse3731"
cy="172.375"
cx="136.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3733"
cy="172.375"
cx="136.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3735">
<circle
r="7.0209999"
id="ellipse3737"
cy="186.39999"
cx="60.700001"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3739"
cy="186.39999"
cx="60.700001"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3741">
<circle
r="7.0209999"
id="ellipse3743"
cy="154.354"
cx="154.5"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3745"
cy="154.354"
cx="154.5"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
<g
id="g3747">
<circle
r="7.0209999"
id="ellipse3749"
cy="151.875"
cx="96"
style="fill:#000000" />
<circle
r="7.0209999"
id="ellipse3751"
cy="151.875"
cx="96"
style="fill:none;fill-opacity:0;stroke:#000000;stroke-width:2" />
</g>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:40px;line-height:100%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="35.911289"
y="155.86783"
id="text3921"
transform="scale(0.9437456,1.0596076)"><tspan
y="155.86783"
x="35.911289"
id="tspan3429">TCX</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
icons/view-fullscreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

File diff suppressed because it is too large Load Diff

1100
lang/gpxsee_de.ts Normal file

File diff suppressed because it is too large Load Diff

1100
lang/gpxsee_sv.ts Normal file

File diff suppressed because it is too large Load Diff

123
pkg/datums.csv Normal file
View File

@ -0,0 +1,123 @@
Adindan,4201,5,-162,-12,206
Afgooye,4205,15,-43,-163,45
Ain el Abd 1970,4204,14,-150,-251,-2
Anna 1 Astro 1965,4708,2,-491,-22,435
Arc 1950,4209,5,-143,-90,-294
Arc 1960,4210,5,-160,-8,-300
Ascension Island 1958,4712,14,-207,107,52
Astro B4 Sorol Atoll,4707,14,114,-116,-333
Astro Beacon 1945,4709,14,145,75,-272
Astro DOS 71/4,4710,14,-320,550,-494
Astronomic Stn 1952,4711,14,124,-234,-25
Australian Geodetic 1966,4202,2,-133,-48,148
Australian Geodetic 1984,4203,2,-134,-48,149
Australian Geocentric 1994 (GDA94),4283,11,0,0,0
Austrian,4312,3,594,84,471
Bellevue (IGN),4714,14,-127,-769,472
Bermuda 1957,4216,4,-73,213,296
Bogota Observatory,4218,14,307,304,-318
Campo Inchauspe,4221,14,-148,136,90
Canton Astro 1966,4716,14,298,-304,-375
Cape,4222,5,-136,-108,-292
Cape Canaveral,4717,4,-2,150,181
Carthage,4223,5,-263,6,431
CH-1903,4149,3,674,15,405
Chatham 1971,4672,14,175,-38,113
Chua Astro,4224,14,-134,229,-29
Corrego Alegre,4225,14,-206,172,-6
Djakarta (Batavia),4211,3,-377,681,-50
DOS 1968,,14,230,-199,-752
Easter Island 1967,4719,14,211,147,111
Egypt,,14,-130,-117,-151
European 1950,4230,14,-87,-98,-121
European 1950 (Mean France),,14,-87,-96,-120
European 1950 (Spain and Portugal),,14,-84,-107,-120
European 1979,4668,14,-86,-98,-119
Finland Hayford,4123,14,-78,-231,-97
Gandajika Base,4233,14,-133,-321,50
Geodetic Datum 1949,4272,14,84,-22,209
GGRS 87,4121,11,-199.87,74.79,246.62
Guam 1963,4675,4,-100,-248,259
GUX 1 Astro,4718,14,252,-209,-751
Hartebeeshoek94,4148,20,0,0,0
Hermannskogel,3906,3,653,-212,449
Hjorsey 1955,4658,14,-73,46,-86
Hong Kong 1963,4739,14,-156,-271,-189
Hu-Tzu-Shan,4236,14,-634,-549,-201
Indian Bangladesh,4682,6,289,734,257
Indian Thailand,4240,6,214,836,303
Israeli,4281,23,-235,-85,264
Ireland 1965,4299,1,506,-122,611
ISTS 073 Astro 1969,4724,14,208,-435,-229
Johnston Island,4725,14,191,-77,-204
Kandawala,4244,6,-97,787,86
Kerguelen Island,4698,14,145,-187,103
Kertau 1948,4245,7,-11,851,5
L.C. 5 Astro,4726,4,42,124,147
Liberia 1964,4251,5,-90,40,88
Luzon Mindanao,,4,-133,-79,-72
Luzon Philippines,4253,4,-133,-77,-51
Mahe 1971,4256,5,41,-220,-134
Marco Astro,4616,14,-289,-124,60
Massawa,4262,3,639,405,60
Merchich,4261,5,31,146,47
Midway Astro 1961,4727,14,912,-58,1227
Minna,4263,5,-92,-93,122
NAD27 Alaska,,4,-5,135,172
NAD27 Bahamas,,4,-4,154,178
NAD27 Canada,,4,-10,158,187
NAD27 Canal Zone,,4,0,125,201
NAD27 Caribbean,,4,-7,152,178
NAD27 Central,,4,0,125,194
NAD27 CONUS,,4,-8,160,176
NAD27 Cuba,,4,-9,152,178
NAD27 Greenland,,4,11,114,195
NAD27 Mexico,,4,-12,130,190
NAD27 San Salvador,,4,1,140,165
NAD83,4269,11,0,0,0
Nahrwn Masirah Ilnd,,5,-247,-148,369
Nahrwn Saudi Arbia,,5,-231,-196,482
Nahrwn United Arab,,5,-249,-156,381
Naparima BWI,4271,14,-2,374,172
NGO1948,4273,27,315,-217,528
NTF France,4275,24,-168,-60,320
Norsk,4817,27,278,93,474
NZGD1949,4272,14,84,-22,209
NZGD2000,4167,20,0,0,0
Observatorio 1966,4182,14,-425,-169,81
Old Egyptian,4229,12,-130,110,-13
Old Hawaiian,4135,4,61,-285,-181
Oman,4232,5,-346,-1,224
Ord Srvy Grt Britn,4277,0,375,-111,431
Pico De Las Nieves,4728,14,-307,-92,127
Pitcairn Astro 1967,4729,14,185,165,42
Potsdam Rauenberg DHDN,4314,3,606,23,413
Prov So Amrican 1956,4248,14,-288,175,-376
Prov So Chilean 1963,4254,14,16,196,93
Puerto Rico,4139,4,11,72,-101
Pulkovo 1942 (1),4284,15,28,-130,-95
Pulkovo 1942 (2),4284,15,28,-130,-95
Qatar National,4285,14,-128,-283,22
Qornoq,4287,14,164,138,-189
Reunion,4626,14,94,-948,-1262
Rijksdriehoeksmeting,4289,3,593,26,478
Rome 1940,4806,14,-225,-65,9
RT 90,4124,3,498,-36,568
S42,4179,15,28,-121,-77
Santo (DOS),4730,14,170,42,84
Sao Braz,4184,14,-203,141,53
Sapper Hill 1943,4292,14,-355,16,74
Schwarzeck,4293,21,616,97,-251
South American 1969,4291,16,-57,1,-41
South Asia,,8,7,-10,-26
Southeast Base,4615,14,-499,-249,314
Southwest Base,4183,14,-104,167,-38
Timbalai 1948,4298,6,-689,691,-46
Tokyo,4301,3,-128,481,664
Tristan Astro 1968,4734,14,-632,438,-609
Viti Levu 1916,4731,5,51,391,-36
Wake-Eniwetok 1960,4732,13,101,52,-39
WGS 72,4322,19,0,0,5
WGS 84,4326,20,0,0,0
Yacare,4309,14,-155,171,37
Zanderij,4311,14,-265,120,-358
1 Adindan 4201 5 -162 -12 206
2 Afgooye 4205 15 -43 -163 45
3 Ain el Abd 1970 4204 14 -150 -251 -2
4 Anna 1 Astro 1965 4708 2 -491 -22 435
5 Arc 1950 4209 5 -143 -90 -294
6 Arc 1960 4210 5 -160 -8 -300
7 Ascension Island 1958 4712 14 -207 107 52
8 Astro B4 Sorol Atoll 4707 14 114 -116 -333
9 Astro Beacon 1945 4709 14 145 75 -272
10 Astro DOS 71/4 4710 14 -320 550 -494
11 Astronomic Stn 1952 4711 14 124 -234 -25
12 Australian Geodetic 1966 4202 2 -133 -48 148
13 Australian Geodetic 1984 4203 2 -134 -48 149
14 Australian Geocentric 1994 (GDA94) 4283 11 0 0 0
15 Austrian 4312 3 594 84 471
16 Bellevue (IGN) 4714 14 -127 -769 472
17 Bermuda 1957 4216 4 -73 213 296
18 Bogota Observatory 4218 14 307 304 -318
19 Campo Inchauspe 4221 14 -148 136 90
20 Canton Astro 1966 4716 14 298 -304 -375
21 Cape 4222 5 -136 -108 -292
22 Cape Canaveral 4717 4 -2 150 181
23 Carthage 4223 5 -263 6 431
24 CH-1903 4149 3 674 15 405
25 Chatham 1971 4672 14 175 -38 113
26 Chua Astro 4224 14 -134 229 -29
27 Corrego Alegre 4225 14 -206 172 -6
28 Djakarta (Batavia) 4211 3 -377 681 -50
29 DOS 1968 14 230 -199 -752
30 Easter Island 1967 4719 14 211 147 111
31 Egypt 14 -130 -117 -151
32 European 1950 4230 14 -87 -98 -121
33 European 1950 (Mean France) 14 -87 -96 -120
34 European 1950 (Spain and Portugal) 14 -84 -107 -120
35 European 1979 4668 14 -86 -98 -119
36 Finland Hayford 4123 14 -78 -231 -97
37 Gandajika Base 4233 14 -133 -321 50
38 Geodetic Datum 1949 4272 14 84 -22 209
39 GGRS 87 4121 11 -199.87 74.79 246.62
40 Guam 1963 4675 4 -100 -248 259
41 GUX 1 Astro 4718 14 252 -209 -751
42 Hartebeeshoek94 4148 20 0 0 0
43 Hermannskogel 3906 3 653 -212 449
44 Hjorsey 1955 4658 14 -73 46 -86
45 Hong Kong 1963 4739 14 -156 -271 -189
46 Hu-Tzu-Shan 4236 14 -634 -549 -201
47 Indian Bangladesh 4682 6 289 734 257
48 Indian Thailand 4240 6 214 836 303
49 Israeli 4281 23 -235 -85 264
50 Ireland 1965 4299 1 506 -122 611
51 ISTS 073 Astro 1969 4724 14 208 -435 -229
52 Johnston Island 4725 14 191 -77 -204
53 Kandawala 4244 6 -97 787 86
54 Kerguelen Island 4698 14 145 -187 103
55 Kertau 1948 4245 7 -11 851 5
56 L.C. 5 Astro 4726 4 42 124 147
57 Liberia 1964 4251 5 -90 40 88
58 Luzon Mindanao 4 -133 -79 -72
59 Luzon Philippines 4253 4 -133 -77 -51
60 Mahe 1971 4256 5 41 -220 -134
61 Marco Astro 4616 14 -289 -124 60
62 Massawa 4262 3 639 405 60
63 Merchich 4261 5 31 146 47
64 Midway Astro 1961 4727 14 912 -58 1227
65 Minna 4263 5 -92 -93 122
66 NAD27 Alaska 4 -5 135 172
67 NAD27 Bahamas 4 -4 154 178
68 NAD27 Canada 4 -10 158 187
69 NAD27 Canal Zone 4 0 125 201
70 NAD27 Caribbean 4 -7 152 178
71 NAD27 Central 4 0 125 194
72 NAD27 CONUS 4 -8 160 176
73 NAD27 Cuba 4 -9 152 178
74 NAD27 Greenland 4 11 114 195
75 NAD27 Mexico 4 -12 130 190
76 NAD27 San Salvador 4 1 140 165
77 NAD83 4269 11 0 0 0
78 Nahrwn Masirah Ilnd 5 -247 -148 369
79 Nahrwn Saudi Arbia 5 -231 -196 482
80 Nahrwn United Arab 5 -249 -156 381
81 Naparima BWI 4271 14 -2 374 172
82 NGO1948 4273 27 315 -217 528
83 NTF France 4275 24 -168 -60 320
84 Norsk 4817 27 278 93 474
85 NZGD1949 4272 14 84 -22 209
86 NZGD2000 4167 20 0 0 0
87 Observatorio 1966 4182 14 -425 -169 81
88 Old Egyptian 4229 12 -130 110 -13
89 Old Hawaiian 4135 4 61 -285 -181
90 Oman 4232 5 -346 -1 224
91 Ord Srvy Grt Britn 4277 0 375 -111 431
92 Pico De Las Nieves 4728 14 -307 -92 127
93 Pitcairn Astro 1967 4729 14 185 165 42
94 Potsdam Rauenberg DHDN 4314 3 606 23 413
95 Prov So Amrican 1956 4248 14 -288 175 -376
96 Prov So Chilean 1963 4254 14 16 196 93
97 Puerto Rico 4139 4 11 72 -101
98 Pulkovo 1942 (1) 4284 15 28 -130 -95
99 Pulkovo 1942 (2) 4284 15 28 -130 -95
100 Qatar National 4285 14 -128 -283 22
101 Qornoq 4287 14 164 138 -189
102 Reunion 4626 14 94 -948 -1262
103 Rijksdriehoeksmeting 4289 3 593 26 478
104 Rome 1940 4806 14 -225 -65 9
105 RT 90 4124 3 498 -36 568
106 S42 4179 15 28 -121 -77
107 Santo (DOS) 4730 14 170 42 84
108 Sao Braz 4184 14 -203 141 53
109 Sapper Hill 1943 4292 14 -355 16 74
110 Schwarzeck 4293 21 616 97 -251
111 South American 1969 4291 16 -57 1 -41
112 South Asia 8 7 -10 -26
113 Southeast Base 4615 14 -499 -249 314
114 Southwest Base 4183 14 -104 167 -38
115 Timbalai 1948 4298 6 -689 691 -46
116 Tokyo 4301 3 -128 481 664
117 Tristan Astro 1968 4734 14 -632 438 -609
118 Viti Levu 1916 4731 5 51 391 -36
119 Wake-Eniwetok 1960 4732 13 101 52 -39
120 WGS 72 4322 19 0 0 5
121 WGS 84 4326 20 0 0 0
122 Yacare 4309 14 -155 171 37
123 Zanderij 4311 14 -265 120 -358

30
pkg/ellipsoids.csv Normal file
View File

@ -0,0 +1,30 @@
0,Airy 1830,6377563.396,299.3249646
1,Modified Airy,6377340.189,299.3249646
2,Australian National,6378160.0,298.25
3,Bessel 1841,6377397.155,299.1528128
4,Clarke 1866,6378206.4,294.9786982
5,Clarke 1880,6378249.145,293.465
6,Everest (India 1830),6377276.345,300.8017
7,Everest (1948),6377304.063,300.8017
8,Modified Fischer 1960,6378155.0,298.3
9,Everest (Pakistan),6377309.613,300.8017
10,Indonesian 1974,6378160.0,298.247
11,GRS 80,6378137.0,298.257222101
12,Helmert 1906,6378200.0,298.3
13,Hough 1960,6378270.0,297.0
14,International 1924,6378388.0,297.0
15,Krassovsky 1940,6378245.0,298.3
16,South American 1969,6378160.0,298.25
17,Everest (Malaysia 1969),6377295.664,300.8017
18,Everest (Sabah Sarawak),6377298.556,300.8017
19,WGS 72,6378135.0,298.26
20,WGS 84,6378137.0,298.257223563
21,Bessel 1841 (Namibia),6377483.865,299.1528128
22,Everest (India 1956),6377301.243,300.8017
23,Clarke 1880 Palestine,6378300.789,293.466
24,Clarke 1880 IGN,6378249.2,293.466021
25,Hayford 1909,6378388.0,296.959263
26,Clarke 1858,6378350.87,294.26
27,Bessel 1841 (Norway),6377492.0176,299.1528
28,Plessis 1817 (France),6376523.0,308.6409971
29,Hayford 1924,6378388.0,297.0
1 0 Airy 1830 6377563.396 299.3249646
2 1 Modified Airy 6377340.189 299.3249646
3 2 Australian National 6378160.0 298.25
4 3 Bessel 1841 6377397.155 299.1528128
5 4 Clarke 1866 6378206.4 294.9786982
6 5 Clarke 1880 6378249.145 293.465
7 6 Everest (India 1830) 6377276.345 300.8017
8 7 Everest (1948) 6377304.063 300.8017
9 8 Modified Fischer 1960 6378155.0 298.3
10 9 Everest (Pakistan) 6377309.613 300.8017
11 10 Indonesian 1974 6378160.0 298.247
12 11 GRS 80 6378137.0 298.257222101
13 12 Helmert 1906 6378200.0 298.3
14 13 Hough 1960 6378270.0 297.0
15 14 International 1924 6378388.0 297.0
16 15 Krassovsky 1940 6378245.0 298.3
17 16 South American 1969 6378160.0 298.25
18 17 Everest (Malaysia 1969) 6377295.664 300.8017
19 18 Everest (Sabah Sarawak) 6377298.556 300.8017
20 19 WGS 72 6378135.0 298.26
21 20 WGS 84 6378137.0 298.257223563
22 21 Bessel 1841 (Namibia) 6377483.865 299.1528128
23 22 Everest (India 1956) 6377301.243 300.8017
24 23 Clarke 1880 Palestine 6378300.789 293.466
25 24 Clarke 1880 IGN 6378249.2 293.466021
26 25 Hayford 1909 6378388.0 296.959263
27 26 Clarke 1858 6378350.87 294.26
28 27 Bessel 1841 (Norway) 6377492.0176 299.1528
29 28 Plessis 1817 (France) 6376523.0 308.6409971
30 29 Hayford 1924 6378388.0 297.0

BIN
pkg/gpxsee.cer Normal file

Binary file not shown.

255
pkg/gpxsee.nsi Normal file
View File

@ -0,0 +1,255 @@
!include "MUI2.nsh"
!include "x64.nsh"
!include "WinVer.nsh"
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "4.5"
; The file to write
OutFile "GPXSee-${VERSION}.exe"
; Required execution level
RequestExecutionLevel admin
; The default installation directory
InstallDir "$PROGRAMFILES\GPXSee"
; Installer executable info
VIProductVersion "${VERSION}.0.0"
VIAddVersionKey "ProductVersion" ${VERSION}
VIAddVersionKey "FileVersion" "${VERSION}.0.0"
VIAddVersionKey "ProductName" "GPXSee"
VIAddVersionKey "LegalCopyright" "GPXSee project"
VIAddVersionKey "FileDescription" "GPXSee installer"
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically)
InstallDirRegKey HKLM "Software\GPXSee" "Install_Dir"
; Registry key for uninstaller
!define REGENTRY "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPXSee"
; File types registry entries
!define REGGPX "GPXSee.gpx"
!define REGTCX "GPXSee.tcx"
!define REGKML "GPXSee.kml"
!define REGFIT "GPXSee.fit"
!define REGIGC "GPXSee.igc"
!define REGNMEA "GPXSee.nmea"
; Start menu page configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\GPXSee"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "GPXSee"
Var StartMenuFolder
;--------------------------------
; Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "licence.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
; Languages
!insertmacro MUI_LANGUAGE "English"
Function .onInit
${IfNot} ${AtLeastWin7}
MessageBox MB_OK "GPXSee can only be installed on Windows 7 or later."
Abort
${EndIf}
FunctionEnd
; The stuff to install
Section "GPXSee" SEC_APP
SectionIn RO
; Set output path to the installation directory.
SetOutPath $INSTDIR
; Put the files there
File "gpxsee.exe"
File "maps.txt"
File "ellipsoids.csv"
File "datums.csv"
; Create start menu entry and add links
SetShellVarContext all
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GPXSee.lnk" "$INSTDIR\gpxsee.exe"
!insertmacro MUI_STARTMENU_WRITE_END
; Create the uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
; Write the installation path into the registry
DetailPrint "Registering application..."
WriteRegStr HKLM SOFTWARE\GPXSee "Install_Dir" "$INSTDIR"
; Write the uninstall keys for Windows
WriteRegStr HKLM "${REGENTRY}" "DisplayName" "GPXSee"
WriteRegStr HKLM "${REGENTRY}" "Publisher" "Martin Tuma"
WriteRegStr HKLM "${REGENTRY}" "DisplayVersion" "${VERSION}"
WriteRegStr HKLM "${REGENTRY}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKLM "${REGENTRY}" "NoModify" 1
WriteRegDWORD HKLM "${REGENTRY}" "NoRepair" 1
; Associate file formats
DetailPrint "Associating file types..."
WriteRegStr HKCR ".gpx" "" "${REGGPX}"
WriteRegStr HKCR "${REGGPX}" "" "GPS Exchange Format"
WriteRegStr HKCR "${REGGPX}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,1"
WriteRegStr HKCR "${REGGPX}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".tcx" "" "${REGTCX}"
WriteRegStr HKCR "${REGTCX}" "" "Training Center XML"
WriteRegStr HKCR "${REGTCX}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,2"
WriteRegStr HKCR "${REGTCX}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".kml" "" "${REGKML}"
WriteRegStr HKCR "${REGKML}" "" "Keyhole Markup Language"
WriteRegStr HKCR "${REGKML}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,3"
WriteRegStr HKCR "${REGKML}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".fit" "" "${REGFIT}"
WriteRegStr HKCR "${REGFIT}" "" "Flexible and Interoperable Data Transfer"
WriteRegStr HKCR "${REGFIT}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,4"
WriteRegStr HKCR "${REGFIT}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".igc" "" "${REGIGC}"
WriteRegStr HKCR "${REGIGC}" "" "Flight Recorder Data Format"
WriteRegStr HKCR "${REGIGC}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,5"
WriteRegStr HKCR "${REGIGC}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".nmea" "" "${REGNMEA}"
WriteRegStr HKCR "${REGNMEA}" "" "NMEA 0183 data"
WriteRegStr HKCR "${REGNMEA}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,6"
WriteRegStr HKCR "${REGNMEA}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd
Section "QT framework" SEC_QT
SectionIn RO
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Widgets.dll"
File "Qt5PrintSupport.dll"
File "Qt5Network.dll"
File /r "platforms"
File /r "imageformats"
File /r "printsupport"
SectionEnd
Section "MSVC runtime" SEC_MSVC
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
File "VC_redist.x86.exe"
ExecWait '"$TEMP/VC_redist.x86.exe" /install /quiet /norestart'
SetOutPath $INSTDIR
done:
SectionEnd
Section "OpenSSL" SEC_OPENSSL
File "libeay32.dll"
File "ssleay32.dll"
SectionEnd
Section "ANGLE" SEC_ANGLE
File "libGLESv2.dll"
File "libEGL.dll"
File "D3DCompiler_47.dll"
SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
; Remove registry keys
DeleteRegKey HKLM "${REGENTRY}"
DeleteRegKey HKLM SOFTWARE\GPXSee
; Remove directories used
RMDir /r "$INSTDIR"
; Remove Start menu entries
SetShellVarContext all
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder
Delete "$SMPROGRAMS\$StartMenuFolder\*.*"
RMDir "$SMPROGRAMS\$StartMenuFolder"
; Remove GPX file association
DeleteRegKey HKCR "${REGGPX}"
DeleteRegKey HKCR ".gpx"
DeleteRegKey HKCR "${REGTCX}"
DeleteRegKey HKCR ".tcx"
DeleteRegKey HKCR "${REGKML}"
DeleteRegKey HKCR ".kml"
DeleteRegKey HKCR "${REGFIT}"
DeleteRegKey HKCR ".fit"
DeleteRegKey HKCR "${REGIGC}"
DeleteRegKey HKCR ".igc"
DeleteRegKey HKCR "${REGNMEA}"
DeleteRegKey HKCR ".nmea"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd
;-------------------------------
;Descriptions
;Language strings
LangString DESC_QT ${LANG_ENGLISH} \
"QT cross-platform application framework."
LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2015 runtime components. If already installed, will be skipped."
LangString DESC_OPENSSL ${LANG_ENGLISH} \
"OpenSSL library. Required for HTTPS to work."
LangString DESC_ANGLE ${LANG_ENGLISH} \
"ANGLE (OpenGL via Direct3D). Enables OpenGL on systems without native OpenGL drivers."
LangString DESC_APP ${LANG_ENGLISH} \
"GPXSee application"
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QT} $(DESC_QT)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_OPENSSL} $(DESC_OPENSSL)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_ANGLE} $(DESC_ANGLE)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_MSVC} $(DESC_MSVC)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APP} $(DESC_APP)
!insertmacro MUI_FUNCTION_DESCRIPTION_END

258
pkg/gpxsee64.nsi Normal file
View File

@ -0,0 +1,258 @@
!include "MUI2.nsh"
!include "x64.nsh"
!include "WinVer.nsh"
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "4.5"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"
; Required execution level
RequestExecutionLevel admin
; The default installation directory
InstallDir "$PROGRAMFILES64\GPXSee"
; Installer executable info
VIProductVersion "${VERSION}.0.0"
VIAddVersionKey "ProductVersion" ${VERSION}
VIAddVersionKey "FileVersion" "${VERSION}.0.0"
VIAddVersionKey "ProductName" "GPXSee"
VIAddVersionKey "LegalCopyright" "GPXSee project"
VIAddVersionKey "FileDescription" "GPXSee installer (x64)"
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically)
InstallDirRegKey HKLM "Software\GPXSee" "Install_Dir"
; Registry key for uninstaller
!define REGENTRY "Software\Microsoft\Windows\CurrentVersion\Uninstall\GPXSee"
; File types registry entries
!define REGGPX "GPXSee.gpx"
!define REGTCX "GPXSee.tcx"
!define REGKML "GPXSee.kml"
!define REGFIT "GPXSee.fit"
!define REGIGC "GPXSee.igc"
!define REGNMEA "GPXSee.nmea"
; Start menu page configuration
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\GPXSee"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "GPXSee"
Var StartMenuFolder
;--------------------------------
; Pages
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "licence.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
; Languages
!insertmacro MUI_LANGUAGE "English"
Function .onInit
${IfNot} ${AtLeastWin7}
MessageBox MB_OK "GPXSee can only be installed on Windows 7 or later."
Abort
${EndIf}
${If} ${RunningX64}
SetRegView 64
${Else}
MessageBox MB_OK "The 64b version of GPXSee can not be run on 32b systems."
Abort
${EndIf}
FunctionEnd
; The stuff to install
Section "GPXSee" SEC_APP
SectionIn RO
; Set output path to the installation directory.
SetOutPath $INSTDIR
; Put the files there
File "gpxsee.exe"
File "maps.txt"
File "ellipsoids.csv"
File "datums.csv"
; Create start menu entry and add links
SetShellVarContext all
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GPXSee.lnk" "$INSTDIR\gpxsee.exe"
!insertmacro MUI_STARTMENU_WRITE_END
; Create the uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
; Write the installation path into the registry
DetailPrint "Registering application..."
WriteRegStr HKLM SOFTWARE\GPXSee "Install_Dir" "$INSTDIR"
; Write the uninstall keys for Windows
WriteRegStr HKLM "${REGENTRY}" "DisplayName" "GPXSee (x64)"
WriteRegStr HKLM "${REGENTRY}" "Publisher" "Martin Tuma"
WriteRegStr HKLM "${REGENTRY}" "DisplayVersion" "${VERSION}"
WriteRegStr HKLM "${REGENTRY}" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKLM "${REGENTRY}" "NoModify" 1
WriteRegDWORD HKLM "${REGENTRY}" "NoRepair" 1
; Associate file formats
DetailPrint "Associating file types..."
WriteRegStr HKCR ".gpx" "" "${REGGPX}"
WriteRegStr HKCR "${REGGPX}" "" "GPS Exchange Format"
WriteRegStr HKCR "${REGGPX}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,1"
WriteRegStr HKCR "${REGGPX}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".tcx" "" "${REGTCX}"
WriteRegStr HKCR "${REGTCX}" "" "Training Center XML"
WriteRegStr HKCR "${REGTCX}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,2"
WriteRegStr HKCR "${REGTCX}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".kml" "" "${REGKML}"
WriteRegStr HKCR "${REGKML}" "" "Keyhole Markup Language"
WriteRegStr HKCR "${REGKML}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,3"
WriteRegStr HKCR "${REGKML}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".fit" "" "${REGFIT}"
WriteRegStr HKCR "${REGFIT}" "" "Flexible and Interoperable Data Transfer"
WriteRegStr HKCR "${REGFIT}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,4"
WriteRegStr HKCR "${REGFIT}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".igc" "" "${REGIGC}"
WriteRegStr HKCR "${REGIGC}" "" "Flight Recorder Data Format"
WriteRegStr HKCR "${REGIGC}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,5"
WriteRegStr HKCR "${REGIGC}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
WriteRegStr HKCR ".nmea" "" "${REGNMEA}"
WriteRegStr HKCR "${REGNMEA}" "" "NMEA 0183 data"
WriteRegStr HKCR "${REGNMEA}\DefaultIcon" "" "$INSTDIR\GPXSee.exe,6"
WriteRegStr HKCR "${REGNMEA}\shell\open\command" "" "$\"$INSTDIR\GPXSee.exe$\" $\"%1$\""
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd
Section "QT framework" SEC_QT
SectionIn RO
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Widgets.dll"
File "Qt5PrintSupport.dll"
File "Qt5Network.dll"
File /r "platforms"
File /r "imageformats"
File /r "printsupport"
SectionEnd
Section "MSVC runtime" SEC_MSVC
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
File "VC_redist.x64.exe"
ExecWait '"$TEMP/VC_redist.x64.exe" /install /quiet /norestart'
SetOutPath $INSTDIR
done:
SectionEnd
Section "OpenSSL" SEC_OPENSSL
File "libeay32.dll"
File "ssleay32.dll"
SectionEnd
Section "ANGLE" SEC_ANGLE
File "libGLESv2.dll"
File "libEGL.dll"
File "D3DCompiler_47.dll"
SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
; Remove registry keys
SetRegView 64
DeleteRegKey HKLM "${REGENTRY}"
DeleteRegKey HKLM SOFTWARE\GPXSee
; Remove directories used
RMDir /r "$INSTDIR"
; Remove Start menu entries
SetShellVarContext all
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder
Delete "$SMPROGRAMS\$StartMenuFolder\*.*"
RMDir "$SMPROGRAMS\$StartMenuFolder"
; Remove File associations
DeleteRegKey HKCR "${REGGPX}"
DeleteRegKey HKCR ".gpx"
DeleteRegKey HKCR "${REGTCX}"
DeleteRegKey HKCR ".tcx"
DeleteRegKey HKCR "${REGKML}"
DeleteRegKey HKCR ".kml"
DeleteRegKey HKCR "${REGFIT}"
DeleteRegKey HKCR ".fit"
DeleteRegKey HKCR "${REGIGC}"
DeleteRegKey HKCR ".igc"
DeleteRegKey HKCR "${REGNMEA}"
DeleteRegKey HKCR ".nmea"
System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)'
SectionEnd
;-------------------------------
;Descriptions
;Language strings
LangString DESC_QT ${LANG_ENGLISH} \
"QT cross-platform application framework."
LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2015 runtime components. If already installed, will be skipped."
LangString DESC_OPENSSL ${LANG_ENGLISH} \
"OpenSSL library. Required for HTTPS to work."
LangString DESC_ANGLE ${LANG_ENGLISH} \
"ANGLE (OpenGL via Direct3D). Enables OpenGL on systems without native OpenGL drivers."
LangString DESC_APP ${LANG_ENGLISH} \
"GPXSee application"
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QT} $(DESC_QT)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_OPENSSL} $(DESC_OPENSSL)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_ANGLE} $(DESC_ANGLE)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_MSVC} $(DESC_MSVC)
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APP} $(DESC_APP)
!insertmacro MUI_FUNCTION_DESCRIPTION_END

3
pkg/maps.txt Normal file
View File

@ -0,0 +1,3 @@
Open Topo Map https://a.tile.opentopomap.org/$z/$x/$y.png
Thunderforest http://tile.thunderforest.com/outdoors/$z/$x/$y.png
Open Street Map http://tile.openstreetmap.org/$z/$x/$y.png

57
src/app.cpp Normal file
View File

@ -0,0 +1,57 @@
#include <QtGlobal>
#include <QTranslator>
#include <QLocale>
#include <QFileOpenEvent>
#include <QNetworkProxyFactory>
#include <QPixmapCache>
#include "opengl.h"
#include "gui.h"
#include "onlinemap.h"
#include "downloader.h"
#include "app.h"
App::App(int &argc, char **argv) : QApplication(argc, argv),
_argc(argc), _argv(argv)
{
QTranslator *translator = new QTranslator(this);
QString locale = QLocale::system().name();
translator->load(QString(":/lang/gpxsee_") + locale);
installTranslator(translator);
#ifdef Q_OS_MAC
setAttribute(Qt::AA_DontShowIconsInMenus);
#endif // Q_OS_MAC
QNetworkProxyFactory::setUseSystemConfiguration(true);
QPixmapCache::setCacheLimit(65536);
OnlineMap::setDownloader(new Downloader(this));
OPENGL_SET_SAMPLES(4);
_gui = new GUI();
}
App::~App()
{
delete _gui;
}
void App::run()
{
_gui->show();
for (int i = 1; i < _argc; i++)
_gui->openFile(QString::fromLocal8Bit(_argv[i]));
exec();
}
bool App::event(QEvent *event)
{
if (event->type() == QEvent::FileOpen) {
QFileOpenEvent *e = static_cast<QFileOpenEvent *>(event);
return _gui->openFile(e->file());
}
return QApplication::event(event);
}

26
src/app.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef APP_H
#define APP_H
#include <QApplication>
class GUI;
class App : QApplication
{
Q_OBJECT
public:
App(int &argc, char **argv);
~App();
void run();
protected:
bool event(QEvent *event);
private:
int &_argc;
char **_argv;
GUI *_gui;
};
#endif // APP_H

11
src/assert.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef ASSERT_H
#define ASSERT_H
template<bool> struct CompileTimeAssert;
template<> struct CompileTimeAssert <true> {};
#define STATIC_ASSERT(e) \
(CompileTimeAssert <(e) != 0>())
#endif // ASSERT_H

303
src/atlas.cpp Normal file
View File

@ -0,0 +1,303 @@
#include <QDir>
#include <QtAlgorithms>
#include <QPainter>
#include "tar.h"
#include "atlas.h"
#define ZOOM_THRESHOLD 0.9
#define TL(m) ((m)->xy2pp((m)->bounds().topLeft()))
#define BR(m) ((m)->xy2pp((m)->bounds().bottomRight()))
static bool resCmp(const OfflineMap *m1, const OfflineMap *m2)
{
qreal r1, r2;
r1 = m1->resolution(m1->bounds().center());
r2 = m2->resolution(m2->bounds().center());
return r1 > r2;
}
static bool xCmp(const OfflineMap *m1, const OfflineMap *m2)
{
return TL(m1).x() < TL(m2).x();
}
static bool yCmp(const OfflineMap *m1, const OfflineMap *m2)
{
return TL(m1).y() > TL(m2).y();
}
bool Atlas::isAtlas(Tar &tar, const QString &path)
{
QFileInfo fi(path);
QByteArray ba;
QString suffix = fi.suffix().toLower();
if (suffix == "tar") {
if (!tar.load(path)) {
_errorString = "Error reading tar file";
return false;
}
QString tbaFileName = fi.completeBaseName() + ".tba";
ba = tar.file(tbaFileName);
} else if (suffix == "tba") {
QFile tbaFile(path);
if (!tbaFile.open(QIODevice::ReadOnly)) {
_errorString = QString("Error opening tba file: %1")
.arg(tbaFile.errorString());
return false;
}
ba = tbaFile.readAll();
}
if (ba.startsWith("Atlas 1.0"))
return true;
else {
_errorString = "Missing or invalid tba file";
return false;
}
}
void Atlas::computeZooms()
{
qSort(_maps.begin(), _maps.end(), resCmp);
_zooms.append(QPair<int, int>(0, _maps.count() - 1));
for (int i = 1; i < _maps.count(); i++) {
qreal last = _maps.at(i-1)->resolution(_maps.at(i)->bounds().center());
qreal cur = _maps.at(i)->resolution(_maps.at(i)->bounds().center());
if (cur < last * ZOOM_THRESHOLD) {
_zooms.last().second = i-1;
_zooms.append(QPair<int, int>(i, _maps.count() - 1));
}
}
}
void Atlas::computeBounds()
{
QList<QPointF> offsets;
for (int i = 0; i < _maps.count(); i++)
offsets.append(QPointF());
for (int z = 0; z < _zooms.count(); z++) {
qreal w = 0, h = 0;
QList<OfflineMap*> m;
for (int i = _zooms.at(z).first; i <= _zooms.at(z).second; i++)
m.append(_maps.at(i));
qSort(m.begin(), m.end(), xCmp);
offsets[_maps.indexOf(m.first())].setX(w);
for (int i = 1; i < m.size(); i++) {
w += round(m.at(i-1)->pp2xy(TL(m.at(i))).x());
offsets[_maps.indexOf(m.at(i))].setX(w);
}
qSort(m.begin(), m.end(), yCmp);
offsets[_maps.indexOf(m.first())].setY(h);
for (int i = 1; i < m.size(); i++) {
h += round(m.at(i-1)->pp2xy(TL(m.at(i))).y());
offsets[_maps.indexOf(m.at(i))].setY(h);
}
}
for (int i = 0; i < _maps.count(); i++)
_bounds.append(QPair<QRectF, QRectF>(QRectF(TL(_maps.at(i)),
BR(_maps.at(i))), QRectF(offsets.at(i), _maps.at(i)->bounds().size())));
}
Atlas::Atlas(const QString &fileName, QObject *parent) : Map(parent)
{
Tar tar;
QFileInfo fi(fileName);
_valid = false;
_zoom = 0;
_name = fi.dir().dirName();
if (!isAtlas(tar, fileName))
return;
QDir dir(fi.absolutePath());
QFileInfoList layers = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (int n = 0; n < layers.count(); n++) {
QDir zdir(layers.at(n).absoluteFilePath());
QFileInfoList maps = zdir.entryInfoList(QDir::Dirs
| QDir::NoDotAndDotDot);
for (int i = 0; i < maps.count(); i++) {
QString mapFile = maps.at(i).absoluteFilePath() + "/"
+ maps.at(i).fileName() + ".map";
OfflineMap *map;
if (tar.isOpen())
map = new OfflineMap(mapFile, tar, this);
else
map = new OfflineMap(mapFile, this);
if (map->isValid())
_maps.append(map);
else {
_errorString = QString("Error loading map: %1: %2")
.arg(mapFile, map->errorString());
return;
}
}
}
if (_maps.isEmpty()) {
_errorString = "No maps found in atlas";
return;
}
computeZooms();
computeBounds();
_valid = true;
}
Atlas::~Atlas()
{
for (int i = 0; i < _maps.size(); i++)
delete _maps.at(i);
}
QRectF Atlas::bounds() const
{
QSizeF s(0, 0);
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).second.right() > s.width())
s.setWidth(_bounds.at(i).second.right());
if (_bounds.at(i).second.bottom() > s.height())
s.setHeight(_bounds.at(i).second.bottom());
}
return QRectF(QPointF(0, 0), s);
}
qreal Atlas::resolution(const QPointF &p) const
{
int idx = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).second.contains(_maps.at(i)->xy2pp(p))) {
idx = i;
break;
}
}
return _maps.at(idx)->resolution(p);
}
qreal Atlas::zoom() const
{
return _zoom;
}
qreal Atlas::zoomFit(const QSize &size, const QRectF &br)
{
_zoom = 0;
for (int z = 0; z < _zooms.count(); z++) {
for (int i = _zooms.at(z).first; i <= _zooms.at(z).second; i++) {
if (!_bounds.at(i).first.contains(_maps.at(i)->ll2pp(br.center())))
continue;
QRect sbr = QRectF(_maps.at(i)->ll2xy(br.topLeft()),
_maps.at(i)->ll2xy(br.bottomRight())).toRect().normalized();
if (sbr.size().width() > size.width()
|| sbr.size().height() > size.height())
return _zoom;
_zoom = z;
break;
}
}
return _zoom;
}
qreal Atlas::zoomIn()
{
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
return _zoom;
}
qreal Atlas::zoomOut()
{
_zoom = qMax(_zoom - 1, 0);
return _zoom;
}
QPointF Atlas::ll2xy(const Coordinates &c) const
{
int idx = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).first.contains(_maps.at(i)->ll2pp(c))) {
idx = i;
break;
}
}
QPointF p = _maps.at(idx)->ll2xy(c);
return p + _bounds.at(idx).second.topLeft();
}
Coordinates Atlas::xy2ll(const QPointF &p) const
{
int idx = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).second.contains(_maps.at(i)->xy2pp(p))) {
idx = i;
break;
}
}
QPointF p2 = p - _bounds.at(idx).second.topLeft();
return _maps.at(idx)->xy2ll(p2);
}
void Atlas::draw(QPainter *painter, const QRectF &rect)
{
// All in one map
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
QRectF ir = rect.intersected(_bounds.at(i).second);
if (ir == rect) {
draw(painter, rect, i);
return;
}
}
// Multiple maps
painter->fillRect(rect, Qt::white);
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
QRectF ir = rect.intersected(_bounds.at(i).second);
if (!ir.isNull())
draw(painter, ir, i);
}
}
void Atlas::draw(QPainter *painter, const QRectF &rect, int mapIndex)
{
OfflineMap *map = _maps.at(mapIndex);
const QPointF offset = _bounds.at(mapIndex).second.topLeft();
QRectF pr = QRectF(rect.topLeft() - offset, rect.size());
map->load();
painter->translate(offset);
map->draw(painter, pr);
painter->translate(-offset);
}
void Atlas::unload()
{
for (int i = 0; i < _maps.count(); i++)
_maps.at(i)->unload();
}

53
src/atlas.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef ATLAS_H
#define ATLAS_H
#include <QFileInfoList>
#include "map.h"
#include "offlinemap.h"
class Atlas : public Map
{
Q_OBJECT
public:
Atlas(const QString &fileName, QObject *parent = 0);
~Atlas();
const QString &name() const {return _name;}
QRectF bounds() const;
qreal resolution(const QPointF &p) const;
qreal zoom() const;
qreal zoomFit(const QSize &size, const QRectF &br);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const;
Coordinates xy2ll(const QPointF &p) const;
void draw(QPainter *painter, const QRectF &rect);
void unload();
bool isValid() const {return _valid;}
const QString &errorString() const {return _errorString;}
private:
void draw(QPainter *painter, const QRectF &rect, int mapIndex);
bool isAtlas(Tar &tar, const QString &path);
void computeZooms();
void computeBounds();
QString _name;
bool _valid;
QString _errorString;
QList<OfflineMap*> _maps;
QVector<QPair<int, int> > _zooms;
QVector<QPair<QRectF, QRectF> > _bounds;
int _zoom;
};
#endif // ATLAS_H

View File

@ -1,9 +1,11 @@
#include <cmath>
#include <QPainter>
#include "config.h"
#include "misc.h"
#include "axisitem.h"
#define AXIS_WIDTH 1
#define TICK 6
#define PADDING 6
#define XTICKS 15
@ -15,38 +17,6 @@ struct Label {
double d;
};
static double niceNum(double x, int round)
{
int expv;
double f;
double nf;
expv = floor(log10(x));
f = x / pow(10.0, expv);
if (round) {
if (f < 1.5)
nf = 1.0;
else if (f < 3.0)
nf = 2.0;
else if (f < 7.0)
nf = 5.0;
else
nf = 10.0;
} else {
if (f <= 1.0)
nf = 1.;
else if (f <= 2.0)
nf = 2.0;
else if (f <= 5.0)
nf = 5.0;
else
nf = 10.0;
}
return nf * pow(10.0, expv);
}
static struct Label label(double min, double max, int ticks)
{
double range;
@ -65,27 +35,34 @@ AxisItem::AxisItem(Type type, QGraphicsItem *parent) : QGraphicsItem(parent)
{
_type = type;
_size = 0;
#ifndef Q_OS_MAC
setCacheMode(QGraphicsItem::DeviceCoordinateCache);
#endif // Q_OS_MAC
}
void AxisItem::setRange(const QPointF &range)
void AxisItem::setRange(const RangeF &range)
{
prepareGeometryChange();
_range = range;
updateBoundingRect();
prepareGeometryChange();
update();
}
void AxisItem::setSize(qreal size)
{
prepareGeometryChange();
_size = size;
updateBoundingRect();
prepareGeometryChange();
update();
}
void AxisItem::setLabel(const QString& label)
{
prepareGeometryChange();
_label = label;
updateBoundingRect();
prepareGeometryChange();
update();
}
void AxisItem::updateBoundingRect()
@ -98,7 +75,7 @@ void AxisItem::updateBoundingRect()
struct Label l;
l = label(_range.x(), _range.y(), (_type == X) ? XTICKS : YTICKS);
l = label(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS);
es = fm.tightBoundingRect(QString::number(l.max));
ss = fm.tightBoundingRect(QString::number(l.min));
ls = fm.tightBoundingRect(_label);
@ -106,11 +83,21 @@ void AxisItem::updateBoundingRect()
if (_type == X) {
_boundingRect = QRectF(-ss.width()/2, -TICK/2,
_size + es.width()/2 + ss.width()/2,
ls.height() + es.height() - fm.descent() + TICK + 2*PADDING);
ls.height() + es.height() - fm.descent() + TICK + 2*PADDING + 1);
} else {
_boundingRect = QRectF(-(ls.height() + es.width() + 2*PADDING
- fm.descent() + TICK/2), -(_size + es.height()/2
+ fm.descent()), ls.height() -fm.descent() + es.width() + 2*PADDING
int mtw = 0;
QRect ts;
qreal val;
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) {
val = l.min + i * l.d;
QString str = QString::number(val);
ts = fm.tightBoundingRect(str);
mtw = qMax(ts.width(), mtw);
}
_boundingRect = QRectF(-(ls.height() + mtw + 2*PADDING + TICK/2),
-(_size + es.height()/2 + fm.descent()), ls.height() + mtw + 2*PADDING
+ TICK, _size + es.height()/2 + fm.descent() + ss.height()/2);
}
}
@ -123,28 +110,32 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QFont font;
font.setPixelSize(FONT_SIZE);
font.setFamily(FONT_FAMILY);
painter->setFont(font);
QFontMetrics fm(font);
QRect ts, ls;
struct Label l;
qreal range = _range.y() - _range.x();
qreal range = _range.size();
qreal val;
QPen pen = QPen(Qt::black, AXIS_WIDTH);
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setFont(font);
painter->setPen(pen);
ls = fm.tightBoundingRect(_label);
if (_type == X) {
painter->drawLine(0, 0, _size, 0);
l = label(_range.x(), _range.y(), XTICKS);
l = label(_range.min(), _range.max(), XTICKS);
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) {
val = l.min + i * l.d;
QString str = QString::number(val);
painter->drawLine((_size/range) * (val - _range.x()), TICK/2,
(_size/range) * (val - _range.x()), -TICK/2);
painter->drawLine((_size/range) * (val - _range.min()), TICK/2,
(_size/range) * (val - _range.min()), -TICK/2);
ts = fm.tightBoundingRect(str);
painter->drawText(((_size/range) * (val - _range.x()))
painter->drawText(((_size/range) * (val - _range.min()))
- (ts.width()/2), ts.height() + TICK/2 + PADDING, str);
}
@ -153,21 +144,23 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
} else {
painter->drawLine(0, 0, 0, -_size);
l = label(_range.x(), _range.y(), YTICKS);
l = label(_range.min(), _range.max(), YTICKS);
int mtw = 0;
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) {
val = l.min + i * l.d;
QString str = QString::number(val);
painter->drawLine(TICK/2, -((_size/range) * (val - _range.x())),
-TICK/2, -((_size/range) * (val - _range.x())));
painter->drawLine(TICK/2, -((_size/range) * (val - _range.min())),
-TICK/2, -((_size/range) * (val - _range.min())));
ts = fm.tightBoundingRect(str);
mtw = qMax(ts.width(), mtw);
painter->drawText(-(ts.width() + PADDING + TICK/2), -((_size/range)
* (val - _range.x())) + (ts.height()/2), str);
* (val - _range.min())) + (ts.height()/2), str);
}
painter->rotate(-90);
painter->drawText(_size/2 - ls.width()/2, -(ts.width()
+ 2*PADDING + TICK/2), _label);
painter->drawText(_size/2 - ls.width()/2, -(mtw + 2*PADDING + TICK/2),
_label);
painter->rotate(90);
}
@ -177,7 +170,7 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
*/
}
QSizeF AxisItem::margin()
QSizeF AxisItem::margin() const
{
QFont font;
font.setPixelSize(FONT_SIZE);
@ -186,7 +179,7 @@ QSizeF AxisItem::margin()
struct Label l;
l = label(_range.x(), _range.y(), (_type == X) ? XTICKS : YTICKS);
l = label(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS);
es = fm.tightBoundingRect(QString::number(l.max));
ss = fm.tightBoundingRect(QString::number(l.min));
ls = fm.tightBoundingRect(_label);
@ -195,7 +188,31 @@ QSizeF AxisItem::margin()
return QSizeF(es.width()/2,
ls.height() + es.height() - fm.descent() + TICK/2 + 2*PADDING);
} else {
return QSizeF(ls.height() -fm.descent() + es.width() + 2*PADDING
int mtw = 0;
QRect ts;
qreal val;
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) {
val = l.min + i * l.d;
QString str = QString::number(val);
ts = fm.tightBoundingRect(str);
mtw = qMax(ts.width(), mtw);
}
return QSizeF(ls.height() -fm.descent() + mtw + 2*PADDING
+ TICK/2, es.height()/2 + fm.descent());
}
}
QList<qreal> AxisItem::ticks() const
{
struct Label l;
QList<qreal> list;
l = label(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS);
for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++)
list.append(((_size/_range.size()) * ((l.min + i * l.d)
- _range.min())));
return list;
}

View File

@ -2,6 +2,7 @@
#define AXISITEM_H
#include <QGraphicsItem>
#include "range.h"
class AxisItem : public QGraphicsItem
{
@ -14,17 +15,18 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
void setRange(const QPointF &range);
void setRange(const RangeF &range);
void setSize(qreal size);
void setLabel(const QString& label);
QSizeF margin();
QSizeF margin() const;
QList<qreal> ticks() const;
private:
void updateBoundingRect();
Type _type;
QPointF _range;
RangeF _range;
qreal _size;
QString _label;
QRectF _boundingRect;

84
src/cadencegraph.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "data.h"
#include "cadencegraph.h"
CadenceGraph::CadenceGraph(QWidget *parent) : GraphTab(parent)
{
_units = Metric;
_showTracks = true;
GraphView::setYUnits(tr("1/min"));
setYLabel(tr("Cadence"));
setSliderPrecision(1);
}
void CadenceGraph::setInfo()
{
if (_showTracks) {
GraphView::addInfo(tr("Average"), QString::number(avg() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), QString::number(max() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
} else
clearInfo();
}
void CadenceGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
for (int i = 0; i < data.tracks().count(); i++) {
const Graph &graph = data.tracks().at(i)->cadence();
qreal sum = 0, w = 0;
if (graph.size() < 2) {
skipColor();
continue;
}
for (int j = 1; j < graph.size(); j++) {
qreal ds = graph.at(j).s() - graph.at(j-1).s();
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(data.tracks().at(i)->distance(), sum/w));
GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < data.routes().count(); i++)
skipColor();
setInfo();
redraw();
}
qreal CadenceGraph::avg() const
{
qreal sum = 0, w = 0;
QList<QPointF>::const_iterator it;
for (it = _avg.begin(); it != _avg.end(); it++) {
sum += it->y() * it->x();
w += it->x();
}
return (sum / w);
}
void CadenceGraph::clear()
{
_avg.clear();
GraphView::clear();
}
void CadenceGraph::showTracks(bool show)
{
_showTracks = show;
showGraph(show);
setInfo();
redraw();
}

31
src/cadencegraph.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef CADENCEGRAPH_H
#define CADENCEGRAPH_H
#include "graphtab.h"
class CadenceGraph : public GraphTab
{
Q_OBJECT
public:
CadenceGraph(QWidget *parent = 0);
QString label() const {return tr("Cadence");}
void loadData(const Data &data, const QList<PathItem *> &paths);
void clear();
void setUnits(enum Units) {}
void showTracks(bool show);
void showRoutes(bool show) {Q_UNUSED(show);}
private:
qreal avg() const;
qreal max() const {return bounds().bottom();}
void setInfo();
QList<QPointF> _avg;
enum Units _units;
bool _showTracks;
};
#endif // CADENCEGRAPH_H

68
src/colorbox.cpp Normal file
View File

@ -0,0 +1,68 @@
#include <QStylePainter>
#include <QStyleOptionComboBox>
#include <QMouseEvent>
#include <QColorDialog>
#include <QComboBox>
#include "colorbox.h"
ColorBox::ColorBox(QWidget *parent) : QWidget(parent)
{
_color = Qt::red;
setSizePolicy(QSizePolicy::QSizePolicy::Minimum, QSizePolicy::Fixed);
}
QSize ColorBox::sizeHint() const
{
static QSize size;
if (size.isValid())
return size;
QComboBox cb;
size = cb.sizeHint();
return size;
}
void ColorBox::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QStylePainter painter(this);
QStyleOptionComboBox option;
option.initFrom(this);
#if defined(Q_OS_MAC) || defined(Q_OS_WIN32)
painter.setBrush(_color);
painter.drawPrimitive(QStyle::PE_Frame, option);
#else // Q_OS_MAC || Q_OS_WIN32
// Fallback for some broken QT4 styles that do not draw the background
painter.setBrush(_color);
painter.setPen(Qt::NoPen);
painter.drawRect(event->rect().adjusted(2, 2, -2, -2));
// If works (QT5 and most QT4 styles) overpaints the previous rectangle
option.palette.setBrush(QPalette::Base, _color);
painter.drawPrimitive(QStyle::PE_PanelLineEdit, option);
painter.drawPrimitive(QStyle::PE_FrameLineEdit, option);
#endif // Q_OS_MAC || Q_OS_WIN32
}
void ColorBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() != Qt::LeftButton)
return;
QColor color = QColorDialog::getColor(_color, this, QString(),
QColorDialog::ShowAlphaChannel);
if (color.isValid()) {
_color = color;
update();
emit colorChanged(_color);
}
}
void ColorBox::setColor(const QColor &color)
{
_color = color;
update();
}

29
src/colorbox.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef COLORBOX_H
#define COLORBOX_H
#include <QWidget>
class ColorBox : public QWidget
{
Q_OBJECT
public:
ColorBox(QWidget *parent = 0);
const QColor &color() const {return _color;}
void setColor(const QColor &color);
QSize sizeHint() const;
signals:
void colorChanged(const QColor &color);
protected:
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *event);
private:
QColor _color;
};
#endif // COLORBOX_H

View File

@ -1,62 +0,0 @@
#include "colorshop.h"
#define HUE_INIT 0.1f
#define HUE_INCREMENT 0.62f
#define SATURATION 0.99f
#define VALUE 0.99f
static unsigned hsv2rgb(float h, float s, float v)
{
unsigned hi;
float r = 0, g = 0, b = 0, p, q, t, f;
hi = (unsigned)(h * 6.0f);
f = h * 6.0f - hi;
p = v * (1.0f - s);
q = v * (1.0f - f * s);
t = v * (1.0f - (1.0f - f) * s);
switch (hi) {
case 0:
r = v; g = t; b = p;
break;
case 1:
r = q; g = v; b = p;
break;
case 2:
r = p; g = v; b = t;
break;
case 3:
r = p; g = q; b = v;
break;
case 4:
r = t; g = p; b = v;
break;
case 5:
r = v; g = p; b = q;
break;
}
return ((unsigned)(r * 256) << 16)
+ ((unsigned)(g * 256) << 8)
+ (unsigned)(b * 256);
}
ColorShop::ColorShop()
{
_hueState = HUE_INIT;
}
QColor ColorShop::color()
{
_hueState += HUE_INCREMENT;
_hueState -= (int) _hueState;
return QColor(hsv2rgb(_hueState, SATURATION, VALUE));
}
void ColorShop::reset()
{
_hueState = HUE_INIT;
}

View File

@ -1,17 +0,0 @@
#ifndef COLORSHOP_H
#define COLORSHOP_H
#include <QColor>
class ColorShop
{
public:
ColorShop();
QColor color();
void reset();
private:
float _hueState;
};
#endif // COLORSHOP_H

View File

@ -1,20 +1,46 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <QtGlobal>
#include <QDir>
#include <QApplication>
#include <QString>
#define APP_NAME "GPXSee"
#define APP_HOMEPAGE "http://tumic.wz.cz/gpxsee"
#define APP_VERSION "2.5"
#define FONT_FAMILY "Arial"
#define FONT_SIZE 12
#define SCREEN_DPI 96.0
#define ELLIPSOID_FILE QString("ellipsoids.csv")
#define DATUM_FILE QString("datums.csv")
#define MAP_FILE QString("maps.txt")
#define MAP_DIR QString("maps")
#define POI_DIR QString("POI")
#if defined(Q_OS_WIN32)
#define APP_DIR "GPXSee"
#define USER_DIR QDir::homePath() + QString("/GPXSee")
#define GLOBAL_DIR QApplication::applicationDirPath()
#elif defined(Q_OS_MAC)
#define USER_DIR QDir::homePath() + QString("/.gpxsee")
#define GLOBAL_DIR QApplication::applicationDirPath() \
+ QString("/../Resources")
#else
#define APP_DIR ".gpxsee"
#define USER_DIR QDir::homePath() + QString("/.gpxsee")
#define GLOBAL_DIR QString("/usr/share/gpxsee")
#endif
#define POI_DIR APP_DIR"/POI"
#define TILES_DIR APP_DIR"/tiles"
#define MAP_LIST_FILE APP_DIR"/maps.txt"
#define USER_ELLIPSOID_FILE USER_DIR + QString("/") + ELLIPSOID_FILE
#define USER_DATUM_FILE USER_DIR + QString("/") + DATUM_FILE
#define USER_MAP_DIR USER_DIR + QString("/") + MAP_DIR
#define USER_MAP_FILE USER_DIR + QString("/") + MAP_FILE
#define USER_POI_DIR USER_DIR + QString("/") + POI_DIR
#define GLOBAL_ELLIPSOID_FILE GLOBAL_DIR + QString("/") + ELLIPSOID_FILE
#define GLOBAL_DATUM_FILE GLOBAL_DIR + QString("/") + DATUM_FILE
#define GLOBAL_MAP_DIR GLOBAL_DIR + QString("/") + MAP_DIR
#define GLOBAL_MAP_FILE GLOBAL_DIR + QString("/") + MAP_FILE
#define GLOBAL_POI_DIR GLOBAL_DIR + QString("/") + POI_DIR
#define TILES_DIR USER_DIR + QString("/tiles")
#endif /* CONFIG_H */

55
src/coordinates.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "rd.h"
#include "wgs84.h"
#include "coordinates.h"
#define MIN_LAT deg2rad(-90.0)
#define MAX_LAT deg2rad(90.0)
#define MIN_LON deg2rad(-180.0)
#define MAX_LON deg2rad(180.0)
qreal Coordinates::distanceTo(const Coordinates &c) const
{
qreal dLat = deg2rad(c.lat() - _lat);
qreal dLon = deg2rad(c.lon() - _lon);
qreal a = pow(sin(dLat / 2.0), 2.0)
+ cos(deg2rad(_lat)) * cos(deg2rad(c.lat())) * pow(sin(dLon / 2.0), 2.0);
return (WGS84_RADIUS * (2.0 * atan2(sqrt(a), sqrt(1.0 - a))));
}
QDebug operator<<(QDebug dbg, const Coordinates &coordinates)
{
dbg.nospace() << "Coordinates(" << coordinates.lon() << ", "
<< coordinates.lat() << ")";
return dbg.space();
}
QPair<Coordinates, Coordinates> Coordinates::boundingRect(qreal distance) const
{
qreal radDist = distance / WGS84_RADIUS;
qreal minLat = deg2rad(_lat) - radDist;
qreal maxLat = deg2rad(_lat) + radDist;
qreal minLon, maxLon;
if (minLat > MIN_LAT && maxLat < MAX_LAT) {
qreal deltaLon = asin(sin(radDist) / cos(_lat));
minLon = deg2rad(_lon) - deltaLon;
if (minLon < MIN_LON)
minLon += 2.0 * M_PI;
maxLon = deg2rad(_lon) + deltaLon;
if (maxLon > MAX_LON)
maxLon -= 2.0 * M_PI;
} else {
// a pole is within the distance
minLat = qMax(minLat, MIN_LAT);
maxLat = qMin(maxLat, MAX_LAT);
minLon = MIN_LON;
maxLon = MAX_LON;
}
return QPair<Coordinates, Coordinates>(Coordinates(rad2deg(qMin(minLon,
maxLon)), rad2deg(qMin(minLat, maxLat))), Coordinates(rad2deg(qMax(minLon,
maxLon)), rad2deg(qMax(minLat, maxLat))));
}

44
src/coordinates.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef COORDINATES_H
#define COORDINATES_H
#include <cmath>
#include <QPointF>
#include <QDebug>
class Coordinates
{
public:
Coordinates() {_lon = NAN; _lat = NAN;}
Coordinates(const Coordinates &c) {_lon = c._lon; _lat = c._lat;}
Coordinates(qreal lon, qreal lat) {_lon = lon; _lat = lat;}
Coordinates(const QPointF &p) {_lon = p.x(), _lat = p.y();}
QPointF toPointF() const {return QPointF(_lon, _lat);}
qreal &rlon() {return _lon;}
qreal &rlat() {return _lat;}
void setLon(qreal lon) {_lon = lon;}
void setLat(qreal lat) {_lat = lat;}
qreal lon() const {return _lon;}
qreal lat() const {return _lat;}
bool isNull() const
{return (std::isnan(_lon) || std::isnan(_lat)) ? true : false;}
bool isValid() const
{return (_lon >= -180.0 && _lon <= 180.0 && _lat >= -90.0
&& _lat <= 90.0) ? true : false;}
qreal distanceTo(const Coordinates &c) const;
QPair<Coordinates, Coordinates> boundingRect(qreal distance) const;
private:
qreal _lat, _lon;
};
inline bool operator==(const Coordinates &c1, const Coordinates &c2)
{return (c1.lat() == c2.lat() && c1.lon() == c2.lon());}
inline bool operator!=(const Coordinates &c1, const Coordinates &c2)
{return !(c1 == c2);}
QDebug operator<<(QDebug dbg, const Coordinates &trackpoint);
#endif // COORDINATES_H

42
src/cpuarch.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef CPUARCH_H
#define CPUARCH_H
#include <QtGlobal>
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 0)
#if defined(__arm64__)
#define CPU_ARCH_STR "arm64"
#elif defined(__arm__) || defined(__TARGET_ARCH_ARM) || defined(_M_ARM)
#define CPU_ARCH_STR "arm"
#elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) \
|| defined(_M_X64)
#define CPU_ARCH_STR "x86_64"
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
#define CPU_ARCH_STR "i386"
#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
#define CPU_ARCH_STR "ia64"
#elif defined(_MIPS_ARCH_MIPS64) || defined(__mips64)
#define CPU_ARCH_STR "mips64"
#elif defined(__mips) || defined(__mips__) || defined(_M_MRX000)
#define CPU_ARCH_STR "mips"
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__)
#define CPU_ARCH_STR "power64"
#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \
|| defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \
|| defined(_M_MPPC) || defined(_M_PPC)
#define CPU_ARCH_STR "power"
#else
#define CPU_ARCH_STR "unknown"
#endif
#define CPU_ARCH QString(CPU_ARCH_STR)
#else // QT_VERSION < 5.4
#include <QSysInfo>
#define CPU_ARCH QSysInfo::buildCpuArchitecture()
#endif // QT_VERSION < 5.4
#endif // CPUARCH_H

44
src/csvparser.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "csvparser.h"
bool CSVParser::loadFile(QFile *file)
{
bool res;
_errorLine = 1;
_errorString.clear();
while (!file->atEnd()) {
QByteArray line = file->readLine();
QList<QByteArray> list = line.split(',');
if (list.size() < 3) {
_errorString = "Parse error";
return false;
}
qreal lat = list[0].trimmed().toDouble(&res);
if (!res || (lat < -90.0 || lat > 90.0)) {
_errorString = "Invalid latitude";
return false;
}
qreal lon = list[1].trimmed().toDouble(&res);
if (!res || (lon < -180.0 || lon > 180.0)) {
_errorString = "Invalid longitude";
return false;
}
Waypoint wp(Coordinates(lon, lat));
QByteArray ba = list[2].trimmed();
QString name = QString::fromUtf8(ba.data(), ba.size());
wp.setName(name);
if (list.size() > 3) {
ba = list[3].trimmed();
wp.setDescription(QString::fromUtf8(ba.data(), ba.size()));
}
_waypoints.append(wp);
_errorLine++;
}
return true;
}

23
src/csvparser.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef CSVPARSER_H
#define CSVPARSER_H
#include "parser.h"
class CSVParser : public Parser
{
public:
CSVParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
{_errorLine = 0;}
~CSVParser() {}
bool loadFile(QFile *file);
QString errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
private:
QString _errorString;
int _errorLine;
};
#endif // CSVPARSER_H

95
src/data.cpp Normal file
View File

@ -0,0 +1,95 @@
#include <QFile>
#include <QFileInfo>
#include <QLineF>
#include "gpxparser.h"
#include "tcxparser.h"
#include "csvparser.h"
#include "kmlparser.h"
#include "fitparser.h"
#include "igcparser.h"
#include "nmeaparser.h"
#include "data.h"
Data::Data() : _errorLine(0)
{
_parsers.insert("gpx", new GPXParser(_trackData, _routeData,
_waypointData));
_parsers.insert("tcx", new TCXParser(_trackData, _routeData,
_waypointData));
_parsers.insert("kml", new KMLParser(_trackData, _routeData,
_waypointData));
_parsers.insert("fit", new FITParser(_trackData, _routeData,
_waypointData));
_parsers.insert("csv", new CSVParser(_trackData, _routeData,
_waypointData));
_parsers.insert("igc", new IGCParser(_trackData, _routeData,
_waypointData));
_parsers.insert("nmea", new NMEAParser(_trackData, _routeData,
_waypointData));
}
Data::~Data()
{
QHash<QString, Parser*>::iterator it;
for (it = _parsers.begin(); it != _parsers.end(); it++)
delete it.value();
for (int i = 0; i < _tracks.count(); i++)
delete _tracks.at(i);
for (int i = 0; i < _routes.count(); i++)
delete _routes.at(i);
}
void Data::createData()
{
for (int i = 0; i < _trackData.count(); i++)
_tracks.append(new Track(_trackData.at(i)));
for (int i = 0; i < _routeData.count(); i++)
_routes.append(new Route(_routeData.at(i)));
}
bool Data::loadFile(const QString &fileName)
{
QFile file(fileName);
QFileInfo fi(fileName);
_errorString.clear();
_errorLine = 0;
if (!file.open(QFile::ReadOnly)) {
_errorString = qPrintable(file.errorString());
return false;
}
QHash<QString, Parser*>::iterator it;
if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) {
if (it.value()->loadFile(&file)) {
createData();
return true;
}
_errorLine = it.value()->errorLine();
_errorString = it.value()->errorString();
} else {
for (it = _parsers.begin(); it != _parsers.end(); it++) {
if (it.value()->loadFile(&file)) {
createData();
return true;
}
file.reset();
}
qWarning("Error loading data file: %s:\n", qPrintable(fileName));
for (it = _parsers.begin(); it != _parsers.end(); it++)
qWarning("%s: line %d: %s\n", qPrintable(it.key()),
it.value()->errorLine(), qPrintable(it.value()->errorString()));
_errorLine = 0;
_errorString = "Unknown format";
}
return false;
}

44
src/data.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef DATA_H
#define DATA_H
#include <QVector>
#include <QList>
#include <QHash>
#include <QPointF>
#include <QString>
#include "waypoint.h"
#include "track.h"
#include "route.h"
#include "parser.h"
class Data
{
public:
Data();
~Data();
bool loadFile(const QString &fileName);
const QString &errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
const QList<Track*> &tracks() const {return _tracks;}
const QList<Route*> &routes() const {return _routes;}
const QList<Waypoint> &waypoints() const {return _waypointData;}
private:
void createData();
QString _errorString;
int _errorLine;
QHash<QString, Parser*> _parsers;
QList<Track*> _tracks;
QList<Route*> _routes;
QList<TrackData> _trackData;
QList<RouteData> _routeData;
QList<Waypoint> _waypointData;
};
#endif // DATA_H

83
src/datum.cpp Normal file
View File

@ -0,0 +1,83 @@
#include <QFile>
#include "wgs84.h"
#include "datum.h"
static QMap<QString, Datum> WGS84()
{
QMap<QString, Datum> map;
map.insert("WGS 84", Datum(Ellipsoid(WGS84_RADIUS, WGS84_FLATTENING),
0, 0, 0));
return map;
}
QMap<QString, Datum> Datum::_datums = WGS84();
QString Datum::_errorString;
int Datum::_errorLine = 0;
Datum Datum::datum(const QString &name)
{
QMap<QString, Datum>::const_iterator it = _datums.find(name);
if (it == _datums.end())
return Datum();
return it.value();
}
bool Datum::loadList(const QString &path)
{
QFile file(path);
bool res;
if (!file.open(QFile::ReadOnly)) {
_errorString = qPrintable(file.errorString());
return false;
}
_errorLine = 1;
_errorString.clear();
while (!file.atEnd()) {
QByteArray line = file.readLine();
QList<QByteArray> list = line.split(',');
if (list.size() != 6) {
_errorString = "Format error";
return false;
}
int eid = list[2].trimmed().toInt(&res);
if (!res) {
_errorString = "Invalid ellipsoid id";
return false;
}
double dx = list[3].trimmed().toDouble(&res);
if (!res) {
_errorString = "Invalid dx";
return false;
}
double dy = list[4].trimmed().toDouble(&res);
if (!res) {
_errorString = "Invalid dy";
return false;
}
double dz = list[5].trimmed().toDouble(&res);
if (!res) {
_errorString = "Invalid dz";
return false;
}
Ellipsoid e = Ellipsoid::ellipsoid(eid);
if (e.isNull()) {
_errorString = "Unknown ellipsoid ID";
return false;
}
Datum d(e, dx, dy, dz);
_datums.insert(list[0].trimmed(), d);
_errorLine++;
}
return true;
}

38
src/datum.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef DATUM_H
#define DATUM_H
#include <QMap>
#include "ellipsoid.h"
class Datum
{
public:
Datum() : _ellipsoid(Ellipsoid()), _dx(0), _dy(0), _dz(0) {}
Datum(const Ellipsoid &ellipsoid, double dx, double dy, double dz)
: _ellipsoid(ellipsoid), _dx(dx), _dy(dy), _dz(dz) {}
const Ellipsoid &ellipsoid() const {return _ellipsoid;}
double dx() const {return _dx;}
double dy() const {return _dy;}
double dz() const {return _dz;}
bool isNull() const {return _ellipsoid.isNull();}
bool isWGS84() const
{return _ellipsoid.isWGS84() && _dx == 0 && _dy == 0 && _dz == 0;}
static bool loadList(const QString &path);
static const QString &errorString() {return _errorString;}
static int errorLine() {return _errorLine;}
static Datum datum(const QString &name);
private:
Ellipsoid _ellipsoid;
double _dx, _dy, _dz;
static QMap<QString, Datum> _datums;
static QString _errorString;
static int _errorLine;
};
#endif // DATUM_H

View File

@ -1,5 +1,7 @@
#include <QFile>
#include <QFileInfo>
#include <QNetworkRequest>
#include <QNetworkReply>
#include "config.h"
#include "downloader.h"
@ -14,27 +16,44 @@
#define PLATFORM_STR "Unknown"
#endif
#define USER_AGENT APP_NAME"/"APP_VERSION" ("PLATFORM_STR"; Qt "QT_VERSION_STR")"
#define USER_AGENT \
APP_NAME "/" APP_VERSION " (" PLATFORM_STR "; Qt " QT_VERSION_STR ")"
Downloader::Downloader()
#define ATTR_REDIRECT QNetworkRequest::RedirectionTargetAttribute
#define ATTR_FILE QNetworkRequest::User
#define ATTR_ORIGIN (QNetworkRequest::Attribute)(QNetworkRequest::User + 1)
#define ATTR_LEVEL (QNetworkRequest::Attribute)(QNetworkRequest::User + 2)
#define MAX_REDIRECT_LEVEL 5
Downloader::Downloader(QObject *parent) : QObject(parent)
{
connect(&_manager, SIGNAL(finished(QNetworkReply*)),
SLOT(downloadFinished(QNetworkReply*)));
}
void Downloader::doDownload(const Download &dl)
bool Downloader::doDownload(const Download &dl, const Redirect &redirect)
{
QUrl url(dl.url());
if (_errorDownloads.contains(url))
return;
return false;
if (_currentDownloads.contains(url))
return false;
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::User, QVariant(dl.file()));
request.setAttribute(ATTR_FILE, QVariant(dl.file()));
if (!redirect.isNull()) {
request.setAttribute(ATTR_ORIGIN, QVariant(redirect.origin()));
request.setAttribute(ATTR_LEVEL, QVariant(redirect.level()));
}
request.setRawHeader("User-Agent", USER_AGENT);
QNetworkReply *reply = _manager.get(request);
_currentDownloads.append(reply);
_currentDownloads.insert(url, reply);
return true;
}
bool Downloader::saveToDisk(const QString &filename, QIODevice *data)
@ -42,7 +61,7 @@ bool Downloader::saveToDisk(const QString &filename, QIODevice *data)
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
fprintf(stderr, "Error writing map tile: %s: %s\n",
qWarning("Error writing map tile: %s: %s\n",
qPrintable(filename), qPrintable(file.errorString()));
return false;
}
@ -55,26 +74,61 @@ bool Downloader::saveToDisk(const QString &filename, QIODevice *data)
void Downloader::downloadFinished(QNetworkReply *reply)
{
QUrl url = reply->url();
QUrl url = reply->request().url();
if (reply->error()) {
QUrl origin = reply->request().attribute(ATTR_ORIGIN).toUrl();
if (origin.isEmpty()) {
_errorDownloads.insert(url);
fprintf(stderr, "Error downloading map tile: %s: %s\n",
qWarning("Error downloading map tile: %s: %s\n",
url.toEncoded().constData(), qPrintable(reply->errorString()));
} else {
QString filename = reply->request().attribute(QNetworkRequest::User)
_errorDownloads.insert(origin);
qWarning("Error downloading map tile: %s -> %s: %s\n",
origin.toEncoded().constData(), url.toEncoded().constData(),
qPrintable(reply->errorString()));
}
} else {
QUrl location = reply->attribute(ATTR_REDIRECT).toUrl();
QString filename = reply->request().attribute(ATTR_FILE)
.toString();
saveToDisk(filename, reply);
if (!location.isEmpty()) {
QUrl origin = reply->request().attribute(ATTR_ORIGIN).toUrl();
int level = reply->request().attribute(ATTR_LEVEL).toInt();
if (location == url) {
_errorDownloads.insert(url);
qWarning("Error downloading map tile: %s: "
"redirect loop\n", url.toEncoded().constData());
} else if (level >= MAX_REDIRECT_LEVEL) {
_errorDownloads.insert(origin);
qWarning("Error downloading map tile: %s: "
"redirect level limit reached\n",
origin.toEncoded().constData());
} else {
Redirect redirect(origin.isEmpty() ? url : origin, level + 1);
Download dl(location, filename);
doDownload(dl, redirect);
}
} else
if (!saveToDisk(filename, reply))
_errorDownloads.insert(url);
}
_currentDownloads.removeAll(reply);
_currentDownloads.remove(url);
reply->deleteLater();
if (_currentDownloads.isEmpty())
emit finished();
}
void Downloader::get(const QList<Download> &list)
bool Downloader::get(const QList<Download> &list)
{
bool finishEmitted = false;
for (int i = 0; i < list.count(); i++)
doDownload(list.at(i));
finishEmitted |= doDownload(list.at(i));
return finishEmitted;
}

View File

@ -2,23 +2,24 @@
#define DOWNLOADER_H
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QList>
#include <QMap>
#include <QSet>
class QNetworkReply;
class Download
{
public:
Download(const QString &url, const QString &file)
Download(const QUrl &url, const QString &file)
{_url = url; _file = file;}
const QString& url() const {return _url;}
const QUrl& url() const {return _url;}
const QString& file() const {return _file;}
private:
QString _url;
QUrl _url;
QString _file;
};
@ -28,9 +29,9 @@ class Downloader : public QObject
Q_OBJECT
public:
static Downloader& instance()
{static Downloader i; return i;}
void get(const QList<Download> &list);
Downloader(QObject *parent = 0);
bool get(const QList<Download> &list);
signals:
void finished();
@ -39,15 +40,28 @@ private slots:
void downloadFinished(QNetworkReply *reply);
private:
Downloader();
Downloader(Downloader const&);
void operator=(Downloader const&);
class Redirect
{
public:
Redirect() : _level(0) {}
Redirect(const QUrl &origin, int level) :
_origin(origin), _level(level) {}
void doDownload(const Download &dl);
const QUrl &origin() const {return _origin;}
int level() const {return _level;}
bool isNull() const {return (_level == 0);}
private:
QUrl _origin;
int _level;
};
bool doDownload(const Download &dl, const Redirect &redirect = Redirect());
bool saveToDisk(const QString &filename, QIODevice *data);
QNetworkAccessManager _manager;
QList<QNetworkReply *> _currentDownloads;
QMap<QUrl, QNetworkReply *> _currentDownloads;
QSet<QUrl> _errorDownloads;
};

View File

@ -1,96 +1,234 @@
#include <float.h>
#include <cmath>
#include "config.h"
#include "data.h"
#include "elevationgraph.h"
ElevationGraph::ElevationGraph(QWidget *parent) : Graph(parent)
static qreal nMin(qreal a, qreal b)
{
_ascent = 0;
_descent = 0;
_max = -FLT_MAX;
_min = FLT_MAX;
Graph::setXLabel(tr("Distance"));
Graph::setYLabel(tr("Elevation"));
Graph::setXUnits(tr("km"));
Graph::setYUnits(tr("m"));
Graph::setXScale(M2KM);
if (!std::isnan(a) && !std::isnan(b))
return qMin(a, b);
else if (!std::isnan(a))
return a;
else if (!std::isnan(b))
return b;
else
return NAN;
}
void ElevationGraph::addInfo()
static qreal nMax(qreal a, qreal b)
{
Graph::addInfo(tr("Ascent"), QString::number(_ascent * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits);
Graph::addInfo(tr("Descent"), QString::number(_descent * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits);
Graph::addInfo(tr("Maximum"), QString::number(_max * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits);
Graph::addInfo(tr("Minimum"), QString::number(_min * _yScale, 'f', 0)
+ THIN_SPACE + _yUnits);
if (!std::isnan(a) && !std::isnan(b))
return qMax(a, b);
else if (!std::isnan(a))
return a;
else if (!std::isnan(b))
return b;
else
return NAN;
}
void ElevationGraph::loadGPX(const GPX &gpx)
ElevationGraph::ElevationGraph(QWidget *parent) : GraphTab(parent)
{
QVector<QPointF> data;
qreal min, max, ascent = 0, descent = 0;
_trackAscent = 0;
_routeAscent = 0;
_trackDescent = 0;
_routeDescent = 0;
_trackMin = NAN;
_trackMax = NAN;
_routeMin = NAN;
_routeMax = NAN;
_showRoutes = true;
_showTracks = true;
gpx.elevationGraph(data);
if (data.isEmpty())
_units = Metric;
setYUnits();
setYLabel(tr("Elevation"));
setMinYRange(50.0);
}
void ElevationGraph::setInfo()
{
if (std::isnan(max()) || std::isnan(min()))
clearInfo();
else {
GraphView::addInfo(tr("Ascent"), QString::number(ascent() * yScale(),
'f', 0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Descent"), QString::number(descent() * yScale(),
'f', 0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), QString::number(max() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Minimum"), QString::number(min() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
}
}
void ElevationGraph::loadGraph(const Graph &graph, Type type, PathItem *path)
{
qreal ascent = 0, descent = 0;
qreal min, max;
if (graph.size() < 2) {
skipColor();
return;
}
min = max = data.at(0).y();
for (int i = 1; i < data.size(); i++) {
qreal cur = data.at(i).y();
qreal prev = data.at(i-1).y();
max = min = graph.at(0).y();
for (int j = 1; j < graph.size(); j++) {
qreal cur = graph.at(j).y();
qreal prev = graph.at(j-1).y();
if (cur > prev)
ascent += cur - prev;
if (cur < prev)
descent += prev - cur;
if (cur > max)
max = cur;
if (cur < min)
min = cur;
if (cur > max)
max = cur;
}
_ascent += ascent;
_descent += descent;
_max = qMax(_max, max);
_min = qMin(_min, min);
if (type == Track) {
_trackAscent += ascent;
_trackDescent += descent;
_trackMax = nMax(_trackMax, max);
_trackMin = nMin(_trackMin, min);
} else {
_routeAscent += ascent;
_routeDescent += descent;
_routeMax = nMax(_routeMax, max);
_routeMin = nMin(_routeMin, min);
}
addInfo();
loadData(data);
GraphView::loadGraph(graph, path, type);
}
void ElevationGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
int p = 0;
for (int i = 0; i < data.tracks().count(); i++)
loadGraph(data.tracks().at(i)->elevation(), Track, paths.at(p++));
for (int i = 0; i < data.routes().count(); i++)
loadGraph(data.routes().at(i)->elevation(), Route, paths.at(p++));
setInfo();
redraw();
}
void ElevationGraph::clear()
{
_ascent = 0;
_descent = 0;
_max = -FLT_MAX;
_min = FLT_MAX;
_trackAscent = 0;
_routeAscent = 0;
_trackDescent = 0;
_routeDescent = 0;
_trackMin = NAN;
_trackMax = NAN;
_routeMin = NAN;
_routeMax = NAN;
Graph::clear();
GraphView::clear();
}
void ElevationGraph::setYUnits()
{
if (_units == Metric) {
GraphView::setYUnits(tr("m"));
setYScale(1);
} else {
GraphView::setYUnits(tr("ft"));
setYScale(M2FT);
}
}
void ElevationGraph::setUnits(enum Units units)
{
if (units == Metric) {
Graph::setXUnits(tr("km"));
Graph::setYUnits(tr("m"));
Graph::setXScale(M2KM);
Graph::setYScale(1);
} else {
Graph::setXUnits(tr("mi"));
Graph::setYUnits(tr("ft"));
Graph::setXScale(M2MI);
Graph::setYScale(M2FT);
}
_units = units;
clearInfo();
addInfo();
setYUnits();
setInfo();
GraphView::setUnits(units);
redraw();
}
void ElevationGraph::showTracks(bool show)
{
_showTracks = show;
setInfo();
showGraph(show, Track);
redraw();
}
void ElevationGraph::showRoutes(bool show)
{
_showRoutes = show;
showGraph(show, Route);
setInfo();
redraw();
}
qreal ElevationGraph::ascent() const
{
qreal val = 0;
if (_showRoutes)
val += _routeAscent;
if (_showTracks)
val += _trackAscent;
return val;
}
qreal ElevationGraph::descent() const
{
qreal val = 0;
if (_showRoutes)
val += _routeDescent;
if (_showTracks)
val += _trackDescent;
return val;
}
qreal ElevationGraph::max() const
{
qreal val;
if (_showRoutes && _showTracks)
val = nMax(_routeMax, _trackMax);
else if (_showTracks)
val = _trackMax;
else if (_showRoutes)
val = _routeMax;
else
val = NAN;
return val;
}
qreal ElevationGraph::min() const
{
qreal val;
if (_showRoutes && _showTracks)
val = nMin(_routeMin, _trackMin);
else if (_showTracks)
val = _trackMin;
else if (_showRoutes)
val = _routeMin;
else
val = NAN;
return val;
}

View File

@ -1,31 +1,42 @@
#ifndef ELEVATIONGRAPH_H
#define ELEVATIONGRAPH_H
#include "graph.h"
#include "gpx.h"
#include "units.h"
#include "graphtab.h"
class ElevationGraph : public Graph
class ElevationGraph : public GraphTab
{
Q_OBJECT
public:
ElevationGraph(QWidget *parent = 0);
void loadGPX(const GPX &gpx);
QString label() const {return tr("Elevation");}
void loadData(const Data &data, const QList<PathItem *> &paths);
void clear();
void setUnits(enum Units units);
qreal ascent() const {return _ascent;}
qreal descent() const {return _descent;}
qreal max() const {return _max;}
qreal min() const {return _min;}
void showTracks(bool show);
void showRoutes(bool show);
private:
void addInfo();
enum Type {Track, Route};
qreal _ascent, _descent;
qreal _max, _min;
qreal max() const;
qreal min() const;
qreal ascent() const;
qreal descent() const;
void setYUnits();
void setInfo();
void loadGraph(const Graph &graph, Type type, PathItem *path);
qreal _trackAscent, _trackDescent;
qreal _routeAscent, _routeDescent;
qreal _trackMax, _routeMax;
qreal _trackMin, _routeMin;
enum Units _units;
bool _showTracks, _showRoutes;
};
#endif // ELEVATIONGRAPH_H

62
src/ellipsoid.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <QFile>
#include "ellipsoid.h"
QMap<int, Ellipsoid> Ellipsoid::_ellipsoids;
QString Ellipsoid::_errorString;
int Ellipsoid::_errorLine = 0;
Ellipsoid Ellipsoid::ellipsoid(int id)
{
QMap<int, Ellipsoid>::const_iterator it = _ellipsoids.find(id);
if (it == _ellipsoids.end())
return Ellipsoid();
return it.value();
}
bool Ellipsoid::loadList(const QString &path)
{
QFile file(path);
bool res;
if (!file.open(QFile::ReadOnly)) {
_errorString = qPrintable(file.errorString());
return false;
}
_errorLine = 1;
_errorString.clear();
while (!file.atEnd()) {
QByteArray line = file.readLine();
QList<QByteArray> list = line.split(',');
if (list.size() != 4) {
_errorString = "Format error";
return false;
}
int id = list[0].trimmed().toInt(&res);
if (!res) {
_errorString = "Invalid ellipsoid id";
return false;
}
double radius = list[2].trimmed().toDouble(&res);
if (!res) {
_errorString = "Invalid ellipsoid radius";
return false;
}
double flattening = list[3].trimmed().toDouble(&res);
if (!res) {
_errorString = "Invalid ellipsoid flattening";
return false;
}
Ellipsoid e(radius, 1.0/flattening);
_ellipsoids.insert(id, e);
_errorLine++;
}
return true;
}

37
src/ellipsoid.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef ELLIPSOID_H
#define ELLIPSOID_H
#include <QString>
#include <QMap>
#include "wgs84.h"
class Ellipsoid
{
public:
Ellipsoid() : _radius(-1.0), _flattening(-1.0) {}
Ellipsoid(double radius, double flattening)
: _radius(radius), _flattening(flattening) {}
double radius() const {return _radius;}
double flattening() const {return _flattening;}
bool isNull() const {return _radius < 0 || _flattening < 0;}
bool isWGS84() const
{return _radius == WGS84_RADIUS && _flattening == WGS84_FLATTENING;}
static bool loadList(const QString &path);
static const QString &errorString() {return _errorString;}
static int errorLine() {return _errorLine;}
static Ellipsoid ellipsoid(int id);
private:
double _radius;
double _flattening;
static QMap<int, Ellipsoid> _ellipsoids;
static QString _errorString;
static int _errorLine;
};
#endif // ELLIPSOID_H

77
src/emptymap.cpp Normal file
View File

@ -0,0 +1,77 @@
#include <QtGlobal>
#include <QPainter>
#include "misc.h"
#include "rd.h"
#include "wgs84.h"
#include "coordinates.h"
#include "mercator.h"
#include "emptymap.h"
#define SCALE_MIN 0.5
#define SCALE_MAX 1.0E-6
EmptyMap::EmptyMap(QObject *parent) : Map(parent)
{
_scale = SCALE_MAX;
}
QRectF EmptyMap::bounds() const
{
return scaled(QRectF(QPointF(-180, -180), QSizeF(360, 360)), 1.0/_scale);
}
qreal EmptyMap::zoomFit(const QSize &size, const QRectF &br)
{
if (br.isNull())
_scale = SCALE_MAX;
else {
Coordinates topLeft(br.topLeft());
Coordinates bottomRight(br.bottomRight());
QRectF tbr(Mercator().ll2xy(topLeft), Mercator().ll2xy(bottomRight));
QPointF sc(tbr.width() / size.width(), tbr.height() / size.height());
_scale = qMax(sc.x(), sc.y());
}
_scale = qMax(_scale, SCALE_MAX);
_scale = qMin(_scale, SCALE_MIN);
return _scale;
}
qreal EmptyMap::resolution(const QPointF &p) const
{
return (WGS84_RADIUS * 2 * M_PI * _scale / 360.0
* cos(2.0 * atan(exp(deg2rad(-p.y() * _scale))) - M_PI/2));
}
qreal EmptyMap::zoomIn()
{
_scale = qMax(_scale / 2.0, SCALE_MAX);
return _scale;
}
qreal EmptyMap::zoomOut()
{
_scale = qMin(_scale * 2.0, SCALE_MIN);
return _scale;
}
void EmptyMap::draw(QPainter *painter, const QRectF &rect)
{
painter->fillRect(rect, Qt::white);
}
QPointF EmptyMap::ll2xy(const Coordinates &c) const
{
QPointF m = Mercator().ll2xy(c);
return QPointF(m.x() / _scale, m.y() / -_scale);
}
Coordinates EmptyMap::xy2ll(const QPointF &p) const
{
QPointF m(p.x() * _scale, -p.y() * _scale);
return Mercator().xy2ll(m);
}

33
src/emptymap.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef EMPTYMAP_H
#define EMPTYMAP_H
#include "map.h"
class EmptyMap : public Map
{
Q_OBJECT
public:
EmptyMap(QObject *parent = 0);
const QString &name() const {return _name;}
QRectF bounds() const;
qreal resolution(const QPointF &p) const;
qreal zoom() const {return _scale;}
qreal zoomFit(const QSize &size, const QRectF &br);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const;
Coordinates xy2ll(const QPointF &p) const;
void draw(QPainter *painter, const QRectF &rect);
private:
QString _name;
qreal _scale;
};
#endif // EMPTYMAP_H

177
src/exportdialog.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
#include <QGridLayout>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QComboBox>
#include <QRadioButton>
#include <QPushButton>
#include <QFileInfo>
#include <QMessageBox>
#include <QTabWidget>
#include <QDoubleSpinBox>
#include "fileselectwidget.h"
#include "units.h"
#include "exportdialog.h"
ExportDialog::ExportDialog(Export *exp, QWidget *parent)
: QDialog(parent), _export(exp)
{
int index;
_fileSelect = new FileSelectWidget();
_fileSelect->setFilter(tr("PDF files (*.pdf);;All files (*)"));
_fileSelect->setFile(_export->fileName);
_paperSize = new QComboBox();
_paperSize->addItem("A3", QPrinter::A3);
_paperSize->addItem("A4", QPrinter::A4);
_paperSize->addItem("A5", QPrinter::A5);
_paperSize->addItem("Tabloid", QPrinter::Tabloid);
_paperSize->addItem("Legal", QPrinter::Legal);
_paperSize->addItem("Letter", QPrinter::Letter);
if ((index = _paperSize->findData(_export->paperSize)) >= 0)
_paperSize->setCurrentIndex(index);
_portrait = new QRadioButton(tr("Portrait"));
_landscape = new QRadioButton(tr("Landscape"));
QHBoxLayout *orientationLayout = new QHBoxLayout();
orientationLayout->addWidget(_portrait);
orientationLayout->addWidget(_landscape);
if (_export->orientation == QPrinter::Portrait)
_portrait->setChecked(true);
else
_landscape->setChecked(true);
_topMargin = new QDoubleSpinBox();
_bottomMargin = new QDoubleSpinBox();
_leftMargin = new QDoubleSpinBox();
_rightMargin = new QDoubleSpinBox();
QString us = (_export->units == Imperial) ? tr("in") : tr("mm");
_topMargin->setSuffix(UNIT_SPACE + us);
_bottomMargin->setSuffix(UNIT_SPACE + us);
_leftMargin->setSuffix(UNIT_SPACE + us);
_rightMargin->setSuffix(UNIT_SPACE + us);
if (_export->units == Imperial) {
_topMargin->setValue(_export->margins.top() * MM2IN);
_bottomMargin->setValue(_export->margins.bottom() * MM2IN);
_leftMargin->setValue(_export->margins.left() * MM2IN);
_rightMargin->setValue(_export->margins.right() * MM2IN);
_topMargin->setSingleStep(0.1);
_bottomMargin->setSingleStep(0.1);
_leftMargin->setSingleStep(0.1);
_rightMargin->setSingleStep(0.1);
} else {
_topMargin->setValue(_export->margins.top());
_bottomMargin->setValue(_export->margins.bottom());
_leftMargin->setValue(_export->margins.left());
_rightMargin->setValue(_export->margins.right());
}
QGridLayout *marginsLayout = new QGridLayout();
marginsLayout->addWidget(_topMargin, 0, 0, 1, 2, Qt::AlignCenter);
marginsLayout->addWidget(_leftMargin, 1, 0, 1, 1, Qt::AlignRight);
marginsLayout->addWidget(_rightMargin, 1, 1, 1, 1, Qt::AlignLeft);
marginsLayout->addWidget(_bottomMargin, 2, 0, 1, 2, Qt::AlignCenter);
#ifndef Q_OS_MAC
QGroupBox *pageSetupBox = new QGroupBox(tr("Page Setup"));
#endif // Q_OS_MAC
QFormLayout *pageSetupLayout = new QFormLayout;
pageSetupLayout->addRow(tr("Page size:"), _paperSize);
pageSetupLayout->addRow(tr("Orientation:"), orientationLayout);
pageSetupLayout->addRow(tr("Margins:"), marginsLayout);
#ifdef Q_OS_MAC
QFrame *line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
pageSetupLayout->addRow(line);
pageSetupLayout->addRow(tr("File:"), _fileSelect);
pageSetupLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
#else // Q_OS_MAC
pageSetupBox->setLayout(pageSetupLayout);
#endif // Q_OS_MAC
#ifndef Q_OS_MAC
QGroupBox *outputFileBox = new QGroupBox(tr("Output file"));
QHBoxLayout *outputFileLayout = new QHBoxLayout();
outputFileLayout->addWidget(_fileSelect);
outputFileBox->setLayout(outputFileLayout);
#endif // Q_OS_MAC
QDialogButtonBox *buttonBox = new QDialogButtonBox();
buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
buttonBox->addButton(QDialogButtonBox::Cancel);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
QVBoxLayout *layout = new QVBoxLayout;
#ifdef Q_OS_MAC
layout->addLayout(pageSetupLayout);
#else // Q_OS_MAC
layout->addWidget(pageSetupBox);
layout->addWidget(outputFileBox);
#endif // Q_OS_MAC
layout->addWidget(buttonBox);
setLayout(layout);
setWindowTitle(tr("Export to PDF"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
}
bool ExportDialog::checkFile()
{
if (_fileSelect->file().isEmpty()) {
QMessageBox::warning(this, tr("Error"), tr("No output file selected."));
return false;
}
QFile file(_fileSelect->file());
QFileInfo fi(file);
bool exists = fi.exists();
bool opened = false;
if (exists && fi.isDir()) {
QMessageBox::warning(this, tr("Error"), tr("%1 is a directory.")
.arg(file.fileName()));
return false;
} else if ((exists && !fi.isWritable())
|| !(opened = file.open(QFile::Append))) {
QMessageBox::warning(this, tr("Error"), tr("%1 is not writable.")
.arg(file.fileName()));
return false;
}
if (opened) {
file.close();
if (!exists)
file.remove();
}
return true;
}
void ExportDialog::accept()
{
if (!checkFile())
return;
QPrinter::Orientation orientation = _portrait->isChecked()
? QPrinter::Portrait : QPrinter::Landscape;
QPrinter::PaperSize paperSize = static_cast<QPrinter::PaperSize>
(_paperSize->itemData(_paperSize->currentIndex()).toInt());
_export->fileName = _fileSelect->file();
_export->paperSize = paperSize;
_export->orientation = orientation;
if (_export->units == Imperial)
_export->margins = MarginsF(_leftMargin->value() / MM2IN,
_topMargin->value() / MM2IN, _rightMargin->value() / MM2IN,
_bottomMargin->value() / MM2IN);
else
_export->margins = MarginsF(_leftMargin->value(), _topMargin->value(),
_rightMargin->value(), _bottomMargin->value());
QDialog::accept();
}

48
src/exportdialog.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef EXPORTDIALOG_H
#define EXPORTDIALOG_H
#include <QDialog>
#include <QPrinter>
#include "margins.h"
#include "units.h"
class QComboBox;
class QRadioButton;
class FileSelectWidget;
class QDoubleSpinBox;
struct Export {
QString fileName;
QPrinter::PaperSize paperSize;
QPrinter::Orientation orientation;
MarginsF margins;
Units units;
};
class ExportDialog : public QDialog
{
Q_OBJECT
public:
ExportDialog(Export *exp, QWidget *parent = 0);
public slots:
void accept();
private:
bool checkFile();
Export *_export;
FileSelectWidget *_fileSelect;
QComboBox *_paperSize;
QRadioButton *_portrait;
QRadioButton *_landscape;
QDoubleSpinBox *_topMargin;
QDoubleSpinBox *_bottomMargin;
QDoubleSpinBox *_leftMargin;
QDoubleSpinBox *_rightMargin;
};
#endif // EXPORTDIALOG_H

View File

@ -41,12 +41,12 @@ void FileBrowser::setFilter(const QStringList &filter)
reloadDirectory(_files.last().canonicalPath());
}
bool FileBrowser::isLast()
bool FileBrowser::isLast() const
{
return (_files.size() > 0 && _index == _files.size() - 1);
}
bool FileBrowser::isFirst()
bool FileBrowser::isFirst() const
{
return (_files.size() > 0 && _index == 0);
}

View File

@ -23,8 +23,8 @@ public:
QString last();
QString first();
bool isLast();
bool isFirst();
bool isLast() const;
bool isFirst() const;
private slots:
void reloadDirectory(const QString &path);

43
src/fileselectwidget.cpp Normal file
View File

@ -0,0 +1,43 @@
#include <QPushButton>
#include <QToolButton>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QFileInfo>
#include <QApplication>
#include <QFontMetrics>
#include "fileselectwidget.h"
FileSelectWidget::FileSelectWidget(QWidget *parent) : QWidget(parent)
{
QFontMetrics fm(QApplication::font());
_edit = new QLineEdit();
_edit->setMinimumWidth(fm.boundingRect(QDir::homePath()).width());
#ifdef Q_OS_WIN32
_button = new QPushButton("...");
_button->setMaximumWidth(_button->sizeHint().width() / 2);
#else // Q_OS_WIN32
_button = new QToolButton();
_button->setText("...");
#endif // Q_OS_WIN32
connect(_button, SIGNAL(clicked()), this, SLOT(browse()));
QHBoxLayout *layout = new QHBoxLayout();
layout->setMargin(0);
layout->addWidget(_edit);
layout->addWidget(_button);
setLayout(layout);
QSizePolicy p(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
setSizePolicy(p);
}
void FileSelectWidget::browse()
{
QFileInfo fi(_edit->text());
QString fileName = QFileDialog::getSaveFileName(this, tr("Select file"),
fi.dir().absolutePath(), _filter);
if (!fileName.isEmpty())
_edit->setText(fileName);
}

35
src/fileselectwidget.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef FILESELECTWIDGET_H
#define FILESELECTWIDGET_H
#include <QWidget>
#include <QLineEdit>
class QPushButton;
class QToolButton;
class FileSelectWidget : public QWidget
{
Q_OBJECT
public:
FileSelectWidget(QWidget *parent = 0);
QString file() {return _edit->text();}
void setFile(const QString &file) {_edit->setText(file);}
void setFilter(const QString &filter) {_filter = filter;}
private slots:
void browse();
private:
QLineEdit *_edit;
#ifdef Q_OS_WIN32
QPushButton *_button;
#else // Q_OS_WIN32
QToolButton *_button;
#endif // Q_OS_WIN32
QString _filter;
};
#endif // FILESELECTWIDGET_H

360
src/fitparser.cpp Normal file
View File

@ -0,0 +1,360 @@
#include <cstring>
#include <QtEndian>
#include "assert.h"
#include "fitparser.h"
const quint32 FIT_MAGIC = 0x5449462E; // .FIT
#define RECORD_MESSAGE 20
#define TIMESTAMP_FIELD 253
FITParser::FITParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
{
memset(_defs, 0, sizeof(_defs));
_device = 0;
_endian = 0;
_timestamp = 0;
_len = 0;
}
void FITParser::clearDefinitions()
{
for (int i = 0; i < 16; i++) {
if (_defs[i].fields)
delete[] _defs[i].fields;
if (_defs[i].devFields)
delete[] _defs[i].devFields;
}
memset(_defs, 0, sizeof(_defs));
}
void FITParser::warning(const char *text) const
{
const QFile *file = static_cast<QFile *>(_device);
qWarning("%s:%d: %s\n", qPrintable(file->fileName()), _len, text);
}
bool FITParser::readData(char *data, size_t size)
{
qint64 n;
n = _device->read(data, size);
if (n < 0) {
_errorString = "I/O error";
return false;
} else if ((size_t)n < size) {
_errorString = "Premature end of data";
return false;
}
return true;
}
template<class T> bool FITParser::readValue(T &val)
{
T data;
if (!readData((char*)&data, sizeof(T)))
return false;
_len -= sizeof(T);
if (sizeof(T) > 1) {
if (_endian)
val = qFromBigEndian(data);
else
val = qFromLittleEndian(data);
} else
val = data;
return true;
}
bool FITParser::skipValue(size_t size)
{
size_t i;
quint8 val;
for (i = 0; i < size; i++)
if (!readValue(val))
return false;
return true;
}
bool FITParser::parseDefinitionMessage(quint8 header)
{
int local_id = header & 0x0f;
MessageDefinition* def = &_defs[local_id];
quint8 i;
if (def->fields) {
delete[] def->fields;
def->fields = 0;
}
if (def->devFields) {
delete[] def->devFields;
def->devFields = 0;
}
// reserved/unused
if (!readValue(i))
return false;
// endianness
if (!readValue(def->endian))
return false;
if (def->endian > 1) {
_errorString = "Bad endian field";
return false;
}
_endian = def->endian;
// global message number
if (!readValue(def->globalId))
return false;
// number of records
if (!readValue(def->numFields))
return false;
// definition records
def->fields = new Field[def->numFields];
for (i = 0; i < def->numFields; i++) {
STATIC_ASSERT(sizeof(def->fields[i]) == 3);
if (!readData((char*)&(def->fields[i]), sizeof(def->fields[i])))
return false;
_len -= sizeof(def->fields[i]);
}
// developer definition records
if (header & 0x20) {
if (!readValue(def->numDevFields))
return false;
def->devFields = new Field[def->numDevFields];
for (i = 0; i < def->numDevFields; i++) {
STATIC_ASSERT(sizeof(def->devFields[i]) == 3);
if (!readData((char*)&(def->devFields[i]),
sizeof(def->devFields[i])))
return false;
_len -= sizeof(def->devFields[i]);
}
}
return true;
}
bool FITParser::readField(Field *f, quint32 &val)
{
quint8 v8 = (quint8)-1;
quint16 v16 = (quint16)-1;
bool ret;
val = (quint32)-1;
switch (f->type) {
case 1: // sint8
case 2: // uint8
if (f->size == 1) {
ret = readValue(v8);
val = v8;
} else
ret = skipValue(f->size);
break;
case 0x83: // sint16
case 0x84: // uint16
if (f->size == 2) {
ret = readValue(v16);
val = v16;
} else
ret = skipValue(f->size);
break;
case 0x85: // sint32
case 0x86: // uint32
if (f->size == 4)
ret = readValue(val);
else
ret = skipValue(f->size);
break;
default:
ret = skipValue(f->size);
break;
}
return ret;
}
bool FITParser::parseData(MessageDefinition *def, quint8 offset)
{
Field *field;
quint32 timestamp = _timestamp + offset;
quint32 val;
Trackpoint trackpoint;
int i;
if (!def->fields && !def->devFields) {
_errorString = "Undefined data message";
return false;
}
_endian = def->endian;
for (i = 0; i < def->numFields; i++) {
field = &def->fields[i];
if (!readField(field, val))
return false;
if (field->id == TIMESTAMP_FIELD)
_timestamp = timestamp = val;
else if (def->globalId == RECORD_MESSAGE) {
switch (field->id) {
case 0:
if (val != 0x7fffffff)
trackpoint.rcoordinates().setLat(
((qint32)val / (double)0x7fffffff) * 180);
break;
case 1:
if (val != 0x7fffffff)
trackpoint.rcoordinates().setLon(
((qint32)val / (double)0x7fffffff) * 180);
break;
case 2:
if (val != 0xffff)
trackpoint.setElevation((val / 5.0) - 500);
break;
case 3:
if (val != 0xff)
trackpoint.setHeartRate(val);
break;
case 4:
if (val != 0xff)
trackpoint.setCadence(val);
break;
case 6:
if (val != 0xffff)
trackpoint.setSpeed(val / 1000.0f);
break;
case 7:
if (val != 0xffff)
trackpoint.setPower(val);
break;
case 13:
if (val != 0x7f)
trackpoint.setTemperature((qint8)val);
break;
default:
break;
}
}
}
for (i = 0; i < def->numDevFields; i++) {
field = &def->devFields[i];
if (!readField(field, val))
return false;
}
if (def->globalId == RECORD_MESSAGE) {
if (trackpoint.coordinates().isValid()) {
trackpoint.setTimestamp(QDateTime::fromTime_t(timestamp
+ 631065600));
_tracks.last().append(trackpoint);
} else {
if (trackpoint.coordinates().isNull())
warning("Missing coordinates");
else {
_errorString = "Invalid coordinates";
return false;
}
}
}
return true;
}
bool FITParser::parseDataMessage(quint8 header)
{
int local_id = header & 0xf;
MessageDefinition* def = &_defs[local_id];
return parseData(def, 0);
}
bool FITParser::parseCompressedMessage(quint8 header)
{
int local_id = (header >> 5) & 3;
MessageDefinition* def = &_defs[local_id];
return parseData(def, header & 0x1f);
}
bool FITParser::parseRecord()
{
quint8 header;
if (!readValue(header))
return false;
if (header & 0x80)
return parseCompressedMessage(header);
else if (header & 0x40)
return parseDefinitionMessage(header);
else
return parseDataMessage(header);
}
bool FITParser::parseHeader()
{
FileHeader hdr;
quint16 crc;
qint64 len;
STATIC_ASSERT(sizeof(hdr) == 12);
len = _device->read((char*)&hdr, sizeof(hdr));
if (len < 0) {
_errorString = "I/O error";
return false;
} else if ((size_t)len < sizeof(hdr)
|| hdr.magic != qToLittleEndian(FIT_MAGIC)) {
_errorString = "Not a FIT file";
return false;
}
_len = qFromLittleEndian(hdr.dataSize);
if (hdr.headerSize > sizeof(hdr))
if (!readData((char *)&crc, sizeof(crc)))
return false;
return true;
}
bool FITParser::loadFile(QFile *file)
{
bool ret = true;
_device = file;
_endian = 0;
_timestamp = 0;
if (!parseHeader())
return false;
_tracks.append(TrackData());
while (_len)
if ((ret = parseRecord()) == false)
break;
clearDefinitions();
return ret;
}

66
src/fitparser.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef FITPARSER_H
#define FITPARSER_H
#include "parser.h"
class FITParser : public Parser
{
public:
FITParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints);
~FITParser() {}
bool loadFile(QFile *file);
QString errorString() const {return _errorString;}
int errorLine() const {return 0;}
private:
typedef struct {
quint8 headerSize;
quint8 protocolVersion;
quint16 profileVersion;
quint32 dataSize;
quint32 magic;
} FileHeader;
typedef struct {
quint8 id;
quint8 size;
quint8 type;
} Field;
typedef struct {
quint8 endian;
quint16 globalId;
quint8 numFields;
Field *fields;
quint8 numDevFields;
Field *devFields;
} MessageDefinition;
void warning(const char *text) const;
void clearDefinitions();
bool readData(char *data, size_t size);
template<class T> bool readValue(T &val);
bool skipValue(size_t size);
bool parseHeader();
bool parseRecord();
bool parseDefinitionMessage(quint8 header);
bool parseCompressedMessage(quint8 header);
bool parseDataMessage(quint8 header);
bool parseData(MessageDefinition *def, quint8 offset);
bool readField(Field *f, quint32 &val);
QIODevice *_device;
QString _errorString;
quint32 _len;
quint8 _endian;
quint32 _timestamp;
MessageDefinition _defs[16];
};
#endif // FITPARSER_H

53
src/format.cpp Normal file
View File

@ -0,0 +1,53 @@
#include <QApplication>
#include "coordinates.h"
#include "format.h"
QString Format::timeSpan(qreal time)
{
unsigned h, m, s;
h = time / 3600;
m = (time - (h * 3600)) / 60;
s = time - (h * 3600) - (m * 60);
return QString("%1:%2:%3").arg(h).arg(m, 2, 10, QChar('0'))
.arg(s, 2, 10, QChar('0'));
}
QString Format::distance(qreal value, Units units)
{
if (units == Imperial) {
if (value < MIINM)
return QString::number(value * M2FT, 'f', 0) + UNIT_SPACE
+ qApp->translate("Format", "ft");
else
return QString::number(value * M2MI, 'f', 1) + UNIT_SPACE
+ qApp->translate("Format", "mi");
} else {
if (value < KMINM)
return QString::number(value, 'f', 0) + UNIT_SPACE
+ qApp->translate("Format", "m");
else
return QString::number(value * M2KM, 'f', 1) + UNIT_SPACE
+ qApp->translate("Format", "km");
}
}
QString Format::elevation(qreal value, Units units)
{
if (units == Metric)
return QString::number(value, 'f', 0) + UNIT_SPACE
+ qApp->translate("Format", "m");
else
return QString::number(value * M2FT, 'f', 0) + UNIT_SPACE
+ qApp->translate("Format", "ft");
}
QString Format::coordinates(const Coordinates &value)
{
QChar yH = (value.lat() < 0) ? 'S' : 'N';
QChar xH = (value.lon() < 0) ? 'W' : 'E';
return QString::number(qAbs(value.lat()), 'f', 5) + yH + "," + QChar(0x00A0)
+ QString::number(qAbs(value.lon()), 'f', 5) + xH;
}

17
src/format.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef FORMAT_H
#define FORMAT_H
#include <QString>
#include "units.h"
class Coordinates;
namespace Format
{
QString timeSpan(qreal time);
QString distance(qreal value, Units units);
QString elevation(qreal value, Units units);
QString coordinates(const Coordinates &value);
}
#endif // FORMAT_H

View File

@ -1,182 +0,0 @@
#include <QFile>
#include <QLineF>
#include "ll.h"
#include "gpx.h"
#define WINDOW_EF 3
#define WINDOW_SE 11
#define WINDOW_SF 11
static bool lt(const QPointF &p1, const QPointF &p2)
{
return p1.y() < p2.y();
}
static qreal median(QVector<QPointF> v)
{
qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2).y();
}
static qreal MAD(QVector<QPointF> v, qreal m)
{
for (int i = 0; i < v.size(); i++)
v[i].setY(qAbs(v.at(i).y() - m));
qSort(v.begin(), v.end(), lt);
return v.at(v.size() / 2).y();
}
static QVector<QPointF> eliminate(const QVector<QPointF> &v, int window)
{
QList<int> rm;
QVector<QPointF> ret;
qreal m, M;
if (v.size() < window)
return QVector<QPointF>(v);
for (int i = window/2; i < v.size() - window/2; i++) {
m = median(v.mid(i - window/2, window));
M = MAD(v.mid(i - window/2, window), m);
if (qAbs((0.6745 * (v.at(i).y() - m)) / M) > 3.5)
rm.append(i);
}
QList<int>::const_iterator it = rm.begin();
for (int i = 0; i < v.size(); i++) {
if (it == rm.end() || *it != i)
ret.append(v.at(i));
else
it++;
}
return ret;
}
static QVector<QPointF> filter(const QVector<QPointF> &v, int window)
{
qreal acc = 0;
QVector<QPointF> ret;
if (v.size() < window)
return QVector<QPointF>(v);
for (int i = 0; i < window; i++)
acc += v.at(i).y();
for (int i = 0; i <= window/2; i++)
ret.append(QPointF(v.at(i).x(), acc/window));
for (int i = window/2 + 1; i < v.size() - window/2; i++) {
acc += v.at(i + window/2).y() - v.at(i - (window/2 + 1)).y();
ret.append(QPointF(v.at(i).x(), acc/window));
}
for (int i = v.size() - window/2; i < v.size(); i++)
ret.append(QPointF(v.at(i).x(), acc/window));
return ret;
}
bool GPX::loadFile(const QString &fileName)
{
QFile file(fileName);
bool ret;
_data.clear();
_error.clear();
_errorLine = 0;
if (!file.open(QFile::ReadOnly | QFile::Text)) {
_error = qPrintable(file.errorString());
return false;
}
if (!(ret = _parser.loadFile(&file, _data))) {
_error = _parser.errorString();
_errorLine = _parser.errorLine();
}
file.close();
return ret;
}
void GPX::elevationGraph(QVector<QPointF> &graph) const
{
qreal dist = 0;
QVector<QPointF> raw;
if (!_data.size())
return;
raw.append(QPointF(0, _data.at(0).elevation));
for (int i = 1; i < _data.size(); i++) {
dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
raw.append(QPointF(dist, _data.at(i).elevation
- _data.at(i).geoidheight));
}
graph = filter(raw, WINDOW_EF);
}
void GPX::speedGraph(QVector<QPointF> &graph) const
{
qreal dist = 0, v, ds, dt;
QVector<QPointF> raw;
if (!_data.size())
return;
raw.append(QPointF(0, 0));
for (int i = 1; i < _data.size(); i++) {
ds = llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
dt = _data.at(i-1).timestamp.msecsTo(_data.at(i).timestamp) / 1000.0;
dist += ds;
if (_data.at(i).speed < 0) {
if (dt == 0)
continue;
v = ds / dt;
} else
v = _data.at(i).speed;
raw.append(QPointF(dist, v));
}
graph = filter(eliminate(raw, WINDOW_SE), WINDOW_SF);
}
void GPX::track(QVector<QPointF> &track) const
{
for (int i = 0; i < _data.size(); i++)
track.append(ll2mercator(_data.at(i).coordinates));
}
qreal GPX::distance() const
{
qreal dist = 0;
for (int i = 1; i < _data.size(); i++)
dist += llDistance(_data.at(i).coordinates, _data.at(i-1).coordinates);
return dist;
}
qreal GPX::time() const
{
if (_data.size() < 2)
return 0;
return (_data.at(0).timestamp.msecsTo(_data.at(_data.size() - 1).timestamp)
/ 1000.0);
}
QDateTime GPX::date() const
{
if (_data.size())
return _data.at(0).timestamp;
else
return QDateTime();
}

View File

@ -1,30 +0,0 @@
#ifndef GPX_H
#define GPX_H
#include <QVector>
#include <QPointF>
#include <QString>
#include "parser.h"
class GPX
{
public:
bool loadFile(const QString &fileName);
const QString &errorString() const {return _error;}
int errorLine() const {return _errorLine;}
void elevationGraph(QVector<QPointF> &graph) const;
void speedGraph(QVector<QPointF> &graph) const;
void track(QVector<QPointF> &track) const;
qreal distance() const;
qreal time() const;
QDateTime date() const;
private:
Parser _parser;
QVector<TrackPoint> _data;
QString _error;
int _errorLine;
};
#endif // GPX_H

205
src/gpxparser.cpp Normal file
View File

@ -0,0 +1,205 @@
#include "gpxparser.h"
qreal GPXParser::number()
{
bool res;
qreal ret = _reader.readElementText().toDouble(&res);
if (!res)
_reader.raiseError(QString("Invalid %1").arg(
_reader.name().toString()));
return ret;
}
QDateTime GPXParser::time()
{
QDateTime d = QDateTime::fromString(_reader.readElementText(),
Qt::ISODate);
if (!d.isValid())
_reader.raiseError(QString("Invalid %1").arg(
_reader.name().toString()));
return d;
}
Coordinates GPXParser::coordinates()
{
bool res;
qreal lon, lat;
const QXmlStreamAttributes &attr = _reader.attributes();
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
lon = attr.value("lon").toString().toDouble(&res);
#else // QT_VERSION < 5
lon = attr.value("lon").toDouble(&res);
#endif // QT_VERSION < 5
if (!res || (lon < -180.0 || lon > 180.0)) {
_reader.raiseError("Invalid longitude");
return Coordinates();
}
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
lat = attr.value("lat").toString().toDouble(&res);
#else // QT_VERSION < 5
lat = attr.value("lat").toDouble(&res);
#endif // QT_VERSION < 5
if (!res || (lat < -90.0 || lat > 90.0)) {
_reader.raiseError("Invalid latitude");
return Coordinates();
}
return Coordinates(lon, lat);
}
void GPXParser::tpExtension(Trackpoint &trackpoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "hr")
trackpoint.setHeartRate(number());
else if (_reader.name() == "atemp")
trackpoint.setTemperature(number());
else
_reader.skipCurrentElement();
}
}
void GPXParser::extensions(Trackpoint &trackpoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "speed")
trackpoint.setSpeed(number());
else if (_reader.name() == "hr" || _reader.name() == "heartrate")
trackpoint.setHeartRate(number());
else if (_reader.name() == "temp")
trackpoint.setTemperature(number());
else if (_reader.name() == "cadence")
trackpoint.setCadence(number());
else if (_reader.name() == "power")
trackpoint.setPower(number());
else if (_reader.name() == "TrackPointExtension")
tpExtension(trackpoint);
else
_reader.skipCurrentElement();
}
}
void GPXParser::trackpointData(Trackpoint &trackpoint)
{
qreal gh = NAN;
while (_reader.readNextStartElement()) {
if (_reader.name() == "ele")
trackpoint.setElevation(number());
else if (_reader.name() == "time")
trackpoint.setTimestamp(time());
else if (_reader.name() == "geoidheight")
gh = number();
else if (_reader.name() == "extensions")
extensions(trackpoint);
else
_reader.skipCurrentElement();
}
if (!std::isnan(gh) && !std::isnan(trackpoint.elevation()))
trackpoint.setElevation(trackpoint.elevation() - gh);
}
void GPXParser::waypointData(Waypoint &waypoint)
{
qreal gh = NAN;
while (_reader.readNextStartElement()) {
if (_reader.name() == "name")
waypoint.setName(_reader.readElementText());
else if (_reader.name() == "desc")
waypoint.setDescription(_reader.readElementText());
else if (_reader.name() == "ele")
waypoint.setElevation(number());
else if (_reader.name() == "geoidheight")
gh = number();
else if (_reader.name() == "time")
waypoint.setTimestamp(time());
else
_reader.skipCurrentElement();
}
if (!std::isnan(gh) && !std::isnan(waypoint.elevation()))
waypoint.setElevation(waypoint.elevation() - gh);
}
void GPXParser::trackpoints(TrackData &track)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "trkpt") {
track.append(Trackpoint(coordinates()));
trackpointData(track.last());
} else
_reader.skipCurrentElement();
}
}
void GPXParser::routepoints(RouteData &route)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "rtept") {
route.append(Waypoint(coordinates()));
waypointData(route.last());
} else if (_reader.name() == "name")
route.setName(_reader.readElementText());
else if (_reader.name() == "desc")
route.setDescription(_reader.readElementText());
else
_reader.skipCurrentElement();
}
}
void GPXParser::track(TrackData &track)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "trkseg")
trackpoints(track);
else if (_reader.name() == "name")
track.setName(_reader.readElementText());
else if (_reader.name() == "desc")
track.setDescription(_reader.readElementText());
else
_reader.skipCurrentElement();
}
}
void GPXParser::gpx()
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "trk") {
_tracks.append(TrackData());
track(_tracks.back());
} else if (_reader.name() == "rte") {
_routes.append(RouteData());
routepoints(_routes.back());
} else if (_reader.name() == "wpt") {
_waypoints.append(Waypoint(coordinates()));
waypointData(_waypoints.last());
} else
_reader.skipCurrentElement();
}
}
bool GPXParser::parse()
{
if (_reader.readNextStartElement()) {
if (_reader.name() == "gpx")
gpx();
else
_reader.raiseError("Not a GPX file");
}
return !_reader.error();
}
bool GPXParser::loadFile(QFile *file)
{
_reader.clear();
_reader.setDevice(file);
return parse();
}

36
src/gpxparser.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef GPXPARSER_H
#define GPXPARSER_H
#include <QXmlStreamReader>
#include "parser.h"
class GPXParser : public Parser
{
public:
GPXParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints) {}
~GPXParser() {}
bool loadFile(QFile *file);
QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();}
private:
bool parse();
void gpx();
void track(TrackData &track);
void trackpoints(TrackData &track);
void routepoints(RouteData &route);
void tpExtension(Trackpoint &trackpoint);
void extensions(Trackpoint &trackpoint);
void trackpointData(Trackpoint &trackpoint);
void waypointData(Waypoint &waypoint);
qreal number();
QDateTime time();
Coordinates coordinates();
QXmlStreamReader _reader;
};
#endif // GPXPARSER_H

View File

@ -1,316 +1,9 @@
#include <float.h>
#include <QGraphicsView>
#include <QGraphicsSceneMouseEvent>
#include <QEvent>
#include <QGraphicsSimpleTextItem>
#include "slideritem.h"
#include "sliderinfoitem.h"
#include "infoitem.h"
#include "config.h"
#include "graph.h"
#define MARGIN 10.0
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
QDebug operator<<(QDebug dbg, const GraphPoint &point)
{
if (e->button() == Qt::LeftButton)
emit mouseClicked(e->scenePos());
dbg.nospace() << "GraphPoint(" << point.s() << ", " << point.t() << ", "
<< point.y() << ")";
QGraphicsScene::mousePressEvent(e);
}
Graph::Graph(QWidget *parent)
: QGraphicsView(parent)
{
_scene = new Scene(this);
setScene(_scene);
_xAxis = new AxisItem(AxisItem::X);
_yAxis = new AxisItem(AxisItem::Y);
_slider = new SliderItem();
_slider->setZValue(2.0);
connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
SLOT(emitSliderPositionChanged(const QPointF&)));
connect(_scene, SIGNAL(mouseClicked(const QPointF&)), this,
SLOT(newSliderPosition(const QPointF&)));
_info = new InfoItem();
_sliderInfo = new SliderInfoItem(_slider);
_sliderInfo->setZValue(2.0);
_xMax = -FLT_MAX;
_xMin = FLT_MAX;
_yMax = -FLT_MAX;
_yMin = FLT_MAX;
_xScale = 1;
_yScale = 1;
_precision = 0;
}
Graph::~Graph()
{
if (_xAxis->scene() != _scene)
delete _xAxis;
if (_yAxis->scene() != _scene)
delete _yAxis;
if (_slider->scene() != _scene)
delete _slider;
if (_info->scene() != _scene)
delete _info;
delete _scene;
}
void Graph::updateBounds(const QPointF &point)
{
if (point.x() < _xMin)
_xMin = point.x();
if (point.x() > _xMax)
_xMax = point.x();
if (point.y() < _yMin)
_yMin = point.y();
if (point.y() > _yMax)
_yMax = point.y();
}
void Graph::createXLabel()
{
_xAxis->setLabel(QString("%1 [%2]").arg(_xLabel).arg(_xUnits));
}
void Graph::createYLabel()
{
_yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits));
}
void Graph::setXLabel(const QString &label)
{
_xLabel = label;
createXLabel();
}
void Graph::setYLabel(const QString &label)
{
_yLabel = label;
createYLabel();
}
void Graph::setXUnits(const QString &units)
{
_xUnits = units;
createXLabel();
}
void Graph::setYUnits(const QString &units)
{
_yUnits = units;
createYLabel();
}
void Graph::setXScale(qreal scale)
{
_xScale = scale;
}
void Graph::setYScale(qreal scale)
{
_yScale = scale;
}
void Graph::loadData(const QVector<QPointF> &data)
{
QPainterPath path;
QGraphicsPathItem *pi;
QColor color = _colorShop.color();
if (data.size() < 2)
return;
updateBounds(data.at(0));
path.moveTo(data.at(0).x(), -data.at(0).y());
for (int i = 1; i < data.size(); i++) {
path.lineTo(data.at(i).x(), -data.at(i).y());
updateBounds(data.at(i));
}
pi = new QGraphicsPathItem(path);
QBrush brush(color, Qt::SolidPattern);
QPen pen(brush, 0);
pi->setPen(pen);
_scene->addItem(pi);
_graphs.append(pi);
if (_graphs.size() > 1)
_sliderInfo->hide();
redraw();
}
void Graph::redraw()
{
if (!_graphs.isEmpty())
resize(viewport()->size() - QSizeF(MARGIN, MARGIN));
}
void Graph::resize(const QSizeF &size)
{
QRectF r;
QSizeF mx, my;
QTransform transform;
qreal xs, ys;
if (_xAxis->scene() == _scene)
_scene->removeItem(_xAxis);
if (_yAxis->scene() == _scene)
_scene->removeItem(_yAxis);
if (_slider->scene() == _scene)
_scene->removeItem(_slider);
if (_info->scene() == _scene)
_scene->removeItem(_info);
for (int i = 0; i < _graphs.size(); i++)
_graphs.at(i)->resetTransform();
_xAxis->setRange(QPointF(_xMin * _xScale, _xMax * _xScale));
_yAxis->setRange(QPointF(_yMin * _yScale, _yMax * _yScale));
mx = _xAxis->margin();
my = _yAxis->margin();
r = _scene->itemsBoundingRect();
xs = (size.width() - (my.width() + mx.width())) / r.width();
ys = (size.height() - (mx.height() + my.height())
- _info->boundingRect().height()) / r.height();
transform.scale(xs, ys);
for (int i = 0; i < _graphs.size(); i++)
_graphs.at(i)->setTransform(transform);
r = _scene->itemsBoundingRect();
_xAxis->setSize(r.width());
_yAxis->setSize(r.height());
_xAxis->setPos(r.bottomLeft());
_yAxis->setPos(r.bottomLeft());
_scene->addItem(_xAxis);
_scene->addItem(_yAxis);
qreal sp = (_slider->pos().x() == _slider->area().left())
? 0 : (_slider->pos().x() - _slider->area().left())
/ _slider->area().width();
_slider->setArea(r);
_slider->setPos(QPointF(sp * r.width(), r.bottom()));
_scene->addItem(_slider);
const QPainterPath &path = _graphs.at(0)->path();
QPointF p = path.pointAtPercent(sp);
_sliderInfo->setText(QString::number(-p.y() * _yScale, 'f', _precision));
r = _scene->itemsBoundingRect();
_info->setPos(r.topLeft() + QPointF(r.width()/2
- _info->boundingRect().width()/2, -_info->boundingRect().height()));
_scene->addItem(_info);
_scene->setSceneRect(_scene->itemsBoundingRect());
}
void Graph::resizeEvent(QResizeEvent *)
{
if (!_graphs.empty())
redraw();
}
void Graph::plot(QPainter *painter, const QRectF &target)
{
qreal ratio = target.width() / target.height();
QSizeF orig = _scene->sceneRect().size();
QSizeF canvas = QSizeF(orig.height() * ratio, orig.height());
resize(canvas);
_slider->hide();
_info->hide();
_scene->render(painter, target, QRectF(), Qt::KeepAspectRatioByExpanding);
_slider->show();
_info->show();
resize(orig);
}
void Graph::clear()
{
if (_xAxis->scene() == _scene)
_scene->removeItem(_xAxis);
if (_yAxis->scene() == _scene)
_scene->removeItem(_yAxis);
if (_slider->scene() == _scene)
_scene->removeItem(_slider);
if (_info->scene() == _scene)
_scene->removeItem(_info);
_sliderInfo->show();
_slider->clear();
_info->clear();
_scene->clear();
_graphs.clear();
_colorShop.reset();
_xMax = -FLT_MAX;
_xMin = FLT_MAX;
_yMax = -FLT_MAX;
_yMin = FLT_MAX;
_scene->setSceneRect(0, 0, 0, 0);
}
void Graph::emitSliderPositionChanged(const QPointF &pos)
{
if (_graphs.isEmpty())
return;
qreal val = pos.x() / _slider->area().width();
emit sliderPositionChanged(val);
const QPainterPath &path = _graphs.at(0)->path();
QPointF p = path.pointAtPercent(val);
qreal r = (p.y() - path.boundingRect().bottom())
/ path.boundingRect().height();
_sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r));
_sliderInfo->setText(QString::number(-p.y() * _yScale, 'f', _precision));
}
qreal Graph::sliderPosition() const
{
return _slider->pos().x() / _slider->area().width();
}
void Graph::setSliderPosition(qreal pos)
{
_slider->setPos(pos * _slider->area().width(), 0);
}
void Graph::newSliderPosition(const QPointF &pos)
{
if (_slider->area().contains(pos)) {
_slider->setPos(pos);
emitSliderPositionChanged(pos);
}
}
void Graph::addInfo(const QString &key, const QString &value)
{
_info->insert(key, value);
}
void Graph::clearInfo()
{
_info->clear();
return dbg.maybeSpace();
}

View File

@ -1,92 +1,52 @@
#ifndef GRAPH_H
#define GRAPH_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QVector>
#include <QList>
#include <QPointF>
#include "axisitem.h"
#include "colorshop.h"
#include <QDebug>
#include <cmath>
enum GraphType {Distance, Time};
#define THIN_SPACE QString::fromUtf8("\xE2\x80\x89")
class SliderItem;
class SliderInfoItem;
class InfoItem;
class Scene : public QGraphicsScene
class GraphPoint
{
Q_OBJECT
public:
Scene(QObject *parent = 0) : QGraphicsScene(parent) {}
void mousePressEvent(QGraphicsSceneMouseEvent *e);
GraphPoint(qreal s = NAN, qreal t = NAN, qreal y = NAN)
: _s(s), _t(t), _y(y) {}
signals:
void mouseClicked(const QPointF &pos);
};
qreal s() const {return _s;}
qreal t() const {return _t;}
qreal y() const {return _y;}
qreal x(GraphType type) const {return (type == Distance) ? _s : _t;}
class Graph : public QGraphicsView
{
Q_OBJECT
public:
Graph(QWidget *parent = 0);
~Graph();
void loadData(const QVector<QPointF> &data);
void setXLabel(const QString &label);
void setYLabel(const QString &label);
void setXUnits(const QString &units);
void setYUnits(const QString &units);
void setXScale(qreal scale);
void setYScale(qreal scale);
void setPrecision(int p) {_precision = p;}
void redraw();
void plot(QPainter *painter, const QRectF &target);
void clear();
qreal sliderPosition() const;
void setSliderPosition(qreal pos);
void addInfo(const QString &key, const QString &value);
void clearInfo();
signals:
void sliderPositionChanged(qreal);
protected:
void resizeEvent(QResizeEvent *);
qreal _xScale, _yScale;
QString _xUnits, _yUnits;
QString _xLabel, _yLabel;
int _precision;
private slots:
void emitSliderPositionChanged(const QPointF &pos);
void newSliderPosition(const QPointF &pos);
void setS(qreal s) {_s = s;}
void setT(qreal t) {_t = t;}
void setY(qreal y) {_y = y;}
private:
void createXLabel();
void createYLabel();
void updateBounds(const QPointF &point);
void resize(const QSizeF &size);
qreal _s;
qreal _t;
qreal _y;
};
Scene *_scene;
Q_DECLARE_TYPEINFO(GraphPoint, Q_PRIMITIVE_TYPE);
QDebug operator<<(QDebug dbg, const GraphPoint &point);
AxisItem *_xAxis, *_yAxis;
SliderItem *_slider;
SliderInfoItem *_sliderInfo;
InfoItem *_info;
QList<QGraphicsPathItem*> _graphs;
qreal _xMin, _xMax, _yMin, _yMax;
ColorShop _colorShop;
class Graph : public QVector<GraphPoint>
{
public:
Graph() : QVector<GraphPoint>() {_time = true;}
void append(const GraphPoint &p)
{
if (std::isnan(p.t()))
_time = false;
QVector<GraphPoint>::append(p);
}
bool hasTime() const {return _time;}
private:
bool _time;
};
#endif // GRAPH_H

193
src/graphitem.cpp Normal file
View File

@ -0,0 +1,193 @@
#include <QPainter>
#include "graphitem.h"
GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
: QGraphicsObject(parent)
{
_id = 0;
_width = 1;
_pen = QPen(Qt::black, _width);
_type = Distance;
_graph = graph;
_sx = 1.0; _sy = 1.0;
setZValue(1.0);
updatePath();
updateBounds();
}
void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(_pen);
painter->drawPath(_path);
/*
QPen p = QPen(QBrush(Qt::red), 0);
painter->setPen(p);
painter->drawRect(boundingRect());
*/
}
void GraphItem::setGraphType(GraphType type)
{
prepareGeometryChange();
_type = type;
updatePath();
updateBounds();
}
void GraphItem::setColor(const QColor &color)
{
_pen.setColor(color);
update();
}
void GraphItem::setWidth(int width)
{
prepareGeometryChange();
_width = width;
_pen.setWidth(width);
}
qreal GraphItem::yAtX(qreal x)
{
int low = 0;
int high = _graph.count() - 1;
int mid = 0;
Q_ASSERT(high > low);
Q_ASSERT(x >= _graph.at(low).x(_type) && x <= _graph.at(high).x(_type));
while (low <= high) {
mid = low + ((high - low) / 2);
const GraphPoint &p = _graph.at(mid);
if (p.x(_type) > x)
high = mid - 1;
else if (p.x(_type) < x)
low = mid + 1;
else
return -p.y();
}
QLineF l;
if (_graph.at(mid).x(_type) < x)
l = QLineF(_graph.at(mid).x(_type), _graph.at(mid).y(),
_graph.at(mid+1).x(_type), _graph.at(mid+1).y());
else
l = QLineF(_graph.at(mid-1).x(_type), _graph.at(mid-1).y(),
_graph.at(mid).x(_type), _graph.at(mid).y());
return -l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
}
qreal GraphItem::distanceAtTime(qreal time)
{
int low = 0;
int high = _graph.count() - 1;
int mid = 0;
Q_ASSERT(high > low);
Q_ASSERT(time >= _graph.at(low).t() && time <= _graph.at(high).t());
while (low <= high) {
mid = low + ((high - low) / 2);
const GraphPoint &p = _graph.at(mid);
if (p.t() > time)
high = mid - 1;
else if (p.t() < time)
low = mid + 1;
else
return _graph.at(mid).s();
}
QLineF l;
if (_graph.at(mid).t() < time)
l = QLineF(_graph.at(mid).t(), _graph.at(mid).s(), _graph.at(mid+1).t(),
_graph.at(mid+1).s());
else
l = QLineF(_graph.at(mid-1).t(), _graph.at(mid-1).s(),
_graph.at(mid).t(), _graph.at(mid).s());
return l.pointAt((time - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
}
void GraphItem::emitSliderPositionChanged(qreal pos)
{
if (_type == Time) {
if (_graph.hasTime()) {
if (pos >= _graph.first().t() && pos <= _graph.last().t())
emit sliderPositionChanged(distanceAtTime(pos));
else
emit sliderPositionChanged(NAN);
} else
emit sliderPositionChanged(NAN);
} else
emit sliderPositionChanged(pos);
}
void GraphItem::selected(bool selected)
{
if (selected) {
_pen.setWidth(_width + 1);
setZValue(zValue() + 1.0);
} else {
_pen.setWidth(_width);
setZValue(zValue() - 1.0);
}
update();
}
void GraphItem::setScale(qreal sx, qreal sy)
{
if (_sx == sx && _sy == sy)
return;
prepareGeometryChange();
_sx = sx; _sy = sy;
updatePath();
}
void GraphItem::updatePath()
{
_path = QPainterPath();
if (_type == Time && !_graph.hasTime())
return;
_path.moveTo(_graph.first().x(_type) * _sx, -_graph.first().y() * _sy);
for (int i = 1; i < _graph.size(); i++)
_path.lineTo(_graph.at(i).x(_type) * _sx, -_graph.at(i).y() * _sy);
}
void GraphItem::updateBounds()
{
if (_type == Time && !_graph.hasTime()) {
_bounds = QRectF();
return;
}
qreal bottom, top, left, right;
QPointF p = QPointF(_graph.first().x(_type), -_graph.first().y());
bottom = p.y(); top = p.y(); left = p.x(); right = p.x();
for (int i = 1; i < _graph.size(); i++) {
p = QPointF(_graph.at(i).x(_type), -_graph.at(i).y());
bottom = qMax(bottom, p.y()); top = qMin(top, p.y());
right = qMax(right, p.x()); left = qMin(left, p.x());
}
_bounds = QRectF(QPointF(left, top), QPointF(right, bottom));
}

55
src/graphitem.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef GRAPHITEM_H
#define GRAPHITEM_H
#include <QGraphicsObject>
#include <QPen>
#include "graph.h"
class GraphItem : public QGraphicsObject
{
Q_OBJECT
public:
GraphItem(const Graph &graph, QGraphicsItem *parent = 0);
QRectF boundingRect() const
{return _path.boundingRect();}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
const QRectF &bounds() const {return _bounds;}
void setScale(qreal sx, qreal sy);
void setGraphType(GraphType type);
int id() const {return _id;}
void setId(int id) {_id = id;}
void setColor(const QColor &color);
void setWidth(int width);
qreal yAtX(qreal x);
qreal distanceAtTime(qreal time);
signals:
void sliderPositionChanged(qreal);
public slots:
void emitSliderPositionChanged(qreal);
void selected(bool selected);
private:
void updatePath();
void updateBounds();
int _id;
QPen _pen;
int _width;
Graph _graph;
GraphType _type;
QPainterPath _path;
QRectF _bounds;
qreal _sx, _sy;
};
#endif // GRAPHITEM_H

29
src/graphtab.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef GRAPHTAB_H
#define GRAPHTAB_H
#include <QList>
#include "graphview.h"
#include "units.h"
#include "timetype.h"
class Data;
class PathItem;
class GraphTab : public GraphView
{
Q_OBJECT
public:
GraphTab(QWidget *parent = 0) : GraphView(parent)
{setFrameShape(QFrame::NoFrame);}
virtual QString label() const = 0;
virtual void loadData(const Data &data, const QList<PathItem *> &paths) = 0;
virtual void clear() {}
virtual void setUnits(enum Units units) {Q_UNUSED(units)}
virtual void setTimeType(enum TimeType type) {Q_UNUSED(type)}
virtual void showTracks(bool show) {Q_UNUSED(show)}
virtual void showRoutes(bool show) {Q_UNUSED(show)}
};
#endif // GRAPHTAB_H

465
src/graphview.cpp Normal file
View File

@ -0,0 +1,465 @@
#include <QGraphicsScene>
#include <QEvent>
#include <QMouseEvent>
#include <QPaintEngine>
#include <QPaintDevice>
#include "opengl.h"
#include "config.h"
#include "axisitem.h"
#include "slideritem.h"
#include "sliderinfoitem.h"
#include "infoitem.h"
#include "griditem.h"
#include "graph.h"
#include "graphitem.h"
#include "pathitem.h"
#include "graphview.h"
#define MARGIN 10.0
GraphView::GraphView(QWidget *parent)
: QGraphicsView(parent)
{
_scene = new QGraphicsScene(this);
setScene(_scene);
setBackgroundBrush(QBrush(Qt::white));
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
setRenderHint(QPainter::Antialiasing, true);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
_xAxis = new AxisItem(AxisItem::X);
_xAxis->setZValue(2.0);
_yAxis = new AxisItem(AxisItem::Y);
_yAxis->setZValue(2.0);
_slider = new SliderItem();
_slider->setZValue(3.0);
_sliderInfo = new SliderInfoItem(_slider);
_sliderInfo->setZValue(3.0);
_info = new InfoItem();
_grid = new GridItem();
connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
SLOT(emitSliderPositionChanged(const QPointF&)));
_width = 1;
_xScale = 1;
_yScale = 1;
_yOffset = 0;
_precision = 0;
_minYRange = 0.01;
_sliderPos = 0;
_units = Metric;
_graphType = Distance;
_xLabel = tr("Distance");
}
GraphView::~GraphView()
{
if (_xAxis->scene() != _scene)
delete _xAxis;
if (_yAxis->scene() != _scene)
delete _yAxis;
if (_slider->scene() != _scene)
delete _slider;
if (_info->scene() != _scene)
delete _info;
if (_grid->scene() != _scene)
delete _grid;
for (int i = 0; i < _graphs.count(); i++)
if (_graphs.at(i)->scene() != _scene)
delete _graphs[i];
}
void GraphView::createXLabel()
{
_xAxis->setLabel(QString("%1 [%2]").arg(_xLabel).arg(_xUnits));
}
void GraphView::createYLabel()
{
_yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits));
}
void GraphView::setYLabel(const QString &label)
{
_yLabel = label;
createYLabel();
}
void GraphView::setYUnits(const QString &units)
{
_yUnits = units;
createYLabel();
}
void GraphView::setXUnits()
{
if (_graphType == Distance) {
if (_units == Metric) {
if (bounds().width() < KMINM) {
_xUnits = tr("m");
_xScale = 1;
} else {
_xUnits = tr("km");
_xScale = M2KM;
}
} else {
if (bounds().width() < MIINM) {
_xUnits = tr("ft");
_xScale = M2FT;
} else {
_xUnits = tr("mi");
_xScale = M2MI;
}
}
} else {
if (bounds().width() < MININS) {
_xUnits = tr("s");
_xScale = 1;
} else if (bounds().width() < HINS) {
_xUnits = tr("min");
_xScale = MIN2S;
} else {
_xUnits = tr("h");
_xScale = H2S;
}
}
createXLabel();
}
void GraphView::setUnits(Units units)
{
_units = units;
setXUnits();
}
void GraphView::setGraphType(GraphType type)
{
_graphType = type;
_bounds = QRectF();
for (int i = 0; i < _graphs.count(); i++) {
_graphs.at(i)->setGraphType(type);
if (_graphs.at(i)->scene() == _scene)
_bounds |= _graphs.at(i)->bounds();
}
if (type == Distance)
_xLabel = tr("Distance");
else
_xLabel = tr("Time");
setXUnits();
redraw();
}
void GraphView::showGrid(bool show)
{
_grid->setVisible(show);
}
void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
{
if (graph.size() < 2)
return;
GraphItem *gi = new GraphItem(graph);
gi->setGraphType(_graphType);
gi->setId(id);
gi->setColor(_palette.nextColor());
gi->setWidth(_width);
connect(this, SIGNAL(sliderPositionChanged(qreal)), gi,
SLOT(emitSliderPositionChanged(qreal)));
connect(gi, SIGNAL(sliderPositionChanged(qreal)), path,
SLOT(moveMarker(qreal)));
connect(path, SIGNAL(selected(bool)), gi, SLOT(selected(bool)));
_graphs.append(gi);
if (!_hide.contains(id)) {
_visible.append(gi);
_scene->addItem(gi);
_bounds |= gi->bounds();
setXUnits();
}
}
void GraphView::removeItem(QGraphicsItem *item)
{
if (item->scene() == _scene)
_scene->removeItem(item);
}
void GraphView::addItem(QGraphicsItem *item)
{
if (item->scene() != _scene)
_scene->addItem(item);
}
void GraphView::showGraph(bool show, int id)
{
if (show)
_hide.remove(id);
else
_hide.insert(id);
_visible.clear();
_bounds = QRectF();
for (int i = 0; i < _graphs.count(); i++) {
GraphItem* gi = _graphs.at(i);
if (_hide.contains(gi->id()))
removeItem(gi);
else {
addItem(gi);
_visible.append(gi);
_bounds |= gi->bounds();
}
}
}
void GraphView::redraw()
{
redraw(viewport()->size() - QSizeF(MARGIN, MARGIN));
}
QRectF GraphView::bounds() const
{
QRectF br(_bounds);
br.moveTopLeft(QPointF(br.left(), -br.top() - br.height()));
return br;
}
void GraphView::redraw(const QSizeF &size)
{
QRectF r;
QSizeF mx, my;
RangeF rx, ry;
qreal sx, sy;
if (_visible.isEmpty() || _bounds.isNull()) {
removeItem(_xAxis);
removeItem(_yAxis);
removeItem(_slider);
removeItem(_info);
removeItem(_grid);
_scene->setSceneRect(QRectF());
return;
}
addItem(_xAxis);
addItem(_yAxis);
addItem(_slider);
addItem(_info);
addItem(_grid);
rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
+ _yOffset);
if (ry.size() < _minYRange)
ry.resize(_minYRange);
_xAxis->setRange(rx);
_yAxis->setRange(ry);
mx = _xAxis->margin();
my = _yAxis->margin();
r = _bounds;
if (r.height() < _minYRange)
r.adjust(0, -(_minYRange/2 - r.height()/2), 0,
_minYRange/2 - r.height()/2);
sx = (size.width() - (my.width() + mx.width())) / r.width();
sy = (size.height() - (mx.height() + my.height())
- _info->boundingRect().height()) / r.height();
for (int i = 0; i < _visible.size(); i++)
_visible.at(i)->setScale(sx, sy);
QPointF p(r.left() * sx, r.top() * sy);
QSizeF s(r.width() * sx, r.height() * sy);
r = QRectF(p, s);
if (r.height() < _minYRange * sy)
r.adjust(0, -(_minYRange/2 * sy - r.height()/2), 0,
(_minYRange/2) * sy - r.height()/2);
_xAxis->setSize(r.width());
_yAxis->setSize(r.height());
_xAxis->setPos(r.bottomLeft());
_yAxis->setPos(r.bottomLeft());
_grid->setSize(r.size());
_grid->setTicks(_xAxis->ticks(), _yAxis->ticks());
_grid->setPos(r.bottomLeft());
_slider->setArea(r);
updateSliderPosition();
r |= _xAxis->sceneBoundingRect();
r |= _yAxis->sceneBoundingRect();
_info->setPos(r.topLeft() + QPointF(r.width()/2
- _info->boundingRect().width()/2, -_info->boundingRect().height()));
_scene->setSceneRect(_scene->itemsBoundingRect());
}
void GraphView::resizeEvent(QResizeEvent *)
{
redraw();
}
void GraphView::mousePressEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
newSliderPosition(mapToScene(e->pos()));
QGraphicsView::mousePressEvent(e);
}
void GraphView::plot(QPainter *painter, const QRectF &target)
{
qreal ratio = painter->paintEngine()->paintDevice()->logicalDpiX()
/ SCREEN_DPI;
QSizeF canvas = QSizeF(target.width() / ratio, target.height() / ratio);
setUpdatesEnabled(false);
redraw(canvas);
if (_slider->pos().x() == _slider->area().left())
_slider->hide();
_scene->render(painter, target);
_slider->show();
redraw();
setUpdatesEnabled(true);
}
void GraphView::clear()
{
_slider->clear();
_info->clear();
for (int i = 0; i < _graphs.count(); i++)
delete _graphs[i];
_graphs.clear();
_visible.clear();
_palette.reset();
_bounds = QRectF();
_sliderPos = 0;
_scene->setSceneRect(0, 0, 0, 0);
}
void GraphView::updateSliderPosition()
{
if (bounds().width() <= 0)
return;
if (_sliderPos <= bounds().right() && _sliderPos >= bounds().left()) {
_slider->setPos((_sliderPos / bounds().width())
* _slider->area().width(), _slider->area().bottom());
_slider->setVisible(!_visible.isEmpty());
} else {
_slider->setPos(_slider->area().left(), _slider->area().bottom());
_slider->setVisible(false);
}
updateSliderInfo();
}
void GraphView::updateSliderInfo()
{
_sliderInfo->setVisible(_visible.count() == 1);
if (!_sliderInfo->isVisible())
return;
QRectF br(_visible.first()->bounds());
if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2);
qreal y = _visible.first()->yAtX(_sliderPos);
qreal r = (y - br.bottom()) / br.height();
qreal pos = (_sliderPos / bounds().width()) * _slider->area().width();
SliderInfoItem::Side s = (pos + _sliderInfo->boundingRect().width()
> _slider->area().right()) ? SliderInfoItem::Left : SliderInfoItem::Right;
_sliderInfo->setSide(s);
_sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r));
_sliderInfo->setText(QString::number(-y * _yScale + _yOffset, 'f',
_precision));
}
void GraphView::emitSliderPositionChanged(const QPointF &pos)
{
if (_slider->area().width() <= 0)
return;
_sliderPos = (pos.x() / _slider->area().width()) * bounds().width();
_sliderPos = qMax(_sliderPos, bounds().left());
_sliderPos = qMin(_sliderPos, bounds().right());
updateSliderPosition();
emit sliderPositionChanged(_sliderPos);
}
void GraphView::setSliderPosition(qreal pos)
{
if (_visible.isEmpty())
return;
_sliderPos = pos;
updateSliderPosition();
}
void GraphView::newSliderPosition(const QPointF &pos)
{
if (_slider->area().contains(pos))
_slider->setPos(pos);
}
void GraphView::addInfo(const QString &key, const QString &value)
{
_info->insert(key, value);
}
void GraphView::clearInfo()
{
_info->clear();
}
void GraphView::setPalette(const Palette &palette)
{
_palette = palette;
_palette.reset();
for (int i = 0; i < _graphs.count(); i++)
_graphs.at(i)->setColor(_palette.nextColor());
}
void GraphView::setGraphWidth(int width)
{
_width = width;
for (int i = 0; i < _graphs.count(); i++)
_graphs.at(i)->setWidth(width);
}
void GraphView::useOpenGL(bool use)
{
if (use)
setViewport(new OPENGL_WIDGET);
else
setViewport(new QWidget);
}

113
src/graphview.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef GRAPHVIEW_H
#define GRAPHVIEW_H
#include <QGraphicsView>
#include <QList>
#include <QSet>
#include "palette.h"
#include "units.h"
#include "graph.h"
class AxisItem;
class SliderItem;
class SliderInfoItem;
class InfoItem;
class GraphItem;
class PathItem;
class GridItem;
class GraphView : public QGraphicsView
{
Q_OBJECT
public:
GraphView(QWidget *parent = 0);
~GraphView();
void loadGraph(const Graph &graph, PathItem *path, int id = 0);
int count() const {return _graphs.count();}
void redraw();
void clear();
void showGraph(bool show, int id = 0);
void setGraphType(GraphType type);
void setUnits(Units units);
void showGrid(bool show);
void setPalette(const Palette &palette);
void setGraphWidth(int width);
const QString &yLabel() const {return _yLabel;}
const QString &yUnits() const {return _yUnits;}
qreal yScale() const {return _yScale;}
qreal yOffset() const {return _yOffset;}
void setYLabel(const QString &label);
void setYUnits(const QString &units);
void setYScale(qreal scale) {_yScale = scale;}
void setYOffset(qreal offset) {_yOffset = offset;}
void setSliderPrecision(int precision) {_precision = precision;}
void setMinYRange(qreal range) {_minYRange = range;}
qreal sliderPosition() const {return _sliderPos;}
void setSliderPosition(qreal pos);
void plot(QPainter *painter, const QRectF &target);
void useOpenGL(bool use);
signals:
void sliderPositionChanged(qreal);
protected:
QRectF bounds() const;
void redraw(const QSizeF &size);
void addInfo(const QString &key, const QString &value);
void clearInfo();
void skipColor() {_palette.nextColor();}
private slots:
void emitSliderPositionChanged(const QPointF &pos);
void newSliderPosition(const QPointF &pos);
private:
void setXUnits();
void createXLabel();
void createYLabel();
void updateSliderPosition();
void updateSliderInfo();
void removeItem(QGraphicsItem *item);
void addItem(QGraphicsItem *item);
void resizeEvent(QResizeEvent *);
void mousePressEvent(QMouseEvent *);
qreal _xScale, _yScale;
qreal _yOffset;
QString _xUnits, _yUnits;
QString _xLabel, _yLabel;
int _precision;
qreal _minYRange;
qreal _sliderPos;
QGraphicsScene *_scene;
AxisItem *_xAxis, *_yAxis;
SliderItem *_slider;
SliderInfoItem *_sliderInfo;
InfoItem *_info;
GridItem *_grid;
QList<GraphItem*> _graphs;
QList<GraphItem*> _visible;
QSet<int> _hide;
QRectF _bounds;
Palette _palette;
int _width;
Units _units;
GraphType _graphType;
};
#endif // GRAPHVIEW_H

50
src/griditem.cpp Normal file
View File

@ -0,0 +1,50 @@
#include <QPainter>
#include "griditem.h"
#define GRID_WIDTH 0
GridItem::GridItem(QGraphicsItem *parent) : QGraphicsItem(parent)
{
#ifndef Q_OS_MAC
setCacheMode(QGraphicsItem::DeviceCoordinateCache);
#endif // Q_OS_MAC
}
void GridItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
QBrush brush(Qt::gray);
QPen pen = QPen(brush, GRID_WIDTH, Qt::DotLine);
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setPen(pen);
for (int i = 0; i < _xTicks.size(); i++)
painter->drawLine(_xTicks.at(i), 0, _xTicks.at(i),
-_boundingRect.height());
for (int i = 0; i < _yTicks.size(); i++)
painter->drawLine(0, -_yTicks.at(i), boundingRect().width(),
-_yTicks.at(i));
/*
painter->setPen(Qt::red);
painter->drawRect(boundingRect());
*/
}
void GridItem::setTicks(const QList<qreal> &x, const QList<qreal> &y)
{
_xTicks = x; _yTicks = y;
update();
}
void GridItem::setSize(const QSizeF &size)
{
prepareGeometryChange();
_boundingRect = QRectF(QPointF(0, -size.height()), size);
}

23
src/griditem.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef GRIDITEM_H
#define GRIDITEM_H
#include <QGraphicsItem>
class GridItem : public QGraphicsItem
{
public:
GridItem(QGraphicsItem *parent = 0);
QRectF boundingRect() const {return _boundingRect;}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
void setTicks(const QList<qreal> &x, const QList<qreal> &y);
void setSize(const QSizeF &size);
private:
QRectF _boundingRect;
QList<qreal> _xTicks, _yTicks;
};
#endif // GRIDITEM_H

File diff suppressed because it is too large Load Diff

174
src/gui.h
View File

@ -2,21 +2,31 @@
#define GUI_H
#include <QMainWindow>
#include <QMenu>
#include <QToolBar>
#include <QTabWidget>
#include <QGraphicsView>
#include <QActionGroup>
#include <QAction>
#include <QLabel>
#include <QString>
#include <QList>
#include <QDate>
#include <QPrinter>
#include "units.h"
#include "timetype.h"
#include "graph.h"
#include "poi.h"
#include "exportdialog.h"
#include "optionsdialog.h"
class QMenu;
class QToolBar;
class QTabWidget;
class QActionGroup;
class QAction;
class QLabel;
class QSignalMapper;
class QPrinter;
class FileBrowser;
class ElevationGraph;
class SpeedGraph;
class Track;
class GraphTab;
class PathView;
class Map;
class MapList;
class GUI : public QMainWindow
{
@ -24,6 +34,7 @@ class GUI : public QMainWindow
public:
GUI();
~GUI();
bool openFile(const QString &fileName);
@ -31,74 +42,124 @@ private slots:
void about();
void keys();
void dataSources();
void saveFile();
void saveAs();
void printFile();
void exportFile();
void openFile();
void closeFile();
void closeAll();
void reloadFile();
void openPOIFile();
void showPOI(bool checked);
void showMap(bool checked);
void showGraphs(bool checked);
void showToolbars(bool checked);
void closePOIFiles();
void showGraphs(bool show);
void showGraphGrids(bool show);
void showToolbars(bool show);
void showFullscreen(bool show);
void showTracks(bool show);
void showRoutes(bool show);
void loadMap();
void clearMapCache();
void nextMap();
void prevMap();
void openOptions();
void mapChanged(int);
void graphChanged(int);
void poiFileChecked(int);
void next();
void prev();
void last();
void first();
void setMetricUnits();
void setImperialUnits();
void setTotalTime() {setTimeType(Total);}
void setMovingTime() {setTimeType(Moving);}
void setMetricUnits() {setUnits(Metric);}
void setImperialUnits() {setUnits(Imperial);}
void setDistanceGraph() {setGraphType(Distance);}
void setTimeGraph() {setGraphType(Time);}
void sliderPositionChanged(qreal pos);
private:
void loadFiles();
typedef QPair<QDate, QDate> DateRange;
void loadDatums();
void loadMaps();
void loadPOIs();
void closeFiles();
void plot(QPrinter *printer);
QAction *createPOIFileAction(int index);
void createPOIFilesActions();
void createMapActions();
void createActions();
void createMenus();
void createToolBars();
void createStatusBar();
void createTrackView();
void createTrackGraphs();
void createPathView();
void createGraphTabs();
void createBrowser();
bool openPOIFile(const QString &fileName);
bool loadFile(const QString &fileName);
void saveFile(const QString &fileName);
void exportFile(const QString &fileName);
void updateStatusBarInfo();
void updateWindowTitle();
void updateNavigationActions();
void updateGraphTabs();
void updatePathView();
void keyPressEvent(QKeyEvent * event);
TimeType timeType() const;
Units units() const;
void setTimeType(TimeType type);
void setUnits(Units units);
void setGraphType(GraphType type);
QMenu *_fileMenu;
QMenu *_helpMenu;
QMenu *_poiMenu;
QMenu *_mapMenu;
QMenu *_settingsMenu;
QMenu *_unitsMenu;
qreal distance() const;
qreal time() const;
qreal movingTime() const;
int mapIndex(const QString &name);
void readSettings();
void writeSettings();
const QString fileFormats() const;
void keyPressEvent(QKeyEvent *event);
void closeEvent(QCloseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
QToolBar *_fileToolBar;
QToolBar *_showToolBar;
QToolBar *_navigationToolBar;
QTabWidget *_trackGraphs;
QMenu *_poiFilesMenu;
QMenu *_mapMenu;
QActionGroup *_fileActionGroup;
QActionGroup *_navigationActionGroup;
QActionGroup *_mapsActionGroup;
QAction *_exitAction;
QAction *_keysAction;
QAction *_dataSourcesAction;
QAction *_aboutAction;
QAction *_aboutQtAction;
QAction *_saveFileAction;
QAction *_saveAsAction;
QAction *_printFileAction;
QAction *_exportFileAction;
QAction *_openFileAction;
QAction *_closeFileAction;
QAction *_reloadFileAction;
QAction *_openPOIAction;
QAction *_closePOIAction;
QAction *_showPOIAction;
QAction *_overlapPOIAction;
QAction *_showPOILabelsAction;
QAction *_showMapAction;
QAction *_fullscreenAction;
QAction *_loadMapAction;
QAction *_clearMapCacheAction;
QAction *_showGraphsAction;
QAction *_showGraphGridAction;
QAction *_distanceGraphAction;
QAction *_timeGraphAction;
QAction *_showToolbarsAction;
QAction *_nextAction;
QAction *_prevAction;
@ -106,26 +167,55 @@ private:
QAction *_firstAction;
QAction *_metricUnitsAction;
QAction *_imperialUnitsAction;
QAction *_totalTimeAction;
QAction *_movingTimeAction;
QAction *_nextMapAction;
QAction *_prevMapAction;
QAction *_showTracksAction;
QAction *_showRoutesAction;
QAction *_showWaypointsAction;
QAction *_showWaypointLabelsAction;
QAction *_showRouteWaypointsAction;
QAction *_openOptionsAction;
QAction *_mapsEnd;
QList<QAction*> _mapActions;
QList<QAction*> _poiFilesActions;
QSignalMapper *_poiFilesSignalMapper;
QSignalMapper *_mapsSignalMapper;
QLabel *_fileNameLabel;
QLabel *_distanceLabel;
QLabel *_timeLabel;
ElevationGraph *_elevationGraph;
SpeedGraph *_speedGraph;
Track *_track;
PathView *_pathView;
QTabWidget *_graphTabWidget;
QList<GraphTab*> _tabs;
POI _poi;
QList<Map*> _maps;
POI *_poi;
MapList *_ml;
FileBrowser *_browser;
QList<QString> _files;
QString _saveFileName;
Map *_currentMap;
qreal _distance;
Map *_map;
int _trackCount;
int _routeCount;
int _waypointCount;
qreal _trackDistance;
qreal _routeDistance;
qreal _time;
qreal _movingTime;
DateRange _dateRange;
QString _pathName;
qreal _sliderPos;
int _frameStyle;
bool _showGraphs;
Export _export;
Options _options;
};
#endif // GUI_H

84
src/heartrategraph.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "data.h"
#include "heartrategraph.h"
HeartRateGraph::HeartRateGraph(QWidget *parent) : GraphTab(parent)
{
_units = Metric;
_showTracks = true;
GraphView::setYUnits(tr("1/min"));
setYLabel(tr("Heart rate"));
setSliderPrecision(0);
}
void HeartRateGraph::setInfo()
{
if (_showTracks) {
GraphView::addInfo(tr("Average"), QString::number(avg() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), QString::number(max() * yScale(), 'f',
0) + UNIT_SPACE + yUnits());
} else
clearInfo();
}
void HeartRateGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
for (int i = 0; i < data.tracks().count(); i++) {
const Graph &graph = data.tracks().at(i)->heartRate();
qreal sum = 0, w = 0;
if (graph.size() < 2) {
skipColor();
continue;
}
for (int j = 1; j < graph.size(); j++) {
qreal ds = graph.at(j).s() - graph.at(j-1).s();
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(data.tracks().at(i)->distance(), sum/w));
GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < data.routes().count(); i++)
skipColor();
setInfo();
redraw();
}
qreal HeartRateGraph::avg() const
{
qreal sum = 0, w = 0;
QList<QPointF>::const_iterator it;
for (it = _avg.begin(); it != _avg.end(); it++) {
sum += it->y() * it->x();
w += it->x();
}
return (sum / w);
}
void HeartRateGraph::clear()
{
_avg.clear();
GraphView::clear();
}
void HeartRateGraph::showTracks(bool show)
{
_showTracks = show;
showGraph(show);
setInfo();
redraw();
}

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