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

Compare commits

...

243 Commits
2.20 ... 4.6

Author SHA1 Message Date
1a66ed0a36 Use the proper layer when displaying a single waypoint on an atlas. 2017-05-01 22:49:01 +02:00
273a127069 Version++ 2017-05-01 13:04:15 +02:00
1c0a0fd0b3 Fixed broken print/PDF export
(wrong map scale/waypoints size)
2017-05-01 12:59:56 +02:00
713e331b2a Some more OZFx extensions 2017-04-30 18:27:12 +02:00
96f406aad7 Removed Thunderforest map (requires API key)
Added USGS maps (topo & imagery)
2017-04-30 13:21:32 +02:00
0aedec66c4 Fixed atlas ll2xy() caching 2017-04-30 00:19:53 +02:00
b500031713 Atlas ll2xy() optimization 2017-04-29 23:15:44 +02:00
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
175 changed files with 12736 additions and 2255 deletions

View File

@ -35,6 +35,86 @@
<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>
@ -60,6 +140,111 @@
<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>

View File

@ -1,33 +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/heart rate 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.
* Export to PDF.
* Full-screen mode
* 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/linux.png)
![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
* Windows & OS X builds: http://sourceforge.net/projects/gpxsee
* Linux packages: https://build.opensuse.org/project/repositories/home:tumic: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,6 +0,0 @@
CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"
set PATH=C:\qt\qtbase\bin;%PATH%
set PATH=C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin;%PATH%
set INCLUDE=C:\Program Files\Microsoft SDKs\Windows\v7.1\Include;%INCLUDE%
set LIB=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib;%LIB%
set CL=/D_USING_V110_SDK71_

View File

@ -1,6 +0,0 @@
CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
set PATH=C:\qt64\qtbase\bin;%PATH%
set PATH=C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin;%PATH%
set INCLUDE=C:\Program Files\Microsoft SDKs\Windows\v7.1\Include;%INCLUDE%
set LIB=C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\x64;%LIB%
set CL=/D_USING_V110_SDK71_

View File

@ -1,18 +1,17 @@
TARGET = GPXSee
VERSION = 2.20
VERSION = 4.6
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/parser.h \
src/poi.h \
src/rtree.h \
src/ll.h \
src/axisitem.h \
src/keys.h \
src/slideritem.h \
@ -23,7 +22,7 @@ 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/scaleitem.h \
@ -52,13 +51,51 @@ HEADERS += src/config.h \
src/graphitem.h \
src/graph.h \
src/pathitem.h \
src/pathview.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/parser.cpp \
src/poi.cpp \
src/ll.cpp \
src/axisitem.cpp \
src/slideritem.cpp \
src/markeritem.cpp \
@ -67,8 +104,7 @@ SOURCES += src/main.cpp \
src/speedgraph.cpp \
src/sliderinfoitem.cpp \
src/filebrowser.cpp \
src/map.cpp \
src/maplist.cpp \
src/onlinemap.cpp \
src/downloader.cpp \
src/scaleitem.cpp \
src/track.cpp \
@ -91,19 +127,64 @@ SOURCES += src/main.cpp \
src/routeitem.cpp \
src/graphitem.cpp \
src/pathitem.cpp \
src/pathview.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
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 \
pkg/maps.txt
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/gpx.ico \
icons/tcx.ico \
icons/kml.ico \
icons/fit.ico \
icons/igc.ico \
icons/nmea.ico
}
DEFINES += APP_VERSION=\\\"$$VERSION\\\"

View File

@ -15,6 +15,12 @@
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

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: 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

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.

View File

@ -1,10 +1,11 @@
!include "MUI2.nsh"
!include "x64.nsh"
!include "WinVer.nsh"
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "2.20"
!define VERSION "4.6"
; The file to write
OutFile "GPXSee-${VERSION}.exe"
@ -15,14 +16,27 @@ 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"
; GPX file type registry entry
; 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"
@ -50,9 +64,15 @@ Var StartMenuFolder
; 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 (required)" SEC_APP
Section "GPXSee" SEC_APP
SectionIn RO
@ -62,18 +82,8 @@ Section "GPXSee (required)" SEC_APP
; Put the files there
File "gpxsee.exe"
File "maps.txt"
; Write the installation path into the registry
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
WriteUninstaller "$INSTDIR\uninstall.exe"
File "ellipsoids.csv"
File "datums.csv"
; Create start menu entry and add links
SetShellVarContext all
@ -83,23 +93,61 @@ Section "GPXSee (required)" SEC_APP
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GPXSee.lnk" "$INSTDIR\gpxsee.exe"
!insertmacro MUI_STARTMENU_WRITE_END
; Associate .gpx files
; 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 libs" SEC_QT
Section "QT framework" SEC_QT
SectionIn RO
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Widgets.dll"
File "Qt5PrintSupport.dll"
File "Qt5Network.dll"
File "libGLESv2.dll"
File /r "platforms"
File /r "imageformats"
File /r "printsupport"
@ -108,6 +156,8 @@ 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"
@ -123,10 +173,26 @@ Section "MSVC runtime" SEC_MSVC
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
@ -149,6 +215,16 @@ Section "Uninstall"
; 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
@ -159,15 +235,21 @@ SectionEnd
;Language strings
LangString DESC_QT ${LANG_ENGLISH} \
"QT Library. Unselct only if you have QT already installed!"
"QT cross-platform application framework."
LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2015 runtime components. Unselct only if you have the runtime already installed!"
"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
!insertmacro MUI_FUNCTION_DESCRIPTION_END

View File

@ -1,10 +1,11 @@
!include "MUI2.nsh"
!include "x64.nsh"
!include "WinVer.nsh"
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "2.20"
!define VERSION "4.6"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"
@ -15,14 +16,27 @@ 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"
; GPX file type registry entry
!define REGGPX "GPXSee.gpx"
; 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"
@ -50,7 +64,12 @@ Var StartMenuFolder
; Languages
!insertmacro MUI_LANGUAGE "English"
Function .onInit
Function .onInit
${IfNot} ${AtLeastWin7}
MessageBox MB_OK "GPXSee can only be installed on Windows 7 or later."
Abort
${EndIf}
${If} ${RunningX64}
SetRegView 64
${Else}
@ -60,7 +79,7 @@ Function .onInit
FunctionEnd
; The stuff to install
Section "GPXSee (required)" SEC_APP
Section "GPXSee" SEC_APP
SectionIn RO
@ -70,19 +89,9 @@ Section "GPXSee (required)" SEC_APP
; Put the files there
File "gpxsee.exe"
File "maps.txt"
File "ellipsoids.csv"
File "datums.csv"
; Write the installation path into the registry
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
WriteUninstaller "$INSTDIR\uninstall.exe"
; Create start menu entry and add links
SetShellVarContext all
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
@ -91,23 +100,61 @@ Section "GPXSee (required)" SEC_APP
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\GPXSee.lnk" "$INSTDIR\gpxsee.exe"
!insertmacro MUI_STARTMENU_WRITE_END
; Associate .gpx files
; 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 libs" SEC_QT
Section "QT framework" SEC_QT
SectionIn RO
File "Qt5Core.dll"
File "Qt5Gui.dll"
File "Qt5Widgets.dll"
File "Qt5PrintSupport.dll"
File "Qt5Network.dll"
File "libGLESv2.dll"
File /r "platforms"
File /r "imageformats"
File /r "printsupport"
@ -116,6 +163,8 @@ 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
@ -126,10 +175,26 @@ Section "MSVC runtime" SEC_MSVC
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
@ -150,9 +215,19 @@ Section "Uninstall"
Delete "$SMPROGRAMS\$StartMenuFolder\*.*"
RMDir "$SMPROGRAMS\$StartMenuFolder"
; Remove GPX file association
; 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
@ -163,15 +238,21 @@ SectionEnd
;Language strings
LangString DESC_QT ${LANG_ENGLISH} \
"QT Library. Unselct only if you have QT already installed!"
"QT cross-platform application framework."
LangString DESC_MSVC ${LANG_ENGLISH} \
"Visual C++ 2015 runtime components. Unselct only if you have the runtime already installed!"
"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
!insertmacro MUI_FUNCTION_DESCRIPTION_END

View File

@ -1,2 +1,4 @@
Thunderforest http://tile.thunderforest.com/outdoors/$z/$x/$y.png
Open Topo Map https://a.tile.opentopomap.org/$z/$x/$y.png
Open Street Map http://tile.openstreetmap.org/$z/$x/$y.png
USGS Topo https://navigator.er.usgs.gov/tiles/tcr.cgi/$z/$x/$y.png
USGS Imagery https://navigator.er.usgs.gov/tiles/aerial_Imagery.cgi/$z/$x/$y

View File

@ -3,23 +3,30 @@
#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)
{
_translator = new QTranslator();
QTranslator *translator = new QTranslator(this);
QString locale = QLocale::system().name();
_translator->load(QString(":/lang/gpxsee_") + locale);
installTranslator(_translator);
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();
}
@ -27,7 +34,6 @@ App::App(int &argc, char **argv) : QApplication(argc, argv),
App::~App()
{
delete _gui;
delete _translator;
}
void App::run()

View File

@ -4,7 +4,6 @@
#include <QApplication>
class GUI;
class QTranslator;
class App : QApplication
{
@ -22,7 +21,6 @@ private:
int &_argc;
char **_argv;
GUI *_gui;
QTranslator *_translator;
};
#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

320
src/atlas.cpp Normal file
View File

@ -0,0 +1,320 @@
#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();
_ci = -1; _cz = -1;
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;
if (br.isNull()) {
_zoom = _zooms.size() - 1;
return _zoom;
}
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)
{
QPointF pp;
if (_cz != _zoom) {
_ci = -1;
_cz = _zoom;
}
if (_ci >= 0)
pp = _maps.at(_ci)->ll2pp(c);
if (_ci < 0 || !_bounds.at(_ci).first.contains(pp)) {
_ci = _zooms.at(_zoom).first;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
pp = _maps.at(i)->ll2pp(c);
if (_bounds.at(i).first.contains(pp)) {
_ci = i;
break;
}
}
}
QPointF p = _maps.at(_ci)->pp2xy(pp);
return p + _bounds.at(_ci).second.topLeft();
}
Coordinates Atlas::xy2ll(const QPointF &p)
{
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();
}

55
src/atlas.h Normal file
View File

@ -0,0 +1,55 @@
#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);
Coordinates xy2ll(const QPointF &p);
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;
int _ci, _cz;
};
#endif // ATLAS_H

View File

@ -118,6 +118,7 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QPen pen = QPen(Qt::black, AXIS_WIDTH);
painter->setRenderHint(QPainter::Antialiasing, false);
painter->setFont(font);
painter->setPen(pen);
@ -202,3 +203,16 @@ QSizeF AxisItem::margin() const
+ 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

@ -20,6 +20,7 @@ public:
void setLabel(const QString& label);
QSizeF margin() const;
QList<qreal> ticks() const;
private:
void updateBoundingRect();

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

@ -13,7 +13,10 @@
#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)
@ -28,10 +31,16 @@
#define GLOBAL_DIR QString("/usr/share/gpxsee")
#endif
#define USER_MAP_FILE USER_DIR + QString("/") + MAP_FILE
#define USER_POI_DIR USER_DIR + QString("/") + POI_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")
#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

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;
}

View File

@ -1,8 +1,9 @@
#ifndef GPX_H
#define GPX_H
#ifndef DATA_H
#define DATA_H
#include <QVector>
#include <QList>
#include <QHash>
#include <QPointF>
#include <QString>
#include "waypoint.h"
@ -10,31 +11,34 @@
#include "route.h"
#include "parser.h"
class GPX
class Data
{
public:
GPX();
~GPX();
Data();
~Data();
bool loadFile(const QString &fileName);
const QString &errorString() const {return _error;}
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 _waypoint_data;}
const QList<Waypoint> &waypoints() const {return _waypointData;}
private:
Parser _parser;
QString _error;
void createData();
QString _errorString;
int _errorLine;
QHash<QString, Parser*> _parsers;
QList<Track*> _tracks;
QList<Route*> _routes;
QList<QVector<Trackpoint> > _track_data;
QList<QVector<Waypoint> > _route_data;
QList<Waypoint> _waypoint_data;
QList<TrackData> _trackData;
QList<RouteData> _routeData;
QList<Waypoint> _waypointData;
};
#endif // GPX_H
#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"
@ -17,25 +19,39 @@
#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*)));
}
bool Downloader::doDownload(const Download &dl)
bool Downloader::doDownload(const Download &dl, const Redirect &redirect)
{
QUrl url(dl.url());
if (_errorDownloads.contains(url))
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;
}
@ -45,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;
}
@ -58,26 +74,49 @@ 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()) {
_errorDownloads.insert(url);
fprintf(stderr, "Error downloading map tile: %s: %s\n",
url.toEncoded().constData(), qPrintable(reply->errorString()));
QUrl origin = reply->request().attribute(ATTR_ORIGIN).toUrl();
if (origin.isEmpty()) {
_errorDownloads.insert(url);
qWarning("Error downloading map tile: %s: %s\n",
url.toEncoded().constData(), qPrintable(reply->errorString()));
} else {
_errorDownloads.insert(origin);
qWarning("Error downloading map tile: %s -> %s: %s\n",
origin.toEncoded().constData(), url.toEncoded().constData(),
qPrintable(reply->errorString()));
}
} else {
QUrl redirect = reply->attribute(
QNetworkRequest::RedirectionTargetAttribute).toUrl();
QString filename = reply->request().attribute(QNetworkRequest::User)
QUrl location = reply->attribute(ATTR_REDIRECT).toUrl();
QString filename = reply->request().attribute(ATTR_FILE)
.toString();
if (!redirect.isEmpty()) {
Download dl(redirect, filename);
doDownload(dl);
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())

View File

@ -2,13 +2,14 @@
#define DOWNLOADER_H
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QList>
#include <QMap>
#include <QSet>
class QNetworkReply;
class Download
{
public:
@ -28,8 +29,8 @@ class Downloader : public QObject
Q_OBJECT
public:
static Downloader& instance()
{static Downloader i; return i;}
Downloader(QObject *parent = 0);
bool get(const QList<Download> &list);
signals:
@ -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) {}
bool 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,6 +1,6 @@
#include <cmath>
#include "config.h"
#include "gpx.h"
#include "data.h"
#include "elevationgraph.h"
@ -107,14 +107,14 @@ void ElevationGraph::loadGraph(const Graph &graph, Type type, PathItem *path)
GraphView::loadGraph(graph, path, type);
}
void ElevationGraph::loadGPX(const GPX &gpx, const QList<PathItem *> &paths)
void ElevationGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
int p = 0;
for (int i = 0; i < gpx.tracks().count(); i++)
loadGraph(gpx.tracks().at(i)->elevation(), Track, paths.at(p++));
for (int i = 0; i < gpx.routes().count(); i++)
loadGraph(gpx.routes().at(i)->elevation(), Route, paths.at(p++));
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();

View File

@ -11,7 +11,7 @@ public:
ElevationGraph(QWidget *parent = 0);
QString label() const {return tr("Elevation");}
void loadGPX(const GPX &gpx, const QList<PathItem *> &paths);
void loadData(const Data &data, const QList<PathItem *> &paths);
void clear();
void setUnits(enum Units units);
void showTracks(bool show);

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)
{
QPointF m = Mercator().ll2xy(c);
return QPointF(m.x() / _scale, m.y() / -_scale);
}
Coordinates EmptyMap::xy2ll(const QPointF &p)
{
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);
Coordinates xy2ll(const QPointF &p);
void draw(QPainter *painter, const QRectF &rect);
private:
QString _name;
qreal _scale;
};
#endif // EMPTYMAP_H

View File

@ -11,23 +11,19 @@
#include <QMessageBox>
#include <QTabWidget>
#include <QDoubleSpinBox>
#include <QLocale>
#include "fileselectwidget.h"
#include "units.h"
#include "exportdialog.h"
ExportDialog::ExportDialog(QPrinter *printer, QWidget *parent)
: QDialog(parent), _printer(printer)
ExportDialog::ExportDialog(Export *exp, QWidget *parent)
: QDialog(parent), _export(exp)
{
int index;
_units = (QLocale::system().measurementSystem()
== QLocale::ImperialSystem) ? QPrinter::Inch : QPrinter::Millimeter;
_fileSelect = new FileSelectWidget();
_fileSelect->setFilter(tr("PDF files (*.pdf);;All files (*)"));
_fileSelect->setFile(_printer->outputFileName());
_fileSelect->setFile(_export->fileName);
_paperSize = new QComboBox();
_paperSize->addItem("A3", QPrinter::A3);
@ -36,7 +32,7 @@ ExportDialog::ExportDialog(QPrinter *printer, QWidget *parent)
_paperSize->addItem("Tabloid", QPrinter::Tabloid);
_paperSize->addItem("Legal", QPrinter::Legal);
_paperSize->addItem("Letter", QPrinter::Letter);
if ((index = _paperSize->findData(_printer->paperSize())) >= 0)
if ((index = _paperSize->findData(_export->paperSize)) >= 0)
_paperSize->setCurrentIndex(index);
_portrait = new QRadioButton(tr("Portrait"));
@ -44,32 +40,34 @@ ExportDialog::ExportDialog(QPrinter *printer, QWidget *parent)
QHBoxLayout *orientationLayout = new QHBoxLayout();
orientationLayout->addWidget(_portrait);
orientationLayout->addWidget(_landscape);
if (_printer->orientation() == QPrinter::Portrait)
if (_export->orientation == QPrinter::Portrait)
_portrait->setChecked(true);
else
_landscape->setChecked(true);
qreal top, bottom, left, right;
_printer->getPageMargins(&left, &top, &right, &bottom, _units);
QString us = _units == QPrinter::Inch ? tr("in") : tr("mm");
_topMargin = new QDoubleSpinBox();
_bottomMargin = new QDoubleSpinBox();
_leftMargin = new QDoubleSpinBox();
_rightMargin = new QDoubleSpinBox();
_topMargin->setValue(top);
QString us = (_export->units == Imperial) ? tr("in") : tr("mm");
_topMargin->setSuffix(UNIT_SPACE + us);
_bottomMargin->setValue(bottom);
_bottomMargin->setSuffix(UNIT_SPACE + us);
_leftMargin->setValue(left);
_leftMargin->setSuffix(UNIT_SPACE + us);
_rightMargin->setValue(right);
_rightMargin->setSuffix(UNIT_SPACE + us);
if (_units == QPrinter::Inch) {
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();
@ -164,12 +162,16 @@ void ExportDialog::accept()
QPrinter::PaperSize paperSize = static_cast<QPrinter::PaperSize>
(_paperSize->itemData(_paperSize->currentIndex()).toInt());
_printer->setOutputFormat(QPrinter::PdfFormat);
_printer->setOutputFileName(_fileSelect->file());
_printer->setPaperSize(paperSize);
_printer->setOrientation(orientation);
_printer->setPageMargins(_leftMargin->value(), _topMargin->value(),
_rightMargin->value(), _bottomMargin->value(), _units);
_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();
}

View File

@ -3,18 +3,29 @@
#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(QPrinter *printer, QWidget *parent = 0);
ExportDialog(Export *exp, QWidget *parent = 0);
public slots:
void accept();
@ -22,9 +33,7 @@ public slots:
private:
bool checkFile();
QPrinter *_printer;
QPrinter::Unit _units;
Export *_export;
FileSelectWidget *_fileSelect;
QComboBox *_paperSize;

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,47 +0,0 @@
#include <QFile>
#include <QLineF>
#include "ll.h"
#include "gpx.h"
GPX::GPX() : _parser(_track_data, _route_data, _waypoint_data), _errorLine(0)
{
}
GPX::~GPX()
{
for (int i = 0; i < _tracks.count(); i++)
delete _tracks.at(i);
for (int i = 0; i < _routes.count(); i++)
delete _routes.at(i);
}
bool GPX::loadFile(const QString &fileName)
{
bool ret;
QFile file(fileName);
_error.clear();
_errorLine = 0;
if (!file.open(QFile::ReadOnly | QFile::Text)) {
_error = qPrintable(file.errorString());
return false;
}
ret = _parser.loadFile(&file);
file.close();
if (ret == false) {
_error = _parser.errorString();
_errorLine = _parser.errorLine();
return false;
}
for (int i = 0; i < _track_data.count(); i++)
_tracks.append(new Track(_track_data.at(i)));
for (int i = 0; i < _route_data.count(); i++)
_routes.append(new Route(_route_data.at(i)));
return true;
}

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

9
src/graph.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "graph.h"
QDebug operator<<(QDebug dbg, const GraphPoint &point)
{
dbg.nospace() << "GraphPoint(" << point.s() << ", " << point.t() << ", "
<< point.y() << ")";
return dbg.maybeSpace();
}

View File

@ -2,6 +2,7 @@
#define GRAPH_H
#include <QVector>
#include <QDebug>
#include <cmath>
enum GraphType {Distance, Time};
@ -27,6 +28,25 @@ private:
qreal _y;
};
typedef QVector<GraphPoint> Graph;
Q_DECLARE_TYPEINFO(GraphPoint, Q_PRIMITIVE_TYPE);
QDebug operator<<(QDebug dbg, const GraphPoint &point);
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

View File

@ -2,29 +2,19 @@
#include "graphitem.h"
#define GRAPH_WIDTH 1
#define HOVER_WIDTH 2
static bool hasTime(const Graph &graph)
{
for (int i = 0; i < graph.count(); i++)
if (std::isnan(graph.at(i).t()))
return false;
return true;
}
GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
: QGraphicsObject(parent)
{
_id = 0;
_width = 1;
_pen = QPen(Qt::black, GRAPH_WIDTH);
_pen = QPen(Qt::black, _width);
_type = Distance;
_graph = graph;
_sx = 1.0; _sy = 1.0;
_time = hasTime(_graph);
setZValue(1.0);
updatePath();
updateBounds();
@ -61,6 +51,14 @@ void GraphItem::setColor(const QColor &color)
update();
}
void GraphItem::setWidth(int width)
{
prepareGeometryChange();
_width = width;
_pen.setWidth(width);
}
qreal GraphItem::yAtX(qreal x)
{
int low = 0;
@ -126,13 +124,13 @@ qreal GraphItem::distanceAtTime(qreal time)
void GraphItem::emitSliderPositionChanged(qreal pos)
{
if (_type == Time) {
if (_time) {
if (pos <= _graph.last().t())
if (_graph.hasTime()) {
if (pos >= _graph.first().t() && pos <= _graph.last().t())
emit sliderPositionChanged(distanceAtTime(pos));
else
emit sliderPositionChanged(_graph.last().s() + 1);
emit sliderPositionChanged(NAN);
} else
emit sliderPositionChanged(_graph.last().s() + 1);
emit sliderPositionChanged(NAN);
} else
emit sliderPositionChanged(pos);
}
@ -140,10 +138,10 @@ void GraphItem::emitSliderPositionChanged(qreal pos)
void GraphItem::selected(bool selected)
{
if (selected) {
_pen.setWidth(HOVER_WIDTH);
_pen.setWidth(_width + 1);
setZValue(zValue() + 1.0);
} else {
_pen.setWidth(GRAPH_WIDTH);
_pen.setWidth(_width);
setZValue(zValue() - 1.0);
}
@ -165,7 +163,7 @@ void GraphItem::updatePath()
{
_path = QPainterPath();
if (_type == Time && !_time)
if (_type == Time && !_graph.hasTime())
return;
_path.moveTo(_graph.first().x(_type) * _sx, -_graph.first().y() * _sy);
@ -175,7 +173,7 @@ void GraphItem::updatePath()
void GraphItem::updateBounds()
{
if (_type == Time && !_time) {
if (_type == Time && !_graph.hasTime()) {
_bounds = QRectF();
return;
}

View File

@ -24,6 +24,7 @@ public:
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);
@ -41,10 +42,10 @@ private:
int _id;
QPen _pen;
int _width;
Graph _graph;
GraphType _type;
bool _time;
QPainterPath _path;
QRectF _bounds;

View File

@ -4,8 +4,9 @@
#include <QList>
#include "graphview.h"
#include "units.h"
#include "timetype.h"
class GPX;
class Data;
class PathItem;
class GraphTab : public GraphView
@ -17,11 +18,12 @@ public:
{setFrameShape(QFrame::NoFrame);}
virtual QString label() const = 0;
virtual void loadGPX(const GPX &gpx, const QList<PathItem *> &paths) = 0;
virtual void clear() = 0;
virtual void setUnits(enum Units units) = 0;
virtual void showTracks(bool show) = 0;
virtual void showRoutes(bool show) = 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

View File

@ -1,12 +1,15 @@
#include <QGraphicsSceneMouseEvent>
#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"
@ -15,39 +18,33 @@
#define MARGIN 10.0
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
emit mouseClicked(e->scenePos());
QGraphicsScene::mousePressEvent(e);
}
GraphView::GraphView(QWidget *parent)
: QGraphicsView(parent)
{
_scene = new Scene(this);
_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(1.0);
_xAxis->setZValue(2.0);
_yAxis = new AxisItem(AxisItem::Y);
_yAxis->setZValue(1.0);
_yAxis->setZValue(2.0);
_slider = new SliderItem();
_slider->setZValue(2.0);
_slider->setZValue(3.0);
_sliderInfo = new SliderInfoItem(_slider);
_sliderInfo->setZValue(2.0);
_sliderInfo->setZValue(3.0);
_info = new InfoItem();
_grid = new GridItem();
connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
SLOT(emitSliderPositionChanged(const QPointF&)));
connect(_scene, SIGNAL(mouseClicked(const QPointF&)), this,
SLOT(newSliderPosition(const QPointF&)));
_width = 1;
_xScale = 1;
_yScale = 1;
@ -60,9 +57,7 @@ GraphView::GraphView(QWidget *parent)
_units = Metric;
_graphType = Distance;
setGraphType(_graphType);
setUnits(_units);
_xLabel = tr("Distance");
}
GraphView::~GraphView()
@ -75,6 +70,8 @@ GraphView::~GraphView()
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)
@ -165,6 +162,11 @@ void GraphView::setGraphType(GraphType type)
redraw();
}
void GraphView::showGrid(bool show)
{
_grid->setVisible(show);
}
void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
{
if (graph.size() < 2)
@ -173,7 +175,8 @@ void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
GraphItem *gi = new GraphItem(graph);
gi->setGraphType(_graphType);
gi->setId(id);
gi->setColor(_palette.color());
gi->setColor(_palette.nextColor());
gi->setWidth(_width);
connect(this, SIGNAL(sliderPositionChanged(qreal)), gi,
SLOT(emitSliderPositionChanged(qreal)));
@ -249,6 +252,7 @@ void GraphView::redraw(const QSizeF &size)
removeItem(_yAxis);
removeItem(_slider);
removeItem(_info);
removeItem(_grid);
_scene->setSceneRect(QRectF());
return;
}
@ -257,6 +261,7 @@ void GraphView::redraw(const QSizeF &size)
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
@ -293,6 +298,10 @@ void GraphView::redraw(const QSizeF &size)
_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();
@ -309,6 +318,14 @@ 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()
@ -390,6 +407,8 @@ void GraphView::emitSliderPositionChanged(const QPointF &pos)
return;
_sliderPos = (pos.x() / _slider->area().width()) * bounds().width();
_sliderPos = qMax(_sliderPos, bounds().left());
_sliderPos = qMin(_sliderPos, bounds().right());
updateSliderPosition();
emit sliderPositionChanged(_sliderPos);
@ -419,3 +438,28 @@ 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);
}

View File

@ -2,11 +2,8 @@
#define GRAPHVIEW_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QVector>
#include <QList>
#include <QSet>
#include <QPointF>
#include "palette.h"
#include "units.h"
#include "graph.h"
@ -18,18 +15,7 @@ class SliderInfoItem;
class InfoItem;
class GraphItem;
class PathItem;
class Scene : public QGraphicsScene
{
Q_OBJECT
public:
Scene(QObject *parent = 0) : QGraphicsScene(parent) {}
void mousePressEvent(QGraphicsSceneMouseEvent *e);
signals:
void mouseClicked(const QPointF &pos);
};
class GridItem;
class GraphView : public QGraphicsView
{
@ -47,6 +33,10 @@ public:
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;}
@ -65,6 +55,8 @@ public:
void plot(QPainter *painter, const QRectF &target);
void useOpenGL(bool use);
signals:
void sliderPositionChanged(qreal);
@ -73,8 +65,7 @@ protected:
void redraw(const QSizeF &size);
void addInfo(const QString &key, const QString &value);
void clearInfo();
void skipColor() {_palette.color();}
void resizeEvent(QResizeEvent *);
void skipColor() {_palette.nextColor();}
private slots:
void emitSliderPositionChanged(const QPointF &pos);
@ -89,6 +80,9 @@ private:
void removeItem(QGraphicsItem *item);
void addItem(QGraphicsItem *item);
void resizeEvent(QResizeEvent *);
void mousePressEvent(QMouseEvent *);
qreal _xScale, _yScale;
qreal _yOffset;
QString _xUnits, _yUnits;
@ -97,18 +91,20 @@ private:
qreal _minYRange;
qreal _sliderPos;
Scene *_scene;
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;

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

View File

@ -7,9 +7,12 @@
#include <QDate>
#include <QPrinter>
#include "units.h"
#include "timetype.h"
#include "graph.h"
#include "poi.h"
#include "margins.h"
#include "exportdialog.h"
#include "optionsdialog.h"
class QMenu;
class QToolBar;
@ -23,13 +26,14 @@ class FileBrowser;
class GraphTab;
class PathView;
class Map;
class MapList;
class GUI : public QMainWindow
{
Q_OBJECT
public:
GUI(QWidget *parent = 0);
GUI();
~GUI();
bool openFile(const QString &fileName);
@ -45,15 +49,17 @@ private slots:
void reloadFile();
void openPOIFile();
void closePOIFiles();
void showMap(bool checked);
void showGraphs(bool checked);
void showToolbars(bool checked);
void showFullscreen(bool checked);
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);
@ -64,6 +70,8 @@ private slots:
void last();
void first();
void setTotalTime() {setTimeType(Total);}
void setMovingTime() {setTimeType(Moving);}
void setMetricUnits() {setUnits(Metric);}
void setImperialUnits() {setUnits(Imperial);}
void setDistanceGraph() {setGraphType(Distance);}
@ -74,6 +82,7 @@ private slots:
private:
typedef QPair<QDate, QDate> DateRange;
void loadDatums();
void loadMaps();
void loadPOIs();
void closeFiles();
@ -88,6 +97,7 @@ private:
void createStatusBar();
void createPathView();
void createGraphTabs();
void createBrowser();
bool openPOIFile(const QString &fileName);
bool loadFile(const QString &fileName);
@ -96,17 +106,23 @@ private:
void updateWindowTitle();
void updateNavigationActions();
void updateGraphTabs();
void updateTrackView();
void updatePathView();
TimeType timeType() const;
Units units() const;
void setTimeType(TimeType type);
void setUnits(Units units);
void setGraphType(GraphType type);
qreal distance();
qreal time();
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);
@ -116,9 +132,11 @@ private:
QToolBar *_showToolBar;
QToolBar *_navigationToolBar;
QMenu *_poiFilesMenu;
QMenu *_mapMenu;
QActionGroup *_fileActionGroup;
QActionGroup *_navigationActionGroup;
QActionGroup *_mapsActionGroup;
QAction *_exitAction;
QAction *_keysAction;
QAction *_dataSourcesAction;
@ -136,8 +154,10 @@ private:
QAction *_showPOILabelsAction;
QAction *_showMapAction;
QAction *_fullscreenAction;
QAction *_loadMapAction;
QAction *_clearMapCacheAction;
QAction *_showGraphsAction;
QAction *_showGraphGridAction;
QAction *_distanceGraphAction;
QAction *_timeGraphAction;
QAction *_showToolbarsAction;
@ -147,6 +167,8 @@ private:
QAction *_firstAction;
QAction *_metricUnitsAction;
QAction *_imperialUnitsAction;
QAction *_totalTimeAction;
QAction *_movingTimeAction;
QAction *_nextMapAction;
QAction *_prevMapAction;
QAction *_showTracksAction;
@ -154,10 +176,13 @@ private:
QAction *_showWaypointsAction;
QAction *_showWaypointLabelsAction;
QAction *_showRouteWaypointsAction;
QAction *_openOptionsAction;
QAction *_mapsEnd;
QList<QAction*> _mapActions;
QList<QAction*> _poiFilesActions;
QSignalMapper *_poiFilesSM;
QSignalMapper *_poiFilesSignalMapper;
QSignalMapper *_mapsSignalMapper;
QLabel *_fileNameLabel;
QLabel *_distanceLabel;
@ -168,29 +193,29 @@ private:
QList<GraphTab*> _tabs;
POI *_poi;
QList<Map*> _maps;
MapList *_ml;
FileBrowser *_browser;
QList<QString> _files;
Map *_currentMap;
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;
QString _exportFileName;
QPrinter::PaperSize _exportPaperSize;
QPrinter::Orientation _exportOrientation;
MarginsF _exportMargins;
Export _export;
Options _options;
};
#endif // GUI_H

View File

@ -1,4 +1,4 @@
#include "gpx.h"
#include "data.h"
#include "heartrategraph.h"
@ -24,10 +24,10 @@ void HeartRateGraph::setInfo()
clearInfo();
}
void HeartRateGraph::loadGPX(const GPX &gpx, const QList<PathItem *> &paths)
void HeartRateGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
for (int i = 0; i < gpx.tracks().count(); i++) {
const Graph &graph = gpx.tracks().at(i)->heartRate();
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) {
@ -40,12 +40,12 @@ void HeartRateGraph::loadGPX(const GPX &gpx, const QList<PathItem *> &paths)
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(gpx.tracks().at(i)->distance(), sum/w));
_avg.append(QPointF(data.tracks().at(i)->distance(), sum/w));
GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < gpx.routes().count(); i++)
for (int i = 0; i < data.routes().count(); i++)
skipColor();
setInfo();

View File

@ -11,11 +11,9 @@ public:
HeartRateGraph(QWidget *parent = 0);
QString label() const {return tr("Heart rate");}
void loadGPX(const GPX &gpx, const QList<PathItem *> &paths);
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;

View File

@ -1,20 +1,28 @@
#ifndef ICONS_H
#define ICONS_H
#define APP_ICON ":/icons/gpxsee.png"
#define OPEN_FILE_ICON ":/icons/document-open.png"
#define EXPORT_FILE_ICON ":/icons/document-export.png"
#define PRINT_FILE_ICON ":/icons/document-print.png"
#define CLOSE_FILE_ICON ":/icons/dialog-close.png"
#define SHOW_POI_ICON ":/icons/flag.png"
#define SHOW_MAP_ICON ":/icons/applications-internet.png"
#define SHOW_GRAPHS_ICON ":/icons/office-chart-line-stacked.png"
#define QUIT_ICON ":/icons/application-exit.png"
#define RELOAD_FILE_ICON ":/icons/view-refresh.png"
#define NEXT_FILE_ICON ":/icons/arrow-right.png"
#define PREV_FILE_ICON ":/icons/arrow-left.png"
#define LAST_FILE_ICON ":/icons/arrow-right-double.png"
#define FIRST_FILE_ICON ":/icons/arrow-left-double.png"
#define FULLSCREEN_ICON ":/icons/view-fullscreen.png"
#define APP_ICON ":/icons/gpxsee.png"
// Toolbar/menu icons
#define OPEN_FILE_ICON ":/icons/document-open.png"
#define EXPORT_FILE_ICON ":/icons/document-export.png"
#define PRINT_FILE_ICON ":/icons/document-print.png"
#define CLOSE_FILE_ICON ":/icons/dialog-close.png"
#define SHOW_POI_ICON ":/icons/flag.png"
#define SHOW_MAP_ICON ":/icons/applications-internet.png"
#define SHOW_GRAPHS_ICON ":/icons/office-chart-line-stacked.png"
#define QUIT_ICON ":/icons/application-exit.png"
#define RELOAD_FILE_ICON ":/icons/view-refresh.png"
#define NEXT_FILE_ICON ":/icons/arrow-right.png"
#define PREV_FILE_ICON ":/icons/arrow-left.png"
#define LAST_FILE_ICON ":/icons/arrow-right-double.png"
#define FIRST_FILE_ICON ":/icons/arrow-left-double.png"
#define FULLSCREEN_ICON ":/icons/view-fullscreen.png"
// Options dialog icons
#define APPEARANCE_ICON ":/icons/preferences-desktop-display.png"
#define POI_ICON ":/icons/flag_48.png"
#define SYSTEM_ICON ":/icons/system-run.png"
#define PRINT_EXPORT_ICON ":/icons/document-print-preview.png"
#endif /* ICONS_H */

249
src/igcparser.cpp Normal file
View File

@ -0,0 +1,249 @@
#include <cstring>
#include "misc.h"
#include "igcparser.h"
static bool readLat(const char *data, qreal &lat)
{
int d = str2int(data, 2);
int mi = str2int(data + 2, 2);
int mf = str2int(data + 4, 3);
if (d < 0 || mi < 0 || mf < 0)
return false;
if (!(data[7] == 'N' || data[7] == 'S'))
return false;
lat = d + (((qreal)mi + (qreal)mf/1000) / 60);
if (lat > 90)
return false;
if (data[7] == 'S')
lat = -lat;
return true;
}
static bool readLon(const char *data, qreal &lon)
{
int d = str2int(data, 3);
int mi = str2int(data + 3, 2);
int mf = str2int(data + 5, 3);
if (d < 0 || mi < 0 || mf < 0)
return false;
if (!(data[8] == 'E' || data[8] == 'W'))
return false;
lon = d + (((qreal)mi + (qreal)mf/1000) / 60);
if (lon > 180)
return false;
if (data[8] == 'W')
lon = -lon;
return true;
}
static bool readAltitude(const char *data, qreal &ele)
{
int p;
if (!(data[0] == 'A' || data[0] == 'V'))
return false;
if (data[1] == '-')
p = str2int(data + 2, 4);
else
p = str2int(data + 1, 5);
int g = str2int(data + 6, 5);
if (p < 0 || g < 0)
return false;
if (data[0] == 'A')
ele = (qreal)g;
else
ele = NAN;
return true;
}
static bool readTimestamp(const char *data, QTime &time)
{
int h = str2int(data, 2);
int m = str2int(data + 2, 2);
int s = str2int(data + 4, 2);
if (h < 0 || m < 0 || s < 0)
return false;
time = QTime(h, m, s);
if (!time.isValid())
return false;
return true;
}
static bool readARecord(const char *line, qint64 len)
{
if (len < 9 || line[0] != 'A')
return false;
for (int i = 1; i < 7; i++)
if (!::isprint(line[i]))
return false;
return true;
}
bool IGCParser::readHRecord(const char *line, int len)
{
if (len < 10 || ::strncmp(line, "HFDTE", 5))
return true;
int d = str2int(line + 5, 2);
int m = str2int(line + 7, 2);
int y = str2int(line + 9, 2);
if (y < 0 || m < 0 || d < 0) {
_errorString = "Invalid date header format";
return false;
}
_date = QDate(y + 2000 < QDate::currentDate().year() ? 2000 + y : 1900 + y,
m, d);
if (!_date.isValid()) {
_errorString = "Invalid date";
return false;
}
return true;
}
bool IGCParser::readBRecord(const char *line, int len)
{
qreal lat, lon, ele;
QTime time;
if (len < 35)
return false;
if (!readTimestamp(line + 1, time)) {
_errorString = "Invalid timestamp";
return false;
}
if (!readLat(line + 7, lat)) {
_errorString = "Invalid latitude";
return false;
}
if (!readLon(line + 15, lon)) {
_errorString = "Invalid longitude";
return false;
}
if (!readAltitude(line + 24, ele)) {
_errorString = "Invalid altitude";
return false;
}
if (time < _time)
_date = _date.addDays(1);
_time = time;
Trackpoint t(Coordinates(lon, lat));
t.setTimestamp(QDateTime(_date, _time, Qt::UTC));
t.setElevation(ele);
_tracks.last().append(t);
return true;
}
bool IGCParser::readCRecord(const char *line, int len)
{
qreal lat, lon;
if (len < 18)
return false;
if (!readLat(line + 1, lat)) {
_errorString = "Invalid latitude";
return false;
}
if (!readLon(line + 9, lon)) {
_errorString = "Invalid longitude";
return false;
}
if (!(lat == 0 && lon == 0)) {
QByteArray ba(line + 18, len - 19);
Waypoint w(Coordinates(lon, lat));
w.setName(QString(ba.trimmed()));
_routes.last().append(w);
}
return true;
}
bool IGCParser::loadFile(QFile *file)
{
qint64 len;
char line[76 + 2 + 1 + 1];
bool route = false, track = false;
_errorLine = 1;
_errorString.clear();
while (!file->atEnd()) {
len = file->readLine(line, sizeof(line));
if (len < 0) {
_errorString = "I/O error";
return false;
} else if (len > (qint64)sizeof(line) - 1) {
_errorString = "Line limit exceeded";
return false;
}
if (_errorLine == 1) {
if (!readARecord(line, len)) {
_errorString = "Invalid/missing A record";
return false;
}
} else {
if (line[0] == 'H') {
if (!readHRecord(line, len))
return false;
} else if (line[0] == 'C') {
if (route) {
if (!readCRecord(line, len))
return false;
} else {
route = true;
_routes.append(RouteData());
}
} else if (line[0] == 'B') {
if (_date.isNull()) {
_errorString = "Missing date header";
return false;
}
if (!track) {
_tracks.append(TrackData());
_time = QTime(0, 0);
track = true;
}
if (!readBRecord(line, len))
return false;
}
}
_errorLine++;
}
return true;
}

33
src/igcparser.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef IGCPARSER_H
#define IGCPARSER_H
#include <QDate>
#include <QTime>
#include "parser.h"
class IGCParser : public Parser
{
public:
IGCParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
{_errorLine = 0;}
~IGCParser() {}
bool loadFile(QFile *file);
QString errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
private:
bool readHRecord(const char *line, int len);
bool readBRecord(const char *line, int len);
bool readCRecord(const char *line, int len);
int _errorLine;
QString _errorString;
QDate _date;
QTime _time;
};
#endif // IGCPARSER_H

View File

@ -7,7 +7,9 @@
InfoItem::InfoItem(QGraphicsItem *parent) : QGraphicsItem(parent)
{
#ifndef Q_OS_MAC
setCacheMode(QGraphicsItem::DeviceCoordinateCache);
#endif // Q_OS_MAC
}
void InfoItem::updateBoundingRect()
@ -40,6 +42,9 @@ void InfoItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QList<KV>::const_iterator i;
int width = 0;
painter->setRenderHint(QPainter::Antialiasing, false);
for (i = _list.constBegin(); i != _list.constEnd(); i++) {
painter->drawText(width, fm.height() - fm.descent(), i->key + ": ");
width += fm.width(i->key + ": ");
@ -73,6 +78,7 @@ void InfoItem::insert(const QString &key, const QString &value)
_list[i] = kv;
updateBoundingRect();
update();
}
void InfoItem::clear()

View File

@ -10,18 +10,22 @@
#define LAST_KEY Qt::Key_End
#define MODIFIER Qt::ShiftModifier
#define QUIT_SHORTCUT QKeySequence::Quit
#define OPEN_SHORTCUT QKeySequence::Open
#define CLOSE_SHORTCUT QKeySequence::Close
#define RELOAD_SHORTCUT QKeySequence::Refresh
#define ZOOM_IN QKeySequence::ZoomIn
#define ZOOM_OUT QKeySequence::ZoomOut
#define QUIT_SHORTCUT QKeySequence(QKeySequence::Quit)
#define OPEN_SHORTCUT QKeySequence(QKeySequence::Open)
#define CLOSE_SHORTCUT QKeySequence(QKeySequence::Close)
#define RELOAD_SHORTCUT QKeySequence(QKeySequence::Refresh)
#define EXPORT_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_E)
#define SHOW_POI_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_P)
#define SHOW_MAP_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_M)
#define NEXT_MAP_SHORTCUT QKeySequence::Forward
#define PREV_MAP_SHORTCUT QKeySequence::Back
#define NEXT_MAP_SHORTCUT QKeySequence(QKeySequence::Forward)
#define PREV_MAP_SHORTCUT QKeySequence(QKeySequence::Back)
#define SHOW_GRAPHS_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_G)
#define DISTANCE_GRAPH_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_D)
#define TIME_GRAPH_SHORTCUT QKeySequence(Qt::CTRL + Qt::Key_T)
#ifdef Q_OS_MAC
#define FULLSCREEN_SHORTCUT QKeySequence(Qt::META + Qt::CTRL + Qt::Key_F)
#else // Q_OS_MAC

368
src/kmlparser.cpp Normal file
View File

@ -0,0 +1,368 @@
#include "kmlparser.h"
qreal KMLParser::number()
{
bool res;
qreal ret = _reader.readElementText().toDouble(&res);
if (!res)
_reader.raiseError(QString("Invalid %1").arg(
_reader.name().toString()));
return ret;
}
QDateTime KMLParser::time()
{
QDateTime d = QDateTime::fromString(_reader.readElementText(),
Qt::ISODate);
if (!d.isValid())
_reader.raiseError(QString("Invalid %1").arg(
_reader.name().toString()));
return d;
}
bool KMLParser::coord(Trackpoint &trackpoint)
{
QString data = _reader.readElementText();
const QChar *sp, *ep, *cp, *vp;
int c = 0;
qreal val[3];
bool res;
sp = data.constData();
ep = sp + data.size();
for (cp = sp; cp < ep; cp++)
if (!cp->isSpace())
break;
for (vp = cp; cp <= ep; cp++) {
if (cp->isSpace() || cp->isNull()) {
if (c > 2)
return false;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
val[c] = QString(vp, cp - vp).toDouble(&res);
#else // QT_VERSION < 5
val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res);
#endif // QT_VERSION < 5
if (!res)
return false;
if (c == 1) {
trackpoint.setCoordinates(Coordinates(val[0], val[1]));
if (!trackpoint.coordinates().isValid())
return false;
} else if (c == 2)
trackpoint.setElevation(val[2]);
while (cp->isSpace())
cp++;
vp = cp;
c++;
}
}
return true;
}
bool KMLParser::pointCoordinates(Waypoint &waypoint)
{
QString data = _reader.readElementText();
const QChar *sp, *ep, *cp, *vp;
int c = 0;
qreal val[3];
bool res;
sp = data.constData();
ep = sp + data.size();
for (cp = sp; cp < ep; cp++)
if (!cp->isSpace())
break;
for (vp = cp; cp <= ep; cp++) {
if (*cp == ',') {
if (c > 1)
return false;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
val[c] = QString(vp, cp - vp).toDouble(&res);
#else // QT_VERSION < 5
val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res);
#endif // QT_VERSION < 5
if (!res)
return false;
c++;
vp = cp + 1;
} else if (cp->isSpace() || cp->isNull()) {
if (c < 1)
return false;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
val[c] = QString(vp, cp - vp).toDouble(&res);
#else // QT_VERSION < 5
val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res);
#endif // QT_VERSION < 5
if (!res)
return false;
waypoint.setCoordinates(Coordinates(val[0], val[1]));
if (!waypoint.coordinates().isValid())
return false;
if (c == 2)
waypoint.setElevation(val[2]);
while (cp->isSpace())
cp++;
c = 3;
}
}
return true;
}
bool KMLParser::lineCoordinates(TrackData &track)
{
QString data = _reader.readElementText();
const QChar *sp, *ep, *cp, *vp;
int c = 0;
qreal val[3];
bool res;
sp = data.constData();
ep = sp + data.size();
for (cp = sp; cp < ep; cp++)
if (!cp->isSpace())
break;
for (vp = cp; cp <= ep; cp++) {
if (*cp == ',') {
if (c > 1)
return false;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
val[c] = QString(vp, cp - vp).toDouble(&res);
#else // QT_VERSION < 5
val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res);
#endif // QT_VERSION < 5
if (!res)
return false;
c++;
vp = cp + 1;
} else if (cp->isSpace() || cp->isNull()) {
if (c < 1 || c > 2)
return false;
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
val[c] = QString(vp, cp - vp).toDouble(&res);
#else // QT_VERSION < 5
val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res);
#endif // QT_VERSION < 5
if (!res)
return false;
track.append(Trackpoint(Coordinates(val[0], val[1])));
if (!track.last().coordinates().isValid())
return false;
if (c == 2)
track.last().setElevation(val[2]);
while (cp->isSpace())
cp++;
c = 0;
vp = cp;
}
}
return true;
}
QDateTime KMLParser::timeStamp()
{
QDateTime ts;
while (_reader.readNextStartElement()) {
if (_reader.name() == "when")
ts = time();
else
_reader.skipCurrentElement();
}
return ts;
}
void KMLParser::lineString(TrackData &track)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "coordinates") {
if (!lineCoordinates(track))
_reader.raiseError("Invalid coordinates");
} else
_reader.skipCurrentElement();
}
}
void KMLParser::point(Waypoint &waypoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "coordinates") {
if (!pointCoordinates(waypoint))
_reader.raiseError("Invalid coordinates");
} else
_reader.skipCurrentElement();
}
if (waypoint.coordinates().isNull())
_reader.raiseError("Missing Point coordinates");
}
void KMLParser::track(TrackData &track)
{
const char mismatchError[] = "gx:coord/when element count mismatch";
int i = track.size();
while (_reader.readNextStartElement()) {
if (_reader.name() == "when") {
track.append(Trackpoint());
track.last().setTimestamp(time());
} else if (_reader.name() == "coord") {
if (i == track.size()) {
_reader.raiseError(mismatchError);
return;
} else if (!coord(track[i])) {
_reader.raiseError("Invalid coordinates");
return;
}
i++;
} else
_reader.skipCurrentElement();
}
if (i != track.size())
_reader.raiseError(mismatchError);
}
void KMLParser::multiGeometry(const QString &name, const QString &desc,
const QDateTime timestamp)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Point") {
_waypoints.append(Waypoint());
Waypoint &w = _waypoints.last();
w.setName(name);
w.setDescription(desc);
w.setTimestamp(timestamp);
point(w);
} else if (_reader.name() == "LineString") {
_tracks.append(TrackData());
TrackData &t = _tracks.last();
t.setName(name);
t.setDescription(desc);
lineString(t);
} else
_reader.skipCurrentElement();
}
}
void KMLParser::placemark()
{
QString name, desc;
QDateTime timestamp;
while (_reader.readNextStartElement()) {
if (_reader.name() == "name")
name = _reader.readElementText();
else if (_reader.name() == "description")
desc = _reader.readElementText();
else if (_reader.name() == "TimeStamp")
timestamp = timeStamp();
else if (_reader.name() == "MultiGeometry")
multiGeometry(name, desc, timestamp);
else if (_reader.name() == "Point") {
_waypoints.append(Waypoint());
Waypoint &w = _waypoints.last();
w.setName(name);
w.setDescription(desc);
w.setTimestamp(timestamp);
point(w);
} else if (_reader.name() == "LineString") {
_tracks.append(TrackData());
TrackData &t = _tracks.last();
t.setName(name);
t.setDescription(desc);
lineString(t);
} else if (_reader.name() == "Track") {
_tracks.append(TrackData());
TrackData &t = _tracks.last();
t.setName(name);
t.setDescription(desc);
track(t);
} else
_reader.skipCurrentElement();
}
}
void KMLParser::folder()
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Placemark")
placemark();
else if (_reader.name() == "Folder")
folder();
else
_reader.skipCurrentElement();
}
}
void KMLParser::document()
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Placemark")
placemark();
else if (_reader.name() == "Folder")
folder();
else
_reader.skipCurrentElement();
}
}
void KMLParser::kml()
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Document")
document();
else if (_reader.name() == "Placemark")
placemark();
else
_reader.skipCurrentElement();
}
}
bool KMLParser::parse()
{
if (_reader.readNextStartElement()) {
if (_reader.name() == "kml")
kml();
else
_reader.raiseError("Not a KML file");
}
return !_reader.error();
}
bool KMLParser::loadFile(QFile *file)
{
_reader.clear();
_reader.setDevice(file);
return parse();
}

40
src/kmlparser.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef KMLPARSER_H
#define KMLPARSER_H
#include <QXmlStreamReader>
#include <QDateTime>
#include "parser.h"
class KMLParser : public Parser
{
public:
KMLParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints) {}
~KMLParser() {}
bool loadFile(QFile *file);
QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();}
private:
bool parse();
void kml();
void document();
void folder();
void placemark();
void multiGeometry(const QString &name, const QString &desc,
const QDateTime timestamp);
void track(TrackData &track);
void lineString(TrackData &track);
void point(Waypoint &waypoint);
bool pointCoordinates(Waypoint &waypoint);
bool lineCoordinates(TrackData &track);
bool coord(Trackpoint &trackpoint);
QDateTime timeStamp();
qreal number();
QDateTime time();
QXmlStreamReader _reader;
};
#endif // KMLPARSER_H

81
src/lambertconic.cpp Normal file
View File

@ -0,0 +1,81 @@
#include <cmath>
#include "rd.h"
#include "lambertconic.h"
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#endif // M_PI_2
#ifndef M_PI_4
#define M_PI_4 0.78539816339744830962
#endif // M_PI_4
static double q(const Ellipsoid &el, double b)
{
double e = sqrt(el.flattening() * (2. - el.flattening()));
double esb = e * sin(b);
return log(tan(M_PI_4 + b / 2.) * pow((1. - esb) / (1. + esb), e / 2.));
}
static double iq(const Ellipsoid &el, double q)
{
double e = sqrt(el.flattening() * (2. - el.flattening()));
double b0 = 0.;
double b = 2. * atan(exp(q)) - M_PI_2;
do {
b0 = b;
double esb = e * sin(b);
b = 2. * atan(exp(q) * pow((1. - esb) / (1. + esb), -e / 2.)) - M_PI_2;
} while (fabs(b - b0) > 1e-10);
return b;
}
static double nradius(const Ellipsoid &el, double phi)
{
double sin_phi = sin(phi);
return (el.radius() / sqrt(1. - (el.flattening() * (2. - el.flattening()))
* sin_phi * sin_phi));
}
LambertConic::LambertConic(const Ellipsoid &ellipsoid, double standardParallel1,
double standardParallel2, double latitudeOrigin, double longitudeOrigin,
double scale, double falseEasting, double falseNorthing) : _e(ellipsoid)
{
_cm = longitudeOrigin;
_fe = falseEasting;
_fn = falseNorthing;
double sp1 = deg2rad(standardParallel1);
double sp2 = deg2rad(standardParallel2);
double N1 = nradius(_e, sp1);
double N2 = nradius(_e, sp2);
_q0 = q(_e, deg2rad(latitudeOrigin));
double q1 = q(_e, sp1);
double q2 = q(_e, sp2);
_n = log((N1 * cos(sp1)) / (N2 * cos(sp2))) / (q2 - q1);
double R1 = N1 * cos(sp1) / _n;
_R0 = scale * R1 * exp(_n * (q1 - _q0));
}
QPointF LambertConic::ll2xy(const Coordinates &c) const
{
double dl = _n * (deg2rad(c.lon()) - deg2rad(_cm));
double R = _R0 * exp(_n * (_q0 - q(_e, deg2rad(c.lat()))));
return QPointF(_fe + R * sin(dl), _fn + _R0 - R * cos(dl));
}
Coordinates LambertConic::xy2ll(const QPointF &p) const
{
double dl = atan((p.x() - _fe) / (_fn + _R0 - p.y()));
double dx = p.x() - _fe;
double dy = p.y() - _fn - _R0;
double R = sqrt(dx * dx + dy * dy);
double q = _q0 - log(R / _R0) / _n;
return Coordinates(rad2deg(deg2rad(_cm) + dl / _n), rad2deg(iq(_e, q)));
}

29
src/lambertconic.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef LAMBERTCONIC_H
#define LAMBERTCONIC_H
#include "ellipsoid.h"
#include "projection.h"
class LambertConic : public Projection
{
public:
LambertConic(const Ellipsoid &ellipsoid, double standardParallel1,
double standardParallel2, double latitudeOrigin, double longitudeOrigin,
double scale, double falseEasting, double falseNorthing);
virtual QPointF ll2xy(const Coordinates &c) const;
virtual Coordinates xy2ll(const QPointF &p) const;
private:
Ellipsoid _e;
double _cm;
double _fe;
double _fn;
double _q0;
double _R0;
double _n;
};
#endif // LAMBERTCONIC_H

15
src/latlon.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef LATLON_H
#define LATLON_H
#include "projection.h"
class LatLon : public Projection
{
public:
virtual QPointF ll2xy(const Coordinates &c) const
{return c.toPointF();}
virtual Coordinates xy2ll(const QPointF &p) const
{return Coordinates(p);}
};
#endif // LATLON_H

View File

@ -1,86 +0,0 @@
#include <cmath>
#include "ll.h"
// MSVC workarounds
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif // M_PI
#if defined(_MSC_VER) && (_MSC_VER < 1800)
#define log2(n) (log(n)/log(2.0))
#endif
#define WGS84_RADIUS 6378137.0
#define deg2rad(d) (((d)*M_PI)/180.0)
#define rad2deg(d) (((d)*180.0)/M_PI)
qreal llDistance(const QPointF &p1, const QPointF &p2)
{
qreal dLat = deg2rad(p2.y() - p1.y());
qreal dLon = deg2rad(p2.x() - p1.x());
qreal a = pow(sin(dLat / 2.0), 2.0)
+ cos(deg2rad(p1.y())) * cos(deg2rad(p2.y())) * pow(sin(dLon / 2.0), 2.0);
qreal c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a));
return (WGS84_RADIUS * c);
}
QPointF ll2mercator(const QPointF &ll)
{
QPointF m;
m.setX(ll.x());
m.setY(rad2deg(log(tan(M_PI/4.0 + deg2rad(ll.y())/2.0))));
return m;
}
QPointF mercator2ll(const QPointF &m)
{
QPointF ll;
ll.setX(m.x());
ll.setY(rad2deg(2 * atan(exp(deg2rad(m.y()))) - M_PI/2));
return ll;
}
QPoint mercator2tile(const QPointF &m, int z)
{
QPoint tile;
tile.setX((int)(floor((m.x() + 180.0) / 360.0 * pow(2.0, z))));
tile.setY((int)(floor((1.0 - (m.y() / 180.0)) / 2.0 * pow(2.0, z))));
return tile;
}
QPointF tile2mercator(const QPoint &tile, int z)
{
QPointF m;
m.setX(tile.x() / pow(2.0, z) * 360.0 - 180);
qreal n = M_PI - 2.0 * M_PI * tile.y() / pow(2.0, z);
m.setY(rad2deg(atan(0.5 * (exp(n) - exp(-n)))));
return ll2mercator(m);
}
int scale2zoom(qreal scale)
{
int zoom;
zoom = (int)log2(360.0/(scale * (qreal)TILE_SIZE));
if (zoom < ZOOM_MIN)
return ZOOM_MIN;
if (zoom > ZOOM_MAX)
return ZOOM_MAX;
return zoom;
}
qreal zoom2resolution(int zoom, qreal y)
{
return (WGS84_RADIUS * 2 * M_PI / 256 * cos(2 * atan(exp(deg2rad(y)))
- M_PI/2)) / pow(2.0, zoom);
}

View File

@ -1,18 +0,0 @@
#ifndef LL_H
#define LL_H
#include <QPointF>
#define TILE_SIZE 256
#define ZOOM_MAX 18
#define ZOOM_MIN 3
QPointF ll2mercator(const QPointF &ll);
QPointF mercator2ll(const QPointF &m);
qreal llDistance(const QPointF &p1, const QPointF &p2);
QPoint mercator2tile(const QPointF &m, int zoom);
QPointF tile2mercator(const QPoint &tile, int zoom);
int scale2zoom(qreal scale);
qreal zoom2resolution(int zoom, qreal y);
#endif // LL_H

View File

@ -1,135 +0,0 @@
#include <QFileInfo>
#include <QDir>
#include "downloader.h"
#include "ll.h"
#include "config.h"
#include "map.h"
Map::Map(QObject *parent, const QString &name, const QString &url)
: QObject(parent)
{
_name = name;
_url = url;
connect(&Downloader::instance(), SIGNAL(finished()), this,
SLOT(emitLoaded()));
QString path = TILES_DIR + QString("/") + _name;
if (!QDir().mkpath(path))
fprintf(stderr, "Error creating tiles dir: %s\n", qPrintable(path));
}
void Map::emitLoaded()
{
emit loaded();
}
void Map::loadTiles(QList<Tile> &list, bool block)
{
if (block)
loadTilesSync(list);
else
loadTilesAsync(list);
}
void Map::loadTilesAsync(QList<Tile> &list)
{
QList<Download> dl;
for (int i = 0; i < list.size(); i++) {
Tile &t = list[i];
QString file = tileFile(t);
QFileInfo fi(file);
if (!fi.exists()) {
fillTile(t);
dl.append(Download(tileUrl(t), file));
} else
loadTileFile(t, file);
}
if (!dl.empty())
Downloader::instance().get(dl);
}
void Map::loadTilesSync(QList<Tile> &list)
{
QList<Download> dl;
for (int i = 0; i < list.size(); i++) {
Tile &t = list[i];
QString file = tileFile(t);
QFileInfo fi(file);
if (!fi.exists())
dl.append(Download(tileUrl(t), file));
else
loadTileFile(t, file);
}
if (dl.empty())
return;
QEventLoop wait;
connect(&Downloader::instance(), SIGNAL(finished()), &wait, SLOT(quit()));
if (Downloader::instance().get(dl))
wait.exec();
for (int i = 0; i < list.size(); i++) {
Tile &t = list[i];
if (t.pixmap().isNull()) {
QString file = tileFile(t);
QFileInfo fi(file);
if (!(fi.exists() && loadTileFile(t, file)))
fillTile(t);
}
}
}
void Map::fillTile(Tile &tile)
{
tile.pixmap() = QPixmap(TILE_SIZE, TILE_SIZE);
tile.pixmap().fill();
}
bool Map::loadTileFile(Tile &tile, const QString &file)
{
if (!tile.pixmap().load(file)) {
fprintf(stderr, "%s: error loading tile file\n", qPrintable(file));
return false;
}
return true;
}
QString Map::tileUrl(const Tile &tile)
{
QString url(_url);
url.replace("$z", QString::number(tile.zoom()));
url.replace("$x", QString::number(tile.xy().x()));
url.replace("$y", QString::number(tile.xy().y()));
return url;
}
QString Map::tileFile(const Tile &tile)
{
QString file = TILES_DIR + QString("/%1/%2-%3-%4").arg(_name)
.arg(tile.zoom()).arg(tile.xy().x()).arg(tile.xy().y());
return file;
}
void Map::clearCache()
{
QString path = TILES_DIR + QString("/") + _name;
QDir dir = QDir(path);
QStringList list = dir.entryList();
for (int i = 0; i < list.count(); i++)
dir.remove(list.at(i));
}

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