1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-18 12:54:23 +02:00

Compare commits

...

112 Commits
7.21 ... 7.29

Author SHA1 Message Date
cbe312d9c8 Version++ 2020-04-19 14:52:40 +02:00
08334d7fde Move the world maps bounds limit hack to the propper place 2020-04-19 11:36:17 +02:00
33bbd6a592 Yet another special case 2020-04-18 00:00:48 +02:00
7811527239 Rather show less road shields than more 2020-04-15 22:48:28 +02:00
31da4e1906 Some more default IMG style tweaking 2020-04-15 22:48:02 +02:00
652cbd7c11 Fixed Qt4 build 2020-04-09 10:17:30 +02:00
eb0ff84379 Code cleanup 2020-04-08 22:28:35 +02:00
6ee3a8ea8d Added support for FIT course points 2020-04-08 00:54:35 +02:00
ee3d43e249 A slightly darker white 2020-04-08 00:00:43 +02:00
242babb741 Improved default IMG style
("less green")
2020-04-07 00:54:31 +02:00
e26d1abd5a Version++ 2020-04-05 10:50:24 +02:00
e80d16bec5 Merge branch 'origin/master' into Weblate. 2020-04-05 10:39:16 +02:00
412ae74bfa Fixed broken map enable condition
(falsly enabled map can crash)
2020-04-05 10:38:16 +02:00
1c472e47b9 Translated using Weblate (Hungarian)
Currently translated at 100.0% (356 of 356 strings)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/sv/
2020-03-18 16:45:12 +01:00
e2f2e9700f Localization update 2020-03-17 21:13:21 +01:00
654bfcd058 Version++ 2020-03-17 21:10:45 +01:00
82c0c1f8a7 Asynchronous WMS/WMTS map loading
(also fixes crash on OS X)
2020-03-17 21:06:51 +01:00
9ce6e16b60 Fixed graph axis ticks when range < min range and units != m
Fixes #278
2020-03-14 23:55:57 +01:00
98cd3c3922 Merge branch 'master' of https://github.com/tumic0/GPXSee 2020-03-12 09:37:55 +01:00
a776f1d30e Added missing button group on OS X
fixes #276
2020-03-12 09:36:23 +01:00
aea17c9fed Added support for GPX comments (cmt tag)
Closes #272
2020-03-09 20:04:13 +01:00
23c18d4acd Optimization 2020-03-07 19:24:39 +01:00
eb8fc7b540 Merge branch 'origin/master' into Weblate. 2020-03-04 19:49:27 +01:00
bf0dd1b24a Version++ 2020-03-04 19:49:11 +01:00
9b687bb830 Merge branch 'origin/master' into Weblate. 2020-03-04 19:48:15 +01:00
9859608115 Added missing support for URLs defined in OnlineResources 2020-03-04 19:47:23 +01:00
3d66b2fbb6 Merge branch 'origin/master' into Weblate. 2020-03-03 09:39:27 +01:00
9f62b7114e The service parameter is expected in the GetMap request by some servers
(The WMS specification is not 100% clear here)
2020-03-03 09:38:18 +01:00
c8f7e6f691 Merge branch 'origin/master' into Weblate. 2020-03-03 09:29:47 +01:00
c85f404d28 Enable specifiing of format parameters 2020-03-03 09:29:16 +01:00
273a0f0f27 Merge branch 'origin/master' into Weblate. 2020-03-01 14:39:44 +01:00
bb6d6a4044 Version++ 2020-03-01 14:39:40 +01:00
bd3a3b90bc Merge branch 'origin/master' into Weblate. 2020-03-01 13:59:42 +01:00
521369a6ec Make the WMS tile size configurable 2020-03-01 13:59:15 +01:00
440a5736f6 Merge branch 'origin/master' into Weblate. 2020-03-01 13:26:52 +01:00
45a6cdeda0 Strip the format parameters for format comparsion 2020-03-01 13:26:19 +01:00
f73c27c39c Merge branch 'origin/master' into Weblate. 2020-03-01 11:46:52 +01:00
12827edcb2 Removed obsolete include 2020-03-01 11:46:44 +01:00
3ec5c37fd5 Merge branch 'origin/master' into Weblate. 2020-03-01 11:43:41 +01:00
ee24bd54f1 Fixed tile cache reload issues 2020-03-01 11:43:08 +01:00
cc22df3bf2 Cosmetics 2020-03-01 10:30:00 +01:00
ef017edbf6 Merge branch 'origin/master' into Weblate. 2020-02-29 21:40:28 +01:00
d7f0cda4b2 Properly parse the ScaleHint tag 2020-02-29 21:40:13 +01:00
dc03ab91d6 Merge branch 'origin/master' into Weblate. 2020-02-29 20:12:26 +01:00
a898ff2807 Use 72dpi in the ScaleHint to scaleDenominator transformation 2020-02-29 20:11:49 +01:00
497017091f Merge branch 'origin/master' into Weblate. 2020-02-29 13:48:19 +01:00
9dd4e117f6 Added missing WMS 1.1 ScaleHint handling 2020-02-29 13:47:27 +01:00
86535021aa Merge branch 'origin/master' into Weblate. 2020-02-23 12:45:59 +01:00
92deaaaf2b Use the latest xmlns 2020-02-23 12:45:36 +01:00
86a943d143 Translated using Weblate (Polish)
Currently translated at 98.5% (346 of 351 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/pl/
2020-02-20 12:42:26 +01:00
015a9187a0 Fixed memory leak
(formal only, the data is allocated during the whole application life anyway)
2020-02-20 09:02:01 +01:00
1de9c6ef5d Version++ 2020-02-17 20:19:11 +01:00
54b6225c6c Fixed "rect inversion" problems 2020-02-17 19:23:36 +01:00
48c7299ba6 Improved railroad lines default style 2020-02-17 19:21:36 +01:00
c284b9fa7c Back to lon, lat order to not correspond with all the APIs 2020-02-17 19:20:18 +01:00
2c503a2406 Enable world basemap projection in web mercator projection 2020-02-17 09:47:47 +01:00
27edc4d6b5 Properly handle IMG basemaps (gmapbmap.img) 2020-02-17 09:19:15 +01:00
f333a76ef7 Some more regions/countries rendering improvement 2020-02-16 20:31:09 +01:00
2c114f43c5 Code cleanup 2020-02-16 15:59:51 +01:00
29e29591f8 Fixed typo 2020-02-16 13:59:19 +01:00
e4ac9fda0e Improved country names labels handling 2020-02-16 12:57:40 +01:00
26229e5871 Fixed tile bounds exceeding map bounds 2020-02-16 08:43:37 +01:00
64bee2f2f4 Use a beter default min zoom value 2020-02-15 21:58:29 +01:00
e4288ee95c Remove devel code 2020-02-15 21:51:47 +01:00
c9b3c2eedd Compute the min map zoom from the tiles 2020-02-15 21:49:00 +01:00
42e4b0769f Fixed broken tile/subdivs/polygon bounds/coordinates
+ do the coordinates left shift in a C++ standard defined way
2020-02-15 11:46:16 +01:00
ce043ef8fa Do not try to open the user style file when it is not set 2020-02-14 20:00:42 +01:00
8c7050e273 Fixed broken subdiv bounds 2020-02-14 09:20:10 +01:00
d670107a11 Fixed gap between the basemap and normal map tiles on some maps 2020-02-13 22:49:15 +01:00
7b03c4d852 Fixed error handling 2020-02-13 22:48:47 +01:00
2002b828dd Version++ 2020-02-12 20:35:59 +01:00
71f0e1d0ac Cleanup the data loading code 2020-02-12 20:33:23 +01:00
eb9767f2dd Cleanup the map loading code 2020-02-12 20:32:57 +01:00
8d06ab6208 Enable loading of GMAP maps when Garmin BaseCamp has associated them 2020-02-12 09:37:03 +01:00
187cb77858 Added missing Hungarian localization copy 2020-02-12 08:36:56 +01:00
8167a995f6 Added GMAP support info 2020-02-11 23:31:51 +01:00
107 changed files with 5680 additions and 4812 deletions

View File

@ -1,4 +1,4 @@
version: 7.21.{build} version: 7.29.{build}
configuration: configuration:
- Release - Release

View File

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

View File

@ -3,7 +3,7 @@ unix:!macx {
} else { } else {
TARGET = GPXSee TARGET = GPXSee
} }
VERSION = 7.21 VERSION = 7.29
QT += core \ QT += core \
gui \ gui \
@ -20,6 +20,7 @@ equals(QT_MAJOR_VERSION, 5) : lessThan(QT_MINOR_VERSION, 4) {QT += opengl}
INCLUDEPATH += ./src INCLUDEPATH += ./src
HEADERS += src/common/config.h \ HEADERS += src/common/config.h \
src/GUI/graphicsscene.h \ src/GUI/graphicsscene.h \
src/GUI/mapaction.h \
src/GUI/popup.h \ src/GUI/popup.h \
src/common/garmin.h \ src/common/garmin.h \
src/common/staticassert.h \ src/common/staticassert.h \
@ -248,6 +249,7 @@ SOURCES += src/main.cpp \
src/GUI/gearratiographitem.cpp \ src/GUI/gearratiographitem.cpp \
src/GUI/mapview.cpp \ src/GUI/mapview.cpp \
src/GUI/areaitem.cpp \ src/GUI/areaitem.cpp \
src/data/waypoint.cpp \
src/map/IMG/bitmapline.cpp \ src/map/IMG/bitmapline.cpp \
src/map/IMG/bitstream.cpp \ src/map/IMG/bitstream.cpp \
src/map/IMG/deltastream.cpp \ src/map/IMG/deltastream.cpp \
@ -383,7 +385,8 @@ macx {
lang/gpxsee_tr.qm \ lang/gpxsee_tr.qm \
lang/gpxsee_es.qm \ lang/gpxsee_es.qm \
lang/gpxsee_pt_BR.qm \ lang/gpxsee_pt_BR.qm \
lang/gpxsee_uk.qm lang/gpxsee_uk.qm \
lang/gpxsee_hu.qm
csv.path = Contents/Resources csv.path = Contents/Resources
csv.files = pkg/csv csv.files = pkg/csv
maps.path = Contents/Resources maps.path = Contents/Resources

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
; The name of the installer ; The name of the installer
Name "GPXSee" Name "GPXSee"
; Program version ; Program version
!define VERSION "7.21" !define VERSION "7.29"
; The file to write ; The file to write
OutFile "GPXSee-${VERSION}.exe" OutFile "GPXSee-${VERSION}.exe"

View File

@ -7,7 +7,7 @@
; The name of the installer ; The name of the installer
Name "GPXSee" Name "GPXSee"
; Program version ; Program version
!define VERSION "7.21" !define VERSION "7.29"
; The file to write ; The file to write
OutFile "GPXSee-${VERSION}_x64.exe" OutFile "GPXSee-${VERSION}_x64.exe"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<map xmlns="http://www.gpxsee.org/map/1.3"> <map xmlns="http://www.gpxsee.org/map/1.4">
<name>4UMaps</name> <name>4UMaps</name>
<url>https://tileserver.4umaps.com/$z/$x/$y.png</url> <url>https://tileserver.4umaps.com/$z/$x/$y.png</url>
<zoom min="2" max="15"/> <zoom min="2" max="15"/>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -51,6 +51,7 @@
#include "graphtab.h" #include "graphtab.h"
#include "graphitem.h" #include "graphitem.h"
#include "pathitem.h" #include "pathitem.h"
#include "mapaction.h"
#include "gui.h" #include "gui.h"
@ -58,7 +59,6 @@
GUI::GUI() GUI::GUI()
{ {
loadMaps();
loadPOIs(); loadPOIs();
createMapView(); createMapView();
@ -106,24 +106,13 @@ GUI::GUI()
updateStatusBarInfo(); updateStatusBarInfo();
} }
void GUI::loadMaps()
{
_ml = new MapList(this);
QString mapDir(ProgramPaths::mapDir());
if (!mapDir.isNull() && !_ml->loadDir(mapDir))
qWarning("%s", qPrintable(_ml->errorString()));
_map = new EmptyMap(this);
}
void GUI::loadPOIs() void GUI::loadPOIs()
{ {
_poi = new POI(this); _poi = new POI(this);
QString poiDir(ProgramPaths::poiDir());
if (!poiDir.isNull() && !_poi->loadDir(poiDir)) QString poiDir(ProgramPaths::poiDir());
qWarning("%s", qPrintable(_poi->errorString())); if (!poiDir.isNull())
_poi->loadDir(poiDir);
} }
void GUI::createBrowser() void GUI::createBrowser()
@ -134,40 +123,56 @@ void GUI::createBrowser()
void GUI::createMapActions() void GUI::createMapActions()
{ {
_mapsSignalMapper = new QSignalMapper(this);
_mapsActionGroup = new QActionGroup(this); _mapsActionGroup = new QActionGroup(this);
_mapsActionGroup->setExclusive(true); _mapsActionGroup->setExclusive(true);
for (int i = 0; i < _ml->maps().count(); i++) QString mapDir(ProgramPaths::mapDir());
createMapAction(_ml->maps().at(i)); if (mapDir.isNull())
return;
connect(_mapsSignalMapper, SIGNAL(mapped(int)), this, QString unused;
SLOT(mapChanged(int))); QList<Map*> maps(MapList::loadMaps(mapDir, unused));
for (int i = 0; i < maps.count(); i++) {
MapAction *a = createMapAction(maps.at(i));
connect(a, SIGNAL(loaded()), this, SLOT(mapInitialized()));
}
} }
QAction *GUI::createMapAction(const Map *map) MapAction *GUI::createMapAction(Map *map)
{ {
QAction *a = new QAction(map->name(), this); MapAction *a = new MapAction(map);
a->setMenuRole(QAction::NoRole); a->setMenuRole(QAction::NoRole);
a->setCheckable(true); a->setCheckable(true);
a->setActionGroup(_mapsActionGroup); a->setActionGroup(_mapsActionGroup);
connect(a, SIGNAL(triggered()), this, SLOT(mapChanged()));
_mapActions.append(a);
_mapsSignalMapper->setMapping(a, _mapActions.size() - 1);
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
return a; return a;
} }
void GUI::mapInitialized()
{
MapAction *action = static_cast<MapAction*>(QObject::sender());
Map *map = action->data().value<Map*>();
if (map->isValid()) {
if (!_mapsActionGroup->checkedAction())
action->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} else {
qWarning("%s: %s", qPrintable(map->name()), qPrintable(map->errorString()));
action->deleteLater();
}
}
void GUI::createPOIFilesActions() void GUI::createPOIFilesActions()
{ {
_poiFilesSignalMapper = new QSignalMapper(this); _poiFilesSignalMapper = new QSignalMapper(this);
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
for (int i = 0; i < _poi->files().count(); i++) for (int i = 0; i < _poi->files().count(); i++)
createPOIFileAction(_poi->files().at(i)); createPOIFileAction(_poi->files().at(i));
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
SLOT(poiFileChecked(int)));
} }
QAction *GUI::createPOIFileAction(const QString &fileName) QAction *GUI::createPOIFileAction(const QString &fileName)
@ -281,8 +286,10 @@ void GUI::createActions()
createPOIFilesActions(); createPOIFilesActions();
// Map actions // Map actions
createMapActions();
_showMapAction = new QAction(QIcon(SHOW_MAP_ICON), tr("Show map"), _showMapAction = new QAction(QIcon(SHOW_MAP_ICON), tr("Show map"),
this); this);
_showMapAction->setEnabled(false);
_showMapAction->setMenuRole(QAction::NoRole); _showMapAction->setMenuRole(QAction::NoRole);
_showMapAction->setCheckable(true); _showMapAction->setCheckable(true);
_showMapAction->setShortcut(SHOW_MAP_SHORTCUT); _showMapAction->setShortcut(SHOW_MAP_SHORTCUT);
@ -294,10 +301,10 @@ void GUI::createActions()
_loadMapAction->setMenuRole(QAction::NoRole); _loadMapAction->setMenuRole(QAction::NoRole);
connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap())); connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap()));
_clearMapCacheAction = new QAction(tr("Clear tile cache"), this); _clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
_clearMapCacheAction->setEnabled(false);
_clearMapCacheAction->setMenuRole(QAction::NoRole); _clearMapCacheAction->setMenuRole(QAction::NoRole);
connect(_clearMapCacheAction, SIGNAL(triggered()), _mapView, connect(_clearMapCacheAction, SIGNAL(triggered()), _mapView,
SLOT(clearMapCache())); SLOT(clearMapCache()));
createMapActions();
_nextMapAction = new QAction(tr("Next map"), this); _nextMapAction = new QAction(tr("Next map"), this);
_nextMapAction->setMenuRole(QAction::NoRole); _nextMapAction->setMenuRole(QAction::NoRole);
_nextMapAction->setShortcut(NEXT_MAP_SHORTCUT); _nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
@ -308,10 +315,6 @@ void GUI::createActions()
_prevMapAction->setShortcut(PREV_MAP_SHORTCUT); _prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap())); connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap()));
addAction(_prevMapAction); addAction(_prevMapAction);
if (_ml->maps().isEmpty()) {
_showMapAction->setEnabled(false);
_clearMapCacheAction->setEnabled(false);
}
_showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this); _showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this);
_showCoordinatesAction->setMenuRole(QAction::NoRole); _showCoordinatesAction->setMenuRole(QAction::NoRole);
_showCoordinatesAction->setCheckable(true); _showCoordinatesAction->setCheckable(true);
@ -506,7 +509,7 @@ void GUI::createMenus()
#endif // Q_OS_MAC #endif // Q_OS_MAC
_mapMenu = menuBar()->addMenu(tr("&Map")); _mapMenu = menuBar()->addMenu(tr("&Map"));
_mapMenu->addActions(_mapActions); _mapMenu->addActions(_mapsActionGroup->actions());
_mapsEnd = _mapMenu->addSeparator(); _mapsEnd = _mapMenu->addSeparator();
_mapMenu->addAction(_loadMapAction); _mapMenu->addAction(_loadMapAction);
_mapMenu->addAction(_clearMapCacheAction); _mapMenu->addAction(_clearMapCacheAction);
@ -608,6 +611,7 @@ void GUI::createToolBars()
void GUI::createMapView() void GUI::createMapView()
{ {
_map = new EmptyMap(this);
_mapView = new MapView(_map, _poi, this); _mapView = new MapView(_map, _poi, this);
_mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, _mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
QSizePolicy::Expanding)); QSizePolicy::Expanding));
@ -905,9 +909,14 @@ void GUI::openOptions()
Track::action(options.option); \ Track::action(options.option); \
reload = true; \ reload = true; \
} }
#define SET_DATA_OPTION(option, action) \ #define SET_ROUTE_OPTION(option, action) \
if (options.option != _options.option) { \ if (options.option != _options.option) { \
Data::action(options.option); \ Route::action(options.option); \
reload = true; \
}
#define SET_WAYPOINT_OPTION(option, action) \
if (options.option != _options.option) { \
Waypoint::action(options.option); \
reload = true; \ reload = true; \
} }
@ -953,13 +962,18 @@ void GUI::openOptions()
SET_TRACK_OPTION(pauseSpeed, setPauseSpeed); SET_TRACK_OPTION(pauseSpeed, setPauseSpeed);
SET_TRACK_OPTION(pauseInterval, setPauseInterval); SET_TRACK_OPTION(pauseInterval, setPauseInterval);
SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed); SET_TRACK_OPTION(useReportedSpeed, useReportedSpeed);
SET_TRACK_OPTION(dataUseDEM, useDEM);
SET_TRACK_OPTION(showSecondaryElevation, showSecondaryElevation);
SET_TRACK_OPTION(showSecondarySpeed, showSecondarySpeed);
SET_DATA_OPTION(dataUseDEM, useDEM); SET_ROUTE_OPTION(dataUseDEM, useDEM);
SET_ROUTE_OPTION(showSecondaryElevation, showSecondaryElevation);
SET_WAYPOINT_OPTION(dataUseDEM, useDEM);
SET_WAYPOINT_OPTION(showSecondaryElevation, showSecondaryElevation);
if (options.poiRadius != _options.poiRadius) if (options.poiRadius != _options.poiRadius)
_poi->setRadius(options.poiRadius); _poi->setRadius(options.poiRadius);
if (options.poiUseDEM != _options.poiUseDEM)
_poi->useDEM(options.poiUseDEM);
if (options.pixmapCache != _options.pixmapCache) if (options.pixmapCache != _options.pixmapCache)
QPixmapCache::setCacheLimit(options.pixmapCache * 1024); QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
@ -1322,22 +1336,49 @@ void GUI::loadMap()
bool GUI::loadMap(const QString &fileName) bool GUI::loadMap(const QString &fileName)
{ {
// On OS X fileName may be a directory!
if (fileName.isEmpty()) if (fileName.isEmpty())
return false; return false;
if (_ml->loadFile(fileName)) { QString error;
QAction *a = createMapAction(_ml->maps().last()); QList<Map*> maps = MapList::loadMaps(fileName, error);
if (maps.isEmpty()) {
error = tr("Error loading map:") + "\n\n"
+ fileName + "\n\n" + error;
QMessageBox::critical(this, APP_NAME, error);
return false;
}
for (int i = 0; i < maps.size(); i++) {
Map *map = maps.at(i);
MapAction *a = createMapAction(map);
_mapMenu->insertAction(_mapsEnd, a); _mapMenu->insertAction(_mapsEnd, a);
if (map->isReady()) {
a->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} else
connect(a, SIGNAL(loaded()), this, SLOT(mapLoaded()));
}
return true;
}
void GUI::mapLoaded()
{
MapAction *action = static_cast<MapAction*>(QObject::sender());
Map *map = action->data().value<Map*>();
if (map->isValid()) {
action->trigger();
_showMapAction->setEnabled(true); _showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true); _clearMapCacheAction->setEnabled(true);
a->trigger();
return true;
} else { } else {
QString error = tr("Error loading map:") + "\n\n" QString error = tr("Error loading map:") + "\n\n"
+ fileName + "\n\n" + _ml->errorString(); + map->name() + "\n\n" + map->errorString();
QMessageBox::critical(this, APP_NAME, error); QMessageBox::critical(this, APP_NAME, error);
action->deleteLater();
return false;
} }
} }
@ -1379,31 +1420,42 @@ void GUI::updateWindowTitle()
setWindowTitle(APP_NAME); setWindowTitle(APP_NAME);
} }
void GUI::mapChanged(int index) void GUI::mapChanged()
{ {
_map = _ml->maps().at(index); _map = _mapsActionGroup->checkedAction()->data().value<Map*>();
_mapView->setMap(_map); _mapView->setMap(_map);
} }
void GUI::nextMap() void GUI::nextMap()
{ {
if (_ml->maps().count() < 2) QAction *checked = _mapsActionGroup->checkedAction();
if (!checked)
return; return;
int next = (_ml->maps().indexOf(_map) + 1) % _ml->maps().count(); QList<QAction*> maps = _mapsActionGroup->actions();
_mapActions.at(next)->setChecked(true); for (int i = 1; i < maps.size(); i++) {
mapChanged(next); int next = (maps.indexOf(checked) + i) % maps.count();
if (maps.at(next)->isEnabled()) {
maps.at(next)->trigger();
break;
}
}
} }
void GUI::prevMap() void GUI::prevMap()
{ {
if (_ml->maps().count() < 2) QAction *checked = _mapsActionGroup->checkedAction();
if (!checked)
return; return;
int prev = (_ml->maps().indexOf(_map) + _ml->maps().count() - 1) QList<QAction*> maps = _mapsActionGroup->actions();
% _ml->maps().count(); for (int i = 1; i < maps.size(); i++) {
_mapActions.at(prev)->setChecked(true); int prev = (maps.indexOf(checked) + maps.count() - i) % maps.count();
mapChanged(prev); if (maps.at(prev)->isEnabled()) {
maps.at(prev)->trigger();
break;
}
}
} }
void GUI::poiFileChecked(int index) void GUI::poiFileChecked(int index)
@ -1809,10 +1861,14 @@ void GUI::writeSettings()
settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed); settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed);
if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT) if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT)
settings.setValue(DATA_USE_DEM_SETTING, _options.dataUseDEM); settings.setValue(DATA_USE_DEM_SETTING, _options.dataUseDEM);
if (_options.showSecondaryElevation != SHOW_SECONDARY_ELEVATION_DEFAULT)
settings.setValue(SHOW_SECONDARY_ELEVATION_SETTING,
_options.showSecondaryElevation);
if (_options.showSecondarySpeed != SHOW_SECONDARY_SPEED_DEFAULT)
settings.setValue(SHOW_SECONDARY_SPEED_SETTING,
_options.showSecondarySpeed);
if (_options.poiRadius != POI_RADIUS_DEFAULT) if (_options.poiRadius != POI_RADIUS_DEFAULT)
settings.setValue(POI_RADIUS_SETTING, _options.poiRadius); settings.setValue(POI_RADIUS_SETTING, _options.poiRadius);
if (_options.poiUseDEM != POI_USE_DEM_DEFAULT)
settings.setValue(POI_USE_DEM_SETTING, _options.poiUseDEM);
if (_options.useOpenGL != USE_OPENGL_DEFAULT) if (_options.useOpenGL != USE_OPENGL_DEFAULT)
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL); settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2
@ -1896,9 +1952,11 @@ void GUI::readSettings()
_showMapAction->setChecked(true); _showMapAction->setChecked(true);
else else
_mapView->showMap(false); _mapView->showMap(false);
if (_ml->maps().count()) { QAction *ma = mapAction(settings.value(CURRENT_MAP_SETTING).toString());
int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString()); if (ma) {
_mapActions.at(index)->trigger(); ma->trigger();
_showMapAction->setEnabled(true);
_clearMapCacheAction->setEnabled(true);
} }
if (settings.value(SHOW_COORDINATES_SETTING, SHOW_COORDINATES_DEFAULT) if (settings.value(SHOW_COORDINATES_SETTING, SHOW_COORDINATES_DEFAULT)
.toBool()) { .toBool()) {
@ -2075,14 +2133,18 @@ void GUI::readSettings()
USE_REPORTED_SPEED_DEFAULT).toBool(); USE_REPORTED_SPEED_DEFAULT).toBool();
_options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING, _options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING,
DATA_USE_DEM_DEFAULT).toBool(); DATA_USE_DEM_DEFAULT).toBool();
_options.showSecondaryElevation = settings.value(
SHOW_SECONDARY_ELEVATION_SETTING,
SHOW_SECONDARY_ELEVATION_DEFAULT).toBool();
_options.showSecondarySpeed = settings.value(
SHOW_SECONDARY_SPEED_SETTING,
SHOW_SECONDARY_SPEED_DEFAULT).toBool();
_options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING, _options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING,
AUTOMATIC_PAUSE_DEFAULT).toBool(); AUTOMATIC_PAUSE_DEFAULT).toBool();
_options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING, _options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING,
PAUSE_INTERVAL_DEFAULT).toInt(); PAUSE_INTERVAL_DEFAULT).toInt();
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT) _options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
.toInt(); .toInt();
_options.poiUseDEM = settings.value(POI_USE_DEM_SETTING,
POI_USE_DEM_DEFAULT).toBool();
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT) _options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
.toBool(); .toBool();
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2
@ -2162,21 +2224,38 @@ void GUI::readSettings()
Track::setPauseSpeed(_options.pauseSpeed); Track::setPauseSpeed(_options.pauseSpeed);
Track::setPauseInterval(_options.pauseInterval); Track::setPauseInterval(_options.pauseInterval);
Track::useReportedSpeed(_options.useReportedSpeed); Track::useReportedSpeed(_options.useReportedSpeed);
Data::useDEM(_options.dataUseDEM); Track::useDEM(_options.dataUseDEM);
Track::showSecondaryElevation(_options.showSecondaryElevation);
Track::showSecondarySpeed(_options.showSecondarySpeed);
Route::useDEM(_options.dataUseDEM);
Route::showSecondaryElevation(_options.showSecondaryElevation);
Waypoint::useDEM(_options.dataUseDEM);
Waypoint::showSecondaryElevation(_options.showSecondaryElevation);
_poi->setRadius(_options.poiRadius); _poi->setRadius(_options.poiRadius);
_poi->useDEM(_options.poiUseDEM);
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024); QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
settings.endGroup(); settings.endGroup();
} }
int GUI::mapIndex(const QString &name) QAction *GUI::mapAction(const QString &name)
{ {
for (int i = 0; i < _ml->maps().count(); i++) QList<QAction *> maps = _mapsActionGroup->actions();
if (_ml->maps().at(i)->name() == name)
return i; // Last map
for (int i = 0; i < maps.count(); i++) {
Map *map = maps.at(i)->data().value<Map*>();
if (map->name() == name && map->isReady())
return maps.at(i);
}
// Any usable map
for (int i = 0; i < maps.count(); i++) {
Map *map = maps.at(i)->data().value<Map*>();
if (map->isReady())
return maps.at(i);
}
return 0; return 0;
} }

View File

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

View File

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

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

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

View File

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

View File

@ -12,6 +12,7 @@
#include <QRadioButton> #include <QRadioButton>
#include <QLabel> #include <QLabel>
#include <QSysInfo> #include <QSysInfo>
#include <QButtonGroup>
#include "map/pcs.h" #include "map/pcs.h"
#include "icons.h" #include "icons.h"
#include "colorbox.h" #include "colorbox.h"
@ -406,6 +407,8 @@ QWidget *OptionsDialog::createDataPage()
_reportedSpeed->setChecked(true); _reportedSpeed->setChecked(true);
else else
_computedSpeed->setChecked(true); _computedSpeed->setChecked(true);
_showSecondarySpeed = new QCheckBox(tr("Show secondary speed"));
_showSecondarySpeed->setChecked(_options->showSecondarySpeed);
_dataGPSElevation = new QRadioButton(tr("GPS data")); _dataGPSElevation = new QRadioButton(tr("GPS data"));
_dataDEMElevation = new QRadioButton(tr("DEM data")); _dataDEMElevation = new QRadioButton(tr("DEM data"));
@ -413,19 +416,28 @@ QWidget *OptionsDialog::createDataPage()
_dataDEMElevation->setChecked(true); _dataDEMElevation->setChecked(true);
else else
_dataGPSElevation->setChecked(true); _dataGPSElevation->setChecked(true);
_showSecondaryElevation = new QCheckBox(tr("Show secondary elevation"));
_showSecondaryElevation->setChecked(_options->showSecondaryElevation);
QWidget *sourceTab = new QWidget(); QWidget *sourceTab = new QWidget();
QVBoxLayout *sourceTabLayout = new QVBoxLayout(); QVBoxLayout *sourceTabLayout = new QVBoxLayout();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QButtonGroup *speedGroup = new QButtonGroup(this);
speedGroup->addButton(_computedSpeed);
speedGroup->addButton(_reportedSpeed);
QVBoxLayout *speedOptions = new QVBoxLayout(); QVBoxLayout *speedOptions = new QVBoxLayout();
speedOptions->addWidget(_computedSpeed); speedOptions->addWidget(_computedSpeed);
speedOptions->addWidget(_reportedSpeed); speedOptions->addWidget(_reportedSpeed);
speedOptions->addWidget(_showSecondarySpeed);
QButtonGroup *elevationGroup = new QButtonGroup(this);
elevationGroup->addButton(_dataGPSElevation);
elevationGroup->addButton(_dataDEMElevation);
QVBoxLayout *elevationOptions = new QVBoxLayout(); QVBoxLayout *elevationOptions = new QVBoxLayout();
elevationOptions->addWidget(_dataGPSElevation); elevationOptions->addWidget(_dataGPSElevation);
elevationOptions->addWidget(_dataDEMElevation); elevationOptions->addWidget(_dataDEMElevation);
elevationOptions->addWidget(_showSecondaryElevation);
QFormLayout *formLayout = new QFormLayout(); QFormLayout *formLayout = new QFormLayout();
formLayout->addRow(tr("Speed:"), speedOptions); formLayout->addRow(tr("Speed:"), speedOptions);
@ -438,12 +450,14 @@ QWidget *OptionsDialog::createDataPage()
speedLayout->addWidget(_computedSpeed); speedLayout->addWidget(_computedSpeed);
speedLayout->addWidget(_reportedSpeed); speedLayout->addWidget(_reportedSpeed);
speedLayout->addWidget(_showSecondarySpeed);
QGroupBox *speedBox = new QGroupBox(tr("Speed")); QGroupBox *speedBox = new QGroupBox(tr("Speed"));
speedBox->setLayout(speedLayout); speedBox->setLayout(speedLayout);
elevationLayout->addWidget(_dataGPSElevation); elevationLayout->addWidget(_dataGPSElevation);
elevationLayout->addWidget(_dataDEMElevation); elevationLayout->addWidget(_dataDEMElevation);
elevationLayout->addWidget(_showSecondaryElevation);
QGroupBox *elevationBox = new QGroupBox(tr("Elevation")); QGroupBox *elevationBox = new QGroupBox(tr("Elevation"));
elevationBox->setLayout(elevationLayout); elevationBox->setLayout(elevationLayout);
@ -465,13 +479,6 @@ QWidget *OptionsDialog::createDataPage()
QWidget *OptionsDialog::createPOIPage() QWidget *OptionsDialog::createPOIPage()
{ {
_poiGPSElevation = new QRadioButton(tr("GPS data"));
_poiDEMElevation = new QRadioButton(tr("DEM data"));
if (_options->poiUseDEM)
_poiDEMElevation->setChecked(true);
else
_poiGPSElevation->setChecked(true);
_poiRadius = new QDoubleSpinBox(); _poiRadius = new QDoubleSpinBox();
_poiRadius->setSingleStep(1); _poiRadius->setSingleStep(1);
_poiRadius->setDecimals(1); _poiRadius->setDecimals(1);
@ -486,13 +493,8 @@ QWidget *OptionsDialog::createPOIPage()
_poiRadius->setSuffix(UNIT_SPACE + tr("km")); _poiRadius->setSuffix(UNIT_SPACE + tr("km"));
} }
QVBoxLayout *elevationLayout = new QVBoxLayout();
elevationLayout->addWidget(_poiGPSElevation);
elevationLayout->addWidget(_poiDEMElevation);
QFormLayout *poiLayout = new QFormLayout(); QFormLayout *poiLayout = new QFormLayout();
poiLayout->addRow(tr("Radius:"), _poiRadius); poiLayout->addRow(tr("Radius:"), _poiRadius);
poiLayout->addRow(tr("Elevation:"), elevationLayout);
QWidget *poiTab = new QWidget(); QWidget *poiTab = new QWidget();
poiTab->setLayout(poiLayout); poiTab->setLayout(poiLayout);
@ -718,13 +720,14 @@ void OptionsDialog::accept()
_options->pauseInterval = _pauseInterval->value(); _options->pauseInterval = _pauseInterval->value();
_options->useReportedSpeed = _reportedSpeed->isChecked(); _options->useReportedSpeed = _reportedSpeed->isChecked();
_options->dataUseDEM = _dataDEMElevation->isChecked(); _options->dataUseDEM = _dataDEMElevation->isChecked();
_options->showSecondaryElevation = _showSecondaryElevation->isChecked();
_options->showSecondarySpeed = _showSecondarySpeed->isChecked();
qreal poiRadius = (_options->units == Imperial) qreal poiRadius = (_options->units == Imperial)
? _poiRadius->value() * MIINM : (_options->units == Nautical) ? _poiRadius->value() * MIINM : (_options->units == Nautical)
? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM; ? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM;
if (qAbs(poiRadius - _options->poiRadius) > 0.01) if (qAbs(poiRadius - _options->poiRadius) > 0.01)
_options->poiRadius = poiRadius; _options->poiRadius = poiRadius;
_options->poiUseDEM = _poiDEMElevation->isChecked();
_options->useOpenGL = _useOpenGL->isChecked(); _options->useOpenGL = _useOpenGL->isChecked();
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2

View File

@ -54,9 +54,10 @@ struct Options {
int pauseInterval; int pauseInterval;
bool useReportedSpeed; bool useReportedSpeed;
bool dataUseDEM; bool dataUseDEM;
bool showSecondaryElevation;
bool showSecondarySpeed;
// POI // POI
int poiRadius; int poiRadius;
bool poiUseDEM;
// System // System
bool useOpenGL; bool useOpenGL;
#ifdef ENABLE_HTTP2 #ifdef ENABLE_HTTP2
@ -142,10 +143,10 @@ private:
QRadioButton *_reportedSpeed; QRadioButton *_reportedSpeed;
QRadioButton *_dataGPSElevation; QRadioButton *_dataGPSElevation;
QRadioButton *_dataDEMElevation; QRadioButton *_dataDEMElevation;
QCheckBox *_showSecondaryElevation;
QCheckBox *_showSecondarySpeed;
// POI // POI
QDoubleSpinBox *_poiRadius; QDoubleSpinBox *_poiRadius;
QRadioButton *_poiGPSElevation;
QRadioButton *_poiDEMElevation;
// System // System
QSpinBox *_pixmapCache; QSpinBox *_pixmapCache;
QSpinBox *_connectionTimeout; QSpinBox *_connectionTimeout;

View File

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

View File

@ -15,6 +15,8 @@ QString RouteItem::info() const
tt.insert(tr("Name"), _name); tt.insert(tr("Name"), _name);
if (!_desc.isEmpty()) if (!_desc.isEmpty())
tt.insert(tr("Description"), _desc); tt.insert(tr("Description"), _desc);
if (!_comment.isEmpty() && _comment != _desc)
tt.insert(tr("Comment"), _comment);
tt.insert(tr("Distance"), Format::distance(path().last().last().distance(), tt.insert(tr("Distance"), Format::distance(path().last().last().distance(),
_units)); _units));
if (!_links.isEmpty()) { if (!_links.isEmpty()) {
@ -43,6 +45,7 @@ RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent)
_name = route.name(); _name = route.name();
_desc = route.description(); _desc = route.description();
_comment = route.comment();
_links = route.links(); _links = route.links();
_coordinatesFormat = DecimalDegrees; _coordinatesFormat = DecimalDegrees;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,15 +21,23 @@ QString WaypointItem::info() const
tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name()); tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name());
tt.insert(qApp->translate("WaypointItem", "Coordinates"), tt.insert(qApp->translate("WaypointItem", "Coordinates"),
Format::coordinates(_waypoint.coordinates(), _format)); Format::coordinates(_waypoint.coordinates(), _format));
if (_waypoint.hasElevation()) if (!std::isnan(_waypoint.elevations().first)) {
tt.insert(qApp->translate("WaypointItem", "Elevation"), QString val = Format::elevation(_waypoint.elevations().first, _units);
Format::elevation(_waypoint.elevation(), _units)); if (!std::isnan(_waypoint.elevations().second))
val += " (" + Format::elevation(_waypoint.elevations().second,
_units) + ")";
tt.insert(qApp->translate("WaypointItem", "Elevation"), val);
}
if (_waypoint.timestamp().isValid()) if (_waypoint.timestamp().isValid())
tt.insert(qApp->translate("WaypointItem", "Date"), tt.insert(qApp->translate("WaypointItem", "Date"),
_waypoint.timestamp().toString(Qt::SystemLocaleShortDate)); _waypoint.timestamp().toString(Qt::SystemLocaleShortDate));
if (!_waypoint.description().isEmpty()) if (!_waypoint.description().isEmpty())
tt.insert(qApp->translate("WaypointItem", "Description"), tt.insert(qApp->translate("WaypointItem", "Description"),
_waypoint.description()); _waypoint.description());
if (!_waypoint.comment().isEmpty()
&& _waypoint.comment() != _waypoint.description())
tt.insert(qApp->translate("WaypointItem", "Comment"),
_waypoint.comment());
if (_waypoint.address().isValid()) { if (_waypoint.address().isValid()) {
QString addr("<address>"); QString addr("<address>");
addr += _waypoint.address().street(); addr += _waypoint.address().street();

View File

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

View File

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

View File

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

View File

@ -20,7 +20,6 @@
#include "cupparser.h" #include "cupparser.h"
#include "gpiparser.h" #include "gpiparser.h"
#include "smlparser.h" #include "smlparser.h"
#include "dem.h"
#include "data.h" #include "data.h"
@ -44,78 +43,46 @@ static CUPParser cup;
static GPIParser gpi; static GPIParser gpi;
static SMLParser sml; static SMLParser sml;
static QHash<QString, Parser*> parsers() static QMap<QString, Parser*> parsers()
{ {
QHash<QString, Parser*> hash; QMap<QString, Parser*> map;
hash.insert("gpx", &gpx); map.insert("gpx", &gpx);
hash.insert("tcx", &tcx); map.insert("tcx", &tcx);
hash.insert("kml", &kml); map.insert("kml", &kml);
hash.insert("fit", &fit); map.insert("fit", &fit);
hash.insert("csv", &csv); map.insert("csv", &csv);
hash.insert("igc", &igc); map.insert("igc", &igc);
hash.insert("nmea", &nmea); map.insert("nmea", &nmea);
hash.insert("plt", &plt); map.insert("plt", &plt);
hash.insert("wpt", &wpt); map.insert("wpt", &wpt);
hash.insert("rte", &rte); map.insert("rte", &rte);
hash.insert("loc", &loc); map.insert("loc", &loc);
hash.insert("slf", &slf); map.insert("slf", &slf);
#ifdef ENABLE_GEOJSON #ifdef ENABLE_GEOJSON
hash.insert("json", &geojson); map.insert("json", &geojson);
hash.insert("geojson", &geojson); map.insert("geojson", &geojson);
#endif // ENABLE_GEOJSON #endif // ENABLE_GEOJSON
hash.insert("jpeg", &exif); map.insert("jpeg", &exif);
hash.insert("jpg", &exif); map.insert("jpg", &exif);
hash.insert("cup", &cup); map.insert("cup", &cup);
hash.insert("gpi", &gpi); map.insert("gpi", &gpi);
hash.insert("sml", &sml); map.insert("sml", &sml);
return hash; return map;
} }
QMap<QString, Parser*> Data::_parsers = parsers();
QHash<QString, Parser*> Data::_parsers = parsers();
bool Data::_useDEM = false;
void Data::processData(QList<TrackData> &trackData, QList<RouteData> &routeData) void Data::processData(QList<TrackData> &trackData, QList<RouteData> &routeData)
{ {
for (int i = 0; i < trackData.count(); i++) { for (int i = 0; i < trackData.count(); i++)
TrackData &track = trackData[i];
for (int j = 0; j < track.size(); j++) {
SegmentData &segment = track[j];
for (int k = 0; k < segment.size(); k++) {
Trackpoint &t = segment[k];
if (!t.hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(t.coordinates());
if (!std::isnan(elevation))
t.setElevation(elevation);
}
}
}
_tracks.append(Track(trackData.at(i))); _tracks.append(Track(trackData.at(i)));
} for (int i = 0; i < routeData.count(); i++)
for (int i = 0; i < routeData.count(); i++) {
RouteData &route = routeData[i];
for (int j = 0; j < route.size(); j++) {
Waypoint &w = route[j];
if (!w.hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(w.coordinates());
if (!std::isnan(elevation))
w.setElevation(elevation);
}
}
_routes.append(Route(routeData.at(i))); _routes.append(Route(routeData.at(i)));
}
for (int i = 0; i < _waypoints.size(); i++) {
if (!_waypoints.at(i).hasElevation() || _useDEM) {
qreal elevation = DEM::elevation(_waypoints.at(i).coordinates());
if (!std::isnan(elevation))
_waypoints[i].setElevation(elevation);
}
}
} }
Data::Data(const QString &fileName, bool poi) Data::Data(const QString &fileName)
{ {
QFile file(fileName); QFile file(fileName);
QFileInfo fi(fileName); QFileInfo fi(fileName);
@ -130,12 +97,11 @@ Data::Data(const QString &fileName, bool poi)
return; return;
} }
QHash<QString, Parser*>::iterator it; QMap<QString, Parser*>::iterator it;
if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) { if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) {
if (it.value()->parse(&file, trackData, routeData, _polygons, if (it.value()->parse(&file, trackData, routeData, _polygons,
_waypoints)) { _waypoints)) {
if (!poi) processData(trackData, routeData);
processData(trackData, routeData);
_valid = true; _valid = true;
return; return;
} else { } else {
@ -146,8 +112,7 @@ Data::Data(const QString &fileName, bool poi)
for (it = _parsers.begin(); it != _parsers.end(); it++) { for (it = _parsers.begin(); it != _parsers.end(); it++) {
if (it.value()->parse(&file, trackData, routeData, _polygons, if (it.value()->parse(&file, trackData, routeData, _polygons,
_waypoints)) { _waypoints)) {
if (!poi) processData(trackData, routeData);
processData(trackData, routeData);
_valid = true; _valid = true;
return; return;
} }
@ -166,17 +131,8 @@ Data::Data(const QString &fileName, bool poi)
QString Data::formats() QString Data::formats()
{ {
QStringList l(filter());
qSort(l);
QString supported;
for (int i = 0; i < l.size(); i++) {
supported += l.at(i);
if (i != l.size() - 1)
supported += " ";
}
return return
qApp->translate("Data", "Supported files") + " (" + supported + ");;" qApp->translate("Data", "Supported files") + " (" + filter().join(" ") + ");;"
+ qApp->translate("Data", "CSV files") + " (*.csv);;" + qApp->translate("Data", "CSV files") + " (*.csv);;"
+ qApp->translate("Data", "CUP files") + " (*.cup);;" + qApp->translate("Data", "CUP files") + " (*.cup);;"
+ qApp->translate("Data", "FIT files") + " (*.fit);;" + qApp->translate("Data", "FIT files") + " (*.fit);;"
@ -200,15 +156,10 @@ QString Data::formats()
QStringList Data::filter() QStringList Data::filter()
{ {
QStringList filter; QStringList filter;
QHash<QString, Parser*>::iterator it;
for (it = _parsers.begin(); it != _parsers.end(); it++) for (QMap<QString, Parser*>::iterator it = _parsers.begin();
filter << QString("*.%1").arg(it.key()); it != _parsers.end(); it++)
filter << "*." + it.key();
return filter; return filter;
} }
void Data::useDEM(bool use)
{
_useDEM = use;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
#include "dem.h"
#include "track.h" #include "track.h"
@ -13,6 +14,9 @@ int Track::_pauseInterval = 10;
bool Track::_outlierEliminate = true; bool Track::_outlierEliminate = true;
bool Track::_useReportedSpeed = false; bool Track::_useReportedSpeed = false;
bool Track::_useDEM = false;
bool Track::_show2ndElevation = false;
bool Track::_show2ndSpeed = false;
static qreal avg(const QVector<qreal> &v) static qreal avg(const QVector<qreal> &v)
@ -47,7 +51,7 @@ static QSet<int> eliminate(const QVector<qreal> &v)
qreal M = MAD(w, m); qreal M = MAD(w, m);
for (int i = 0; i < v.size(); i++) for (int i = 0; i < v.size(); i++)
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 5) if (qAbs((0.6745 * (v.at(i) - m)) / M) > 3.5)
rm.insert(i); rm.insert(i);
return rm; return rm;
@ -213,7 +217,7 @@ Track::Track(const TrackData &data) : _data(data), _pause(0)
} }
} }
Graph Track::elevation() const Graph Track::gpsElevation() const
{ {
Graph ret; Graph ret;
@ -237,7 +241,48 @@ Graph Track::elevation() const
return ret; return ret;
} }
Graph Track::speed() const Graph Track::demElevation() const
{
Graph ret;
for (int i = 0; i < _data.size(); i++) {
const SegmentData &sd = _data.at(i);
if (sd.size() < 2)
continue;
const Segment &seg = _segments.at(i);
GraphSegment gs;
for (int j = 0; j < sd.size(); j++) {
qreal dem = DEM::elevation(sd.at(j).coordinates());
if (std::isnan(dem) || seg.outliers.contains(j))
continue;
gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), dem));
}
ret.append(filter(gs, _elevationWindow));
}
return ret;
}
GraphPair Track::elevation() const
{
if (_useDEM) {
Graph dem(demElevation());
if (dem.isValid())
return GraphPair(dem, _show2ndElevation ? gpsElevation() : Graph());
else
return GraphPair(gpsElevation(), Graph());
} else {
Graph gps(gpsElevation());
if (gps.isValid())
return GraphPair(gps, _show2ndElevation ? demElevation() : Graph());
else
return GraphPair(demElevation(), Graph());
}
}
Graph Track::computedSpeed() const
{ {
Graph ret; Graph ret;
@ -251,14 +296,10 @@ Graph Track::speed() const
qreal v; qreal v;
for (int j = 0; j < sd.size(); j++) { for (int j = 0; j < sd.size(); j++) {
if (seg.stop.contains(j) && (!std::isnan(seg.speed.at(j)) if (seg.stop.contains(j) && !std::isnan(seg.speed.at(j))) {
|| sd.at(j).hasSpeed())) {
v = 0; v = 0;
stop.append(gs.size()); stop.append(gs.size());
} else if (_useReportedSpeed && sd.at(j).hasSpeed() } else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
&& !seg.outliers.contains(j))
v = sd.at(j).speed();
else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
v = seg.speed.at(j); v = seg.speed.at(j);
else else
continue; continue;
@ -276,6 +317,60 @@ Graph Track::speed() const
return ret; return ret;
} }
Graph Track::reportedSpeed() const
{
Graph ret;
for (int i = 0; i < _data.size(); i++) {
const SegmentData &sd = _data.at(i);
if (sd.size() < 2)
continue;
const Segment &seg = _segments.at(i);
GraphSegment gs;
QList<int> stop;
qreal v;
for (int j = 0; j < sd.size(); j++) {
if (seg.stop.contains(j) && sd.at(j).hasSpeed()) {
v = 0;
stop.append(gs.size());
} else if (sd.at(j).hasSpeed() && !seg.outliers.contains(j))
v = sd.at(j).speed();
else
continue;
gs.append(GraphPoint(seg.distance.at(j), seg.time.at(j), v));
}
ret.append(filter(gs, _speedWindow));
GraphSegment &filtered = ret.last();
for (int j = 0; j < stop.size(); j++)
filtered[stop.at(j)].setY(0);
}
return ret;
}
GraphPair Track::speed() const
{
if (_useReportedSpeed) {
Graph reported(reportedSpeed());
if (reported.isValid())
return GraphPair(reported, _show2ndSpeed ? computedSpeed()
: Graph());
else
return GraphPair(computedSpeed(), Graph());
} else {
Graph computed(computedSpeed());
if (computed.isValid())
return GraphPair(computed, _show2ndSpeed ? reportedSpeed()
: Graph());
else
return GraphPair(reportedSpeed(), Graph());
}
}
Graph Track::heartRate() const Graph Track::heartRate() const
{ {
Graph ret; Graph ret;

View File

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

View File

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

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

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

View File

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

View File

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

View File

@ -84,11 +84,13 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
tile->addFile(fi.absoluteFilePath(), tileType(fi.suffix())); tile->addFile(fi.absoluteFilePath(), tileType(fi.suffix()));
} }
if (!tile->init(baseMap)) { if (!tile->init()) {
qWarning("%s: Invalid map tile", qPrintable(dir.path())); qWarning("%s: Invalid map tile", qPrintable(dir.path()));
delete tile; delete tile;
return false; return false;
} }
if (baseMap)
tile->markAsBasemap();
double min[2], max[2]; double min[2], max[2];
min[0] = tile->bounds().left(); min[0] = tile->bounds().left();
@ -98,6 +100,8 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
_tileTree.Insert(min, max, tile); _tileTree.Insert(min, max, tile);
_bounds |= tile->bounds(); _bounds |= tile->bounds();
if (tile->zooms().min() < _zooms.min())
_zooms.setMin(tile->zooms().min());
return true; return true;
} }

View File

@ -132,11 +132,13 @@ IMG::IMG(const QString &fileName) : _file(fileName)
} }
// Create tile tree // Create tile tree
int minMapZoom = 24;
for (TileMap::const_iterator it = tileMap.constBegin(); for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) { it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value(); VectorTile *tile = it.value();
if (!tile->init(false)) { if (!tile->init()) {
qWarning("%s: %s: Invalid map tile", qPrintable(_file.fileName()), qWarning("%s: %s: Invalid map tile", qPrintable(_file.fileName()),
qPrintable(it.key())); qPrintable(it.key()));
delete tile; delete tile;
@ -151,6 +153,19 @@ IMG::IMG(const QString &fileName) : _file(fileName)
_tileTree.Insert(min, max, tile); _tileTree.Insert(min, max, tile);
_bounds |= tile->bounds(); _bounds |= tile->bounds();
if (tile->zooms().min() < _zooms.min())
_zooms.setMin(tile->zooms().min());
if (tile->zooms().min() < minMapZoom)
minMapZoom = tile->zooms().min();
}
for (TileMap::const_iterator it = tileMap.constBegin();
it != tileMap.constEnd(); ++it) {
VectorTile *tile = it.value();
if (tile->zooms().min() > minMapZoom)
_baseMap = true;
if (tile->zooms().min() == minMapZoom)
tile->markAsBasemap();
} }
if (!_tileTree.Count()) if (!_tileTree.Count())

View File

@ -30,15 +30,20 @@ static quint8 SPECIAL_CHARS[] = {
'8', '9', '~', '~', '~', '~', '~', '~' '8', '9', '~', '~', '~', '~', '~', '~'
}; };
static QString capitalize(const QString &str) static bool isAllUpperCase(const QString &str)
{ {
if (str.isEmpty()) if (str.isEmpty())
return str; return false;
for (int i = 0; i < str.size(); i++) for (int i = 0; i < str.size(); i++)
if (str.at(i).isLetter() && !(str.at(i).isUpper() if (str.at(i).isLetter() && !(str.at(i).isUpper()
|| str.at(i) == QChar(0x00DF))) || str.at(i) == QChar(0x00DF)))
return str; return false;
return true;
}
static QString capitalized(const QString &str)
{
QString ret(str); QString ret(str);
for (int i = 0; i < str.size(); i++) for (int i = 0; i < str.size(); i++)
if (i && !str.at(i-1).isSpace()) if (i && !str.at(i-1).isSpace())
@ -77,7 +82,7 @@ bool LBLFile::init(Handle &hdl)
return true; return true;
} }
Label LBLFile::label6b(Handle &hdl, quint32 offset) const Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
{ {
Label::Shield::Type shieldType = Label::Shield::None; Label::Shield::Type shieldType = Label::Shield::None;
QByteArray label, shieldLabel; QByteArray label, shieldLabel;
@ -95,9 +100,12 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset) const
int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F}; int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F};
for (int cpt = 0; cpt < 4; cpt++) { for (int cpt = 0; cpt < 4; cpt++) {
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
return Label(capitalize(QString::fromLatin1(label)), QString text(QString::fromLatin1(label));
Label::Shield(shieldType, shieldLabel)); return Label(capitalize && isAllUpperCase(text)
? capitalized(text) : text, Label::Shield(shieldType,
shieldLabel));
}
switch (curCharSet) { switch (curCharSet) {
case Normal: case Normal:
if (c[cpt] == 0x1c) if (c[cpt] == 0x1c)
@ -127,7 +135,7 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset) const
} }
} }
Label LBLFile::label8b(Handle &hdl, quint32 offset) const Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize) const
{ {
Label::Shield::Type shieldType = Label::Shield::None; Label::Shield::Type shieldType = Label::Shield::None;
QByteArray label, shieldLabel; QByteArray label, shieldLabel;
@ -143,7 +151,9 @@ Label LBLFile::label8b(Handle &hdl, quint32 offset) const
if (!c || c == 0x1d) if (!c || c == 0x1d)
break; break;
if ((c >= 0x1e && c <= 0x1f)) { if (c == 0x1c)
capitalize = false;
else if ((c >= 0x1e && c <= 0x1f)) {
if (bap == &shieldLabel) if (bap == &shieldLabel)
bap = &label; bap = &label;
else else
@ -157,12 +167,15 @@ Label LBLFile::label8b(Handle &hdl, quint32 offset) const
bap->append(c); bap->append(c);
} }
return Label(capitalize(_codec ? _codec->toUnicode(label) QString text(_codec ? _codec->toUnicode(label) : QString::fromLatin1(label));
: QString::fromLatin1(label)), Label::Shield(shieldType, _codec QString shieldText(_codec ? _codec->toUnicode(shieldLabel)
? _codec->toUnicode(shieldLabel) : QString::fromLatin1(shieldLabel))); : QString::fromLatin1(shieldLabel));
return Label(capitalize && isAllUpperCase(text) ? capitalized(text) : text,
Label::Shield(shieldType, shieldText));
} }
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi) Label LBLFile::label(Handle &hdl, quint32 offset, bool poi, bool capitalize)
{ {
if (!_multiplier && !init(hdl)) if (!_multiplier && !init(hdl))
return QString(); return QString();
@ -183,10 +196,10 @@ Label LBLFile::label(Handle &hdl, quint32 offset, bool poi)
switch (_encoding) { switch (_encoding) {
case 6: case 6:
return label6b(hdl, labelOffset); return label6b(hdl, labelOffset, capitalize);
case 9: case 9:
case 10: case 10:
return label8b(hdl, labelOffset); return label8b(hdl, labelOffset, capitalize);
default: default:
return Label(); return Label();
} }

View File

@ -19,13 +19,14 @@ public:
_codec(0), _offset(0), _size(0), _poiOffset(0), _poiSize(0), _codec(0), _offset(0), _size(0), _poiOffset(0), _poiSize(0),
_poiMultiplier(0), _multiplier(0), _encoding(0) {} _poiMultiplier(0), _multiplier(0), _encoding(0) {}
Label label(Handle &hdl, quint32 offset, bool poi = false); Label label(Handle &hdl, quint32 offset, bool poi = false,
bool capitalize = true);
private: private:
bool init(Handle &hdl); bool init(Handle &hdl);
Label label6b(Handle &hdl, quint32 offset) const; Label label6b(Handle &hdl, quint32 offset, bool capitalize) const;
Label label8b(Handle &hdl, quint32 offset) const; Label label8b(Handle &hdl, quint32 offset, bool capitalize) const;
QTextCodec *_codec; QTextCodec *_codec;
quint32 _offset; quint32 _offset;

View File

@ -54,7 +54,8 @@ inline bool pointCb(VectorTile *tile, void *context)
} }
MapData::MapData() : _typ(0), _style(0), _baseMap(false), _valid(false) MapData::MapData() : _typ(0), _style(0), _zooms(15, 28), _baseMap(false),
_valid(false)
{ {
_polyCache.setMaxCost(CACHED_SUBDIVS_COUNT); _polyCache.setMaxCost(CACHED_SUBDIVS_COUNT);
_pointCache.setMaxCost(CACHED_SUBDIVS_COUNT); _pointCache.setMaxCost(CACHED_SUBDIVS_COUNT);
@ -104,8 +105,12 @@ void MapData::load()
if (_typ) if (_typ)
_style = new Style(_typ); _style = new Style(_typ);
else { else {
SubFile typ(ProgramPaths::typFile()); QString typFile(ProgramPaths::typFile());
_style = new Style(&typ); if (!typFile.isEmpty()) {
SubFile typ(typFile);
_style = new Style(&typ);
} else
_style = new Style();
} }
} }

View File

@ -7,6 +7,7 @@
#include <QDebug> #include <QDebug>
#include "common/rectc.h" #include "common/rectc.h"
#include "common/rtree.h" #include "common/rtree.h"
#include "common/range.h"
#include "label.h" #include "label.h"
class Style; class Style;
@ -58,6 +59,7 @@ public:
const QString &name() const {return _name;} const QString &name() const {return _name;}
const RectC &bounds() const {return _bounds;} const RectC &bounds() const {return _bounds;}
const Range &zooms() const {return _zooms;}
const Style *style() const {return _style;} const Style *style() const {return _style;}
void polys(const RectC &rect, int bits, QList<Poly> *polygons, void polys(const RectC &rect, int bits, QList<Poly> *polygons,
QList<Poly> *lines); QList<Poly> *lines);
@ -79,6 +81,7 @@ protected:
SubFile *_typ; SubFile *_typ;
Style *_style; Style *_style;
TileTree _tileTree; TileTree _tileTree;
Range _zooms;
bool _baseMap; bool _baseMap;
bool _valid; bool _valid;

View File

@ -8,6 +8,20 @@
#include "rgnfile.h" #include "rgnfile.h"
static quint64 pointId(const QPoint &pos, quint32 type, quint32 labelPtr)
{
quint64 id;
uint hash = qHash(QPair<uint,uint>(qHash(QPair<int, int>(pos.x(),
pos.y())), labelPtr & 0x3FFFFF));
id = ((quint64)type)<<32 | hash;
// Make country labels precedent over city labels
if (!(type >= 0x1400 && type <= 0x153f))
id |= 1ULL<<63;
return id;
}
bool RGNFile::skipClassFields(Handle &hdl) const bool RGNFile::skipClassFields(Handle &hdl) const
{ {
quint8 flags; quint8 flags;
@ -154,8 +168,8 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8; ? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8;
QPoint pos(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())), QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits()))); subdiv->lat() + LS(lat, 24-subdiv->bits()));
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y())); Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.boundingRect = RectC(c, c); poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat())); poly.points.append(QPointF(c.lon(), c.lat()));
@ -164,8 +178,10 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
DeltaStream stream(*this, hdl, len, bitstreamInfo, labelPtr & 0x400000, DeltaStream stream(*this, hdl, len, bitstreamInfo, labelPtr & 0x400000,
false); false);
while (stream.readNext(lonDelta, latDelta)) { while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += lonDelta<<(24-subdiv->bits()); pos.rx() += LS(lonDelta, (24-subdiv->bits()));
pos.ry() += latDelta<<(24-subdiv->bits()); if (pos.rx() >= 0x800000 && subdiv->lon() >= 0)
pos.rx() = 0x7fffff;
pos.ry() += LS(latDelta, (24-subdiv->bits()));
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y())); Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat())); poly.points.append(QPointF(c.lon(), c.lat()));
@ -219,8 +235,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
labelPtr = 0; labelPtr = 0;
if (!_huffmanTable.isNull()) { if (!_huffmanTable.isNull()) {
pos = QPoint((subdiv->lon()<<8) + ((qint32)lon<<(32-subdiv->bits())), pos = QPoint(LS(subdiv->lon(), 8) + LS(lon, 32-subdiv->bits()),
(subdiv->lat()<<8) + ((qint32)lat<<(32-subdiv->bits()))); LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
qint32 lonDelta, latDelta; qint32 lonDelta, latDelta;
HuffmanStream stream(*this, hdl, len, _huffmanTable, HuffmanStream stream(*this, hdl, len, _huffmanTable,
@ -229,16 +245,18 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
if (shift) { if (shift) {
if (!stream.readOffset(lonDelta, latDelta)) if (!stream.readOffset(lonDelta, latDelta))
return false; return false;
pos = QPoint(pos.x() | lonDelta<<(32-subdiv->bits()-shift), pos = QPoint(pos.x() | LS(lonDelta, 32-subdiv->bits()-shift),
pos.y() | latDelta<<(32-subdiv->bits()-shift)); pos.y() | LS(latDelta, 32-subdiv->bits()-shift));
} }
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y())); Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
poly.boundingRect = RectC(c, c); poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat())); poly.points.append(QPointF(c.lon(), c.lat()));
while (stream.readNext(lonDelta, latDelta)) { while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += lonDelta<<(32-subdiv->bits()-shift); pos.rx() += LS(lonDelta, 32-subdiv->bits()-shift);
pos.ry() += latDelta<<(32-subdiv->bits()-shift); if (pos.rx() < 0 && subdiv->lon() >= 0)
pos.rx() = 0x7fffffff;
pos.ry() += LS(latDelta, 32-subdiv->bits()-shift);
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y())); Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat())); poly.points.append(QPointF(c.lon(), c.lat()));
@ -248,8 +266,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
if (!(stream.atEnd() && stream.flush())) if (!(stream.atEnd() && stream.flush()))
return false; return false;
} else { } else {
pos = QPoint(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())), pos = QPoint(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits()))); subdiv->lat() + LS(lat, 24-subdiv->bits()));
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y())); Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.boundingRect = RectC(c, c); poly.boundingRect = RectC(c, c);
poly.points.append(QPointF(c.lon(), c.lat())); poly.points.append(QPointF(c.lon(), c.lat()));
@ -262,8 +280,10 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
DeltaStream stream(*this, hdl, len - 1, bitstreamInfo, false, true); DeltaStream stream(*this, hdl, len - 1, bitstreamInfo, false, true);
while (stream.readNext(lonDelta, latDelta)) { while (stream.readNext(lonDelta, latDelta)) {
pos.rx() += lonDelta<<(24-subdiv->bits()); pos.rx() += LS(lonDelta, 24-subdiv->bits());
pos.ry() += latDelta<<(24-subdiv->bits()); if (pos.rx() >= 0x800000 && subdiv->lon() >= 0)
pos.rx() = 0x7fffff;
pos.ry() += LS(latDelta, 24-subdiv->bits());
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y())); Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
poly.points.append(QPointF(c.lon(), c.lat())); poly.points.append(QPointF(c.lon(), c.lat()));
@ -294,9 +314,6 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
SegmentType segmentType, LBLFile *lbl, Handle &lblHdl, SegmentType segmentType, LBLFile *lbl, Handle &lblHdl,
QList<IMG::Point> *points) const QList<IMG::Point> *points) const
{ {
quint8 type, subtype;
qint16 lon, lat;
quint32 labelPtr;
const SubDiv::Segment &segment = (segmentType == IndexedPoint) const SubDiv::Segment &segment = (segmentType == IndexedPoint)
? subdiv->idxPoints() : subdiv->points(); ? subdiv->idxPoints() : subdiv->points();
@ -307,6 +324,9 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
while (hdl.pos() < (int)segment.end()) { while (hdl.pos() < (int)segment.end()) {
IMG::Point point; IMG::Point point;
quint8 type, subtype;
qint16 lon, lat;
quint32 labelPtr;
if (!(readUInt8(hdl, type) && readUInt24(hdl, labelPtr) if (!(readUInt8(hdl, type) && readUInt24(hdl, labelPtr)
&& readInt16(hdl, lon) && readInt16(hdl, lat))) && readInt16(hdl, lon) && readInt16(hdl, lat)))
@ -317,20 +337,17 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
} else } else
subtype = 0; subtype = 0;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + LS(lat, 24-subdiv->bits()));
point.type = (quint16)type<<8 | subtype; point.type = (quint16)type<<8 | subtype;
point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
qint32 lonOffset = lon<<(24-subdiv->bits()); point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
qint32 latOffset = lat<<(24-subdiv->bits());
point.coordinates = Coordinates(toWGS24(subdiv->lon() + lonOffset),
toWGS24(subdiv->lat() + latOffset));
uint hash = qHash(QPair<uint,uint>(qHash(QPair<qint32, qint32>
(subdiv->lon() + lonOffset, subdiv->lat() + latOffset)),
labelPtr & 0x3FFFFF));
point.id = ((quint64)point.type)<<32 | hash;
point.poi = labelPtr & 0x400000; point.poi = labelPtr & 0x400000;
if (lbl && (labelPtr & 0x3FFFFF)) if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi); point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi,
!(point.type == 0x1400 || point.type == 0x1500
|| point.type == 0x1e00));
points->append(point); points->append(point);
} }
@ -341,12 +358,8 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl, bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
Handle &lblHdl, QList<IMG::Point> *points) const Handle &lblHdl, QList<IMG::Point> *points) const
{ {
quint8 type, subtype;
qint16 lon, lat;
quint32 labelPtr;
const SubDiv::Segment &segment = subdiv->extPoints(); const SubDiv::Segment &segment = subdiv->extPoints();
if (!segment.isValid()) if (!segment.isValid())
return true; return true;
if (!seek(hdl, segment.offset())) if (!seek(hdl, segment.offset()))
@ -354,19 +367,14 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
while (hdl.pos() < (int)segment.end()) { while (hdl.pos() < (int)segment.end()) {
IMG::Point point; IMG::Point point;
qint16 lon, lat;
quint8 type, subtype;
quint32 labelPtr = 0;
if (!(readUInt8(hdl, type) && readUInt8(hdl, subtype) if (!(readUInt8(hdl, type) && readUInt8(hdl, subtype)
&& readInt16(hdl, lon) && readInt16(hdl, lat))) && readInt16(hdl, lon) && readInt16(hdl, lat)))
return false; return false;
point.type = 0x10000 | (((quint32)type)<<8) | (subtype & 0x1F);
qint32 lonOffset = lon<<(24-subdiv->bits());
qint32 latOffset = lat<<(24-subdiv->bits());
point.coordinates = Coordinates(toWGS24(subdiv->lon() + lonOffset),
toWGS24(subdiv->lat() + latOffset));
labelPtr = 0;
if (subtype & 0x20 && !readUInt24(hdl, labelPtr)) if (subtype & 0x20 && !readUInt24(hdl, labelPtr))
return false; return false;
if (subtype & 0x80 && !skipClassFields(hdl)) if (subtype & 0x80 && !skipClassFields(hdl))
@ -374,15 +382,17 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags, Point)) if (subtype & 0x40 && !skipLclFields(hdl, _pointsFlags, Point))
return false; return false;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
subdiv->lat() + LS(lat, 24-subdiv->bits()));
point.type = 0x10000 | (((quint32)type)<<8) | (subtype & 0x1F);
// Discard NT points breaking style draw order logic (and causing huge // Discard NT points breaking style draw order logic (and causing huge
// performance drawback) // performance drawback)
if (point.type == 0x11400) if (point.type == 0x11400)
continue; continue;
uint hash = qHash(QPair<uint,uint>(qHash(QPair<qint32, qint32> point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
(subdiv->lon() + lonOffset, subdiv->lat() + latOffset)), point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
labelPtr & 0x3FFFFF));
point.id = ((quint64)point.type)<<32 | hash;
point.poi = labelPtr & 0x400000; point.poi = labelPtr & 0x400000;
if (lbl && (labelPtr & 0x3FFFFF)) if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi); point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi);
@ -415,7 +425,7 @@ QMap<RGNFile::SegmentType, SubDiv::Segment> RGNFile::segments(Handle &hdl,
quint32 ls = 0; quint32 ls = 0;
SegmentType lt = (SegmentType)0; SegmentType lt = (SegmentType)0;
for (quint16 mask = 0x1; mask <= 0x10; mask <<= 1) { for (quint8 mask = 0x1; mask <= 0x10; mask <<= 1) {
if (subdiv->objects() & mask) { if (subdiv->objects() & mask) {
if (ls) { if (ls) {
quint16 po; quint16 po;

View File

@ -1,4 +1,5 @@
#include <QImage> #include <QImage>
#include <QPainter>
#include "style.h" #include "style.h"
@ -24,12 +25,12 @@ void Style::defaultPolygonStyle()
_polygons[TYPE(0x12)] = Polygon(QBrush("#e6e2d9")); _polygons[TYPE(0x12)] = Polygon(QBrush("#e6e2d9"));
_polygons[TYPE(0x13)] = Polygon(QBrush("#dbd0b6"), _polygons[TYPE(0x13)] = Polygon(QBrush("#dbd0b6"),
QPen(QColor("#cdccc4"), 1)); QPen(QColor("#cdccc4"), 1));
_polygons[TYPE(0x14)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x14)] = Polygon(QBrush("#cadfaf"));
_polygons[TYPE(0x15)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x15)] = Polygon(QBrush("#cadfaf"));
_polygons[TYPE(0x16)] = Polygon(QBrush(QColor("#9ac269"), _polygons[TYPE(0x16)] = Polygon(QBrush(QColor("#9ac269"),
Qt::BDiagPattern)); Qt::BDiagPattern));
_polygons[TYPE(0x17)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x17)] = Polygon(QBrush("#e4efcf"));
_polygons[TYPE(0x18)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x18)] = Polygon(QBrush("#e3edc6"));
_polygons[TYPE(0x19)] = Polygon(QBrush("#e3edc6"), QPen("#c9d3a5")); _polygons[TYPE(0x19)] = Polygon(QBrush("#e3edc6"), QPen("#c9d3a5"));
_polygons[TYPE(0x1a)] = Polygon(QBrush("#000000", Qt::Dense6Pattern), _polygons[TYPE(0x1a)] = Polygon(QBrush("#000000", Qt::Dense6Pattern),
QPen(QColor("#cdccc4"), 1)); QPen(QColor("#cdccc4"), 1));
@ -59,33 +60,39 @@ void Style::defaultPolygonStyle()
_polygons[TYPE(0x4a)] = Polygon(QBrush("#f1f0e5"), QPen("#f1f0e5")); _polygons[TYPE(0x4a)] = Polygon(QBrush("#f1f0e5"), QPen("#f1f0e5"));
_polygons[TYPE(0x4c)] = Polygon(QBrush("#9fc4e1", Qt::Dense6Pattern)); _polygons[TYPE(0x4c)] = Polygon(QBrush("#9fc4e1", Qt::Dense6Pattern));
_polygons[TYPE(0x4d)] = Polygon(QBrush("#ddf1fd")); _polygons[TYPE(0x4d)] = Polygon(QBrush("#ddf1fd"));
_polygons[TYPE(0x4e)] = Polygon(QBrush("#e3edc1")); _polygons[TYPE(0x4e)] = Polygon(QBrush("#f8f8f8"));
_polygons[TYPE(0x4f)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x4f)] = Polygon(QBrush("#e4efcf"));
_polygons[TYPE(0x50)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x50)] = Polygon(QBrush("#cadfaf"));
_polygons[TYPE(0x51)] = Polygon(QBrush("#9fc4e1", Qt::Dense4Pattern)); _polygons[TYPE(0x51)] = Polygon(QBrush("#9fc4e1", Qt::Dense4Pattern));
_polygons[TYPE(0x52)] = Polygon(QBrush("#d4ebb8")); _polygons[TYPE(0x52)] = Polygon(QBrush("#cadfaf"));
_drawOrder << TYPE(0x4b) << TYPE(0x4a) << TYPE(0x01) << TYPE(0x02) _drawOrder << TYPE(0x4b) << TYPE(0x4a) << TYPE(0x01) << TYPE(0x02)
<< TYPE(0x03) << TYPE(0x17) << TYPE(0x18) << TYPE(0x19) << TYPE(0x1a) << TYPE(0x03) << TYPE(0x17) << TYPE(0x18) << TYPE(0x1a) << TYPE(0x28)
<< TYPE(0x28) << TYPE(0x29) << TYPE(0x32) << TYPE(0x3b) << TYPE(0x3c) << TYPE(0x29) << TYPE(0x32) << TYPE(0x3b) << TYPE(0x3c) << TYPE(0x3d)
<< TYPE(0x3d) << TYPE(0x3e) << TYPE(0x3f) << TYPE(0x40) << TYPE(0x41) << TYPE(0x3e) << TYPE(0x3f) << TYPE(0x40) << TYPE(0x41) << TYPE(0x42)
<< TYPE(0x42) << TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46) << TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46) << TYPE(0x47)
<< TYPE(0x47) << TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d) << TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d) << TYPE(0x4e)
<< TYPE(0x4e) << TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52) << TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52) << TYPE(0x14)
<< TYPE(0x14) << TYPE(0x15) << TYPE(0x16) << TYPE(0x1e) << TYPE(0x1f) << TYPE(0x15) << TYPE(0x16) << TYPE(0x1e) << TYPE(0x1f) << TYPE(0x04)
<< TYPE(0x04) << TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08) << TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08) << TYPE(0x09)
<< TYPE(0x09) << TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d) << TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d) << TYPE(0x0e)
<< TYPE(0x0e) << TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12) << TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12) << TYPE(0x19)
<< TYPE(0x13); << TYPE(0x13);
} }
static QImage railroad()
{
QImage img(16, 4, QImage::Format_ARGB32_Premultiplied);
img.fill(QColor("#717171"));
QPainter p(&img);
p.setPen(QPen(Qt::white, 2));
p.drawLine(9, 2, 15, 2);
return img;
}
void Style::defaultLineStyle() void Style::defaultLineStyle()
{ {
QVector<qreal> pattern;
pattern << 4 << 4;
QPen rr(QColor("#717171"), 3, Qt::CustomDashLine);
rr.setDashPattern(pattern);
_lines[TYPE(0x01)] = Line(QPen(QColor("#9bd772"), 2, Qt::SolidLine), _lines[TYPE(0x01)] = Line(QPen(QColor("#9bd772"), 2, Qt::SolidLine),
QPen(QColor("#72a35a"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); QPen(QColor("#72a35a"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x02)] = Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine), _lines[TYPE(0x02)] = Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine),
@ -109,13 +116,13 @@ void Style::defaultLineStyle()
QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x0c)] = Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine), _lines[TYPE(0x0c)] = Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine),
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x14)] = Line(rr, QPen(Qt::white, 3, Qt::SolidLine, _lines[TYPE(0x14)] = Line(railroad());
Qt::RoundCap, Qt::RoundJoin));
_lines[TYPE(0x16)] = Line(QPen(QColor("#aba083"), 1, Qt::DotLine)); _lines[TYPE(0x16)] = Line(QPen(QColor("#aba083"), 1, Qt::DotLine));
_lines[TYPE(0x18)] = Line(QPen(QColor("#9fc4e1"), 2, Qt::SolidLine)); _lines[TYPE(0x18)] = Line(QPen(QColor("#9fc4e1"), 2, Qt::SolidLine));
_lines[TYPE(0x18)].setTextColor(QColor("#9fc4e1")); _lines[TYPE(0x18)].setTextColor(QColor("#9fc4e1"));
//_lines[TYPE(0x1a)] = Line(QPen(QColor("#7697b7"), 1, Qt::DashLine)); //_lines[TYPE(0x1a)] = Line(QPen(QColor("#7697b7"), 1, Qt::DashLine));
_lines[TYPE(0x1b)] = Line(QPen(QColor("#7697b7"), 1, Qt::DashLine)); _lines[TYPE(0x1b)] = Line(QPen(QColor("#7697b7"), 1, Qt::DashLine));
_lines[TYPE(0x1c)] = Line(QPen(QColor("#505145"), 1, Qt::DashLine));
_lines[TYPE(0x1e)] = Line(QPen(QColor("#505145"), 2, Qt::DashDotLine)); _lines[TYPE(0x1e)] = Line(QPen(QColor("#505145"), 2, Qt::DashDotLine));
_lines[TYPE(0x1f)] = Line(QPen(QColor("#9fc4e1"), 3, Qt::SolidLine)); _lines[TYPE(0x1f)] = Line(QPen(QColor("#9fc4e1"), 3, Qt::SolidLine));
_lines[TYPE(0x1f)].setTextColor(QColor("#9fc4e1")); _lines[TYPE(0x1f)].setTextColor(QColor("#9fc4e1"));
@ -145,6 +152,17 @@ void Style::defaultLineStyle()
void Style::defaultPointStyle() void Style::defaultPointStyle()
{ {
// Countries
_points[TYPE(0x14)].setTextColor(QColor("#505145"));
_points[TYPE(0x14)].setTextFontSize(Small);
_points[TYPE(0x15)].setTextColor(QColor("#505145"));
_points[TYPE(0x15)].setTextFontSize(Small);
// Regions
_points[TYPE(0x1e)].setTextColor(QColor("#505145"));
_points[TYPE(0x1e)].setTextFontSize(ExtraSmall);
_points[TYPE(0x28)].setTextFontSize(Small);
// Cities // Cities
_points[TYPE(0x01)].setTextFontSize(Large); _points[TYPE(0x01)].setTextFontSize(Large);
_points[TYPE(0x02)].setTextFontSize(Large); _points[TYPE(0x02)].setTextFontSize(Large);

View File

@ -16,7 +16,8 @@ public:
None = 1, None = 1,
Small = 2, Small = 2,
Normal = 3, Normal = 3,
Large = 4 Large = 4,
ExtraSmall = 5
}; };
enum POIClass { enum POIClass {

View File

@ -2,7 +2,6 @@
#define SUBFILE_H #define SUBFILE_H
#include <QVector> #include <QVector>
#include <QDebug>
#include <QFile> #include <QFile>
#include "img.h" #include "img.h"

View File

@ -3,6 +3,11 @@
#include "trefile.h" #include "trefile.h"
static inline double RB(qint32 val)
{
return (val == -0x800000 || val == 0x800000) ? 180.0 : toWGS24(val);
}
static void demangle(quint8 *data, quint32 size, quint32 key) static void demangle(quint8 *data, quint32 size, quint32 key)
{ {
static const unsigned char shuf[] = { static const unsigned char shuf[] = {
@ -37,12 +42,13 @@ TREFile::~TREFile()
clear(); clear();
} }
bool TREFile::init(bool baseMap) bool TREFile::init()
{ {
Handle hdl(this); Handle hdl(this);
quint8 locked; quint8 locked;
quint16 hdrLen; quint16 hdrLen;
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen) if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)
&& seek(hdl, _gmpOffset + 0x0D) && readUInt8(hdl, locked))) && seek(hdl, _gmpOffset + 0x0D) && readUInt8(hdl, locked)))
return false; return false;
@ -53,7 +59,8 @@ bool TREFile::init(bool baseMap)
&& readInt24(hdl, east) && readInt24(hdl, south) && readInt24(hdl, west))) && readInt24(hdl, east) && readInt24(hdl, south) && readInt24(hdl, west)))
return false; return false;
_bounds = RectC(Coordinates(toWGS24(west), toWGS24(north)), _bounds = RectC(Coordinates(toWGS24(west), toWGS24(north)),
Coordinates(toWGS24(east), toWGS24(south))); Coordinates(RB(east), toWGS24(south)));
Q_ASSERT(_bounds.left() <= _bounds.right());
// Levels & subdivs info // Levels & subdivs info
quint32 levelsOffset, levelsSize, subdivSize; quint32 levelsOffset, levelsSize, subdivSize;
@ -108,7 +115,7 @@ bool TREFile::init(bool baseMap)
} }
} }
_isBaseMap = baseMap; _isBaseMap = false;
return (_firstLevel >= 0); return (_firstLevel >= 0);
} }
@ -132,8 +139,8 @@ bool TREFile::load(int idx)
for (int j = 0; j < _levels.at(idx).subdivs; j++) { for (int j = 0; j < _levels.at(idx).subdivs; j++) {
quint32 oo; quint32 oo;
qint32 lon, lat; qint32 lon, lat, width, height;
quint16 width, height, nextLevel; quint16 nextLevel;
if (!(readUInt32(hdl, oo) && readInt24(hdl, lon) && readInt24(hdl, lat) if (!(readUInt32(hdl, oo) && readInt24(hdl, lon) && readInt24(hdl, lat)
&& readUInt16(hdl, width) && readUInt16(hdl, height))) && readUInt16(hdl, width) && readUInt16(hdl, height)))
@ -149,17 +156,16 @@ bool TREFile::load(int idx)
s->setEnd(offset); s->setEnd(offset);
width &= 0x7FFF; width &= 0x7FFF;
width <<= (24 - _levels.at(idx).bits); width = LS(width, 24 - _levels.at(idx).bits);
height <<= (24 - _levels.at(idx).bits); height = LS(height, 24 - _levels.at(idx).bits);
s = new SubDiv(offset, lon, lat, _levels.at(idx).bits, objects); s = new SubDiv(offset, lon, lat, _levels.at(idx).bits, objects);
sl.append(s); sl.append(s);
double min[2], max[2]; double min[2], max[2];
RectC bounds(Coordinates(toWGS24(lon - width), RectC bounds(Coordinates(toWGS24(lon - width), toWGS24(lat + height)),
toWGS24(lat + height + 1)), Coordinates(toWGS24(lon + width + 1), Coordinates(RB(lon + width), toWGS24(lat - height)));
toWGS24(lat - height))); Q_ASSERT(bounds.left() <= bounds.right());
min[0] = bounds.left(); min[0] = bounds.left();
min[1] = bounds.bottom(); min[1] = bounds.bottom();
@ -240,15 +246,15 @@ void TREFile::clear()
int TREFile::level(int bits, bool baseMap) int TREFile::level(int bits, bool baseMap)
{ {
int idx = _firstLevel;
if (baseMap) { if (baseMap) {
if (!_isBaseMap && _levels.at(idx).bits > bits) if (!_isBaseMap && _levels.first().bits > bits)
return -1; return -1;
if (_isBaseMap && bits > _levels.last().bits) if (_isBaseMap && bits > _levels.last().bits)
return -1; return -1;
} }
int idx = _firstLevel;
for (int i = idx + 1; i < _levels.size(); i++) { for (int i = idx + 1; i < _levels.size(); i++) {
if (_levels.at(i).bits > bits) if (_levels.at(i).bits > bits)
break; break;

View File

@ -18,13 +18,16 @@ public:
TREFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset) {} TREFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset) {}
~TREFile(); ~TREFile();
bool init(bool baseMap); bool init();
void markAsBasemap() {_isBaseMap = true;}
void clear(); void clear();
const RectC &bounds() const {return _bounds;} const RectC &bounds() const {return _bounds;}
QList<SubDiv*> subdivs(const RectC &rect, int bits, bool baseMap); QList<SubDiv*> subdivs(const RectC &rect, int bits, bool baseMap);
quint32 shift(quint8 bits) const quint32 shift(quint8 bits) const
{return (bits == _levels.last().bits) ? (_flags >> 0xb) & 7 : 0;} {return (bits == _levels.last().bits) ? (_flags >> 0xb) & 7 : 0;}
Range zooms() const
{return Range(_levels.at(_firstLevel).bits, _levels.last().bits);}
private: private:
struct MapLevel { struct MapLevel {

View File

@ -82,12 +82,12 @@ SubFile *VectorTile::addFile(const QString &path, SubFile::Type type)
} }
} }
bool VectorTile::init(bool baseMap) bool VectorTile::init()
{ {
if (_gmp && !initGMP()) if (_gmp && !initGMP())
return false; return false;
if (!(_tre && _tre->init(baseMap) && _rgn)) if (!(_tre && _tre->init() && _rgn))
return false; return false;
return true; return true;

View File

@ -15,10 +15,12 @@ public:
delete _tre; delete _rgn; delete _lbl; delete _net; delete _gmp; delete _tre; delete _rgn; delete _lbl; delete _net; delete _gmp;
} }
bool init(bool baseMap); bool init();
void markAsBasemap() {_tre->markAsBasemap();}
void clear() {_tre->clear();} void clear() {_tre->clear();}
const RectC &bounds() const {return _tre->bounds();} const RectC &bounds() const {return _tre->bounds();}
Range zooms() const {return _tre->zooms();}
SubFile *file(SubFile::Type type); SubFile *file(SubFile::Type type);
SubFile *addFile(IMG *img, SubFile::Type type); SubFile *addFile(IMG *img, SubFile::Type type);

View File

@ -16,6 +16,7 @@
#include "IMG/style.h" #include "IMG/style.h"
#include "IMG/img.h" #include "IMG/img.h"
#include "IMG/gmap.h" #include "IMG/gmap.h"
#include "osm.h"
#include "pcs.h" #include "pcs.h"
#include "rectd.h" #include "rectd.h"
#include "imgmap.h" #include "imgmap.h"
@ -79,8 +80,6 @@ private:
}; };
static const Range zooms(12, 28);
static const QColor shieldColor(Qt::white); static const QColor shieldColor(Qt::white);
static const QColor shieldBgColor1("#dd3e3e"); static const QColor shieldBgColor1("#dd3e3e");
static const QColor shieldBgColor2("#379947"); static const QColor shieldBgColor2("#379947");
@ -129,6 +128,7 @@ static QFont *font(Style::FontSize size, Style::FontSize defaultSize
static QFont large = pixelSizeFont(16); static QFont large = pixelSizeFont(16);
static QFont normal = pixelSizeFont(14); static QFont normal = pixelSizeFont(14);
static QFont small = pixelSizeFont(12); static QFont small = pixelSizeFont(12);
static QFont extraSmall = pixelSizeFont(10);
switch (size) { switch (size) {
case Style::None: case Style::None:
@ -139,6 +139,8 @@ static QFont *font(Style::FontSize size, Style::FontSize defaultSize
return &normal; return &normal;
case Style::Small: case Style::Small:
return &small; return &small;
case Style::ExtraSmall:
return &extraSmall;
default: default:
return font(defaultSize); return font(defaultSize);
} }
@ -243,7 +245,12 @@ IMGMap::IMGMap(const QString &fileName, QObject *parent)
return; return;
} }
_zoom = zooms.min(); // Limit world maps bounds so that the maps can be projected using
// the default Web Mercator projection
_dataBounds = (_data->bounds().height() > 120)
? _data->bounds() & OSM::BOUNDS : _data->bounds();
_zoom = _data->zooms().min();
updateTransform(); updateTransform();
_valid = true; _valid = true;
@ -259,20 +266,13 @@ void IMGMap::unload()
_data->clear(); _data->clear();
} }
QRectF IMGMap::bounds()
{
RectD prect(_data->bounds(), _projection);
return QRectF(_transform.proj2img(prect.topLeft()),
_transform.proj2img(prect.bottomRight()));
}
int IMGMap::zoomFit(const QSize &size, const RectC &rect) int IMGMap::zoomFit(const QSize &size, const RectC &rect)
{ {
if (rect.isValid()) { if (rect.isValid()) {
RectD pr(rect, _projection, 10); RectD pr(rect, _projection, 10);
_zoom = zooms.min(); _zoom = _data->zooms().min();
for (int i = zooms.min() + 1; i <= zooms.max(); i++) { for (int i = _data->zooms().min() + 1; i <= _data->zooms().max(); i++) {
Transform t(transform(i)); Transform t(transform(i));
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight())); QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
if (size.width() < r.width() || size.height() < r.height()) if (size.width() < r.width() || size.height() < r.height())
@ -280,7 +280,7 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
_zoom = i; _zoom = i;
} }
} else } else
_zoom = zooms.max(); _zoom = _data->zooms().max();
updateTransform(); updateTransform();
@ -289,14 +289,14 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
int IMGMap::zoomIn() int IMGMap::zoomIn()
{ {
_zoom = qMin(_zoom + 1, zooms.max()); _zoom = qMin(_zoom + 1, _data->zooms().max());
updateTransform(); updateTransform();
return _zoom; return _zoom;
} }
int IMGMap::zoomOut() int IMGMap::zoomOut()
{ {
_zoom = qMax(_zoom - 1, zooms.min()); _zoom = qMax(_zoom - 1, _data->zooms().min());
updateTransform(); updateTransform();
return _zoom; return _zoom;
} }
@ -311,7 +311,7 @@ Transform IMGMap::transform(int zoom) const
{ {
double scale = _projection.isGeographic() double scale = _projection.isGeographic()
? 360.0 / (1<<zoom) : (2.0 * M_PI * WGS84_RADIUS) / (1<<zoom); ? 360.0 / (1<<zoom) : (2.0 * M_PI * WGS84_RADIUS) / (1<<zoom);
PointD topLeft(_projection.ll2xy(_data->bounds().topLeft())); PointD topLeft(_projection.ll2xy(_dataBounds.topLeft()));
return Transform(ReferencePoint(PointD(0, 0), topLeft), return Transform(ReferencePoint(PointD(0, 0), topLeft),
PointD(scale, scale)); PointD(scale, scale));
} }
@ -319,6 +319,10 @@ Transform IMGMap::transform(int zoom) const
void IMGMap::updateTransform() void IMGMap::updateTransform()
{ {
_transform = transform(_zoom); _transform = transform(_zoom);
RectD prect(_dataBounds, _projection);
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
_transform.proj2img(prect.bottomRight()));
} }
QPointF IMGMap::ll2xy(const Coordinates &c) QPointF IMGMap::ll2xy(const Coordinates &c)
@ -485,7 +489,7 @@ void IMGMap::processShields(QList<MapData::Poly> &lines, const QRect &tileRect,
= shields.constBegin(); it != shields.constEnd(); ++it) { = shields.constBegin(); it != shields.constEnd(); ++it) {
const QPolygonF &p = it.value(); const QPolygonF &p = it.value();
QRectF rect(p.boundingRect() & tileRect); QRectF rect(p.boundingRect() & tileRect);
if (qSqrt(AREA(rect)) < TILE_SIZE/8) if (qSqrt(AREA(rect)) < TILE_SIZE/4)
continue; continue;
QMap<qreal, int> map; QMap<qreal, int> map;
@ -590,15 +594,21 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
tiles.append(RasterTile(this, ttl, key)); tiles.append(RasterTile(this, ttl, key));
RasterTile &tile = tiles.last(); RasterTile &tile = tiles.last();
RectD polyRect(_transform.img2proj(ttl), _transform.img2proj( QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
QPointF(ttl.x() + TILE_SIZE, ttl.y() + TILE_SIZE))); ttl.y() + TILE_SIZE));
_data->polys(polyRect.toRectC(_projection, 4), _zoom, polyRect &= bounds().adjusted(0.5, 0.5, -0.5, -0.5);
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
_data->polys(polyRectD.toRectC(_projection, 4), _zoom,
&(tile.polygons()), &(tile.lines())); &(tile.polygons()), &(tile.lines()));
RectD pointRect(_transform.img2proj(QPointF(ttl.x() - TEXT_EXTENT, QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
ttl.y() - TEXT_EXTENT)), _transform.img2proj(QPointF(ttl.x() ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
+ TILE_SIZE + TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT))); + TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
_data->points(pointRect.toRectC(_projection, 4), _zoom, pointRect &= bounds().adjusted(0.5, 0.5, -0.5, -0.5);
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data->points(pointRectD.toRectC(_projection, 4), _zoom,
&(tile.points())); &(tile.points()));
} }
} }

View File

@ -18,7 +18,7 @@ public:
QString name() const {return _data->name();} QString name() const {return _data->name();}
QRectF bounds(); QRectF bounds() {return _bounds;}
virtual int zoom() const {return _zoom;} virtual int zoom() const {return _zoom;}
virtual void setZoom(int zoom); virtual void setZoom(int zoom);
@ -62,6 +62,8 @@ private:
int _zoom; int _zoom;
Projection _projection; Projection _projection;
Transform _transform; Transform _transform;
QRectF _bounds;
RectC _dataBounds;
bool _valid; bool _valid;
QString _errorString; QString _errorString;

View File

@ -50,12 +50,15 @@ public:
virtual void setProjection(const Projection &) {} virtual void setProjection(const Projection &) {}
virtual bool isValid() const {return true;} virtual bool isValid() const {return true;}
virtual bool isReady() const {return true;}
virtual QString errorString() const {return QString();} virtual QString errorString() const {return QString();}
signals: signals:
void loaded(); void tilesLoaded();
void mapLoaded();
}; };
Q_DECLARE_METATYPE(Map*)
Q_DECLARE_OPERATORS_FOR_FLAGS(Map::Flags) Q_DECLARE_OPERATORS_FOR_FLAGS(Map::Flags)
#endif // MAP_H #endif // MAP_H

View File

@ -1,5 +1,6 @@
#include <QFileInfo> #include <QFileInfo>
#include <QDir> #include <QDir>
#include <QApplication>
#include "atlas.h" #include "atlas.h"
#include "ozimap.h" #include "ozimap.h"
#include "jnxmap.h" #include "jnxmap.h"
@ -12,139 +13,112 @@
#include "maplist.h" #include "maplist.h"
bool MapList::loadMap(Map *map, const QString &path, bool dir) Map *MapList::loadFile(const QString &path, QString &errorString,
{ bool *terminate)
if (map && map->isValid()) {
_maps.append(map);
return true;
} else if (map) {
if (dir)
_errorString += path + ": " + map->errorString() + "\n";
else
_errorString = map->errorString();
return false;
} else {
if (dir)
_errorString += path + ": " + "Unknown map format\n";
else
_errorString = "Unknown map format";
return false;
}
}
Map *MapList::loadSource(const QString &path, bool dir)
{
QString err;
Map *map = MapSource::loadMap(path, err);
if (!map) {
if (dir)
_errorString += path + ": " + err + "\n";
else
_errorString = err;
} else
map->setParent(this);
return map;
}
bool MapList::loadFile(const QString &path, bool *terminate, bool dir)
{ {
QFileInfo fi(path); QFileInfo fi(path);
QString suffix = fi.suffix().toLower(); QString suffix = fi.suffix().toLower();
Map *map = 0; Map *map = 0;
if (Atlas::isAtlas(path)) { if (Atlas::isAtlas(path)) {
*terminate = true; if (terminate)
map = new Atlas(path, this);
} else if (suffix == "xml") {
if (MapSource::isMap(path) && !(map = loadSource(path, dir)))
return false;
else if (GMAP::isGMAP(path)) {
*terminate = true; *terminate = true;
map = new Atlas(path);
} else if (suffix == "xml") {
if (MapSource::isMap(path)) {
if (!(map = MapSource::loadMap(path, errorString)))
return 0;
} else if (GMAP::isGMAP(path)) {
if (terminate)
*terminate = true;
map = new IMGMap(path); map = new IMGMap(path);
} }
} else if (suffix == "jnx") } else if (suffix == "jnx")
map = new JNXMap(path, this); map = new JNXMap(path);
else if (suffix == "tif" || suffix == "tiff") else if (suffix == "tif" || suffix == "tiff")
map = new GeoTIFFMap(path, this); map = new GeoTIFFMap(path);
else if (suffix == "mbtiles") else if (suffix == "mbtiles")
map = new MBTilesMap(path, this); map = new MBTilesMap(path);
else if (suffix == "rmap" || suffix == "rtmap") else if (suffix == "rmap" || suffix == "rtmap")
map = new RMap(path, this); map = new RMap(path);
else if (suffix == "img") else if (suffix == "img")
map = new IMGMap(path, this); map = new IMGMap(path);
else else if (suffix == "map" || suffix == "tar")
map = new OziMap(path, this); map = new OziMap(path);
if (!loadMap(map, path, dir)) { if (map && map->isValid())
return map;
else {
errorString = (map) ? map->errorString() : "Unknown file format";
delete map; delete map;
return false; return 0;
} }
return true;
} }
bool MapList::loadDirR(const QString &path) QList<Map*> MapList::loadDir(const QString &path, QString &errorString)
{ {
QDir md(path); QDir md(path);
md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
md.setSorting(QDir::DirsLast); md.setSorting(QDir::DirsLast);
QFileInfoList ml = md.entryInfoList(); QFileInfoList ml = md.entryInfoList();
bool ret = true; QList<Map*> list;
for (int i = 0; i < ml.size(); i++) { for (int i = 0; i < ml.size(); i++) {
const QFileInfo &fi = ml.at(i); const QFileInfo &fi = ml.at(i);
QString suffix = fi.suffix().toLower(); QString suffix = fi.suffix().toLower();
bool terminate = false; bool terminate = false;
if (fi.isDir() && fi.fileName() != "set") { if (fi.isDir() && fi.fileName() != "set")
if (!loadDirR(fi.absoluteFilePath())) list.append(loadDir(fi.absoluteFilePath(), errorString));
ret = false; else if (filter().contains("*." + suffix)) {
} else if (filter().contains("*." + suffix)) { Map *map = loadFile(fi.absoluteFilePath(), errorString, &terminate);
if (!loadFile(fi.absoluteFilePath(), &terminate, true)) if (map)
ret = false; list.append(map);
else
qWarning("%s: %s", qPrintable(fi.absoluteFilePath()),
qPrintable(errorString));
if (terminate) if (terminate)
break; break;
} }
} }
return ret; return list;
} }
bool MapList::loadFile(const QString &path) QList<Map*> MapList::loadMaps(const QString &path, QString &errorString)
{ {
bool atlas; if (QFileInfo(path).isDir())
return loadDir(path, errorString);
_errorString.clear(); else {
return loadFile(path, &atlas, false); QList<Map*> list;
} Map *map = loadFile(path, errorString, 0);
if (map)
bool MapList::loadDir(const QString &path) list.append(map);
{ return list;
_errorString.clear(); }
return loadDirR(path);
} }
QString MapList::formats() QString MapList::formats()
{ {
return return
tr("Supported files") qApp->translate("MapList", "Supported files")
+ " (*.img *.jnx *.map *.mbtiles *.rmap *.rtmap *.tar *.tba *.tif *.tiff *.xml);;" + " (" + filter().join(" ") + ");;"
+ tr("Garmin IMG maps") + " (*.img *.xml);;" + qApp->translate("MapList", "Garmin IMG maps")
+ tr("Garmin JNX maps") + " (*.jnx);;" + " (*.gmap *.gmapi *.img *.xml);;"
+ tr("OziExplorer maps") + " (*.map);;" + qApp->translate("MapList", "Garmin JNX maps") + " (*.jnx);;"
+ tr("MBTiles maps") + " (*.mbtiles);;" + qApp->translate("MapList", "OziExplorer maps") + " (*.map);;"
+ tr("TrekBuddy maps/atlases") + " (*.tar *.tba);;" + qApp->translate("MapList", "MBTiles maps") + " (*.mbtiles);;"
+ tr("GeoTIFF images") + " (*.tif *.tiff);;" + qApp->translate("MapList", "TrekBuddy maps/atlases") + " (*.tar *.tba);;"
+ tr("TwoNav maps") + " (*.rmap *.rtmap);;" + qApp->translate("MapList", "GeoTIFF images") + " (*.tif *.tiff);;"
+ tr("Online map sources") + " (*.xml)"; + qApp->translate("MapList", "TwoNav maps") + " (*.rmap *.rtmap);;"
+ qApp->translate("MapList", "Online map sources") + " (*.xml)";
} }
QStringList MapList::filter() QStringList MapList::filter()
{ {
QStringList filter; QStringList filter;
filter << "*.img" << "*.jnx" << "*.map" << "*.tba" << "*.tar" << "*.xml" filter << "*.gmap" << "*.gmapi" << "*.img" << "*.jnx" << "*.map"
<< "*.tif" << "*.tiff" << "*.mbtiles" << "*.rmap" << "*.rtmap" << "*.img"; << "*.mbtiles" << "*.rmap" << "*.rtmap" << "*.tar" << "*.tba" << "*.tif"
<< "*.tiff" << "*.xml";
return filter; return filter;
} }

View File

@ -1,35 +1,21 @@
#ifndef MAPLIST_H #ifndef MAPLIST_H
#define MAPLIST_H #define MAPLIST_H
#include <QObject>
#include <QString> #include <QString>
class Map; class Map;
class MapList : public QObject class MapList
{ {
Q_OBJECT
public: public:
MapList(QObject *parent = 0) : QObject(parent) {} static QList<Map*> loadMaps(const QString &path, QString &errorString);
bool loadFile(const QString &path);
bool loadDir(const QString &path);
const QList<Map*> &maps() const {return _maps;}
const QString &errorString() const {return _errorString;}
static QString formats(); static QString formats();
static QStringList filter(); static QStringList filter();
private: private:
bool loadFile(const QString &path, bool *terminate, bool dir); static Map *loadFile(const QString &path, QString &errorString,
bool loadDirR(const QString &path); bool *terminate);
Map *loadSource(const QString &path, bool dir); static QList<Map*> loadDir(const QString &path, QString &errorString);
bool loadMap(Map *map, const QString &path, bool dir);
QList<Map*> _maps;
QString _errorString;
}; };
#endif // MAPLIST_H #endif // MAPLIST_H

View File

@ -300,7 +300,7 @@ Map *MapSource::loadMap(const QString &path, QString &errorString)
case WMS: case WMS:
return new WMSMap(config.name, WMS::Setup(config.url, config.layer, return new WMSMap(config.name, WMS::Setup(config.url, config.layer,
config.style, config.format, config.crs, config.coordinateSystem, config.style, config.format, config.crs, config.coordinateSystem,
config.dimensions, config.authorization)); config.dimensions, config.authorization), config.tileSize);
case TMS: case TMS:
return new OnlineMap(config.name, config.url, config.zooms, return new OnlineMap(config.name, config.url, config.zooms,
config.bounds, config.tileRatio, config.authorization, config.bounds, config.tileRatio, config.authorization,

View File

@ -21,7 +21,7 @@ OnlineMap::OnlineMap(const QString &name, const QString &url,
_tileLoader->setUrl(url); _tileLoader->setUrl(url);
_tileLoader->setAuthorization(authorization); _tileLoader->setAuthorization(authorization);
_tileLoader->setQuadTiles(quadTiles); _tileLoader->setQuadTiles(quadTiles);
connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(loaded())); connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(tilesLoaded()));
} }
QRectF OnlineMap::bounds() QRectF OnlineMap::bounds()

View File

@ -169,6 +169,17 @@ void TileLoader::clearCache()
dir.remove(list.at(i)); dir.remove(list.at(i));
_downloader->clearErrors(); _downloader->clearErrors();
QPixmapCache::clear();
}
void TileLoader::setScaledSize(int size)
{
if (_scaledSize == size)
return;
_scaledSize = size;
QPixmapCache::clear();
} }
QUrl TileLoader::tileUrl(const Tile &tile) const QUrl TileLoader::tileUrl(const Tile &tile) const

View File

@ -16,7 +16,7 @@ public:
void setUrl(const QString &url) {_url = url;} void setUrl(const QString &url) {_url = url;}
void setAuthorization(const Authorization &authorization) void setAuthorization(const Authorization &authorization)
{_authorization = authorization;} {_authorization = authorization;}
void setScaledSize(int size) {_scaledSize = size;} void setScaledSize(int size);
void setQuadTiles(bool quadTiles) {_quadTiles = quadTiles;} void setQuadTiles(bool quadTiles) {_quadTiles = quadTiles;}
void loadTilesAsync(QVector<Tile> &list); void loadTilesAsync(QVector<Tile> &list);

View File

@ -1,3 +1,4 @@
#include <cmath>
#include <QFileInfo> #include <QFileInfo>
#include <QEventLoop> #include <QEventLoop>
#include <QXmlStreamReader> #include <QXmlStreamReader>
@ -7,6 +8,19 @@
#include "wms.h" #include "wms.h"
static QString bareFormat(const QString &format)
{
return format.left(format.indexOf(';')).trimmed();
}
static inline double hint2denominator(double h)
{
/* Some WMS 1.1.1 servers use a 72dpi resolution by default. Using the usual
90dpi (0.28mm) resolution known from later standards (WMS 1.3, WMTS) does
make them return emty images in the "max" scale level. */
return h / (M_SQRT2 * 0.36e-3);
}
WMS::CTX::CTX(const Setup &setup) : setup(setup), formatSupported(false) WMS::CTX::CTX(const Setup &setup) : setup(setup), formatSupported(false)
{ {
QStringList ll = setup.layer().split(','); QStringList ll = setup.layer().split(',');
@ -24,13 +38,48 @@ WMS::CTX::CTX(const Setup &setup) : setup(setup), formatSupported(false)
} }
} }
void WMS::get(QXmlStreamReader &reader, CTX &ctx)
{
while (reader.readNextStartElement()) {
if (reader.name() == "OnlineResource") {
QXmlStreamAttributes attr = reader.attributes();
ctx.url = attr.value("xlink:href").toString();
reader.skipCurrentElement();
} else
reader.skipCurrentElement();
}
}
void WMS::http(QXmlStreamReader &reader, CTX &ctx)
{
while (reader.readNextStartElement()) {
if (reader.name() == "Get")
get(reader, ctx);
else
reader.skipCurrentElement();
}
}
void WMS::dcpType(QXmlStreamReader &reader, CTX &ctx)
{
while (reader.readNextStartElement()) {
if (reader.name() == "HTTP")
http(reader, ctx);
else
reader.skipCurrentElement();
}
}
void WMS::getMap(QXmlStreamReader &reader, CTX &ctx) void WMS::getMap(QXmlStreamReader &reader, CTX &ctx)
{ {
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (reader.name() == "Format") { if (reader.name() == "Format") {
if (reader.readElementText() == ctx.setup.format()) QString format(reader.readElementText());
if (bareFormat(format) == bareFormat(ctx.setup.format()))
ctx.formatSupported = true; ctx.formatSupported = true;
} else } else if (reader.name() == "DCPType")
dcpType(reader, ctx);
else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
@ -97,7 +146,16 @@ void WMS::layer(QXmlStreamReader &reader, CTX &ctx,
CRSs.append(reader.readElementText()); CRSs.append(reader.readElementText());
else if (reader.name() == "Style") else if (reader.name() == "Style")
styles.append(style(reader)); styles.append(style(reader));
else if (reader.name() == "MinScaleDenominator") { else if (reader.name() == "ScaleHint") {
QXmlStreamAttributes attr = reader.attributes();
double minHint = attr.value("min").toString().toDouble();
double maxHint = attr.value("max").toString().toDouble();
if (minHint > 0)
scaleDenominator.setMin(hint2denominator(minHint));
if (maxHint > 0)
scaleDenominator.setMax(hint2denominator(maxHint));
reader.skipCurrentElement();
} else if (reader.name() == "MinScaleDenominator") {
double sd = reader.readElementText().toDouble(); double sd = reader.readElementText().toDouble();
if (sd > 0) if (sd > 0)
scaleDenominator.setMin(sd); scaleDenominator.setMin(sd);
@ -160,10 +218,10 @@ void WMS::capabilities(QXmlStreamReader &reader, CTX &ctx)
} }
} }
bool WMS::parseCapabilities(const QString &path, const Setup &setup) bool WMS::parseCapabilities()
{ {
QFile file(path); QFile file(_path);
CTX ctx(setup); CTX ctx(_setup);
QXmlStreamReader reader; QXmlStreamReader reader;
@ -186,7 +244,7 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup)
reader.raiseError("Not a WMS Capabilities XML file"); reader.raiseError("Not a WMS Capabilities XML file");
} }
if (reader.error()) { if (reader.error()) {
_errorString = QString("%1:%2: %3").arg(path).arg(reader.lineNumber()) _errorString = QString("%1:%2: %3").arg(_path).arg(reader.lineNumber())
.arg(reader.errorString()); .arg(reader.errorString());
return false; return false;
} }
@ -232,10 +290,10 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup)
return false; return false;
} }
_boundingBox = ctx.layers.first().boundingBox; _bbox = ctx.layers.first().boundingBox;
for (int i = 1; i < ctx.layers.size(); i++) for (int i = 1; i < ctx.layers.size(); i++)
_boundingBox &= ctx.layers.at(i).boundingBox; _bbox &= ctx.layers.at(i).boundingBox;
if (_boundingBox.isNull()) { if (_bbox.isNull()) {
_errorString = "Empty layers bounding box join"; _errorString = "Empty layers bounding box join";
return false; return false;
} }
@ -248,40 +306,57 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup)
return false; return false;
} }
if (_version >= "1.3.0") {
if (_setup.coordinateSystem().axisOrder() == CoordinateSystem::Unknown)
_cs = _projection.coordinateSystem();
else
_cs = _setup.coordinateSystem();
} else
_cs = CoordinateSystem::XY;
_getMapUrl = ctx.url.isEmpty() ? _setup.url() : ctx.url;
return true; return true;
} }
bool WMS::getCapabilities(const QString &url, const QString &file, bool WMS::downloadCapabilities(const QString &url)
const Authorization &authorization)
{ {
Downloader d; if (!_downloader) {
QList<Download> dl; _downloader = new Downloader(this);
connect(_downloader, SIGNAL(finished()), this,
dl.append(Download(url, file)); SLOT(capabilitiesReady()));
QEventLoop wait;
QObject::connect(&d, SIGNAL(finished()), &wait, SLOT(quit()));
if (d.get(dl, authorization))
wait.exec();
if (!QFileInfo(file).exists()) {
_errorString = "Error downloading capabilities XML file";
return false;
} }
return true; QList<Download> dl;
dl.append(Download(url, _path));
return _downloader->get(dl, _setup.authorization());
} }
WMS::WMS(const QString &file, const WMS::Setup &setup) : _valid(false) void WMS::capabilitiesReady()
{ {
QString capaUrl = QString("%1%2service=WMS&request=GetCapabilities") if (!QFileInfo(_path).exists()) {
_errorString = "Error downloading capabilities XML file";
_valid = false;
} else {
_ready = true;
_valid = parseCapabilities();
}
emit downloadFinished();
}
WMS::WMS(const QString &file, const WMS::Setup &setup, QObject *parent)
: QObject(parent), _setup(setup), _path(file), _downloader(0), _valid(false),
_ready(false)
{
QString url = QString("%1%2service=WMS&request=GetCapabilities")
.arg(setup.url(), setup.url().contains('?') ? "&" : "?"); .arg(setup.url(), setup.url().contains('?') ? "&" : "?");
if (!QFileInfo(file).exists()) if (!QFileInfo(file).exists())
if (!getCapabilities(capaUrl, file, setup.authorization())) _valid = downloadCapabilities(url);
return; else {
if (!parseCapabilities(file, setup)) _ready = true;
return; _valid = parseCapabilities();
}
_valid = true;
} }

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