mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-07-04 14:49:30 +02:00
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
9e7ebe930e | |||
19bc509043 | |||
335794ee21 | |||
2404107d87 | |||
9447addd19 | |||
b1647d944c | |||
77ac919b83 | |||
4d652aeaff | |||
3ef2361523 | |||
e2b1c2c778 | |||
1f5ecdfc38 | |||
c55b4f1217 | |||
fd71a4c7ce | |||
0b6b09f226 | |||
517ca89814 | |||
455ec3a54b | |||
8cb8d97ee2 | |||
40e520d3bf | |||
6b75442312 | |||
cbc5b2466e | |||
19a847c7d4 | |||
f9e5cb206f | |||
441c738d0f | |||
0bf6d41de6 | |||
b7869e985d | |||
5152d5eb0b | |||
c0653ab0a8 | |||
41d27cabe2 | |||
e2f2e9700f | |||
654bfcd058 | |||
82c0c1f8a7 | |||
9ce6e16b60 | |||
98cd3c3922 | |||
a776f1d30e | |||
aea17c9fed | |||
23c18d4acd | |||
eb8fc7b540 | |||
bf0dd1b24a | |||
9b687bb830 | |||
9859608115 | |||
3d66b2fbb6 | |||
9f62b7114e | |||
c8f7e6f691 | |||
c85f404d28 | |||
273a0f0f27 | |||
bb6d6a4044 | |||
bd3a3b90bc | |||
521369a6ec | |||
440a5736f6 | |||
45a6cdeda0 | |||
f73c27c39c | |||
12827edcb2 | |||
3ec5c37fd5 | |||
ee24bd54f1 | |||
cc22df3bf2 | |||
ef017edbf6 | |||
d7f0cda4b2 | |||
dc03ab91d6 | |||
a898ff2807 | |||
497017091f | |||
9dd4e117f6 | |||
86535021aa | |||
92deaaaf2b | |||
86a943d143 | |||
015a9187a0 | |||
1de9c6ef5d | |||
54b6225c6c | |||
48c7299ba6 | |||
c284b9fa7c | |||
2c503a2406 | |||
27edc4d6b5 | |||
f333a76ef7 | |||
2c114f43c5 | |||
29e29591f8 | |||
e4ac9fda0e | |||
26229e5871 | |||
64bee2f2f4 | |||
e4288ee95c | |||
c9b3c2eedd | |||
42e4b0769f | |||
ce043ef8fa | |||
8c7050e273 | |||
d670107a11 | |||
7b03c4d852 |
@ -1,4 +1,4 @@
|
||||
version: 7.22.{build}
|
||||
version: 7.26.{build}
|
||||
|
||||
configuration:
|
||||
- Release
|
||||
|
@ -3,7 +3,7 @@ unix:!macx {
|
||||
} else {
|
||||
TARGET = GPXSee
|
||||
}
|
||||
VERSION = 7.22
|
||||
VERSION = 7.26
|
||||
|
||||
QT += core \
|
||||
gui \
|
||||
@ -20,6 +20,7 @@ equals(QT_MAJOR_VERSION, 5) : lessThan(QT_MINOR_VERSION, 4) {QT += opengl}
|
||||
INCLUDEPATH += ./src
|
||||
HEADERS += src/common/config.h \
|
||||
src/GUI/graphicsscene.h \
|
||||
src/GUI/mapaction.h \
|
||||
src/GUI/popup.h \
|
||||
src/common/garmin.h \
|
||||
src/common/staticassert.h \
|
||||
@ -248,6 +249,7 @@ SOURCES += src/main.cpp \
|
||||
src/GUI/gearratiographitem.cpp \
|
||||
src/GUI/mapview.cpp \
|
||||
src/GUI/areaitem.cpp \
|
||||
src/data/waypoint.cpp \
|
||||
src/map/IMG/bitmapline.cpp \
|
||||
src/map/IMG/bitstream.cpp \
|
||||
src/map/IMG/deltastream.cpp \
|
||||
|
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
@ -7,7 +7,7 @@
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "7.22"
|
||||
!define VERSION "7.26"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}.exe"
|
||||
|
@ -7,7 +7,7 @@
|
||||
; The name of the installer
|
||||
Name "GPXSee"
|
||||
; Program version
|
||||
!define VERSION "7.22"
|
||||
!define VERSION "7.26"
|
||||
|
||||
; The file to write
|
||||
OutFile "GPXSee-${VERSION}_x64.exe"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<url>https://tileserver.4umaps.com/$z/$x/$y.png</url>
|
||||
<zoom min="2" max="15"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<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>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<url>https://tile.openstreetmap.org/$z/$x/$y.png</url>
|
||||
<copyright>Map data: © OpenStreetMap contributors (ODbL) | Rendering: © OpenStreetMap (CC-BY-SA)</copyright>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<url>https://a.tile.opentopomap.org/$z/$x/$y.png</url>
|
||||
<zoom max="17"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<url>https://basemap.nationalmap.gov/ArcGIS/rest/services/USGSImageryOnly/MapServer/tile/$z/$y/$x</url>
|
||||
<zoom min="2" max="15"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<url>https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/$z/$y/$x</url>
|
||||
<zoom min="2" max="15"/>
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
CadenceGraphItem::CadenceGraphItem(const Graph &graph, GraphType type,
|
||||
int width, const QColor &color, QGraphicsItem *parent)
|
||||
: GraphItem(graph, type, width, color, parent)
|
||||
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
_palette.nextColor();
|
||||
if (!graph.isValid())
|
||||
return 0;
|
||||
}
|
||||
|
||||
ElevationGraphItem *gi = new ElevationGraphItem(graph, _graphType, _width,
|
||||
_palette.nextColor());
|
||||
color, primary ? Qt::SolidLine : Qt::DashLine);
|
||||
gi->setUnits(_units);
|
||||
|
||||
if (type == Track) {
|
||||
if (type == TrackPath) {
|
||||
_tracks.append(gi);
|
||||
if (_showTracks)
|
||||
addGraph(gi);
|
||||
|
||||
_trackAscent += gi->ascent();
|
||||
_trackDescent += gi->descent();
|
||||
_trackMax = nMax(_trackMax, gi->max());
|
||||
_trackMin = nMin(_trackMin, gi->min());
|
||||
if (primary) {
|
||||
_trackAscent += gi->ascent();
|
||||
_trackDescent += gi->descent();
|
||||
_trackMax = nMax(_trackMax, gi->max());
|
||||
_trackMin = nMin(_trackMin, gi->min());
|
||||
}
|
||||
} else {
|
||||
_routes.append(gi);
|
||||
if (_showRoutes)
|
||||
addGraph(gi);
|
||||
|
||||
_routeAscent += gi->ascent();
|
||||
_routeDescent += gi->descent();
|
||||
_routeMax = nMax(_routeMax, gi->max());
|
||||
_routeMin = nMin(_routeMin, gi->min());
|
||||
if (primary) {
|
||||
_routeAscent += gi->ascent();
|
||||
_routeDescent += gi->descent();
|
||||
_routeMax = nMax(_routeMax, gi->max());
|
||||
_routeMin = nMin(_routeMin, gi->min());
|
||||
}
|
||||
}
|
||||
|
||||
return gi;
|
||||
@ -102,11 +105,32 @@ GraphItem *ElevationGraph::loadGraph(const Graph &graph, Type type)
|
||||
QList<GraphItem*> ElevationGraph::loadData(const Data &data)
|
||||
{
|
||||
QList<GraphItem*> graphs;
|
||||
GraphItem *primary, *secondary;
|
||||
|
||||
for (int i = 0; i < data.tracks().count(); i++)
|
||||
graphs.append(loadGraph(data.tracks().at(i).elevation(), Track));
|
||||
for (int i = 0; i < data.routes().count(); i++)
|
||||
graphs.append(loadGraph(data.routes().at(i).elevation(), Route));
|
||||
for (int i = 0; i < data.tracks().count(); i++) {
|
||||
QColor color(_palette.nextColor());
|
||||
const GraphPair &gp = data.tracks().at(i).elevation();
|
||||
|
||||
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++)
|
||||
_palette.nextColor();
|
||||
|
||||
|
@ -21,7 +21,7 @@ public:
|
||||
void showRoutes(bool show);
|
||||
|
||||
private:
|
||||
enum Type {Track, Route};
|
||||
enum PathType {TrackPath, RoutePath};
|
||||
|
||||
qreal max() const;
|
||||
qreal min() const;
|
||||
@ -31,7 +31,8 @@ private:
|
||||
void setYUnits(Units units);
|
||||
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);
|
||||
|
||||
qreal _trackAscent, _trackDescent;
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
|
||||
ElevationGraphItem::ElevationGraphItem(const Graph &graph, GraphType type,
|
||||
int width, const QColor &color, QGraphicsItem *parent)
|
||||
: GraphItem(graph, type, width, color, parent)
|
||||
int width, const QColor &color, Qt::PenStyle style, QGraphicsItem *parent)
|
||||
: GraphItem(graph, type, width, color, style, parent)
|
||||
{
|
||||
_min = GraphItem::min();
|
||||
_max = GraphItem::max();
|
||||
@ -42,5 +42,6 @@ QString ElevationGraphItem::info() const
|
||||
tt.insert(tr("Minimum"), l.toString(min() * scale, 'f', 0)
|
||||
+ UNIT_SPACE + su);
|
||||
|
||||
|
||||
return tt.toString();
|
||||
}
|
||||
|
@ -8,8 +8,10 @@ class ElevationGraphItem : public GraphItem
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum DataType {GPS, DEM};
|
||||
|
||||
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 descent() const {return _descent;}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
GearRatioGraphItem::GearRatioGraphItem(const Graph &graph, GraphType type,
|
||||
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++) {
|
||||
const GraphSegment &segment = graph.at(i);
|
||||
|
@ -5,13 +5,13 @@
|
||||
|
||||
|
||||
GraphItem::GraphItem(const Graph &graph, GraphType type, int width,
|
||||
const QColor &color, QGraphicsItem *parent)
|
||||
: GraphicsItem(parent), _graph(graph), _type(type)
|
||||
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent)
|
||||
: GraphicsItem(parent), _graph(graph), _type(type), _secondaryGraph(0)
|
||||
{
|
||||
Q_ASSERT(_graph.isValid());
|
||||
|
||||
_units = Metric;
|
||||
_pen = QPen(color, width);
|
||||
_pen = QPen(color, width, style);
|
||||
_sx = 0; _sy = 0;
|
||||
_time = _graph.hasTime();
|
||||
setZValue(2.0);
|
||||
|
@ -12,8 +12,8 @@ class GraphItem : public QObject, public GraphicsItem
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GraphItem(const Graph &graph, GraphType type, int width, const QColor &color,
|
||||
QGraphicsItem *parent = 0);
|
||||
GraphItem(const Graph &graph, GraphType type, int width,
|
||||
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0);
|
||||
virtual ~GraphItem() {}
|
||||
|
||||
virtual QString info() const = 0;
|
||||
@ -35,6 +35,9 @@ public:
|
||||
void setWidth(int width);
|
||||
void setUnits(Units units) {_units = units;}
|
||||
|
||||
GraphItem *secondaryGraph() const {return _secondaryGraph;}
|
||||
void setSecondaryGraph(GraphItem *graph) {_secondaryGraph = graph;}
|
||||
|
||||
qreal yAtX(qreal x);
|
||||
qreal distanceAtTime(qreal time);
|
||||
|
||||
@ -69,6 +72,8 @@ private:
|
||||
qreal _sx, _sy;
|
||||
QPen _pen;
|
||||
bool _time;
|
||||
|
||||
GraphItem *_secondaryGraph;
|
||||
};
|
||||
|
||||
#endif // GRAPHITEM_H
|
||||
|
@ -274,8 +274,8 @@ void GraphView::redraw(const QSizeF &size)
|
||||
rx = RangeF(bounds().left() * _xScale, bounds().right() * _xScale);
|
||||
ry = RangeF(bounds().top() * _yScale + _yOffset, bounds().bottom() * _yScale
|
||||
+ _yOffset);
|
||||
if (ry.size() < _minYRange)
|
||||
ry.resize(_minYRange);
|
||||
if (ry.size() < _minYRange * _yScale)
|
||||
ry.resize(_minYRange * _yScale);
|
||||
|
||||
_xAxis->setRange(rx);
|
||||
_yAxis->setRange(ry);
|
||||
@ -419,14 +419,16 @@ void GraphView::updateSliderInfo()
|
||||
{
|
||||
QLocale l(QLocale::system());
|
||||
qreal r = 0, y = 0;
|
||||
GraphItem *cardinal = (_graphs.count() == 1 || (_graphs.count() == 2
|
||||
&& _graphs.first()->secondaryGraph())) ? _graphs.first() : 0;
|
||||
|
||||
if (_graphs.count() == 1) {
|
||||
QRectF br(_graphs.first()->bounds());
|
||||
if (cardinal) {
|
||||
QRectF br(_bounds);
|
||||
if (br.height() < _minYRange)
|
||||
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
|
||||
_minYRange/2 - br.height()/2);
|
||||
|
||||
y = _graphs.first()->yAtX(_sliderPos);
|
||||
y = cardinal->yAtX(_sliderPos);
|
||||
r = (y - br.bottom()) / br.height();
|
||||
}
|
||||
|
||||
@ -436,11 +438,17 @@ void GraphView::updateSliderInfo()
|
||||
|
||||
_sliderInfo->setSide(s);
|
||||
_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)
|
||||
+ UNIT_SPACE + _xUnits, (_graphs.count() > 1) ? QString()
|
||||
: l.toString(-y * _yScale + _yOffset, 'f', _precision) + UNIT_SPACE
|
||||
+ _yUnits);
|
||||
+ UNIT_SPACE + _xUnits);
|
||||
QString yText((!cardinal) ? QString() : l.toString(-y * _yScale + _yOffset,
|
||||
'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)
|
||||
|
222
src/GUI/gui.cpp
222
src/GUI/gui.cpp
@ -51,6 +51,7 @@
|
||||
#include "graphtab.h"
|
||||
#include "graphitem.h"
|
||||
#include "pathitem.h"
|
||||
#include "mapaction.h"
|
||||
#include "gui.h"
|
||||
|
||||
|
||||
@ -58,7 +59,6 @@
|
||||
|
||||
GUI::GUI()
|
||||
{
|
||||
loadMaps();
|
||||
loadPOIs();
|
||||
|
||||
createMapView();
|
||||
@ -106,24 +106,13 @@ GUI::GUI()
|
||||
updateStatusBarInfo();
|
||||
}
|
||||
|
||||
void GUI::loadMaps()
|
||||
{
|
||||
_ml = new MapList(this);
|
||||
QString mapDir(ProgramPaths::mapDir());
|
||||
|
||||
if (!mapDir.isNull() && !_ml->loadDir(mapDir))
|
||||
qWarning("%s", qPrintable(_ml->errorPath() + ": " + _ml->errorString()));
|
||||
|
||||
_map = new EmptyMap(this);
|
||||
}
|
||||
|
||||
void GUI::loadPOIs()
|
||||
{
|
||||
_poi = new POI(this);
|
||||
QString poiDir(ProgramPaths::poiDir());
|
||||
|
||||
if (!poiDir.isNull() && !_poi->loadDir(poiDir))
|
||||
qWarning("%s", qPrintable(_poi->errorString()));
|
||||
QString poiDir(ProgramPaths::poiDir());
|
||||
if (!poiDir.isNull())
|
||||
_poi->loadDir(poiDir);
|
||||
}
|
||||
|
||||
void GUI::createBrowser()
|
||||
@ -134,40 +123,56 @@ void GUI::createBrowser()
|
||||
|
||||
void GUI::createMapActions()
|
||||
{
|
||||
_mapsSignalMapper = new QSignalMapper(this);
|
||||
_mapsActionGroup = new QActionGroup(this);
|
||||
_mapsActionGroup->setExclusive(true);
|
||||
|
||||
for (int i = 0; i < _ml->maps().count(); i++)
|
||||
createMapAction(_ml->maps().at(i));
|
||||
QString mapDir(ProgramPaths::mapDir());
|
||||
if (mapDir.isNull())
|
||||
return;
|
||||
|
||||
connect(_mapsSignalMapper, SIGNAL(mapped(int)), this,
|
||||
SLOT(mapChanged(int)));
|
||||
QString unused;
|
||||
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->setCheckable(true);
|
||||
a->setActionGroup(_mapsActionGroup);
|
||||
|
||||
_mapActions.append(a);
|
||||
_mapsSignalMapper->setMapping(a, _mapActions.size() - 1);
|
||||
connect(a, SIGNAL(triggered()), _mapsSignalMapper, SLOT(map()));
|
||||
connect(a, SIGNAL(triggered()), this, SLOT(mapChanged()));
|
||||
|
||||
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()
|
||||
{
|
||||
_poiFilesSignalMapper = new QSignalMapper(this);
|
||||
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
|
||||
SLOT(poiFileChecked(int)));
|
||||
|
||||
for (int i = 0; i < _poi->files().count(); i++)
|
||||
createPOIFileAction(_poi->files().at(i));
|
||||
|
||||
connect(_poiFilesSignalMapper, SIGNAL(mapped(int)), this,
|
||||
SLOT(poiFileChecked(int)));
|
||||
}
|
||||
|
||||
QAction *GUI::createPOIFileAction(const QString &fileName)
|
||||
@ -281,8 +286,10 @@ void GUI::createActions()
|
||||
createPOIFilesActions();
|
||||
|
||||
// Map actions
|
||||
createMapActions();
|
||||
_showMapAction = new QAction(QIcon(SHOW_MAP_ICON), tr("Show map"),
|
||||
this);
|
||||
_showMapAction->setEnabled(false);
|
||||
_showMapAction->setMenuRole(QAction::NoRole);
|
||||
_showMapAction->setCheckable(true);
|
||||
_showMapAction->setShortcut(SHOW_MAP_SHORTCUT);
|
||||
@ -294,10 +301,10 @@ void GUI::createActions()
|
||||
_loadMapAction->setMenuRole(QAction::NoRole);
|
||||
connect(_loadMapAction, SIGNAL(triggered()), this, SLOT(loadMap()));
|
||||
_clearMapCacheAction = new QAction(tr("Clear tile cache"), this);
|
||||
_clearMapCacheAction->setEnabled(false);
|
||||
_clearMapCacheAction->setMenuRole(QAction::NoRole);
|
||||
connect(_clearMapCacheAction, SIGNAL(triggered()), _mapView,
|
||||
SLOT(clearMapCache()));
|
||||
createMapActions();
|
||||
_nextMapAction = new QAction(tr("Next map"), this);
|
||||
_nextMapAction->setMenuRole(QAction::NoRole);
|
||||
_nextMapAction->setShortcut(NEXT_MAP_SHORTCUT);
|
||||
@ -308,10 +315,6 @@ void GUI::createActions()
|
||||
_prevMapAction->setShortcut(PREV_MAP_SHORTCUT);
|
||||
connect(_prevMapAction, SIGNAL(triggered()), this, SLOT(prevMap()));
|
||||
addAction(_prevMapAction);
|
||||
if (_ml->maps().isEmpty()) {
|
||||
_showMapAction->setEnabled(false);
|
||||
_clearMapCacheAction->setEnabled(false);
|
||||
}
|
||||
_showCoordinatesAction = new QAction(tr("Show cursor coordinates"), this);
|
||||
_showCoordinatesAction->setMenuRole(QAction::NoRole);
|
||||
_showCoordinatesAction->setCheckable(true);
|
||||
@ -506,7 +509,7 @@ void GUI::createMenus()
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
_mapMenu = menuBar()->addMenu(tr("&Map"));
|
||||
_mapMenu->addActions(_mapActions);
|
||||
_mapMenu->addActions(_mapsActionGroup->actions());
|
||||
_mapsEnd = _mapMenu->addSeparator();
|
||||
_mapMenu->addAction(_loadMapAction);
|
||||
_mapMenu->addAction(_clearMapCacheAction);
|
||||
@ -608,6 +611,7 @@ void GUI::createToolBars()
|
||||
|
||||
void GUI::createMapView()
|
||||
{
|
||||
_map = new EmptyMap(this);
|
||||
_mapView = new MapView(_map, _poi, this);
|
||||
_mapView->setSizePolicy(QSizePolicy(QSizePolicy::Ignored,
|
||||
QSizePolicy::Expanding));
|
||||
@ -905,9 +909,14 @@ void GUI::openOptions()
|
||||
Track::action(options.option); \
|
||||
reload = true; \
|
||||
}
|
||||
#define SET_DATA_OPTION(option, action) \
|
||||
#define SET_ROUTE_OPTION(option, action) \
|
||||
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; \
|
||||
}
|
||||
|
||||
@ -953,13 +962,18 @@ void GUI::openOptions()
|
||||
SET_TRACK_OPTION(pauseSpeed, setPauseSpeed);
|
||||
SET_TRACK_OPTION(pauseInterval, setPauseInterval);
|
||||
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)
|
||||
_poi->setRadius(options.poiRadius);
|
||||
if (options.poiUseDEM != _options.poiUseDEM)
|
||||
_poi->useDEM(options.poiUseDEM);
|
||||
|
||||
if (options.pixmapCache != _options.pixmapCache)
|
||||
QPixmapCache::setCacheLimit(options.pixmapCache * 1024);
|
||||
@ -1322,25 +1336,49 @@ void GUI::loadMap()
|
||||
|
||||
bool GUI::loadMap(const QString &fileName)
|
||||
{
|
||||
// On OS X fileName may be a directory!
|
||||
|
||||
if (fileName.isEmpty())
|
||||
return false;
|
||||
|
||||
QFileInfo fi(fileName);
|
||||
bool res = fi.isDir() ? _ml->loadDir(fileName) : _ml->loadFile(fileName);
|
||||
QString error;
|
||||
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;
|
||||
}
|
||||
|
||||
if (res) {
|
||||
QAction *a = createMapAction(_ml->maps().last());
|
||||
for (int i = 0; i < maps.size(); i++) {
|
||||
Map *map = maps.at(i);
|
||||
MapAction *a = createMapAction(map);
|
||||
_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);
|
||||
_clearMapCacheAction->setEnabled(true);
|
||||
a->trigger();
|
||||
return true;
|
||||
} else {
|
||||
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);
|
||||
|
||||
return false;
|
||||
action->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1382,31 +1420,42 @@ void GUI::updateWindowTitle()
|
||||
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);
|
||||
}
|
||||
|
||||
void GUI::nextMap()
|
||||
{
|
||||
if (_ml->maps().count() < 2)
|
||||
QAction *checked = _mapsActionGroup->checkedAction();
|
||||
if (!checked)
|
||||
return;
|
||||
|
||||
int next = (_ml->maps().indexOf(_map) + 1) % _ml->maps().count();
|
||||
_mapActions.at(next)->setChecked(true);
|
||||
mapChanged(next);
|
||||
QList<QAction*> maps = _mapsActionGroup->actions();
|
||||
for (int i = 1; i < maps.size(); i++) {
|
||||
int next = (maps.indexOf(checked) + i) % maps.count();
|
||||
if (maps.at(next)->isEnabled()) {
|
||||
maps.at(next)->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::prevMap()
|
||||
{
|
||||
if (_ml->maps().count() < 2)
|
||||
QAction *checked = _mapsActionGroup->checkedAction();
|
||||
if (!checked)
|
||||
return;
|
||||
|
||||
int prev = (_ml->maps().indexOf(_map) + _ml->maps().count() - 1)
|
||||
% _ml->maps().count();
|
||||
_mapActions.at(prev)->setChecked(true);
|
||||
mapChanged(prev);
|
||||
QList<QAction*> maps = _mapsActionGroup->actions();
|
||||
for (int i = 1; i < maps.size(); i++) {
|
||||
int prev = (maps.indexOf(checked) + maps.count() - i) % maps.count();
|
||||
if (maps.at(prev)->isEnabled()) {
|
||||
maps.at(prev)->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::poiFileChecked(int index)
|
||||
@ -1812,10 +1861,14 @@ void GUI::writeSettings()
|
||||
settings.setValue(USE_REPORTED_SPEED_SETTING, _options.useReportedSpeed);
|
||||
if (_options.dataUseDEM != DATA_USE_DEM_DEFAULT)
|
||||
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)
|
||||
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)
|
||||
settings.setValue(USE_OPENGL_SETTING, _options.useOpenGL);
|
||||
#ifdef ENABLE_HTTP2
|
||||
@ -1899,9 +1952,11 @@ void GUI::readSettings()
|
||||
_showMapAction->setChecked(true);
|
||||
else
|
||||
_mapView->showMap(false);
|
||||
if (_ml->maps().count()) {
|
||||
int index = mapIndex(settings.value(CURRENT_MAP_SETTING).toString());
|
||||
_mapActions.at(index)->trigger();
|
||||
QAction *ma = mapAction(settings.value(CURRENT_MAP_SETTING).toString());
|
||||
if (ma) {
|
||||
ma->trigger();
|
||||
_showMapAction->setEnabled(true);
|
||||
_clearMapCacheAction->setEnabled(true);
|
||||
}
|
||||
if (settings.value(SHOW_COORDINATES_SETTING, SHOW_COORDINATES_DEFAULT)
|
||||
.toBool()) {
|
||||
@ -2078,14 +2133,18 @@ void GUI::readSettings()
|
||||
USE_REPORTED_SPEED_DEFAULT).toBool();
|
||||
_options.dataUseDEM = settings.value(DATA_USE_DEM_SETTING,
|
||||
DATA_USE_DEM_DEFAULT).toBool();
|
||||
_options.showSecondaryElevation = settings.value(
|
||||
SHOW_SECONDARY_ELEVATION_SETTING,
|
||||
SHOW_SECONDARY_ELEVATION_DEFAULT).toBool();
|
||||
_options.showSecondarySpeed = settings.value(
|
||||
SHOW_SECONDARY_SPEED_SETTING,
|
||||
SHOW_SECONDARY_SPEED_DEFAULT).toBool();
|
||||
_options.automaticPause = settings.value(AUTOMATIC_PAUSE_SETTING,
|
||||
AUTOMATIC_PAUSE_DEFAULT).toBool();
|
||||
_options.pauseInterval = settings.value(PAUSE_INTERVAL_SETTING,
|
||||
PAUSE_INTERVAL_DEFAULT).toInt();
|
||||
_options.poiRadius = settings.value(POI_RADIUS_SETTING, POI_RADIUS_DEFAULT)
|
||||
.toInt();
|
||||
_options.poiUseDEM = settings.value(POI_USE_DEM_SETTING,
|
||||
POI_USE_DEM_DEFAULT).toBool();
|
||||
_options.useOpenGL = settings.value(USE_OPENGL_SETTING, USE_OPENGL_DEFAULT)
|
||||
.toBool();
|
||||
#ifdef ENABLE_HTTP2
|
||||
@ -2165,21 +2224,38 @@ void GUI::readSettings()
|
||||
Track::setPauseSpeed(_options.pauseSpeed);
|
||||
Track::setPauseInterval(_options.pauseInterval);
|
||||
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->useDEM(_options.poiUseDEM);
|
||||
|
||||
QPixmapCache::setCacheLimit(_options.pixmapCache * 1024);
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
int GUI::mapIndex(const QString &name)
|
||||
QAction *GUI::mapAction(const QString &name)
|
||||
{
|
||||
for (int i = 0; i < _ml->maps().count(); i++)
|
||||
if (_ml->maps().at(i)->name() == name)
|
||||
return i;
|
||||
QList<QAction *> maps = _mapsActionGroup->actions();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -26,9 +26,9 @@ class FileBrowser;
|
||||
class GraphTab;
|
||||
class MapView;
|
||||
class Map;
|
||||
class MapList;
|
||||
class POI;
|
||||
class QScreen;
|
||||
class MapAction;
|
||||
|
||||
class GUI : public QMainWindow
|
||||
{
|
||||
@ -64,7 +64,7 @@ private slots:
|
||||
void prevMap();
|
||||
void openOptions();
|
||||
|
||||
void mapChanged(int);
|
||||
void mapChanged();
|
||||
void graphChanged(int);
|
||||
void poiFileChecked(int);
|
||||
|
||||
@ -88,16 +88,18 @@ private slots:
|
||||
void screenChanged(QScreen *screen);
|
||||
void logicalDotsPerInchChanged(qreal dpi);
|
||||
|
||||
void mapLoaded();
|
||||
void mapInitialized();
|
||||
|
||||
private:
|
||||
typedef QPair<QDate, QDate> DateRange;
|
||||
|
||||
void loadMaps();
|
||||
void loadPOIs();
|
||||
void closeFiles();
|
||||
void plot(QPrinter *printer);
|
||||
|
||||
QAction *createPOIFileAction(const QString &fileName);
|
||||
QAction *createMapAction(const Map *map);
|
||||
MapAction *createMapAction(Map *map);
|
||||
void createPOIFilesActions();
|
||||
void createMapActions();
|
||||
void createActions();
|
||||
@ -127,7 +129,7 @@ private:
|
||||
qreal distance() const;
|
||||
qreal time() const;
|
||||
qreal movingTime() const;
|
||||
int mapIndex(const QString &name);
|
||||
QAction *mapAction(const QString &name);
|
||||
void readSettings();
|
||||
void writeSettings();
|
||||
|
||||
@ -196,11 +198,9 @@ private:
|
||||
QAction *_showCoordinatesAction;
|
||||
QAction *_openOptionsAction;
|
||||
QAction *_mapsEnd;
|
||||
QList<QAction*> _mapActions;
|
||||
QList<QAction*> _poiFilesActions;
|
||||
|
||||
QList<QAction*> _poiFilesActions;
|
||||
QSignalMapper *_poiFilesSignalMapper;
|
||||
QSignalMapper *_mapsSignalMapper;
|
||||
|
||||
QLabel *_fileNameLabel;
|
||||
QLabel *_distanceLabel;
|
||||
@ -212,7 +212,6 @@ private:
|
||||
QList<GraphTab*> _tabs;
|
||||
|
||||
POI *_poi;
|
||||
MapList *_ml;
|
||||
Map *_map;
|
||||
|
||||
FileBrowser *_browser;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
HeartRateGraphItem::HeartRateGraphItem(const Graph &graph, GraphType type,
|
||||
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
32
src/GUI/mapaction.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef MAPACTION_H
|
||||
#define MAPACTION_H
|
||||
|
||||
#include <QAction>
|
||||
#include "map/map.h"
|
||||
|
||||
class MapAction : public QAction
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MapAction(Map *map, QObject *parent = 0) : QAction(map->name(), parent)
|
||||
{
|
||||
map->setParent(this);
|
||||
setData(QVariant::fromValue(map));
|
||||
setEnabled(map->isValid());
|
||||
connect(map, SIGNAL(mapLoaded()), this, SLOT(mapLoaded()));
|
||||
}
|
||||
|
||||
signals:
|
||||
void loaded();
|
||||
|
||||
private slots:
|
||||
void mapLoaded()
|
||||
{
|
||||
Map *map = data().value<Map*>();
|
||||
setEnabled(map->isValid());
|
||||
emit loaded();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // MAPACTION_H
|
@ -2,7 +2,6 @@
|
||||
#include <QGraphicsScene>
|
||||
#include <QWheelEvent>
|
||||
#include <QApplication>
|
||||
#include <QPixmapCache>
|
||||
#include <QScrollBar>
|
||||
#include "data/poi.h"
|
||||
#include "data/data.h"
|
||||
@ -55,7 +54,7 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent)
|
||||
_map = map;
|
||||
_map->load();
|
||||
_map->setProjection(_projection);
|
||||
connect(_map, SIGNAL(loaded()), this, SLOT(reloadMap()));
|
||||
connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
|
||||
|
||||
_poi = poi;
|
||||
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()));
|
||||
|
||||
_map->unload();
|
||||
disconnect(_map, SIGNAL(loaded()), this, SLOT(reloadMap()));
|
||||
disconnect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
|
||||
|
||||
_map = map;
|
||||
_map->load();
|
||||
@ -325,7 +324,7 @@ void MapView::setMap(Map *map)
|
||||
#ifdef ENABLE_HIDPI
|
||||
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
|
||||
#endif // ENABLE_HIDPI
|
||||
connect(_map, SIGNAL(loaded()), this, SLOT(reloadMap()));
|
||||
connect(_map, SIGNAL(tilesLoaded()), this, SLOT(reloadMap()));
|
||||
|
||||
digitalZoom(0);
|
||||
|
||||
@ -351,7 +350,6 @@ void MapView::setMap(Map *map)
|
||||
centerOn(nc);
|
||||
|
||||
reloadMap();
|
||||
QPixmapCache::clear();
|
||||
}
|
||||
|
||||
void MapView::setPOI(POI *poi)
|
||||
@ -453,10 +451,7 @@ void MapView::setCoordinatesFormat(CoordinatesFormat format)
|
||||
void MapView::clearMapCache()
|
||||
{
|
||||
_map->clearCache();
|
||||
|
||||
fitMapZoom();
|
||||
rescale();
|
||||
centerOn(contentCenter());
|
||||
reloadMap();
|
||||
}
|
||||
|
||||
void MapView::digitalZoom(int zoom)
|
||||
@ -982,7 +977,6 @@ void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
|
||||
|
||||
_deviceRatio = deviceRatio;
|
||||
_mapRatio = mapRatio;
|
||||
QPixmapCache::clear();
|
||||
|
||||
QRectF vr(mapToScene(viewport()->rect()).boundingRect()
|
||||
.intersected(_map->bounds()));
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <QRadioButton>
|
||||
#include <QLabel>
|
||||
#include <QSysInfo>
|
||||
#include <QButtonGroup>
|
||||
#include "map/pcs.h"
|
||||
#include "icons.h"
|
||||
#include "colorbox.h"
|
||||
@ -406,6 +407,8 @@ QWidget *OptionsDialog::createDataPage()
|
||||
_reportedSpeed->setChecked(true);
|
||||
else
|
||||
_computedSpeed->setChecked(true);
|
||||
_showSecondarySpeed = new QCheckBox(tr("Show secondary speed"));
|
||||
_showSecondarySpeed->setChecked(_options->showSecondarySpeed);
|
||||
|
||||
_dataGPSElevation = new QRadioButton(tr("GPS data"));
|
||||
_dataDEMElevation = new QRadioButton(tr("DEM data"));
|
||||
@ -413,19 +416,28 @@ QWidget *OptionsDialog::createDataPage()
|
||||
_dataDEMElevation->setChecked(true);
|
||||
else
|
||||
_dataGPSElevation->setChecked(true);
|
||||
|
||||
_showSecondaryElevation = new QCheckBox(tr("Show secondary elevation"));
|
||||
_showSecondaryElevation->setChecked(_options->showSecondaryElevation);
|
||||
|
||||
QWidget *sourceTab = new QWidget();
|
||||
QVBoxLayout *sourceTabLayout = new QVBoxLayout();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QButtonGroup *speedGroup = new QButtonGroup(this);
|
||||
speedGroup->addButton(_computedSpeed);
|
||||
speedGroup->addButton(_reportedSpeed);
|
||||
QVBoxLayout *speedOptions = new QVBoxLayout();
|
||||
speedOptions->addWidget(_computedSpeed);
|
||||
speedOptions->addWidget(_reportedSpeed);
|
||||
speedOptions->addWidget(_showSecondarySpeed);
|
||||
|
||||
QButtonGroup *elevationGroup = new QButtonGroup(this);
|
||||
elevationGroup->addButton(_dataGPSElevation);
|
||||
elevationGroup->addButton(_dataDEMElevation);
|
||||
QVBoxLayout *elevationOptions = new QVBoxLayout();
|
||||
elevationOptions->addWidget(_dataGPSElevation);
|
||||
elevationOptions->addWidget(_dataDEMElevation);
|
||||
elevationOptions->addWidget(_showSecondaryElevation);
|
||||
|
||||
QFormLayout *formLayout = new QFormLayout();
|
||||
formLayout->addRow(tr("Speed:"), speedOptions);
|
||||
@ -438,12 +450,14 @@ QWidget *OptionsDialog::createDataPage()
|
||||
|
||||
speedLayout->addWidget(_computedSpeed);
|
||||
speedLayout->addWidget(_reportedSpeed);
|
||||
speedLayout->addWidget(_showSecondarySpeed);
|
||||
|
||||
QGroupBox *speedBox = new QGroupBox(tr("Speed"));
|
||||
speedBox->setLayout(speedLayout);
|
||||
|
||||
elevationLayout->addWidget(_dataGPSElevation);
|
||||
elevationLayout->addWidget(_dataDEMElevation);
|
||||
elevationLayout->addWidget(_showSecondaryElevation);
|
||||
|
||||
QGroupBox *elevationBox = new QGroupBox(tr("Elevation"));
|
||||
elevationBox->setLayout(elevationLayout);
|
||||
@ -465,13 +479,6 @@ QWidget *OptionsDialog::createDataPage()
|
||||
|
||||
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->setSingleStep(1);
|
||||
_poiRadius->setDecimals(1);
|
||||
@ -486,13 +493,8 @@ QWidget *OptionsDialog::createPOIPage()
|
||||
_poiRadius->setSuffix(UNIT_SPACE + tr("km"));
|
||||
}
|
||||
|
||||
QVBoxLayout *elevationLayout = new QVBoxLayout();
|
||||
elevationLayout->addWidget(_poiGPSElevation);
|
||||
elevationLayout->addWidget(_poiDEMElevation);
|
||||
|
||||
QFormLayout *poiLayout = new QFormLayout();
|
||||
poiLayout->addRow(tr("Radius:"), _poiRadius);
|
||||
poiLayout->addRow(tr("Elevation:"), elevationLayout);
|
||||
|
||||
QWidget *poiTab = new QWidget();
|
||||
poiTab->setLayout(poiLayout);
|
||||
@ -718,13 +720,14 @@ void OptionsDialog::accept()
|
||||
_options->pauseInterval = _pauseInterval->value();
|
||||
_options->useReportedSpeed = _reportedSpeed->isChecked();
|
||||
_options->dataUseDEM = _dataDEMElevation->isChecked();
|
||||
_options->showSecondaryElevation = _showSecondaryElevation->isChecked();
|
||||
_options->showSecondarySpeed = _showSecondarySpeed->isChecked();
|
||||
|
||||
qreal poiRadius = (_options->units == Imperial)
|
||||
? _poiRadius->value() * MIINM : (_options->units == Nautical)
|
||||
? _poiRadius->value() * NMIINM : _poiRadius->value() * KMINM;
|
||||
if (qAbs(poiRadius - _options->poiRadius) > 0.01)
|
||||
_options->poiRadius = poiRadius;
|
||||
_options->poiUseDEM = _poiDEMElevation->isChecked();
|
||||
|
||||
_options->useOpenGL = _useOpenGL->isChecked();
|
||||
#ifdef ENABLE_HTTP2
|
||||
|
@ -54,9 +54,10 @@ struct Options {
|
||||
int pauseInterval;
|
||||
bool useReportedSpeed;
|
||||
bool dataUseDEM;
|
||||
bool showSecondaryElevation;
|
||||
bool showSecondarySpeed;
|
||||
// POI
|
||||
int poiRadius;
|
||||
bool poiUseDEM;
|
||||
// System
|
||||
bool useOpenGL;
|
||||
#ifdef ENABLE_HTTP2
|
||||
@ -142,10 +143,10 @@ private:
|
||||
QRadioButton *_reportedSpeed;
|
||||
QRadioButton *_dataGPSElevation;
|
||||
QRadioButton *_dataDEMElevation;
|
||||
QCheckBox *_showSecondaryElevation;
|
||||
QCheckBox *_showSecondarySpeed;
|
||||
// POI
|
||||
QDoubleSpinBox *_poiRadius;
|
||||
QRadioButton *_poiGPSElevation;
|
||||
QRadioButton *_poiDEMElevation;
|
||||
// System
|
||||
QSpinBox *_pixmapCache;
|
||||
QSpinBox *_connectionTimeout;
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
PowerGraphItem::PowerGraphItem(const Graph &graph, GraphType type, int width,
|
||||
const QColor &color, QGraphicsItem *parent)
|
||||
: GraphItem(graph, type, width, color, parent)
|
||||
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ QString RouteItem::info() const
|
||||
tt.insert(tr("Name"), _name);
|
||||
if (!_desc.isEmpty())
|
||||
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(),
|
||||
_units));
|
||||
if (!_links.isEmpty()) {
|
||||
@ -43,6 +45,7 @@ RouteItem::RouteItem(const Route &route, Map *map, QGraphicsItem *parent)
|
||||
|
||||
_name = route.name();
|
||||
_desc = route.description();
|
||||
_comment = route.comment();
|
||||
_links = route.links();
|
||||
_coordinatesFormat = DecimalDegrees;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
private:
|
||||
QString _name;
|
||||
QString _desc;
|
||||
QString _comment;
|
||||
QVector<Link> _links;
|
||||
CoordinatesFormat _coordinatesFormat;
|
||||
|
||||
|
@ -145,10 +145,12 @@
|
||||
#define USE_REPORTED_SPEED_DEFAULT false
|
||||
#define DATA_USE_DEM_SETTING "dataUseDEM"
|
||||
#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_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_DEFAULT false
|
||||
#define ENABLE_HTTP2_SETTING "enableHTTP2"
|
||||
|
@ -40,31 +40,46 @@ void SpeedGraph::setInfo()
|
||||
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*> graphs;
|
||||
|
||||
for (int i = 0; i < data.tracks().count(); i++) {
|
||||
GraphItem *primary, *secondary;
|
||||
QColor color(_palette.nextColor());
|
||||
const Track &track = data.tracks().at(i);
|
||||
const Graph &graph = track.speed();
|
||||
const GraphPair &gp = track.speed();
|
||||
|
||||
if (!graph.isValid()) {
|
||||
_palette.nextColor();
|
||||
graphs.append(0);
|
||||
} else {
|
||||
SpeedGraphItem *gi = new SpeedGraphItem(graph, _graphType, _width,
|
||||
_palette.nextColor(), track.movingTime());
|
||||
gi->setTimeType(_timeType);
|
||||
gi->setUnits(_units);
|
||||
primary = loadGraph(gp.primary(), track, color, true);
|
||||
secondary = primary
|
||||
? loadGraph(gp.secondary(), track, color, false) : 0;
|
||||
if (primary && secondary)
|
||||
primary->setSecondaryGraph(secondary);
|
||||
|
||||
_tracks.append(gi);
|
||||
if (_showTracks)
|
||||
addGraph(gi);
|
||||
|
||||
_avg.append(QPointF(track.distance(), gi->avg()));
|
||||
_mavg.append(QPointF(track.distance(), gi->mavg()));
|
||||
graphs.append(gi);
|
||||
}
|
||||
graphs.append(primary);
|
||||
}
|
||||
|
||||
for (int i = 0; i < data.routes().count(); i++) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "graphtab.h"
|
||||
|
||||
class SpeedGraphItem;
|
||||
class Track;
|
||||
|
||||
class SpeedGraph : public GraphTab
|
||||
{
|
||||
@ -22,6 +23,8 @@ public:
|
||||
void showTracks(bool show);
|
||||
|
||||
private:
|
||||
GraphItem *loadGraph(const Graph &graph, const Track &track,
|
||||
const QColor &color, bool primary);
|
||||
qreal avg() const;
|
||||
qreal max() const {return bounds().bottom();}
|
||||
void setYUnits();
|
||||
|
@ -5,8 +5,8 @@
|
||||
|
||||
|
||||
SpeedGraphItem::SpeedGraphItem(const Graph &graph, GraphType type, int width,
|
||||
const QColor &color, qreal movingTime, QGraphicsItem *parent)
|
||||
: GraphItem(graph, type, width, color, parent)
|
||||
const QColor &color, Qt::PenStyle style, qreal movingTime,
|
||||
QGraphicsItem *parent) : GraphItem(graph, type, width, color, style, parent)
|
||||
{
|
||||
_timeType = Total;
|
||||
|
||||
|
@ -10,7 +10,8 @@ class SpeedGraphItem : public GraphItem
|
||||
|
||||
public:
|
||||
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 mavg() const {return _mavg;}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
TemperatureGraphItem::TemperatureGraphItem(const Graph &graph, GraphType type,
|
||||
int width, const QColor &color, QGraphicsItem *parent)
|
||||
: GraphItem(graph, type, width, color, parent)
|
||||
: GraphItem(graph, type, width, color, Qt::SolidLine, parent)
|
||||
{
|
||||
_min = GraphItem::min();
|
||||
_max = GraphItem::max();
|
||||
|
@ -13,6 +13,8 @@ QString TrackItem::info() const
|
||||
tt.insert(tr("Name"), _name);
|
||||
if (!_desc.isEmpty())
|
||||
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(),
|
||||
_units));
|
||||
if (_time > 0)
|
||||
@ -41,6 +43,7 @@ TrackItem::TrackItem(const Track &track, Map *map, QGraphicsItem *parent)
|
||||
{
|
||||
_name = track.name();
|
||||
_desc = track.description();
|
||||
_comment = track.comment();
|
||||
_links = track.links();
|
||||
_date = track.date();
|
||||
_time = track.time();
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
private:
|
||||
QString _name;
|
||||
QString _desc;
|
||||
QString _comment;
|
||||
QVector<Link> _links;
|
||||
QDateTime _date;
|
||||
qreal _time;
|
||||
|
@ -21,15 +21,23 @@ QString WaypointItem::info() const
|
||||
tt.insert(qApp->translate("WaypointItem", "Name"), _waypoint.name());
|
||||
tt.insert(qApp->translate("WaypointItem", "Coordinates"),
|
||||
Format::coordinates(_waypoint.coordinates(), _format));
|
||||
if (_waypoint.hasElevation())
|
||||
tt.insert(qApp->translate("WaypointItem", "Elevation"),
|
||||
Format::elevation(_waypoint.elevation(), _units));
|
||||
if (!std::isnan(_waypoint.elevations().first)) {
|
||||
QString val = Format::elevation(_waypoint.elevations().first, _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())
|
||||
tt.insert(qApp->translate("WaypointItem", "Date"),
|
||||
_waypoint.timestamp().toString(Qt::SystemLocaleShortDate));
|
||||
if (!_waypoint.description().isEmpty())
|
||||
tt.insert(qApp->translate("WaypointItem", "Description"),
|
||||
_waypoint.description());
|
||||
if (!_waypoint.comment().isEmpty()
|
||||
&& _waypoint.comment() != _waypoint.description())
|
||||
tt.insert(qApp->translate("WaypointItem", "Comment"),
|
||||
_waypoint.comment());
|
||||
if (_waypoint.address().isValid()) {
|
||||
QString addr("<address>");
|
||||
addr += _waypoint.address().street();
|
||||
|
@ -14,8 +14,8 @@ double Coordinates::distanceTo(const Coordinates &c) const
|
||||
#ifndef QT_NO_DEBUG
|
||||
QDebug operator<<(QDebug dbg, const Coordinates &c)
|
||||
{
|
||||
dbg.nospace() << qSetRealNumberPrecision(10) << "Coordinates(" << c.lat()
|
||||
<< ", " << c.lon() << ")";
|
||||
dbg.nospace() << qSetRealNumberPrecision(10) << "Coordinates(" << c.lon()
|
||||
<< ", " << c.lat() << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
#endif // QT_NO_DEBUG
|
||||
|
@ -3,14 +3,16 @@
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#define LS(val, bits) ((qint32)(((quint32)(val))<<(bits)))
|
||||
|
||||
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)
|
||||
{
|
||||
return toWGS32(v<<8);
|
||||
return toWGS32(LS(v, 8));
|
||||
}
|
||||
|
||||
#endif // GARMIN_H
|
||||
|
@ -28,6 +28,9 @@ public:
|
||||
double left() const {return _tl.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 setRight(double val) {_br.rlon() = val;}
|
||||
void setTop(double val) {_tl.rlat() = val;}
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "cupparser.h"
|
||||
#include "gpiparser.h"
|
||||
#include "smlparser.h"
|
||||
#include "dem.h"
|
||||
#include "data.h"
|
||||
|
||||
|
||||
@ -73,49 +72,17 @@ static QMap<QString, Parser*> parsers()
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
QMap<QString, Parser*> Data::_parsers = parsers();
|
||||
bool Data::_useDEM = false;
|
||||
|
||||
void Data::processData(QList<TrackData> &trackData, QList<RouteData> &routeData)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < trackData.count(); i++)
|
||||
_tracks.append(Track(trackData.at(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);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < routeData.count(); 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);
|
||||
QFileInfo fi(fileName);
|
||||
@ -134,8 +101,7 @@ Data::Data(const QString &fileName, bool poi)
|
||||
if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) {
|
||||
if (it.value()->parse(&file, trackData, routeData, _polygons,
|
||||
_waypoints)) {
|
||||
if (!poi)
|
||||
processData(trackData, routeData);
|
||||
processData(trackData, routeData);
|
||||
_valid = true;
|
||||
return;
|
||||
} else {
|
||||
@ -146,8 +112,7 @@ Data::Data(const QString &fileName, bool poi)
|
||||
for (it = _parsers.begin(); it != _parsers.end(); it++) {
|
||||
if (it.value()->parse(&file, trackData, routeData, _polygons,
|
||||
_waypoints)) {
|
||||
if (!poi)
|
||||
processData(trackData, routeData);
|
||||
processData(trackData, routeData);
|
||||
_valid = true;
|
||||
return;
|
||||
}
|
||||
@ -198,8 +163,3 @@ QStringList Data::filter()
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
void Data::useDEM(bool use)
|
||||
{
|
||||
_useDEM = use;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
Data(const QString &fileName, bool poi = false);
|
||||
Data(const QString &fileName);
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
const QString &errorString() const {return _errorString;}
|
||||
@ -28,8 +28,6 @@ public:
|
||||
static QString formats();
|
||||
static QStringList filter();
|
||||
|
||||
static void useDEM(bool use);
|
||||
|
||||
private:
|
||||
void processData(QList<TrackData> &trackData, QList<RouteData> &routeData);
|
||||
|
||||
@ -43,7 +41,6 @@ private:
|
||||
QVector<Waypoint> _waypoints;
|
||||
|
||||
static QMap<QString, Parser*> _parsers;
|
||||
static bool _useDEM;
|
||||
};
|
||||
|
||||
#endif // DATA_H
|
||||
|
@ -282,14 +282,14 @@ static quint32 readNotes(QDataStream &stream, QTextCodec *codec,
|
||||
if (s1 & 0x1) {
|
||||
QList<TranslatedString> obj;
|
||||
ds += readTranslatedObjects(stream, codec, obj);
|
||||
if (!obj.isEmpty() && waypoint.description().isNull())
|
||||
waypoint.setDescription(obj.first().str());
|
||||
if (!obj.isEmpty())
|
||||
waypoint.setComment(obj.first().str());
|
||||
}
|
||||
if (s1 & 0x2) {
|
||||
QString str;
|
||||
ds += readString(stream, codec, str);
|
||||
if (!str.isEmpty() && waypoint.description().isNull())
|
||||
waypoint.setDescription(str);
|
||||
if (!str.isEmpty())
|
||||
waypoint.setComment(str);
|
||||
}
|
||||
|
||||
if (ds != rh.size)
|
||||
|
@ -192,6 +192,8 @@ void GPXParser::waypointData(Waypoint &waypoint, SegmentData *autoRoute)
|
||||
waypoint.setName(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("desc"))
|
||||
waypoint.setDescription(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("cmt"))
|
||||
waypoint.setComment(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("ele"))
|
||||
waypoint.setElevation(number());
|
||||
else if (_reader.name() == QLatin1String("geoidheight"))
|
||||
@ -244,6 +246,8 @@ void GPXParser::routepoints(RouteData &route, QList<TrackData> &tracks)
|
||||
route.setName(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("desc"))
|
||||
route.setDescription(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("cmt"))
|
||||
route.setComment(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("link")) {
|
||||
Link l(link());
|
||||
if (!l.URL().isEmpty())
|
||||
@ -278,6 +282,8 @@ void GPXParser::track(TrackData &track)
|
||||
track.setName(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("desc"))
|
||||
track.setDescription(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("cmt"))
|
||||
track.setComment(_reader.readElementText());
|
||||
else if (_reader.name() == QLatin1String("link")) {
|
||||
Link l(link());
|
||||
if (!l.URL().isEmpty())
|
||||
|
@ -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
|
||||
|
@ -87,10 +87,12 @@ static bool readTimestamp(const char *data, QTime &time)
|
||||
|
||||
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;
|
||||
|
||||
for (int i = 1; i < 7; i++)
|
||||
for (int i = 1; i < 6; i++)
|
||||
if (!::isprint(line[i]))
|
||||
return false;
|
||||
return true;
|
||||
|
@ -14,28 +14,19 @@ POI::POI(QObject *parent) : QObject(parent)
|
||||
{
|
||||
_errorLine = 0;
|
||||
_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;
|
||||
|
||||
index.enabled = true;
|
||||
index.start = _data.size();
|
||||
|
||||
if (!data.isValid()) {
|
||||
if (dir) {
|
||||
if (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();
|
||||
}
|
||||
_errorString = data.errorString();
|
||||
_errorLine = data.errorLine();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -59,37 +50,22 @@ bool POI::loadFile(const QString &path, bool dir)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool POI::loadFile(const QString &path)
|
||||
{
|
||||
_errorString.clear();
|
||||
_errorLine = 0;
|
||||
|
||||
return loadFile(path, false);
|
||||
}
|
||||
|
||||
bool POI::loadDir(const QString &path)
|
||||
void POI::loadDir(const QString &path)
|
||||
{
|
||||
QDir md(path);
|
||||
md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
QFileInfoList fl = md.entryInfoList();
|
||||
bool ret = true;
|
||||
|
||||
_errorString.clear();
|
||||
_errorLine = 0;
|
||||
|
||||
for (int i = 0; i < fl.size(); i++) {
|
||||
const QFileInfo &fi = fl.at(i);
|
||||
|
||||
if (fi.isDir()) {
|
||||
if (!loadDir(fi.absoluteFilePath()))
|
||||
ret = false;
|
||||
} else {
|
||||
if (!loadFile(fi.absoluteFilePath(), true))
|
||||
ret = false;
|
||||
}
|
||||
if (fi.isDir())
|
||||
loadDir(fi.absoluteFilePath());
|
||||
else
|
||||
if (!loadFile(fi.absoluteFilePath()))
|
||||
qWarning(qPrintable(fi.absoluteFilePath() + ": "
|
||||
+ _errorString));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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> ret;
|
||||
@ -158,8 +123,6 @@ QList<Waypoint> POI::points(const Path &path) const
|
||||
for (it = set.constBegin(); it != set.constEnd(); ++it)
|
||||
ret.append(_data.at(*it));
|
||||
|
||||
appendElevation(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -181,8 +144,6 @@ QList<Waypoint> POI::points(const Waypoint &point) const
|
||||
for (it = set.constBegin(); it != set.constEnd(); ++it)
|
||||
ret.append(_data.at(*it));
|
||||
|
||||
appendElevation(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -206,8 +167,6 @@ QList<Waypoint> POI::points(const Area &area) const
|
||||
for (it = set.constBegin(); it != set.constEnd(); ++it)
|
||||
ret.append(_data.at(*it));
|
||||
|
||||
appendElevation(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -253,10 +212,3 @@ void POI::setRadius(unsigned radius)
|
||||
|
||||
emit pointsChanged();
|
||||
}
|
||||
|
||||
void POI::useDEM(bool use)
|
||||
{
|
||||
_useDEM = use;
|
||||
|
||||
emit pointsChanged();
|
||||
}
|
||||
|
@ -20,13 +20,12 @@ public:
|
||||
POI(QObject *parent = 0);
|
||||
|
||||
bool loadFile(const QString &path);
|
||||
bool loadDir(const QString &path);
|
||||
void loadDir(const QString &path);
|
||||
const QString &errorString() const {return _errorString;}
|
||||
int errorLine() const {return _errorLine;}
|
||||
|
||||
unsigned radius() const {return _radius;}
|
||||
void setRadius(unsigned radius);
|
||||
void useDEM(bool use);
|
||||
|
||||
QList<Waypoint> points(const Path &path) const;
|
||||
QList<Waypoint> points(const Waypoint &point) const;
|
||||
@ -49,7 +48,6 @@ private:
|
||||
|
||||
bool loadFile(const QString &path, bool dir);
|
||||
void search(const RectC &rect, QSet<int> &set) const;
|
||||
void appendElevation(QList<Waypoint> &points) const;
|
||||
|
||||
POITree _tree;
|
||||
QVector<Waypoint> _data;
|
||||
@ -57,7 +55,6 @@ private:
|
||||
QList<FileIndex> _indexes;
|
||||
|
||||
unsigned _radius;
|
||||
bool _useDEM;
|
||||
|
||||
QString _errorString;
|
||||
int _errorLine;
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include "dem.h"
|
||||
#include "route.h"
|
||||
|
||||
bool Route::_useDEM = false;
|
||||
bool Route::_show2ndElevation = false;
|
||||
|
||||
Route::Route(const RouteData &data) : _data(data)
|
||||
{
|
||||
@ -25,7 +28,7 @@ Path Route::path() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
Graph Route::elevation() const
|
||||
Graph Route::gpsElevation() const
|
||||
{
|
||||
Graph graph;
|
||||
graph.append(GraphSegment());
|
||||
@ -38,6 +41,38 @@ Graph Route::elevation() const
|
||||
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
|
||||
{
|
||||
return (_distance.isEmpty()) ? 0 : _distance.last();
|
||||
|
@ -11,23 +11,31 @@ class Route
|
||||
public:
|
||||
Route(const RouteData &data);
|
||||
|
||||
Path path() const;
|
||||
|
||||
const RouteData &data() const {return _data;}
|
||||
|
||||
Graph elevation() const;
|
||||
|
||||
Path path() const;
|
||||
GraphPair elevation() const;
|
||||
qreal distance() const;
|
||||
|
||||
const QString &name() const {return _data.name();}
|
||||
const QString &description() const {return _data.description();}
|
||||
const QString &comment() const {return _data.comment();}
|
||||
const QVector<Link> &links() const {return _data.links();}
|
||||
|
||||
bool isValid() const {return _data.size() >= 2;}
|
||||
|
||||
static void useDEM(bool use) {_useDEM = use;}
|
||||
static void showSecondaryElevation(bool show)
|
||||
{_show2ndElevation = show;}
|
||||
|
||||
private:
|
||||
Graph gpsElevation() const;
|
||||
Graph demElevation() const;
|
||||
|
||||
RouteData _data;
|
||||
QVector<qreal> _distance;
|
||||
|
||||
static bool _useDEM;
|
||||
static bool _show2ndElevation;
|
||||
};
|
||||
|
||||
#endif // ROUTE_H
|
||||
|
@ -11,15 +11,18 @@ class RouteData : public QVector<Waypoint>
|
||||
public:
|
||||
const QString &name() const {return _name;}
|
||||
const QString &description() const {return _desc;}
|
||||
const QString &comment() const {return _comment;}
|
||||
const QVector<Link> &links() const {return _links;}
|
||||
|
||||
void setName(const QString &name) {_name = name;}
|
||||
void setDescription(const QString &desc) {_desc = desc;}
|
||||
void setComment(const QString &comment) {_comment = comment;}
|
||||
void addLink(const Link &link) {_links.append(link);}
|
||||
|
||||
private:
|
||||
QString _name;
|
||||
QString _desc;
|
||||
QString _comment;
|
||||
QVector<Link> _links;
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "dem.h"
|
||||
#include "track.h"
|
||||
|
||||
|
||||
@ -13,6 +14,9 @@ int Track::_pauseInterval = 10;
|
||||
|
||||
bool Track::_outlierEliminate = true;
|
||||
bool Track::_useReportedSpeed = false;
|
||||
bool Track::_useDEM = false;
|
||||
bool Track::_show2ndElevation = false;
|
||||
bool Track::_show2ndSpeed = false;
|
||||
|
||||
|
||||
static qreal avg(const QVector<qreal> &v)
|
||||
@ -47,7 +51,7 @@ static QSet<int> eliminate(const QVector<qreal> &v)
|
||||
qreal M = MAD(w, m);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
@ -237,7 +241,48 @@ Graph Track::elevation() const
|
||||
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;
|
||||
|
||||
@ -251,14 +296,10 @@ Graph Track::speed() const
|
||||
qreal v;
|
||||
|
||||
for (int j = 0; j < sd.size(); j++) {
|
||||
if (seg.stop.contains(j) && (!std::isnan(seg.speed.at(j))
|
||||
|| sd.at(j).hasSpeed())) {
|
||||
if (seg.stop.contains(j) && !std::isnan(seg.speed.at(j))) {
|
||||
v = 0;
|
||||
stop.append(gs.size());
|
||||
} else if (_useReportedSpeed && sd.at(j).hasSpeed()
|
||||
&& !seg.outliers.contains(j))
|
||||
v = sd.at(j).speed();
|
||||
else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
|
||||
} else if (!std::isnan(seg.speed.at(j)) && !seg.outliers.contains(j))
|
||||
v = seg.speed.at(j);
|
||||
else
|
||||
continue;
|
||||
@ -276,6 +317,60 @@ Graph Track::speed() const
|
||||
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 ret;
|
||||
|
@ -17,8 +17,8 @@ public:
|
||||
|
||||
Path path() const;
|
||||
|
||||
Graph elevation() const;
|
||||
Graph speed() const;
|
||||
GraphPair elevation() const;
|
||||
GraphPair speed() const;
|
||||
Graph heartRate() const;
|
||||
Graph temperature() const;
|
||||
Graph cadence() const;
|
||||
@ -32,6 +32,7 @@ public:
|
||||
|
||||
const QString &name() const {return _data.name();}
|
||||
const QString &description() const {return _data.description();}
|
||||
const QString &comment() const {return _data.comment();}
|
||||
const QVector<Link> &links() const {return _data.links();}
|
||||
|
||||
bool isValid() const;
|
||||
@ -47,6 +48,11 @@ public:
|
||||
static void setOutlierElimination(bool eliminate)
|
||||
{_outlierEliminate = eliminate;}
|
||||
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:
|
||||
struct Segment {
|
||||
@ -59,6 +65,11 @@ private:
|
||||
|
||||
bool discardStopPoint(const Segment &seg, int i) const;
|
||||
|
||||
Graph demElevation() const;
|
||||
Graph gpsElevation() const;
|
||||
Graph reportedSpeed() const;
|
||||
Graph computedSpeed() const;
|
||||
|
||||
TrackData _data;
|
||||
QList<Segment> _segments;
|
||||
qreal _pause;
|
||||
@ -73,6 +84,9 @@ private:
|
||||
static qreal _pauseSpeed;
|
||||
static int _pauseInterval;
|
||||
static bool _useReportedSpeed;
|
||||
static bool _useDEM;
|
||||
static bool _show2ndElevation;
|
||||
static bool _show2ndSpeed;
|
||||
};
|
||||
|
||||
#endif // TRACK_H
|
||||
|
@ -14,15 +14,18 @@ class TrackData : public QList<SegmentData>
|
||||
public:
|
||||
const QString &name() const {return _name;}
|
||||
const QString &description() const {return _desc;}
|
||||
const QString &comment() const {return _comment;}
|
||||
const QVector<Link> &links() const {return _links;}
|
||||
|
||||
void setName(const QString &name) {_name = name;}
|
||||
void setDescription(const QString &desc) {_desc = desc;}
|
||||
void setComment(const QString &comment) {_comment = comment;}
|
||||
void addLink(const Link &link) {_links.append(link);}
|
||||
|
||||
private:
|
||||
QString _name;
|
||||
QString _desc;
|
||||
QString _comment;
|
||||
QVector<Link> _links;
|
||||
};
|
||||
|
||||
|
23
src/data/waypoint.cpp
Normal file
23
src/data/waypoint.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -14,24 +14,28 @@
|
||||
class Waypoint
|
||||
{
|
||||
public:
|
||||
Waypoint() {_elevation = NAN;}
|
||||
Waypoint(const Coordinates &coordinates) : _coordinates(coordinates)
|
||||
{_elevation = NAN;}
|
||||
Waypoint() : _elevation(NAN) {}
|
||||
Waypoint(const Coordinates &coordinates)
|
||||
: _coordinates(coordinates), _elevation(NAN) {}
|
||||
|
||||
const Coordinates &coordinates() const {return _coordinates;}
|
||||
const QString &name() const {return _name;}
|
||||
const QString &description() const {return _description;}
|
||||
const QString &comment() const {return _comment;}
|
||||
const Address &address() const {return _address;}
|
||||
const QVector<ImageInfo> &images() const {return _images;}
|
||||
const QVector<Link> &links() const {return _links;}
|
||||
const QDateTime ×tamp() const {return _timestamp;}
|
||||
qreal elevation() const {return _elevation;}
|
||||
|
||||
QPair<qreal, qreal> elevations() const;
|
||||
|
||||
void setCoordinates(const Coordinates &coordinates)
|
||||
{_coordinates = coordinates;}
|
||||
void setName(const QString &name) {_name = name;}
|
||||
void setDescription(const QString &description)
|
||||
{_description = description;}
|
||||
void setComment(const QString &comment) {_comment = comment;}
|
||||
void setAddress(const Address &address) {_address = address;}
|
||||
void setTimestamp(const QDateTime ×tamp) {_timestamp = timestamp;}
|
||||
void setElevation(qreal elevation) {_elevation = elevation;}
|
||||
@ -44,15 +48,23 @@ public:
|
||||
{return this->_name == other._name
|
||||
&& this->_coordinates == other._coordinates;}
|
||||
|
||||
static void useDEM(bool use) {_useDEM = use;}
|
||||
static void showSecondaryElevation(bool show)
|
||||
{_show2ndElevation = show;}
|
||||
|
||||
private:
|
||||
Coordinates _coordinates;
|
||||
QString _name;
|
||||
QString _description;
|
||||
QString _comment;
|
||||
Address _address;
|
||||
QVector<ImageInfo> _images;
|
||||
QVector<Link> _links;
|
||||
QDateTime _timestamp;
|
||||
qreal _elevation;
|
||||
|
||||
static bool _useDEM;
|
||||
static bool _show2ndElevation;
|
||||
};
|
||||
|
||||
inline uint qHash(const Waypoint &key)
|
||||
@ -64,11 +76,9 @@ inline uint qHash(const Waypoint &key)
|
||||
inline QDebug operator<<(QDebug dbg, const Waypoint &waypoint)
|
||||
{
|
||||
dbg.nospace() << "Waypoint(" << waypoint.coordinates() << ", "
|
||||
<< waypoint.name() << ", " << waypoint.description() << ")";
|
||||
<< waypoint.name() << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
#endif // QT_NO_DEBUG
|
||||
|
||||
Q_DECLARE_TYPEINFO(Waypoint, Q_MOVABLE_TYPE);
|
||||
|
||||
#endif // WAYPOINT_H
|
||||
|
@ -1,7 +1,13 @@
|
||||
#include <QPainter>
|
||||
#include <QImage>
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <QtCore/qmath.h>
|
||||
#else // QT5
|
||||
#include <QtMath>
|
||||
#endif // QT5
|
||||
#include "bitmapline.h"
|
||||
|
||||
|
||||
static QImage img2line(const QImage &img, int width)
|
||||
{
|
||||
Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied);
|
||||
@ -32,8 +38,8 @@ void BitmapLine::draw(QPainter *painter, const QPolygonF &line,
|
||||
painter->save();
|
||||
painter->translate(segment.p1());
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <QXmlStreamReader>
|
||||
#include <QDir>
|
||||
#include "map/osm.h"
|
||||
#include "vectortile.h"
|
||||
#include "gmap.h"
|
||||
|
||||
@ -84,11 +85,13 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
|
||||
tile->addFile(fi.absoluteFilePath(), tileType(fi.suffix()));
|
||||
}
|
||||
|
||||
if (!tile->init(baseMap)) {
|
||||
if (!tile->init()) {
|
||||
qWarning("%s: Invalid map tile", qPrintable(dir.path()));
|
||||
delete tile;
|
||||
return false;
|
||||
}
|
||||
if (baseMap)
|
||||
tile->markAsBasemap();
|
||||
|
||||
double min[2], max[2];
|
||||
min[0] = tile->bounds().left();
|
||||
@ -98,6 +101,13 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
|
||||
_tileTree.Insert(min, max, tile);
|
||||
|
||||
_bounds |= tile->bounds();
|
||||
if (tile->zooms().min() < _zooms.min())
|
||||
_zooms.setMin(tile->zooms().min());
|
||||
|
||||
// Limit world maps bounds so that the maps can be projected using
|
||||
// the default Web Mercator projection
|
||||
if (_bounds.height() > 120)
|
||||
_bounds &= OSM::BOUNDS;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <QMap>
|
||||
#include <QtEndian>
|
||||
#include "map/osm.h"
|
||||
#include "vectortile.h"
|
||||
#include "img.h"
|
||||
|
||||
@ -132,11 +133,13 @@ IMG::IMG(const QString &fileName) : _file(fileName)
|
||||
}
|
||||
|
||||
// Create tile tree
|
||||
|
||||
int minMapZoom = 24;
|
||||
for (TileMap::const_iterator it = tileMap.constBegin();
|
||||
it != tileMap.constEnd(); ++it) {
|
||||
VectorTile *tile = it.value();
|
||||
|
||||
if (!tile->init(false)) {
|
||||
if (!tile->init()) {
|
||||
qWarning("%s: %s: Invalid map tile", qPrintable(_file.fileName()),
|
||||
qPrintable(it.key()));
|
||||
delete tile;
|
||||
@ -151,8 +154,26 @@ IMG::IMG(const QString &fileName) : _file(fileName)
|
||||
_tileTree.Insert(min, max, tile);
|
||||
|
||||
_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();
|
||||
}
|
||||
|
||||
// Limit world maps bounds so that the maps can be projected using
|
||||
// the default Web Mercator projection
|
||||
if (_bounds.height() > 120)
|
||||
_bounds &= OSM::BOUNDS;
|
||||
|
||||
if (!_tileTree.Count())
|
||||
_errorString = "No usable map tile found";
|
||||
else
|
||||
|
@ -30,15 +30,20 @@ static quint8 SPECIAL_CHARS[] = {
|
||||
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||
};
|
||||
|
||||
static QString capitalize(const QString &str)
|
||||
static bool isAllUpperCase(const QString &str)
|
||||
{
|
||||
if (str.isEmpty())
|
||||
return str;
|
||||
return false;
|
||||
for (int i = 0; i < str.size(); i++)
|
||||
if (str.at(i).isLetter() && !(str.at(i).isUpper()
|
||||
|| str.at(i) == QChar(0x00DF)))
|
||||
return str;
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static QString capitalized(const QString &str)
|
||||
{
|
||||
QString ret(str);
|
||||
for (int i = 0; i < str.size(); i++)
|
||||
if (i && !str.at(i-1).isSpace())
|
||||
@ -77,7 +82,7 @@ bool LBLFile::init(Handle &hdl)
|
||||
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;
|
||||
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};
|
||||
|
||||
for (int cpt = 0; cpt < 4; cpt++) {
|
||||
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d))
|
||||
return Label(capitalize(QString::fromLatin1(label)),
|
||||
Label::Shield(shieldType, shieldLabel));
|
||||
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
|
||||
QString text(QString::fromLatin1(label));
|
||||
return Label(capitalize && isAllUpperCase(text)
|
||||
? capitalized(text) : text, Label::Shield(shieldType,
|
||||
shieldLabel));
|
||||
}
|
||||
switch (curCharSet) {
|
||||
case Normal:
|
||||
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;
|
||||
QByteArray label, shieldLabel;
|
||||
@ -157,12 +165,15 @@ Label LBLFile::label8b(Handle &hdl, quint32 offset) const
|
||||
bap->append(c);
|
||||
}
|
||||
|
||||
return Label(capitalize(_codec ? _codec->toUnicode(label)
|
||||
: QString::fromLatin1(label)), Label::Shield(shieldType, _codec
|
||||
? _codec->toUnicode(shieldLabel) : QString::fromLatin1(shieldLabel)));
|
||||
QString text(_codec ? _codec->toUnicode(label) : QString::fromLatin1(label));
|
||||
QString shieldText(_codec ? _codec->toUnicode(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))
|
||||
return QString();
|
||||
@ -183,10 +194,10 @@ Label LBLFile::label(Handle &hdl, quint32 offset, bool poi)
|
||||
|
||||
switch (_encoding) {
|
||||
case 6:
|
||||
return label6b(hdl, labelOffset);
|
||||
return label6b(hdl, labelOffset, capitalize);
|
||||
case 9:
|
||||
case 10:
|
||||
return label8b(hdl, labelOffset);
|
||||
return label8b(hdl, labelOffset, capitalize);
|
||||
default:
|
||||
return Label();
|
||||
}
|
||||
|
@ -19,13 +19,14 @@ public:
|
||||
_codec(0), _offset(0), _size(0), _poiOffset(0), _poiSize(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:
|
||||
bool init(Handle &hdl);
|
||||
|
||||
Label label6b(Handle &hdl, quint32 offset) const;
|
||||
Label label8b(Handle &hdl, quint32 offset) const;
|
||||
Label label6b(Handle &hdl, quint32 offset, bool capitalize) const;
|
||||
Label label8b(Handle &hdl, quint32 offset, bool capitalize) const;
|
||||
|
||||
QTextCodec *_codec;
|
||||
quint32 _offset;
|
||||
|
@ -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);
|
||||
_pointCache.setMaxCost(CACHED_SUBDIVS_COUNT);
|
||||
@ -104,8 +105,12 @@ void MapData::load()
|
||||
if (_typ)
|
||||
_style = new Style(_typ);
|
||||
else {
|
||||
SubFile typ(ProgramPaths::typFile());
|
||||
_style = new Style(&typ);
|
||||
QString typFile(ProgramPaths::typFile());
|
||||
if (!typFile.isEmpty()) {
|
||||
SubFile typ(typFile);
|
||||
_style = new Style(&typ);
|
||||
} else
|
||||
_style = new Style();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <QDebug>
|
||||
#include "common/rectc.h"
|
||||
#include "common/rtree.h"
|
||||
#include "common/range.h"
|
||||
#include "label.h"
|
||||
|
||||
class Style;
|
||||
@ -58,6 +59,7 @@ public:
|
||||
|
||||
const QString &name() const {return _name;}
|
||||
const RectC &bounds() const {return _bounds;}
|
||||
const Range &zooms() const {return _zooms;}
|
||||
const Style *style() const {return _style;}
|
||||
void polys(const RectC &rect, int bits, QList<Poly> *polygons,
|
||||
QList<Poly> *lines);
|
||||
@ -79,6 +81,7 @@ protected:
|
||||
SubFile *_typ;
|
||||
Style *_style;
|
||||
TileTree _tileTree;
|
||||
Range _zooms;
|
||||
bool _baseMap;
|
||||
|
||||
bool _valid;
|
||||
|
@ -8,6 +8,20 @@
|
||||
#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
|
||||
{
|
||||
quint8 flags;
|
||||
@ -154,8 +168,8 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
|
||||
? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8;
|
||||
|
||||
|
||||
QPoint pos(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())),
|
||||
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits())));
|
||||
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
|
||||
subdiv->lat() + LS(lat, 24-subdiv->bits()));
|
||||
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
|
||||
poly.boundingRect = RectC(c, c);
|
||||
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,
|
||||
false);
|
||||
while (stream.readNext(lonDelta, latDelta)) {
|
||||
pos.rx() += lonDelta<<(24-subdiv->bits());
|
||||
pos.ry() += latDelta<<(24-subdiv->bits());
|
||||
pos.rx() += LS(lonDelta, (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()));
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
@ -219,8 +235,8 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
labelPtr = 0;
|
||||
|
||||
if (!_huffmanTable.isNull()) {
|
||||
pos = QPoint((subdiv->lon()<<8) + ((qint32)lon<<(32-subdiv->bits())),
|
||||
(subdiv->lat()<<8) + ((qint32)lat<<(32-subdiv->bits())));
|
||||
pos = QPoint(LS(subdiv->lon(), 8) + LS(lon, 32-subdiv->bits()),
|
||||
LS(subdiv->lat(), 8) + LS(lat, (32-subdiv->bits())));
|
||||
|
||||
qint32 lonDelta, latDelta;
|
||||
HuffmanStream stream(*this, hdl, len, _huffmanTable,
|
||||
@ -229,16 +245,18 @@ bool RGNFile::extPolyObjects(Handle &hdl, const SubDiv *subdiv, quint32 shift,
|
||||
if (shift) {
|
||||
if (!stream.readOffset(lonDelta, latDelta))
|
||||
return false;
|
||||
pos = QPoint(pos.x() | lonDelta<<(32-subdiv->bits()-shift),
|
||||
pos.y() | latDelta<<(32-subdiv->bits()-shift));
|
||||
pos = QPoint(pos.x() | LS(lonDelta, 32-subdiv->bits()-shift),
|
||||
pos.y() | LS(latDelta, 32-subdiv->bits()-shift));
|
||||
}
|
||||
Coordinates c(toWGS32(pos.x()), toWGS32(pos.y()));
|
||||
poly.boundingRect = RectC(c, c);
|
||||
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||
|
||||
while (stream.readNext(lonDelta, latDelta)) {
|
||||
pos.rx() += lonDelta<<(32-subdiv->bits()-shift);
|
||||
pos.ry() += latDelta<<(32-subdiv->bits()-shift);
|
||||
pos.rx() += LS(lonDelta, 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()));
|
||||
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()))
|
||||
return false;
|
||||
} else {
|
||||
pos = QPoint(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())),
|
||||
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits())));
|
||||
pos = QPoint(subdiv->lon() + LS(lon, 24-subdiv->bits()),
|
||||
subdiv->lat() + LS(lat, 24-subdiv->bits()));
|
||||
Coordinates c(toWGS24(pos.x()), toWGS24(pos.y()));
|
||||
poly.boundingRect = RectC(c, c);
|
||||
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);
|
||||
|
||||
while (stream.readNext(lonDelta, latDelta)) {
|
||||
pos.rx() += lonDelta<<(24-subdiv->bits());
|
||||
pos.ry() += latDelta<<(24-subdiv->bits());
|
||||
pos.rx() += LS(lonDelta, 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()));
|
||||
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,
|
||||
QList<IMG::Point> *points) const
|
||||
{
|
||||
quint8 type, subtype;
|
||||
qint16 lon, lat;
|
||||
quint32 labelPtr;
|
||||
const SubDiv::Segment &segment = (segmentType == IndexedPoint)
|
||||
? subdiv->idxPoints() : subdiv->points();
|
||||
|
||||
@ -307,6 +324,9 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
|
||||
|
||||
while (hdl.pos() < (int)segment.end()) {
|
||||
IMG::Point point;
|
||||
quint8 type, subtype;
|
||||
qint16 lon, lat;
|
||||
quint32 labelPtr;
|
||||
|
||||
if (!(readUInt8(hdl, type) && readUInt24(hdl, labelPtr)
|
||||
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
|
||||
@ -317,20 +337,17 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
|
||||
} else
|
||||
subtype = 0;
|
||||
|
||||
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
|
||||
subdiv->lat() + LS(lat, 24-subdiv->bits()));
|
||||
|
||||
point.type = (quint16)type<<8 | subtype;
|
||||
|
||||
qint32 lonOffset = lon<<(24-subdiv->bits());
|
||||
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.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
|
||||
point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
|
||||
point.poi = labelPtr & 0x400000;
|
||||
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);
|
||||
}
|
||||
@ -341,12 +358,8 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
|
||||
bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv, LBLFile *lbl,
|
||||
Handle &lblHdl, QList<IMG::Point> *points) const
|
||||
{
|
||||
quint8 type, subtype;
|
||||
qint16 lon, lat;
|
||||
quint32 labelPtr;
|
||||
const SubDiv::Segment &segment = subdiv->extPoints();
|
||||
|
||||
|
||||
if (!segment.isValid())
|
||||
return true;
|
||||
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()) {
|
||||
IMG::Point point;
|
||||
qint16 lon, lat;
|
||||
quint8 type, subtype;
|
||||
quint32 labelPtr = 0;
|
||||
|
||||
if (!(readUInt8(hdl, type) && readUInt8(hdl, subtype)
|
||||
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
|
||||
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))
|
||||
return false;
|
||||
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))
|
||||
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
|
||||
// performance drawback)
|
||||
if (point.type == 0x11400)
|
||||
continue;
|
||||
|
||||
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.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
|
||||
point.id = pointId(pos, point.type, labelPtr & 0x3FFFFF);
|
||||
point.poi = labelPtr & 0x400000;
|
||||
if (lbl && (labelPtr & 0x3FFFFF))
|
||||
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;
|
||||
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 (ls) {
|
||||
quint16 po;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include "style.h"
|
||||
|
||||
|
||||
@ -79,13 +80,19 @@ void Style::defaultPolygonStyle()
|
||||
<< 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()
|
||||
{
|
||||
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),
|
||||
QPen(QColor("#72a35a"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
_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));
|
||||
_lines[TYPE(0x0c)] = Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine),
|
||||
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||
_lines[TYPE(0x14)] = Line(rr, QPen(Qt::white, 3, Qt::SolidLine,
|
||||
Qt::RoundCap, Qt::RoundJoin));
|
||||
_lines[TYPE(0x14)] = Line(railroad());
|
||||
_lines[TYPE(0x16)] = Line(QPen(QColor("#aba083"), 1, Qt::DotLine));
|
||||
_lines[TYPE(0x18)] = Line(QPen(QColor("#9fc4e1"), 2, Qt::SolidLine));
|
||||
_lines[TYPE(0x18)].setTextColor(QColor("#9fc4e1"));
|
||||
//_lines[TYPE(0x1a)] = 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(0x1f)] = Line(QPen(QColor("#9fc4e1"), 3, Qt::SolidLine));
|
||||
_lines[TYPE(0x1f)].setTextColor(QColor("#9fc4e1"));
|
||||
@ -145,6 +152,17 @@ void Style::defaultLineStyle()
|
||||
|
||||
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
|
||||
_points[TYPE(0x01)].setTextFontSize(Large);
|
||||
_points[TYPE(0x02)].setTextFontSize(Large);
|
||||
|
@ -16,7 +16,8 @@ public:
|
||||
None = 1,
|
||||
Small = 2,
|
||||
Normal = 3,
|
||||
Large = 4
|
||||
Large = 4,
|
||||
ExtraSmall = 5
|
||||
};
|
||||
|
||||
enum POIClass {
|
||||
|
@ -2,7 +2,6 @@
|
||||
#define SUBFILE_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include "img.h"
|
||||
|
||||
|
@ -3,6 +3,11 @@
|
||||
#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 const unsigned char shuf[] = {
|
||||
@ -37,12 +42,13 @@ TREFile::~TREFile()
|
||||
clear();
|
||||
}
|
||||
|
||||
bool TREFile::init(bool baseMap)
|
||||
bool TREFile::init()
|
||||
{
|
||||
Handle hdl(this);
|
||||
quint8 locked;
|
||||
quint16 hdrLen;
|
||||
|
||||
|
||||
if (!(seek(hdl, _gmpOffset) && readUInt16(hdl, hdrLen)
|
||||
&& seek(hdl, _gmpOffset + 0x0D) && readUInt8(hdl, locked)))
|
||||
return false;
|
||||
@ -53,7 +59,8 @@ bool TREFile::init(bool baseMap)
|
||||
&& readInt24(hdl, east) && readInt24(hdl, south) && readInt24(hdl, west)))
|
||||
return false;
|
||||
_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
|
||||
quint32 levelsOffset, levelsSize, subdivSize;
|
||||
@ -108,7 +115,7 @@ bool TREFile::init(bool baseMap)
|
||||
}
|
||||
}
|
||||
|
||||
_isBaseMap = baseMap;
|
||||
_isBaseMap = false;
|
||||
|
||||
return (_firstLevel >= 0);
|
||||
}
|
||||
@ -132,8 +139,8 @@ bool TREFile::load(int idx)
|
||||
|
||||
for (int j = 0; j < _levels.at(idx).subdivs; j++) {
|
||||
quint32 oo;
|
||||
qint32 lon, lat;
|
||||
quint16 width, height, nextLevel;
|
||||
qint32 lon, lat, width, height;
|
||||
quint16 nextLevel;
|
||||
|
||||
if (!(readUInt32(hdl, oo) && readInt24(hdl, lon) && readInt24(hdl, lat)
|
||||
&& readUInt16(hdl, width) && readUInt16(hdl, height)))
|
||||
@ -149,17 +156,16 @@ bool TREFile::load(int idx)
|
||||
s->setEnd(offset);
|
||||
|
||||
width &= 0x7FFF;
|
||||
width <<= (24 - _levels.at(idx).bits);
|
||||
height <<= (24 - _levels.at(idx).bits);
|
||||
|
||||
width = LS(width, 24 - _levels.at(idx).bits);
|
||||
height = LS(height, 24 - _levels.at(idx).bits);
|
||||
|
||||
s = new SubDiv(offset, lon, lat, _levels.at(idx).bits, objects);
|
||||
sl.append(s);
|
||||
|
||||
double min[2], max[2];
|
||||
RectC bounds(Coordinates(toWGS24(lon - width),
|
||||
toWGS24(lat + height + 1)), Coordinates(toWGS24(lon + width + 1),
|
||||
toWGS24(lat - height)));
|
||||
RectC bounds(Coordinates(toWGS24(lon - width), toWGS24(lat + height)),
|
||||
Coordinates(RB(lon + width), toWGS24(lat - height)));
|
||||
Q_ASSERT(bounds.left() <= bounds.right());
|
||||
|
||||
min[0] = bounds.left();
|
||||
min[1] = bounds.bottom();
|
||||
@ -240,15 +246,15 @@ void TREFile::clear()
|
||||
|
||||
int TREFile::level(int bits, bool baseMap)
|
||||
{
|
||||
int idx = _firstLevel;
|
||||
|
||||
if (baseMap) {
|
||||
if (!_isBaseMap && _levels.at(idx).bits > bits)
|
||||
if (!_isBaseMap && _levels.first().bits > bits)
|
||||
return -1;
|
||||
if (_isBaseMap && bits > _levels.last().bits)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int idx = _firstLevel;
|
||||
|
||||
for (int i = idx + 1; i < _levels.size(); i++) {
|
||||
if (_levels.at(i).bits > bits)
|
||||
break;
|
||||
|
@ -18,13 +18,16 @@ public:
|
||||
TREFile(SubFile *gmp, quint32 offset) : SubFile(gmp, offset) {}
|
||||
~TREFile();
|
||||
|
||||
bool init(bool baseMap);
|
||||
bool init();
|
||||
void markAsBasemap() {_isBaseMap = true;}
|
||||
void clear();
|
||||
|
||||
const RectC &bounds() const {return _bounds;}
|
||||
QList<SubDiv*> subdivs(const RectC &rect, int bits, bool baseMap);
|
||||
quint32 shift(quint8 bits) const
|
||||
{return (bits == _levels.last().bits) ? (_flags >> 0xb) & 7 : 0;}
|
||||
Range zooms() const
|
||||
{return Range(_levels.at(_firstLevel).bits, _levels.last().bits);}
|
||||
|
||||
private:
|
||||
struct MapLevel {
|
||||
|
@ -82,12 +82,12 @@ SubFile *VectorTile::addFile(const QString &path, SubFile::Type type)
|
||||
}
|
||||
}
|
||||
|
||||
bool VectorTile::init(bool baseMap)
|
||||
bool VectorTile::init()
|
||||
{
|
||||
if (_gmp && !initGMP())
|
||||
return false;
|
||||
|
||||
if (!(_tre && _tre->init(baseMap) && _rgn))
|
||||
if (!(_tre && _tre->init() && _rgn))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -15,10 +15,12 @@ public:
|
||||
delete _tre; delete _rgn; delete _lbl; delete _net; delete _gmp;
|
||||
}
|
||||
|
||||
bool init(bool baseMap);
|
||||
bool init();
|
||||
void markAsBasemap() {_tre->markAsBasemap();}
|
||||
void clear() {_tre->clear();}
|
||||
|
||||
const RectC &bounds() const {return _tre->bounds();}
|
||||
Range zooms() const {return _tre->zooms();}
|
||||
|
||||
SubFile *file(SubFile::Type type);
|
||||
SubFile *addFile(IMG *img, SubFile::Type type);
|
||||
|
@ -79,8 +79,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
static const Range zooms(12, 28);
|
||||
|
||||
static const QColor shieldColor(Qt::white);
|
||||
static const QColor shieldBgColor1("#dd3e3e");
|
||||
static const QColor shieldBgColor2("#379947");
|
||||
@ -129,6 +127,7 @@ static QFont *font(Style::FontSize size, Style::FontSize defaultSize
|
||||
static QFont large = pixelSizeFont(16);
|
||||
static QFont normal = pixelSizeFont(14);
|
||||
static QFont small = pixelSizeFont(12);
|
||||
static QFont extraSmall = pixelSizeFont(10);
|
||||
|
||||
switch (size) {
|
||||
case Style::None:
|
||||
@ -139,6 +138,8 @@ static QFont *font(Style::FontSize size, Style::FontSize defaultSize
|
||||
return &normal;
|
||||
case Style::Small:
|
||||
return &small;
|
||||
case Style::ExtraSmall:
|
||||
return &extraSmall;
|
||||
default:
|
||||
return font(defaultSize);
|
||||
}
|
||||
@ -243,7 +244,7 @@ IMGMap::IMGMap(const QString &fileName, QObject *parent)
|
||||
return;
|
||||
}
|
||||
|
||||
_zoom = zooms.min();
|
||||
_zoom = _data->zooms().min();
|
||||
updateTransform();
|
||||
|
||||
_valid = true;
|
||||
@ -259,20 +260,13 @@ void IMGMap::unload()
|
||||
_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)
|
||||
{
|
||||
if (rect.isValid()) {
|
||||
RectD pr(rect, _projection, 10);
|
||||
|
||||
_zoom = zooms.min();
|
||||
for (int i = zooms.min() + 1; i <= zooms.max(); i++) {
|
||||
_zoom = _data->zooms().min();
|
||||
for (int i = _data->zooms().min() + 1; i <= _data->zooms().max(); i++) {
|
||||
Transform t(transform(i));
|
||||
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
|
||||
if (size.width() < r.width() || size.height() < r.height())
|
||||
@ -280,7 +274,7 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
_zoom = i;
|
||||
}
|
||||
} else
|
||||
_zoom = zooms.max();
|
||||
_zoom = _data->zooms().max();
|
||||
|
||||
updateTransform();
|
||||
|
||||
@ -289,14 +283,14 @@ int IMGMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
|
||||
int IMGMap::zoomIn()
|
||||
{
|
||||
_zoom = qMin(_zoom + 1, zooms.max());
|
||||
_zoom = qMin(_zoom + 1, _data->zooms().max());
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
int IMGMap::zoomOut()
|
||||
{
|
||||
_zoom = qMax(_zoom - 1, zooms.min());
|
||||
_zoom = qMax(_zoom - 1, _data->zooms().min());
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
@ -319,6 +313,10 @@ Transform IMGMap::transform(int zoom) const
|
||||
void IMGMap::updateTransform()
|
||||
{
|
||||
_transform = transform(_zoom);
|
||||
|
||||
RectD prect(_data->bounds(), _projection);
|
||||
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
|
||||
_transform.proj2img(prect.bottomRight()));
|
||||
}
|
||||
|
||||
QPointF IMGMap::ll2xy(const Coordinates &c)
|
||||
@ -590,15 +588,21 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
tiles.append(RasterTile(this, ttl, key));
|
||||
RasterTile &tile = tiles.last();
|
||||
|
||||
RectD polyRect(_transform.img2proj(ttl), _transform.img2proj(
|
||||
QPointF(ttl.x() + TILE_SIZE, ttl.y() + TILE_SIZE)));
|
||||
_data->polys(polyRect.toRectC(_projection, 4), _zoom,
|
||||
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
|
||||
ttl.y() + TILE_SIZE));
|
||||
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()));
|
||||
|
||||
RectD pointRect(_transform.img2proj(QPointF(ttl.x() - TEXT_EXTENT,
|
||||
ttl.y() - TEXT_EXTENT)), _transform.img2proj(QPointF(ttl.x()
|
||||
+ TILE_SIZE + TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT)));
|
||||
_data->points(pointRect.toRectC(_projection, 4), _zoom,
|
||||
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
|
||||
ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
|
||||
+ TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
|
||||
QString name() const {return _data->name();}
|
||||
|
||||
QRectF bounds();
|
||||
QRectF bounds() {return _bounds;}
|
||||
|
||||
virtual int zoom() const {return _zoom;}
|
||||
virtual void setZoom(int zoom);
|
||||
@ -62,6 +62,7 @@ private:
|
||||
int _zoom;
|
||||
Projection _projection;
|
||||
Transform _transform;
|
||||
QRectF _bounds;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
|
@ -50,12 +50,15 @@ public:
|
||||
virtual void setProjection(const Projection &) {}
|
||||
|
||||
virtual bool isValid() const {return true;}
|
||||
virtual bool isReady() const {return true;}
|
||||
virtual QString errorString() const {return QString();}
|
||||
|
||||
signals:
|
||||
void loaded();
|
||||
void tilesLoaded();
|
||||
void mapLoaded();
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Map*)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Map::Flags)
|
||||
|
||||
#endif // MAP_H
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QApplication>
|
||||
#include "atlas.h"
|
||||
#include "ozimap.h"
|
||||
#include "jnxmap.h"
|
||||
@ -12,35 +13,8 @@
|
||||
#include "maplist.h"
|
||||
|
||||
|
||||
bool MapList::loadMap(Map *map, const QString &path)
|
||||
{
|
||||
if (map && map->isValid()) {
|
||||
_maps.append(map);
|
||||
return true;
|
||||
} else if (map) {
|
||||
_errorPath = path;
|
||||
_errorString = map->errorString();
|
||||
return false;
|
||||
} else {
|
||||
_errorString = path;
|
||||
_errorString = "Unknown file format";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Map *MapList::loadSource(const QString &path)
|
||||
{
|
||||
Map *map = MapSource::loadMap(path, _errorString);
|
||||
|
||||
if (!map)
|
||||
_errorPath = path;
|
||||
else
|
||||
map->setParent(this);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
bool MapList::loadFile(const QString &path, bool *terminate)
|
||||
Map *MapList::loadFile(const QString &path, QString &errorString,
|
||||
bool *terminate)
|
||||
{
|
||||
QFileInfo fi(path);
|
||||
QString suffix = fi.suffix().toLower();
|
||||
@ -49,75 +23,95 @@ bool MapList::loadFile(const QString &path, bool *terminate)
|
||||
if (Atlas::isAtlas(path)) {
|
||||
if (terminate)
|
||||
*terminate = true;
|
||||
map = new Atlas(path, this);
|
||||
map = new Atlas(path);
|
||||
} else if (suffix == "xml") {
|
||||
if (MapSource::isMap(path) && !(map = loadSource(path)))
|
||||
return false;
|
||||
else if (GMAP::isGMAP(path)) {
|
||||
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);
|
||||
}
|
||||
} else if (suffix == "jnx")
|
||||
map = new JNXMap(path, this);
|
||||
map = new JNXMap(path);
|
||||
else if (suffix == "tif" || suffix == "tiff")
|
||||
map = new GeoTIFFMap(path, this);
|
||||
map = new GeoTIFFMap(path);
|
||||
else if (suffix == "mbtiles")
|
||||
map = new MBTilesMap(path, this);
|
||||
map = new MBTilesMap(path);
|
||||
else if (suffix == "rmap" || suffix == "rtmap")
|
||||
map = new RMap(path, this);
|
||||
map = new RMap(path);
|
||||
else if (suffix == "img")
|
||||
map = new IMGMap(path, this);
|
||||
map = new IMGMap(path);
|
||||
else if (suffix == "map" || suffix == "tar")
|
||||
map = new OziMap(path, this);
|
||||
map = new OziMap(path);
|
||||
|
||||
if (!loadMap(map, path)) {
|
||||
if (map && map->isValid())
|
||||
return map;
|
||||
else {
|
||||
errorString = (map) ? map->errorString() : "Unknown file format";
|
||||
delete map;
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapList::loadDir(const QString &path)
|
||||
QList<Map*> MapList::loadDir(const QString &path, QString &errorString)
|
||||
{
|
||||
QDir md(path);
|
||||
md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
md.setSorting(QDir::DirsLast);
|
||||
QFileInfoList ml = md.entryInfoList();
|
||||
bool ret = true;
|
||||
QList<Map*> list;
|
||||
|
||||
for (int i = 0; i < ml.size(); i++) {
|
||||
const QFileInfo &fi = ml.at(i);
|
||||
QString suffix = fi.suffix().toLower();
|
||||
bool terminate = false;
|
||||
|
||||
if (fi.isDir() && fi.fileName() != "set") {
|
||||
if (!loadDir(fi.absoluteFilePath()))
|
||||
ret = false;
|
||||
} else if (filter().contains("*." + suffix)) {
|
||||
if (!loadFile(fi.absoluteFilePath(), &terminate))
|
||||
ret = false;
|
||||
if (fi.isDir() && fi.fileName() != "set")
|
||||
list.append(loadDir(fi.absoluteFilePath(), errorString));
|
||||
else if (filter().contains("*." + suffix)) {
|
||||
Map *map = loadFile(fi.absoluteFilePath(), errorString, &terminate);
|
||||
if (map)
|
||||
list.append(map);
|
||||
else
|
||||
qWarning("%s: %s", qPrintable(fi.absoluteFilePath()),
|
||||
qPrintable(errorString));
|
||||
if (terminate)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Map*> MapList::loadMaps(const QString &path, QString &errorString)
|
||||
{
|
||||
if (QFileInfo(path).isDir())
|
||||
return loadDir(path, errorString);
|
||||
else {
|
||||
QList<Map*> list;
|
||||
Map *map = loadFile(path, errorString, 0);
|
||||
if (map)
|
||||
list.append(map);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
QString MapList::formats()
|
||||
{
|
||||
return
|
||||
tr("Supported files") + " (" + filter().join(" ") + ");;"
|
||||
+ tr("Garmin IMG maps") + " (*.gmap *.gmapi *.img *.xml);;"
|
||||
+ tr("Garmin JNX maps") + " (*.jnx);;"
|
||||
+ tr("OziExplorer maps") + " (*.map);;"
|
||||
+ tr("MBTiles maps") + " (*.mbtiles);;"
|
||||
+ tr("TrekBuddy maps/atlases") + " (*.tar *.tba);;"
|
||||
+ tr("GeoTIFF images") + " (*.tif *.tiff);;"
|
||||
+ tr("TwoNav maps") + " (*.rmap *.rtmap);;"
|
||||
+ tr("Online map sources") + " (*.xml)";
|
||||
qApp->translate("MapList", "Supported files")
|
||||
+ " (" + filter().join(" ") + ");;"
|
||||
+ qApp->translate("MapList", "Garmin IMG maps")
|
||||
+ " (*.gmap *.gmapi *.img *.xml);;"
|
||||
+ qApp->translate("MapList", "Garmin JNX maps") + " (*.jnx);;"
|
||||
+ qApp->translate("MapList", "OziExplorer maps") + " (*.map);;"
|
||||
+ qApp->translate("MapList", "MBTiles maps") + " (*.mbtiles);;"
|
||||
+ qApp->translate("MapList", "TrekBuddy maps/atlases") + " (*.tar *.tba);;"
|
||||
+ qApp->translate("MapList", "GeoTIFF images") + " (*.tif *.tiff);;"
|
||||
+ qApp->translate("MapList", "TwoNav maps") + " (*.rmap *.rtmap);;"
|
||||
+ qApp->translate("MapList", "Online map sources") + " (*.xml)";
|
||||
}
|
||||
|
||||
QStringList MapList::filter()
|
||||
|
@ -1,36 +1,21 @@
|
||||
#ifndef MAPLIST_H
|
||||
#define MAPLIST_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class Map;
|
||||
|
||||
class MapList : public QObject
|
||||
class MapList
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MapList(QObject *parent = 0) : QObject(parent) {}
|
||||
|
||||
bool loadFile(const QString &path, bool *terminate = 0);
|
||||
bool loadDir(const QString &path);
|
||||
|
||||
const QList<Map*> &maps() const {return _maps;}
|
||||
|
||||
const QString &errorString() const {return _errorString;}
|
||||
const QString &errorPath() const {return _errorPath;}
|
||||
|
||||
static QList<Map*> loadMaps(const QString &path, QString &errorString);
|
||||
static QString formats();
|
||||
static QStringList filter();
|
||||
|
||||
private:
|
||||
Map *loadSource(const QString &path);
|
||||
bool loadMap(Map *map, const QString &path);
|
||||
|
||||
QList<Map*> _maps;
|
||||
QString _errorString;
|
||||
QString _errorPath;
|
||||
static Map *loadFile(const QString &path, QString &errorString,
|
||||
bool *terminate);
|
||||
static QList<Map*> loadDir(const QString &path, QString &errorString);
|
||||
};
|
||||
|
||||
#endif // MAPLIST_H
|
||||
|
@ -300,7 +300,7 @@ Map *MapSource::loadMap(const QString &path, QString &errorString)
|
||||
case WMS:
|
||||
return new WMSMap(config.name, WMS::Setup(config.url, config.layer,
|
||||
config.style, config.format, config.crs, config.coordinateSystem,
|
||||
config.dimensions, config.authorization));
|
||||
config.dimensions, config.authorization), config.tileSize);
|
||||
case TMS:
|
||||
return new OnlineMap(config.name, config.url, config.zooms,
|
||||
config.bounds, config.tileRatio, config.authorization,
|
||||
|
@ -21,7 +21,7 @@ OnlineMap::OnlineMap(const QString &name, const QString &url,
|
||||
_tileLoader->setUrl(url);
|
||||
_tileLoader->setAuthorization(authorization);
|
||||
_tileLoader->setQuadTiles(quadTiles);
|
||||
connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(loaded()));
|
||||
connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(tilesLoaded()));
|
||||
}
|
||||
|
||||
QRectF OnlineMap::bounds()
|
||||
|
@ -169,6 +169,17 @@ void TileLoader::clearCache()
|
||||
dir.remove(list.at(i));
|
||||
|
||||
_downloader->clearErrors();
|
||||
|
||||
QPixmapCache::clear();
|
||||
}
|
||||
|
||||
void TileLoader::setScaledSize(int size)
|
||||
{
|
||||
if (_scaledSize == size)
|
||||
return;
|
||||
|
||||
_scaledSize = size;
|
||||
QPixmapCache::clear();
|
||||
}
|
||||
|
||||
QUrl TileLoader::tileUrl(const Tile &tile) const
|
||||
|
@ -16,7 +16,7 @@ public:
|
||||
void setUrl(const QString &url) {_url = url;}
|
||||
void setAuthorization(const Authorization &authorization)
|
||||
{_authorization = authorization;}
|
||||
void setScaledSize(int size) {_scaledSize = size;}
|
||||
void setScaledSize(int size);
|
||||
void setQuadTiles(bool quadTiles) {_quadTiles = quadTiles;}
|
||||
|
||||
void loadTilesAsync(QVector<Tile> &list);
|
||||
|
143
src/map/wms.cpp
143
src/map/wms.cpp
@ -1,3 +1,4 @@
|
||||
#include <cmath>
|
||||
#include <QFileInfo>
|
||||
#include <QEventLoop>
|
||||
#include <QXmlStreamReader>
|
||||
@ -7,6 +8,19 @@
|
||||
#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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "Format") {
|
||||
if (reader.readElementText() == ctx.setup.format())
|
||||
QString format(reader.readElementText());
|
||||
if (bareFormat(format) == bareFormat(ctx.setup.format()))
|
||||
ctx.formatSupported = true;
|
||||
} else
|
||||
} else if (reader.name() == "DCPType")
|
||||
dcpType(reader, ctx);
|
||||
else
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
@ -97,7 +146,16 @@ void WMS::layer(QXmlStreamReader &reader, CTX &ctx,
|
||||
CRSs.append(reader.readElementText());
|
||||
else if (reader.name() == "Style")
|
||||
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();
|
||||
if (sd > 0)
|
||||
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);
|
||||
CTX ctx(setup);
|
||||
QFile file(_path);
|
||||
CTX ctx(_setup);
|
||||
QXmlStreamReader reader;
|
||||
|
||||
|
||||
@ -186,7 +244,7 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup)
|
||||
reader.raiseError("Not a WMS Capabilities XML file");
|
||||
}
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
@ -232,10 +290,10 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup)
|
||||
return false;
|
||||
}
|
||||
|
||||
_boundingBox = ctx.layers.first().boundingBox;
|
||||
_bbox = ctx.layers.first().boundingBox;
|
||||
for (int i = 1; i < ctx.layers.size(); i++)
|
||||
_boundingBox &= ctx.layers.at(i).boundingBox;
|
||||
if (_boundingBox.isNull()) {
|
||||
_bbox &= ctx.layers.at(i).boundingBox;
|
||||
if (_bbox.isNull()) {
|
||||
_errorString = "Empty layers bounding box join";
|
||||
return false;
|
||||
}
|
||||
@ -248,40 +306,57 @@ bool WMS::parseCapabilities(const QString &path, const Setup &setup)
|
||||
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;
|
||||
}
|
||||
|
||||
bool WMS::getCapabilities(const QString &url, const QString &file,
|
||||
const Authorization &authorization)
|
||||
bool WMS::downloadCapabilities(const QString &url)
|
||||
{
|
||||
Downloader d;
|
||||
QList<Download> dl;
|
||||
|
||||
dl.append(Download(url, file));
|
||||
|
||||
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;
|
||||
if (!_downloader) {
|
||||
_downloader = new Downloader(this);
|
||||
connect(_downloader, SIGNAL(finished()), this,
|
||||
SLOT(capabilitiesReady()));
|
||||
}
|
||||
|
||||
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('?') ? "&" : "?");
|
||||
|
||||
if (!QFileInfo(file).exists())
|
||||
if (!getCapabilities(capaUrl, file, setup.authorization()))
|
||||
return;
|
||||
if (!parseCapabilities(file, setup))
|
||||
return;
|
||||
|
||||
_valid = true;
|
||||
_valid = downloadCapabilities(url);
|
||||
else {
|
||||
_ready = true;
|
||||
_valid = parseCapabilities();
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,10 @@
|
||||
|
||||
class QXmlStreamReader;
|
||||
|
||||
class WMS
|
||||
class WMS : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
class Setup
|
||||
{
|
||||
@ -48,16 +50,26 @@ public:
|
||||
};
|
||||
|
||||
|
||||
WMS(const QString &path, const Setup &setup);
|
||||
WMS(const QString &path, const Setup &setup, QObject *parent = 0);
|
||||
|
||||
const RectC &bbox() const {return _bbox;}
|
||||
const Projection &projection() const {return _projection;}
|
||||
CoordinateSystem cs() const {return _cs;}
|
||||
const RangeF &scaleDenominator() const {return _scaleDenominator;}
|
||||
const RectC &boundingBox() const {return _boundingBox;}
|
||||
const QString &version() const {return _version;}
|
||||
const QString &getMapUrl() const {return _getMapUrl;}
|
||||
const WMS::Setup &setup() const {return _setup;}
|
||||
|
||||
bool isReady() const {return _valid && _ready;}
|
||||
bool isValid() const {return _valid;}
|
||||
const QString &errorString() const {return _errorString;}
|
||||
|
||||
signals:
|
||||
void downloadFinished();
|
||||
|
||||
private slots:
|
||||
void capabilitiesReady();
|
||||
|
||||
private:
|
||||
struct Layer {
|
||||
QString name;
|
||||
@ -79,12 +91,16 @@ private:
|
||||
const Setup &setup;
|
||||
QList<Layer> layers;
|
||||
bool formatSupported;
|
||||
QString url;
|
||||
|
||||
CTX(const Setup &setup);
|
||||
};
|
||||
|
||||
RectC geographicBoundingBox(QXmlStreamReader &reader);
|
||||
QString style(QXmlStreamReader &reader);
|
||||
void get(QXmlStreamReader &reader, CTX &ctx);
|
||||
void http(QXmlStreamReader &reader, CTX &ctx);
|
||||
void dcpType(QXmlStreamReader &reader, CTX &ctx);
|
||||
void getMap(QXmlStreamReader &reader, CTX &ctx);
|
||||
void request(QXmlStreamReader &reader, CTX &ctx);
|
||||
void layer(QXmlStreamReader &reader, CTX &ctx, const QList<QString> &pCRSs,
|
||||
@ -92,19 +108,21 @@ private:
|
||||
RectC &pBoundingBox);
|
||||
void capability(QXmlStreamReader &reader, CTX &ctx);
|
||||
void capabilities(QXmlStreamReader &reader, CTX &ctx);
|
||||
bool parseCapabilities(const QString &path, const Setup &setup);
|
||||
bool getCapabilities(const QString &url, const QString &file,
|
||||
const Authorization &authorization);
|
||||
bool parseCapabilities();
|
||||
bool downloadCapabilities(const QString &url);
|
||||
|
||||
WMS::Setup _setup;
|
||||
QString _path;
|
||||
Downloader *_downloader;
|
||||
Projection _projection;
|
||||
RangeF _scaleDenominator;
|
||||
RectC _boundingBox;
|
||||
RectC _bbox;
|
||||
QString _version;
|
||||
QString _getMapUrl;
|
||||
CoordinateSystem _cs;
|
||||
|
||||
bool _valid;
|
||||
bool _valid, _ready;
|
||||
QString _errorString;
|
||||
|
||||
static Downloader *_downloader;
|
||||
};
|
||||
|
||||
#endif // WMS_H
|
||||
|
@ -10,114 +10,98 @@
|
||||
|
||||
|
||||
#define CAPABILITIES_FILE "capabilities.xml"
|
||||
#define TILE_SIZE 256
|
||||
#define EPSILON 1e-6
|
||||
|
||||
double WMSMap::sd2res(double scaleDenominator) const
|
||||
{
|
||||
return scaleDenominator * 0.28e-3 * _projection.units().fromMeters(1.0);
|
||||
return scaleDenominator * _wms->projection().units().fromMeters(1.0)
|
||||
* 0.28e-3;
|
||||
}
|
||||
|
||||
QString WMSMap::tileUrl(const QString &version) const
|
||||
QString WMSMap::tileUrl() const
|
||||
{
|
||||
QString url;
|
||||
const WMS::Setup &setup = _wms->setup();
|
||||
|
||||
url = QString("%1%2version=%3&request=GetMap&bbox=$bbox"
|
||||
QString url = QString("%1%2service=WMS&version=%3&request=GetMap&bbox=$bbox"
|
||||
"&width=%4&height=%5&layers=%6&styles=%7&format=%8&transparent=true")
|
||||
.arg(_setup.url(), _setup.url().contains('?') ? "&" : "?", version,
|
||||
QString::number(TILE_SIZE), QString::number(TILE_SIZE), _setup.layer(),
|
||||
_setup.style(), _setup.format());
|
||||
.arg(_wms->getMapUrl(), _wms->getMapUrl().contains('?') ? "&" : "?",
|
||||
_wms->version(), QString::number(_tileSize), QString::number(_tileSize),
|
||||
setup.layer(), setup.style(), setup.format());
|
||||
|
||||
if (version >= "1.3.0")
|
||||
url.append(QString("&CRS=%1").arg(_setup.crs()));
|
||||
if (_wms->version() >= "1.3.0")
|
||||
url.append(QString("&CRS=%1").arg(setup.crs()));
|
||||
else
|
||||
url.append(QString("&SRS=%1").arg(_setup.crs()));
|
||||
url.append(QString("&SRS=%1").arg(setup.crs()));
|
||||
|
||||
for (int i = 0; i < _setup.dimensions().size(); i++) {
|
||||
const KV<QString, QString> &dim = _setup.dimensions().at(i);
|
||||
for (int i = 0; i < setup.dimensions().size(); i++) {
|
||||
const KV<QString, QString> &dim = setup.dimensions().at(i);
|
||||
url.append(QString("&%1=%2").arg(dim.key(), dim.value()));
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
QString WMSMap::tilesDir() const
|
||||
{
|
||||
return QString(QDir(ProgramPaths::tilesDir()).filePath(_name));
|
||||
}
|
||||
|
||||
void WMSMap::computeZooms(const RangeF &scaleDenominator)
|
||||
void WMSMap::computeZooms()
|
||||
{
|
||||
_zooms.clear();
|
||||
|
||||
if (scaleDenominator.size() > 0) {
|
||||
double ld = log2(scaleDenominator.max() - EPSILON)
|
||||
- log2(scaleDenominator.min() + EPSILON);
|
||||
const RangeF &sd = _wms->scaleDenominator();
|
||||
if (sd.size() > 0) {
|
||||
double ld = log2(sd.max() - EPSILON) - log2(sd.min() + EPSILON);
|
||||
int cld = (int)ceil(ld);
|
||||
double step = ld / (double)cld;
|
||||
double lmax = log2(scaleDenominator.max() - EPSILON);
|
||||
double lmax = log2(sd.max() - EPSILON);
|
||||
for (int i = 0; i <= cld; i++)
|
||||
_zooms.append(pow(2.0, lmax - i * step));
|
||||
} else
|
||||
_zooms.append(scaleDenominator.min() + EPSILON);
|
||||
_zooms.append(sd.min() + EPSILON);
|
||||
}
|
||||
|
||||
void WMSMap::updateTransform()
|
||||
{
|
||||
double pixelSpan = sd2res(_zooms.at(_zoom));
|
||||
if (_projection.isGeographic())
|
||||
if (_wms->projection().isGeographic())
|
||||
pixelSpan /= deg2rad(WGS84_RADIUS);
|
||||
_transform = Transform(ReferencePoint(PointD(0, 0),
|
||||
_projection.ll2xy(_bbox.topLeft())), PointD(pixelSpan, pixelSpan));
|
||||
_wms->projection().ll2xy(_wms->bbox().topLeft())),
|
||||
PointD(pixelSpan, pixelSpan));
|
||||
}
|
||||
|
||||
bool WMSMap::loadWMS()
|
||||
WMSMap::WMSMap(const QString &name, const WMS::Setup &setup, int tileSize,
|
||||
QObject *parent) : Map(parent), _name(name), _tileLoader(0), _zoom(0),
|
||||
_tileSize(tileSize), _mapRatio(1.0)
|
||||
{
|
||||
QString file = tilesDir() + "/" + CAPABILITIES_FILE;
|
||||
QString tilesDir(QDir(ProgramPaths::tilesDir()).filePath(_name));
|
||||
|
||||
WMS wms(file, _setup);
|
||||
if (!wms.isValid()) {
|
||||
_errorString = wms.errorString();
|
||||
return false;
|
||||
}
|
||||
_tileLoader = new TileLoader(tilesDir, this);
|
||||
_tileLoader->setAuthorization(setup.authorization());
|
||||
connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(tilesLoaded()));
|
||||
|
||||
_projection = wms.projection();
|
||||
_bbox = wms.boundingBox();
|
||||
_bounds = RectD(_bbox, _projection);
|
||||
_tileLoader->setUrl(tileUrl(wms.version()));
|
||||
_wms = new WMS(QDir(tilesDir).filePath(CAPABILITIES_FILE), setup, this);
|
||||
connect(_wms, SIGNAL(downloadFinished()), this, SLOT(wmsReady()));
|
||||
if (_wms->isReady())
|
||||
init();
|
||||
}
|
||||
|
||||
if (wms.version() >= "1.3.0") {
|
||||
if (_setup.coordinateSystem().axisOrder() == CoordinateSystem::Unknown)
|
||||
_cs = _projection.coordinateSystem();
|
||||
else
|
||||
_cs = _setup.coordinateSystem();
|
||||
} else
|
||||
_cs = CoordinateSystem::XY;
|
||||
|
||||
computeZooms(wms.scaleDenominator());
|
||||
void WMSMap::init()
|
||||
{
|
||||
_tileLoader->setUrl(tileUrl());
|
||||
_bounds = RectD(_wms->bbox(), _wms->projection());
|
||||
computeZooms();
|
||||
updateTransform();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WMSMap::WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent)
|
||||
: Map(parent), _name(name), _setup(setup), _tileLoader(0), _zoom(0),
|
||||
_mapRatio(1.0), _valid(false)
|
||||
void WMSMap::wmsReady()
|
||||
{
|
||||
_tileLoader = new TileLoader(tilesDir(), this);
|
||||
_tileLoader->setAuthorization(_setup.authorization());
|
||||
connect(_tileLoader, SIGNAL(finished()), this, SIGNAL(loaded()));
|
||||
if (_wms->isValid())
|
||||
init();
|
||||
|
||||
_valid = loadWMS();
|
||||
emit mapLoaded();
|
||||
}
|
||||
|
||||
void WMSMap::clearCache()
|
||||
{
|
||||
_tileLoader->clearCache();
|
||||
_zoom = 0;
|
||||
|
||||
if (!loadWMS())
|
||||
qWarning("%s: %s", qPrintable(_name), qPrintable(_errorString));
|
||||
}
|
||||
|
||||
QRectF WMSMap::bounds()
|
||||
@ -129,10 +113,10 @@ QRectF WMSMap::bounds()
|
||||
int WMSMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
{
|
||||
if (rect.isValid()) {
|
||||
RectD prect(rect, _projection);
|
||||
RectD prect(rect, _wms->projection());
|
||||
PointD sc(prect.width() / size.width(), prect.height() / size.height());
|
||||
double resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
|
||||
if (_projection.isGeographic())
|
||||
if (_wms->projection().isGeographic())
|
||||
resolution *= deg2rad(WGS84_RADIUS);
|
||||
|
||||
_zoom = 0;
|
||||
@ -170,17 +154,17 @@ int WMSMap::zoomOut()
|
||||
|
||||
QPointF WMSMap::ll2xy(const Coordinates &c)
|
||||
{
|
||||
return _transform.proj2img(_projection.ll2xy(c)) / _mapRatio;
|
||||
return _transform.proj2img(_wms->projection().ll2xy(c)) / _mapRatio;
|
||||
}
|
||||
|
||||
Coordinates WMSMap::xy2ll(const QPointF &p)
|
||||
{
|
||||
return _projection.xy2ll(_transform.img2proj(p * _mapRatio));
|
||||
return _wms->projection().xy2ll(_transform.img2proj(p * _mapRatio));
|
||||
}
|
||||
|
||||
qreal WMSMap::tileSize() const
|
||||
{
|
||||
return (TILE_SIZE / _mapRatio);
|
||||
return (_tileSize / _mapRatio);
|
||||
}
|
||||
|
||||
void WMSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
@ -194,11 +178,11 @@ void WMSMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
||||
tiles.reserve((br.x() - tl.x()) * (br.y() - tl.y()));
|
||||
for (int i = tl.x(); i < br.x(); i++) {
|
||||
for (int j = tl.y(); j < br.y(); j++) {
|
||||
PointD ttl(_transform.img2proj(QPointF(i * TILE_SIZE,
|
||||
j * TILE_SIZE)));
|
||||
PointD tbr(_transform.img2proj(QPointF(i * TILE_SIZE + TILE_SIZE,
|
||||
j * TILE_SIZE + TILE_SIZE)));
|
||||
RectD bbox = (_cs.axisOrder() == CoordinateSystem::YX)
|
||||
PointD ttl(_transform.img2proj(QPointF(i * _tileSize,
|
||||
j * _tileSize)));
|
||||
PointD tbr(_transform.img2proj(QPointF(i * _tileSize + _tileSize,
|
||||
j * _tileSize + _tileSize)));
|
||||
RectD bbox = (_wms->cs().axisOrder() == CoordinateSystem::YX)
|
||||
? RectD(PointD(tbr.y(), tbr.x()), PointD(ttl.y(), ttl.x()))
|
||||
: RectD(ttl, tbr);
|
||||
|
||||
|
@ -14,7 +14,8 @@ class WMSMap : public Map
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WMSMap(const QString &name, const WMS::Setup &setup, QObject *parent = 0);
|
||||
WMSMap(const QString &name, const WMS::Setup &setup, int tileSize,
|
||||
QObject *parent = 0);
|
||||
|
||||
QString name() const {return _name;}
|
||||
|
||||
@ -35,33 +36,30 @@ public:
|
||||
{_mapRatio = mapRatio;}
|
||||
void clearCache();
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
QString errorString() const {return _errorString;}
|
||||
bool isReady() const {return _wms->isReady();}
|
||||
bool isValid() const {return _wms->isValid();}
|
||||
QString errorString() const {return _wms->errorString();}
|
||||
|
||||
private slots:
|
||||
void wmsReady();
|
||||
|
||||
private:
|
||||
QString tileUrl(const QString &version) const;
|
||||
QString tileUrl() const;
|
||||
double sd2res(double scaleDenominator) const;
|
||||
QString tilesDir() const;
|
||||
void computeZooms(const RangeF &scaleDenominator);
|
||||
void computeZooms();
|
||||
void updateTransform();
|
||||
bool loadWMS();
|
||||
qreal tileSize() const;
|
||||
void init();
|
||||
|
||||
QString _name;
|
||||
|
||||
WMS::Setup _setup;
|
||||
WMS *_wms;
|
||||
TileLoader *_tileLoader;
|
||||
Projection _projection;
|
||||
Transform _transform;
|
||||
CoordinateSystem _cs;
|
||||
QVector<double> _zooms;
|
||||
RectC _bbox;
|
||||
RectD _bounds;
|
||||
Transform _transform;
|
||||
QVector<double> _zooms;
|
||||
int _zoom;
|
||||
int _tileSize;
|
||||
qreal _mapRatio;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
};
|
||||
|
||||
#endif // WMSMAP_H
|
||||
|
134
src/map/wmts.cpp
134
src/map/wmts.cpp
@ -12,6 +12,11 @@
|
||||
#include "wmts.h"
|
||||
|
||||
|
||||
static QString bareFormat(const QString &format)
|
||||
{
|
||||
return format.left(format.indexOf(';')).trimmed();
|
||||
}
|
||||
|
||||
static void skipParentElement(QXmlStreamReader &reader)
|
||||
{
|
||||
while (reader.readNextStartElement())
|
||||
@ -53,7 +58,7 @@ void WMTS::tileMatrixSet(QXmlStreamReader &reader, CTX &ctx)
|
||||
{
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "Identifier") {
|
||||
if (reader.readElementText() != ctx.setup.set()) {
|
||||
if (reader.readElementText() != _setup.set()) {
|
||||
skipParentElement(reader);
|
||||
return;
|
||||
}
|
||||
@ -109,7 +114,7 @@ void WMTS::tileMatrixSetLink(QXmlStreamReader &reader, CTX &ctx)
|
||||
{
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "TileMatrixSet") {
|
||||
if (reader.readElementText() == ctx.setup.set())
|
||||
if (reader.readElementText() == _setup.set())
|
||||
ctx.hasSet = true;
|
||||
else {
|
||||
skipParentElement(reader);
|
||||
@ -158,7 +163,7 @@ void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
|
||||
{
|
||||
while (reader.readNextStartElement()) {
|
||||
if (reader.name() == "Identifier") {
|
||||
if (reader.readElementText() == ctx.setup.layer())
|
||||
if (reader.readElementText() == _setup.layer())
|
||||
ctx.hasLayer = true;
|
||||
else {
|
||||
skipParentElement(reader);
|
||||
@ -167,10 +172,10 @@ void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
|
||||
} else if (reader.name() == "TileMatrixSetLink")
|
||||
tileMatrixSetLink(reader, ctx);
|
||||
else if (reader.name() == "WGS84BoundingBox")
|
||||
_bounds = wgs84BoundingBox(reader);
|
||||
ctx.bbox = wgs84BoundingBox(reader);
|
||||
else if (reader.name() == "ResourceURL") {
|
||||
const QXmlStreamAttributes &attr = reader.attributes();
|
||||
if (attr.value("resourceType") == "tile" && ctx.setup.rest())
|
||||
if (attr.value("resourceType") == "tile" && _setup.rest())
|
||||
_tileUrl = attr.value("template").toString();
|
||||
reader.skipCurrentElement();
|
||||
} else if (reader.name() == "Style") {
|
||||
@ -179,10 +184,11 @@ void WMTS::layer(QXmlStreamReader &reader, CTX &ctx)
|
||||
QString s = style(reader);
|
||||
if (isDefault)
|
||||
ctx.defaultStyle = s;
|
||||
if (s == ctx.setup.style())
|
||||
if (s == _setup.style())
|
||||
ctx.hasStyle = true;
|
||||
} else if (reader.name() == "Format") {
|
||||
if (reader.readElementText() == ctx.setup.format())
|
||||
QString format(reader.readElementText());
|
||||
if (bareFormat(format) == bareFormat(_setup.format()))
|
||||
ctx.hasFormat = true;
|
||||
} else
|
||||
reader.skipCurrentElement();
|
||||
@ -226,9 +232,9 @@ void WMTS::createZooms(const CTX &ctx)
|
||||
qSort(_zooms);
|
||||
}
|
||||
|
||||
bool WMTS::parseCapabilities(const QString &path, CTX &ctx)
|
||||
bool WMTS::parseCapabilities(CTX &ctx)
|
||||
{
|
||||
QFile file(path);
|
||||
QFile file(_path);
|
||||
QXmlStreamReader reader;
|
||||
|
||||
if (!file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
@ -244,30 +250,30 @@ bool WMTS::parseCapabilities(const QString &path, CTX &ctx)
|
||||
reader.raiseError("Not a Capabilities XML file");
|
||||
}
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ctx.hasLayer) {
|
||||
_errorString = ctx.setup.layer() + ": layer not provided";
|
||||
_errorString = _setup.layer() + ": layer not provided";
|
||||
return false;
|
||||
}
|
||||
if (!ctx.hasStyle && !ctx.setup.style().isEmpty()) {
|
||||
_errorString = ctx.setup.style() + ": style not provided";
|
||||
if (!ctx.hasStyle && !_setup.style().isEmpty()) {
|
||||
_errorString = _setup.style() + ": style not provided";
|
||||
return false;
|
||||
}
|
||||
if (!ctx.hasStyle && ctx.setup.style().isEmpty()
|
||||
if (!ctx.hasStyle && _setup.style().isEmpty()
|
||||
&& ctx.defaultStyle.isEmpty()) {
|
||||
_errorString = "Default style not provided";
|
||||
return false;
|
||||
}
|
||||
if (!ctx.setup.rest() && !ctx.hasFormat) {
|
||||
_errorString = ctx.setup.format() + ": format not provided";
|
||||
if (!_setup.rest() && !ctx.hasFormat) {
|
||||
_errorString = _setup.format() + ": format not provided";
|
||||
return false;
|
||||
}
|
||||
if (!ctx.hasSet) {
|
||||
_errorString = ctx.setup.set() + ": set not provided";
|
||||
_errorString = _setup.set() + ": set not provided";
|
||||
return false;
|
||||
}
|
||||
if (ctx.crs.isNull()) {
|
||||
@ -284,74 +290,92 @@ bool WMTS::parseCapabilities(const QString &path, CTX &ctx)
|
||||
_errorString = "No usable tile matrix found";
|
||||
return false;
|
||||
}
|
||||
if (ctx.setup.rest() && _tileUrl.isNull()) {
|
||||
if (_setup.rest() && _tileUrl.isNull()) {
|
||||
_errorString = "Missing tile URL template";
|
||||
return false;
|
||||
}
|
||||
_bbox = ctx.bbox;
|
||||
_cs = (_setup.coordinateSystem().axisOrder() == CoordinateSystem::Unknown)
|
||||
? _projection.coordinateSystem() : _setup.coordinateSystem();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WMTS::downloadCapabilities(const QString &url, const QString &file,
|
||||
const Authorization &authorization)
|
||||
bool WMTS::downloadCapabilities(const QString &url)
|
||||
{
|
||||
Downloader d;
|
||||
QList<Download> dl;
|
||||
|
||||
dl.append(Download(url, file));
|
||||
|
||||
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;
|
||||
if (!_downloader) {
|
||||
_downloader = new Downloader(this);
|
||||
connect(_downloader, SIGNAL(finished()), this,
|
||||
SLOT(capabilitiesReady()));
|
||||
}
|
||||
|
||||
return true;
|
||||
QList<Download> dl;
|
||||
dl.append(Download(url, _path));
|
||||
|
||||
return _downloader->get(dl, _setup.authorization());
|
||||
}
|
||||
|
||||
WMTS::WMTS(const QString &file, const WMTS::Setup &setup) : _valid(false)
|
||||
void WMTS::capabilitiesReady()
|
||||
{
|
||||
QUrl url(setup.rest() ? setup.url() : QString(
|
||||
"%1%2service=WMTS&Version=1.0.0&request=GetCapabilities").arg(setup.url(),
|
||||
setup.url().contains('?') ? "&" : "?"));
|
||||
if (!QFileInfo(_path).exists()) {
|
||||
_errorString = "Error downloading capabilities XML file";
|
||||
_valid = false;
|
||||
} else {
|
||||
_ready = true;
|
||||
_valid = init();
|
||||
}
|
||||
|
||||
if (!url.isLocalFile() && !QFileInfo(file).exists())
|
||||
if (!downloadCapabilities(url.toString(), file, setup.authorization()))
|
||||
return;
|
||||
emit downloadFinished();
|
||||
}
|
||||
|
||||
CTX ctx(setup);
|
||||
if (!parseCapabilities(url.isLocalFile() ? url.toLocalFile() : file, ctx))
|
||||
return;
|
||||
bool WMTS::init()
|
||||
{
|
||||
CTX ctx;
|
||||
if (!parseCapabilities(ctx))
|
||||
return false;
|
||||
|
||||
QString style = setup.style().isEmpty() ? ctx.defaultStyle : setup.style();
|
||||
if (!setup.rest()) {
|
||||
QString style = _setup.style().isEmpty() ? ctx.defaultStyle : _setup.style();
|
||||
if (!_setup.rest()) {
|
||||
_tileUrl = QString("%1%2service=WMTS&Version=1.0.0&request=GetTile"
|
||||
"&Format=%3&Layer=%4&Style=%5&TileMatrixSet=%6&TileMatrix=$z"
|
||||
"&TileRow=$y&TileCol=$x").arg(setup.url(),
|
||||
setup.url().contains('?') ? "&" : "?" , setup.format(),
|
||||
setup.layer(), style, setup.set());
|
||||
for (int i = 0; i < setup.dimensions().size(); i++) {
|
||||
const KV<QString, QString> &dim = setup.dimensions().at(i);
|
||||
"&TileRow=$y&TileCol=$x").arg(_setup.url(),
|
||||
_setup.url().contains('?') ? "&" : "?" , _setup.format(),
|
||||
_setup.layer(), style, _setup.set());
|
||||
for (int i = 0; i < _setup.dimensions().size(); i++) {
|
||||
const KV<QString, QString> &dim = _setup.dimensions().at(i);
|
||||
_tileUrl.append(QString("&%1=%2").arg(dim.key(), dim.value()));
|
||||
}
|
||||
} else {
|
||||
_tileUrl.replace("{Style}", style, Qt::CaseInsensitive);
|
||||
_tileUrl.replace("{TileMatrixSet}", setup.set(), Qt::CaseInsensitive);
|
||||
_tileUrl.replace("{TileMatrixSet}", _setup.set(), Qt::CaseInsensitive);
|
||||
_tileUrl.replace("{TileMatrix}", "$z", Qt::CaseInsensitive);
|
||||
_tileUrl.replace("{TileRow}", "$y", Qt::CaseInsensitive);
|
||||
_tileUrl.replace("{TileCol}", "$x", Qt::CaseInsensitive);
|
||||
for (int i = 0; i < setup.dimensions().size(); i++) {
|
||||
const KV<QString, QString> &dim = setup.dimensions().at(i);
|
||||
for (int i = 0; i < _setup.dimensions().size(); i++) {
|
||||
const KV<QString, QString> &dim = _setup.dimensions().at(i);
|
||||
_tileUrl.replace(QString("{%1}").arg(dim.key()), dim.value(),
|
||||
Qt::CaseInsensitive);
|
||||
}
|
||||
}
|
||||
|
||||
_valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
WMTS::WMTS(const QString &file, const WMTS::Setup &setup, QObject *parent)
|
||||
: QObject(parent), _setup(setup), _downloader(0), _valid(false), _ready(false)
|
||||
{
|
||||
QUrl url(setup.rest() ? setup.url() : QString(
|
||||
"%1%2service=WMTS&Version=1.0.0&request=GetCapabilities").arg(setup.url(),
|
||||
setup.url().contains('?') ? "&" : "?"));
|
||||
|
||||
_path = url.isLocalFile() ? url.toLocalFile() : file;
|
||||
|
||||
if (!url.isLocalFile() && !QFileInfo(file).exists())
|
||||
_valid = downloadCapabilities(url.toString());
|
||||
else {
|
||||
_ready = true;
|
||||
_valid = init();
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user