1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-01 13:29:16 +02:00

Compare commits

...

115 Commits
4.5 ... 4.13

Author SHA1 Message Date
b14eeb58ab Fixed scale size in hi-res prints 2017-08-31 16:28:37 +02:00
8e932b966c Fixed msvc warning 2017-08-31 15:32:44 +02:00
35818ce16e Removed obsolete size guard 2017-08-31 08:10:09 +02:00
6ef7537dff Added some more paper sizes 2017-08-31 07:57:46 +02:00
abbb823890 Removed DPI dependency in print font size computation 2017-08-31 00:07:29 +02:00
0a4f8a46d4 Update gpxsee_sv.ts (#31)
* Update gpxsee_sv.ts

* Update gpxsee_sv.ts

Sorry, missed 2 strings.
2017-08-30 21:59:14 +02:00
fd2823c703 Version++ 2017-08-30 13:59:22 +02:00
d16b61051f Localization update 2017-08-30 13:56:23 +02:00
e69f17aad5 Print font size id now appropriate to the page size 2017-08-30 13:09:54 +02:00
33b1e179d3 Use the correct DPI value for zooming 2017-08-29 15:31:12 +02:00
5efb9f8a4e Added resolution setting to export dialogue. 2017-08-28 17:48:46 +02:00
ebf39f11bd Added support for hi-resolution printing 2017-08-28 15:25:45 +02:00
8b7422b70a Code cleanup 2017-08-27 12:19:33 +02:00
ef3da2e156 Removed obsolete Windows XP bug workaround 2017-08-24 21:04:01 +02:00
6e13c2b704 Translations update 2017-08-24 19:40:52 +02:00
f3e4719439 Added map opacity (brightness) settings 2017-08-24 17:29:59 +02:00
7b6789e78d Version++ 2017-08-23 15:42:00 +02:00
95442461fc Added 4UMaps to map sources 2017-08-23 15:41:23 +02:00
729cbd641c Added support for the VICGRID and VICGRID94 projections 2017-08-23 10:46:28 +02:00
2e2dad8d04 Fixed broken xy2ll computation on southern hemispheres 2017-08-23 10:45:23 +02:00
4e3b6c2eb2 Added missing error check 2017-08-22 23:14:32 +02:00
6430933a96 Use the (incorrect) scale factor of 1 in France zone projections like OziExplorer does 2017-08-22 22:59:59 +02:00
3e14d4afda Added France Zone I-IV projections support 2017-08-22 19:03:35 +02:00
ebb30a3fca Made OZF3 files parsing more robust. 2017-08-22 15:57:37 +02:00
bd20d40ba7 Fixed content margin computation.
Code cleanup.
2017-08-20 10:49:58 +02:00
77823fba14 Added missing newlines 2017-08-15 15:58:23 +02:00
dcc5cbe9bc Added appveyor build config 2017-08-15 15:49:28 +02:00
cd3c99b065 Added QT4 Travis CI build config 2017-08-15 15:42:47 +02:00
83189a4e65 Version++ 2017-08-15 15:14:44 +02:00
797fd7f02b Fixed QT4 build 2017-08-15 15:13:34 +02:00
a45125be9a Code cleanup 2017-08-15 13:36:18 +02:00
a7154988ab Added support for maps with only 2 reference points 2017-08-14 23:57:53 +02:00
afe9a14d5b Improved debug output 2017-08-14 11:16:48 +02:00
b3f1596918 Make Coverity happy 2017-08-11 09:56:45 +02:00
cd90407f56 Version++ 2017-08-11 09:38:50 +02:00
73f06e61f0 Added support for OZF zoom levels 2017-08-11 00:22:21 +02:00
c708fa35fd Cosmetics 2017-08-10 08:58:21 +02:00
dcedfe1e9f Wrong parameters usage fix 2017-08-09 20:29:49 +02:00
daa9d296bd Code cleanup 2017-08-09 20:25:02 +02:00
b8a9a4481c Improved image part drawing procedure 2017-08-09 17:58:37 +02:00
e792bf3b23 Optimization 2017-08-09 11:32:05 +02:00
359a9f0a5a Avoid error summing when computing map positions 2017-08-08 18:41:19 +02:00
4b0b6513d1 Added missing endianity handling 2017-07-31 00:46:10 +02:00
c7762e26fb Added support for LinearRings in KML 2017-07-30 12:13:45 +02:00
5b8d41afb7 Detect OZF files by contents rather than by file extension 2017-07-29 22:19:03 +02:00
81e1664a9b Data/map loading code cleanup 2017-07-27 19:47:46 +02:00
fad3f116f5 Added missing error checks 2017-07-27 19:45:04 +02:00
a6e522d758 Make Coverity happy 2017-07-12 22:07:18 +02:00
ab65deadb6 Some more cosmetics 2017-07-12 21:30:54 +02:00
7f80e8f7b7 Cosmetics 2017-07-12 21:26:00 +02:00
9a5c5eaf61 Unified isNull() logic 2017-07-12 21:23:56 +02:00
9bc5016141 Indent cleanup 2017-07-12 20:54:00 +02:00
a4a46232e4 Version++ 2017-07-09 10:36:39 +02:00
d85fbe5b48 Do not duplicate Waypoints in POI search structures 2017-07-06 09:37:44 +02:00
141e0cb404 isNull() & isValid() logic improvement 2017-07-02 09:39:31 +02:00
2b7d0cd631 Unified isNull() and isValid() logic with other (QT) classes 2017-06-30 18:38:16 +02:00
d106f47771 Some more code cleanup 2017-06-30 18:15:22 +02:00
e48729fc84 Now using a separate class (RectC) for Coordinates rectangles 2017-06-29 22:29:27 +02:00
dca53bc622 Code cleanup 2017-06-29 19:53:42 +02:00
65045dfee4 Code cleanup 2017-06-27 22:42:59 +02:00
002b9c35e1 Don't rescale the view on map change 2017-06-26 00:20:42 +02:00
945528d6b1 Cosmetics 2017-06-26 00:16:38 +02:00
4697ba4ef0 Unify zoom level calculation logic 2017-06-25 16:25:29 +02:00
9bf1447a7e Back to zoom based zoom values 2017-06-25 16:23:43 +02:00
1a8114af8e Proper cleanup. 2017-06-25 12:34:48 +02:00
ad154dfd7d code cleanup 2017-06-25 12:27:53 +02:00
c2004ded31 Now using tmi index files (if present) when loading tar files 2017-06-25 12:24:05 +02:00
3ced367fab Cosmetics 2017-06-22 22:40:43 +02:00
506d1998f8 Updated project home page URL 2017-06-22 01:40:48 +02:00
afc5e9ca4b Using the platform-correct delimiter 2017-06-19 22:48:48 +02:00
425d0e3013 Cosmetics 2017-06-18 16:06:59 +02:00
3a8b1107fe Explicitly set compression method in NSIS scripts 2017-06-18 13:23:00 +02:00
ad7a413d50 Unified MSVC redistributables names with names used by windeployqt 2017-06-18 12:23:54 +02:00
8d5f72de64 Now using proper menu entries location on OS X
+ proper qt stuff localization
2017-05-31 23:37:02 +02:00
9e798e626c Decreased outlier detection window 2017-05-25 01:55:09 +02:00
cb70f0dded Merge pull request #27 from eson57/patch-1
Update gpxsee_sv.ts
2017-05-23 23:30:38 +02:00
400003f684 Update gpxsee_sv.ts 2017-05-23 21:50:21 +02:00
69db510b10 Decreased default speed filter value 2017-05-23 16:06:05 +02:00
cd9ea0f8a9 Unified settingsorder 2017-05-23 13:43:40 +02:00
0f6c8eb896 Code cleanup 2017-05-23 13:23:14 +02:00
6b3c0d05ca Merge branch 'master' of https://github.com/tumic0/GPXSee 2017-05-23 13:22:41 +02:00
b0cc028c4c Localization update 2017-05-23 13:03:28 +02:00
1513130435 Settings polishing on OS X 2017-05-23 12:50:46 +02:00
ec798451e8 Fixed POI radius setting handling 2017-05-23 00:26:59 +02:00
ffc61552d2 Code cleanup 2017-05-22 23:42:15 +02:00
aff7b1c13f Allow only odd window sizes for moving average filter 2017-05-22 23:41:01 +02:00
d54f22d3f0 Fixed crash with 0 window width 2017-05-22 22:37:35 +02:00
dba6291f3e Better (consistent) graph definition 2017-05-22 18:42:23 +02:00
e96cee2ea8 Improved filtering settings layout 2017-05-22 17:04:37 +02:00
1eaaea98a3 Version++ 2017-05-22 16:12:30 +02:00
a90073c422 Added missing icon 2017-05-22 16:10:44 +02:00
223a13583c Added data filtering settings 2017-05-22 14:54:22 +02:00
4386e1f2e1 Code cleanup 2017-05-16 13:18:57 +02:00
5bfe9c2527 Added missing ellipses 2017-05-16 12:59:59 +02:00
efa2b1c366 Code cleanup 2017-05-16 12:59:40 +02:00
e0ad64a902 Added missing units type setup for new items 2017-05-16 11:51:48 +02:00
f0a1acb96a Added missing libgeotrans Source Code Disclaimer 2017-05-08 20:39:42 +02:00
5f3bdb87f8 Added support for British, Irish and Swedish grid projections 2017-05-08 20:17:14 +02:00
90d062e097 Switched to latitude origin capable transverse mercator implementation 2017-05-08 19:53:50 +02:00
58b4c87d46 Fixed broken southern hemisphere reference points conversion
Fixed broken UTM zone setup
Added New Zealand TM 2000 "projection" support
2017-05-06 00:53:39 +02:00
02654ec862 Removed obsolete check 2017-05-05 09:09:32 +02:00
ff015c8714 Added missing data validity checks 2017-05-05 09:00:57 +02:00
c339116cd1 Added support for Albers Equal-Area projection 2017-05-04 20:25:47 +02:00
d8d70bfd8b Added missing MB->kB conversion 2017-05-04 00:04:28 +02:00
7d8149ace3 Fixed typo 2017-05-04 00:01:27 +02:00
4460b454c4 Added pixmap cache configuration to options. 2017-05-03 23:55:16 +02:00
211310f377 Version++ 2017-05-03 22:00:10 +02:00
1e4bcd0ac1 Added support for OZF3 map files 2017-05-03 21:34:13 +02:00
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
104 changed files with 3761 additions and 1619 deletions

52
.appveyor.yml Normal file
View File

@ -0,0 +1,52 @@
version: 4.13.{build}
configuration: Release
platform: Any CPU
environment:
NSISDIR: C:\Program Files (x86)\NSIS
matrix:
- QTDIR: C:\Qt\5.9\msvc2015
PLATFORM: x86
NSI: gpxsee.nsi
OPENSSLDIR: C:\OpenSSL-Win32\bin
- QTDIR: C:\Qt\5.9\msvc2015_64
PLATFORM: x86_amd64
NSI: gpxsee64.nsi
OPENSSLDIR: C:\OpenSSL-Win64\bin
install:
- cmd: >-
set PATH=%QTDIR%\bin;%NSISDIR%;%PATH%
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %PLATFORM%
build_script:
- cmd: >-
lrelease gpxsee.pro
qmake gpxsee.pro
nmake release
md installer
copy release\GPXSee.exe installer
windeployqt --no-svg --release installer\GPXSee.exe
copy pkg\%NSI% installer
copy pkg\datums.csv installer
copy pkg\ellipsoids.csv installer
copy pkg\maps.txt installer
copy licence.txt installer
copy %OPENSSLDIR%\libeay32.dll installer
copy %OPENSSLDIR%\ssleay32.dll installer
makensis.exe installer\%NSI%
artifacts:
- path: installer\GPXSee-*.exe

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: c++
install:
- sudo apt-get install libqt4-dev
script:
- lrelease gpxsee.pro
- qmake gpxsee.pro
- make

View File

@ -15,7 +15,7 @@
<key>CFBundleExecutable</key>
<string>@EXECUTABLE@</string>
<key>CFBundleIdentifier</key>
<string>cz.wz.tumic.GPXSee</string>
<string>org.gpxsee.GPXSee</string>
<key>CFBundleDocumentTypes</key>
<array>

View File

@ -30,4 +30,4 @@ make
[Changelog](https://build.opensuse.org/package/view_file/home:tumic:GPXSee/gpxsee/gpxsee.changes)
## Homepage
GPXSee homepage: http://tumic.wz.cz/gpxsee
http://www.gpxsee.org

View File

@ -1,5 +1,5 @@
TARGET = GPXSee
VERSION = 4.5
VERSION = 4.13
QT += core \
gui \
network
@ -92,7 +92,12 @@ HEADERS += src/config.h \
src/ellipsoid.h \
src/ozf.h \
src/datum.h \
src/maplist.h
src/maplist.h \
src/albersequal.h \
src/oddspinbox.h \
src/rectc.h \
src/searchpointer.h \
src/percentslider.h
SOURCES += src/main.cpp \
src/gui.cpp \
src/poi.cpp \
@ -158,7 +163,11 @@ SOURCES += src/main.cpp \
src/ellipsoid.cpp \
src/ozf.cpp \
src/datum.cpp \
src/maplist.cpp
src/maplist.cpp \
src/albersequal.cpp \
src/oddspinbox.cpp \
src/rectc.cpp \
src/percentslider.cpp
RESOURCES += gpxsee.qrc
TRANSLATIONS = lang/gpxsee_cs.ts \
lang/gpxsee_sv.ts \

View File

@ -14,11 +14,12 @@
<file>icons/arrow-left-double.png</file>
<file>icons/arrow-right-double.png</file>
<file>icons/view-fullscreen.png</file>
<file>icons/office-chart-line-stacked.png</file>
<file>icons/office-chart-line.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>icons/view-filter.png</file>
<file>lang/gpxsee_cs.qm</file>
<file>lang/gpxsee_sv.qm</file>
<file>lang/gpxsee_de.qm</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 727 B

BIN
icons/office-chart-line.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

BIN
icons/view-filter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,10 +5,12 @@
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "4.5"
!define VERSION "4.13"
; The file to write
OutFile "GPXSee-${VERSION}.exe"
; Compression method
SetCompressor /SOLID lzma
; Required execution level
RequestExecutionLevel admin
@ -171,8 +173,8 @@ Section "MSVC runtime" SEC_MSVC
DetailPrint "Installing Visual C++ 2015 Redistributable..."
SetOutPath $TEMP
File "VC_redist.x86.exe"
ExecWait '"$TEMP/VC_redist.x86.exe" /install /quiet /norestart'
File "vcredist_x86.exe"
ExecWait '"$TEMP\vcredist_x86.exe" /install /quiet /norestart'
SetOutPath $INSTDIR
done:

View File

@ -5,10 +5,12 @@
; The name of the installer
Name "GPXSee"
; Program version
!define VERSION "4.5"
!define VERSION "4.13"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"
; Compression method
SetCompressor /SOLID lzma
; Required execution level
RequestExecutionLevel admin
@ -173,8 +175,8 @@ Section "MSVC runtime" SEC_MSVC
DetailPrint "Installing Visual C++ 2015 Redistributable..."
SetOutPath $TEMP
File "VC_redist.x64.exe"
ExecWait '"$TEMP/VC_redist.x64.exe" /install /quiet /norestart'
File "vcredist_x64.exe"
ExecWait '"$TEMP\vcredist_x64.exe" /install /quiet /norestart'
SetOutPath $INSTDIR
done:

View File

@ -1,3 +1,5 @@
Open Topo Map https://a.tile.opentopomap.org/$z/$x/$y.png
Thunderforest http://tile.thunderforest.com/outdoors/$z/$x/$y.png
4UMaps http://4umaps.eu/$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

229
src/albersequal.cpp Normal file
View File

@ -0,0 +1,229 @@
/*
* Based on libgeotrans with the following Source Code Disclaimer:
1. The GEOTRANS source code ("the software") is provided free of charge by
the National Imagery and Mapping Agency (NIMA) of the United States
Department of Defense. Although NIMA makes no copyright claim under Title 17
U.S.C., NIMA claims copyrights in the source code under other legal regimes.
NIMA hereby grants to each user of the software a license to use and
distribute the software, and develop derivative works.
2. Warranty Disclaimer: The software was developed to meet only the internal
requirements of the U.S. National Imagery and Mapping Agency. The software
is provided "as is," and no warranty, express or implied, including but not
limited to the implied warranties of merchantability and fitness for
particular purpose or arising by statute or otherwise in law or from a
course of dealing or usage in trade, is made by NIMA as to the accuracy and
functioning of the software.
3. NIMA and its personnel are not required to provide technical support or
general assistance with respect to the software.
4. Neither NIMA nor its personnel will be liable for any claims, losses, or
damages arising from or connected with the use of the software. The user
agrees to hold harmless the United States National Imagery and Mapping
Agency. The user's sole and exclusive remedy is to stop using the software.
5. NIMA requests that products developed using the software credit the
source of the software with the following statement, "The product was
developed using GEOTRANS, a product of the National Imagery and Mapping
Agency and U.S. Army Engineering Research and Development Center."
6. For any products developed using the software, NIMA requires a disclaimer
that use of the software does not indicate endorsement or approval of the
product by the Secretary of Defense or the National Imagery and Mapping
Agency. Pursuant to the United States Code, 10 U.S.C. Sec. 2797, the name of
the National Imagery and Mapping Agency, the initials "NIMA", the seal of
the National Imagery and Mapping Agency, or any colorable imitation thereof
shall not be used to imply approval, endorsement, or authorization of a
product without prior written permission from United States Secretary of
Defense.
*/
#include "ellipsoid.h"
#include "rd.h"
#include "albersequal.h"
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#endif // M_PI_2
#define ONE_MINUS_SQR(x) (1.0 - (x) * (x))
#define ALBERS_Q(slat, one_minus_sqr_es_sin, es_sin) \
(_one_minus_es2 * ((slat) / (one_minus_sqr_es_sin) - \
(1 / (_two_es)) * log((1 - (es_sin)) / (1 + (es_sin)))))
#define ALBERS_M(clat, one_minus_sqr_es_sin) \
((clat) / sqrt(one_minus_sqr_es_sin))
AlbersEqual::AlbersEqual(const Ellipsoid &ellipsoid, double standardParallel1,
double standardParallel2, double latitudeOrigin, double longitudeOrigin,
double falseEasting, double falseNorthing)
{
double sin_lat, sin_lat1, sin_lat2, cos_lat1, cos_lat2;
double m1, m2, sqr_m1, sqr_m2;
double q0, q1, q2;
double es_sin, es_sin1, es_sin2;
double one_minus_sqr_es_sin1, one_minus_sqr_es_sin2;
double nq0;
double sp1, sp2;
_e = ellipsoid;
_latitudeOrigin = deg2rad(latitudeOrigin);
_longitudeOrigin = deg2rad(longitudeOrigin);
_falseEasting = falseEasting;
_falseNorthing = falseNorthing;
sp1 = deg2rad(standardParallel1);
sp2 = deg2rad(standardParallel2);
_es2 = 2 * _e.flattening() - _e.flattening() * _e.flattening();
_es = sqrt(_es2);
_one_minus_es2 = 1 - _es2;
_two_es = 2 * _es;
sin_lat = sin(_latitudeOrigin);
es_sin = _es * sin_lat;
q0 = ALBERS_Q(sin_lat, ONE_MINUS_SQR(es_sin), es_sin);
sin_lat1 = sin(sp1);
cos_lat1 = cos(sp1);
es_sin1 = _es * sin_lat1;
one_minus_sqr_es_sin1 = ONE_MINUS_SQR(es_sin1);
m1 = ALBERS_M(cos_lat1, one_minus_sqr_es_sin1);
q1 = ALBERS_Q(sin_lat1, one_minus_sqr_es_sin1, es_sin1);
sqr_m1 = m1 * m1;
if (fabs(sp1 - sp2) > 1.0e-10) {
sin_lat2 = sin(sp2);
cos_lat2 = cos(sp2);
es_sin2 = _es * sin_lat2;
one_minus_sqr_es_sin2 = ONE_MINUS_SQR(es_sin2);
m2 = ALBERS_M(cos_lat2, one_minus_sqr_es_sin2);
q2 = ALBERS_Q(sin_lat2, one_minus_sqr_es_sin2, es_sin2);
sqr_m2 = m2 * m2;
_n = (sqr_m1 - sqr_m2) / (q2 - q1);
} else
_n = sin_lat1;
_C = sqr_m1 + _n * q1;
_a_over_n = _e.radius() / _n;
nq0 = _n * q0;
_rho0 = (_C < nq0) ? 0 : _a_over_n * sqrt(_C - nq0);
}
QPointF AlbersEqual::ll2xy(const Coordinates &c) const
{
double dlam;
double sin_lat;
double es_sin;
double q;
double rho;
double theta;
double nq;
dlam = deg2rad(c.lon()) - _longitudeOrigin;
if (dlam > M_PI)
dlam -= 2.0 * M_PI;
if (dlam < -M_PI)
dlam += 2.0 * M_PI;
sin_lat = sin(deg2rad(c.lat()));
es_sin = _es * sin_lat;
q = ALBERS_Q(sin_lat, ONE_MINUS_SQR(es_sin), es_sin);
nq = _n * q;
rho = (_C < nq) ? 0 : _a_over_n * sqrt(_C - nq);
theta = _n * dlam;
return QPointF(rho * sin(theta) + _falseEasting,
_rho0 - rho * cos(theta) + _falseNorthing);
}
Coordinates AlbersEqual::xy2ll(const QPointF &p) const
{
double dy, dx;
double rho0_minus_dy;
double q, qc, q_over_2;
double rho, rho_n;
double phi, delta_phi = 1.0;
double sin_phi;
double es_sin, one_minus_sqr_es_sin;
double theta = 0.0;
int count = 30;
double tolerance = 4.85e-10;
double lat, lon;
dy = p.y() - _falseNorthing;
dx = p.x() - _falseEasting;
rho0_minus_dy = _rho0 - dy;
rho = sqrt(dx * dx + rho0_minus_dy * rho0_minus_dy);
if (_n < 0) {
rho *= -1.0;
dx *= -1.0;
rho0_minus_dy *= -1.0;
}
if (rho != 0.0)
theta = atan2(dx, rho0_minus_dy);
rho_n = rho * _n;
q = (_C - (rho_n * rho_n) / (_e.radius() * _e.radius())) / _n;
qc = 1 - ((_one_minus_es2) / (_two_es)) * log((1.0 - _es) / (1.0 + _es));
if (fabs(fabs(qc) - fabs(q)) > 1.0e-6) {
q_over_2 = q / 2.0;
if (q_over_2 > 1.0)
lat = M_PI_2;
else if (q_over_2 < -1.0)
lat = -M_PI_2;
else {
phi = asin(q_over_2);
if (_es < 1.0e-10)
lat = phi;
else {
while ((fabs(delta_phi) > tolerance) && count) {
sin_phi = sin(phi);
es_sin = _es * sin_phi;
one_minus_sqr_es_sin = ONE_MINUS_SQR(es_sin);
delta_phi = (one_minus_sqr_es_sin * one_minus_sqr_es_sin)
/ (2.0 * cos(phi)) * (q / (_one_minus_es2) - sin_phi
/ one_minus_sqr_es_sin + (log((1.0 - es_sin)
/ (1.0 + es_sin)) / (_two_es)));
phi += delta_phi;
count --;
}
lat = phi;
}
if (lat > M_PI_2)
lat = M_PI_2;
else if (lat < -M_PI_2)
lat = -M_PI_2;
}
} else {
if (q >= 0.0)
lat = M_PI_2;
else
lat = -M_PI_2;
}
lon = _longitudeOrigin + theta / _n;
if (lon > M_PI)
lon -= M_PI * 2;
if (lon < -M_PI)
lon += M_PI * 2;
if (lon > M_PI)
lon = M_PI;
else if (lon < -M_PI)
lon = -M_PI;
return Coordinates(rad2deg(lon), rad2deg(lat));
}

36
src/albersequal.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef ALBERSEQUAL_H
#define ALBERSEQUAL_H
#include "projection.h"
class Ellipsoid;
class AlbersEqual : public Projection
{
public:
AlbersEqual(const Ellipsoid &ellipsoid, double standardParallel1,
double standardParallel2, double latitudeOrigin, double longitudeOrigin,
double falseEasting, double falseNorthing);
virtual QPointF ll2xy(const Coordinates &c) const;
virtual Coordinates xy2ll(const QPointF &p) const;
private:
Ellipsoid _e;
double _latitudeOrigin;
double _longitudeOrigin;
double _falseEasting;
double _falseNorthing;
double _rho0;
double _C;
double _n;
double _es;
double _es2;
double _a_over_n;
double _one_minus_es2;
double _two_es;
};
#endif // ALBERSEQUAL_H

View File

@ -3,7 +3,7 @@
#include <QLocale>
#include <QFileOpenEvent>
#include <QNetworkProxyFactory>
#include <QPixmapCache>
#include <QLibraryInfo>
#include "opengl.h"
#include "gui.h"
#include "onlinemap.h"
@ -14,17 +14,21 @@
App::App(int &argc, char **argv) : QApplication(argc, argv),
_argc(argc), _argv(argv)
{
QTranslator *translator = new QTranslator(this);
QTranslator *gpxsee = new QTranslator(this);
QString locale = QLocale::system().name();
translator->load(QString(":/lang/gpxsee_") + locale);
installTranslator(translator);
gpxsee->load(QString(":/lang/gpxsee_") + locale);
installTranslator(gpxsee);
QTranslator *qt = new QTranslator(this);
qt->load(QLocale::system(), "qt", "_", QLibraryInfo::location(
QLibraryInfo::TranslationsPath));
installTranslator(qt);
#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);

View File

@ -1,6 +1,7 @@
#include <QDir>
#include <QtAlgorithms>
#include <QPainter>
#include "rectc.h"
#include "tar.h"
#include "atlas.h"
@ -84,23 +85,21 @@ void Atlas::computeBounds()
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);
offsets[_maps.indexOf(m.first())].setX(0);
for (int i = 1; i < m.size(); i++) {
w += round(m.at(i-1)->pp2xy(TL(m.at(i))).x());
qreal w = round(m.first()->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);
offsets[_maps.indexOf(m.first())].setY(0);
for (int i = 1; i < m.size(); i++) {
h += round(m.at(i-1)->pp2xy(TL(m.at(i))).y());
qreal h = round(m.first()->pp2xy(TL(m.at(i))).y());
offsets[_maps.indexOf(m.at(i))].setY(h);
}
}
@ -118,6 +117,7 @@ Atlas::Atlas(const QString &fileName, QObject *parent) : Map(parent)
_valid = false;
_zoom = 0;
_name = fi.dir().dirName();
_ci = -1; _cz = -1;
if (!isAtlas(tar, fileName))
return;
@ -197,10 +197,15 @@ qreal Atlas::zoom() const
return _zoom;
}
qreal Atlas::zoomFit(const QSize &size, const QRectF &br)
qreal Atlas::zoomFit(const QSize &size, const RectC &br)
{
_zoom = 0;
if (!br.isValid()) {
_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())))
@ -221,6 +226,26 @@ qreal Atlas::zoomFit(const QSize &size, const QRectF &br)
return _zoom;
}
qreal Atlas::zoomFit(qreal resolution, const Coordinates &c)
{
_zoom = 0;
for (int z = 0; z < _zooms.count(); z++) {
for (int i = _zooms.at(z).first; i <= _zooms.at(z).second; i++) {
if (!_bounds.at(i).first.contains(_maps.at(i)->ll2pp(c)))
continue;
if (_maps.at(i)->resolution(_maps.at(i)->ll2xy(c)) < resolution)
return _zoom;
_zoom = z;
break;
}
}
return _zoom;
}
qreal Atlas::zoomIn()
{
_zoom = qMin(_zoom + 1, _zooms.size() - 1);
@ -233,22 +258,33 @@ qreal Atlas::zoomOut()
return _zoom;
}
QPointF Atlas::ll2xy(const Coordinates &c) const
QPointF Atlas::ll2xy(const Coordinates &c)
{
int idx = _zooms.at(_zoom).first;
QPointF pp;
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).second; i++) {
if (_bounds.at(i).first.contains(_maps.at(i)->ll2pp(c))) {
idx = i;
break;
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(idx)->ll2xy(c);
return p + _bounds.at(idx).second.topLeft();
QPointF p = _maps.at(_ci)->pp2xy(pp);
return p + _bounds.at(_ci).second.topLeft();
}
Coordinates Atlas::xy2ll(const QPointF &p) const
Coordinates Atlas::xy2ll(const QPointF &p)
{
int idx = _zooms.at(_zoom).first;
@ -267,8 +303,7 @@ 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) {
if (_bounds.at(i).second.contains(rect)) {
draw(painter, rect, i);
return;
}

View File

@ -20,12 +20,13 @@ public:
qreal resolution(const QPointF &p) const;
qreal zoom() const;
qreal zoomFit(const QSize &size, const QRectF &br);
qreal zoomFit(const QSize &size, const RectC &br);
qreal zoomFit(qreal resolution, const Coordinates &c);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const;
Coordinates xy2ll(const QPointF &p) const;
QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p);
void draw(QPainter *painter, const QRectF &rect);
@ -48,6 +49,8 @@ private:
QVector<QPair<int, int> > _zooms;
QVector<QPair<QRectF, QRectF> > _bounds;
int _zoom;
int _ci, _cz;
};
#endif // ATLAS_H

View File

@ -7,11 +7,10 @@
#include <QString>
#define APP_NAME "GPXSee"
#define APP_HOMEPAGE "http://tumic.wz.cz/gpxsee"
#define APP_HOMEPAGE "http://www.gpxsee.org"
#define FONT_FAMILY "Arial"
#define FONT_SIZE 12
#define SCREEN_DPI 96.0
#define FONT_SIZE 12 // px
#define ELLIPSOID_FILE QString("ellipsoids.csv")
#define DATUM_FILE QString("datums.csv")

View File

@ -21,7 +21,6 @@ QDebug operator<<(QDebug dbg, const Coordinates &coordinates)
{
dbg.nospace() << "Coordinates(" << coordinates.lon() << ", "
<< coordinates.lat() << ")";
return dbg.space();
}

View File

@ -2,7 +2,7 @@
#define COORDINATES_H
#include <cmath>
#include <QPointF>
#include <QPair>
#include <QDebug>
class Coordinates
@ -12,9 +12,6 @@ public:
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;}
@ -23,10 +20,10 @@ public:
qreal lat() const {return _lat;}
bool isNull() const
{return (std::isnan(_lon) || std::isnan(_lat)) ? true : false;}
{return std::isnan(_lon) && std::isnan(_lat);}
bool isValid() const
{return (_lon >= -180.0 && _lon <= 180.0 && _lat >= -90.0
&& _lat <= 90.0) ? true : false;}
{return (_lon >= -180.0 && _lon <= 180.0
&& _lat >= -90.0 && _lat <= 90.0);}
qreal distanceTo(const Coordinates &c) const;
QPair<Coordinates, Coordinates> boundingRect(qreal distance) const;
@ -39,6 +36,7 @@ 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

View File

@ -1,7 +1,10 @@
#include "csvparser.h"
bool CSVParser::loadFile(QFile *file)
bool CSVParser::parse(QFile *file, QList<TrackData> &track,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
Q_UNUSED(track);
Q_UNUSED(routes);
bool res;
_errorLine = 1;
@ -36,7 +39,7 @@ bool CSVParser::loadFile(QFile *file)
wp.setDescription(QString::fromUtf8(ba.data(), ba.size()));
}
_waypoints.append(wp);
waypoints.append(wp);
_errorLine++;
}

View File

@ -6,12 +6,11 @@
class CSVParser : public Parser
{
public:
CSVParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
{_errorLine = 0;}
CSVParser() : _errorLine(0) {}
~CSVParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &track, QList<RouteData> &routes,
QList<Waypoint> &waypoints);
QString errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}

View File

@ -11,38 +11,32 @@
#include "data.h"
Data::Data() : _errorLine(0)
static QHash<QString, Parser*> parsers()
{
_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));
QHash<QString, Parser*> hash;
hash.insert("gpx", new GPXParser());
hash.insert("tcx", new TCXParser());
hash.insert("kml", new KMLParser());
hash.insert("fit", new FITParser());
hash.insert("csv", new CSVParser());
hash.insert("igc", new IGCParser());
hash.insert("nmea", new NMEAParser());
return hash;
}
QHash<QString, Parser*> Data::_parsers = parsers();
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()
void Data::processData()
{
for (int i = 0; i < _trackData.count(); i++)
_tracks.append(new Track(_trackData.at(i)));
@ -66,8 +60,8 @@ bool Data::loadFile(const QString &fileName)
QHash<QString, Parser*>::iterator it;
if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) {
if (it.value()->loadFile(&file)) {
createData();
if (it.value()->parse(&file, _trackData, _routeData, _waypoints)) {
processData();
return true;
}
@ -75,8 +69,8 @@ bool Data::loadFile(const QString &fileName)
_errorString = it.value()->errorString();
} else {
for (it = _parsers.begin(); it != _parsers.end(); it++) {
if (it.value()->loadFile(&file)) {
createData();
if (it.value()->parse(&file, _trackData, _routeData, _waypoints)) {
processData();
return true;
}
file.reset();
@ -93,3 +87,23 @@ bool Data::loadFile(const QString &fileName)
return false;
}
QString Data::formats()
{
return tr("Supported files (*.csv *.fit *.gpx *.igc *.kml *.nmea *.tcx)")
+ ";;" + tr("CSV files (*.csv)") + ";;" + tr("FIT files (*.fit)") + ";;"
+ tr("GPX files (*.gpx)") + ";;" + tr("IGC files (*.igc)") + ";;"
+ tr("KML files (*.kml)") + ";;" + tr("NMEA files (*.nmea)") + ";;"
+ tr("TCX files (*.tcx)") + ";;" + tr("All files (*)");
}
QStringList Data::filter()
{
QStringList filter;
QHash<QString, Parser*>::iterator it;
for (it = _parsers.begin(); it != _parsers.end(); it++)
filter << QString("*.%1").arg(it.key());
return filter;
}

View File

@ -6,16 +6,19 @@
#include <QHash>
#include <QPointF>
#include <QString>
#include <QStringList>
#include "waypoint.h"
#include "track.h"
#include "route.h"
#include "parser.h"
class Data
class Data : public QObject
{
Q_OBJECT
public:
Data();
Data(QObject *parent = 0) : QObject(parent), _errorLine(0) {}
~Data();
bool loadFile(const QString &fileName);
@ -24,21 +27,25 @@ public:
const QList<Track*> &tracks() const {return _tracks;}
const QList<Route*> &routes() const {return _routes;}
const QList<Waypoint> &waypoints() const {return _waypointData;}
const QList<Waypoint> &waypoints() const {return _waypoints;}
static QString formats();
static QStringList filter();
private:
void createData();
void processData();
QString _errorString;
int _errorLine;
QHash<QString, Parser*> _parsers;
QList<Track*> _tracks;
QList<Route*> _routes;
QList<Waypoint> _waypoints;
QList<TrackData> _trackData;
QList<RouteData> _routeData;
QList<Waypoint> _waypointData;
static QHash<QString, Parser*> _parsers;
};
#endif // DATA_H

View File

@ -1,5 +1,7 @@
#include <cmath>
#include <QFile>
#include "wgs84.h"
#include "rd.h"
#include "datum.h"
@ -7,7 +9,7 @@ static QMap<QString, Datum> WGS84()
{
QMap<QString, Datum> map;
map.insert("WGS 84", Datum(Ellipsoid(WGS84_RADIUS, WGS84_FLATTENING),
0, 0, 0));
0.0, 0.0, 0.0));
return map;
}
@ -81,3 +83,39 @@ bool Datum::loadList(const QString &path)
return true;
}
// Abridged Molodensky transformation
Coordinates Datum::toWGS84(const Coordinates &c) const
{
if (_ellipsoid.radius() == WGS84_RADIUS
&& _ellipsoid.flattening() == WGS84_FLATTENING
&& _dx == 0.0 && _dy == 0.0 && _dz == 0.0)
return c;
double rlat = deg2rad(c.lat());
double rlon = deg2rad(c.lon());
double slat = sin(rlat);
double clat = cos(rlat);
double slon = sin(rlon);
double clon = cos(rlon);
double ssqlat = slat * slat;
double from_f = ellipsoid().flattening();
double df = WGS84_FLATTENING - from_f;
double from_a = ellipsoid().radius();
double da = WGS84_RADIUS - from_a;
double from_esq = ellipsoid().flattening()
* (2.0 - ellipsoid().flattening());
double adb = 1.0 / (1.0 - from_f);
double rn = from_a / sqrt(1 - from_esq * ssqlat);
double rm = from_a * (1 - from_esq) / pow((1 - from_esq * ssqlat), 1.5);
double from_h = 0.0;
double dlat = (-dx() * slat * clon - dy() * slat * slon + dz() * clat + da
* rn * from_esq * slat * clat / from_a + df * (rm * adb + rn / adb) * slat
* clat) / (rm + from_h);
double dlon = (-dx() * slon + dy() * clon) / ((rn + from_h) * clat);
return Coordinates(c.lon() + rad2deg(dlon), c.lat() + rad2deg(dlat));
}

View File

@ -3,11 +3,12 @@
#include <QMap>
#include "ellipsoid.h"
#include "coordinates.h"
class Datum
{
public:
Datum() : _ellipsoid(Ellipsoid()), _dx(0), _dy(0), _dz(0) {}
Datum() : _ellipsoid(Ellipsoid()), _dx(0.0), _dy(0.0), _dz(0.0) {}
Datum(const Ellipsoid &ellipsoid, double dx, double dy, double dz)
: _ellipsoid(ellipsoid), _dx(dx), _dy(dy), _dz(dz) {}
@ -16,9 +17,10 @@ public:
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;}
bool isNull() const
{return (_ellipsoid.isNull() && _dx == 0.0 && _dy == 0.0 && _dz == 0.0);}
Coordinates toWGS84(const Coordinates &c) const;
static bool loadList(const QString &path);
static const QString &errorString() {return _errorString;}

View File

@ -3,21 +3,18 @@
#include <QString>
#include <QMap>
#include "wgs84.h"
class Ellipsoid
{
public:
Ellipsoid() : _radius(-1.0), _flattening(-1.0) {}
Ellipsoid() : _radius(0.0), _flattening(0.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;}
bool isNull() const {return (_radius == 0.0 && _flattening == 0.0);}
static bool loadList(const QString &path);
static const QString &errorString() {return _errorString;}

View File

@ -1,5 +1,6 @@
#include <QtGlobal>
#include <QPainter>
#include "rectc.h"
#include "misc.h"
#include "rd.h"
#include "wgs84.h"
@ -21,17 +22,14 @@ 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)
qreal EmptyMap::zoomFit(const QSize &size, const RectC &br)
{
if (br.isNull())
if (!br.isValid())
_scale = SCALE_MAX;
else {
Coordinates topLeft(br.topLeft());
Coordinates bottomRight(br.bottomRight());
QRectF tbr(Mercator().ll2xy(topLeft), Mercator().ll2xy(bottomRight));
QRectF tbr(Mercator().ll2xy(br.topLeft()),
Mercator().ll2xy(br.bottomRight()));
QPointF sc(tbr.width() / size.width(), tbr.height() / size.height());
_scale = qMax(sc.x(), sc.y());
}
@ -41,6 +39,17 @@ qreal EmptyMap::zoomFit(const QSize &size, const QRectF &br)
return _scale;
}
qreal EmptyMap::zoomFit(qreal resolution, const Coordinates &c)
{
_scale = (360.0 * resolution) / (WGS84_RADIUS * 2 * M_PI
* cos(deg2rad(c.lat())));
_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
@ -64,13 +73,13 @@ void EmptyMap::draw(QPainter *painter, const QRectF &rect)
painter->fillRect(rect, Qt::white);
}
QPointF EmptyMap::ll2xy(const Coordinates &c) const
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) const
Coordinates EmptyMap::xy2ll(const QPointF &p)
{
QPointF m(p.x() * _scale, -p.y() * _scale);
return Mercator().xy2ll(m);

View File

@ -16,12 +16,13 @@ public:
qreal resolution(const QPointF &p) const;
qreal zoom() const {return _scale;}
qreal zoomFit(const QSize &size, const QRectF &br);
qreal zoomFit(const QSize &size, const RectC &br);
qreal zoomFit(qreal resolution, const Coordinates &c);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const;
Coordinates xy2ll(const QPointF &p) const;
QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p);
void draw(QPainter *painter, const QRectF &rect);

View File

@ -26,15 +26,28 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
_fileSelect->setFile(_export->fileName);
_paperSize = new QComboBox();
_paperSize->addItem("A2", QPrinter::A2);
_paperSize->addItem("A3", QPrinter::A3);
_paperSize->addItem("A4", QPrinter::A4);
_paperSize->addItem("A5", QPrinter::A5);
_paperSize->addItem("A6", QPrinter::A6);
_paperSize->addItem("B3", QPrinter::B3);
_paperSize->addItem("B4", QPrinter::B4);
_paperSize->addItem("B5", QPrinter::B5);
_paperSize->addItem("B6", QPrinter::B6);
_paperSize->addItem("Tabloid", QPrinter::Tabloid);
_paperSize->addItem("Legal", QPrinter::Legal);
_paperSize->addItem("Letter", QPrinter::Letter);
if ((index = _paperSize->findData(_export->paperSize)) >= 0)
_paperSize->setCurrentIndex(index);
_resolution = new QComboBox();
_resolution->addItem("300 DPI", 300);
_resolution->addItem("600 DPI", 600);
_resolution->addItem("1200 DPI", 1200);
if ((index = _resolution->findData(_export->resolution)) >= 0)
_resolution->setCurrentIndex(index);
_portrait = new QRadioButton(tr("Portrait"));
_landscape = new QRadioButton(tr("Landscape"));
QHBoxLayout *orientationLayout = new QHBoxLayout();
@ -81,6 +94,7 @@ ExportDialog::ExportDialog(Export *exp, QWidget *parent)
#endif // Q_OS_MAC
QFormLayout *pageSetupLayout = new QFormLayout;
pageSetupLayout->addRow(tr("Page size:"), _paperSize);
pageSetupLayout->addRow(tr("Resolution:"), _resolution);
pageSetupLayout->addRow(tr("Orientation:"), orientationLayout);
pageSetupLayout->addRow(tr("Margins:"), marginsLayout);
#ifdef Q_OS_MAC
@ -161,9 +175,11 @@ void ExportDialog::accept()
? QPrinter::Portrait : QPrinter::Landscape;
QPrinter::PaperSize paperSize = static_cast<QPrinter::PaperSize>
(_paperSize->itemData(_paperSize->currentIndex()).toInt());
int resolution = _resolution->itemData(_resolution->currentIndex()).toInt();
_export->fileName = _fileSelect->file();
_export->paperSize = paperSize;
_export->resolution = resolution;
_export->orientation = orientation;
if (_export->units == Imperial)
_export->margins = MarginsF(_leftMargin->value() / MM2IN,

View File

@ -16,6 +16,7 @@ struct Export {
QPrinter::PaperSize paperSize;
QPrinter::Orientation orientation;
MarginsF margins;
int resolution;
Units units;
};
@ -37,6 +38,7 @@ private:
FileSelectWidget *_fileSelect;
QComboBox *_paperSize;
QComboBox *_resolution;
QRadioButton *_portrait;
QRadioButton *_landscape;
QDoubleSpinBox *_topMargin;

View File

@ -10,8 +10,7 @@ const quint32 FIT_MAGIC = 0x5449462E; // .FIT
#define TIMESTAMP_FIELD 253
FITParser::FITParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
FITParser::FITParser()
{
memset(_defs, 0, sizeof(_defs));
@ -191,7 +190,8 @@ bool FITParser::readField(Field *f, quint32 &val)
return ret;
}
bool FITParser::parseData(MessageDefinition *def, quint8 offset)
bool FITParser::parseData(TrackData &track, MessageDefinition *def,
quint8 offset)
{
Field *field;
quint32 timestamp = _timestamp + offset;
@ -268,7 +268,7 @@ bool FITParser::parseData(MessageDefinition *def, quint8 offset)
if (trackpoint.coordinates().isValid()) {
trackpoint.setTimestamp(QDateTime::fromTime_t(timestamp
+ 631065600));
_tracks.last().append(trackpoint);
track.append(trackpoint);
} else {
if (trackpoint.coordinates().isNull())
warning("Missing coordinates");
@ -282,21 +282,21 @@ bool FITParser::parseData(MessageDefinition *def, quint8 offset)
return true;
}
bool FITParser::parseDataMessage(quint8 header)
bool FITParser::parseDataMessage(TrackData &track, quint8 header)
{
int local_id = header & 0xf;
MessageDefinition* def = &_defs[local_id];
return parseData(def, 0);
return parseData(track, def, 0);
}
bool FITParser::parseCompressedMessage(quint8 header)
bool FITParser::parseCompressedMessage(TrackData &track, quint8 header)
{
int local_id = (header >> 5) & 3;
MessageDefinition* def = &_defs[local_id];
return parseData(def, header & 0x1f);
return parseData(track, def, header & 0x1f);
}
bool FITParser::parseRecord()
bool FITParser::parseRecord(TrackData &track)
{
quint8 header;
@ -304,11 +304,11 @@ bool FITParser::parseRecord()
return false;
if (header & 0x80)
return parseCompressedMessage(header);
return parseCompressedMessage(track, header);
else if (header & 0x40)
return parseDefinitionMessage(header);
else
return parseDataMessage(header);
return parseDataMessage(track, header);
}
bool FITParser::parseHeader()
@ -337,8 +337,11 @@ bool FITParser::parseHeader()
return true;
}
bool FITParser::loadFile(QFile *file)
bool FITParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
Q_UNUSED(routes);
Q_UNUSED(waypoints);
bool ret = true;
_device = file;
@ -348,10 +351,11 @@ bool FITParser::loadFile(QFile *file)
if (!parseHeader())
return false;
_tracks.append(TrackData());
tracks.append(TrackData());
TrackData &track = tracks.last();
while (_len)
if ((ret = parseRecord()) == false)
if ((ret = parseRecord(track)) == false)
break;
clearDefinitions();

View File

@ -6,37 +6,37 @@
class FITParser : public Parser
{
public:
FITParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints);
FITParser();
~FITParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints);
QString errorString() const {return _errorString;}
int errorLine() const {return 0;}
private:
typedef struct {
struct FileHeader {
quint8 headerSize;
quint8 protocolVersion;
quint16 profileVersion;
quint32 dataSize;
quint32 magic;
} FileHeader;
};
typedef struct {
struct Field {
quint8 id;
quint8 size;
quint8 type;
} Field;
};
typedef struct {
struct MessageDefinition {
quint8 endian;
quint16 globalId;
quint8 numFields;
Field *fields;
quint8 numDevFields;
Field *devFields;
} MessageDefinition;
};
void warning(const char *text) const;
@ -47,11 +47,11 @@ private:
bool skipValue(size_t size);
bool parseHeader();
bool parseRecord();
bool parseRecord(TrackData &track);
bool parseDefinitionMessage(quint8 header);
bool parseCompressedMessage(quint8 header);
bool parseDataMessage(quint8 header);
bool parseData(MessageDefinition *def, quint8 offset);
bool parseCompressedMessage(TrackData &track, quint8 header);
bool parseDataMessage(TrackData &track, quint8 header);
bool parseData(TrackData &track, MessageDefinition *def, quint8 offset);
bool readField(Field *f, quint32 &val);
QIODevice *_device;

View File

@ -167,39 +167,36 @@ void GPXParser::track(TrackData &track)
}
}
void GPXParser::gpx()
void GPXParser::gpx(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "trk") {
_tracks.append(TrackData());
track(_tracks.back());
tracks.append(TrackData());
track(tracks.back());
} else if (_reader.name() == "rte") {
_routes.append(RouteData());
routepoints(_routes.back());
routes.append(RouteData());
routepoints(routes.back());
} else if (_reader.name() == "wpt") {
_waypoints.append(Waypoint(coordinates()));
waypointData(_waypoints.last());
waypoints.append(Waypoint(coordinates()));
waypointData(waypoints.last());
} else
_reader.skipCurrentElement();
}
}
bool GPXParser::parse()
bool GPXParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
_reader.clear();
_reader.setDevice(file);
if (_reader.readNextStartElement()) {
if (_reader.name() == "gpx")
gpx();
gpx(tracks, routes, waypoints);
else
_reader.raiseError("Not a GPX file");
}
return !_reader.error();
}
bool GPXParser::loadFile(QFile *file)
{
_reader.clear();
_reader.setDevice(file);
return parse();
}

View File

@ -8,17 +8,16 @@
class GPXParser : public Parser
{
public:
GPXParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints) {}
~GPXParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints);
QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();}
private:
bool parse();
void gpx();
void gpx(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints);
void track(TrackData &track);
void trackpoints(TrackData &track);
void routepoints(RouteData &route);

View File

@ -4,6 +4,5 @@ QDebug operator<<(QDebug dbg, const GraphPoint &point)
{
dbg.nospace() << "GraphPoint(" << point.s() << ", " << point.t() << ", "
<< point.y() << ")";
return dbg.maybeSpace();
return dbg.space();
}

View File

@ -31,22 +31,6 @@ private:
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;
};
typedef QVector<GraphPoint> Graph;
#endif // GRAPH_H

View File

@ -14,6 +14,14 @@ GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
_graph = graph;
_sx = 1.0; _sy = 1.0;
_time = true;
for (int i = 0; i < _graph.size(); i++) {
if (std::isnan(_graph.at(i).t())) {
_time = false;
break;
}
}
setZValue(1.0);
updatePath();
@ -124,7 +132,7 @@ qreal GraphItem::distanceAtTime(qreal time)
void GraphItem::emitSliderPositionChanged(qreal pos)
{
if (_type == Time) {
if (_graph.hasTime()) {
if (_time) {
if (pos >= _graph.first().t() && pos <= _graph.last().t())
emit sliderPositionChanged(distanceAtTime(pos));
else
@ -163,7 +171,7 @@ void GraphItem::updatePath()
{
_path = QPainterPath();
if (_type == Time && !_graph.hasTime())
if (_type == Time && !_time)
return;
_path.moveTo(_graph.first().x(_type) * _sx, -_graph.first().y() * _sy);
@ -173,7 +181,7 @@ void GraphItem::updatePath()
void GraphItem::updateBounds()
{
if (_type == Time && !_graph.hasTime()) {
if (_type == Time && !_time) {
_bounds = QRectF();
return;
}

View File

@ -50,6 +50,8 @@ private:
QPainterPath _path;
QRectF _bounds;
qreal _sx, _sy;
bool _time;
};
#endif // GRAPHITEM_H

View File

@ -326,11 +326,9 @@ void GraphView::mousePressEvent(QMouseEvent *e)
QGraphicsView::mousePressEvent(e);
}
void GraphView::plot(QPainter *painter, const QRectF &target)
void GraphView::plot(QPainter *painter, const QRectF &target, qreal scale)
{
qreal ratio = painter->paintEngine()->paintDevice()->logicalDpiX()
/ SCREEN_DPI;
QSizeF canvas = QSizeF(target.width() / ratio, target.height() / ratio);
QSizeF canvas = QSizeF(target.width() / scale, target.height() / scale);
setUpdatesEnabled(false);
redraw(canvas);

View File

@ -53,7 +53,7 @@ public:
qreal sliderPosition() const {return _sliderPos;}
void setSliderPosition(qreal pos);
void plot(QPainter *painter, const QRectF &target);
void plot(QPainter *painter, const QRectF &target, qreal scale);
void useOpenGL(bool use);

View File

@ -21,6 +21,7 @@
#include <QLocale>
#include <QMimeData>
#include <QUrl>
#include <QPixmapCache>
#include "config.h"
#include "icons.h"
#include "keys.h"
@ -101,24 +102,6 @@ GUI::~GUI()
}
}
const QString GUI::fileFormats() const
{
return tr("Supported files (*.csv *.fit *.gpx *.igc *.kml *.nmea *.tcx)")
+ ";;" + tr("CSV files (*.csv)") + ";;" + tr("FIT files (*.fit)") + ";;"
+ tr("GPX files (*.gpx)") + ";;" + tr("IGC files (*.igc)") + ";;"
+ tr("KML files (*.kml)") + ";;" + tr("NMEA files (*.nmea)") + ";;"
+ tr("TCX files (*.tcx)") + ";;" + tr("All files (*)");
}
void GUI::createBrowser()
{
QStringList filter;
filter << "*.gpx" << "*.tcx" << "*.kml" << "*.fit" << "*.csv" << "*.igc"
<< "*.nmea";
_browser = new FileBrowser(this);
_browser->setFilter(filter);
}
void GUI::loadDatums()
{
QString ef, df;
@ -174,7 +157,7 @@ void GUI::loadMaps()
else if (QFile::exists(GLOBAL_MAP_FILE))
online = GLOBAL_MAP_FILE;
if (!online.isNull() && !_ml->loadList(online))
if (!online.isNull() && !_ml->loadFile(online))
qWarning("%s: %s", qPrintable(online), qPrintable(_ml->errorString()));
@ -186,12 +169,10 @@ void GUI::loadMaps()
if (!offline.isNull()) {
QDir md(offline);
QFileInfoList ml = md.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
QStringList filters;
filters << "*.map" << "*.tba" << "*.tar";
for (int i = 0; i < ml.size(); i++) {
QDir dir(ml.at(i).absoluteFilePath());
QFileInfoList fl = dir.entryInfoList(filters, QDir::Files);
QFileInfoList fl = dir.entryInfoList(MapList::filter(), QDir::Files);
if (fl.isEmpty())
qWarning("%s: no map/atlas file found",
@ -200,7 +181,7 @@ void GUI::loadMaps()
qWarning("%s: ambiguous directory content",
qPrintable(ml.at(i).absoluteFilePath()));
else
if (!_ml->loadMap(fl.first().absoluteFilePath()))
if (!_ml->loadFile(fl.first().absoluteFilePath()))
qWarning("%s: %s", qPrintable(fl.first().absoluteFilePath()),
qPrintable(_ml->errorString()));
}
@ -233,6 +214,12 @@ void GUI::loadPOIs()
}
}
void GUI::createBrowser()
{
_browser = new FileBrowser(this);
_browser->setFilter(Data::filter());
}
void GUI::createMapActions()
{
_mapsSignalMapper = new QSignalMapper(this);
@ -261,7 +248,8 @@ void GUI::createPOIFilesActions()
for (int i = 0; i < _poi->files().count(); i++)
createPOIFileAction(i);
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this, SLOT(poiFileChecked(int)));
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
}
QAction *GUI::createPOIFileAction(int index)
@ -293,6 +281,7 @@ void GUI::createActions()
// General actions
_exitAction = new QAction(QIcon(QPixmap(QUIT_ICON)), tr("Quit"), this);
_exitAction->setShortcut(QUIT_SHORTCUT);
_exitAction->setMenuRole(QAction::QuitRole);
connect(_exitAction, SIGNAL(triggered()), this, SLOT(close()));
addAction(_exitAction);
@ -303,11 +292,12 @@ void GUI::createActions()
connect(_keysAction, SIGNAL(triggered()), this, SLOT(keys()));
_aboutAction = new QAction(QIcon(QPixmap(APP_ICON)),
tr("About GPXSee"), this);
_aboutAction->setMenuRole(QAction::AboutRole);
connect(_aboutAction, SIGNAL(triggered()), this, SLOT(about()));
// File actions
_openFileAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)),
tr("Open"), this);
tr("Open..."), this);
_openFileAction->setShortcut(OPEN_SHORTCUT);
connect(_openFileAction, SIGNAL(triggered()), this, SLOT(openFile()));
addAction(_openFileAction);
@ -337,7 +327,7 @@ void GUI::createActions()
// POI actions
_openPOIAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)),
tr("Load POI file"), this);
tr("Load POI file..."), this);
connect(_openPOIAction, SIGNAL(triggered()), this, SLOT(openPOIFile()));
_closePOIAction = new QAction(QIcon(QPixmap(CLOSE_FILE_ICON)),
tr("Close POI files"), this);
@ -367,8 +357,8 @@ void GUI::createActions()
connect(_showMapAction, SIGNAL(triggered(bool)), _pathView,
SLOT(showMap(bool)));
addAction(_showMapAction);
_loadMapAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)), tr("Load map"),
this);
_loadMapAction = new QAction(QIcon(QPixmap(OPEN_FILE_ICON)),
tr("Load map..."), this);
connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap()));
_clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
connect(_clearMapCacheAction, SIGNAL(triggered()), this,
@ -475,6 +465,7 @@ void GUI::createActions()
SLOT(showFullscreen(bool)));
addAction(_fullscreenAction);
_openOptionsAction = new QAction(tr("Options..."), this);
_openOptionsAction->setMenuRole(QAction::PreferencesRole);
connect(_openOptionsAction, SIGNAL(triggered()), this,
SLOT(openOptions()));
@ -645,6 +636,7 @@ void GUI::createStatusBar()
void GUI::about()
{
QMessageBox msgBox(this);
QUrl homepage(APP_HOMEPAGE);
msgBox.setWindowTitle(tr("About GPXSee"));
msgBox.setText("<h2>" + QString(APP_NAME) + "</h2><p><p>" + tr("Version ")
@ -652,7 +644,8 @@ void GUI::about()
msgBox.setInformativeText("<table width=\"300\"><tr><td>"
+ tr("GPXSee is distributed under the terms of the GNU General Public "
"License version 3. For more info about GPXSee visit the project "
"homepage at ") + "<a href=\"" + APP_HOMEPAGE + "\">" + APP_HOMEPAGE
"homepage at ") + "<a href=\"" + homepage.toString() + "\">"
+ homepage.toString(QUrl::RemoveScheme).mid(2)
+ "</a>.</td></tr></table>");
QIcon icon = msgBox.windowIcon();
@ -714,8 +707,8 @@ void GUI::dataSources()
"directory:")
+ "</p><p><code>" + USER_MAP_DIR + "</code></p><p>"
+ tr("The expected structure is one map/atlas in a separate subdirectory."
" Supported map formats are OziExplorer image-based maps and tiled"
" TrekBuddy maps/atlases (tared and non-tared).") + "</p>"
" Supported map formats are OziExplorer maps and TrekBuddy maps/atlases"
" (tared and non-tared).") + "</p>"
+ "<h4>" + tr("POIs") + "</h4><p>"
+ tr("To make GPXSee load a POI file automatically on startup, add "
@ -729,7 +722,7 @@ void GUI::dataSources()
void GUI::openFile()
{
QStringList files = QFileDialog::getOpenFileNames(this, tr("Open file"),
QString(), fileFormats());
QString(), Data::formats());
QStringList list = files;
for (QStringList::Iterator it = list.begin(); it != list.end(); it++)
@ -819,7 +812,7 @@ bool GUI::loadFile(const QString &fileName)
void GUI::openPOIFile()
{
QStringList files = QFileDialog::getOpenFileNames(this, tr("Open POI file"),
QString(), fileFormats());
QString(), Data::formats());
QStringList list = files;
for (QStringList::Iterator it = list.begin(); it != list.end(); it++)
@ -861,18 +854,10 @@ void GUI::closePOIFiles()
_poi->clear();
}
void GUI::printFile()
{
QPrinter printer(QPrinter::HighResolution);
QPrintDialog dialog(&printer, this);
if (dialog.exec() == QDialog::Accepted)
plot(&printer);
}
void GUI::openOptions()
{
Options options(_options);
bool reload = false;
OptionsDialog dialog(&options, this);
if (dialog.exec() != QDialog::Accepted)
@ -883,6 +868,8 @@ void GUI::openOptions()
for (int i = 0; i < _tabs.count(); i++)
_tabs.at(i)->setPalette(options.palette);
}
if (options.mapOpacity != _options.mapOpacity)
_pathView->setMapOpacity(options.mapOpacity);
if (options.trackWidth != _options.trackWidth)
_pathView->setTrackWidth(options.trackWidth);
if (options.routeWidth != _options.routeWidth)
@ -902,6 +889,39 @@ void GUI::openOptions()
_tabs.at(i)->setRenderHint(QPainter::Antialiasing,
options.graphAntiAliasing);
if (options.elevationFilter != _options.elevationFilter) {
Track::setElevationFilter(options.elevationFilter);
reload = true;
}
if (options.speedFilter != _options.speedFilter) {
Track::setSpeedFilter(options.speedFilter);
reload = true;
}
if (options.heartRateFilter != _options.heartRateFilter) {
Track::setHeartRateFilter(options.heartRateFilter);
reload = true;
}
if (options.cadenceFilter != _options.cadenceFilter) {
Track::setCadenceFilter(options.cadenceFilter);
reload = true;
}
if (options.powerFilter != _options.powerFilter) {
Track::setPowerFilter(options.powerFilter);
reload = true;
}
if (options.outlierEliminate != _options.outlierEliminate) {
Track::setOutlierElimination(options.outlierEliminate);
reload = true;
}
if (options.pauseSpeed != _options.pauseSpeed) {
Track::setPauseSpeed(options.pauseSpeed);
reload = true;
}
if (options.pauseInterval != _options.pauseInterval) {
Track::setPauseInterval(options.pauseInterval);
reload = true;
}
if (options.poiRadius != _options.poiRadius)
_poi->setRadius(options.poiRadius);
@ -910,10 +930,24 @@ void GUI::openOptions()
for (int i = 0; i < _tabs.count(); i++)
_tabs.at(i)->useOpenGL(options.useOpenGL);
}
if (options.pixmapCache != _options.pixmapCache)
QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
if (reload)
reloadFile();
_options = options;
}
void GUI::printFile()
{
QPrinter printer(QPrinter::HighResolution);
QPrintDialog dialog(&printer, this);
if (dialog.exec() == QDialog::Accepted)
plot(&printer);
}
void GUI::exportFile()
{
ExportDialog dialog(&_export, this);
@ -924,6 +958,7 @@ void GUI::exportFile()
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setCreator(QString(APP_NAME) + QString(" ")
+ QString(APP_VERSION));
printer.setResolution(_export.resolution);
printer.setOrientation(_export.orientation);
printer.setOutputFileName(_export.fileName);
printer.setPaperSize(_export.paperSize);
@ -973,28 +1008,29 @@ void GUI::plot(QPrinter *printer)
if (tm > 0 && _options.printMovingTime)
info.insert(tr("Moving time"), Format::timeSpan(tm));
ratio = p.paintEngine()->paintDevice()->logicalDpiX() / SCREEN_DPI;
qreal fsr = 1085.0 / (qMax(printer->width(), printer->height())
/ (qreal)printer->resolution());
ratio = p.paintEngine()->paintDevice()->logicalDpiX() / fsr;
if (info.isEmpty()) {
ih = 0;
mh = 0;
} else {
ih = info.contentSize().height() * ratio;
mh = ih / 2;
info.plot(&p, QRectF(0, 0, printer->width(), ih));
info.plot(&p, QRectF(0, 0, printer->width(), ih), ratio);
}
if (_graphTabWidget->isVisible() && !_options.separateGraphPage) {
qreal r = (((qreal)(printer)->width()) / (qreal)(printer->height()));
gh = (printer->width() > printer->height())
? 0.15 * r * (printer->height() - ih - 2*mh)
: 0.15 * (printer->height() - ih - 2*mh);
gh = qMax(gh, ratio * 150);
GraphTab *gt = static_cast<GraphTab*>(_graphTabWidget->currentWidget());
gt->plot(&p, QRectF(0, printer->height() - gh, printer->width(), gh));
gt->plot(&p, QRectF(0, printer->height() - gh, printer->width(), gh),
ratio);
} else
gh = 0;
_pathView->plot(&p, QRectF(0, ih + mh, printer->width(), printer->height()
- (ih + 2*mh + gh)));
- (ih + 2*mh + gh)), _options.hiresPrint);
if (_graphTabWidget->isVisible() && _options.separateGraphPage) {
printer->newPage();
@ -1011,7 +1047,8 @@ void GUI::plot(QPrinter *printer)
qreal y = 0;
for (int i = 0; i < _tabs.size(); i++) {
if (_tabs.at(i)->count()) {
_tabs.at(i)->plot(&p, QRectF(0, y, printer->width(), gh));
_tabs.at(i)->plot(&p, QRectF(0, y, printer->width(), gh),
ratio);
y += gh + sp;
}
}
@ -1162,23 +1199,26 @@ void GUI::showGraphGrids(bool show)
void GUI::loadMap()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Load map"),
QString(), tr("Map files (*.map *.tba *.tar)"));
QString fileName = QFileDialog::getOpenFileName(this, tr("Open map file"),
QString(), MapList::formats());
if (fileName.isEmpty())
return;
if (_ml->loadMap(fileName)) {
QAction *a = new QAction(_ml->maps().last()->name(), this);
a->setCheckable(true);
a->setActionGroup(_mapsActionGroup);
_mapsSignalMapper->setMapping(a, _ml->maps().size() - 1);
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
_mapActions.append(a);
_mapMenu->insertAction(_mapsEnd, a);
int count = _ml->maps().count();
if (_ml->loadFile(fileName)) {
for (int i = count; i < _ml->maps().count(); i++) {
QAction *a = new QAction(_ml->maps().at(i)->name(), this);
a->setCheckable(true);
a->setActionGroup(_mapsActionGroup);
_mapsSignalMapper->setMapping(a, i);
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
_mapActions.append(a);
_mapMenu->insertAction(_mapsEnd, a);
}
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
a->activate(QAction::Trigger);
_mapActions.last()->activate(QAction::Trigger);
} else {
QString error = tr("Error loading map:") + "\n\n"
+ fileName + "\n\n" + _ml->errorString();
@ -1544,6 +1584,8 @@ void GUI::writeSettings()
settings.beginGroup(EXPORT_SETTINGS_GROUP);
if (_export.orientation != PAPER_ORIENTATION_DEFAULT)
settings.setValue(PAPER_ORIENTATION_SETTING, _export.orientation);
if (_export.resolution != RESOLUTION_DEFAULT)
settings.setValue(RESOLUTION_SETTING, _export.resolution);
if (_export.paperSize != PAPER_SIZE_DEFAULT)
settings.setValue(PAPER_SIZE_SETTING, _export.paperSize);
if (_export.margins.left() != MARGIN_LEFT_DEFAULT)
@ -1563,6 +1605,8 @@ void GUI::writeSettings()
settings.setValue(PALETTE_COLOR_SETTING, _options.palette.color());
if (_options.palette.shift() != PALETTE_SHIFT_DEFAULT)
settings.setValue(PALETTE_SHIFT_SETTING, _options.palette.shift());
if (_options.mapOpacity != MAP_OPACITY_DEFAULT)
settings.setValue(MAP_OPACITY_SETTING, _options.mapOpacity);
if (_options.trackWidth != TRACK_WIDTH_DEFAULT)
settings.setValue(TRACK_WIDTH_SETTING, _options.trackWidth);
if (_options.routeWidth != ROUTE_WIDTH_DEFAULT)
@ -1577,10 +1621,30 @@ void GUI::writeSettings()
settings.setValue(PATH_AA_SETTING, _options.pathAntiAliasing);
if (_options.graphAntiAliasing != GRAPH_AA_DEFAULT)
settings.setValue(GRAPH_AA_SETTING, _options.graphAntiAliasing);
if (_options.elevationFilter != ELEVATION_FILTER_DEFAULT)
settings.setValue(ELEVATION_FILTER_SETTING, _options.elevationFilter);
if (_options.speedFilter != SPEED_FILTER_DEFAULT)
settings.setValue(SPEED_FILTER_SETTING, _options.speedFilter);
if (_options.heartRateFilter != HEARTRATE_FILTER_DEFAULT)
settings.setValue(HEARTRATE_FILTER_SETTING, _options.heartRateFilter);
if (_options.cadenceFilter != CADENCE_FILTER_DEFAULT)
settings.setValue(CADENCE_FILTER_SETTING, _options.cadenceFilter);
if (_options.powerFilter != POWER_FILTER_DEFAULT)
settings.setValue(POWER_FILTER_SETTING, _options.powerFilter);
if (_options.outlierEliminate != OUTLIER_ELIMINATE_DEFAULT)
settings.setValue(OUTLIER_ELIMINATE_SETTING, _options.outlierEliminate);
if (_options.pauseSpeed != PAUSE_SPEED_DEFAULT)
settings.setValue(PAUSE_SPEED_SETTING, _options.pauseSpeed);
if (_options.pauseInterval != PAUSE_INTERVAL_DEFAULT)
settings.setValue(PAUSE_INTERVAL_SETTING, _options.pauseInterval);
if (_options.poiRadius != POI_RADIUS_DEFAULT)
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
if (_options.useOpenGL != USE_OPENGL_DEFAULT)
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
if (_options.pixmapCache != PIXMAP_CACHE_DEFAULT)
settings.setValue(PIXMAP_CACHE_SETTING, _options.pixmapCache);
if (_options.hiresPrint != HIRES_PRINT_DEFAULT)
settings.setValue(HIRES_PRINT_SETTING, _options.hiresPrint);
if (_options.printName != PRINT_NAME_DEFAULT)
settings.setValue(PRINT_NAME_SETTING, _options.printName);
if (_options.printDate != PRINT_DATE_DEFAULT)
@ -1720,6 +1784,8 @@ void GUI::readSettings()
settings.beginGroup(EXPORT_SETTINGS_GROUP);
_export.orientation = (QPrinter::Orientation) settings.value(
PAPER_ORIENTATION_SETTING, PAPER_ORIENTATION_DEFAULT).toInt();
_export.resolution = settings.value(RESOLUTION_SETTING, RESOLUTION_DEFAULT)
.toInt();
_export.paperSize = (QPrinter::PaperSize) settings.value(PAPER_SIZE_SETTING,
PAPER_SIZE_DEFAULT).toInt();
qreal ml = settings.value(MARGIN_LEFT_SETTING, MARGIN_LEFT_DEFAULT)
@ -1740,6 +1806,8 @@ void GUI::readSettings()
qreal ps = settings.value(PALETTE_SHIFT_SETTING, PALETTE_SHIFT_DEFAULT)
.toDouble();
_options.palette = Palette(pc, ps);
_options.mapOpacity = settings.value(MAP_OPACITY_SETTING,
MAP_OPACITY_DEFAULT).toInt();
_options.trackWidth = settings.value(TRACK_WIDTH_SETTING,
TRACK_WIDTH_DEFAULT).toInt();
_options.routeWidth = settings.value(ROUTE_WIDTH_SETTING,
@ -1754,10 +1822,30 @@ void GUI::readSettings()
GRAPH_WIDTH_DEFAULT).toInt();
_options.graphAntiAliasing = settings.value(GRAPH_AA_SETTING,
GRAPH_AA_DEFAULT).toBool();
_options.elevationFilter = settings.value(ELEVATION_FILTER_SETTING,
ELEVATION_FILTER_DEFAULT).toInt();
_options.speedFilter = settings.value(SPEED_FILTER_SETTING,
SPEED_FILTER_DEFAULT).toInt();
_options.heartRateFilter = settings.value(HEARTRATE_FILTER_SETTING,
HEARTRATE_FILTER_DEFAULT).toInt();
_options.cadenceFilter = settings.value(CADENCE_FILTER_SETTING,
CADENCE_FILTER_DEFAULT).toInt();
_options.powerFilter = settings.value(POWER_FILTER_SETTING,
POWER_FILTER_DEFAULT).toInt();
_options.outlierEliminate = settings.value(OUTLIER_ELIMINATE_SETTING,
OUTLIER_ELIMINATE_DEFAULT).toBool();
_options.pauseSpeed = settings.value(PAUSE_SPEED_SETTING,
PAUSE_SPEED_DEFAULT).toFloat();
_options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING,
PAUSE_INTERVAL_DEFAULT).toInt();
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
.toInt();
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
.toBool();
_options.pixmapCache = settings.value(PIXMAP_CACHE_SETTING,
PIXMAP_CACHE_DEFAULT).toInt();
_options.hiresPrint = settings.value(HIRES_PRINT_SETTING,
HIRES_PRINT_DEFAULT).toBool();
_options.printName = settings.value(PRINT_NAME_SETTING, PRINT_NAME_DEFAULT)
.toBool();
_options.printDate = settings.value(PRINT_DATE_SETTING, PRINT_DATE_DEFAULT)
@ -1774,6 +1862,7 @@ void GUI::readSettings()
SEPARATE_GRAPH_PAGE_DEFAULT).toBool();
_pathView->setPalette(_options.palette);
_pathView->setMapOpacity(_options.mapOpacity);
_pathView->setTrackWidth(_options.trackWidth);
_pathView->setRouteWidth(_options.routeWidth);
_pathView->setTrackStyle(_options.trackStyle);
@ -1791,8 +1880,19 @@ void GUI::readSettings()
_tabs.at(i)->useOpenGL(true);
}
Track::setElevationFilter(_options.elevationFilter);
Track::setSpeedFilter(_options.speedFilter);
Track::setHeartRateFilter(_options.heartRateFilter);
Track::setCadenceFilter(_options.cadenceFilter);
Track::setPowerFilter(_options.powerFilter);
Track::setOutlierElimination(_options.outlierEliminate);
Track::setPauseSpeed(_options.pauseSpeed);
Track::setPauseInterval(_options.pauseInterval);
_poi->setRadius(_options.poiRadius);
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
settings.endGroup();
}

View File

@ -121,8 +121,6 @@ private:
void readSettings();
void writeSettings();
const QString fileFormats() const;
void keyPressEvent(QKeyEvent *event);
void closeEvent(QCloseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);

View File

@ -10,7 +10,7 @@
#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 SHOW_GRAPHS_ICON ":/icons/office-chart-line.png"
#define QUIT_ICON ":/icons/application-exit.png"
#define RELOAD_FILE_ICON ":/icons/view-refresh.png"
#define NEXT_FILE_ICON ":/icons/arrow-right.png"
@ -24,5 +24,6 @@
#define POI_ICON ":/icons/flag_48.png"
#define SYSTEM_ICON ":/icons/system-run.png"
#define PRINT_EXPORT_ICON ":/icons/document-print-preview.png"
#define DATA_ICON ":/icons/view-filter.png"
#endif /* ICONS_H */

View File

@ -120,7 +120,7 @@ bool IGCParser::readHRecord(const char *line, int len)
return true;
}
bool IGCParser::readBRecord(const char *line, int len)
bool IGCParser::readBRecord(TrackData &track, const char *line, int len)
{
qreal lat, lon, ele;
QTime time;
@ -156,12 +156,12 @@ bool IGCParser::readBRecord(const char *line, int len)
Trackpoint t(Coordinates(lon, lat));
t.setTimestamp(QDateTime(_date, _time, Qt::UTC));
t.setElevation(ele);
_tracks.last().append(t);
track.append(t);
return true;
}
bool IGCParser::readCRecord(const char *line, int len)
bool IGCParser::readCRecord(RouteData &route, const char *line, int len)
{
qreal lat, lon;
@ -183,14 +183,16 @@ bool IGCParser::readCRecord(const char *line, int len)
Waypoint w(Coordinates(lon, lat));
w.setName(QString(ba.trimmed()));
_routes.last().append(w);
route.append(w);
}
return true;
}
bool IGCParser::loadFile(QFile *file)
bool IGCParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
Q_UNUSED(waypoints);
qint64 len;
char line[76 + 2 + 1 + 1];
bool route = false, track = false;
@ -221,11 +223,11 @@ bool IGCParser::loadFile(QFile *file)
return false;
} else if (line[0] == 'C') {
if (route) {
if (!readCRecord(line, len))
if (!readCRecord(routes.last() ,line, len))
return false;
} else {
route = true;
_routes.append(RouteData());
routes.append(RouteData());
}
} else if (line[0] == 'B') {
if (_date.isNull()) {
@ -233,11 +235,11 @@ bool IGCParser::loadFile(QFile *file)
return false;
}
if (!track) {
_tracks.append(TrackData());
tracks.append(TrackData());
_time = QTime(0, 0);
track = true;
}
if (!readBRecord(line, len))
if (!readBRecord(tracks.last(), line, len))
return false;
}
}

View File

@ -9,19 +9,18 @@
class IGCParser : public Parser
{
public:
IGCParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
{_errorLine = 0;}
IGCParser() : _errorLine(0) {}
~IGCParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints);
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);
bool readBRecord(TrackData &track, const char *line, int len);
bool readCRecord(RouteData &route, const char *line, int len);
int _errorLine;
QString _errorString;

View File

@ -251,20 +251,21 @@ void KMLParser::track(TrackData &track)
_reader.raiseError(mismatchError);
}
void KMLParser::multiGeometry(const QString &name, const QString &desc,
void KMLParser::multiGeometry(QList<TrackData> &tracks,
QList<Waypoint> &waypoints, const QString &name, const QString &desc,
const QDateTime timestamp)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Point") {
_waypoints.append(Waypoint());
Waypoint &w = _waypoints.last();
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();
tracks.append(TrackData());
TrackData &t = tracks.last();
t.setName(name);
t.setDescription(desc);
lineString(t);
@ -273,7 +274,7 @@ void KMLParser::multiGeometry(const QString &name, const QString &desc,
}
}
void KMLParser::placemark()
void KMLParser::placemark(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
{
QString name, desc;
QDateTime timestamp;
@ -286,23 +287,24 @@ void KMLParser::placemark()
else if (_reader.name() == "TimeStamp")
timestamp = timeStamp();
else if (_reader.name() == "MultiGeometry")
multiGeometry(name, desc, timestamp);
multiGeometry(tracks, waypoints, name, desc, timestamp);
else if (_reader.name() == "Point") {
_waypoints.append(Waypoint());
Waypoint &w = _waypoints.last();
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();
} else if (_reader.name() == "LineString"
|| _reader.name() == "LinearRing") {
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();
tracks.append(TrackData());
TrackData &t = tracks.last();
t.setName(name);
t.setDescription(desc);
track(t);
@ -311,58 +313,56 @@ void KMLParser::placemark()
}
}
void KMLParser::folder()
void KMLParser::folder(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Placemark")
placemark();
placemark(tracks, waypoints);
else if (_reader.name() == "Folder")
folder();
folder(tracks, waypoints);
else
_reader.skipCurrentElement();
}
}
void KMLParser::document()
void KMLParser::document(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Placemark")
placemark();
placemark(tracks, waypoints);
else if (_reader.name() == "Folder")
folder();
folder(tracks, waypoints);
else
_reader.skipCurrentElement();
}
}
void KMLParser::kml()
void KMLParser::kml(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Document")
document();
document(tracks, waypoints);
else if (_reader.name() == "Placemark")
placemark();
placemark(tracks, waypoints);
else
_reader.skipCurrentElement();
}
}
bool KMLParser::parse()
bool KMLParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
Q_UNUSED(routes);
_reader.clear();
_reader.setDevice(file);
if (_reader.readNextStartElement()) {
if (_reader.name() == "kml")
kml();
kml(tracks, waypoints);
else
_reader.raiseError("Not a KML file");
}
return !_reader.error();
}
bool KMLParser::loadFile(QFile *file)
{
_reader.clear();
_reader.setDevice(file);
return parse();
}

View File

@ -8,22 +8,20 @@
class KMLParser : public Parser
{
public:
KMLParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints) {}
~KMLParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints);
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 kml(QList<TrackData> &tracks, QList<Waypoint> &waypoints);
void document(QList<TrackData> &tracks, QList<Waypoint> &waypoints);
void folder(QList<TrackData> &tracks, QList<Waypoint> &waypoints);
void placemark(QList<TrackData> &tracks, QList<Waypoint> &waypoints);
void multiGeometry(QList<TrackData> &tracks, QList<Waypoint> &waypoints,
const QString &name, const QString &desc, const QDateTime timestamp);
void track(TrackData &track);
void lineString(TrackData &track);
void point(Waypoint &waypoint);

View File

@ -75,7 +75,7 @@ Coordinates LambertConic::xy2ll(const QPointF &p) const
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;
double q = _q0 - log(fabs(R / _R0)) / _n;
return Coordinates(rad2deg(deg2rad(_cm) + dl / _n), rad2deg(iq(_e, q)));
}

View File

@ -7,9 +7,9 @@ class LatLon : public Projection
{
public:
virtual QPointF ll2xy(const Coordinates &c) const
{return c.toPointF();}
{return QPointF(c.lon(), c.lat());}
virtual Coordinates xy2ll(const QPointF &p) const
{return Coordinates(p);}
{return Coordinates(p.x(), p.y());}
};
#endif // LATLON_H

View File

@ -7,6 +7,7 @@
class QPainter;
class Coordinates;
class RectC;
class Map : public QObject
{
@ -21,12 +22,13 @@ public:
virtual qreal resolution(const QPointF &p) const = 0;
virtual qreal zoom() const = 0;
virtual qreal zoomFit(const QSize &size, const QRectF &br) = 0;
virtual qreal zoomFit(const QSize &size, const RectC &br) = 0;
virtual qreal zoomFit(qreal resolution, const Coordinates &c) = 0;
virtual qreal zoomIn() = 0;
virtual qreal zoomOut() = 0;
virtual QPointF ll2xy(const Coordinates &c) const = 0;
virtual Coordinates xy2ll(const QPointF &p) const = 0;
virtual QPointF ll2xy(const Coordinates &c) = 0;
virtual Coordinates xy2ll(const QPointF &p) = 0;
virtual void draw(QPainter *painter, const QRectF &rect) = 0;

View File

@ -6,26 +6,25 @@
#include "maplist.h"
bool MapList::loadListEntry(const QByteArray &line)
Map *MapList::loadListEntry(const QByteArray &line)
{
QList<QByteArray> list = line.split('\t');
if (list.size() != 2)
return false;
return 0;
QByteArray ba1 = list[0].trimmed();
QByteArray ba2 = list[1].trimmed();
if (ba1.isEmpty() || ba2.isEmpty())
return false;
return 0;
_maps.append(new OnlineMap(QString::fromUtf8(ba1.data(), ba1.size()),
QString::fromLatin1(ba2.data(), ba2.size()), this));
return true;
return new OnlineMap(QString::fromUtf8(ba1.data(), ba1.size()),
QString::fromLatin1(ba2.data(), ba2.size()), this);
}
bool MapList::loadList(const QString &path)
{
QFile file(path);
QList<Map*> maps;
if (!file.open(QFile::ReadOnly | QFile::Text)) {
_errorString = file.errorString();
@ -36,65 +35,102 @@ bool MapList::loadList(const QString &path)
while (!file.atEnd()) {
ln++;
QByteArray line = file.readLine();
Map *map = loadListEntry(line);
if (!loadListEntry(line)) {
if (map)
maps.append(map);
else {
for (int i = 0; i < maps.count(); i++)
delete maps.at(i);
_errorString = QString("Invalid map list entry on line %1.")
.arg(QString::number(ln));
return false;
}
}
_maps += maps;
return true;
}
bool MapList::loadMap(const QString &path)
{
OfflineMap *map = new OfflineMap(path, this);
if (map->isValid()) {
_maps.append(map);
return true;
} else {
_errorString = map->errorString();
delete map;
return false;
}
}
bool MapList::loadTba(const QString &path)
{
Atlas *atlas = new Atlas(path, this);
if (atlas->isValid()) {
_maps.append(atlas);
return true;
} else {
_errorString = atlas->errorString();
delete atlas;
return false;
}
}
bool MapList::loadTar(const QString &path)
{
Atlas *atlas = new Atlas(path, this);
if (atlas->isValid()) {
_maps.append(atlas);
return true;
} else {
_errorString = atlas->errorString();
delete atlas;
OfflineMap *map = new OfflineMap(path, this);
if (map->isValid()) {
_maps.append(map);
return true;
} else {
qWarning("%s: %s", qPrintable(path), qPrintable(_errorString));
qWarning("%s: %s", qPrintable(path),
qPrintable(map->errorString()));
_errorString = "Not a map/atlas file";
delete map;
return false;
}
}
}
bool MapList::loadFile(const QString &path)
{
QFileInfo fi(path);
QString suffix = fi.suffix().toLower();
if (suffix == "map") {
OfflineMap *om = new OfflineMap(path, this);
if (om->isValid()) {
_maps.append(om);
return true;
} else {
_errorString = om->errorString();
delete om;
return false;
}
} else if (suffix == "tba") {
Atlas *atlas = new Atlas(path, this);
if (atlas->isValid()) {
_maps.append(atlas);
return true;
} else {
_errorString = atlas->errorString();
delete atlas;
return false;
}
} else if (suffix == "tar") {
Atlas *atlas = new Atlas(path, this);
if (atlas->isValid()) {
_maps.append(atlas);
return true;
} else {
_errorString = atlas->errorString();
delete atlas;
OfflineMap *om = new OfflineMap(path, this);
if (om->isValid()) {
_maps.append(om);
return true;
} else {
qWarning("%s: %s", qPrintable(path), qPrintable(_errorString));
qWarning("%s: %s", qPrintable(path),
qPrintable(om->errorString()));
_errorString = "Not a map/atlas file";
delete om;
return false;
}
}
} else {
if (suffix == "txt")
return loadList(path);
else if (suffix == "map")
return loadMap(path);
else if (suffix == "tba")
return loadTba(path);
else if (suffix == "tar")
return loadTar(path);
else {
_errorString = "Not a map/atlas file";
return false;
}
}
QString MapList::formats()
{
return tr("Map files (*.map *.tba *.tar)") + ";;"
+ tr("URL list files (*.txt)");
}
QStringList MapList::filter()
{
QStringList filter;
filter << "*.map" << "*.tba" << "*.tar" << "*.txt";
return filter;
}

View File

@ -12,14 +12,21 @@ class MapList : public QObject
public:
MapList(QObject *parent = 0) : QObject(parent) {}
bool loadMap(const QString &path);
bool loadList(const QString &path);
bool loadFile(const QString &path);
QList<Map*> &maps() {return _maps;}
const QList<Map*> &maps() const {return _maps;}
const QString &errorString() const {return _errorString;}
static QString formats();
static QStringList filter();
private:
bool loadListEntry(const QByteArray &line);
Map *loadListEntry(const QByteArray &line);
bool loadList(const QString &path);
bool loadMap(const QString &path);
bool loadTba(const QString &path);
bool loadTar(const QString &path);
QList<Map*> _maps;
QString _errorString;

View File

@ -115,3 +115,16 @@ void Matrix::zeroize()
for (size_t i = 0; i < _h * _w; i++)
_m[i] = 0;
}
QDebug operator<<(QDebug dbg, const Matrix &matrix)
{
dbg.nospace() << "Matrix(" << endl;
for (size_t i = 0; i < matrix.h(); i++) {
for (size_t j = 0; j < matrix.w(); j++)
dbg << "\t" << matrix.m(i, j);
dbg << endl;
}
dbg << ")";
return dbg.space();
}

View File

@ -3,6 +3,7 @@
#include <cstddef>
#include <cfloat>
#include <QDebug>
class Matrix {
public:
@ -30,4 +31,6 @@ private:
size_t _w;
};
QDebug operator<<(QDebug dbg, const Matrix &matrix);
#endif // MATRIX_H

View File

@ -227,7 +227,7 @@ bool NMEAParser::readEW(const char *data, int len, qreal &lon)
return true;
}
bool NMEAParser::readRMC(const char *line, int len)
bool NMEAParser::readRMC(TrackData &track, const char *line, int len)
{
int col = 1;
const char *vp = line;
@ -280,22 +280,23 @@ bool NMEAParser::readRMC(const char *line, int len)
}
if (!date.isNull()) {
if (_date.isNull() && !_time.isNull() && !_tracks.last().isEmpty())
_tracks.last().last().setTimestamp(QDateTime(date, _time, Qt::UTC));
if (_date.isNull() && !_time.isNull() && !track.isEmpty())
track.last().setTimestamp(QDateTime(date, _time, Qt::UTC));
_date = date;
}
if (valid && !_GGA && !std::isnan(lat) && !std::isnan(lon)) {
Trackpoint t(Coordinates(lon, lat));
Coordinates c(lon, lat);
if (valid && !_GGA && c.isValid()) {
Trackpoint t(c);
if (!_date.isNull() && !time.isNull())
t.setTimestamp(QDateTime(_date, time, Qt::UTC));
_tracks.last().append(t);
track.append(t);
}
return true;
}
bool NMEAParser::readGGA(const char *line, int len)
bool NMEAParser::readGGA(TrackData &track, const char *line, int len)
{
int col = 1;
const char *vp = line;
@ -356,13 +357,14 @@ bool NMEAParser::readGGA(const char *line, int len)
return false;
}
if (!std::isnan(lat) && !std::isnan(lon)) {
Trackpoint t(Coordinates(lon, lat));
Coordinates c(lon, lat);
if (c.isValid()) {
Trackpoint t(c);
if (!(_time.isNull() || _date.isNull()))
t.setTimestamp(QDateTime(_date, _time, Qt::UTC));
if (!std::isnan(ele))
t.setElevation(ele - gh);
_tracks.last().append(t);
track.append(t);
_GGA = true;
}
@ -370,7 +372,7 @@ bool NMEAParser::readGGA(const char *line, int len)
return true;
}
bool NMEAParser::readWPL(const char *line, int len)
bool NMEAParser::readWPL(QList<Waypoint> &waypoints, const char *line, int len)
{
int col = 1;
const char *vp = line;
@ -411,10 +413,11 @@ bool NMEAParser::readWPL(const char *line, int len)
return false;
}
if (!std::isnan(lat) && !std::isnan(lon)) {
Waypoint w(Coordinates(lon, lat));
Coordinates c(lon, lat);
if (c.isValid()) {
Waypoint w(c);
w.setName(name);
_waypoints.append(w);
waypoints.append(w);
}
return true;
@ -474,8 +477,10 @@ bool NMEAParser::readZDA(const char *line, int len)
return true;
}
bool NMEAParser::loadFile(QFile *file)
bool NMEAParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
Q_UNUSED(routes);
qint64 len;
char line[80 + 2 + 1 + 1];
@ -486,7 +491,8 @@ bool NMEAParser::loadFile(QFile *file)
_time = QTime();
_GGA = false;
_tracks.append(TrackData());
tracks.append(TrackData());
TrackData &track = tracks.last();
while (!file->atEnd()) {
len = file->readLine(line, sizeof(line));
@ -501,13 +507,13 @@ bool NMEAParser::loadFile(QFile *file)
if (validSentence(line, len)) {
if (!memcmp(line + 3, "RMC,", 4)) {
if (!readRMC(line + 7, len))
if (!readRMC(track, line + 7, len))
return false;
} else if (!memcmp(line + 3, "GGA,", 4)) {
if (!readGGA(line + 7, len))
if (!readGGA(track, line + 7, len))
return false;
} else if (!memcmp(line + 3, "WPL,", 4)) {
if (!readWPL(line + 7, len))
if (!readWPL(waypoints, line + 7, len))
return false;
} else if (!memcmp(line + 3, "ZDA,", 4)) {
if (!readZDA(line + 7, len))
@ -518,7 +524,7 @@ bool NMEAParser::loadFile(QFile *file)
_errorLine++;
}
if (!_tracks.last().size() && !_waypoints.size()) {
if (!tracks.last().size() && !waypoints.size()) {
_errorString = "No usable NMEA sentence found";
return false;
}

View File

@ -8,12 +8,11 @@
class NMEAParser : public Parser
{
public:
NMEAParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints)
{_errorLine = 0; _GGA = false;}
NMEAParser() : _errorLine(0), _GGA(false) {}
~NMEAParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints);
QString errorString() const {return _errorString;}
int errorLine() const {return _errorLine;}
@ -27,9 +26,9 @@ private:
bool readAltitude(const char *data, int len, qreal &ele);
bool readGeoidHeight(const char *data, int len, qreal &gh);
bool readRMC(const char *line, int len);
bool readGGA(const char *line, int len);
bool readWPL(const char *line, int len);
bool readRMC(TrackData &track, const char *line, int len);
bool readGGA(TrackData &track, const char *line, int len);
bool readWPL(QList<Waypoint> &waypoints, const char *line, int len);
bool readZDA(const char *line, int len);
int _errorLine;

20
src/oddspinbox.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "oddspinbox.h"
OddSpinBox::OddSpinBox(QWidget *parent) : QSpinBox(parent)
{
setSingleStep(2);
setMinimum(1);
}
QValidator::State OddSpinBox::validate(QString &text, int &pos) const
{
Q_UNUSED(pos);
bool ok;
int val;
val = text.toInt(&ok);
if (!ok || val < 0 || val % 2 == 0)
return QValidator::Invalid;
return QValidator::Acceptable;
}

15
src/oddspinbox.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef ODDSPINBOX_H
#define ODDSPINBOX_H
#include <QSpinBox>
class OddSpinBox : public QSpinBox
{
public:
OddSpinBox(QWidget *parent = 0);
protected:
QValidator::State validate(QString &text, int &pos) const;
};
#endif // ODDSPINBOX_H

View File

@ -18,45 +18,12 @@
#include "transversemercator.h"
#include "utm.h"
#include "lambertconic.h"
#include "albersequal.h"
#include "ozf.h"
#include "rectc.h"
#include "offlinemap.h"
// Abridged Molodensky transformation
static Coordinates toWGS84(Coordinates c, const Datum &datum)
{
Ellipsoid WGS84(WGS84_RADIUS, WGS84_FLATTENING);
double dX = datum.dx();
double dY = datum.dy();
double dZ = datum.dz();
double slat = sin(deg2rad(c.lat()));
double clat = cos(deg2rad(c.lat()));
double slon = sin(deg2rad(c.lon()));
double clon = cos(deg2rad(c.lon()));
double ssqlat = slat * slat;
double from_f = datum.ellipsoid().flattening();
double df = WGS84.flattening() - from_f;
double from_a = datum.ellipsoid().radius();
double da = WGS84.radius() - from_a;
double from_esq = datum.ellipsoid().flattening()
* (2.0 - datum.ellipsoid().flattening());
double adb = 1.0 / (1.0 - from_f);
double rn = from_a / sqrt(1 - from_esq * ssqlat);
double rm = from_a * (1 - from_esq) / pow((1 - from_esq * ssqlat), 1.5);
double from_h = 0.0;
double dlat = (-dX * slat * clon - dY * slat * slon + dZ * clat + da * rn
* from_esq * slat * clat / from_a + +df * (rm * adb + rn / adb) * slat
* clat) / (rm + from_h);
double dlon = (-dX * slon + dY * clon) / ((rn + from_h) * clat);
return Coordinates(c.lon() + rad2deg(dlon), c.lat() + rad2deg(dlat));
}
int OfflineMap::parse(QIODevice &device, QList<ReferencePoint> &points,
QString &projection, ProjectionSetup &setup, QString &datum)
{
@ -101,14 +68,18 @@ int OfflineMap::parse(QIODevice &device, QList<ReferencePoint> &points,
qreal lonm = list.at(10).trimmed().toFloat(&res);
if (!res)
ll = false;
if (ll && list.at(8).trimmed() == "S")
if (ll && list.at(8).trimmed() == "S") {
latd = -latd;
if (ll && list.at(11).trimmed() == "W")
latm = -latm;
}
if (ll && list.at(11).trimmed() == "W") {
lond = -lond;
lonm = -lonm;
}
int zone = list.at(13).trimmed().toInt(&res);
setup.zone = list.at(13).trimmed().toInt(&res);
if (!res)
zone = 0;
setup.zone = 0;
qreal ppx = list.at(14).trimmed().toFloat(&res);
if (!res)
pp = false;
@ -116,20 +87,24 @@ int OfflineMap::parse(QIODevice &device, QList<ReferencePoint> &points,
if (!res)
pp = false;
if (list.at(16).trimmed() == "S")
zone = -zone;
setup.zone = -setup.zone;
ReferencePoint p;
p.xy = QPoint(x, y);
if (ll) {
p.ll = Coordinates(lond + lonm/60.0, latd + latm/60.0);
points.append(p);
if (p.ll.isValid())
points.append(p);
else
return ln;
} else if (pp) {
p.pp = QPointF(ppx, ppy);
setup.zone = zone;
points.append(p);
} else
return ln;
} else if (key == "IWH") {
if (list.count() < 4)
return ln;
int w = list.at(2).trimmed().toInt(&res);
if (!res)
return ln;
@ -138,6 +113,8 @@ int OfflineMap::parse(QIODevice &device, QList<ReferencePoint> &points,
return ln;
_size = QSize(w, h);
} else if (key == "Map Projection") {
if (list.count() < 2)
return ln;
projection = list.at(1);
} else if (key == "Projection Setup") {
if (list.count() < 8)
@ -206,12 +183,20 @@ bool OfflineMap::createProjection(const QString &datum,
return false;
}
if (setup.latitudeOrigin < -90.0 || setup.latitudeOrigin > 90.0
|| setup.longitudeOrigin < -180.0 || setup.longitudeOrigin > 180.0
|| setup.standardParallel1 < -90.0 || setup.standardParallel1 > 90.0
|| setup.standardParallel2 < -90.0 || setup.standardParallel2 > 90.0) {
_errorString = "Invalid projection setup";
return false;
}
if (projection == "Mercator")
_projection = new Mercator();
else if (projection == "Transverse Mercator")
_projection = new TransverseMercator(d.ellipsoid(),
setup.longitudeOrigin, setup.scale, setup.falseEasting,
setup.falseNorthing);
setup.latitudeOrigin, setup.longitudeOrigin, setup.scale,
setup.falseEasting, setup.falseNorthing);
else if (projection == "Latitude/Longitude")
_projection = new LatLon();
else if (projection == "Lambert Conformal Conic")
@ -219,6 +204,10 @@ bool OfflineMap::createProjection(const QString &datum,
setup.standardParallel1, setup.standardParallel2,
setup.latitudeOrigin, setup.longitudeOrigin, setup.scale,
setup.falseEasting, setup.falseNorthing);
else if (projection == "Albers Equal Area")
_projection = new AlbersEqual(d.ellipsoid(), setup.standardParallel1,
setup.standardParallel2, setup.latitudeOrigin, setup.longitudeOrigin,
setup.falseEasting, setup.falseNorthing);
else if (projection == "(UTM) Universal Transverse Mercator") {
if (setup.zone)
_projection = new UTM(d.ellipsoid(), setup.zone);
@ -228,44 +217,90 @@ bool OfflineMap::createProjection(const QString &datum,
_errorString = "Can not determine UTM zone";
return false;
}
} else {
} else if (projection == "(NZTM2) New Zealand TM 2000")
_projection = new TransverseMercator(d.ellipsoid(), 0, 173.0, 0.9996,
1600000, 10000000);
else if (projection == "(BNG) British National Grid")
_projection = new TransverseMercator(d.ellipsoid(), 49, -2, 0.999601,
400000, -100000);
else if (projection == "(IG) Irish Grid")
_projection = new TransverseMercator(d.ellipsoid(), 53.5, -8, 1.000035,
200000, 250000);
else if (projection == "(SG) Swedish Grid")
_projection = new TransverseMercator(d.ellipsoid(), 0, 15.808278, 1,
1500000, 0);
else if (projection == "(I) France Zone I")
_projection = new LambertConic(d.ellipsoid(), 48.598523, 50.395912,
49.5, 2.337229, 1 /*0.99987734*/, 600000, 1200000);
else if (projection == "(II) France Zone II")
_projection = new LambertConic(d.ellipsoid(), 45.898919, 47.696014,
46.8, 2.337229, 1 /*0.99987742*/, 600000, 2200000);
else if (projection == "(III) France Zone III")
_projection = new LambertConic(d.ellipsoid(), 43.199291, 44.996094,
44.1, 2.337229, 1 /*0.99987750*/, 600000, 3200000);
else if (projection == "(IV) France Zone IV")
_projection = new LambertConic(d.ellipsoid(), 41.560388, 42.767663,
42.165, 2.337229, 1 /*0.99994471*/, 234.358, 4185861.369);
else if (projection == "(VICGRID) Victoria Australia")
_projection = new LambertConic(d.ellipsoid(), -36, -38, -37, 145, 1,
2500000, 4500000);
else if (projection == "(VG94) VICGRID94 Victoria Australia")
_projection = new LambertConic(d.ellipsoid(), -36, -38, -37, 145, 1,
2500000, 2500000);
else {
_errorString = QString("%1: Unknown map projection").arg(projection);
return false;
}
for (int i = 0; i < points.size(); i++) {
if (points.at(i).ll.isNull()) {
if (d.isWGS84())
points[i].ll = _projection->xy2ll(points.at(i).pp);
else
points[i].ll = toWGS84(_projection->xy2ll(points.at(i).pp), d);
} else {
if (!d.isWGS84())
points[i].ll = toWGS84(points[i].ll, d);
}
if (points.at(i).ll.isNull())
points[i].ll = d.toWGS84(_projection->xy2ll(points.at(i).pp));
else
points[i].ll = d.toWGS84(points[i].ll);
}
return true;
}
bool OfflineMap::computeTransformation(const QList<ReferencePoint> &points)
bool OfflineMap::simpleTransformation(const QList<ReferencePoint> &points)
{
Q_ASSERT(points.count() >= 2);
if (points.at(0).xy.x() == points.at(1).xy.x()
|| points.at(0).xy.y() == points.at(1).xy.y()) {
_errorString = "Invalid reference points tuple";
return false;
}
QPointF p0(_projection->ll2xy(points.at(0).ll));
QPointF p1(_projection->ll2xy(points.at(1).ll));
qreal dX, dY, lat0, lon0;
dX = (p0.x() - p1.x()) / (points.at(0).xy.x() - points.at(1).xy.x());
dY = (p1.y() - p0.y()) / (points.at(1).xy.y() - points.at(0).xy.y());
lat0 = p0.y() - points.at(0).xy.y() * dY;
lon0 = p1.x() - points.at(1).xy.x() * dX;
_transform = QTransform(1.0/dX, 0, 0, 1.0/dY, -lon0/dX, -lat0/dY);
_inverted = _transform.inverted();
return true;
}
bool OfflineMap::affineTransformation(const QList<ReferencePoint> &points)
{
Matrix c(3, 2);
c.zeroize();
for (size_t j = 0; j < c.w(); j++) {
for (size_t k = 0; k < c.h(); k++) {
for (int i = 0; i < points.size(); i++) {
for (size_t i = 0; i < c.h(); i++) {
for (size_t j = 0; j < c.w(); j++) {
for (int k = 0; k < points.size(); k++) {
double f[3], t[2];
QPointF p = _projection->ll2xy(points.at(i).ll);
QPointF p = _projection->ll2xy(points.at(k).ll);
f[0] = p.x();
f[1] = p.y();
f[2] = 1.0;
t[0] = points.at(i).xy.x();
t[1] = points.at(i).xy.y();
c.m(k,j) += f[k] * t[j];
t[0] = points.at(k).xy.x();
t[1] = points.at(k).xy.y();
c.m(i,j) += f[i] * t[j];
}
}
}
@ -297,6 +332,16 @@ bool OfflineMap::computeTransformation(const QList<ReferencePoint> &points)
return true;
}
bool OfflineMap::computeTransformation(const QList<ReferencePoint> &points)
{
Q_ASSERT(points.size() >= 2);
if (points.size() == 2)
return simpleTransformation(points);
else
return affineTransformation(points);
}
bool OfflineMap::computeResolution(QList<ReferencePoint> &points)
{
Q_ASSERT(points.count() >= 2);
@ -347,22 +392,20 @@ bool OfflineMap::getImageInfo(const QString &path)
return false;
}
QString suffix = ii.suffix().toLower();
if (suffix == "ozf3" || suffix == "ozf4") {
_errorString = QString("%1: Obfuscated image files not supported")
.arg(QFileInfo(_imgPath).fileName());
return false;
} else if (suffix == "ozf2") {
_ozf.load(_imgPath);
_size = _ozf.size();
if (OZF::isOZF(_imgPath)) {
if (!_ozf.load(_imgPath)) {
_errorString = QString("%1: Error loading OZF file")
.arg(QFileInfo(_imgPath).fileName());
return false;
}
} else {
QImageReader img(_imgPath);
_size = img.size();
}
if (!_size.isValid()) {
_errorString = QString("%1: Error reading map image")
.arg(QFileInfo(_imgPath).fileName());
return false;
if (!_size.isValid()) {
_errorString = QString("%1: Error reading map image")
.arg(QFileInfo(_imgPath).fileName());
return false;
}
}
return true;
@ -419,7 +462,9 @@ OfflineMap::OfflineMap(const QString &fileName, QObject *parent)
_valid = false;
_img = 0;
_projection = 0;
_resolution = 0;
_resolution = 0.0;
_zoom = 0;
_scale = QPointF(1.0, 1.0);
if (suffix == "tar") {
if (!_tar.load(fileName)) {
@ -485,6 +530,9 @@ OfflineMap::OfflineMap(const QString &fileName, Tar &tar, QObject *parent)
_valid = false;
_img = 0;
_projection = 0;
_resolution = 0.0;
_zoom = 0;
_scale = QPointF(1.0, 1.0);
QFileInfo map(fi.absolutePath());
QFileInfo layer(map.absolutePath());
@ -560,7 +608,7 @@ void OfflineMap::drawTiled(QPainter *painter, const QRectF &rect)
int x = tl.x() + i * _tileSize.width();
int y = tl.y() + j * _tileSize.height();
if (!QRectF(QPointF(x, y), _ozf.tileSize()).intersects(bounds())) {
if (!QRectF(QPointF(x, y), _tileSize).intersects(bounds())) {
painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white);
continue;
}
@ -603,22 +651,24 @@ void OfflineMap::drawOZF(QPainter *painter, const QRectF &rect)
int y = tl.y() + j * _ozf.tileSize().height();
if (!QRectF(QPointF(x, y), _ozf.tileSize()).intersects(bounds())) {
painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white);
painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()),
Qt::white);
continue;
}
QPixmap pixmap;
QString key = _ozf.fileName() + "/" + QString::number(x)
+ "_" + QString::number(y);
QString key = _ozf.fileName() + "/" + QString::number(_zoom) + "_"
+ QString::number(x) + "_" + QString::number(y);
if (!QPixmapCache::find(key, &pixmap)) {
pixmap = _ozf.tile(x, y);
pixmap = _ozf.tile(_zoom, x, y);
if (!pixmap.isNull())
QPixmapCache::insert(key, pixmap);
}
if (pixmap.isNull()) {
qWarning("%s: error loading tile image", qPrintable(key));
painter->fillRect(QRectF(QPoint(x, y), _tileSize), Qt::white);
painter->fillRect(QRectF(QPoint(x, y), _ozf.tileSize()),
Qt::white);
} else
painter->drawPixmap(QPoint(x, y), pixmap);
}
@ -630,9 +680,9 @@ void OfflineMap::drawImage(QPainter *painter, const QRectF &rect)
if (!_img || _img->isNull())
painter->fillRect(rect, Qt::white);
else {
QPoint p = rect.topLeft().toPoint();
QImage crop = _img->copy(QRect(p, rect.size().toSize()));
painter->drawImage(rect.topLeft(), crop);
QRect r(rect.toRect());
painter->drawImage(r.left(), r.top(), *_img, r.left(), r.top(),
r.width(), r.height());
}
}
@ -645,3 +695,101 @@ void OfflineMap::draw(QPainter *painter, const QRectF &rect)
else
drawImage(painter, rect);
}
QPointF OfflineMap::ll2xy(const Coordinates &c)
{
if (_ozf.isOpen()) {
QPointF p(_transform.map(_projection->ll2xy(c)));
return QPointF(p.x() * _scale.x(), p.y() * _scale.y());
} else
return _transform.map(_projection->ll2xy(c));
}
Coordinates OfflineMap::xy2ll(const QPointF &p)
{
if (_ozf.isOpen()) {
return _projection->xy2ll(_inverted.map(QPointF(p.x() / _scale.x(),
p.y() / _scale.y())));
} else
return _projection->xy2ll(_inverted.map(p));
}
QRectF OfflineMap::bounds() const
{
if (_ozf.isOpen())
return QRectF(QPointF(0, 0), _ozf.size(_zoom));
else
return QRectF(QPointF(0, 0), _size);
}
qreal OfflineMap::resolution(const QPointF &p) const
{
Q_UNUSED(p);
if (_ozf.isOpen())
return _resolution / ((_scale.x() + _scale.y()) / 2.0);
else
return _resolution;
}
qreal OfflineMap::zoomFit(const QSize &size, const RectC &br)
{
if (_ozf.isOpen()) {
if (!br.isValid())
rescale(0);
else {
QRect sbr(QRectF(_transform.map(_projection->ll2xy(br.topLeft())),
_transform.map(_projection->ll2xy(br.bottomRight())))
.toRect().normalized());
for (int i = 0; i < _ozf.zooms(); i++) {
rescale(i);
if (sbr.size().width() * _scale.x() <= size.width()
&& sbr.size().height() * _scale.y() <= size.height())
break;
}
}
}
return _zoom;
}
qreal OfflineMap::zoomFit(qreal resolution, const Coordinates &c)
{
Q_UNUSED(c);
if (_ozf.isOpen()) {
for (int i = 0; i < _ozf.zooms(); i++) {
rescale(i);
qreal sr = _resolution / ((_scale.x() + _scale.y()) / 2.0);
if (sr >= resolution)
break;
}
}
return _zoom;
}
qreal OfflineMap::zoomIn()
{
if (_ozf.isOpen())
rescale(qMax(_zoom - 1, 0));
return _zoom;
}
qreal OfflineMap::zoomOut()
{
if (_ozf.isOpen())
rescale(qMin(_zoom + 1, _ozf.zooms() - 1));
return _zoom;
}
void OfflineMap::rescale(int zoom)
{
_zoom = zoom;
_scale = QPointF(
(qreal)_ozf.size(_zoom).width() / (qreal)_ozf.size(0).width(),
(qreal)_ozf.size(_zoom).height() / (qreal)_ozf.size(0).height());
}

View File

@ -23,18 +23,17 @@ public:
const QString &name() const {return _name;}
QRectF bounds() const {return QRectF(QPointF(0, 0), _size);}
qreal resolution(const QPointF &) const {return _resolution;}
QRectF bounds() const;
qreal resolution(const QPointF &p) const;
qreal zoom() const {return 1.0;}
qreal zoomFit(const QSize &, const QRectF &) {return 1.0;}
qreal zoomIn() {return 1.0;}
qreal zoomOut() {return 1.0;}
qreal zoom() const {return _zoom;}
qreal zoomFit(const QSize &size, const RectC &br);
qreal zoomFit(qreal resolution, const Coordinates &c);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const
{return _transform.map(_projection->ll2xy(c));}
Coordinates xy2ll(const QPointF &p) const
{return _projection->xy2ll(_inverted.map(p));}
QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p);
void draw(QPainter *painter, const QRectF &rect);
@ -52,13 +51,13 @@ public:
{return _transform.map(p);}
private:
typedef struct {
struct ReferencePoint {
QPoint xy;
Coordinates ll;
QPointF pp;
} ReferencePoint;
};
typedef struct {
struct ProjectionSetup {
double latitudeOrigin;
double longitudeOrigin;
double scale;
@ -67,7 +66,7 @@ private:
double standardParallel1;
double standardParallel2;
int zone;
} ProjectionSetup;
};
int parse(QIODevice &device, QList<ReferencePoint> &points,
QString &projection, ProjectionSetup &setup, QString &datum);
@ -76,6 +75,8 @@ private:
bool totalSizeSet();
bool createProjection(const QString &datum, const QString &projection,
const ProjectionSetup &setup, QList<ReferencePoint> &points);
bool simpleTransformation(const QList<ReferencePoint> &points);
bool affineTransformation(const QList<ReferencePoint> &points);
bool computeTransformation(const QList<ReferencePoint> &points);
bool computeResolution(QList<ReferencePoint> &points);
bool getTileInfo(const QStringList &tiles, const QString &path = QString());
@ -85,6 +86,8 @@ private:
void drawOZF(QPainter *painter, const QRectF &rect);
void drawImage(QPainter *painter, const QRectF &rect);
void rescale(int zoom);
QString _name;
bool _valid;
QString _errorString;
@ -101,6 +104,9 @@ private:
QString _imgPath;
QSize _tileSize;
QString _tileName;
int _zoom;
QPointF _scale;
};
#endif // OFFLINEMAP_H

View File

@ -1,6 +1,7 @@
#include <QFileInfo>
#include <QDir>
#include <QPainter>
#include "rectc.h"
#include "downloader.h"
#include "config.h"
#include "rd.h"
@ -25,16 +26,14 @@ static QPoint mercator2tile(const QPointF &m, int z)
return tile;
}
static qreal zoom2scale(int zoom)
{
return (360.0/(qreal)((1<<zoom) * TILE_SIZE));
}
static int scale2zoom(qreal scale)
{
int 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;
return (int)log2(360.0/(scale * (qreal)TILE_SIZE));
}
@ -46,7 +45,7 @@ OnlineMap::OnlineMap(const QString &name, const QString &url, QObject *parent)
_name = name;
_url = url;
_block = false;
_scale = ((360.0/(qreal)(1<<ZOOM_MAX))/(qreal)TILE_SIZE);
_zoom = ZOOM_MAX;
connect(downloader, SIGNAL(finished()), this, SLOT(emitLoaded()));
@ -163,53 +162,68 @@ void OnlineMap::clearCache()
QRectF OnlineMap::bounds() const
{
return scaled(QRectF(QPointF(-180, -180), QSizeF(360, 360)), 1.0/_scale);
return scaled(QRectF(QPointF(-180, -180), QSizeF(360, 360)),
1.0/zoom2scale(_zoom));
}
qreal OnlineMap::zoomFit(const QSize &size, const QRectF &br)
qreal OnlineMap::zoomFit(const QSize &size, const RectC &br)
{
if (br.isNull())
_scale = ((360.0/(qreal)(1<<ZOOM_MAX))/(qreal)TILE_SIZE);
if (!br.isValid())
_zoom = ZOOM_MAX;
else {
Coordinates topLeft(br.topLeft());
Coordinates bottomRight(br.bottomRight());
QRectF tbr(Mercator().ll2xy(topLeft), Mercator().ll2xy(bottomRight));
QRectF tbr(Mercator().ll2xy(br.topLeft()),
Mercator().ll2xy(br.bottomRight()));
QPointF sc(tbr.width() / size.width(), tbr.height() / size.height());
_scale = ((360.0/(qreal)(1<<scale2zoom(qMax(sc.x(), sc.y()))))
/ (qreal)TILE_SIZE);
_zoom = scale2zoom(qMax(sc.x(), sc.y()));
if (_zoom < ZOOM_MIN)
_zoom = ZOOM_MIN;
if (_zoom > ZOOM_MAX)
_zoom = ZOOM_MAX;
}
return _scale;
return _zoom;
}
qreal OnlineMap::zoomFit(qreal resolution, const Coordinates &c)
{
_zoom = (int)(log2((WGS84_RADIUS * 2 * M_PI * cos(deg2rad(c.lat())))
/ resolution) - log2(TILE_SIZE));
if (_zoom < ZOOM_MIN)
_zoom = ZOOM_MIN;
if (_zoom > ZOOM_MAX)
_zoom = ZOOM_MAX;
return _zoom;
}
qreal OnlineMap::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 scale = zoom2scale(_zoom);
return (WGS84_RADIUS * 2 * M_PI * scale / 360.0
* cos(2.0 * atan(exp(deg2rad(-p.y() * scale))) - M_PI/2));
}
qreal OnlineMap::zoomIn()
{
int zoom = qMin(scale2zoom(_scale) + 1, ZOOM_MAX);
_scale = ((360.0/(qreal)(1<<zoom))/(qreal)TILE_SIZE);
return _scale;
_zoom = qMin(_zoom + 1, ZOOM_MAX);
return _zoom;
}
qreal OnlineMap::zoomOut()
{
int zoom = qMax(scale2zoom(_scale) - 1, ZOOM_MIN);
_scale = ((360.0/(qreal)(1<<zoom))/(qreal)TILE_SIZE);
return _scale;
_zoom = qMax(_zoom - 1, ZOOM_MIN);
return _zoom;
}
void OnlineMap::draw(QPainter *painter, const QRectF &rect)
{
int zoom = scale2zoom(_scale);
qreal scale = zoom2scale(_zoom);
QPoint tile = mercator2tile(QPointF(rect.topLeft().x() * _scale,
-rect.topLeft().y() * _scale), zoom);
QPoint tile = mercator2tile(QPointF(rect.topLeft().x() * scale,
-rect.topLeft().y() * scale), _zoom);
QPoint tl = QPoint((int)floor(rect.left() / (qreal)TILE_SIZE)
* TILE_SIZE, (int)floor(rect.top() / TILE_SIZE) * TILE_SIZE);
@ -217,7 +231,7 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect)
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
for (int i = 0; i < ceil(s.width() / TILE_SIZE); i++)
for (int j = 0; j < ceil(s.height() / TILE_SIZE); j++)
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), zoom));
tiles.append(Tile(QPoint(tile.x() + i, tile.y() + j), _zoom));
if (_block)
loadTilesSync(tiles);
@ -232,14 +246,16 @@ void OnlineMap::draw(QPainter *painter, const QRectF &rect)
}
}
QPointF OnlineMap::ll2xy(const Coordinates &c) const
QPointF OnlineMap::ll2xy(const Coordinates &c)
{
qreal scale = zoom2scale(_zoom);
QPointF m = Mercator().ll2xy(c);
return QPointF(m.x() / _scale, m.y() / -_scale);
return QPointF(m.x() / scale, m.y() / -scale);
}
Coordinates OnlineMap::xy2ll(const QPointF &p) const
Coordinates OnlineMap::xy2ll(const QPointF &p)
{
QPointF m(p.x() * _scale, -p.y() * _scale);
qreal scale = zoom2scale(_zoom);
QPointF m(p.x() * scale, -p.y() * scale);
return Mercator().xy2ll(m);
}

View File

@ -18,13 +18,14 @@ public:
QRectF bounds() const;
qreal resolution(const QPointF &p) const;
qreal zoom() const {return _scale;}
qreal zoomFit(const QSize &size, const QRectF &br);
qreal zoom() const {return _zoom;}
qreal zoomFit(const QSize &size, const RectC &br);
qreal zoomFit(qreal resolution, const Coordinates &c);
qreal zoomIn();
qreal zoomOut();
QPointF ll2xy(const Coordinates &c) const;
Coordinates xy2ll(const QPointF &p) const;
QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p);
void draw(QPainter *painter, const QRectF &rect);
@ -45,7 +46,7 @@ private:
void loadTilesAsync(QList<Tile> &list);
void loadTilesSync(QList<Tile> &list);
qreal _scale;
int _zoom;
QString _name;
QString _url;
bool _block;

View File

@ -9,17 +9,21 @@
#include <QGroupBox>
#include <QCheckBox>
#include <QComboBox>
#include <QRadioButton>
#include <QLabel>
#include <QSysInfo>
#include "config.h"
#include "icons.h"
#include "colorbox.h"
#include "stylecombobox.h"
#include "oddspinbox.h"
#include "percentslider.h"
#include "optionsdialog.h"
#define MENU_MARGIN 20
#define MENU_ICON_SIZE 32
QWidget *OptionsDialog::createAppearancePage()
{
_baseColor = new ColorBox();
@ -29,14 +33,39 @@ QWidget *OptionsDialog::createAppearancePage()
_colorOffset->setMaximum(1.0);
_colorOffset->setSingleStep(0.01);
_colorOffset->setValue(_options->palette.shift());
QFormLayout *paletteLayout = new QFormLayout();
paletteLayout->addRow(tr("Base color:"), _baseColor);
paletteLayout->addRow(tr("Palette shift:"), _colorOffset);
#ifndef Q_OS_MAC
QGroupBox *pathsBox = new QGroupBox(tr("Paths"));
pathsBox->setLayout(paletteLayout);
#endif
_mapOpacity = new PercentSlider();
_mapOpacity->setValue(_options->mapOpacity);
QFormLayout *mapLayout = new QFormLayout();
mapLayout->addRow(tr("Map opacity:"), _mapOpacity);
#ifndef Q_OS_MAC
QGroupBox *mapBox = new QGroupBox(tr("Map"));
mapBox->setLayout(mapLayout);
#endif
QWidget *colorTab = new QWidget();
colorTab->setLayout(paletteLayout);
QVBoxLayout *colorTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
QFrame *l0 = new QFrame();
l0->setFrameShape(QFrame::HLine);
l0->setFrameShadow(QFrame::Sunken);
colorTabLayout->addLayout(paletteLayout);
colorTabLayout->addWidget(l0);
colorTabLayout->addLayout(mapLayout);
#else // Q_OS_MAC
colorTabLayout->addWidget(pathsBox);
colorTabLayout->addWidget(mapBox);
#endif // O_OS_MAC
colorTabLayout->addStretch();
colorTab->setLayout(colorTabLayout);
_trackWidth = new QSpinBox();
_trackWidth->setValue(_options->trackWidth);
@ -83,11 +112,12 @@ QWidget *OptionsDialog::createAppearancePage()
pathTabLayout->addWidget(l1);
pathTabLayout->addLayout(routeLayout);
pathTabLayout->addWidget(l2);
#else
#else // Q_OS_MAC
pathTabLayout->addWidget(trackBox);
pathTabLayout->addWidget(routeBox);
#endif
#endif // Q_OS_MAC
pathTabLayout->addLayout(pathAALayout);
pathTabLayout->addStretch();
pathTab->setLayout(pathTabLayout);
_graphWidth = new QSpinBox();
@ -117,6 +147,98 @@ QWidget *OptionsDialog::createAppearancePage()
return appearancePage;
}
QWidget *OptionsDialog::createDataPage()
{
QString filterToolTip = tr("Moving average window size");
_elevationFilter = new OddSpinBox();
_elevationFilter->setValue(_options->elevationFilter);
_elevationFilter->setToolTip(filterToolTip);
_speedFilter = new OddSpinBox();
_speedFilter->setValue(_options->speedFilter);
_speedFilter->setToolTip(filterToolTip);
_heartRateFilter = new OddSpinBox();
_heartRateFilter->setValue(_options->heartRateFilter);
_heartRateFilter->setToolTip(filterToolTip);
_cadenceFilter = new OddSpinBox();
_cadenceFilter->setValue(_options->cadenceFilter);
_cadenceFilter->setToolTip(filterToolTip);
_powerFilter = new OddSpinBox();
_powerFilter->setValue(_options->powerFilter);
_powerFilter->setToolTip(filterToolTip);
QFormLayout *smoothLayout = new QFormLayout();
smoothLayout->addRow(tr("Elevation:"), _elevationFilter);
smoothLayout->addRow(tr("Speed:"), _speedFilter);
smoothLayout->addRow(tr("Heart rate:"), _heartRateFilter);
smoothLayout->addRow(tr("Cadence:"), _cadenceFilter);
smoothLayout->addRow(tr("Power:"), _powerFilter);
#ifndef Q_OS_MAC
QGroupBox *smoothBox = new QGroupBox(tr("Smoothing"));
smoothBox->setLayout(smoothLayout);
#endif // Q_OS_MAC
_outlierEliminate = new QCheckBox(tr("Eliminate GPS outliers"));
_outlierEliminate->setChecked(_options->outlierEliminate);
QFormLayout *outlierLayout = new QFormLayout();
outlierLayout->addWidget(_outlierEliminate);
#ifndef Q_OS_MAC
QGroupBox *outlierBox = new QGroupBox(tr("Outlier elimination"));
outlierBox->setLayout(outlierLayout);
#endif // Q_OS_MAC
QWidget *filterTab = new QWidget();
QVBoxLayout *filterTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC
QLabel *label = new QLabel(tr("Smoothing:"));
QFrame *line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
filterTabLayout->addWidget(label);
filterTabLayout->addLayout(smoothLayout);
filterTabLayout->addWidget(line);
filterTabLayout->addLayout(outlierLayout);
#else // Q_OS_MAC
filterTabLayout->addWidget(smoothBox);
filterTabLayout->addWidget(outlierBox);
#endif // Q_OS_MAC
filterTabLayout->addStretch();
filterTab->setLayout(filterTabLayout);
_pauseSpeed = new QDoubleSpinBox();
_pauseSpeed->setDecimals(1);
_pauseSpeed->setSingleStep(0.1);
_pauseSpeed->setMinimum(0.1);
if (_options->units == Imperial) {
_pauseSpeed->setValue(_options->pauseSpeed * MS2MIH);
_pauseSpeed->setSuffix(UNIT_SPACE + tr("mi/h"));
} else {
_pauseSpeed->setValue(_options->pauseSpeed * MS2KMH);
_pauseSpeed->setSuffix(UNIT_SPACE + tr("km/h"));
}
_pauseInterval = new QSpinBox();
_pauseInterval->setMinimum(1);
_pauseInterval->setSuffix(UNIT_SPACE + tr("s"));
_pauseInterval->setValue(_options->pauseInterval);
QFormLayout *pauseLayout = new QFormLayout();
pauseLayout->addRow(tr("Minimal speed:"), _pauseSpeed);
pauseLayout->addRow(tr("Minimal duration:"), _pauseInterval);
QWidget *pauseTab = new QWidget();
pauseTab->setLayout(pauseLayout);
QTabWidget *filterPage = new QTabWidget();
filterPage->addTab(filterTab, tr("Filtering"));
filterPage->addTab(pauseTab, tr("Pause detection"));
return filterPage;
}
QWidget *OptionsDialog::createPOIPage()
{
_poiRadius = new QDoubleSpinBox();
@ -144,6 +266,36 @@ QWidget *OptionsDialog::createPOIPage()
QWidget *OptionsDialog::createExportPage()
{
_wysiwyg = new QRadioButton(tr("WYSIWYG"));
_hires = new QRadioButton(tr("High-Resolution"));
if (_options->hiresPrint)
_hires->setChecked(true);
else
_wysiwyg->setChecked(true);
QLabel *lw = new QLabel(tr("The printed area is approximately the display"
" area. The map zoom level does not change."));
QLabel *lh = new QLabel(tr("The zoom level will be changed so that"
" the whole content (tracks/waypoints) fits to the printed area and"
" the map resolution is as close as possible to the print resolution."));
QFont f = lw->font();
f.setPointSize(f.pointSize() - 1);
lw->setWordWrap(true);
lh->setWordWrap(true);
lw->setFont(f);
lh->setFont(f);
QVBoxLayout *modeTabLayout = new QVBoxLayout();
modeTabLayout->addWidget(_wysiwyg);
modeTabLayout->addWidget(lw);
modeTabLayout->addSpacing(10);
modeTabLayout->addWidget(_hires);
modeTabLayout->addWidget(lh);
modeTabLayout->addStretch();
QWidget *modeTab = new QWidget();
modeTab->setLayout(modeTabLayout);
_name = new QCheckBox(tr("Name"));
_name->setChecked(_options->printName);
_date = new QCheckBox(tr("Date"));
@ -179,6 +331,7 @@ QWidget *OptionsDialog::createExportPage()
QTabWidget *exportPage = new QTabWidget();
exportPage->addTab(modeTab, tr("Print mode"));
exportPage->addTab(headerTab, tr("Header"));
exportPage->addTab(graphTab, tr("Graphs"));
@ -196,11 +349,24 @@ QWidget *OptionsDialog::createSystemPage()
#endif // Q_OS_WIN32
_useOpenGL->setChecked(_options->useOpenGL);
QFormLayout *systemLayout = new QFormLayout();
systemLayout->addWidget(_useOpenGL);
_pixmapCache = new QSpinBox();
_pixmapCache->setMinimum(16);
_pixmapCache->setMaximum(1024);
_pixmapCache->setSuffix(UNIT_SPACE + tr("MB"));
_pixmapCache->setValue(_options->pixmapCache);
QFormLayout *cacheLayout = new QFormLayout();
cacheLayout->addRow(tr("Image cache size:"), _pixmapCache);
QFormLayout *openGLLayout = new QFormLayout();
openGLLayout->addWidget(_useOpenGL);
QWidget *systemTab = new QWidget();
systemTab->setLayout(systemLayout);
QVBoxLayout *systemTabLayout = new QVBoxLayout();
systemTabLayout->addLayout(cacheLayout);
systemTabLayout->addLayout(openGLLayout);
systemTabLayout->addStretch();
systemTab->setLayout(systemTabLayout);
QTabWidget *systemPage = new QTabWidget();
systemPage->addTab(systemTab, tr("System"));
@ -213,6 +379,7 @@ OptionsDialog::OptionsDialog(Options *options, QWidget *parent)
{
QStackedWidget *pages = new QStackedWidget();
pages->addWidget(createAppearancePage());
pages->addWidget(createDataPage());
pages->addWidget(createPOIPage());
pages->addWidget(createExportPage());
pages->addWidget(createSystemPage());
@ -221,6 +388,7 @@ OptionsDialog::OptionsDialog(Options *options, QWidget *parent)
menu->setIconSize(QSize(MENU_ICON_SIZE, MENU_ICON_SIZE));
new QListWidgetItem(QIcon(QPixmap(APPEARANCE_ICON)), tr("Appearance"),
menu);
new QListWidgetItem(QIcon(QPixmap(DATA_ICON)), tr("Data"), menu);
new QListWidgetItem(QIcon(QPixmap(POI_ICON)), tr("POI"), menu);
new QListWidgetItem(QIcon(QPixmap(PRINT_EXPORT_ICON)), tr("Print & Export"),
menu);
@ -259,6 +427,7 @@ void OptionsDialog::accept()
{
_options->palette.setColor(_baseColor->color());
_options->palette.setShift(_colorOffset->value());
_options->mapOpacity = _mapOpacity->value();
_options->trackWidth = _trackWidth->value();
_options->trackStyle = (Qt::PenStyle) _trackStyle->itemData(
_trackStyle->currentIndex()).toInt();
@ -269,13 +438,23 @@ void OptionsDialog::accept()
_options->graphWidth = _graphWidth->value();
_options->graphAntiAliasing = _graphAA->isChecked();
if (_options->units == Imperial)
_options->poiRadius = _poiRadius->value() * MIINM;
else
_options->poiRadius = _poiRadius->value() * KMINM;
_options->elevationFilter = _elevationFilter->value();
_options->speedFilter = _speedFilter->value();
_options->heartRateFilter = _heartRateFilter->value();
_options->cadenceFilter = _cadenceFilter->value();
_options->powerFilter = _powerFilter->value();
_options->outlierEliminate = _outlierEliminate->isChecked();
_options->pauseSpeed = (_options->units == Imperial)
? _pauseSpeed->value() / MS2MIH : _pauseSpeed->value() / MS2KMH;
_options->pauseInterval = _pauseInterval->value();
_options->poiRadius = (_options->units == Imperial)
? _poiRadius->value() * MIINM : _poiRadius->value() * KMINM;
_options->useOpenGL = _useOpenGL->isChecked();
_options->pixmapCache = _pixmapCache->value();
_options->hiresPrint = _hires->isChecked();
_options->printName = _name->isChecked();
_options->printDate = _date->isChecked();
_options->printDistance = _distance->isChecked();

View File

@ -7,14 +7,18 @@
class ColorBox;
class StyleComboBox;
class OddSpinBox;
class QSpinBox;
class QDoubleSpinBox;
class QComboBox;
class QCheckBox;
class QRadioButton;
class PercentSlider;
struct Options {
// Appearance
Palette palette;
int mapOpacity;
int trackWidth;
int routeWidth;
Qt::PenStyle trackStyle;
@ -22,11 +26,22 @@ struct Options {
int graphWidth;
bool pathAntiAliasing;
bool graphAntiAliasing;
// Data
int elevationFilter;
int speedFilter;
int heartRateFilter;
int cadenceFilter;
int powerFilter;
bool outlierEliminate;
qreal pauseSpeed;
int pauseInterval;
// POI
int poiRadius;
// System
bool useOpenGL;
int pixmapCache;
// Print/Export
bool hiresPrint;
bool printName;
bool printDate;
bool printDistance;
@ -50,14 +65,17 @@ public slots:
private:
QWidget *createAppearancePage();
QWidget *createDataPage();
QWidget *createPOIPage();
QWidget *createSystemPage();
QWidget *createExportPage();
Options *_options;
// Appearance
ColorBox *_baseColor;
QDoubleSpinBox *_colorOffset;
PercentSlider *_mapOpacity;
QSpinBox *_trackWidth;
StyleComboBox *_trackStyle;
QSpinBox *_routeWidth;
@ -65,8 +83,23 @@ private:
QCheckBox *_pathAA;
QSpinBox *_graphWidth;
QCheckBox *_graphAA;
// Data
OddSpinBox *_elevationFilter;
OddSpinBox *_speedFilter;
OddSpinBox *_heartRateFilter;
OddSpinBox *_cadenceFilter;
OddSpinBox *_powerFilter;
QCheckBox *_outlierEliminate;
QDoubleSpinBox *_pauseSpeed;
QSpinBox *_pauseInterval;
// POI
QDoubleSpinBox *_poiRadius;
// System
QSpinBox *_pixmapCache;
QCheckBox *_useOpenGL;
// Print/Export
QRadioButton *_wysiwyg;
QRadioButton *_hires;
QCheckBox *_name;
QCheckBox *_date;
QCheckBox *_distance;

View File

@ -1,10 +1,25 @@
#include <cstring>
#include <QtEndian>
#include <QFile>
#include "ozf.h"
#define OZF2_MAGIC 0x7778
#define OZF2_SEPARATOR 0x77777777
#define OZF2_MAGIC 0x7778
#define OZF3_MAGIC 0x7780
static const quint8 XKEY[] =
{
0x2D, 0x4A, 0x43, 0xF1, 0x27, 0x9B, 0x69, 0x4F,
0x36, 0x52, 0x87, 0xEC, 0x5F, 0x42, 0x53, 0x22,
0x9E, 0x8B, 0x2D, 0x83, 0x3D, 0xD2, 0x84, 0xBA,
0xD8, 0x5B
};
static void decrypt(void *data, size_t size, quint8 init)
{
for (size_t i = 0; i < size; i++)
reinterpret_cast<quint8*>(data)[i] ^= XKEY[i % sizeof(XKEY)] + init;
}
template<class T> bool OZF::readValue(T &val)
{
@ -13,6 +28,9 @@ template<class T> bool OZF::readValue(T &val)
if (_file.read((char*)&data, sizeof(T)) < (qint64)sizeof(T))
return false;
if (_decrypt)
decrypt(&data, sizeof(T), _key);
if (sizeof(T) > 1)
val = qFromLittleEndian(data);
else
@ -21,71 +39,150 @@ template<class T> bool OZF::readValue(T &val)
return true;
}
bool OZF::read(void *data, size_t size, size_t decryptSize)
{
if (_file.read((char*)data, size) < (qint64)size)
return false;
if (_decrypt)
decrypt(data, decryptSize ? qMin(decryptSize, size) : size, _key);
return true;
}
bool OZF::initOZF3()
{
quint8 randomNumber, initial;
quint8 h1[8];
quint8 h2[16], h2d[16];
if (!_file.seek(14))
return false;
if (!readValue(randomNumber))
return false;
if (!_file.seek(162))
return false;
if (!readValue(initial))
return false;
_decrypt = true;
_key = initial;
if (!_file.seek(0))
return false;
if (!read(h1, sizeof(h1)))
return false;
_tileSize = *(h1 + 6);
if (!_file.seek(15 + randomNumber + 4))
return false;
if (_file.read((char*)h2, sizeof(h2)) != (qint64)sizeof(h2))
return false;
for (int i = 0; i < 256; i++) {
memcpy(h2d, h2, sizeof(h2d));
decrypt(h2d, sizeof(h2d), (quint8)i);
if ((quint32)*h2d == 40 && (quint16)*(h2d + 12) == 1
&& (quint16)*(h2d + 14) == 8) {
_key = (quint8)i;
return true;
}
}
return false;
}
bool OZF::initOZF2()
{
if (!_file.seek(6))
return false;
if (!readValue(_tileSize))
return false;
return true;
}
bool OZF::readHeaders()
{
quint16 magic;
quint32 separator;
if (!readValue(magic) || magic != OZF2_MAGIC)
if (!readValue(magic))
return false;
if (!_file.seek(_file.pos() + 52))
return false;
if (!readValue(separator) || separator != OZF2_SEPARATOR)
if (magic == OZF2_MAGIC) {
if (!initOZF2())
return false;
} else if (magic == OZF3_MAGIC) {
if (!initOZF3())
return false;
} else {
qWarning("%s: not a OZF2/OZF3 file", qPrintable(_file.fileName()));
return false;
}
return true;
}
bool OZF::readTileTable()
{
quint32 offset, bgr0, w, h;
quint32 tableOffset, headerOffset, bgr0, w, h;
quint16 x, y;
int zooms;
if (!_file.seek(_file.size() - 4))
if (!_file.seek(_file.size() - sizeof(tableOffset)))
return false;
// table offset
if (!readValue(offset))
return false;
if (!_file.seek(offset))
return false;
// tiles offset (zoom level 0)
if (!readValue(offset))
return false;
if (!_file.seek(offset))
if (!readValue(tableOffset))
return false;
zooms = (int)((_file.size() - tableOffset - sizeof(quint32))
/ sizeof(quint32));
if (!readValue(w))
return false;
if (!readValue(h))
return false;
if (!readValue(x))
return false;
if (!readValue(y))
return false;
_size = QSize(w, h);
_dim = QSize(x, y);
_palette = QVector<quint32>(256);
for (int i = 0; i < _palette.size(); i++) {
if (!readValue(bgr0))
for (int i = 0; i < zooms - 2; i++) {
if (!_file.seek(tableOffset + i * sizeof(quint32)))
return false;
if (!readValue(headerOffset))
return false;
if (!_file.seek(headerOffset))
return false;
quint32 b = (bgr0 & 0x000000FF);
quint32 g = (bgr0 & 0x0000FF00) >> 8;
quint32 r = (bgr0 & 0x00FF0000) >> 16;
if (!readValue(w))
return false;
if (!readValue(h))
return false;
if (!readValue(x))
return false;
if (!readValue(y))
return false;
_palette[i] = 0xFF000000 | r << 16 | g << 8 | b;
Zoom zoom;
zoom.size = QSize(w, h);
zoom.dim = QSize(x, y);
zoom.palette = QVector<quint32>(256);
if (!read(&(zoom.palette[0]), sizeof(quint32) * 256))
return false;
for (int i = 0; i < zoom.palette.size(); i++) {
bgr0 = qFromLittleEndian(zoom.palette.at(i));
quint32 b = (bgr0 & 0x000000FF);
quint32 g = (bgr0 & 0x0000FF00) >> 8;
quint32 r = (bgr0 & 0x00FF0000) >> 16;
zoom.palette[i] = 0xFF000000 | r << 16 | g << 8 | b;
}
zoom.tiles = QVector<quint32>(zoom.dim.width() * zoom.dim.height() + 1);
for (int i = 0; i < zoom.tiles.size(); i++)
if (!readValue(zoom.tiles[i]))
return false;
_zooms.append(zoom);
}
_tiles = QVector<quint32>(_dim.width() * _dim.height() + 1);
for (int i = 0; i < _tiles.size(); i++)
if (!readValue(_tiles[i]))
return false;
return true;
return _zooms.isEmpty() ? false : true;
}
bool OZF::load(const QString &path)
@ -98,45 +195,74 @@ bool OZF::load(const QString &path)
return false;
if (!readHeaders()) {
qWarning("%s: not a OZF2 file", qPrintable(_file.fileName()));
qWarning("%s: Invalid header", qPrintable(_file.fileName()));
_file.close();
return false;
}
if (!readTileTable()) {
qWarning("%s: file format error", qPrintable(_file.fileName()));
qWarning("%s: Invalid tile table", qPrintable(_file.fileName()));
_file.close();
_size = QSize();
return false;
}
return true;
}
QPixmap OZF::tile(int x, int y)
QPixmap OZF::tile(int zoom, int x, int y)
{
Q_ASSERT(_file.isOpen());
Q_ASSERT(0 <= zoom && zoom < _zooms.count());
int i = (y/tileSize().height()) * _dim.width() + (x/tileSize().width());
if (i >= _tiles.size() - 1 || i < 0)
const Zoom &z = _zooms.at(zoom);
int i = (y/tileSize().height()) * z.dim.width() + (x/tileSize().width());
if (i >= z.tiles.size() - 1 || i < 0)
return QPixmap();
int size = _tiles.at(i+1) - _tiles.at(i);
if (!_file.seek(_tiles.at(i)))
int size = z.tiles.at(i+1) - z.tiles.at(i);
if (!_file.seek(z.tiles.at(i)))
return QPixmap();
QByteArray ba = _file.read(size);
if (ba.size() != size)
return QPixmap();
quint32 bes = qToBigEndian(tileSize().width() * tileSize().height());
ba.prepend(QByteArray((char*)&bes, sizeof(bes)));
QByteArray ba;
ba.resize(sizeof(bes) + size);
*(ba.data()) = bes;
if (!read(ba.data() + sizeof(bes), size, 16))
return QPixmap();
QByteArray uba = qUncompress(ba);
if (uba.size() != tileSize().width() * tileSize().height())
return QPixmap();
QImage img((const uchar*)uba.constData(), tileSize().width(),
tileSize().height(), QImage::Format_Indexed8);
img.setColorTable(_palette);
img.setColorTable(z.palette);
return QPixmap::fromImage(img.mirrored());
}
QSize OZF::size(int zoom) const
{
Q_ASSERT(_file.isOpen());
Q_ASSERT(0 <= zoom && zoom < _zooms.count());
return _zooms.at(zoom).size;
}
bool OZF::isOZF(const QString &path)
{
QFile file(path);
quint16 magic;
if (!file.open(QIODevice::ReadOnly))
return false;
if (file.read((char*)&magic, sizeof(magic)) < (qint64)sizeof(magic))
return false;
magic = qFromLittleEndian(magic);
if (magic == OZF2_MAGIC || magic == OZF3_MAGIC)
return true;
return false;
}

View File

@ -4,6 +4,7 @@
#include <QString>
#include <QSize>
#include <QColor>
#include <QList>
#include <QVector>
#include <QFile>
#include <QPixmap>
@ -11,25 +12,39 @@
class OZF
{
public:
OZF() : _tileSize(0), _decrypt(false), _key(0) {}
bool load(const QString &path);
QString fileName() const {return _file.fileName();}
bool isOpen() const {return _file.isOpen();}
QSize size() const {return _size;}
QSize tileSize() const {return QSize(64, 64);}
QPixmap tile(int x, int y);
int zooms() const {return _zooms.size();}
QSize size(int zoom) const;
QSize tileSize() const {return QSize(_tileSize, _tileSize);}
QPixmap tile(int zoom, int x, int y);
static bool isOZF(const QString &path);
private:
struct Zoom {
QSize size;
QSize dim;
QVector<QRgb> palette;
QVector<quint32> tiles;
};
template<class T> bool readValue(T &val);
bool read(void *data, size_t size, size_t decryptSize = 0);
bool initOZF3();
bool initOZF2();
bool readHeaders();
bool readTileTable();
QSize _size;
QSize _dim;
QVector<QRgb> _palette;
QVector<quint32> _tiles;
quint16 _tileSize;
bool _decrypt;
quint8 _key;
QList<Zoom> _zooms;
QFile _file;
};

View File

@ -41,6 +41,5 @@ QDebug operator<<(QDebug dbg, const Palette &palette)
{
dbg.nospace() << "Palette(" << palette.color() << ", " << palette.shift()
<< ")";
return dbg.maybeSpace();
return dbg.space();
}

View File

@ -12,19 +12,12 @@
class Parser
{
public:
Parser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : _tracks(tracks), _routes(routes),
_waypoints(waypoints) {}
virtual ~Parser() {}
virtual bool loadFile(QFile *file) = 0;
virtual bool parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints) = 0;
virtual QString errorString() const = 0;
virtual int errorLine() const = 0;
protected:
QList<TrackData> &_tracks;
QList<RouteData> &_routes;
QList<Waypoint> &_waypoints;
};
#endif // PARSER_H

View File

@ -1,33 +1,20 @@
#include "path.h"
QRectF Path::boundingRect() const
RectC Path::boundingRect() const
{
if (size() < 2)
return QRectF();
return RectC();
QPointF topLeft(at(0).coordinates().lon(), at(0).coordinates().lat());
QPointF bottomRight(topLeft);
RectC ret(first().coordinates(), first().coordinates());
for (int i = 1; i < size(); i++)
ret.unite(at(i).coordinates());
for (int i = 1; i < size(); i++) {
qreal x = at(i).coordinates().lon();
qreal y = at(i).coordinates().lat();
if (x < topLeft.x())
topLeft.setX(x);
if (y < topLeft.y())
topLeft.setY(y);
if (x > bottomRight.x())
bottomRight.setX(x);
if (y > bottomRight.y())
bottomRight.setY(y);
}
return QRectF(topLeft, bottomRight);
return ret;
}
QDebug operator<<(QDebug dbg, const PathPoint &point)
{
dbg.nospace() << "PathPoint(" << point.distance() << ", "
<< point.coordinates() << ")";
return dbg.maybeSpace();
return dbg.space();
}

View File

@ -4,6 +4,7 @@
#include <QVector>
#include <QRectF>
#include "coordinates.h"
#include "rectc.h"
class PathPoint
{
@ -28,7 +29,7 @@ QDebug operator<<(QDebug dbg, const PathPoint &point);
class Path : public QVector<PathPoint>
{
public:
QRectF boundingRect() const;
RectC boundingRect() const;
};
#endif // PATH_H

View File

@ -12,20 +12,21 @@ PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent)
: QGraphicsObject(parent)
{
Q_ASSERT(path.count() >= 2);
_path = path;
_map = map;
updatePainterPath(map);
updateShape();
_digitalZoom = 0;
_width = 3;
QBrush brush(Qt::SolidPattern);
_pen = QPen(brush, _width);
updatePainterPath(map);
updateShape();
_marker = new MarkerItem(this);
_marker->setPos(position(_path.at(0).distance()));
_marker->setFlag(QGraphicsItem::ItemIgnoresTransformations);
_md = _path.at(0).distance();
_markerDistance = _path.at(0).distance();
setCursor(Qt::ArrowCursor);
setAcceptHoverEvents(true);
@ -34,7 +35,7 @@ PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent)
void PathItem::updateShape()
{
QPainterPathStroker s;
s.setWidth((_width + 1) * 1.0/scale());
s.setWidth((_width + 1) * pow(2, -_digitalZoom));
_shape = s.createStroke(_painterPath);
}
@ -71,7 +72,7 @@ void PathItem::setMap(Map *map)
updatePainterPath(map);
updateShape();
_marker->setPos(position(_md));
_marker->setPos(position(_markerDistance));
}
void PathItem::setColor(const QColor &color)
@ -85,7 +86,7 @@ void PathItem::setWidth(qreal width)
prepareGeometryChange();
_width = width;
_pen.setWidthF(_width * 1.0/scale());
_pen.setWidthF(_width * pow(2, -_digitalZoom));
updateShape();
}
@ -96,6 +97,17 @@ void PathItem::setStyle(Qt::PenStyle style)
update();
}
void PathItem::setDigitalZoom(int zoom)
{
prepareGeometryChange();
_digitalZoom = zoom;
_pen.setWidthF(_width * pow(2, -_digitalZoom));
_marker->setScale(pow(2, -_digitalZoom));
updateShape();
}
QPointF PathItem::position(qreal x) const
{
int low = 0;
@ -137,7 +149,7 @@ void PathItem::moveMarker(qreal distance)
&& distance <= _path.last().distance()) {
_marker->setVisible(true);
_marker->setPos(position(distance));
_md = distance;
_markerDistance = distance;
} else
_marker->setVisible(false);
}
@ -146,7 +158,7 @@ void PathItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
_pen.setWidthF((_width + 1) * 1.0/scale());
_pen.setWidthF((_width + 1) * pow(2, -_digitalZoom));
setZValue(zValue() + 1.0);
update();
@ -157,7 +169,7 @@ void PathItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
_pen.setWidthF(_width * 1.0/scale());
_pen.setWidthF(_width * pow(2, -_digitalZoom));
setZValue(zValue() - 1.0);
update();

View File

@ -28,8 +28,7 @@ public:
void setColor(const QColor &color);
void setWidth(qreal width);
void setStyle(Qt::PenStyle style);
void showMarker(bool show) {_marker->setVisible(show);}
void setDigitalZoom(int zoom);
public slots:
void moveMarker(qreal distance);
@ -50,7 +49,8 @@ private:
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
Map *_map;
qreal _md;
qreal _markerDistance;
int _digitalZoom;
qreal _width;
QPen _pen;

View File

@ -2,12 +2,12 @@
#include <QGraphicsScene>
#include <QWheelEvent>
#include <QApplication>
#include <QPixmapCache>
#include "opengl.h"
#include "misc.h"
#include "poi.h"
#include "data.h"
#include "map.h"
#include "emptymap.h"
#include "trackitem.h"
#include "routeitem.h"
#include "waypointitem.h"
@ -15,25 +15,14 @@
#include "keys.h"
#include "pathview.h"
#define MAX_ZOOM 1
#define MIN_ZOOM -3
#define MARGIN 10.0
#define SCALE_OFFSET 7
static void unite(QRectF &rect, const QPointF &p)
{
if (p.x() < rect.left())
rect.setLeft(p.x());
if (p.x() > rect.right())
rect.setRight(p.x());
if (p.y() > rect.bottom())
rect.setBottom(p.y());
if (p.y() < rect.top())
rect.setTop(p.y());
}
#define MAX_DIGITAL_ZOOM 1
#define MIN_DIGITAL_ZOOM -3
#define MARGIN 10.0
#define SCALE_OFFSET 7
PathView::PathView(Map *map, POI *poi, QWidget *parent)
: QGraphicsView(parent)
: QGraphicsView(parent)
{
Q_ASSERT(map != 0);
Q_ASSERT(poi != 0);
@ -50,7 +39,6 @@ PathView::PathView(Map *map, POI *poi, QWidget *parent)
_mapScale = new ScaleItem();
_mapScale->setZValue(2.0);
_mapScale->setFlag(QGraphicsItem::ItemIgnoresTransformations);
_map = map;
_poi = poi;
@ -58,6 +46,7 @@ PathView::PathView(Map *map, POI *poi, QWidget *parent)
connect(_poi, SIGNAL(pointsChanged()), this, SLOT(updatePOI()));
_units = Metric;
_opacity = 1.0;
_showMap = true;
_showTracks = true;
@ -97,9 +86,11 @@ PathItem *PathView::addTrack(const Track &track)
_tracks.append(ti);
_tr |= ti->path().boundingRect();
ti->setColor(_palette.nextColor());
ti->setWidth(_trackWidth * pow(2, -_digitalZoom));
ti->setWidth(_trackWidth);
ti->setStyle(_trackStyle);
ti->setUnits(_units);
ti->setVisible(_showTracks);
ti->setDigitalZoom(_digitalZoom);
_scene->addItem(ti);
addPOI(_poi->points(ti->path()));
@ -118,11 +109,13 @@ PathItem *PathView::addRoute(const Route &route)
_routes.append(ri);
_rr |= ri->path().boundingRect();
ri->setColor(_palette.nextColor());
ri->setWidth(_routeWidth * pow(2, -_digitalZoom));
ri->setWidth(_routeWidth);
ri->setStyle(_routeStyle);
ri->setUnits(_units);
ri->setVisible(_showRoutes);
ri->showWaypoints(_showRouteWaypoints);
ri->showWaypointLabels(_showWaypointLabels);
ri->setDigitalZoom(_digitalZoom);
_scene->addItem(ri);
addPOI(_poi->points(ri->path()));
@ -137,12 +130,12 @@ void PathView::addWaypoints(const QList<Waypoint> &waypoints)
WaypointItem *wi = new WaypointItem(w, _map);
_waypoints.append(wi);
Coordinates c = wi->waypoint().coordinates();
updateWaypointsBoundingRect(QPointF(c.lon(), c.lat()));
updateWaypointsBoundingRect(wi->waypoint().coordinates());
wi->setZValue(1);
wi->showLabel(_showWaypointLabels);
wi->setUnits(_units);
wi->setVisible(_showWaypoints);
wi->setFlag(QGraphicsItem::ItemIgnoresTransformations);
wi->setDigitalZoom(_digitalZoom);
_scene->addItem(wi);
}
@ -152,7 +145,7 @@ void PathView::addWaypoints(const QList<Waypoint> &waypoints)
QList<PathItem *> PathView::loadData(const Data &data)
{
QList<PathItem *> paths;
qreal scale = _map->zoom();
qreal zoom = _map->zoom();
for (int i = 0; i < data.tracks().count(); i++)
paths.append(addTrack(*(data.tracks().at(i))));
@ -163,7 +156,7 @@ QList<PathItem *> PathView::loadData(const Data &data)
if (_tracks.empty() && _routes.empty() && _waypoints.empty())
return paths;
if (mapScale() != scale)
if (mapZoom() != zoom)
rescale();
else
updatePOIVisibility();
@ -179,43 +172,31 @@ QList<PathItem *> PathView::loadData(const Data &data)
return paths;
}
void PathView::updateWaypointsBoundingRect(const QPointF &wp)
void PathView::updateWaypointsBoundingRect(const Coordinates &wp)
{
if (_wr.isNull()) {
if (_wp.isNull())
_wp = wp;
else {
_wr = QRectF(_wp, wp).normalized();
_wp = QPointF();
}
} else
unite(_wr, wp);
if (_wr.isNull())
_wr = RectC(wp, wp);
else
_wr.unite(wp);
}
qreal PathView::mapScale() const
qreal PathView::mapZoom() const
{
QRectF br = _tr | _rr | _wr;
if (!br.isNull() && !_wp.isNull())
unite(br, _wp);
RectC br = _tr | _rr | _wr;
return _map->zoomFit(viewport()->size() - QSize(MARGIN/2, MARGIN/2), br);
return _map->zoomFit(viewport()->size() - QSize(2*MARGIN, 2*MARGIN), br);
}
QPointF PathView::contentCenter() const
{
QRectF br = _tr | _rr | _wr;
if (!br.isNull() && !_wp.isNull())
unite(br, _wp);
RectC br = _tr | _rr | _wr;
if (br.isNull())
return _map->ll2xy(_wp);
else
return _map->ll2xy(br.center());
return _map->ll2xy(br.center());
}
void PathView::updatePOIVisibility()
{
QHash<Waypoint, WaypointItem*>::const_iterator it, jt;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it, jt;
if (!_showPOI)
return;
@ -246,7 +227,7 @@ void PathView::rescale()
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(_map);
QHash<Waypoint, WaypointItem*>::const_iterator it;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->setMap(_map);
@ -266,6 +247,10 @@ void PathView::setPalette(const Palette &palette)
void PathView::setMap(Map *map)
{
QPointF pos = mapToScene(viewport()->rect().center());
Coordinates center = _map->xy2ll(pos);
qreal resolution = _map->resolution(pos);
_map->unload();
disconnect(_map, SIGNAL(loaded()), this, SLOT(redraw()));
@ -275,7 +260,7 @@ void PathView::setMap(Map *map)
resetDigitalZoom();
mapScale();
_map->zoomFit(resolution, center);
_scene->setSceneRect(_map->bounds());
for (int i = 0; i < _tracks.size(); i++)
@ -285,18 +270,19 @@ void PathView::setMap(Map *map)
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(map);
QHash<Waypoint, WaypointItem*>::const_iterator it;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->setMap(_map);
updatePOIVisibility();
QPointF center = contentCenter();
centerOn(center);
pos = _map->ll2xy(center);
centerOn(pos);
_res = _map->resolution(center);
_res = _map->resolution(pos);
_mapScale->setResolution(_res);
resetCachedContent();
QPixmapCache::clear();
}
void PathView::setPOI(POI *poi)
@ -311,7 +297,7 @@ void PathView::setPOI(POI *poi)
void PathView::updatePOI()
{
QHash<Waypoint, WaypointItem*>::const_iterator it;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++) {
_scene->removeItem(it.value());
@ -333,17 +319,17 @@ void PathView::addPOI(const QVector<Waypoint> &waypoints)
for (int i = 0; i < waypoints.size(); i++) {
const Waypoint &w = waypoints.at(i);
if (_pois.contains(w))
if (_pois.contains(SearchPointer<Waypoint>(&w)))
continue;
WaypointItem *pi = new WaypointItem(w, _map);
pi->setZValue(1);
pi->showLabel(_showPOILabels);
pi->setVisible(_showPOI);
pi->setFlag(QGraphicsItem::ItemIgnoresTransformations);
pi->setDigitalZoom(_digitalZoom);
_scene->addItem(pi);
_pois.insert(w, pi);
_pois.insert(SearchPointer<Waypoint>(&(pi->waypoint())), pi);
}
}
@ -360,7 +346,7 @@ void PathView::setUnits(enum Units units)
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setUnits(units);
QHash<Waypoint, WaypointItem*>::const_iterator it;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->setUnits(units);
}
@ -372,23 +358,40 @@ void PathView::redraw()
void PathView::resetDigitalZoom()
{
_digitalZoom = 0;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
_digitalZoom = 0;
resetTransform();
setTrackWidth(_trackWidth);
setRouteWidth(_routeWidth);
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setDigitalZoom(0);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setDigitalZoom(0);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setDigitalZoom(0);
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->setDigitalZoom(0);
_mapScale->setDigitalZoom(0);
}
void PathView::digitalZoom(int zoom)
{
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
_digitalZoom += zoom;
scale(pow(2, zoom), pow(2, zoom));
setTrackWidth(_trackWidth);
setRouteWidth(_routeWidth);
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setDigitalZoom(_digitalZoom);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setDigitalZoom(_digitalZoom);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setDigitalZoom(_digitalZoom);
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->setDigitalZoom(_digitalZoom);
_mapScale->setResolution(_res * pow(2, -_digitalZoom));
_mapScale->setDigitalZoom(_digitalZoom);
}
void PathView::zoom(int zoom, const QPoint &pos, const Coordinates &c)
@ -397,8 +400,8 @@ void PathView::zoom(int zoom, const QPoint &pos, const Coordinates &c)
if (_digitalZoom) {
if (((_digitalZoom > 0 && zoom > 0) && (!shift || _digitalZoom
>= MAX_ZOOM)) || ((_digitalZoom < 0 && zoom < 0) && (!shift
|| _digitalZoom <= MIN_ZOOM)))
>= MAX_DIGITAL_ZOOM)) || ((_digitalZoom < 0 && zoom < 0) && (!shift
|| _digitalZoom <= MIN_DIGITAL_ZOOM)))
return;
digitalZoom(zoom);
@ -450,7 +453,7 @@ void PathView::keyPressEvent(QKeyEvent *event)
{
int z;
QPoint pos = QRect(QPoint(), viewport()->size()).center();
QPoint pos = viewport()->rect().center();
Coordinates c = _map->xy2ll(mapToScene(pos));
if (event->matches(ZOOM_IN))
@ -468,13 +471,20 @@ void PathView::keyPressEvent(QKeyEvent *event)
zoom(z, pos, c);
}
void PathView::plot(QPainter *painter, const QRectF &target)
void PathView::plot(QPainter *painter, const QRectF &target, bool hires)
{
QRect orig, adj;
qreal ratio, diff;
qreal ratio, diff, origRes;
QPointF origScene, origPos;
Coordinates origLL;
setUpdatesEnabled(false);
_plot = true;
_map->setBlockingMode(true);
orig = viewport()->rect();
origPos = _mapScale->pos();
if (orig.height() * (target.width() / target.height()) - orig.width() < 0) {
ratio = target.height() / target.width();
@ -486,18 +496,46 @@ void PathView::plot(QPainter *painter, const QRectF &target)
adj = orig.adjusted(-diff/2, 0, diff/2, 0);
}
setUpdatesEnabled(false);
_plot = true;
_map->setBlockingMode(true);
// Adjust the view for printing
if (hires) {
origScene = mapToScene(orig.center());
origLL = _map->xy2ll(origScene);
origRes = _map->resolution(origScene);
QPointF pos = _mapScale->pos();
_mapScale->setPos(mapToScene(QPoint(adj.bottomRight() + QPoint(
-(SCALE_OFFSET + _mapScale->boundingRect().width()),
-(SCALE_OFFSET + _mapScale->boundingRect().height())))));
QPointF s(painter->device()->logicalDpiX()
/ (qreal)metric(QPaintDevice::PdmDpiX),
painter->device()->logicalDpiY()
/ (qreal)metric(QPaintDevice::PdmDpiY));
adj = QRect(0, 0, adj.width() * s.x(), adj.height() * s.y());
_map->zoomFit(adj.size(), _tr | _rr | _wr);
rescale();
QPointF center = contentCenter();
centerOn(center);
adj.moveCenter(mapFromScene(center));
_mapScale->setResolution(_map->resolution(_map->ll2xy(origLL)));
_mapScale->setDigitalZoom(-log2(s.x()));
_mapScale->setPos(mapToScene(QPoint(adj.bottomRight() + QPoint(
-(SCALE_OFFSET + _mapScale->boundingRect().width()) * s.x(),
-(SCALE_OFFSET + _mapScale->boundingRect().height()) * s.x()))));
} else {
_mapScale->setPos(mapToScene(QPoint(adj.bottomRight() + QPoint(
-(SCALE_OFFSET + _mapScale->boundingRect().width()),
-(SCALE_OFFSET + _mapScale->boundingRect().height())))));
}
// Print the view
render(painter, target, adj);
_mapScale->setPos(pos);
// Revert view changes to display mode
if (hires) {
_map->zoomFit(origRes, origLL);
rescale();
centerOn(origScene);
_mapScale->setDigitalZoom(0);
_mapScale->setResolution(origRes);
}
_mapScale->setPos(origPos);
_map->setBlockingMode(false);
_plot = false;
@ -516,13 +554,13 @@ void PathView::clear()
_scene->clear();
_palette.reset();
_tr = QRectF(); _rr = QRectF(); _wr = QRectF();
_wp = QPointF();
_digitalZoom = 0;
resetTransform();
_tr = RectC();
_rr = RectC();
_wr = RectC();
resetDigitalZoom();
resetCachedContent();
QPixmapCache::clear();
}
void PathView::showTracks(bool show)
@ -578,7 +616,7 @@ void PathView::showPOI(bool show)
{
_showPOI = show;
QHash<Waypoint, WaypointItem*>::const_iterator it;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->setVisible(show);
@ -589,7 +627,7 @@ void PathView::showPOILabels(bool show)
{
_showPOILabels = show;
QHash<Waypoint, WaypointItem*>::const_iterator it;
QHash<SearchPointer<Waypoint>, WaypointItem*>::const_iterator it;
for (it = _pois.constBegin(); it != _pois.constEnd(); it++)
it.value()->showLabel(show);
@ -608,7 +646,7 @@ void PathView::setTrackWidth(int width)
_trackWidth = width;
for (int i = 0; i < _tracks.count(); i++)
_tracks.at(i)->setWidth(width * pow(2, -_digitalZoom));
_tracks.at(i)->setWidth(width);
}
void PathView::setRouteWidth(int width)
@ -616,7 +654,7 @@ void PathView::setRouteWidth(int width)
_routeWidth = width;
for (int i = 0; i < _routes.count(); i++)
_routes.at(i)->setWidth(width * pow(2, -_digitalZoom));
_routes.at(i)->setWidth(width);
}
void PathView::setTrackStyle(Qt::PenStyle style)
@ -635,18 +673,28 @@ void PathView::setRouteStyle(Qt::PenStyle style)
_routes.at(i)->setStyle(style);
}
void PathView::setMapOpacity(int opacity)
{
_opacity = opacity / 100.0;
resetCachedContent();
}
void PathView::drawBackground(QPainter *painter, const QRectF &rect)
{
if (_showMap)
if (_showMap) {
if (_opacity < 1.0) {
painter->fillRect(rect, Qt::white);
painter->setOpacity(_opacity);
}
_map->draw(painter, rect);
else
} else
painter->fillRect(rect, Qt::white);
}
void PathView::resizeEvent(QResizeEvent *event)
{
qreal scale = _map->zoom();
if (mapScale() != scale)
qreal zoom = _map->zoom();
if (mapZoom() != zoom)
rescale();
QPointF center = contentCenter();

View File

@ -8,6 +8,8 @@
#include "units.h"
#include "palette.h"
#include "waypoint.h"
#include "rectc.h"
#include "searchpointer.h"
class Data;
class POI;
@ -35,7 +37,7 @@ public:
void setMap(Map *map);
void setUnits(enum Units units);
void plot(QPainter *painter, const QRectF &target);
void plot(QPainter *painter, const QRectF &target, bool hires);
int trackCount() const {return _tracks.count();}
int routeCount() const {return _routes.count();}
@ -61,6 +63,7 @@ public slots:
void setRouteWidth(int width);
void setTrackStyle(Qt::PenStyle style);
void setRouteStyle(Qt::PenStyle style);
void setMapOpacity(int opacity);
private slots:
void updatePOI();
@ -73,14 +76,14 @@ private:
void loadPOI();
void clearPOI();
qreal mapScale() const;
qreal mapZoom() const;
QPointF contentCenter() const;
void rescale();
void zoom(int zoom, const QPoint &pos, const Coordinates &c);
void digitalZoom(int zoom);
void resetDigitalZoom();
void updatePOIVisibility();
void updateWaypointsBoundingRect(const QPointF &wp);
void updateWaypointsBoundingRect(const Coordinates &wp);
void mouseDoubleClickEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
@ -95,16 +98,16 @@ private:
QList<TrackItem*> _tracks;
QList<RouteItem*> _routes;
QList<WaypointItem*> _waypoints;
QHash<Waypoint, WaypointItem*> _pois;
QHash<SearchPointer<Waypoint>, WaypointItem*> _pois;
QRectF _tr, _rr, _wr;
QPointF _wp;
RectC _tr, _rr, _wr;
qreal _res;
Map *_map;
POI *_poi;
Palette _palette;
Units _units;
qreal _opacity;
bool _showMap;
bool _showTracks;

48
src/percentslider.cpp Normal file
View File

@ -0,0 +1,48 @@
#include <QSlider>
#include <QLabel>
#include <QHBoxLayout>
#include "units.h"
#include "percentslider.h"
static QString format(int value)
{
return QString::number(value) + UNIT_SPACE + QString("%");
}
PercentSlider::PercentSlider(QWidget *parent) : QWidget(parent)
{
_slider = new QSlider(Qt::Horizontal);
_label = new QLabel();
_slider->setMinimum(0);
_slider->setMaximum(100);
QFontMetrics fm(_label->font());
_label->setFixedWidth(fm.boundingRect(format(_slider->maximum())).width());
_label->setAlignment(Qt::AlignRight);
connect(_slider, SIGNAL(sliderMoved(int)), this, SLOT(updateLabel(int)));
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(_slider);
layout->addWidget(_label);
setLayout(layout);
}
void PercentSlider::updateLabel(int value)
{
_label->setText(format(value));
}
int PercentSlider::value() const
{
return _slider->value();
}
void PercentSlider::setValue(int value)
{
_slider->setValue(value);
_label->setText(format(value));
}

29
src/percentslider.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef PERCENTSLIDER_H
#define PERCENTSLIDER_H
#include <QWidget>
class QSlider;
class QLabel;
class PercentSlider : public QWidget
{
Q_OBJECT
public:
PercentSlider(QWidget *parent = 0);
int value() const;
public slots:
void setValue(int value);
private slots:
void updateLabel(int value);
private:
QSlider *_slider;
QLabel *_label;
};
#endif // PERCENTSLIDER_H

View File

@ -38,11 +38,11 @@ signals:
private:
typedef RTree<size_t, qreal, 2> POITree;
typedef struct {
struct FileIndex {
int start;
int end;
bool enabled;
} FileIndex;
};
POITree _tree;
QVector<Waypoint> _data;

View File

@ -11,5 +11,5 @@ void RangeF::resize(qreal size)
QDebug operator<<(QDebug dbg, const RangeF &range)
{
dbg.nospace() << "RangeF(" << range.min() << ", " << range.max() << ")";
return dbg.maybeSpace();
return dbg.space();
}

63
src/rectc.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "rectc.h"
RectC RectC::operator|(const RectC &r) const
{
if (isNull())
return r;
if (r.isNull())
return *this;
qreal l1 = _tl.lon();
qreal r1 = _tl.lon();
if (_br.lon() - _tl.lon() < 0)
l1 = _br.lon();
else
r1 = _br.lon();
qreal l2 = r._tl.lon();
qreal r2 = r._tl.lon();
if (r._br.lon() - r._tl.lon() < 0)
l2 = r._br.lon();
else
r2 = r._br.lon();
qreal t1 = _tl.lat();
qreal b1 = _tl.lat();
if (_br.lat() - _tl.lat() < 0)
t1 = _br.lat();
else
b1 = _br.lat();
qreal t2 = r._tl.lat();
qreal b2 = r._tl.lat();
if (r._br.lat() - r._tl.lat() < 0)
t2 = r._br.lat();
else
b2 = r._br.lat();
RectC tmp;
tmp._tl.setLon(qMin(l1, l2));
tmp._br.setLon(qMax(r1, r2));
tmp._tl.setLat(qMin(t1, t2));
tmp._br.setLat(qMax(b1, b2));
return tmp;
}
void RectC::unite(const Coordinates &c)
{
if (c.lon() < _tl.lon())
_tl.setLon(c.lon());
if (c.lon() > _br.lon())
_br.setLon(c.lon());
if (c.lat() > _br.lat())
_br.setLat(c.lat());
if (c.lat() < _tl.lat())
_tl.setLat(c.lat());
}
QDebug operator<<(QDebug dbg, const RectC &rect)
{
dbg.nospace() << "RectC(" << rect.topLeft() << ", " << rect.size() << ")";
return dbg.space();
}

42
src/rectc.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef RECTC_H
#define RECTC_H
#include <QDebug>
#include <QSizeF>
#include "coordinates.h"
class RectC
{
public:
RectC() {}
RectC(const Coordinates &topLeft, const Coordinates &bottomRight)
: _tl(topLeft), _br(bottomRight) {}
bool isNull() const
{return _tl.isNull() && _br.isNull();}
bool isValid() const
{return (_tl.isValid() && _br.isValid() && _tl != _br);}
Coordinates topLeft() const {return _tl;}
Coordinates bottomRight() const {return _br;}
Coordinates center() const
{return Coordinates((_tl.lon() + _br.lon()) / 2.0,
(_tl.lat() + _br.lat()) / 2.0);}
qreal width() const {return _br.lon() - _tl.lon();}
qreal height() const {return _br.lat() - _tl.lat();}
QSizeF size() const {return QSizeF(width(), height());}
RectC operator|(const RectC &r) const;
RectC &operator|=(const RectC &r) {*this = *this | r; return *this;}
void unite(const Coordinates &c);
private:
Coordinates _tl, _br;
};
QDebug operator<<(QDebug dbg, const RectC &rect);
#endif // RECTC_H

View File

@ -1,3 +1,4 @@
#include <cmath>
#include <QPainter>
#include "config.h"
#include "misc.h"
@ -15,6 +16,7 @@ ScaleItem::ScaleItem(QGraphicsItem *parent) : QGraphicsItem(parent)
{
_units = Metric;
_res = 1.0;
_digitalZoom = 0;
#ifndef Q_OS_MAC
setCacheMode(QGraphicsItem::DeviceCoordinateCache);
@ -87,24 +89,26 @@ QString ScaleItem::units() const
void ScaleItem::computeScale()
{
qreal res = _res * pow(2, -_digitalZoom);
if (_units == Imperial) {
_length = niceNum((_res * M2FT * SCALE_WIDTH) / SEGMENTS, 1);
_length = niceNum((res * M2FT * SCALE_WIDTH) / SEGMENTS, 1);
if (_length >= MIINFT) {
_length = niceNum((_res * M2FT * FT2MI * SCALE_WIDTH) / SEGMENTS, 1);
_width = (_length / (_res * M2FT * FT2MI));
_length = niceNum((res * M2FT * FT2MI * SCALE_WIDTH) / SEGMENTS, 1);
_width = (_length / (res * M2FT * FT2MI));
_scale = true;
} else {
_width = (_length / (_res * M2FT));
_width = (_length / (res * M2FT));
_scale = false;
}
} else {
_length = niceNum((_res * SCALE_WIDTH) / SEGMENTS, 1);
_length = niceNum((res * SCALE_WIDTH) / SEGMENTS, 1);
if (_length >= KMINM) {
_length *= M2KM;
_width = (_length / (_res * M2KM));
_width = (_length / (res * M2KM));
_scale = true;
} else {
_width = (_length / _res);
_width = (_length / res);
_scale = false;
}
}
@ -127,3 +131,14 @@ void ScaleItem::setUnits(enum Units units)
updateBoundingRect();
update();
}
void ScaleItem::setDigitalZoom(qreal zoom)
{
prepareGeometryChange();
_digitalZoom = zoom;
computeScale();
updateBoundingRect();
update();
setScale(pow(2, -_digitalZoom));
}

View File

@ -15,6 +15,7 @@ public:
void setResolution(qreal res);
void setUnits(enum Units units);
void setDigitalZoom(qreal zoom);
private:
void updateBoundingRect();
@ -27,6 +28,8 @@ private:
Units _units;
bool _scale;
qreal _digitalZoom;
QRectF _boundingRect;
};

24
src/searchpointer.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef SEARCHPOINTER_H
#define SEARCHPOINTER_H
template <class T>
class SearchPointer
{
public:
SearchPointer(const T *ptr) : _ptr(ptr) {}
const T *data() const {return _ptr;}
bool operator==(const SearchPointer<T> &other) const
{return *data() == *(other.data());}
private:
const T *_ptr;
};
template <class T>
inline uint qHash(const SearchPointer<T> &t)
{
return ::qHash(*(t.data()));
}
#endif // SEARCHPOINTER_H

View File

@ -70,12 +70,16 @@
#define EXPORT_FILENAME_SETTING "fileName"
#define EXPORT_FILENAME_DEFAULT QString("%1/export.pdf"). \
arg(QDir::currentPath())
#define RESOLUTION_SETTING "resolution"
#define RESOLUTION_DEFAULT 600
#define OPTIONS_SETTINGS_GROUP "Options"
#define PALETTE_COLOR_SETTING "paletteColor"
#define PALETTE_COLOR_DEFAULT QColor(Qt::blue)
#define PALETTE_SHIFT_SETTING "paletteShift"
#define PALETTE_SHIFT_DEFAULT 0.62
#define MAP_OPACITY_SETTING "mapOpacity"
#define MAP_OPACITY_DEFAULT 100
#define TRACK_WIDTH_SETTING "trackWidth"
#define TRACK_WIDTH_DEFAULT 3
#define ROUTE_WIDTH_SETTING "routeWidth"
@ -90,10 +94,30 @@
#define PATH_AA_DEFAULT true
#define GRAPH_AA_SETTING "graphAntiAliasing"
#define GRAPH_AA_DEFAULT false
#define ELEVATION_FILTER_SETTING "elevationFilter"
#define ELEVATION_FILTER_DEFAULT 3
#define SPEED_FILTER_SETTING "speedFilter"
#define SPEED_FILTER_DEFAULT 5
#define HEARTRATE_FILTER_SETTING "heartrateFilter"
#define HEARTRATE_FILTER_DEFAULT 3
#define CADENCE_FILTER_SETTING "cadenceFilter"
#define CADENCE_FILTER_DEFAULT 3
#define POWER_FILTER_SETTING "powerFilter"
#define POWER_FILTER_DEFAULT 3
#define OUTLIER_ELIMINATE_SETTING "outlierEliminate"
#define OUTLIER_ELIMINATE_DEFAULT true
#define PAUSE_SPEED_SETTING "pauseSpeed"
#define PAUSE_SPEED_DEFAULT 0.5 /* m/s */
#define PAUSE_INTERVAL_SETTING "pauseInterval"
#define PAUSE_INTERVAL_DEFAULT 10 /* s */
#define POI_RADIUS_SETTING "poiRadius"
#define POI_RADIUS_DEFAULT (IMPERIAL_UNITS() ? MIINM : KMINM)
#define POI_RADIUS_DEFAULT (int)(IMPERIAL_UNITS() ? MIINM : KMINM)
#define USE_OPENGL_SETTING "useOpenGL"
#define USE_OPENGL_DEFAULT false
#define PIXMAP_CACHE_SETTING "pixmapCache"
#define PIXMAP_CACHE_DEFAULT 64 /* MB */
#define HIRES_PRINT_SETTING "hiresPrint"
#define HIRES_PRINT_DEFAULT false
#define PRINT_NAME_SETTING "printName"
#define PRINT_NAME_DEFAULT true
#define PRINT_DATE_SETTING "printDate"

View File

@ -1,5 +1,6 @@
#include <cctype>
#include <QFile>
#include <QFileInfo>
#include "tar.h"
@ -29,7 +30,7 @@ struct Header
/* 500 */
};
static quint64 number(const char* data, size_t size)
static quint64 number(const char* data, size_t size, int base = 8)
{
const char *sp;
quint64 val = 0;
@ -38,18 +39,13 @@ static quint64 number(const char* data, size_t size)
if (isdigit(*sp))
break;
for (; sp < data + size && isdigit(*sp); sp++)
val = val * 8 + *sp - '0';
val = val * base + *sp - '0';
return val;
}
bool Tar::load(const QString &path)
{
char buffer[BLOCKSIZE];
struct Header *hdr = (struct Header*)&buffer;
quint64 size;
qint64 ret;
if (_file.isOpen())
_file.close();
_index.clear();
@ -58,16 +54,33 @@ bool Tar::load(const QString &path)
if (!_file.open(QIODevice::ReadOnly))
return false;
QFileInfo fi(path);
QString tmiPath = fi.path() + "/" + fi.completeBaseName() + ".tmi";
if (loadTmi(tmiPath))
return true;
else
return loadTar();
}
bool Tar::loadTar()
{
char buffer[BLOCKSIZE];
struct Header *hdr = (struct Header*)&buffer;
quint64 size;
qint64 ret;
while ((ret = _file.read(buffer, BLOCKSIZE)) > 0) {
if (ret < BLOCKSIZE) {
_file.close();
_index.clear();
return false;
}
size = number(hdr->size, sizeof(hdr->size));
if (size)
_index.insert(hdr->name, Info(size, _file.pos()));
_index.insert(hdr->name, _file.pos() / BLOCKSIZE - 1);
if (!_file.seek(_file.pos() + BLOCKCOUNT(size) * BLOCKSIZE)) {
_file.close();
_index.clear();
return false;
}
}
@ -75,15 +88,49 @@ bool Tar::load(const QString &path)
return true;
}
bool Tar::loadTmi(const QString &path)
{
quint64 block;
int ln = 1;
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
return false;
while (!file.atEnd()) {
QByteArray line = file.readLine();
int pos = line.indexOf(':');
if (line.size() < 10 || pos < 7 || !line.startsWith("block")) {
qWarning("%s:%d: syntax error\n", qPrintable(path), ln);
_index.clear();
return false;
}
block = number(line.constData() + 6, line.size() - 6, 10);
QString file(line.mid(pos + 1).trimmed());
_index.insert(file, block);
ln++;
}
return true;
}
QByteArray Tar::file(const QString &name)
{
QMap<QString, Tar::Info>::const_iterator it = _index.find(name);
char buffer[BLOCKSIZE];
struct Header *hdr = (struct Header*)&buffer;
quint64 size;
QMap<QString, quint64>::const_iterator it = _index.find(name);
if (it == _index.end())
return QByteArray();
Q_ASSERT(_file.isOpen());
if (_file.seek(it.value().offset()))
return _file.read(it.value().size());
else
if (_file.seek(it.value() * BLOCKSIZE)) {
if (_file.read(buffer, BLOCKSIZE) < BLOCKSIZE)
return QByteArray();
size = number(hdr->size, sizeof(hdr->size));
return _file.read(size);
} else
return QByteArray();
}

View File

@ -17,20 +17,11 @@ public:
bool isOpen() const {return _file.isOpen();}
private:
class Info
{
public:
Info(quint64 size, quint64 offset) : _size(size), _offset(offset) {}
quint64 size() const {return _size;}
quint64 offset() const {return _offset;}
private:
quint64 _size;
quint64 _offset;
};
bool loadTar();
bool loadTmi(const QString &path);
QFile _file;
QMap<QString, Info> _index;
QMap<QString, quint64> _index;
};
#endif // TAR_H

View File

@ -112,7 +112,7 @@ void TCXParser::trackpoints(TrackData &track)
if (_reader.name() == "Trackpoint") {
Trackpoint t;
trackpointData(t);
if (!t.coordinates().isNull())
if (t.coordinates().isValid())
track.append(t);
else
warning("Missing Trackpoint coordinates");
@ -131,7 +131,7 @@ void TCXParser::lap(TrackData &track)
}
}
void TCXParser::course(TrackData &track)
void TCXParser::course(QList<Waypoint> &waypoints, TrackData &track)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Track")
@ -143,8 +143,8 @@ void TCXParser::course(TrackData &track)
else if (_reader.name() == "CoursePoint") {
Waypoint w;
waypointData(w);
if (!w.coordinates().isNull())
_waypoints.append(w);
if (w.coordinates().isValid())
waypoints.append(w);
else
warning("Missing Trackpoint coordinates");
} else
@ -162,56 +162,54 @@ void TCXParser::activity(TrackData &track)
}
}
void TCXParser::courses()
void TCXParser::courses(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Course") {
_tracks.append(TrackData());
course(_tracks.back());
tracks.append(TrackData());
course(waypoints, tracks.back());
} else
_reader.skipCurrentElement();
}
}
void TCXParser::activities()
void TCXParser::activities(QList<TrackData> &tracks)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Activity") {
_tracks.append(TrackData());
activity(_tracks.back());
tracks.append(TrackData());
activity(tracks.back());
} else
_reader.skipCurrentElement();
}
}
void TCXParser::tcx()
void TCXParser::tcx(QList<TrackData> &tracks, QList<Waypoint> &waypoints)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "Courses")
courses();
courses(tracks, waypoints);
else if (_reader.name() == "Activities")
activities();
activities(tracks);
else
_reader.skipCurrentElement();
}
}
bool TCXParser::parse()
bool TCXParser::parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints)
{
Q_UNUSED(routes);
_reader.clear();
_reader.setDevice(file);
if (_reader.readNextStartElement()) {
if (_reader.name() == "TrainingCenterDatabase")
tcx();
tcx(tracks, waypoints);
else
_reader.raiseError("Not a TCX file");
}
return !_reader.error();
}
bool TCXParser::loadFile(QFile *file)
{
_reader.clear();
_reader.setDevice(file);
return parse();
}

View File

@ -8,20 +8,18 @@
class TCXParser : public Parser
{
public:
TCXParser(QList<TrackData> &tracks, QList<RouteData> &routes,
QList<Waypoint> &waypoints) : Parser(tracks, routes, waypoints) {}
~TCXParser() {}
bool loadFile(QFile *file);
bool parse(QFile *file, QList<TrackData> &tracks,
QList<RouteData> &routes, QList<Waypoint> &waypoints);
QString errorString() const {return _reader.errorString();}
int errorLine() const {return _reader.lineNumber();}
private:
bool parse();
void tcx();
void courses();
void activities();
void course(TrackData &track);
void tcx(QList<TrackData> &tracks, QList<Waypoint> &waypoints);
void courses(QList<TrackData> &tracks, QList<Waypoint> &waypoints);
void activities(QList<TrackData> &tracks);
void course(QList<Waypoint> &waypoints, TrackData &track);
void activity(TrackData &track);
void lap(TrackData &track);
void trackpoints(TrackData &track);

View File

@ -1,16 +1,17 @@
#include "track.h"
#define OUTLIER_WINDOW 21
#define WINDOW_OE 31
int Track::_elevationWindow = 3;
int Track::_speedWindow = 5;
int Track::_heartRateWindow = 3;
int Track::_cadenceWindow = 3;
int Track::_powerWindow = 3;
#define WINDOW_EF 3
#define WINDOW_SF 7
#define WINDOW_HF 3
#define WINDOW_CF 3
#define WINDOW_PF 3
qreal Track::_pauseSpeed = 0.5;
int Track::_pauseInterval = 10;
#define PAUSE_SPEED 0.5
#define PAUSE_TIME_DIFF 10
bool Track::_outlierEliminate = true;
static qreal median(QVector<qreal> v)
@ -48,24 +49,26 @@ static QSet<int> eliminate(const QVector<qreal> &v, int window)
static Graph filter(const Graph &g, int window)
{
qreal acc = 0;
Graph ret;
if (g.size() < window)
return ret;
return Graph();
if (window < 2)
return Graph(g);
qreal acc = 0;
Graph ret(g.size());
for (int i = 0; i < window; i++)
acc += g.at(i).y();
for (int i = 0; i <= window/2; i++)
ret.append(GraphPoint(g.at(i).s(), g.at(i).t(), acc/window));
ret[i] = GraphPoint(g.at(i).s(), g.at(i).t(), acc/window);
for (int i = window/2 + 1; i < g.size() - window/2; i++) {
acc += g.at(i + window/2).y() - g.at(i - (window/2 + 1)).y();
ret.append(GraphPoint(g.at(i).s(), g.at(i).t(), acc/window));
ret[i] = GraphPoint(g.at(i).s(), g.at(i).t(), acc/window);
}
for (int i = g.size() - window/2; i < g.size(); i++)
ret.append(GraphPoint(g.at(i).s(), g.at(i).t(), acc/window));
ret[i] = GraphPoint(g.at(i).s(), g.at(i).t(), acc/window);
return ret;
}
@ -104,15 +107,16 @@ Track::Track(const TrackData &data) : _data(data)
_pause = 0;
for (int i = 1; i < data.count(); i++) {
if (_time.at(i) > _time.at(i-1) + PAUSE_TIME_DIFF
&& _speed.at(i) < PAUSE_SPEED) {
if (_time.at(i) > _time.at(i-1) + _pauseInterval
&& _speed.at(i) < _pauseSpeed) {
_pause += _time.at(i) - _time.at(i-1);
_stop.insert(i-1);
_stop.insert(i);
}
}
_outliers = eliminate(_speed, WINDOW_OE);
if (_outlierEliminate)
_outliers = eliminate(_speed, OUTLIER_WINDOW);
QSet<int>::const_iterator it;
for (it = _stop.constBegin(); it != _stop.constEnd(); ++it)
@ -137,7 +141,7 @@ Graph Track::elevation() const
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).elevation()));
return filter(raw, WINDOW_EF);
return filter(raw, _elevationWindow);
}
Graph Track::speed() const
@ -161,7 +165,7 @@ Graph Track::speed() const
raw.append(GraphPoint(_distance.at(i), _time.at(i), v));
}
filtered = filter(raw, WINDOW_SF);
filtered = filter(raw, _speedWindow);
QSet<int>::const_iterator it;
for (it = stop.constBegin(); it != stop.constEnd(); ++it)
@ -179,7 +183,7 @@ Graph Track::heartRate() const
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).heartRate()));
return filter(raw, WINDOW_HF);
return filter(raw, _heartRateWindow);
}
Graph Track::temperature() const
@ -212,7 +216,7 @@ Graph Track::cadence() const
raw.append(GraphPoint(_distance.at(i), _time.at(i), c));
}
filtered = filter(raw, WINDOW_CF);
filtered = filter(raw, _cadenceWindow);
QSet<int>::const_iterator it;
for (it = stop.constBegin(); it != stop.constEnd(); ++it)
@ -239,7 +243,7 @@ Graph Track::power() const
raw.append(GraphPoint(_distance.at(i), _time.at(i), p));
}
filtered = filter(raw, WINDOW_PF);
filtered = filter(raw, _powerWindow);
QSet<int>::const_iterator it;
for (it = stop.constBegin(); it != stop.constEnd(); ++it)

View File

@ -33,6 +33,16 @@ public:
bool isNull() const {return (_data.size() < 2);}
static void setElevationFilter(int window) {_elevationWindow = window;}
static void setSpeedFilter(int window) {_speedWindow = window;}
static void setHeartRateFilter(int window) {_heartRateWindow = window;}
static void setCadenceFilter(int window) {_cadenceWindow = window;}
static void setPowerFilter(int window) {_powerWindow = window;}
static void setPauseSpeed(qreal speed) {_pauseSpeed = speed;}
static void setPauseInterval(int interval) {_pauseInterval = interval;}
static void setOutlierElimination(bool eliminate)
{_outlierEliminate = eliminate;}
private:
bool discardStopPoint(int i) const;
@ -46,6 +56,17 @@ private:
QSet<int> _stop;
qreal _pause;
static bool _outlierEliminate;
static int _elevationWindow;
static int _speedWindow;
static int _heartRateWindow;
static int _cadenceWindow;
static int _powerWindow;
static qreal _pauseSpeed;
static int _pauseInterval;
};
#endif // TRACK_H

View File

@ -16,11 +16,9 @@ void TrackInfo::insert(const QString &key, const QString &value)
_info->insert(key, value);
}
void TrackInfo::plot(QPainter *painter, const QRectF &target)
void TrackInfo::plot(QPainter *painter, const QRectF &target, qreal scale)
{
qreal ratio = painter->paintEngine()->paintDevice()->logicalDpiX()
/ SCREEN_DPI;
QSizeF canvas = QSizeF(target.width() / ratio, target.height() / ratio);
QSizeF canvas = QSizeF(target.width() / scale, target.height() / scale);
QSizeF diff = QSizeF(qAbs(canvas.width() - sceneRect().width()),
qAbs(canvas.height() - sceneRect().height()));
QRectF adj = sceneRect().adjusted(0, -diff.height()/2, diff.width(),

View File

@ -13,7 +13,7 @@ public:
TrackInfo(QObject *parent = 0);
void insert(const QString &key, const QString &value);
void plot(QPainter *painter, const QRectF &target);
void plot(QPainter *painter, const QRectF &target, qreal scale);
bool isEmpty() const;
QSizeF contentSize() const;

View File

@ -6,6 +6,5 @@ QDebug operator<<(QDebug dbg, const Trackpoint &trackpoint)
<< trackpoint.timestamp() << ", " << trackpoint.elevation() << ", "
<< trackpoint.speed() << ", " << trackpoint.heartRate() << ", "
<< trackpoint.temperature() << ")";
return dbg.maybeSpace();
return dbg.space();
}

View File

@ -1,93 +1,256 @@
/*
* Based on libgeotrans with the following Source Code Disclaimer:
1. The GEOTRANS source code ("the software") is provided free of charge by
the National Imagery and Mapping Agency (NIMA) of the United States
Department of Defense. Although NIMA makes no copyright claim under Title 17
U.S.C., NIMA claims copyrights in the source code under other legal regimes.
NIMA hereby grants to each user of the software a license to use and
distribute the software, and develop derivative works.
2. Warranty Disclaimer: The software was developed to meet only the internal
requirements of the U.S. National Imagery and Mapping Agency. The software
is provided "as is," and no warranty, express or implied, including but not
limited to the implied warranties of merchantability and fitness for
particular purpose or arising by statute or otherwise in law or from a
course of dealing or usage in trade, is made by NIMA as to the accuracy and
functioning of the software.
3. NIMA and its personnel are not required to provide technical support or
general assistance with respect to the software.
4. Neither NIMA nor its personnel will be liable for any claims, losses, or
damages arising from or connected with the use of the software. The user
agrees to hold harmless the United States National Imagery and Mapping
Agency. The user's sole and exclusive remedy is to stop using the software.
5. NIMA requests that products developed using the software credit the
source of the software with the following statement, "The product was
developed using GEOTRANS, a product of the National Imagery and Mapping
Agency and U.S. Army Engineering Research and Development Center."
6. For any products developed using the software, NIMA requires a disclaimer
that use of the software does not indicate endorsement or approval of the
product by the Secretary of Defense or the National Imagery and Mapping
Agency. Pursuant to the United States Code, 10 U.S.C. Sec. 2797, the name of
the National Imagery and Mapping Agency, the initials "NIMA", the seal of
the National Imagery and Mapping Agency, or any colorable imitation thereof
shall not be used to imply approval, endorsement, or authorization of a
product without prior written permission from United States Secretary of
Defense.
*/
#include <cmath>
#include "rd.h"
#include "ellipsoid.h"
#include "transversemercator.h"
#define SPHSN(lat) \
((double)(_e.radius() / sqrt(1.e0 - _es * pow(sin(lat), 2))))
#define SPHTMD(lat) \
((double)(_ap * lat - _bp * sin(2.e0 * lat) + _cp * sin(4.e0 * lat) \
- _dp * sin(6.e0 * lat) + _ep * sin(8.e0 * lat)))
#define DENOM(lat) \
((double)(sqrt(1.e0 - _es * pow(sin(lat),2))))
#define SPHSR(lat) \
((double)(_e.radius() * (1.e0 - _es) / pow(DENOM(lat), 3)))
TransverseMercator::TransverseMercator(const Ellipsoid &ellipsoid,
double centralMeridian, double scale, double falseEasting,
double falseNorthing)
double latitudeOrigin, double longitudeOrigin, double scale,
double falseEasting, double falseNorthing)
{
_centralMeridian = centralMeridian;
double tn, tn2, tn3, tn4, tn5;
double b;
_e = ellipsoid;
_longitudeOrigin = deg2rad(longitudeOrigin);
_latitudeOrigin = deg2rad(latitudeOrigin);
_scale = scale;
_falseEasting = falseEasting;
_falseNorthing = falseNorthing;
_es = 2 * _e.flattening() - _e.flattening() * _e.flattening();
_ebs = (1 / (1 - _es)) - 1;
const double e2 = ellipsoid.flattening() * (2 - ellipsoid.flattening());
const double n = ellipsoid.flattening() / (2 - ellipsoid.flattening());
_rectifyingRadius = ellipsoid.radius() / (1 + n)
* (1 + 0.25*pow(n, 2) + 0.015625*pow(n, 4));
b = _e.radius() * (1 - _e.flattening());
_A = e2;
_B = (5 * pow(e2, 2) - pow(e2, 3)) / 6.0;
_C = (104 * pow(e2, 3) - 45 * pow(e2, 4)) / 120.0;
_D = (1237 * pow(e2, 4)) / 1260.0;
tn = (_e.radius() - b) / (_e.radius() + b);
tn2 = tn * tn;
tn3 = tn2 * tn;
tn4 = tn3 * tn;
tn5 = tn4 * tn;
_beta1 = 1/2.0 * n - 2/3.0 * pow(n, 2) + 5/16.0 * pow(n, 3) + 41/180.0
* pow(n, 4);
_beta2 = 13/48.0 * pow(n, 2) - 3/5.0 * pow(n, 3) + 557/1440.0 * pow(n, 4);
_beta3 = 61/240.0 * pow(n, 3) - 103/140.0 * pow(n, 4);
_beta4 = 49561/161280.0 * pow(n, 4);
_delta1 = 1/2.0 * n - 2/3.0 * pow(n, 2) + 37/96.0 * pow(n, 3) - 1/360.0
* pow(n, 4);
_delta2 = 1/48.0 * pow(n, 2) + 1/15.0 * pow(n, 3) - 437/1440.0 * pow(n, 4);
_delta3 = 17/480.0 * pow(n, 3) - 37/840.0 * pow(n, 4);
_delta4 = 4397/161280.0 * pow(n, 4);
_AStar = e2 + pow(e2, 2) + pow(e2, 3) + pow(e2, 4);
_BStar = (7 * pow(e2, 2) + 17 * pow(e2, 3) + 30 * pow(e2, 4)) / -6;
_CStar = (224 * pow(e2, 3) + 889 * pow(e2, 4)) / 120;
_DStar = (4279 * pow(e2, 4)) / -1260;
_ap = _e.radius() * (1.e0 - tn + 5.e0 * (tn2 - tn3) / 4.e0 + 81.e0
* (tn4 - tn5) / 64.e0);
_bp = 3.e0 * _e.radius() * (tn - tn2 + 7.e0 * (tn3 - tn4) / 8.e0 + 55.e0
* tn5 / 64.e0 ) / 2.e0;
_cp = 15.e0 * _e.radius() * (tn2 - tn3 + 3.e0 * (tn4 - tn5 ) / 4.e0) / 16.0;
_dp = 35.e0 * _e.radius() * (tn3 - tn4 + 11.e0 * tn5 / 16.e0) / 48.e0;
_ep = 315.e0 * _e.radius() * (tn4 - tn5) / 512.e0;
}
QPointF TransverseMercator::ll2xy(const Coordinates &c) const
{
QPointF p;
double rl;
double cl, c2, c3, c5, c7;
double dlam;
double eta, eta2, eta3, eta4;
double sl, sn;
double t, tan2, tan3, tan4, tan5, tan6;
double t1, t2, t3, t4, t5, t6, t7, t8, t9;
double tmd, tmdo;
double x, y;
double phi = deg2rad(c.lat());
double lambda = deg2rad(c.lon());
double lambda0 = deg2rad(_centralMeridian);
double deltaLambda = lambda - lambda0;
dlam = deg2rad(c.lon()) - _longitudeOrigin;
double phiStar = phi - sin(phi) * cos(phi) * (_A + _B*pow(sin(phi), 2)
+ _C*pow(sin(phi), 4) + _D*pow(sin(phi), 6));
if (dlam > M_PI)
dlam -= (2 * M_PI);
if (dlam < -M_PI)
dlam += (2 * M_PI);
if (fabs(dlam) < 2.e-10)
dlam = 0.0;
double xiPrim = atan(tan(phiStar) / cos(deltaLambda));
double etaPrim = atanh(cos(phiStar) * sin(deltaLambda));
rl = deg2rad(c.lat());
sl = sin(rl);
cl = cos(rl);
c2 = cl * cl;
c3 = c2 * cl;
c5 = c3 * c2;
c7 = c5 * c2;
t = sl / cl;
tan2 = t * t;
tan3 = tan2 * t;
tan4 = tan3 * t;
tan5 = tan4 * t;
tan6 = tan5 * t;
eta = _ebs * c2;
eta2 = eta * eta;
eta3 = eta2 * eta;
eta4 = eta3 * eta;
p.ry() = _falseNorthing + _scale * _rectifyingRadius * (xiPrim + _beta1
* sin(2*xiPrim) * cosh(2*etaPrim) + _beta2 * sin(4*xiPrim)
* cosh(4*etaPrim) + _beta3 * sin(6*xiPrim) * cosh(6*etaPrim) + _beta4
* sin(8*xiPrim) * cosh(8*etaPrim));
p.rx() = _falseEasting + _scale * _rectifyingRadius * (etaPrim + _beta1
* cos(2*xiPrim) * sinh(2*etaPrim) + _beta2 * cos(4*xiPrim)
* sinh(4*etaPrim) + _beta3 * cos(6*xiPrim) * sinh(6*etaPrim) + _beta4
* cos(8*xiPrim) * sinh(8*etaPrim));
sn = SPHSN(rl);
tmd = SPHTMD(rl);
tmdo = SPHTMD (_latitudeOrigin);
return p;
t1 = (tmd - tmdo) * _scale;
t2 = sn * sl * cl * _scale / 2.e0;
t3 = sn * sl * c3 * _scale * (5.e0 - tan2 + 9.e0 * eta + 4.e0 * eta2)
/ 24.e0;
t4 = sn * sl * c5 * _scale * (61.e0 - 58.e0 * tan2 + tan4 + 270.e0 * eta
- 330.e0 * tan2 * eta + 445.e0 * eta2 + 324.e0 * eta3 - 680.e0 * tan2
* eta2 + 88.e0 * eta4 - 600.e0 * tan2 * eta3 - 192.e0 * tan2 * eta4)
/ 720.e0;
t5 = sn * sl * c7 * _scale * (1385.e0 - 3111.e0 * tan2 + 543.e0 * tan4
- tan6) / 40320.e0;
y = _falseNorthing + t1 + pow(dlam, 2.e0) * t2 + pow(dlam, 4.e0) * t3
+ pow(dlam, 6.e0) * t4 + pow(dlam, 8.e0) * t5;
t6 = sn * cl * _scale;
t7 = sn * c3 * _scale * (1.e0 - tan2 + eta) /6.e0;
t8 = sn * c5 * _scale * (5.e0 - 18.e0 * tan2 + tan4 + 14.e0 * eta - 58.e0
* tan2 * eta + 13.e0 * eta2 + 4.e0 * eta3 - 64.e0 * tan2 * eta2 - 24.e0
* tan2 * eta3) / 120.e0;
t9 = sn * c7 * _scale * (61.e0 - 479.e0 * tan2 + 179.e0 * tan4 - tan6)
/ 5040.e0;
x = _falseEasting + dlam * t6 + pow(dlam, 3.e0) * t7 + pow(dlam, 5.e0)
* t8 + pow(dlam, 7.e0) * t9;
return QPointF(x, y);
}
Coordinates TransverseMercator::xy2ll(const QPointF &p) const
{
double xi = (p.y() - _falseNorthing) / (_scale * _rectifyingRadius);
double eta = (p.x() - _falseEasting) / (_scale * _rectifyingRadius);
double cl;
double de;
double dlam;
double eta, eta2, eta3, eta4;
double ftphi;
double sn;
double sr;
double t, tan2, tan4;
double t10, t11, t12, t13, t14, t15, t16, t17;
double tmd, tmdo;
double lat, lon;
double xiPrim = xi - _delta1 * sin(2*xi) * cosh(2*eta) - _delta2 * sin(4*xi)
* cosh(4*eta) - _delta3 * sin(6*xi) * cosh(6*eta) - _delta4 * sin(8*xi)
* cosh(8*eta);
double etaPrim = eta - _delta1 * cos(2*xi) * sinh(2*eta) - _delta2
* cos(4*xi) * sinh(4*eta) - _delta3 * cos(6*xi) * sinh(6*eta) - _delta4
* cos(8*xi) * sinh(8*eta);
double phiStar = asin(sin(xiPrim) / cosh(etaPrim));
double deltaLambda = atan(sinh(etaPrim) / cos(xiPrim));
tmdo = SPHTMD(_latitudeOrigin);
tmd = tmdo + (p.y() - _falseNorthing) / _scale;
double phi = phiStar + sin(phiStar) * cos(phiStar) * (_AStar + _BStar
* pow(sin(phiStar), 2) + _CStar * pow(sin(phiStar), 4) + _DStar
* pow(sin(phiStar), 6));
sr = SPHSR(0.e0);
ftphi = tmd / sr;
return Coordinates(_centralMeridian + rad2deg(deltaLambda), rad2deg(phi));
for (int i = 0; i < 5 ; i++) {
t10 = SPHTMD(ftphi);
sr = SPHSR(ftphi);
ftphi = ftphi + (tmd - t10) / sr;
}
sr = SPHSR(ftphi);
sn = SPHSN(ftphi);
cl = cos(ftphi);
t = tan(ftphi);
tan2 = t * t;
tan4 = tan2 * tan2;
eta = _ebs * pow(cl, 2);
eta2 = eta * eta;
eta3 = eta2 * eta;
eta4 = eta3 * eta;
de = p.x() - _falseEasting;
if (fabs(de) < 0.0001)
de = 0.0;
t10 = t / (2.e0 * sr * sn * pow(_scale, 2));
t11 = t * (5.e0 + 3.e0 * tan2 + eta - 4.e0 * pow(eta, 2) - 9.e0 * tan2
* eta) / (24.e0 * sr * pow(sn, 3) * pow(_scale, 4));
t12 = t * (61.e0 + 90.e0 * tan2 + 46.e0 * eta + 45.E0 * tan4 - 252.e0 * tan2
* eta - 3.e0 * eta2 + 100.e0 * eta3 - 66.e0 * tan2 * eta2 - 90.e0 * tan4
* eta + 88.e0 * eta4 + 225.e0 * tan4 * eta2 + 84.e0 * tan2 * eta3 - 192.e0
* tan2 * eta4) / (720.e0 * sr * pow(sn, 5) * pow(_scale, 6));
t13 = t * (1385.e0 + 3633.e0 * tan2 + 4095.e0 * tan4 + 1575.e0 * pow(t,6))
/ (40320.e0 * sr * pow(sn, 7) * pow(_scale, 8));
lat = ftphi - pow(de, 2) * t10 + pow(de, 4) * t11 - pow(de, 6) * t12
+ pow(de, 8) * t13;
t14 = 1.e0 / (sn * cl * _scale);
t15 = (1.e0 + 2.e0 * tan2 + eta) / (6.e0 * pow(sn, 3) * cl * pow(_scale, 3));
t16 = (5.e0 + 6.e0 * eta + 28.e0 * tan2 - 3.e0 * eta2 + 8.e0 * tan2 * eta
+ 24.e0 * tan4 - 4.e0 * eta3 + 4.e0 * tan2 * eta2 + 24.e0 * tan2 * eta3)
/ (120.e0 * pow(sn, 5) * cl * pow(_scale, 5));
t17 = (61.e0 + 662.e0 * tan2 + 1320.e0 * tan4 + 720.e0 * pow(t,6))
/ (5040.e0 * pow(sn, 7) * cl * pow(_scale, 7));
dlam = de * t14 - pow(de, 3) * t15 + pow(de, 5) * t16 - pow(de, 7) * t17;
lon = _longitudeOrigin + dlam;
while (lat > deg2rad(90.0)) {
lat = M_PI - lat;
lon += M_PI;
if (lon > M_PI)
lon -= (2 * M_PI);
}
while (lat < deg2rad(-90.0)) {
lat = - (lat + M_PI);
lon += M_PI;
if (lon > M_PI)
lon -= (2 * M_PI);
}
if (lon > (2 * M_PI))
lon -= (2 * M_PI);
if (lon < -M_PI)
lon += (2 * M_PI);
return Coordinates(rad2deg(lon), rad2deg(lat));
}

View File

@ -2,29 +2,29 @@
#define TRANSVERSEMERCATOR_H
#include "projection.h"
class Ellipsoid;
#include "ellipsoid.h"
class TransverseMercator : public Projection
{
public:
TransverseMercator(const Ellipsoid &ellipsoid, double centralMeridian,
double scale, double falseEasting, double falseNorthing);
TransverseMercator(const Ellipsoid &ellipsoid, 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:
double _centralMeridian;
Ellipsoid _e;
double _longitudeOrigin;
double _latitudeOrigin;
double _scale;
double _falseEasting;
double _falseNorthing;
double _rectifyingRadius;
double _A, _B, _C, _D;
double _beta1, _beta2, _beta3, _beta4;
double _delta1, _delta2, _delta3, _delta4;
double _AStar, _BStar, _CStar, _DStar;
double _es;
double _ebs;
double _ap, _bp, _cp, _dp, _ep;
};
#endif // TRANSVERSEMERCATOR_H

View File

@ -25,10 +25,6 @@ enum Units {
#define C2FS 1.8 // Celsius to Farenheit - scale
#define C2FO 32.0 // Celsius to Farenheit - offset
#ifdef Q_OS_WIN32
#define UNIT_SPACE QString(" ")
#else // Q_OS_WIN32
#define UNIT_SPACE QString::fromUtf8("\xE2\x80\x89")
#endif // Q_OS_WIN32
#endif // UNITS_H

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