1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-15 03:14:24 +02:00

Compare commits

..

68 Commits
13.0 ... 13.5

Author SHA1 Message Date
66036f6cd7 Yet another one-way arrows improvement 2023-07-24 19:04:19 +02:00
dfda3a6630 Use less agressive one-way street arrows 2023-07-14 10:33:45 +02:00
96e762beb5 Properly mark one-way streets in data from NET links 2023-07-14 09:52:27 +02:00
b4be5ea206 Display one-way streets info in IMG maps 2023-07-13 11:59:53 +02:00
56c77df176 Build universal x86_64/arm64 binaries on OS X 2023-07-06 10:56:56 +02:00
1871f85acc Yet another ENV path fix 2023-07-06 10:19:26 +02:00
3ec636632f Fixed ENV file path 2023-07-06 10:12:32 +02:00
5e1af275b8 Switched to MSVC 2022 and Qt 6.5 2023-07-06 10:07:13 +02:00
cd220216dd Do not affect the map object scaling when resizing the tiles 2023-07-04 20:27:41 +02:00
0d6b02f466 Removed obsolete include 2023-06-30 09:55:35 +02:00
059c515175 Added graph pinch zooming
Fixes #501
2023-06-29 07:22:11 +02:00
1dc963b133 Revert "Removed SDK/buildtools workaround"
This reverts commit f0036bfd28.
2023-06-23 10:08:30 +02:00
f0036bfd28 Removed SDK/buildtools workaround 2023-06-23 09:33:04 +02:00
688861bf65 Code cleanup 2023-06-23 09:30:44 +02:00
c449024584 Version++ 2023-06-14 00:42:16 +02:00
41188360bf Make the Mapsforge tiles sufficient large for the layout 2023-06-14 00:40:00 +02:00
1086a74f99 Back to Qt 6.4 on Windows 2023-06-12 01:03:49 +02:00
7b5a1c701d Switched Qt 6 CI builds to Qt 6.5 2023-06-11 08:30:43 +02:00
9afeaf672a Properly match symbols to captions 2023-06-10 08:11:18 +02:00
5ddd63e697 Do not limit text that was not inserted 2023-06-06 07:32:42 +02:00
88fa1ed786 Some more Mapsforge maps rendering improvements 2023-06-06 07:18:31 +02:00
1233d20a21 Added support for lineSymbols in Mapsforge maps 2023-06-04 23:56:00 +02:00
1746eddb8d Code cleanup 2023-06-03 13:35:29 +02:00
ecda5103c8 Properly handle Mapsforge style menus 2023-05-31 01:01:42 +02:00
50b0ff1c56 Cosmetics 2023-05-29 23:19:28 +02:00
2b300fab54 Code cleanup 2023-05-29 23:19:16 +02:00
961061b643 Added rescue station 2023-05-28 11:28:12 +02:00
8bebea53ad Added LNDELV elevation values 2023-05-28 10:40:39 +02:00
c3b484bb75 Properly include std::isnan() 2023-05-26 21:57:45 +02:00
d6d43baec5 Optimization 2023-05-26 21:30:27 +02:00
c6c3e0978c Use generic icon rotate instead of special icon draw functions 2023-05-26 21:28:44 +02:00
320b04c3fa Added support for line "dy" parameter 2023-05-22 23:29:04 +02:00
ab7185bd25 Version++ 2023-05-22 23:28:50 +02:00
822a0c2866 Tile search can be done lock-free 2023-05-21 09:14:19 +02:00
a92d6efec6 Do not do any time consuming actions in the mapview redraw callback
On all vector maps (ENC, IMG and Mapsforge), do the data loading
asynchronous like the tile rendering.
2023-05-19 19:33:22 +02:00
4615709b99 Merge branch 'origin/master' into Weblate. 2023-05-19 01:31:46 +02:00
8a72b20af8 Added support for all paths scaling modes 2023-05-19 01:30:54 +02:00
f86aa8c012 Merge branch 'origin/master' into Weblate. 2023-05-17 23:15:09 +02:00
e351eb6370 Only preallocate the memory when usin moveto/lineto 2023-05-17 23:10:45 +02:00
81e967f20d Only fetch data when the rect is valid 2023-05-17 23:08:17 +02:00
dbf5828e65 Merge branch 'origin/master' into Weblate. 2023-05-16 23:03:34 +02:00
cf81a90865 Some more micro-optimizations & code cleanup 2023-05-16 23:03:07 +02:00
37d408c953 Translated using Weblate (Danish)
Currently translated at 100.0% (467 of 467 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/da/
2023-05-16 21:51:52 +02:00
61c3ed60d7 Translated using Weblate (French)
Currently translated at 99.7% (466 of 467 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/fr/
2023-05-14 00:53:09 +02:00
04c203625f Version++ 2023-05-13 15:05:10 +02:00
d0cea97c90 Added support for custom HTTP headers in map tile requests 2023-05-13 15:01:35 +02:00
ddc7eb7149 Do not outline soundings like in ENC maps 2023-05-12 10:19:52 +02:00
bb22ad95b7 Use the point label as additional source for the id hash
Some (marine) maps have different points with the same type on the same
position.
2023-05-11 22:57:35 +02:00
682fcc09cc Merge branch 'origin/master' into Weblate. 2023-05-11 21:31:32 +02:00
60e83b24f9 Use as much as possible of the 64b hash in Qt6 2023-05-11 21:31:10 +02:00
c30501185c Merge branch 'origin/master' into Weblate. 2023-05-07 21:20:08 +02:00
9b3c11cc68 Version++ 2023-05-07 21:19:55 +02:00
61f77ef19e Merge branch 'origin/master' into Weblate. 2023-05-07 21:16:26 +02:00
c0834491d3 Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (467 of 467 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/nb_NO/
2023-05-07 21:16:25 +02:00
e6fdd0f53d Fixed crash on empty routes 2023-05-07 21:15:44 +02:00
fe444e88a3 Merge branch 'origin/master' into Weblate. 2023-05-06 21:53:53 +02:00
d9c0770b51 Code cleanup 2023-05-06 21:53:40 +02:00
3a0e9bb733 Merge branch 'origin/master' into Weblate. 2023-05-06 16:15:14 +02:00
ca6c7247c0 Added missing cache insert 2023-05-06 16:14:49 +02:00
0b25cb9f81 Merge branch 'origin/master' into Weblate. 2023-05-04 21:49:22 +02:00
190a961242 Fixed wrong date info on tracks with segments 2023-05-04 21:49:02 +02:00
ccfb748404 Fixed KMZ maps bounds computation 2023-05-04 21:14:52 +02:00
b19c3a83f3 Merge branch 'origin/master' into Weblate. 2023-05-04 09:52:05 +02:00
a4a54101a3 Removed forgotten debug stuff 2023-05-04 09:51:42 +02:00
728361ad7b Merge branch 'origin/master' into Weblate. 2023-05-04 09:41:07 +02:00
af58893cbe Version++ 2023-05-04 09:41:02 +02:00
97e12d809f Map API refactoring
Including several map providers refactoring/rewrite
especial KML, JNX and TrekBuddy atlases.
2023-05-04 09:38:35 +02:00
raf
f5e3e5bd21 Translated using Weblate (Catalan)
Currently translated at 100.0% (467 of 467 strings)

Translation: GPXSee/Translations
Translate-URL: https://hosted.weblate.org/projects/gpxsee/translations/ca/
2023-04-29 11:48:37 +02:00
121 changed files with 2076 additions and 1579 deletions

View File

@ -1,23 +1,23 @@
version: 13.0.{build} version: 13.5.{build}
configuration: configuration:
- Release - Release
image: image:
- Visual Studio 2019 - Visual Studio 2022
environment: environment:
NSISDIR: C:\Program Files (x86)\NSIS NSISDIR: C:\Program Files (x86)\NSIS
OPENSSLDIR: C:\OpenSSL-v111-Win64\bin OPENSSLDIR: C:\OpenSSL-v111-Win64\bin
matrix: matrix:
- QTDIR: C:\Qt\5.15\msvc2019_64 - QTDIR: C:\Qt\5.15\msvc2019_64
- QTDIR: C:\Qt\6.4\msvc2019_64 - QTDIR: C:\Qt\6.5\msvc2019_64
NSISDEF: /DQT6 NSISDEF: /DQT6
install: install:
- cmd: |- - cmd: |-
set PATH=%QTDIR%\bin;%NSISDIR%;%PATH% set PATH=%QTDIR%\bin;%NSISDIR%;%PATH%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat
build_script: build_script:
- cmd: |- - cmd: |-

View File

@ -39,12 +39,12 @@ jobs:
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
with: with:
version: '6.4.3' version: '6.5.1'
modules: qtpositioning qt5compat qtserialport modules: qtpositioning qt5compat qtserialport
- name: Create localization - name: Create localization
run: lrelease gpxsee.pro run: lrelease gpxsee.pro
- name: Configure build - name: Configure build
run: qmake gpxsee.pro run: qmake gpxsee.pro QMAKE_APPLE_DEVICE_ARCHS="x86_64h arm64"
- name: Build project - name: Build project
run: make -j3 run: make -j3
- name: Create DMG - name: Create DMG

View File

@ -3,7 +3,8 @@ unix:!macx:!android {
} else { } else {
TARGET = GPXSee TARGET = GPXSee
} }
VERSION = 13.0 VERSION = 13.5
QT += core \ QT += core \
gui \ gui \

View File

@ -55,6 +55,11 @@
<file alias="transform-move_32@2x.png">icons/GUI/transform-move_32@2x.png</file> <file alias="transform-move_32@2x.png">icons/GUI/transform-move_32@2x.png</file>
</qresource> </qresource>
<!-- Common map stuff -->
<qresource prefix="/map">
<file alias="arrow.png">icons/map/arrow.png</file>
</qresource>
<!-- POIs (IMG & ENC style) --> <!-- POIs (IMG & ENC style) -->
<qresource prefix="/POI"> <qresource prefix="/POI">
<file alias="airfield-11.png">icons/map/POI/airfield-11.png</file> <file alias="airfield-11.png">icons/map/POI/airfield-11.png</file>
@ -198,6 +203,9 @@
<file alias="building.png">icons/map/marine/building.png</file> <file alias="building.png">icons/map/marine/building.png</file>
<file alias="fog-signal.png">icons/map/marine/fog-signal.png</file> <file alias="fog-signal.png">icons/map/marine/fog-signal.png</file>
<file alias="construction.png">icons/map/marine/construction.png</file> <file alias="construction.png">icons/map/marine/construction.png</file>
<file alias="radio-call.png">icons/map/marine/radio-call.png</file>
<file alias="current.png">icons/map/marine/current.png</file>
<file alias="rescue-station.png">icons/map/marine/rescue-station.png</file>
</qresource> </qresource>
<!-- Mapsforge rendertheme --> <!-- Mapsforge rendertheme -->

BIN
icons/map/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

View File

@ -866,7 +866,7 @@
<location filename="../src/GUI/gui.cpp" line="907"/> <location filename="../src/GUI/gui.cpp" line="907"/>
<location filename="../src/GUI/gui.cpp" line="925"/> <location filename="../src/GUI/gui.cpp" line="925"/>
<source>CRS directory:</source> <source>CRS directory:</source>
<translation type="unfinished"></translation> <translation>Directori CRS:</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/gui.cpp" line="909"/> <location filename="../src/GUI/gui.cpp" line="909"/>
@ -1020,6 +1020,7 @@
<translation> <translation>
<numerusform>%n fitxer</numerusform> <numerusform>%n fitxer</numerusform>
<numerusform>%n fitxers</numerusform> <numerusform>%n fitxers</numerusform>
<numerusform>%n fitxers</numerusform>
</translation> </translation>
</message> </message>
</context> </context>

View File

@ -712,7 +712,7 @@
<location filename="../src/GUI/gui.cpp" line="907"/> <location filename="../src/GUI/gui.cpp" line="907"/>
<location filename="../src/GUI/gui.cpp" line="925"/> <location filename="../src/GUI/gui.cpp" line="925"/>
<source>CRS directory:</source> <source>CRS directory:</source>
<translation type="unfinished"></translation> <translation>CRS-katalog:</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/gui.cpp" line="913"/> <location filename="../src/GUI/gui.cpp" line="913"/>

View File

@ -958,7 +958,7 @@
<location filename="../src/GUI/gui.cpp" line="907"/> <location filename="../src/GUI/gui.cpp" line="907"/>
<location filename="../src/GUI/gui.cpp" line="925"/> <location filename="../src/GUI/gui.cpp" line="925"/>
<source>CRS directory:</source> <source>CRS directory:</source>
<translation type="unfinished"></translation> <translation>Dossier CRS:</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/gui.cpp" line="915"/> <location filename="../src/GUI/gui.cpp" line="915"/>
@ -1427,12 +1427,12 @@
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="70"/> <location filename="../src/GUI/optionsdialog.cpp" line="70"/>
<source>Select the proper coordinate reference system (CRS) of maps without a CRS definition (JNX, KMZ and World file maps).</source> <source>Select the proper coordinate reference system (CRS) of maps without a CRS definition (JNX, KMZ and World file maps).</source>
<translation type="unfinished"></translation> <translation type="unfinished">Sélectionnez le système de référence de coordonnée (CRS) approprié pour les cartes sans définition de CRS (JNX, KMZ et World file maps).</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="73"/> <location filename="../src/GUI/optionsdialog.cpp" line="73"/>
<source>Select the desired projection of vector maps (IMG, Mapsforge and ENC maps). The projection must be valid for the whole map area.</source> <source>Select the desired projection of vector maps (IMG, Mapsforge and ENC maps). The projection must be valid for the whole map area.</source>
<translation type="unfinished"></translation> <translation>Sélectionnez la projection désirée de la carte vectorielle (IMG, Mapsforge et carte ENC). La projection doit être valide pour la totalité de la zone de la carte.</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="77"/> <location filename="../src/GUI/optionsdialog.cpp" line="77"/>
@ -1912,7 +1912,7 @@
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="758"/> <location filename="../src/GUI/optionsdialog.cpp" line="758"/>
<source>DEM cache size:</source> <source>DEM cache size:</source>
<translation type="unfinished"></translation> <translation>Taille du cache DEM :</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="778"/> <location filename="../src/GUI/optionsdialog.cpp" line="778"/>

View File

@ -712,7 +712,7 @@
<location filename="../src/GUI/gui.cpp" line="907"/> <location filename="../src/GUI/gui.cpp" line="907"/>
<location filename="../src/GUI/gui.cpp" line="925"/> <location filename="../src/GUI/gui.cpp" line="925"/>
<source>CRS directory:</source> <source>CRS directory:</source>
<translation type="unfinished"></translation> <translation>CRS-mappe:</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/gui.cpp" line="913"/> <location filename="../src/GUI/gui.cpp" line="913"/>
@ -1718,12 +1718,12 @@
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="70"/> <location filename="../src/GUI/optionsdialog.cpp" line="70"/>
<source>Select the proper coordinate reference system (CRS) of maps without a CRS definition (JNX, KMZ and World file maps).</source> <source>Select the proper coordinate reference system (CRS) of maps without a CRS definition (JNX, KMZ and World file maps).</source>
<translation type="unfinished"></translation> <translation>Velg riktig koordinatreferansesystem (CRS) for kart uten en CRS-definisjon (JNX, KMZ og World-file kart).</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="73"/> <location filename="../src/GUI/optionsdialog.cpp" line="73"/>
<source>Select the desired projection of vector maps (IMG, Mapsforge and ENC maps). The projection must be valid for the whole map area.</source> <source>Select the desired projection of vector maps (IMG, Mapsforge and ENC maps). The projection must be valid for the whole map area.</source>
<translation type="unfinished"></translation> <translation>Velg ønsket projeksjon for vektorkart (IMG, Mapsforge og ENC-kart). Projeksjonen være gyldig for hele kartområdet.</translation>
</message> </message>
<message> <message>
<location filename="../src/GUI/optionsdialog.cpp" line="104"/> <location filename="../src/GUI/optionsdialog.cpp" line="104"/>

View File

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

View File

@ -25,7 +25,6 @@ ToolTip AreaItem::info() const
AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent) AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent)
: PlaneItem(parent), _area(area) : PlaneItem(parent), _area(area)
{ {
_map = map;
_digitalZoom = 0; _digitalZoom = 0;
_width = 2; _width = 2;
_opacity = 0.5; _opacity = 0.5;
@ -35,34 +34,34 @@ AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent)
_pen = QPen(strokeColor(), width()); _pen = QPen(strokeColor(), width());
_brush = QBrush(fillColor()); _brush = QBrush(fillColor());
updatePainterPath(); updatePainterPath(map);
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
setAcceptHoverEvents(true); setAcceptHoverEvents(true);
} }
QPainterPath AreaItem::painterPath(const Polygon &polygon) QPainterPath AreaItem::painterPath(Map *map, const Polygon &polygon)
{ {
QPainterPath path; QPainterPath path;
for (int i = 0; i < polygon.size(); i++) { for (int i = 0; i < polygon.size(); i++) {
const QVector<Coordinates> &subpath = polygon.at(i); const QVector<Coordinates> &subpath = polygon.at(i);
path.moveTo(_map->ll2xy(subpath.first())); path.moveTo(map->ll2xy(subpath.first()));
for (int j = 1; j < subpath.size(); j++) for (int j = 1; j < subpath.size(); j++)
path.lineTo(_map->ll2xy(subpath.at(j))); path.lineTo(map->ll2xy(subpath.at(j)));
path.closeSubpath(); path.closeSubpath();
} }
return path; return path;
} }
void AreaItem::updatePainterPath() void AreaItem::updatePainterPath(Map *map)
{ {
_painterPath = QPainterPath(); _painterPath = QPainterPath();
for (int i = 0; i < _area.polygons().size(); i++) for (int i = 0; i < _area.polygons().size(); i++)
_painterPath.addPath(painterPath(_area.polygons().at(i))); _painterPath.addPath(painterPath(map, _area.polygons().at(i)));
} }
void AreaItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, void AreaItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
@ -85,10 +84,7 @@ void AreaItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
void AreaItem::setMap(Map *map) void AreaItem::setMap(Map *map)
{ {
prepareGeometryChange(); prepareGeometryChange();
updatePainterPath(map);
_map = map;
updatePainterPath();
} }
const QColor &AreaItem::strokeColor() const const QColor &AreaItem::strokeColor() const

View File

@ -33,8 +33,8 @@ protected:
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
private: private:
QPainterPath painterPath(const Polygon &polygon); QPainterPath painterPath(Map *map, const Polygon &polygon);
void updatePainterPath(); void updatePainterPath(Map *map);
void updateColor(); void updateColor();
void updateWidth(); void updateWidth();
void updatePenStyle(); void updatePenStyle();
@ -45,8 +45,6 @@ private:
Area _area; Area _area;
Map *_map;
qreal _width; qreal _width;
QColor _color; QColor _color;
qreal _opacity; qreal _opacity;

View File

@ -224,6 +224,12 @@ qreal GraphItem::timeAtDistance(qreal distance) const
return l.pointAt((distance - l.p1().x()) / (l.p2().x() - l.p1().x())).y(); return l.pointAt((distance - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
} }
GraphItem::SegmentTime GraphItem::date(qreal x)
{
const GraphSegment *seg = segment(x, _type);
return seg ? SegmentTime(seg->start(), seg->first().t()) : SegmentTime();
}
void GraphItem::hover(bool hover) void GraphItem::hover(bool hover)
{ {
if (hover) { if (hover) {

View File

@ -12,6 +12,16 @@ class GraphItem : public QObject, public GraphicsItem
Q_OBJECT Q_OBJECT
public: public:
struct SegmentTime
{
SegmentTime() : time(NAN) {}
SegmentTime(const QDateTime &date, qreal time)
: date(date), time(time) {}
QDateTime date;
qreal time;
};
GraphItem(const Graph &graph, GraphType type, int width, GraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0); const QColor &color, Qt::PenStyle style, QGraphicsItem *parent = 0);
virtual ~GraphItem() {} virtual ~GraphItem() {}
@ -43,6 +53,7 @@ public:
qreal yAtX(qreal x) const; qreal yAtX(qreal x) const;
qreal distanceAtTime(qreal time) const; qreal distanceAtTime(qreal time) const;
qreal timeAtDistance(qreal distance) const; qreal timeAtDistance(qreal distance) const;
SegmentTime date(qreal x);
void redraw(); void redraw();

View File

@ -2,6 +2,7 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QEvent> #include <QEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QGestureEvent>
#include <QScrollBar> #include <QScrollBar>
#include <QGraphicsSimpleTextItem> #include <QGraphicsSimpleTextItem>
#include <QPalette> #include <QPalette>
@ -37,6 +38,8 @@ GraphView::GraphView(QWidget *parent)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setBackgroundBrush(QBrush(palette().brush(QPalette::Base))); setBackgroundBrush(QBrush(palette().brush(QPalette::Base)));
viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
grabGesture(Qt::PinchGesture);
_xAxis = new AxisItem(AxisItem::X); _xAxis = new AxisItem(AxisItem::X);
_xAxis->setZValue(1.0); _xAxis->setZValue(1.0);
@ -381,6 +384,27 @@ void GraphView::wheelEvent(QWheelEvent *e)
QGraphicsView::wheelEvent(e); QGraphicsView::wheelEvent(e);
} }
void GraphView::pinchGesture(QPinchGesture *gesture)
{
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
if (changeFlags & QPinchGesture::ScaleFactorChanged) {
QPointF pos = mapToScene(gesture->centerPoint().toPoint());
QRectF gr(_grid->boundingRect());
QPointF r(pos.x() / gr.width(), pos.y() / gr.height());
_zoom = qMax(_zoom * gesture->scaleFactor(), 1.0);
redraw();
QRectF ngr(_grid->boundingRect());
QPointF npos(mapFromScene(QPointF(r.x() * ngr.width(),
r.y() * ngr.height())));
QScrollBar *sb = horizontalScrollBar();
sb->setSliderPosition(sb->sliderPosition() + npos.x()
- gesture->centerPoint().x());
}
}
void GraphView::paintEvent(QPaintEvent *e) void GraphView::paintEvent(QPaintEvent *e)
{ {
QRectF viewRect(mapToScene(rect()).boundingRect()); QRectF viewRect(mapToScene(rect()).boundingRect());
@ -577,3 +601,19 @@ void GraphView::changeEvent(QEvent *e)
QGraphicsView::changeEvent(e); QGraphicsView::changeEvent(e);
} }
bool GraphView::event(QEvent *event)
{
if (event->type() == QEvent::Gesture)
return gestureEvent(static_cast<QGestureEvent*>(event));
return QGraphicsView::event(event);
}
bool GraphView::gestureEvent(QGestureEvent *event)
{
if (QGesture *pinch = event->gesture(Qt::PinchGesture))
pinchGesture(static_cast<QPinchGesture *>(pinch));
return true;
}

View File

@ -18,6 +18,8 @@ class PathItem;
class GridItem; class GridItem;
class QGraphicsSimpleTextItem; class QGraphicsSimpleTextItem;
class GraphicsScene; class GraphicsScene;
class QGestureEvent;
class QPinchGesture;
class GraphView : public QGraphicsView class GraphView : public QGraphicsView
{ {
@ -59,6 +61,7 @@ protected:
void wheelEvent(QWheelEvent *e); void wheelEvent(QWheelEvent *e);
void changeEvent(QEvent *e); void changeEvent(QEvent *e);
void paintEvent(QPaintEvent *e); void paintEvent(QPaintEvent *e);
bool event(QEvent *event);
const QString &yLabel() const {return _yLabel;} const QString &yLabel() const {return _yLabel;}
const QString &yUnits() const {return _yUnits;} const QString &yUnits() const {return _yUnits;}
@ -94,6 +97,8 @@ private:
void removeItem(QGraphicsItem *item); void removeItem(QGraphicsItem *item);
void addItem(QGraphicsItem *item); void addItem(QGraphicsItem *item);
bool singleGraph() const; bool singleGraph() const;
bool gestureEvent(QGestureEvent *event);
void pinchGesture(QPinchGesture *gesture);
GraphicsScene *_scene; GraphicsScene *_scene;

View File

@ -1721,8 +1721,7 @@ bool GUI::loadMapNode(const TreeNode<Map*> &node, MapAction *&action,
bool GUI::loadMap(const QString &fileName, MapAction *&action, bool silent) bool GUI::loadMap(const QString &fileName, MapAction *&action, bool silent)
{ {
TreeNode<Map*> maps(MapList::loadMaps(fileName, TreeNode<Map*> maps(MapList::loadMaps(fileName));
CRS::projection(_options.inputProjection)));
QList<QAction*> existingActions(_mapsActionGroup->actions()); QList<QAction*> existingActions(_mapsActionGroup->actions());
return loadMapNode(maps, action, silent, existingActions); return loadMapNode(maps, action, silent, existingActions);
@ -1812,8 +1811,7 @@ void GUI::loadMapDir()
return; return;
QFileInfo fi(dir); QFileInfo fi(dir);
TreeNode<Map*> maps(MapList::loadMaps(dir, TreeNode<Map*> maps(MapList::loadMaps(dir));
CRS::projection(_options.inputProjection)));
QList<QAction*> existingActions(_mapsActionGroup->actions()); QList<QAction*> existingActions(_mapsActionGroup->actions());
QList<MapAction*> actions; QList<MapAction*> actions;
QMenu *menu = new QMenu(maps.name()); QMenu *menu = new QMenu(maps.name());
@ -2741,13 +2739,9 @@ void GUI::loadOptions()
_mapView->setPOIColor(_options.poiColor); _mapView->setPOIColor(_options.poiColor);
_mapView->setRenderHint(QPainter::Antialiasing, _options.pathAntiAliasing); _mapView->setRenderHint(QPainter::Antialiasing, _options.pathAntiAliasing);
_mapView->setMarkerColor(_options.sliderColor); _mapView->setMarkerColor(_options.sliderColor);
if (_options.useOpenGL) _mapView->useOpenGL(_options.useOpenGL);
_mapView->useOpenGL(true); _mapView->setMapConfig(CRS::projection(_options.inputProjection),
_mapView->setDevicePixelRatio(devicePixelRatioF(), CRS::projection(4326, _options.outputProjection), _options.hidpiMap);
_options.hidpiMap ? devicePixelRatioF() : 1.0);
_mapView->setOutputProjection(CRS::projection(4326,
_options.outputProjection));
_mapView->setInputProjection(CRS::projection(_options.inputProjection));
_mapView->setTimeZone(_options.timeZone.zone()); _mapView->setTimeZone(_options.timeZone.zone());
_mapView->setPositionSource(_positionSource); _mapView->setPositionSource(_positionSource);
@ -2856,14 +2850,12 @@ void GUI::updateOptions(const Options &options)
_positionSource = source; _positionSource = source;
} }
if (options.hidpiMap != _options.hidpiMap) if (options.hidpiMap != _options.hidpiMap
_mapView->setDevicePixelRatio(devicePixelRatioF(), || options.outputProjection != _options.outputProjection
options.hidpiMap ? devicePixelRatioF() : 1.0); || options.inputProjection != _options.inputProjection)
if (options.outputProjection != _options.outputProjection) _mapView->setMapConfig(CRS::projection(options.inputProjection),
_mapView->setOutputProjection(CRS::projection(4326, CRS::projection(4326, options.outputProjection), options.hidpiMap);
options.outputProjection));
if (options.inputProjection != _options.inputProjection)
_mapView->setInputProjection(CRS::projection(options.inputProjection));
if (options.timeZone != _options.timeZone) { if (options.timeZone != _options.timeZone) {
_mapView->setTimeZone(options.timeZone.zone()); _mapView->setTimeZone(options.timeZone.zone());
_dateRange.first = _dateRange.first.toTimeZone(options.timeZone.zone()); _dateRange.first = _dateRange.first.toTimeZone(options.timeZone.zone());
@ -2941,8 +2933,7 @@ void GUI::loadInitialMaps(const QString &selected)
if (mapDir.isNull()) if (mapDir.isNull())
return; return;
TreeNode<Map*> maps(MapList::loadMaps(mapDir, TreeNode<Map*> maps(MapList::loadMaps(mapDir));
CRS::projection(_options.inputProjection)));
createMapNodeMenu(createMapActionsNode(maps), _mapMenu, _mapsEnd); createMapNodeMenu(createMapActionsNode(maps), _mapMenu, _mapsEnd);
// Select the active map according to the user settings // Select the active map according to the user settings
@ -3044,8 +3035,7 @@ void GUI::show()
void GUI::screenChanged(QScreen *screen) void GUI::screenChanged(QScreen *screen)
{ {
_mapView->setDevicePixelRatio(devicePixelRatioF(), _mapView->setDevicePixelRatio(devicePixelRatioF());
_options.hidpiMap ? devicePixelRatioF() : 1.0);
disconnect(SIGNAL(logicalDotsPerInchChanged(qreal)), this, disconnect(SIGNAL(logicalDotsPerInchChanged(qreal)), this,
SLOT(logicalDotsPerInchChanged(qreal))); SLOT(logicalDotsPerInchChanged(qreal)));
@ -3057,6 +3047,5 @@ void GUI::logicalDotsPerInchChanged(qreal dpi)
{ {
Q_UNUSED(dpi) Q_UNUSED(dpi)
_mapView->setDevicePixelRatio(devicePixelRatioF(), _mapView->setDevicePixelRatio(devicePixelRatioF());
_options.hidpiMap ? devicePixelRatioF() : 1.0);
} }

View File

@ -81,19 +81,18 @@ ToolTip MapItem::info() const
return tt; return tt;
} }
MapItem::MapItem(MapAction *action, Map *map, GraphicsItem *parent) MapItem::MapItem(MapAction *action, Map *map, const Projection &proj,
: PlaneItem(parent) GraphicsItem *parent) : PlaneItem(parent)
{ {
Map *src = action->data().value<Map*>(); Map *src = action->data().value<Map*>();
Q_ASSERT(map->isReady()); Q_ASSERT(map->isReady());
_name = src->name(); _name = src->name();
_fileName = src->path(); _fileName = src->path();
_bounds = src->llBounds(); _bounds = src->llBounds(proj);
connect(this, &MapItem::triggered, action, &MapAction::trigger); connect(this, &MapItem::triggered, action, &MapAction::trigger);
_map = map;
_digitalZoom = 0; _digitalZoom = 0;
_width = 2; _width = 2;
@ -101,23 +100,23 @@ MapItem::MapItem(MapAction *action, Map *map, GraphicsItem *parent)
QBrush brush(Qt::SolidPattern); QBrush brush(Qt::SolidPattern);
_pen = QPen(brush, _width); _pen = QPen(brush, _width);
updatePainterPath(); updatePainterPath(map);
setCursor(Qt::ArrowCursor); setCursor(Qt::ArrowCursor);
setAcceptHoverEvents(true); setAcceptHoverEvents(true);
} }
void MapItem::updatePainterPath() void MapItem::updatePainterPath(Map *map)
{ {
_painterPath = QPainterPath(); _painterPath = QPainterPath();
QRectF r(bbox(_bounds, _map)); QRectF r(bbox(_bounds, map));
if (r.left() > r.right()) { if (r.left() > r.right()) {
QRectF r1(bbox(RectC(_bounds.topLeft(), QRectF r1(bbox(RectC(_bounds.topLeft(),
Coordinates(180, _bounds.bottomRight().lat())), _map)); Coordinates(180, _bounds.bottomRight().lat())), map));
QRectF r2(bbox(RectC(Coordinates(-180, _bounds.topLeft().lat()), QRectF r2(bbox(RectC(Coordinates(-180, _bounds.topLeft().lat()),
_bounds.bottomRight()), _map)); _bounds.bottomRight()), map));
_painterPath.addRect(r1); _painterPath.addRect(r1);
_painterPath.addRect(r2); _painterPath.addRect(r2);
@ -143,10 +142,7 @@ void MapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
void MapItem::setMap(Map *map) void MapItem::setMap(Map *map)
{ {
prepareGeometryChange(); prepareGeometryChange();
updatePainterPath(map);
_map = map;
updatePainterPath();
} }
void MapItem::setColor(const QColor &color) void MapItem::setColor(const QColor &color)

View File

@ -4,13 +4,15 @@
#include "planeitem.h" #include "planeitem.h"
class MapAction; class MapAction;
class Projection;
class MapItem : public QObject, public PlaneItem class MapItem : public QObject, public PlaneItem
{ {
Q_OBJECT Q_OBJECT
public: public:
MapItem(MapAction *action, Map *map, GraphicsItem *parent = 0); MapItem(MapAction *action, Map *map, const Projection &proj,
GraphicsItem *parent = 0);
QPainterPath shape() const {return _painterPath;} QPainterPath shape() const {return _painterPath;}
QRectF boundingRect() const {return _painterPath.boundingRect();} QRectF boundingRect() const {return _painterPath.boundingRect();}
@ -37,13 +39,12 @@ protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
private: private:
void updatePainterPath(); void updatePainterPath(Map *map);
RectC _bounds; RectC _bounds;
QString _name; QString _name;
QString _fileName; QString _fileName;
Map *_map;
int _digitalZoom; int _digitalZoom;
qreal _width; qreal _width;

View File

@ -60,12 +60,12 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent) : QGraphicsView(parent)
_cursorCoordinates->setVisible(false); _cursorCoordinates->setVisible(false);
_scene->addItem(_cursorCoordinates); _scene->addItem(_cursorCoordinates);
_deviceRatio = devicePixelRatioF();
_outputProjection = PCS::pcs(3857); _outputProjection = PCS::pcs(3857);
_inputProjection = GCS::gcs(4326); _inputProjection = GCS::gcs(4326);
_hidpi = true;
_map = map; _map = map;
_map->load(); _map->load(_inputProjection, _outputProjection, _deviceRatio, _hidpi);
_map->setOutputProjection(_outputProjection);
_map->setInputProjection(_inputProjection);
connect(_map, &Map::tilesLoaded, this, &MapView::reloadMap); connect(_map, &Map::tilesLoaded, this, &MapView::reloadMap);
_poi = poi; _poi = poi;
@ -119,8 +119,6 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent) : QGraphicsView(parent)
_showPositionCoordinates = false; _showPositionCoordinates = false;
_showMotionInfo = false; _showMotionInfo = false;
_deviceRatio = 1.0;
_mapRatio = 1.0;
_opengl = false; _opengl = false;
_plot = false; _plot = false;
_digitalZoom = 0; _digitalZoom = 0;
@ -247,7 +245,7 @@ void MapView::addWaypoints(const QVector<Waypoint> &waypoints)
MapItem *MapView::addMap(MapAction *map) MapItem *MapView::addMap(MapAction *map)
{ {
MapItem *mi = new MapItem(map, _map); MapItem *mi = new MapItem(map, _map, _inputProjection);
mi->setColor(_palette.nextColor()); mi->setColor(_palette.nextColor());
mi->setWidth(_areaWidth); mi->setWidth(_areaWidth);
mi->setPenStyle(_areaStyle); mi->setPenStyle(_areaStyle);
@ -328,7 +326,7 @@ int MapView::fitMapZoom() const
RectC br = _tr | _rr | _wr | _ar; RectC br = _tr | _rr | _wr | _ar;
return _map->zoomFit(viewport()->size() - QSize(2*MARGIN, 2*MARGIN), return _map->zoomFit(viewport()->size() - QSize(2*MARGIN, 2*MARGIN),
br.isNull() ? _map->llBounds() : br); br.isNull() ? _map->llBounds(_inputProjection) : br);
} }
QPointF MapView::contentCenter() const QPointF MapView::contentCenter() const
@ -402,36 +400,18 @@ void MapView::setMap(Map *map)
.intersected(_map->bounds())); .intersected(_map->bounds()));
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight())); RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
_map->unload();
disconnect(_map, &Map::tilesLoaded, this, &MapView::reloadMap); disconnect(_map, &Map::tilesLoaded, this, &MapView::reloadMap);
_map->unload();
_map = map; _map = map;
_map->load(); _map->load(_inputProjection, _outputProjection, _deviceRatio, _hidpi);
_map->setOutputProjection(_outputProjection);
_map->setInputProjection(_inputProjection);
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
connect(_map, &Map::tilesLoaded, this, &MapView::reloadMap); connect(_map, &Map::tilesLoaded, this, &MapView::reloadMap);
digitalZoom(0); digitalZoom(0);
_map->zoomFit(viewport()->rect().size(), cr); _map->zoomFit(viewport()->rect().size(), cr);
_scene->setSceneRect(_map->bounds());
for (int i = 0; i < _tracks.size(); i++) rescale();
_tracks.at(i)->setMap(_map);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMap(_map);
for (int i = 0; i < _areas.size(); i++)
_areas.at(i)->setMap(_map);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(_map);
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setMap(_map);
updatePOIVisibility();
_crosshair->setMap(_map);
QPointF nc = QRectF(_map->ll2xy(cr.topLeft()), QPointF nc = QRectF(_map->ll2xy(cr.topLeft()),
_map->ll2xy(cr.bottomRight())).center(); _map->ll2xy(cr.bottomRight())).center();
@ -702,8 +682,9 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
PlotFlags flags) PlotFlags flags)
{ {
QRect orig, adj; QRect orig, adj;
qreal mapRatio, ratio, diff, q, p; qreal ratio, diff, q, p;
QPointF scenePos, scalePos, posPos, motionPos; QPointF scenePos, scalePos, posPos, motionPos;
bool hidpi = _hidpi && _deviceRatio > 1.0;
int zoom; int zoom;
@ -728,8 +709,8 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
} }
// Expand the view if plotting into a bitmap // Expand the view if plotting into a bitmap
mapRatio = _mapRatio; if (hidpi)
setDevicePixelRatio(_deviceRatio, 1.0); setHidpi(false);
if (flags & Expand) { if (flags & Expand) {
qreal xdiff = (target.width() - adj.width()) / 2.0; qreal xdiff = (target.width() - adj.width()) / 2.0;
@ -784,7 +765,8 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale,
centerOn(scenePos); centerOn(scenePos);
} }
setDevicePixelRatio(_deviceRatio, mapRatio); if (hidpi)
setHidpi(true);
_mapScale->setDigitalZoom(_digitalZoom); _mapScale->setDigitalZoom(_digitalZoom);
_mapScale->setPos(scalePos); _mapScale->setPos(scalePos);
@ -1236,6 +1218,9 @@ bool MapView::gestureEvent(QGestureEvent *event)
void MapView::useOpenGL(bool use) void MapView::useOpenGL(bool use)
{ {
if (_opengl == use)
return;
_opengl = use; _opengl = use;
if (use) if (use)
@ -1286,60 +1271,21 @@ void MapView::reloadMap()
_scene->invalidate(); _scene->invalidate();
} }
void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) void MapView::setMapConfig(const Projection &in, const Projection &out,
bool hidpi)
{ {
if (_deviceRatio == deviceRatio && _mapRatio == mapRatio) _inputProjection = in;
return; _outputProjection = out;
_hidpi = hidpi;
_deviceRatio = deviceRatio; setMap(_map);
_mapRatio = mapRatio;
QRectF vr(mapToScene(viewport()->rect()).boundingRect()
.intersected(_map->bounds()));
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
_map->setDevicePixelRatio(_deviceRatio, _mapRatio);
_scene->setSceneRect(_map->bounds());
for (int i = 0; i < _tracks.size(); i++)
_tracks.at(i)->setMap(_map);
for (int i = 0; i < _routes.size(); i++)
_routes.at(i)->setMap(_map);
for (int i = 0; i < _areas.size(); i++)
_areas.at(i)->setMap(_map);
for (int i = 0; i < _waypoints.size(); i++)
_waypoints.at(i)->setMap(_map);
for (POIHash::const_iterator it = _pois.constBegin();
it != _pois.constEnd(); it++)
it.value()->setMap(_map);
updatePOIVisibility();
_crosshair->setMap(_map);
QPointF nc = QRectF(_map->ll2xy(cr.topLeft()),
_map->ll2xy(cr.bottomRight())).center();
centerOn(nc);
reloadMap();
} }
void MapView::setOutputProjection(const Projection &proj) void MapView::setDevicePixelRatio(qreal ratio)
{ {
_outputProjection = proj; _deviceRatio = ratio;
Coordinates center = _map->xy2ll(mapToScene(viewport()->rect().center()));
_map->setOutputProjection(_outputProjection);
rescale();
centerOn(_map->ll2xy(center));
}
void MapView::setInputProjection(const Projection &proj) setMap(_map);
{
_inputProjection = proj;
Coordinates center = _map->xy2ll(mapToScene(viewport()->rect().center()));
_map->setInputProjection(_inputProjection);
rescale();
centerOn(_map->ll2xy(center));
} }
void MapView::fitContentToSize() void MapView::fitContentToSize()
@ -1436,3 +1382,20 @@ void MapView::drawInfoBackground(bool draw)
_positionCoordinates->drawBackground(draw); _positionCoordinates->drawBackground(draw);
_motionInfo->drawBackground(draw); _motionInfo->drawBackground(draw);
} }
void MapView::setHidpi(bool hidpi)
{
QRectF vr(mapToScene(viewport()->rect()).boundingRect()
.intersected(_map->bounds()));
RectC cr(_map->xy2ll(vr.topLeft()), _map->xy2ll(vr.bottomRight()));
_map->unload();
_map->load(_inputProjection, _outputProjection, _deviceRatio, hidpi);
rescale();
QPointF nc = QRectF(_map->ll2xy(cr.topLeft()),
_map->ll2xy(cr.bottomRight())).center();
centerOn(nc);
reloadMap();
}

View File

@ -92,9 +92,8 @@ public:
void useAntiAliasing(bool use); void useAntiAliasing(bool use);
void setCoordinatesFormat(CoordinatesFormat format); void setCoordinatesFormat(CoordinatesFormat format);
void setTimeZone(const QTimeZone &zone); void setTimeZone(const QTimeZone &zone);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void setMapConfig(const Projection &in, const Projection &out, bool hidpi);
void setOutputProjection(const Projection &proj); void setDevicePixelRatio(qreal ratio);
void setInputProjection(const Projection &proj);
void clearMapCache(); void clearMapCache();
void fitContentToSize(); void fitContentToSize();
@ -156,6 +155,7 @@ private:
bool gestureEvent(QGestureEvent *event); bool gestureEvent(QGestureEvent *event);
void pinchGesture(QPinchGesture *gesture); void pinchGesture(QPinchGesture *gesture);
void skipColor() {_palette.nextColor();} void skipColor() {_palette.nextColor();}
void setHidpi(bool hidpi);
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);
@ -209,7 +209,7 @@ private:
QCursor _cursor; QCursor _cursor;
qreal _deviceRatio; qreal _deviceRatio;
qreal _mapRatio; bool _hidpi;
bool _opengl; bool _opengl;
int _pinchZoom; int _pinchZoom;

View File

@ -334,12 +334,18 @@ void PathItem::setMarkerPosition(qreal pos)
void PathItem::setMarkerInfo(qreal pos) void PathItem::setMarkerInfo(qreal pos)
{ {
if (_markerInfoType == MarkerInfoItem::Date) { if (_markerInfoType == MarkerInfoItem::Date) {
qreal time = _graph QDateTime date;
? (_graph->graphType() == Time) ? pos : _graph->timeAtDistance(pos)
: NAN; if (_graph) {
QDateTime d(date()); qreal time = (_graph->graphType() == Time)
if (!std::isnan(time) && d.isValid()) ? pos : _graph->timeAtDistance(pos);
_markerInfo->setDate(d.addSecs(time).toTimeZone(_timeZone)); GraphItem::SegmentTime st(_graph->date(pos));
if (st.date.isValid() && !std::isnan(time))
date = st.date.addSecs(time - st.time);
}
if (date.isValid())
_markerInfo->setDate(date.toTimeZone(_timeZone));
else else
_markerInfo->setDate(QDateTime()); _markerInfo->setDate(QDateTime());
} else if (_markerInfoType == MarkerInfoItem::Position) } else if (_markerInfoType == MarkerInfoItem::Position)

View File

@ -3,6 +3,7 @@
#include <cmath> #include <cmath>
#include <QDebug> #include <QDebug>
#include "hash.h"
#define deg2rad(d) (((d)*M_PI)/180.0) #define deg2rad(d) (((d)*M_PI)/180.0)
#define rad2deg(d) (((d)*180.0)/M_PI) #define rad2deg(d) (((d)*180.0)/M_PI)
@ -48,6 +49,11 @@ inline bool operator<(const Coordinates &c1, const Coordinates &c2)
return (c1.lat() < c2.lat()); return (c1.lat() < c2.lat());
} }
inline HASH_T qHash(const Coordinates &c)
{
return qHash(QPair<double, double>(c.lon(), c.lat()));
}
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Coordinates &c); QDebug operator<<(QDebug dbg, const Coordinates &c);
#endif // QT_NO_DEBUG #endif // QT_NO_DEBUG

View File

@ -53,7 +53,7 @@ Authorization::Authorization(const QString &username, const QString &password)
{ {
QString concatenated = username + ":" + password; QString concatenated = username + ":" + password;
QByteArray data = concatenated.toLocal8Bit().toBase64(); QByteArray data = concatenated.toLocal8Bit().toBase64();
_header = "Basic " + data; _header = HTTPHeader("Authorization", "Basic " + data);
} }
NetworkTimeout::NetworkTimeout(int timeout, QNetworkReply *reply) NetworkTimeout::NetworkTimeout(int timeout, QNetworkReply *reply)
@ -83,9 +83,10 @@ QNetworkAccessManager *Downloader::_manager = 0;
int Downloader::_timeout = 30; int Downloader::_timeout = 30;
bool Downloader::_http2 = true; bool Downloader::_http2 = true;
bool Downloader::doDownload(const Download &dl, const Authorization &auth) bool Downloader::doDownload(const Download &dl, const QList<HTTPHeader> &headers)
{ {
const QUrl &url = dl.url(); const QUrl &url = dl.url();
bool userAgent = false;
if (!url.isValid() || !(url.scheme() == QLatin1String("http") if (!url.isValid() || !(url.scheme() == QLatin1String("http")
|| url.scheme() == QLatin1String("https"))) { || url.scheme() == QLatin1String("https"))) {
@ -103,9 +104,15 @@ bool Downloader::doDownload(const Download &dl, const Authorization &auth)
request.setAttribute(ATTR_REDIRECT_POLICY, request.setAttribute(ATTR_REDIRECT_POLICY,
QNetworkRequest::NoLessSafeRedirectPolicy); QNetworkRequest::NoLessSafeRedirectPolicy);
request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2)); request.setAttribute(ATTR_HTTP2_ALLOWED, QVariant(_http2));
request.setRawHeader("User-Agent", USER_AGENT);
if (!auth.isNull()) for (int i = 0; i < headers.size(); i++) {
request.setRawHeader("Authorization", auth.header()); const HTTPHeader &hdr = headers.at(i);
request.setRawHeader(hdr.key(), hdr.value());
if (hdr.key() == "User-Agent")
userAgent = true;
}
if (!userAgent)
request.setRawHeader("User-Agent", USER_AGENT);
QFile *file = new QFile(tmpName(dl.file())); QFile *file = new QFile(tmpName(dl.file()));
if (!file->open(QIODevice::WriteOnly)) { if (!file->open(QIODevice::WriteOnly)) {
@ -183,12 +190,12 @@ void Downloader::downloadFinished(QNetworkReply *reply)
} }
bool Downloader::get(const QList<Download> &list, bool Downloader::get(const QList<Download> &list,
const Authorization &authorization) const QList<HTTPHeader> &headers)
{ {
bool finishEmitted = false; bool finishEmitted = false;
for (int i = 0; i < list.count(); i++) for (int i = 0; i < list.count(); i++)
finishEmitted |= doDownload(list.at(i), authorization); finishEmitted |= doDownload(list.at(i), headers);
return finishEmitted; return finishEmitted;
} }

View File

@ -7,9 +7,12 @@
#include <QUrl> #include <QUrl>
#include <QList> #include <QList>
#include <QHash> #include <QHash>
#include "common/kv.h"
class QFile; class QFile;
typedef KV<QByteArray, QByteArray> HTTPHeader;
class Download class Download
{ {
public: public:
@ -29,11 +32,11 @@ public:
Authorization() {} Authorization() {}
Authorization(const QString &username, const QString &password); Authorization(const QString &username, const QString &password);
bool isNull() const {return _header.isNull();} const HTTPHeader &header() const {return _header;}
const QByteArray &header() const {return _header;} bool isNull() const {return _header.key().isNull();}
private: private:
QByteArray _header; HTTPHeader _header;
}; };
class NetworkTimeout : public QObject class NetworkTimeout : public QObject
@ -60,8 +63,7 @@ class Downloader : public QObject
public: public:
Downloader(QObject *parent = 0) : QObject(parent) {} Downloader(QObject *parent = 0) : QObject(parent) {}
bool get(const QList<Download> &list, const Authorization &authorization bool get(const QList<Download> &list, const QList<HTTPHeader> &headers);
= Authorization());
void clearErrors() {_errorDownloads.clear();} void clearErrors() {_errorDownloads.clear();}
static void setNetworkManager(QNetworkAccessManager *manager) static void setNetworkManager(QNetworkAccessManager *manager)
@ -80,7 +82,7 @@ private:
class ReplyTimeout; class ReplyTimeout;
void insertError(const QUrl &url, QNetworkReply::NetworkError error); void insertError(const QUrl &url, QNetworkReply::NetworkError error);
bool doDownload(const Download &dl, const Authorization &auth); bool doDownload(const Download &dl, const QList<HTTPHeader> &headers);
void downloadFinished(QNetworkReply *reply); void downloadFinished(QNetworkReply *reply);
void readData(QNetworkReply *reply); void readData(QNetworkReply *reply);

View File

@ -6,13 +6,13 @@
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#define HASH_T uint #define HASH_T uint
inline uint qHash(const QPoint &p)
{
return qHash(QPair<int, int>(p.x(), p.y()));
}
#else // QT6 #else // QT6
#define HASH_T size_t #define HASH_T size_t
#endif // QT6 #endif // QT6
inline HASH_T qHash(const QPoint &p)
{
return ::qHash(p.x()) ^ ::qHash(p.y());
}
#endif // HASH_H #endif // HASH_H

View File

@ -4,6 +4,7 @@
template <class KEY, class VALUE> template <class KEY, class VALUE>
class KV { class KV {
public: public:
KV() {}
KV(const KEY &key, const VALUE &value) : _key(key), _value(value) {} KV(const KEY &key, const VALUE &value) : _key(key), _value(value) {}
const KEY &key() const {return _key;} const KEY &key() const {return _key;}

View File

@ -64,7 +64,7 @@ bool DEMLoader::loadTiles(const RectC &rect)
} }
} }
return _downloader->get(dl, _authorization); return _downloader->get(dl, _headers);
} }
bool DEMLoader::checkTiles(const RectC &rect) const bool DEMLoader::checkTiles(const RectC &rect) const
@ -97,3 +97,11 @@ QString DEMLoader::tileFile(const DEM::Tile &tile) const
{ {
return _dir.absoluteFilePath(tile.baseName()); return _dir.absoluteFilePath(tile.baseName());
} }
void DEMLoader::setAuthorization(const Authorization &authorization)
{
QList<HTTPHeader> headers;
if (!authorization.isNull())
headers.append(authorization.header());
_headers = headers;
}

View File

@ -16,8 +16,7 @@ public:
DEMLoader(const QString &dir, QObject *parent = 0); DEMLoader(const QString &dir, QObject *parent = 0);
void setUrl(const QString &url) {_url = url;} void setUrl(const QString &url) {_url = url;}
void setAuthorization(const Authorization &authorization) void setAuthorization(const Authorization &authorization);
{_authorization = authorization;}
bool loadTiles(const RectC &rect); bool loadTiles(const RectC &rect);
bool checkTiles(const RectC &rect) const; bool checkTiles(const RectC &rect) const;
@ -34,7 +33,7 @@ private:
Downloader *_downloader; Downloader *_downloader;
QString _url; QString _url;
QDir _dir; QDir _dir;
Authorization _authorization; QList<HTTPHeader> _headers;
}; };
#endif // DEMLOADER_H #endif // DEMLOADER_H

View File

@ -4,6 +4,7 @@
#include <QList> #include <QList>
#include <QVector> #include <QVector>
#include <QColor> #include <QColor>
#include <QDateTime>
#include <QDebug> #include <QDebug>
#include <cmath> #include <cmath>
@ -41,7 +42,19 @@ inline QDebug operator<<(QDebug dbg, const GraphPoint &point)
} }
#endif // QT_NO_DEBUG #endif // QT_NO_DEBUG
typedef QVector<GraphPoint> GraphSegment; class GraphSegment : public QVector<GraphPoint>
{
public:
GraphSegment(const QDateTime &start)
: _start(start) {}
GraphSegment(int size, const QDateTime &start)
: QVector<GraphPoint>(size), _start(start) {}
const QDateTime &start() const {return _start;}
private:
QDateTime _start;
};
class Graph : public QList<GraphSegment> class Graph : public QList<GraphSegment>
{ {

View File

@ -33,13 +33,16 @@ Path Route::path() const
Graph Route::gpsElevation() const Graph Route::gpsElevation() const
{ {
Graph graph; Graph graph;
graph.append(GraphSegment()); QDateTime date;
GraphSegment &gs = graph.last(); GraphSegment gs(date);
for (int i = 0; i < _data.size(); i++) for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation()) if (_data.at(i).hasElevation())
gs.append(GraphPoint(_distance.at(i), NAN, _data.at(i).elevation())); gs.append(GraphPoint(_distance.at(i), NAN, _data.at(i).elevation()));
if (gs.size() >= 2)
graph.append(gs);
if (_data.style().color().isValid()) if (_data.style().color().isValid())
graph.setColor(_data.style().color()); graph.setColor(_data.style().color());
@ -49,8 +52,8 @@ Graph Route::gpsElevation() const
Graph Route::demElevation() const Graph Route::demElevation() const
{ {
Graph graph; Graph graph;
graph.append(GraphSegment()); QDateTime date;
GraphSegment &gs = graph.last(); GraphSegment gs(date);
for (int i = 0; i < _data.size(); i++) { for (int i = 0; i < _data.size(); i++) {
qreal dem = DEM::elevation(_data.at(i).coordinates()); qreal dem = DEM::elevation(_data.at(i).coordinates());
@ -58,6 +61,9 @@ Graph Route::demElevation() const
gs.append(GraphPoint(_distance.at(i), NAN, dem)); gs.append(GraphPoint(_distance.at(i), NAN, dem));
} }
if (gs.size() >= 2)
graph.append(gs);
if (_data.style().color().isValid()) if (_data.style().color().isValid())
graph.setColor(_data.style().color()); graph.setColor(_data.style().color());

View File

@ -66,10 +66,10 @@ static QSet<int> eliminate(const QVector<qreal> &v)
static GraphSegment filter(const GraphSegment &g, int window) static GraphSegment filter(const GraphSegment &g, int window)
{ {
if (g.size() < window || window < 2) if (g.size() < window || window < 2)
return GraphSegment(g); return g;
qreal acc = 0; qreal acc = 0;
GraphSegment ret(g.size()); GraphSegment ret(g.size(), g.start());
for (int i = 0; i < window; i++) for (int i = 0; i < window; i++)
acc += g.at(i).y(); acc += g.at(i).y();
@ -131,6 +131,7 @@ Track::Track(const TrackData &data) : _pause(0)
Segment &seg = _segments.last(); Segment &seg = _segments.last();
seg.start = sd.first().timestamp();
seg.distance.append(lastDistance(i)); seg.distance.append(lastDistance(i));
seg.time.append(sd.first().hasTimestamp() ? lastTime(i) : NAN); seg.time.append(sd.first().hasTimestamp() ? lastTime(i) : NAN);
seg.speed.append(sd.first().hasTimestamp() ? 0 : NAN); seg.speed.append(sd.first().hasTimestamp() ? 0 : NAN);
@ -257,7 +258,7 @@ Graph Track::gpsElevation() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
for (int j = 0; j < sd.size(); j++) { for (int j = 0; j < sd.size(); j++) {
if (!sd.at(j).hasElevation() || seg.outliers.contains(j)) if (!sd.at(j).hasElevation() || seg.outliers.contains(j))
@ -285,7 +286,7 @@ Graph Track::demElevation() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
for (int j = 0; j < sd.size(); j++) { for (int j = 0; j < sd.size(); j++) {
qreal dem = DEM::elevation(sd.at(j).coordinates()); qreal dem = DEM::elevation(sd.at(j).coordinates());
@ -328,7 +329,7 @@ Graph Track::computedSpeed() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
QList<int> stop; QList<int> stop;
qreal v; qreal v;
@ -368,7 +369,7 @@ Graph Track::reportedSpeed() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
QList<int> stop; QList<int> stop;
qreal v; qreal v;
@ -423,7 +424,7 @@ Graph Track::heartRate() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
for (int j = 0; j < sd.size(); j++) for (int j = 0; j < sd.size(); j++)
if (sd.at(j).hasHeartRate() && !seg.outliers.contains(j)) if (sd.at(j).hasHeartRate() && !seg.outliers.contains(j))
@ -449,7 +450,7 @@ Graph Track::temperature() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
for (int j = 0; j < sd.count(); j++) { for (int j = 0; j < sd.count(); j++) {
if (sd.at(j).hasTemperature() && !seg.outliers.contains(j)) if (sd.at(j).hasTemperature() && !seg.outliers.contains(j))
@ -476,7 +477,7 @@ Graph Track::ratio() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
for (int j = 0; j < sd.size(); j++) for (int j = 0; j < sd.size(); j++)
if (sd.at(j).hasRatio() && !seg.outliers.contains(j)) if (sd.at(j).hasRatio() && !seg.outliers.contains(j))
@ -502,7 +503,7 @@ Graph Track::cadence() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
QList<int> stop; QList<int> stop;
qreal c; qreal c;
@ -545,7 +546,7 @@ Graph Track::power() const
if (sd.size() < 2) if (sd.size() < 2)
continue; continue;
const Segment &seg = _segments.at(i); const Segment &seg = _segments.at(i);
GraphSegment gs; GraphSegment gs(seg.start);
for (int j = 0; j < sd.size(); j++) { for (int j = 0; j < sd.size(); j++) {
if (sd.at(j).hasPower() && seg.stop.contains(j)) { if (sd.at(j).hasPower() && seg.stop.contains(j)) {

View File

@ -56,6 +56,7 @@ public:
private: private:
struct Segment { struct Segment {
QDateTime start;
QVector<qreal> distance; QVector<qreal> distance;
QVector<qreal> time; QVector<qreal> time;
QVector<qreal> speed; QVector<qreal> speed;

View File

@ -23,6 +23,7 @@ static QMap<uint,uint> orderMapInit()
map.insert(TYPE(FOGSIG), 0); map.insert(TYPE(FOGSIG), 0);
map.insert(TYPE(CGUSTA), 1); map.insert(TYPE(CGUSTA), 1);
map.insert(TYPE(RSCSTA), 1);
map.insert(SUBTYPE(BUAARE, 1), 2); map.insert(SUBTYPE(BUAARE, 1), 2);
map.insert(SUBTYPE(BUAARE, 5), 3); map.insert(SUBTYPE(BUAARE, 5), 3);
map.insert(SUBTYPE(BUAARE, 4), 4); map.insert(SUBTYPE(BUAARE, 4), 4);
@ -264,6 +265,13 @@ MapData::Point::Point(uint type, const Coordinates &c, const QString &label,
if (_label.isEmpty()) if (_label.isEmpty())
_label = sistat(type & 0xFF); _label = sistat(type & 0xFF);
_type = TYPE(SISTAT); _type = TYPE(SISTAT);
} else if (type>>16 == LNDELV && params.size()) {
if (_label.isEmpty())
_label = QString::fromLatin1(params.at(0))
+ QString::fromUtf8("\xE2\x80\x89m");
else
_label += "\n(" + QString::fromLatin1(params.at(0))
+ "\xE2\x80\x89m)";
} }
} }
@ -552,7 +560,8 @@ MapData::Attr MapData::pointAttr(const ISO8211::Record &r, uint OBJL)
if ((OBJL == I_DISMAR && key == I_WTWDIS) if ((OBJL == I_DISMAR && key == I_WTWDIS)
|| (OBJL == RDOCAL && key == ORIENT) || (OBJL == RDOCAL && key == ORIENT)
|| (OBJL == I_RDOCAL && key == ORIENT) || (OBJL == I_RDOCAL && key == ORIENT)
|| (OBJL == CURENT && key == ORIENT)) || (OBJL == CURENT && key == ORIENT)
|| (OBJL == LNDELV && key == ELEVAT))
params[0] = av.at(1).toByteArray(); params[0] = av.at(1).toByteArray();
if ((OBJL == I_RDOCAL && key == COMCHA) if ((OBJL == I_RDOCAL && key == COMCHA)
|| (OBJL == RDOCAL && key == COMCHA) || (OBJL == RDOCAL && key == COMCHA)
@ -893,7 +902,7 @@ void MapData::clear()
_points.RemoveAll(); _points.RemoveAll();
} }
void MapData::points(const RectC &rect, QList<Point*> *points) void MapData::points(const RectC &rect, QList<Point*> *points) const
{ {
double min[2], max[2]; double min[2], max[2];
@ -901,7 +910,7 @@ void MapData::points(const RectC &rect, QList<Point*> *points)
_points.Search(min, max, pointCb, points); _points.Search(min, max, pointCb, points);
} }
void MapData::lines(const RectC &rect, QList<Line*> *lines) void MapData::lines(const RectC &rect, QList<Line*> *lines) const
{ {
double min[2], max[2]; double min[2], max[2];
@ -909,7 +918,7 @@ void MapData::lines(const RectC &rect, QList<Line*> *lines)
_lines.Search(min, max, lineCb, lines); _lines.Search(min, max, lineCb, lines);
} }
void MapData::polygons(const RectC &rect, QList<Poly*> *polygons) void MapData::polygons(const RectC &rect, QList<Poly*> *polygons) const
{ {
double min[2], max[2]; double min[2], max[2];

View File

@ -72,9 +72,9 @@ public:
RectC bounds() const {return _bounds;} RectC bounds() const {return _bounds;}
Range zooms() const; Range zooms() const;
void polygons(const RectC &rect, QList<Poly*> *polygons); void polygons(const RectC &rect, QList<Poly*> *polygons) const;
void lines(const RectC &rect, QList<Line*> *lines); void lines(const RectC &rect, QList<Line*> *lines) const;
void points(const RectC &rect, QList<Point*> *points); void points(const RectC &rect, QList<Point*> *points) const;
void load(); void load();
void clear(); void clear();

View File

@ -70,6 +70,7 @@
#define RAILWY 106 #define RAILWY 106
#define RCRTCL 108 #define RCRTCL 108
#define RECTRC 109 #define RECTRC 109
#define RSCSTA 111
#define RESARE 112 #define RESARE 112
#define RIVERS 114 #define RIVERS 114
#define ROADWY 116 #define ROADWY 116

View File

@ -3,19 +3,18 @@
#include "common/linec.h" #include "common/linec.h"
#include "map/bitmapline.h" #include "map/bitmapline.h"
#include "map/textpathitem.h" #include "map/textpathitem.h"
#include "map/rectd.h"
#include "style.h" #include "style.h"
#include "rastertile.h" #include "rastertile.h"
using namespace ENC; using namespace ENC;
#define TEXT_EXTENT 160
#define TSSLPT_SIZE 0.005 /* ll */ #define TSSLPT_SIZE 0.005 /* ll */
#define RDOCAL_SIZE 12 /* px */
#define CURENT_SIZE 12 /* px */
typedef QMap<Coordinates, const MapData::Point*> PointMap; typedef QMap<Coordinates, const MapData::Point*> PointMap;
const float C1 = 0.866025f; /* sqrt(3)/2 */ static const float C1 = 0.866025f; /* sqrt(3)/2 */
static const QColor haloColor(Qt::white); static const QColor haloColor(Qt::white);
static struct { static struct {
@ -102,67 +101,12 @@ static Coordinates centroid(const QVector<Coordinates> &polygon)
return Coordinates(cx * factor, cy * factor); return Coordinates(cx * factor, cy * factor);
} }
static QImage *rdocalArrow(qreal angle) static double angle(uint type, const QVariant &param)
{ {
QImage *img = new QImage(RDOCAL_SIZE*2, RDOCAL_SIZE*2, uint bt = type>>16;
QImage::Format_ARGB32_Premultiplied);
img->fill(Qt::transparent);
QPainter p(img);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(QPen(QColor("#eb49eb"), 1));
QPointF arrow[3]; return (bt == RDOCAL || bt == I_RDOCAL || bt == CURENT)
arrow[0] = QPointF(img->width()/2, img->height()/2); ? 90 + param.toDouble() : NAN;
arrow[1] = arrow[0] + QPointF(qSin(angle - M_PI/3) * RDOCAL_SIZE,
qCos(angle - M_PI/3) * RDOCAL_SIZE);
arrow[2] = arrow[0] + QPointF(qSin(angle - M_PI + M_PI/3) * RDOCAL_SIZE,
qCos(angle - M_PI + M_PI/3) * RDOCAL_SIZE);
QLineF l(arrow[1], arrow[2]);
QPointF pt(l.pointAt(0.5));
p.translate(arrow[0] - pt);
p.drawPolyline(QPolygonF() << arrow[1] << arrow[0] << arrow[2]);
p.drawEllipse(pt, RDOCAL_SIZE/2, RDOCAL_SIZE/2);
return img;
}
static QImage *currentArrow(qreal angle)
{
QImage *img = new QImage(CURENT_SIZE*2, CURENT_SIZE*2,
QImage::Format_ARGB32_Premultiplied);
img->fill(Qt::transparent);
QPainter p(img);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(QPen(Qt::black, 1));
QPointF arrow[3];
arrow[0] = QPointF(img->width()/2, img->height()/2);
arrow[1] = arrow[0] + QPointF(qSin(angle - M_PI/3) * CURENT_SIZE,
qCos(angle - M_PI/3) * CURENT_SIZE);
arrow[2] = arrow[0] + QPointF(qSin(angle - M_PI + M_PI/3) * CURENT_SIZE,
qCos(angle - M_PI + M_PI/3) * CURENT_SIZE);
QLineF l(arrow[1], arrow[2]);
QPointF pt(l.pointAt(0.5));
QLineF l2(arrow[0], pt);
p.translate(arrow[0] - pt);
p.drawPolyline(QPolygonF() << arrow[1] << arrow[0] << arrow[2]);
p.drawLine(arrow[0], l2.pointAt(2));
return img;
}
static QImage *image(uint type, const QVariant &param)
{
if (type>>16 == RDOCAL || type>>16 == I_RDOCAL)
return rdocalArrow(deg2rad(90 - param.toDouble()));
else if (type>>16 == CURENT)
return currentArrow(deg2rad(90 - param.toDouble()));
else
return 0;
} }
static bool showLabel(const QImage *img, const Range &range, int zoom, int type) static bool showLabel(const QImage *img, const Range &range, int zoom, int type)
@ -183,9 +127,10 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon) const
for (int i = 0; i < polygon.size(); i++) { for (int i = 0; i < polygon.size(); i++) {
const QVector<Coordinates> &subpath = polygon.at(i); const QVector<Coordinates> &subpath = polygon.at(i);
path.moveTo(ll2xy(subpath.first())); QVector<QPointF> p(subpath.size());
for (int j = 1; j < subpath.size(); j++) for (int j = 0; j < subpath.size(); j++)
path.lineTo(ll2xy(subpath.at(j))); p[j] = ll2xy(subpath.at(j));
path.addPolygon(p);
} }
return path; return path;
@ -227,10 +172,11 @@ QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const
return polygon; return polygon;
} }
void RasterTile::drawArrows(QPainter *painter) void RasterTile::drawArrows(QPainter *painter,
const QList<MapData::Poly*> &polygons)
{ {
for (int i = 0; i < _polygons.size(); i++) { for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly *poly = _polygons.at(i); const MapData::Poly *poly = polygons.at(i);
if (poly->type()>>16 == TSSLPT) { if (poly->type()>>16 == TSSLPT) {
QPolygonF polygon(tsslptArrow(centroid(poly->path().first()), QPolygonF polygon(tsslptArrow(centroid(poly->path().first()),
@ -243,13 +189,14 @@ void RasterTile::drawArrows(QPainter *painter)
} }
} }
void RasterTile::drawPolygons(QPainter *painter) void RasterTile::drawPolygons(QPainter *painter,
const QList<MapData::Poly*> &polygons)
{ {
const Style &s = style(); const Style &s = style();
for (int n = 0; n < s.drawOrder().size(); n++) { for (int n = 0; n < s.drawOrder().size(); n++) {
for (int i = 0; i < _polygons.size(); i++) { for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly *poly = _polygons.at(i); const MapData::Poly *poly = polygons.at(i);
if (poly->type() != s.drawOrder().at(n)) if (poly->type() != s.drawOrder().at(n))
continue; continue;
const Style::Polygon &style = s.polygon(poly->type()); const Style::Polygon &style = s.polygon(poly->type());
@ -267,14 +214,14 @@ void RasterTile::drawPolygons(QPainter *painter)
} }
} }
void RasterTile::drawLines(QPainter *painter) void RasterTile::drawLines(QPainter *painter, const QList<MapData::Line*> &lines)
{ {
const Style &s = style(); const Style &s = style();
painter->setBrush(Qt::NoBrush); painter->setBrush(Qt::NoBrush);
for (int i = 0; i < _lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
const MapData::Line *line = _lines.at(i); const MapData::Line *line = lines.at(i);
const Style::Line &style = s.line(line->type()); const Style::Line &style = s.line(line->type());
if (!style.img().isNull()) { if (!style.img().isNull()) {
@ -293,12 +240,13 @@ void RasterTile::drawTextItems(QPainter *painter,
textItems.at(i)->paint(painter); textItems.at(i)->paint(painter);
} }
void RasterTile::processPolygons(QList<TextItem*> &textItems) void RasterTile::processPolygons(const QList<MapData::Poly*> &polygons,
QList<TextItem*> &textItems)
{ {
const Style &s = style(); const Style &s = style();
for (int i = 0; i < _polygons.size(); i++) { for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly *poly = _polygons.at(i); const MapData::Poly *poly = polygons.at(i);
uint type = poly->type()>>16; uint type = poly->type()>>16;
if (!(type == HRBFAC || type == I_TRNBSN if (!(type == HRBFAC || type == I_TRNBSN
@ -319,18 +267,18 @@ void RasterTile::processPolygons(QList<TextItem*> &textItems)
} }
} }
void RasterTile::processPoints(QList<TextItem*> &textItems, void RasterTile::processPoints(QList<MapData::Point*> &points,
QList<TextItem*> &lights) QList<TextItem*> &textItems, QList<TextItem*> &lights)
{ {
const Style &s = style(); const Style &s = style();
PointMap lightsMap, signalsMap; PointMap lightsMap, signalsMap;
int i; int i;
std::sort(_points.begin(), _points.end(), pointLess); std::sort(points.begin(), points.end(), pointLess);
/* Lights & Signals */ /* Lights & Signals */
for (i = 0; i < _points.size(); i++) { for (i = 0; i < points.size(); i++) {
const MapData::Point *point = _points.at(i); const MapData::Point *point = points.at(i);
if (point->type()>>16 == LIGHTS) if (point->type()>>16 == LIGHTS)
lightsMap.insert(point->pos(), point); lightsMap.insert(point->pos(), point);
else if (point->type()>>16 == FOGSIG) else if (point->type()>>16 == FOGSIG)
@ -340,26 +288,25 @@ void RasterTile::processPoints(QList<TextItem*> &textItems,
} }
/* Everything else */ /* Everything else */
for ( ; i < _points.size(); i++) { for ( ; i < points.size(); i++) {
const MapData::Point *point = _points.at(i); const MapData::Point *point = points.at(i);
QPoint pos(ll2xy(point->pos()).toPoint()); QPoint pos(ll2xy(point->pos()).toPoint());
const Style::Point &style = s.point(point->type()); const Style::Point &style = s.point(point->type());
const QString *label = point->label().isEmpty() ? 0 : &(point->label()); const QString *label = point->label().isEmpty() ? 0 : &(point->label());
QImage *rimg = style.img().isNull() const QImage *img = style.img().isNull() ? 0 : &style.img();
? image(point->type(), point->param()) : 0; const QFont *fnt = showLabel(img, _data->zooms(), _zoom, point->type())
const QImage *img = style.img().isNull() ? rimg : &style.img();
const QFont *fnt = showLabel(img, _zooms, _zoom, point->type())
? font(style.textFontSize()) : 0; ? font(style.textFontSize()) : 0;
const QColor *color = &style.textColor(); const QColor *color = &style.textColor();
const QColor *hColor = style.haloColor().isValid() const QColor *hColor = style.haloColor().isValid()
? &style.haloColor() : 0; ? &style.haloColor() : 0;
double rotate = angle(point->type(), point->param());
if ((!label || !fnt) && !img) if ((!label || !fnt) && !img)
continue; continue;
PointItem *item = new PointItem(pos, label, fnt, img, rimg, color, TextPointItem *item = new TextPointItem(pos, label, fnt, img, color,
hColor); hColor, 0, 2, rotate);
if (item->isValid() && !item->collides(textItems)) { if (item->isValid() && !item->collides(textItems)) {
textItems.append(item); textItems.append(item);
if (lightsMap.contains(point->pos())) if (lightsMap.contains(point->pos()))
@ -371,12 +318,13 @@ void RasterTile::processPoints(QList<TextItem*> &textItems,
} }
} }
void RasterTile::processLines(QList<TextItem*> &textItems) void RasterTile::processLines(const QList<MapData::Line*> &lines,
QList<TextItem*> &textItems)
{ {
const Style &s = style(); const Style &s = style();
for (int i = 0; i < _lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
const MapData::Line *line = _lines.at(i); const MapData::Line *line = lines.at(i);
const Style::Line &style = s.line(line->type()); const Style::Line &style = s.line(line->type());
if (style.img().isNull() && style.pen() == Qt::NoPen) if (style.img().isNull() && style.pen() == Qt::NoPen)
@ -396,25 +344,51 @@ void RasterTile::processLines(QList<TextItem*> &textItems)
} }
} }
void RasterTile::fetchData(QList<MapData::Poly*> &polygons,
QList<MapData::Line*> &lines, QList<MapData::Point*> &points)
{
QPoint ttl(_rect.topLeft());
QRectF polyRect(ttl, QPointF(ttl.x() + _rect.width(), ttl.y()
+ _rect.height()));
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
RectC polyRectC(polyRectD.toRectC(_proj, 20));
_data->lines(polyRectC, &lines);
_data->polygons(polyRectC, &polygons);
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
+ TEXT_EXTENT));
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data->points(pointRectD.toRectC(_proj, 20), &points);
}
void RasterTile::render() void RasterTile::render()
{ {
QList<MapData::Line*> lines;
QList<MapData::Poly*> polygons;
QList<MapData::Point*> points;
QList<TextItem*> textItems, lights; QList<TextItem*> textItems, lights;
_pixmap.setDevicePixelRatio(_ratio); _pixmap.setDevicePixelRatio(_ratio);
_pixmap.fill(Qt::transparent); _pixmap.fill(Qt::transparent);
processPolygons(textItems); fetchData(polygons, lines, points);
processPoints(textItems, lights);
processLines(textItems); processPolygons(polygons, textItems);
processPoints(points, textItems, lights);
processLines(lines, textItems);
QPainter painter(&_pixmap); QPainter painter(&_pixmap);
painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
painter.translate(-_rect.x(), -_rect.y()); painter.translate(-_rect.x(), -_rect.y());
drawPolygons(&painter); drawPolygons(&painter, polygons);
drawLines(&painter); drawLines(&painter, lines);
drawArrows(&painter); drawArrows(&painter, polygons);
drawTextItems(&painter, lights); drawTextItems(&painter, lights);
drawTextItems(&painter, textItems); drawTextItems(&painter, textItems);

View File

@ -15,13 +15,10 @@ class RasterTile
{ {
public: public:
RasterTile(const Projection &proj, const Transform &transform, RasterTile(const Projection &proj, const Transform &transform,
const Range &zooms, int zoom, const QRect &rect, qreal ratio, const MapData *data, int zoom, const QRect &rect, qreal ratio)
const QList<MapData::Line*> &lines, const QList<MapData::Poly*> &polygons, : _proj(proj), _transform(transform), _data(data), _zoom(zoom),
const QList<MapData::Point*> &points)
: _proj(proj), _transform(transform), _zooms(zooms), _zoom(zoom),
_rect(rect), _ratio(ratio), _rect(rect), _ratio(ratio),
_pixmap(rect.width() * ratio, rect.height() * ratio), _lines(lines), _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {}
_polygons(polygons), _points(points), _valid(false) {}
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
QPoint xy() const {return _rect.topLeft();} QPoint xy() const {return _rect.topLeft();}
@ -31,44 +28,33 @@ public:
void render(); void render();
private: private:
class PointItem : public TextPointItem void fetchData(QList<MapData::Poly*> &polygons, QList<MapData::Line*> &lines,
{ QList<MapData::Point*> &points);
public:
PointItem(const QPoint &point, const QString *text, const QFont *font,
const QImage *img, const QImage *rimg, const QColor *color,
const QColor *haloColor) : TextPointItem(point, text, font, img, color,
haloColor, 0, 2), _rimg(rimg) {}
~PointItem() {delete _rimg;}
private:
const QImage *_rimg;
};
QPointF ll2xy(const Coordinates &c) const QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));} {return _transform.proj2img(_proj.ll2xy(c));}
QPainterPath painterPath(const Polygon &polygon) const; QPainterPath painterPath(const Polygon &polygon) const;
QPolygonF polyline(const QVector<Coordinates> &path) const; QPolygonF polyline(const QVector<Coordinates> &path) const;
QPolygonF tsslptArrow(const Coordinates &c, qreal angle) const; QPolygonF tsslptArrow(const Coordinates &c, qreal angle) const;
void processPoints(QList<TextItem*> &textItems, QList<TextItem *> &lights); void processPoints(QList<MapData::Point *> &points,
void processLines(QList<TextItem*> &textItems); QList<TextItem*> &textItems, QList<TextItem *> &lights);
void processPolygons(QList<TextItem*> &textItems); void processLines(const QList<MapData::Line *> &lines,
QList<TextItem*> &textItems);
void processPolygons(const QList<MapData::Poly *> &polygons,
QList<TextItem*> &textItems);
void drawBitmapPath(QPainter *painter, const QImage &img, void drawBitmapPath(QPainter *painter, const QImage &img,
const Polygon &polygon); const Polygon &polygon);
void drawArrows(QPainter *painter); void drawArrows(QPainter *painter, const QList<MapData::Poly*> &polygons);
void drawPolygons(QPainter *painter); void drawPolygons(QPainter *painter, const QList<MapData::Poly *> &polygons);
void drawLines(QPainter *painter); void drawLines(QPainter *painter, const QList<MapData::Line *> &lines);
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems); void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
Projection _proj; Projection _proj;
Transform _transform; Transform _transform;
Range _zooms; const MapData *_data;
int _zoom; int _zoom;
QRect _rect; QRect _rect;
qreal _ratio; qreal _ratio;
QPixmap _pixmap; QPixmap _pixmap;
QList<MapData::Line*> _lines;
QList<MapData::Poly*> _polygons;
QList<MapData::Point*> _points;
bool _valid; bool _valid;
}; };

View File

@ -248,6 +248,7 @@ void Style::pointStyle()
_points[SUBTYPE(I_DISMAR, 3)] = _points[SUBTYPE(I_DISMAR, 2)]; _points[SUBTYPE(I_DISMAR, 3)] = _points[SUBTYPE(I_DISMAR, 2)];
_points[SUBTYPE(I_DISMAR, 4)] = _points[SUBTYPE(I_DISMAR, 2)]; _points[SUBTYPE(I_DISMAR, 4)] = _points[SUBTYPE(I_DISMAR, 2)];
_points[TYPE(CGUSTA)] = Point(QImage(":/marine/coast-guard.png")); _points[TYPE(CGUSTA)] = Point(QImage(":/marine/coast-guard.png"));
_points[TYPE(RSCSTA)] = Point(QImage(":/marine/rescue-station.png"));
_points[TYPE(RDOSTA)] = Point(QImage(":/marine/radio.png")); _points[TYPE(RDOSTA)] = Point(QImage(":/marine/radio.png"));
_points[TYPE(RADSTA)] = Point(QImage(":/marine/radar.png")); _points[TYPE(RADSTA)] = Point(QImage(":/marine/radar.png"));
_points[TYPE(RTPBCN)] = Point(QImage(":/marine/radar-transponder.png")); _points[TYPE(RTPBCN)] = Point(QImage(":/marine/radar-transponder.png"));
@ -255,7 +256,9 @@ void Style::pointStyle()
_points[TYPE(I_TRNBSN)] = Point(QImage(":/marine/turning-basin.png")); _points[TYPE(I_TRNBSN)] = Point(QImage(":/marine/turning-basin.png"));
_points[TYPE(I_TRNBSN)].setTextColor(QColor("#eb49eb")); _points[TYPE(I_TRNBSN)].setTextColor(QColor("#eb49eb"));
_points[TYPE(I_WTWGAG)] = Point(QImage(":/marine/gauge.png"), Small); _points[TYPE(I_WTWGAG)] = Point(QImage(":/marine/gauge.png"), Small);
_points[TYPE(RDOCAL)] = Point(QImage(":/marine/radio-call.png"));
_points[TYPE(RDOCAL)].setTextColor(QColor("#eb49eb")); _points[TYPE(RDOCAL)].setTextColor(QColor("#eb49eb"));
_points[TYPE(I_RDOCAL)] = Point(QImage(":/marine/radio-call.png"));
_points[TYPE(I_RDOCAL)].setTextColor(QColor("#eb49eb")); _points[TYPE(I_RDOCAL)].setTextColor(QColor("#eb49eb"));
_points[TYPE(PYLONS)] = Point(QImage(":/marine/pylon.png")); _points[TYPE(PYLONS)] = Point(QImage(":/marine/pylon.png"));
_points[SUBTYPE(I_BERTHS, 6)] = Point(QImage(":/marine/fleeting-area.png")); _points[SUBTYPE(I_BERTHS, 6)] = Point(QImage(":/marine/fleeting-area.png"));
@ -265,6 +268,7 @@ void Style::pointStyle()
_points[TYPE(PILBOP)] = Point(QImage(":/marine/boarding-place.png")); _points[TYPE(PILBOP)] = Point(QImage(":/marine/boarding-place.png"));
_points[TYPE(SISTAT)] = Point(QImage(":/marine/pylon.png")); _points[TYPE(SISTAT)] = Point(QImage(":/marine/pylon.png"));
_points[TYPE(SLCONS)] = Point(QImage(":/marine/construction.png"), Small); _points[TYPE(SLCONS)] = Point(QImage(":/marine/construction.png"), Small);
_points[TYPE(CURENT)] = Point(QImage(":/marine/current.png"));
_points[SUBTYPE(SMCFAC, 7)] = Point(QImage(":/POI/restaurant-11.png"), Small); _points[SUBTYPE(SMCFAC, 7)] = Point(QImage(":/POI/restaurant-11.png"), Small);
_points[SUBTYPE(SMCFAC, 11)] = Point(QImage(":/POI/pharmacy-11.png"), Small); _points[SUBTYPE(SMCFAC, 11)] = Point(QImage(":/POI/pharmacy-11.png"), Small);

View File

@ -13,14 +13,14 @@ bool MapData::polyCb(VectorTile *tile, void *context)
{ {
PolyCTX *ctx = (PolyCTX*)context; PolyCTX *ctx = (PolyCTX*)context;
tile->polys(ctx->rect, ctx->zoom, ctx->polygons, ctx->lines, tile->polys(ctx->rect, ctx->zoom, ctx->polygons, ctx->lines,
ctx->polyCache); ctx->polyCache, ctx->lock);
return true; return true;
} }
bool MapData::pointCb(VectorTile *tile, void *context) bool MapData::pointCb(VectorTile *tile, void *context)
{ {
PointCTX *ctx = (PointCTX*)context; PointCTX *ctx = (PointCTX*)context;
tile->points(ctx->rect, ctx->zoom, ctx->points, ctx->pointCache); tile->points(ctx->rect, ctx->zoom, ctx->points, ctx->pointCache, ctx->lock);
return true; return true;
} }
@ -45,7 +45,7 @@ MapData::~MapData()
void MapData::polys(const RectC &rect, int bits, QList<Poly> *polygons, void MapData::polys(const RectC &rect, int bits, QList<Poly> *polygons,
QList<Poly> *lines) QList<Poly> *lines)
{ {
PolyCTX ctx(rect, zoom(bits), polygons, lines, &_polyCache); PolyCTX ctx(rect, zoom(bits), polygons, lines, &_polyCache, &_lock);
double min[2], max[2]; double min[2], max[2];
min[0] = rect.left(); min[0] = rect.left();
@ -58,7 +58,7 @@ void MapData::polys(const RectC &rect, int bits, QList<Poly> *polygons,
void MapData::points(const RectC &rect, int bits, QList<Point> *points) void MapData::points(const RectC &rect, int bits, QList<Point> *points)
{ {
PointCTX ctx(rect, zoom(bits), points, &_pointCache); PointCTX ctx(rect, zoom(bits), points, &_pointCache, &_lock);
double min[2], max[2]; double min[2], max[2];
min[0] = rect.left(); min[0] = rect.left();

View File

@ -4,6 +4,7 @@
#include <QList> #include <QList>
#include <QPointF> #include <QPointF>
#include <QCache> #include <QCache>
#include <QMutex>
#include <QDebug> #include <QDebug>
#include "common/rectc.h" #include "common/rectc.h"
#include "common/rtree.h" #include "common/rtree.h"
@ -24,6 +25,8 @@ class MapData
{ {
public: public:
struct Poly { struct Poly {
Poly() : oneway(false) {}
/* QPointF insted of Coordinates for performance reasons (no need to /* QPointF insted of Coordinates for performance reasons (no need to
duplicate all the vectors for drawing). Note, that we do not want to duplicate all the vectors for drawing). Note, that we do not want to
ll2xy() the points in the MapData class as this can not be done in ll2xy() the points in the MapData class as this can not be done in
@ -33,6 +36,7 @@ public:
Raster raster; Raster raster;
quint32 type; quint32 type;
RectC boundingRect; RectC boundingRect;
bool oneway;
bool operator<(const Poly &other) const bool operator<(const Poly &other) const
{return type > other.type;} {return type > other.type;}
@ -97,32 +101,37 @@ private:
QList<Poly> lines; QList<Poly> lines;
}; };
typedef QCache<const SubDiv*, Polys> PolyCache;
typedef QCache<const SubDiv*, QList<Point> > PointCache;
struct PolyCTX struct PolyCTX
{ {
PolyCTX(const RectC &rect, const Zoom &zoom, PolyCTX(const RectC &rect, const Zoom &zoom,
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines, QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
QCache<const SubDiv*, MapData::Polys> *polyCache) PolyCache *polyCache, QMutex *lock)
: rect(rect), zoom(zoom), polygons(polygons), lines(lines), : rect(rect), zoom(zoom), polygons(polygons), lines(lines),
polyCache(polyCache) {} polyCache(polyCache), lock(lock) {}
const RectC &rect; const RectC &rect;
const Zoom &zoom; const Zoom &zoom;
QList<MapData::Poly> *polygons; QList<MapData::Poly> *polygons;
QList<MapData::Poly> *lines; QList<MapData::Poly> *lines;
QCache<const SubDiv*, MapData::Polys> *polyCache; PolyCache *polyCache;
QMutex *lock;
}; };
struct PointCTX struct PointCTX
{ {
PointCTX(const RectC &rect, const Zoom &zoom, PointCTX(const RectC &rect, const Zoom &zoom,
QList<MapData::Point> *points, QList<MapData::Point> *points, PointCache *pointCache, QMutex *lock)
QCache<const SubDiv*, QList<MapData::Point> > *pointCache) : rect(rect), zoom(zoom), points(points), pointCache(pointCache),
: rect(rect), zoom(zoom), points(points), pointCache(pointCache) {} lock(lock) {}
const RectC &rect; const RectC &rect;
const Zoom &zoom; const Zoom &zoom;
QList<MapData::Point> *points; QList<MapData::Point> *points;
QCache<const SubDiv*, QList<MapData::Point> > *pointCache; PointCache *pointCache;
QMutex *lock;
}; };
const Zoom &zoom(int bits) const; const Zoom &zoom(int bits) const;
@ -130,8 +139,9 @@ private:
static bool polyCb(VectorTile *tile, void *context); static bool polyCb(VectorTile *tile, void *context);
static bool pointCb(VectorTile *tile, void *context); static bool pointCb(VectorTile *tile, void *context);
QCache<const SubDiv*, Polys> _polyCache; PolyCache _polyCache;
QCache<const SubDiv*, QList<Point> > _pointCache; PointCache _pointCache;
QMutex _lock;
friend class VectorTile; friend class VectorTile;
}; };

View File

@ -479,6 +479,8 @@ bool NETFile::link(const SubDiv *subdiv, quint32 shift, Handle &hdl,
if (lbl) if (lbl)
linkLabel(hdl, linkOffset, lbl, lblHdl, poly.label); linkLabel(hdl, linkOffset, lbl, lblHdl, poly.label);
if ((linkInfo.flags >> 3) & 1)
poly.oneway = true;
lines->append(poly); lines->append(poly);

View File

@ -5,12 +5,14 @@
#include "map/textpathitem.h" #include "map/textpathitem.h"
#include "map/textpointitem.h" #include "map/textpointitem.h"
#include "map/bitmapline.h" #include "map/bitmapline.h"
#include "map/rectd.h"
#include "style.h" #include "style.h"
#include "lblfile.h" #include "lblfile.h"
#include "rastertile.h" #include "rastertile.h"
using namespace IMG; using namespace IMG;
#define TEXT_EXTENT 160
#define ICON_PADDING 2 #define ICON_PADDING 2
#define AREA(rect) \ #define AREA(rect) \
@ -23,6 +25,12 @@ static const QColor shieldBgColor1("#dd3e3e");
static const QColor shieldBgColor2("#379947"); static const QColor shieldBgColor2("#379947");
static const QColor shieldBgColor3("#4a7fc1"); static const QColor shieldBgColor3("#4a7fc1");
static const QImage *arrow()
{
static QImage img(":/map/arrow.png");
return &img;
}
static QFont pixelSizeFont(int pixelSize) static QFont pixelSizeFont(int pixelSize)
{ {
QFont f; QFont f;
@ -147,40 +155,6 @@ static bool rectNearPolygon(const QPolygonF &polygon, const QRectF &rect)
|| polygon.containsPoint(rect.bottomRight(), Qt::OddEvenFill))); || polygon.containsPoint(rect.bottomRight(), Qt::OddEvenFill)));
} }
void RasterTile::render()
{
QList<TextItem*> textItems;
ll2xy(_polygons);
ll2xy(_lines);
ll2xy(_points);
processPoints(textItems);
processPolygons(textItems);
processLines(textItems);
_pixmap.setDevicePixelRatio(_ratio);
_pixmap.fill(Qt::transparent);
QPainter painter(&_pixmap);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(-_rect.x(), -_rect.y());
drawPolygons(&painter);
drawLines(&painter);
drawTextItems(&painter, textItems);
qDeleteAll(textItems);
_valid = true;
//painter.setPen(Qt::red);
//painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(QRect(_xy, _pixmap.size()));
}
void RasterTile::ll2xy(QList<MapData::Poly> &polys) void RasterTile::ll2xy(QList<MapData::Poly> &polys)
{ {
for (int i = 0; i < polys.size(); i++) { for (int i = 0; i < polys.size(); i++) {
@ -200,14 +174,15 @@ void RasterTile::ll2xy(QList<MapData::Point> &points)
} }
} }
void RasterTile::drawPolygons(QPainter *painter) void RasterTile::drawPolygons(QPainter *painter,
const QList<MapData::Poly> &polygons)
{ {
QCache<const LBLFile *, SubFile::Handle> hc(16); QCache<const LBLFile *, SubFile::Handle> hc(16);
for (int n = 0; n < _style->drawOrder().size(); n++) { for (int n = 0; n < _data->style()->drawOrder().size(); n++) {
for (int i = 0; i < _polygons.size(); i++) { for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly &poly = _polygons.at(i); const MapData::Poly &poly = polygons.at(i);
if (poly.type != _style->drawOrder().at(n)) if (poly.type != _data->style()->drawOrder().at(n))
continue; continue;
if (poly.raster.isValid()) { if (poly.raster.isValid()) {
@ -237,7 +212,7 @@ void RasterTile::drawPolygons(QPainter *painter)
//painter->setBrush(Qt::NoBrush); //painter->setBrush(Qt::NoBrush);
//painter->drawRect(QRectF(tl, br)); //painter->drawRect(QRectF(tl, br));
} else { } else {
const Style::Polygon &style = _style->polygon(poly.type); const Style::Polygon &style = _data->style()->polygon(poly.type);
painter->setPen(style.pen()); painter->setPen(style.pen());
painter->setBrush(style.brush()); painter->setBrush(style.brush());
@ -247,13 +222,13 @@ void RasterTile::drawPolygons(QPainter *painter)
} }
} }
void RasterTile::drawLines(QPainter *painter) void RasterTile::drawLines(QPainter *painter, const QList<MapData::Poly> &lines)
{ {
painter->setBrush(Qt::NoBrush); painter->setBrush(Qt::NoBrush);
for (int i = 0; i < _lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i); const MapData::Poly &poly = lines.at(i);
const Style::Line &style = _style->line(poly.type); const Style::Line &style = _data->style()->line(poly.type);
if (style.background() == Qt::NoPen) if (style.background() == Qt::NoPen)
continue; continue;
@ -262,9 +237,9 @@ void RasterTile::drawLines(QPainter *painter)
painter->drawPolyline(poly.points); painter->drawPolyline(poly.points);
} }
for (int i = 0; i < _lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i); const MapData::Poly &poly = lines.at(i);
const Style::Line &style = _style->line(poly.type); const Style::Line &style = _data->style()->line(poly.type);
if (!style.img().isNull()) if (!style.img().isNull())
BitmapLine::draw(painter, poly.points, style.img()); BitmapLine::draw(painter, poly.points, style.img());
@ -295,13 +270,14 @@ static void removeDuplicitLabel(QList<TextItem *> &labels, const QString &text,
} }
} }
void RasterTile::processPolygons(QList<TextItem*> &textItems) void RasterTile::processPolygons(const QList<MapData::Poly> &polygons,
QList<TextItem*> &textItems)
{ {
QSet<QString> set; QSet<QString> set;
QList<TextItem *> labels; QList<TextItem *> labels;
for (int i = 0; i < _polygons.size(); i++) { for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly &poly = _polygons.at(i); const MapData::Poly &poly = polygons.at(i);
bool exists = set.contains(poly.label.text()); bool exists = set.contains(poly.label.text());
if (poly.label.text().isEmpty()) if (poly.label.text().isEmpty())
@ -310,7 +286,7 @@ void RasterTile::processPolygons(QList<TextItem*> &textItems)
if (_zoom <= 23 && (Style::isWaterArea(poly.type) if (_zoom <= 23 && (Style::isWaterArea(poly.type)
|| Style::isMilitaryArea(poly.type) || Style::isMilitaryArea(poly.type)
|| Style::isNatureReserve(poly.type))) { || Style::isNatureReserve(poly.type))) {
const Style::Polygon &style = _style->polygon(poly.type); const Style::Polygon &style = _data->style()->polygon(poly.type);
TextPointItem *item = new TextPointItem( TextPointItem *item = new TextPointItem(
centroid(poly.points).toPoint(), &poly.label.text(), poiFont(), centroid(poly.points).toPoint(), &poly.label.text(), poiFont(),
0, &style.brush().color(), &haloColor); 0, &style.brush().color(), &haloColor);
@ -331,20 +307,22 @@ void RasterTile::processPolygons(QList<TextItem*> &textItems)
textItems.append(labels); textItems.append(labels);
} }
void RasterTile::processLines(QList<TextItem*> &textItems) void RasterTile::processLines(QList<MapData::Poly> &lines,
QList<TextItem*> &textItems)
{ {
std::stable_sort(_lines.begin(), _lines.end()); std::stable_sort(lines.begin(), lines.end());
if (_zoom >= 22) if (_zoom >= 22)
processStreetNames(textItems); processStreetNames(lines, textItems);
processShields(textItems); processShields(lines, textItems);
} }
void RasterTile::processStreetNames(QList<TextItem*> &textItems) void RasterTile::processStreetNames(const QList<MapData::Poly> &lines,
QList<TextItem*> &textItems)
{ {
for (int i = 0; i < _lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i); const MapData::Poly &poly = lines.at(i);
const Style::Line &style = _style->line(poly.type); const Style::Line &style = _data->style()->line(poly.type);
if (style.img().isNull() && style.foreground() == Qt::NoPen) if (style.img().isNull() && style.foreground() == Qt::NoPen)
continue; continue;
@ -355,18 +333,30 @@ void RasterTile::processStreetNames(QList<TextItem*> &textItems)
const QFont *fnt = font(style.textFontSize(), Style::Small); const QFont *fnt = font(style.textFontSize(), Style::Small);
const QColor *color = style.textColor().isValid() const QColor *color = style.textColor().isValid()
? &style.textColor() : 0; ? &style.textColor() : 0;
const QColor *hColor = Style::isContourLine(poly.type) ? 0 : &haloColor;
const QImage *img = poly.oneway ? arrow() : 0;
TextPathItem *item = new TextPathItem(poly.points, TextPathItem *item = new TextPathItem(poly.points,
&poly.label.text(), _rect, fnt, color, Style::isContourLine(poly.type) &poly.label.text(), _rect, fnt, color, hColor, img);
? 0 : &haloColor);
if (item->isValid() && !item->collides(textItems)) if (item->isValid() && !item->collides(textItems))
textItems.append(item); textItems.append(item);
else else {
delete item; delete item;
if (img) {
TextPathItem *item = new TextPathItem(poly.points, 0, _rect, 0,
0, 0, img);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
}
} }
} }
void RasterTile::processShields(QList<TextItem*> &textItems) void RasterTile::processShields(const QList<MapData::Poly> &lines,
QList<TextItem*> &textItems)
{ {
for (int type = FIRST_SHIELD; type <= LAST_SHIELD; type++) { for (int type = FIRST_SHIELD; type <= LAST_SHIELD; type++) {
if (minShieldZoom(static_cast<Shield::Type>(type)) > _zoom) if (minShieldZoom(static_cast<Shield::Type>(type)) > _zoom)
@ -375,8 +365,8 @@ void RasterTile::processShields(QList<TextItem*> &textItems)
QHash<Shield, QPolygonF> shields; QHash<Shield, QPolygonF> shields;
QHash<Shield, const Shield*> sp; QHash<Shield, const Shield*> sp;
for (int i = 0; i < _lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i); const MapData::Poly &poly = lines.at(i);
const Shield &shield = poly.label.shield(); const Shield &shield = poly.label.shield();
if (!shield.isValid() || shield.type() != type if (!shield.isValid() || shield.type() != type
|| !Style::isMajorRoad(poly.type)) || !Style::isMajorRoad(poly.type))
@ -429,13 +419,14 @@ void RasterTile::processShields(QList<TextItem*> &textItems)
} }
} }
void RasterTile::processPoints(QList<TextItem*> &textItems) void RasterTile::processPoints(QList<MapData::Point> &points,
QList<TextItem*> &textItems)
{ {
std::sort(_points.begin(), _points.end()); std::sort(points.begin(), points.end());
for (int i = 0; i < _points.size(); i++) { for (int i = 0; i < points.size(); i++) {
const MapData::Point &point = _points.at(i); const MapData::Point &point = points.at(i);
const Style::Point &style = _style->point(point.type); const Style::Point &style = _data->style()->point(point.type);
bool poi = Style::isPOI(point.type); bool poi = Style::isPOI(point.type);
const QString *label = point.label.text().isEmpty() const QString *label = point.label.text().isEmpty()
@ -446,12 +437,14 @@ void RasterTile::processPoints(QList<TextItem*> &textItems)
: font(style.textFontSize()); : font(style.textFontSize());
const QColor *color = style.textColor().isValid() const QColor *color = style.textColor().isValid()
? &style.textColor() : &textColor; ? &style.textColor() : &textColor;
const QColor *hcolor = Style::isDepthPoint(point.type)
? 0 : &haloColor;
if ((!label || !fnt) && !img) if ((!label || !fnt) && !img)
continue; continue;
TextPointItem *item = new TextPointItem(QPoint(point.coordinates.lon(), TextPointItem *item = new TextPointItem(QPoint(point.coordinates.lon(),
point.coordinates.lat()), label, fnt, img, color, &haloColor, 0, point.coordinates.lat()), label, fnt, img, color, hcolor, 0,
ICON_PADDING); ICON_PADDING);
if (item->isValid() && !item->collides(textItems)) if (item->isValid() && !item->collides(textItems))
textItems.append(item); textItems.append(item);
@ -459,3 +452,60 @@ void RasterTile::processPoints(QList<TextItem*> &textItems)
delete item; delete item;
} }
} }
void RasterTile::fetchData(QList<MapData::Poly> &polygons,
QList<MapData::Poly> &lines, QList<MapData::Point> &points)
{
QPoint ttl(_rect.topLeft());
QRectF polyRect(ttl, QPointF(ttl.x() + _rect.width(), ttl.y()
+ _rect.height()));
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
_data->polys(polyRectD.toRectC(_proj, 20), _zoom,
&polygons, &lines);
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
+ TEXT_EXTENT));
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data->points(pointRectD.toRectC(_proj, 20), _zoom, &points);
}
void RasterTile::render()
{
QList<MapData::Poly> polygons;
QList<MapData::Poly> lines;
QList<MapData::Point> points;
QList<TextItem*> textItems;
fetchData(polygons, lines, points);
ll2xy(polygons);
ll2xy(lines);
ll2xy(points);
processPoints(points, textItems);
processPolygons(polygons, textItems);
processLines(lines, textItems);
_pixmap.setDevicePixelRatio(_ratio);
_pixmap.fill(Qt::transparent);
QPainter painter(&_pixmap);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(-_rect.x(), -_rect.y());
drawPolygons(&painter, polygons);
drawLines(&painter, lines);
drawTextItems(&painter, textItems);
qDeleteAll(textItems);
_valid = true;
//painter.setPen(Qt::red);
//painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(_rect);
}

View File

@ -17,14 +17,11 @@ class Style;
class RasterTile class RasterTile
{ {
public: public:
RasterTile(const Projection &proj, const Transform &transform, RasterTile(const Projection &proj, const Transform &transform, MapData *data,
const Style *style, int zoom, const QRect &rect, qreal ratio, int zoom, const QRect &rect, qreal ratio, const QString &key)
const QString &key, const QList<MapData::Poly> &polygons, : _proj(proj), _transform(transform), _data(data), _zoom(zoom),
const QList<MapData::Poly> &lines, QList<MapData::Point> &points) _rect(rect), _ratio(ratio), _key(key),
: _proj(proj), _transform(transform), _style(style), _zoom(zoom), _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {}
_rect(rect), _ratio(ratio), _key(key),
_pixmap(rect.width() * ratio, rect.height() * ratio), _polygons(polygons),
_lines(lines), _points(points), _valid(false) {}
const QString &key() const {return _key;} const QString &key() const {return _key;}
QPoint xy() const {return _rect.topLeft();} QPoint xy() const {return _rect.topLeft();}
@ -34,32 +31,36 @@ public:
void render(); void render();
private: private:
void fetchData(QList<MapData::Poly> &polygons, QList<MapData::Poly> &lines,
QList<MapData::Point> &points);
QPointF ll2xy(const Coordinates &c) const QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));} {return _transform.proj2img(_proj.ll2xy(c));}
void ll2xy(QList<MapData::Poly> &polys); void ll2xy(QList<MapData::Poly> &polys);
void ll2xy(QList<MapData::Point> &points); void ll2xy(QList<MapData::Point> &points);
void drawPolygons(QPainter *painter); void drawPolygons(QPainter *painter, const QList<MapData::Poly> &polygons);
void drawLines(QPainter *painter); void drawLines(QPainter *painter, const QList<MapData::Poly> &lines);
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems); void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
void processPolygons(QList<TextItem *> &textItems); void processPolygons(const QList<MapData::Poly> &polygons,
void processLines(QList<TextItem*> &textItems); QList<TextItem *> &textItems);
void processPoints(QList<TextItem*> &textItems); void processLines(QList<MapData::Poly> &lines,
void processShields(QList<TextItem*> &textItems); QList<TextItem*> &textItems);
void processStreetNames(QList<TextItem*> &textItems); void processPoints(QList<MapData::Point> &points,
QList<TextItem*> &textItems);
void processShields(const QList<MapData::Poly> &lines,
QList<TextItem*> &textItems);
void processStreetNames(const QList<MapData::Poly> &lines,
QList<TextItem*> &textItems);
Projection _proj; Projection _proj;
Transform _transform; Transform _transform;
const Style *_style; MapData *_data;
int _zoom; int _zoom;
QRect _rect; QRect _rect;
qreal _ratio; qreal _ratio;
QString _key; QString _key;
QPixmap _pixmap; QPixmap _pixmap;
QList<MapData::Poly> _polygons;
QList<MapData::Poly> _lines;
QList<MapData::Point> _points;
bool _valid; bool _valid;
}; };

View File

@ -1,5 +1,6 @@
#include "common/rectc.h" #include "common/rectc.h"
#include "common/garmin.h" #include "common/garmin.h"
#include "common/hash.h"
#include "deltastream.h" #include "deltastream.h"
#include "huffmanstream.h" #include "huffmanstream.h"
#include "style.h" #include "style.h"
@ -13,12 +14,10 @@ using namespace IMG;
#define MASK(bits) ((1U << (bits)) - 1U) #define MASK(bits) ((1U << (bits)) - 1U)
static quint64 pointId(const QPoint &pos, quint32 type) static quint64 pointId(const QPoint &pos, quint32 type, const QString &label)
{ {
quint64 id; quint64 hash = qHash(pos) ^ qHash(label);
quint64 id = ((quint64)type)<<40 | (hash & 0xFFFFFFFFFF);
uint hash = (uint)qHash(QPair<int, int>(pos.x(), pos.y()));
id = ((quint64)type)<<32 | hash;
// Increase rendering priorities for some special items // Increase rendering priorities for some special items
if (!Style::isCountry(type) && !Style::isMarina(type)) if (!Style::isCountry(type) && !Style::isMarina(type))
@ -293,6 +292,8 @@ bool RGNFile::polyObjects(Handle &hdl, const SubDiv *subdiv,
poly.type = (segmentType == Polygon) poly.type = (segmentType == Polygon)
? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8; ? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8;
if (segmentType == Line && type & 0x40)
poly.oneway = true;
QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()), QPoint pos(subdiv->lon() + LS(lon, 24-subdiv->bits()),
@ -484,11 +485,11 @@ bool RGNFile::pointObjects(Handle &hdl, const SubDiv *subdiv,
point.type = (quint16)type<<8 | subtype; point.type = (quint16)type<<8 | subtype;
point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y())); point.coordinates = Coordinates(toWGS24(pos.x()), toWGS24(pos.y()));
point.id = pointId(pos, point.type);
if (lbl && (labelPtr & 0x3FFFFF)) if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF,
labelPtr & 0x400000, !(Style::isCountry(point.type) labelPtr & 0x400000, !(Style::isCountry(point.type)
|| Style::isState(point.type)), Style::isSpot(point.type)); || Style::isState(point.type)), Style::isSpot(point.type));
point.id = pointId(pos, point.type, point.label.text());
points->append(point); points->append(point);
} }
@ -537,9 +538,9 @@ bool RGNFile::extPointObjects(Handle &hdl, const SubDiv *subdiv,
continue; continue;
point.coordinates = Coordinates(toWGS24(p.x()), toWGS24(p.y())); point.coordinates = Coordinates(toWGS24(p.x()), toWGS24(p.y()));
point.id = pointId(p, point.type);
if (lbl && (labelPtr & 0x3FFFFF)) if (lbl && (labelPtr & 0x3FFFFF))
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF); point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
point.id = pointId(p, point.type, point.label.text());
points->append(point); points->append(point);
} }

View File

@ -102,13 +102,18 @@ void VectorTile::clear()
void VectorTile::polys(const RectC &rect, const Zoom &zoom, void VectorTile::polys(const RectC &rect, const Zoom &zoom,
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines, QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
QCache<const SubDiv *, MapData::Polys> *polyCache) MapData::PolyCache *polyCache, QMutex *lock)
{ {
SubFile::Handle *rgnHdl = 0, *lblHdl = 0, *netHdl = 0, *nodHdl = 0, SubFile::Handle *rgnHdl = 0, *lblHdl = 0, *netHdl = 0, *nodHdl = 0,
*nodHdl2 = 0; *nodHdl2 = 0;
if (_loaded < 0) lock->lock();
if (_loaded < 0) {
lock->unlock();
return; return;
}
if (!_loaded) { if (!_loaded) {
rgnHdl = new SubFile::Handle(_rgn); rgnHdl = new SubFile::Handle(_rgn);
lblHdl = new SubFile::Handle(_lbl); lblHdl = new SubFile::Handle(_lbl);
@ -116,6 +121,7 @@ void VectorTile::polys(const RectC &rect, const Zoom &zoom,
nodHdl = new SubFile::Handle(_nod); nodHdl = new SubFile::Handle(_nod);
if (!load(*rgnHdl, *lblHdl, *netHdl, *nodHdl)) { if (!load(*rgnHdl, *lblHdl, *netHdl, *nodHdl)) {
lock->unlock();
delete rgnHdl; delete lblHdl; delete netHdl; delete nodHdl; delete rgnHdl; delete lblHdl; delete netHdl; delete nodHdl;
return; return;
} }
@ -166,17 +172,24 @@ void VectorTile::polys(const RectC &rect, const Zoom &zoom,
} }
} }
lock->unlock();
delete rgnHdl; delete lblHdl; delete netHdl; delete nodHdl; delete nodHdl2; delete rgnHdl; delete lblHdl; delete netHdl; delete nodHdl; delete nodHdl2;
} }
void VectorTile::points(const RectC &rect, const Zoom &zoom, void VectorTile::points(const RectC &rect, const Zoom &zoom,
QList<MapData::Point> *points, QCache<const SubDiv *, QList<MapData::Point> *points, QCache<const SubDiv *,
QList<MapData::Point> > *pointCache) QList<MapData::Point> > *pointCache, QMutex *lock)
{ {
SubFile::Handle *rgnHdl = 0, *lblHdl = 0; SubFile::Handle *rgnHdl = 0, *lblHdl = 0;
if (_loaded < 0) lock->lock();
if (_loaded < 0) {
lock->unlock();
return; return;
}
if (!_loaded) { if (!_loaded) {
rgnHdl = new SubFile::Handle(_rgn); rgnHdl = new SubFile::Handle(_rgn);
lblHdl = new SubFile::Handle(_lbl); lblHdl = new SubFile::Handle(_lbl);
@ -184,6 +197,7 @@ void VectorTile::points(const RectC &rect, const Zoom &zoom,
SubFile::Handle netHdl(_net); SubFile::Handle netHdl(_net);
if (!load(*rgnHdl, *lblHdl, netHdl, nodHdl)) { if (!load(*rgnHdl, *lblHdl, netHdl, nodHdl)) {
lock->unlock();
delete rgnHdl; delete lblHdl; delete rgnHdl; delete lblHdl;
return; return;
} }
@ -217,6 +231,8 @@ void VectorTile::points(const RectC &rect, const Zoom &zoom,
copyPoints(rect, pl, points); copyPoints(rect, pl, points);
} }
lock->unlock();
delete rgnHdl; delete lblHdl; delete rgnHdl; delete lblHdl;
} }

View File

@ -29,10 +29,10 @@ public:
void polys(const RectC &rect, const Zoom &zoom, void polys(const RectC &rect, const Zoom &zoom,
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines, QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
QCache<const SubDiv *, MapData::Polys> *polyCache); MapData::PolyCache *polyCache, QMutex *lock);
void points(const RectC &rect, const Zoom &zoom, void points(const RectC &rect, const Zoom &zoom,
QList<MapData::Point> *points, QCache<const SubDiv*, QList<MapData::Point> *points, QCache<const SubDiv*,
QList<MapData::Point> > *pointCache); QList<MapData::Point> > *pointCache, QMutex *lock);
static bool isTileFile(SubFile::Type type) static bool isTileFile(SubFile::Type type)
{ {

View File

@ -252,8 +252,13 @@ AQMMap::AQMMap(const QString &fileName, QObject *parent)
_valid = true; _valid = true;
} }
void AQMMap::load() void AQMMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
_file.open(QIODevice::ReadOnly); _file.open(QIODevice::ReadOnly);
} }
@ -305,12 +310,6 @@ int AQMMap::zoomOut()
return _zoom; return _zoom;
} }
void AQMMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
{
Q_UNUSED(deviceRatio);
_mapRatio = mapRatio;
}
QPointF AQMMap::ll2xy(const Coordinates &c) QPointF AQMMap::ll2xy(const Coordinates &c)
{ {
const Zoom &z = _zooms.at(_zoom); const Zoom &z = _zooms.at(_zoom);
@ -406,7 +405,7 @@ void AQMMap::drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp)
painter->drawPixmap(tp, pixmap); painter->drawPixmap(tp, pixmap);
} }
Map *AQMMap::create(const QString &path, const Projection &, bool *isDir) Map *AQMMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -17,7 +17,7 @@ public:
QString name() const {return _name;} QString name() const {return _name;}
QRectF bounds(); QRectF bounds();
RectC llBounds() {return _bounds;} RectC llBounds(const Projection &) {return _bounds;}
qreal resolution(const QRectF &rect); qreal resolution(const QRectF &rect);
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
@ -26,9 +26,9 @@ public:
int zoomIn(); int zoomIn();
int zoomOut(); int zoomOut();
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
QPointF ll2xy(const Coordinates &c); QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p); Coordinates xy2ll(const QPointF &p);
@ -38,7 +38,7 @@ public:
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
struct File { struct File {

View File

@ -14,12 +14,7 @@
static bool resCmp(OziMap *m1, OziMap *m2) static bool resCmp(OziMap *m1, OziMap *m2)
{ {
qreal r1, r2; return m1->resolution(m1->bounds()) > m2->resolution(m2->bounds());
r1 = m1->resolution(m1->bounds());
r2 = m2->resolution(m2->bounds());
return r1 > r2;
} }
static bool xCmp(OziMap *m1, OziMap *m2) static bool xCmp(OziMap *m1, OziMap *m2)
@ -38,7 +33,7 @@ void Atlas::computeZooms()
_zooms.append(Zoom(0, _maps.count() - 1)); _zooms.append(Zoom(0, _maps.count() - 1));
for (int i = 1; i < _maps.count(); i++) { for (int i = 1; i < _maps.count(); i++) {
qreal last = _maps.at(i-1)->resolution(_maps.at(i)->bounds()); qreal last = _maps.at(i-1)->resolution(_maps.at(i-1)->bounds());
qreal cur = _maps.at(i)->resolution(_maps.at(i)->bounds()); qreal cur = _maps.at(i)->resolution(_maps.at(i)->bounds());
if (cur < last * ZOOM_THRESHOLD) { if (cur < last * ZOOM_THRESHOLD) {
_zooms.last().last = i-1; _zooms.last().last = i-1;
@ -136,18 +131,17 @@ Atlas::Atlas(const QString &fileName, bool TAR, QObject *parent)
return; return;
} }
computeZooms();
computeBounds();
_valid = true; _valid = true;
} }
void Atlas::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) RectC Atlas::llBounds(const Projection &proj)
{ {
for (int i = 0; i < _maps.size(); i++) RectC bounds;
_maps[i]->setDevicePixelRatio(deviceRatio, mapRatio);
computeBounds(); for (int i = 0; i < _maps.size(); i++)
bounds |= _maps.at(i)->llBounds(proj);
return bounds;
} }
QRectF Atlas::bounds() QRectF Atlas::bounds()
@ -277,20 +271,31 @@ void Atlas::draw(QPainter *painter, const QRectF &rect, int mapIndex,
const QPointF offset = _bounds.at(mapIndex).xy.topLeft(); const QPointF offset = _bounds.at(mapIndex).xy.topLeft();
QRectF pr = QRectF(rect.topLeft() - offset, rect.size()); QRectF pr = QRectF(rect.topLeft() - offset, rect.size());
map->load();
painter->translate(offset); painter->translate(offset);
map->draw(painter, pr, flags); map->draw(painter, pr, flags);
painter->translate(-offset); painter->translate(-offset);
} }
void Atlas::load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi)
{
for (int i = 0; i < _maps.count(); i++)
_maps.at(i)->load(in, out, deviceRatio, hidpi);
computeZooms();
computeBounds();
}
void Atlas::unload() void Atlas::unload()
{ {
for (int i = 0; i < _maps.count(); i++) for (int i = 0; i < _maps.count(); i++)
_maps.at(i)->unload(); _maps.at(i)->unload();
_zooms.clear();
_bounds.clear();
} }
Map *Atlas::createTAR(const QString &path, const Projection &, bool *isDir) Map *Atlas::createTAR(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = true; *isDir = true;
@ -298,7 +303,7 @@ Map *Atlas::createTAR(const QString &path, const Projection &, bool *isDir)
return new Atlas(path, true); return new Atlas(path, true);
} }
Map *Atlas::createTBA(const QString &path, const Projection &, bool *isDir) Map *Atlas::createTBA(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = true; *isDir = true;

View File

@ -3,6 +3,7 @@
#include "map.h" #include "map.h"
#include "rectd.h" #include "rectd.h"
#include "projection.h"
class OziMap; class OziMap;
@ -16,6 +17,7 @@ public:
QString name() const {return _name;} QString name() const {return _name;}
QRectF bounds(); QRectF bounds();
RectC llBounds(const Projection &proj);
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
void setZoom(int zoom); void setZoom(int zoom);
@ -28,14 +30,15 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *createTAR(const QString &path, const Projection &, bool *isDir); static Map *createTAR(const QString &path, bool *isDir);
static Map *createTBA(const QString &path, const Projection &, bool *isDir); static Map *createTBA(const QString &path, bool *isDir);
private: private:
struct Zoom { struct Zoom {

View File

@ -390,7 +390,7 @@ QImage BSBMap::readImage()
} }
BSBMap::BSBMap(const QString &fileName, QObject *parent) BSBMap::BSBMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _img(0), _ratio(1.0), _dataOffset(-1), _valid(false) : Map(fileName, parent), _img(0), _mapRatio(1.0), _dataOffset(-1), _valid(false)
{ {
QFile file(fileName); QFile file(fileName);
@ -414,19 +414,19 @@ BSBMap::~BSBMap()
QPointF BSBMap::ll2xy(const Coordinates &c) QPointF BSBMap::ll2xy(const Coordinates &c)
{ {
return QPointF(_transform.proj2img(_projection.ll2xy(c))) / _ratio; return QPointF(_transform.proj2img(_projection.ll2xy(c))) / _mapRatio;
} }
Coordinates BSBMap::xy2ll(const QPointF &p) Coordinates BSBMap::xy2ll(const QPointF &p)
{ {
return _projection.xy2ll(_transform.img2proj(p * _ratio)); return _projection.xy2ll(_transform.img2proj(p * _mapRatio));
} }
QRectF BSBMap::bounds() QRectF BSBMap::bounds()
{ {
return _skewSize.isValid() return _skewSize.isValid()
? QRectF(QPointF(0, 0), _skewSize / _ratio) ? QRectF(QPointF(0, 0), _skewSize / _mapRatio)
: QRectF(QPointF(0, 0), _size / _ratio); : QRectF(QPointF(0, 0), _size / _mapRatio);
} }
void BSBMap::draw(QPainter *painter, const QRectF &rect, Flags flags) void BSBMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
@ -435,17 +435,14 @@ void BSBMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
_img->draw(painter, rect, flags); _img->draw(painter, rect, flags);
} }
void BSBMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) void BSBMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(deviceRatio); Q_UNUSED(in);
Q_UNUSED(out);
_ratio = mapRatio; _mapRatio = hidpi ? deviceRatio : 1.0;
if (_img)
_img->setDevicePixelRatio(_ratio);
}
void BSBMap::load()
{
if (!_img) { if (!_img) {
if (_skew > 0.0 && _skew < 360.0) { if (_skew > 0.0 && _skew < 360.0) {
QTransform matrix; QTransform matrix;
@ -454,6 +451,9 @@ void BSBMap::load()
} else } else
_img = new Image(readImage()); _img = new Image(readImage());
} }
if (_img)
_img->setDevicePixelRatio(_mapRatio);
} }
void BSBMap::unload() void BSBMap::unload()
@ -462,7 +462,7 @@ void BSBMap::unload()
_img = 0; _img = 0;
} }
Map *BSBMap::create(const QString &path, const Projection &, bool *isMap) Map *BSBMap::create(const QString &path, bool *isMap)
{ {
if (isMap) if (isMap)
*isMap = false; *isMap = false;

View File

@ -25,14 +25,14 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isMap); static Map *create(const QString &path, bool *isMap);
private: private:
bool parseBSB(const QByteArray &line); bool parseBSB(const QByteArray &line);
@ -56,7 +56,7 @@ private:
Image *_img; Image *_img;
QSize _size; QSize _size;
QSize _skewSize; QSize _skewSize;
qreal _ratio; qreal _mapRatio;
qint64 _dataOffset; qint64 _dataOffset;
QVector<QRgb> _palette; QVector<QRgb> _palette;

View File

@ -14,7 +14,7 @@ public:
QString name() const {return QString();} QString name() const {return QString();}
QRectF bounds(); QRectF bounds();
RectC llBounds() {return OSM::BOUNDS;} RectC llBounds(const Projection &) {return OSM::BOUNDS;}
qreal resolution(const QRectF &rect); qreal resolution(const QRectF &rect);
int zoom() const {return _zoom;} int zoom() const {return _zoom;}

View File

@ -6,11 +6,10 @@
#include "pcs.h" #include "pcs.h"
#include "encmap.h" #include "encmap.h"
#define TILE_SIZE 512
#define TEXT_EXTENT 160
using namespace ENC; using namespace ENC;
#define TILE_SIZE 512
ENCMap::ENCMap(const QString &fileName, QObject *parent) ENCMap::ENCMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _data(fileName), _projection(PCS::pcs(3857)), : Map(fileName, parent), _data(fileName), _projection(PCS::pcs(3857)),
@ -22,9 +21,16 @@ ENCMap::ENCMap(const QString &fileName, QObject *parent)
} }
} }
void ENCMap::load() void ENCMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(hidpi);
_tileRatio = deviceRatio;
_projection = out;
_data.load(); _data.load();
QPixmapCache::clear();
} }
void ENCMap::unload() void ENCMap::unload()
@ -173,32 +179,9 @@ void ENCMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QPixmap pm; QPixmap pm;
if (QPixmapCache::find(key(_zoom, ttl), &pm)) if (QPixmapCache::find(key(_zoom, ttl), &pm))
painter->drawPixmap(ttl, pm); painter->drawPixmap(ttl, pm);
else { else
QList<MapData::Poly*> polygons; tiles.append(RasterTile(_projection, _transform, &_data,
QList<MapData::Line*> lines; _zoom, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio));
QList<MapData::Point*> points;
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
ttl.y() + TILE_SIZE));
polyRect &= _bounds;
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
RectC polyRectC(polyRectD.toRectC(_projection, 20));
_data.lines(polyRectC, &lines);
_data.polygons(polyRectC, &polygons);
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;
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data.points(pointRectD.toRectC(_projection, 20), &points);
tiles.append(RasterTile(_projection, _transform, _data.zooms(),
_zoom, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio,
lines, polygons, points));
}
} }
} }
@ -218,24 +201,7 @@ void ENCMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
} }
} }
void ENCMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) Map *ENCMap::create(const QString &path, bool *isMap)
{
Q_UNUSED(mapRatio);
_tileRatio = deviceRatio;
}
void ENCMap::setOutputProjection(const Projection &projection)
{
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;
updateTransform();
QPixmapCache::clear();
}
Map *ENCMap::create(const QString &path, const Projection &, bool *isMap)
{ {
if (isMap) if (isMap)
*isMap = false; *isMap = false;

View File

@ -53,7 +53,7 @@ public:
QString name() const {return _data.name();} QString name() const {return _data.name();}
QRectF bounds() {return _bounds;} QRectF bounds() {return _bounds;}
RectC llBounds() {return _llBounds;} RectC llBounds(const Projection &) {return _llBounds;}
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
void setZoom(int zoom); void setZoom(int zoom);
@ -61,10 +61,9 @@ public:
int zoomIn(); int zoomIn();
int zoomOut(); int zoomOut();
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setOutputProjection(const Projection &projection);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
QPointF ll2xy(const Coordinates &c) QPointF ll2xy(const Coordinates &c)
{return _transform.proj2img(_projection.ll2xy(c));} {return _transform.proj2img(_projection.ll2xy(c));}
@ -76,7 +75,7 @@ public:
bool isValid() const {return _data.isValid();} bool isValid() const {return _data.isValid();}
QString errorString() const {return _data.errorString();} QString errorString() const {return _data.errorString();}
static Map *create(const QString &path, const Projection &, bool *isMap); static Map *create(const QString &path, bool *isMap);
private slots: private slots:
void jobFinished(ENCMapJob *job); void jobFinished(ENCMapJob *job);

View File

@ -182,8 +182,13 @@ Coordinates GEMFMap::xy2ll(const QPointF &p)
return OSM::m2ll(QPointF(p.x() * scale, -p.y() * scale) * _mapRatio); return OSM::m2ll(QPointF(p.x() * scale, -p.y() * scale) * _mapRatio);
} }
void GEMFMap::load() void GEMFMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
_file.open(QIODevice::ReadOnly); _file.open(QIODevice::ReadOnly);
} }
@ -296,7 +301,7 @@ void GEMFMap::drawTile(QPainter *painter, QPixmap &pixmap, QPointF &tp)
painter->drawPixmap(tp, pixmap); painter->drawPixmap(tp, pixmap);
} }
Map *GEMFMap::create(const QString &path, const Projection &, bool *isDir) Map *GEMFMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -14,7 +14,7 @@ public:
GEMFMap(const QString &fileName, QObject *parent = 0); GEMFMap(const QString &fileName, QObject *parent = 0);
QRectF bounds(); QRectF bounds();
RectC llBounds() {return _bounds;} RectC llBounds(const Projection &) {return _bounds;}
int zoom() const {return _zi;} int zoom() const {return _zi;}
void setZoom(int zoom) {_zi = zoom;} void setZoom(int zoom) {_zi = zoom;}
@ -26,18 +26,16 @@ public:
QPointF ll2xy(const Coordinates &c); QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p); Coordinates xy2ll(const QPointF &p);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setDevicePixelRatio(qreal /*deviceRatio*/, qreal mapRatio)
{_mapRatio = mapRatio;}
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
struct Region { struct Region {

View File

@ -54,28 +54,26 @@ void GeoTIFFMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
_img->draw(painter, rect, flags); _img->draw(painter, rect, flags);
} }
void GeoTIFFMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) void GeoTIFFMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(deviceRatio); Q_UNUSED(in);
Q_UNUSED(out);
_ratio = mapRatio; _ratio = hidpi ? deviceRatio : 1.0;
_img = new Image(path());
if (_img) if (_img)
_img->setDevicePixelRatio(_ratio); _img->setDevicePixelRatio(_ratio);
} }
void GeoTIFFMap::load()
{
if (!_img)
_img = new Image(path());
}
void GeoTIFFMap::unload() void GeoTIFFMap::unload()
{ {
delete _img; delete _img;
_img = 0; _img = 0;
} }
Map *GeoTIFFMap::create(const QString &path, const Projection &, bool *isDir) Map *GeoTIFFMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -21,14 +21,15 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
Projection _projection; Projection _projection;

View File

@ -16,7 +16,21 @@
using namespace IMG; using namespace IMG;
#define TILE_SIZE 384 #define TILE_SIZE 384
#define TEXT_EXTENT 160
static RectC limitBounds(const RectC &bounds, const Projection &proj)
{
/* Limit the bounds for some well known projections
(world maps have N/S bounds up to 90/-90!) */
if (proj == PCS::pcs(3857) || proj == PCS::pcs(3395))
return bounds & OSM::BOUNDS;
else if (proj == PCS::pcs(3031) || proj == PCS::pcs(3976))
return bounds & RectC(Coordinates(-180, -60), Coordinates(180, -90));
else if (proj == PCS::pcs(3995) || proj == PCS::pcs(3413))
return bounds & RectC(Coordinates(-180, 90), Coordinates(180, 60));
else
return bounds;
}
static QList<MapData*> overlays(const QString &fileName) static QList<MapData*> overlays(const QString &fileName)
{ {
@ -56,17 +70,27 @@ IMGMap::IMGMap(const QString &fileName, bool GMAP, QObject *parent)
return; return;
} }
_dataBounds = _data.first()->bounds() & OSM::BOUNDS;
_zoom = _data.first()->zooms().min(); _zoom = _data.first()->zooms().min();
updateTransform();
_valid = true; _valid = true;
} }
void IMGMap::load() void IMGMap::load(const Projection &in, const Projection &out,
qreal devicelRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(hidpi);
_tileRatio = devicelRatio;
_projection = out;
_dataBounds = limitBounds(_data.first()->bounds(), _projection);
for (int i = 0; i < _data.size(); i++) for (int i = 0; i < _data.size(); i++)
_data.at(i)->load(); _data.at(i)->load();
updateTransform();
QPixmapCache::clear();
} }
void IMGMap::unload() void IMGMap::unload()
@ -216,30 +240,9 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
if (QPixmapCache::find(key, &pm)) if (QPixmapCache::find(key, &pm))
painter->drawPixmap(ttl, pm); painter->drawPixmap(ttl, pm);
else { else {
QList<MapData::Poly> polygons, lines; tiles.append(RasterTile(_projection, _transform, _data.at(n),
QList<MapData::Point> points; _zoom, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio,
key));
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
ttl.y() + TILE_SIZE));
polyRect &= _bounds;
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
_data.at(n)->polys(polyRectD.toRectC(_projection, 20), _zoom,
&polygons, &lines);
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;
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data.at(n)->points(pointRectD.toRectC(_projection, 20),
_zoom, &points);
tiles.append(RasterTile(_projection, _transform,
_data.at(n)->style(), _zoom,
QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio, key,
polygons, lines, points));
} }
} }
} }
@ -261,37 +264,7 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
} }
} }
void IMGMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) Map* IMGMap::createIMG(const QString &path, bool *isDir)
{
Q_UNUSED(mapRatio);
_tileRatio = deviceRatio;
}
void IMGMap::setOutputProjection(const Projection &projection)
{
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;
// Limit the bounds for some well known projections
// (world maps have N/S bounds up to 90/-90!)
if (_projection == PCS::pcs(3857) || _projection == PCS::pcs(3395))
_dataBounds = _data.first()->bounds() & OSM::BOUNDS;
else if (_projection == PCS::pcs(3031) || _projection == PCS::pcs(3976))
_dataBounds = _data.first()->bounds() & RectC(Coordinates(-180, -60),
Coordinates(180, -90));
else if (_projection == PCS::pcs(3995) || _projection == PCS::pcs(3413))
_dataBounds = _data.first()->bounds() & RectC(Coordinates(-180, 90),
Coordinates(180, 60));
else
_dataBounds = _data.first()->bounds();
updateTransform();
QPixmapCache::clear();
}
Map* IMGMap::createIMG(const QString &path, const Projection &, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;
@ -299,7 +272,7 @@ Map* IMGMap::createIMG(const QString &path, const Projection &, bool *isDir)
return new IMGMap(path, false); return new IMGMap(path, false);
} }
Map* IMGMap::createGMAP(const QString &path, const Projection &, bool *isDir) Map* IMGMap::createGMAP(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = true; *isDir = true;

View File

@ -55,7 +55,7 @@ public:
QString name() const {return _data.first()->name();} QString name() const {return _data.first()->name();}
QRectF bounds() {return _bounds;} QRectF bounds() {return _bounds;}
RectC llBounds() {return _dataBounds;} RectC llBounds(const Projection &) {return _data.first()->bounds();}
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
void setZoom(int zoom); void setZoom(int zoom);
@ -70,17 +70,15 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setOutputProjection(const Projection &projection); void load(const Projection &in, const Projection &out, qreal devicelRatio,
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); bool hidpi);
void load();
void unload(); void unload();
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map* createIMG(const QString &path, const Projection &, bool *isDir); static Map* createIMG(const QString &path, bool *isDir);
static Map* createGMAP(const QString &path, const Projection &, bool *isDir); static Map* createGMAP(const QString &path, bool *isDir);
private slots: private slots:
void jobFinished(IMGMapJob *job); void jobFinished(IMGMapJob *job);

View File

@ -11,22 +11,6 @@
#define ic2dc(x) ((x) * 180.0 / 0x7FFFFFFF) #define ic2dc(x) ((x) * 180.0 / 0x7FFFFFFF)
struct Level {
quint32 count;
quint32 offset;
quint32 scale;
};
struct Ctx {
QPainter *painter;
QFile *file;
qreal ratio;
Ctx(QPainter *painter, QFile *file, qreal ratio)
: painter(painter), file(file), ratio(ratio) {}
};
template<class T> bool JNXMap::readValue(T &val) template<class T> bool JNXMap::readValue(T &val)
{ {
T data; T data;
@ -53,7 +37,7 @@ bool JNXMap::readString(QByteArray& ba)
} }
} }
bool JNXMap::readTiles() bool JNXMap::readHeader()
{ {
qint32 lat1, lon2, lat2, lon1; qint32 lat1, lon2, lat2, lon1;
quint32 version, dummy, levels; quint32 version, dummy, levels;
@ -88,16 +72,22 @@ bool JNXMap::readTiles()
} }
_zooms.reserve(lh.size()); _zooms.reserve(lh.size());
for (int i = 0; i < lh.count(); i++) { for (int i = 0; i < lh.count(); i++)
_zooms.append(new Zoom()); _zooms.append(new Zoom(lh.at(i)));
Zoom *z = _zooms.last();
const Level &l = lh.at(i);
if (!_file.seek(l.offset)) return true;
}
bool JNXMap::readTiles()
{
for (int i = 0; i < _zooms.size(); i++) {
Zoom *z = _zooms[i];
if (!_file.seek(z->level.offset))
return false; return false;
z->tiles = QVector<Tile>(l.count); z->tiles = QVector<Tile>(z->level.count);
for (quint32 j = 0; j < l.count; j++) { for (quint32 j = 0; j < z->level.count; j++) {
Tile &tile = z->tiles[j]; Tile &tile = z->tiles[j];
if (!(readValue(tile.top) && readValue(tile.right) if (!(readValue(tile.top) && readValue(tile.right)
@ -134,16 +124,25 @@ bool JNXMap::readTiles()
return true; return true;
} }
JNXMap::JNXMap(const QString &fileName, const Projection &proj, QObject *parent) void JNXMap::clearTiles()
: Map(fileName, parent), _file(fileName), _zoom(0), _projection(proj), {
_mapRatio(1.0), _valid(false) for (int i = 0; i < _zooms.size(); i++) {
Zoom *z = _zooms[i];
z->tiles.clear();
z->tree.RemoveAll();
}
}
JNXMap::JNXMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _file(fileName), _zoom(0), _mapRatio(1.0),
_valid(false)
{ {
if (!_file.open(QIODevice::ReadOnly)) { if (!_file.open(QIODevice::ReadOnly)) {
_errorString = fileName + ": " + _file.errorString(); _errorString = _file.errorString();
return; return;
} }
if (!readTiles()) { if (!readHeader()) {
_errorString = "JNX file format error"; _errorString = "JNX file format error";
return; return;
} }
@ -158,14 +157,22 @@ JNXMap::~JNXMap()
qDeleteAll(_zooms); qDeleteAll(_zooms);
} }
void JNXMap::load() void JNXMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
_file.open(QIODevice::ReadOnly); Q_UNUSED(out);
_projection = in;
_mapRatio = hidpi ? deviceRatio : 1.0;
if (_file.open(QIODevice::ReadOnly))
readTiles();
} }
void JNXMap::unload() void JNXMap::unload()
{ {
_file.close(); _file.close();
clearTiles();
} }
QPointF JNXMap::ll2xy(const Coordinates &c) QPointF JNXMap::ll2xy(const Coordinates &c)
@ -267,51 +274,10 @@ void JNXMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
tree.Search(min, max, cb, &ctx); tree.Search(min, max, cb, &ctx);
} }
void JNXMap::setInputProjection(const Projection &projection) Map *JNXMap::create(const QString &path, bool *isDir)
{
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;
for (int i = 0; i < _zooms.size(); i++) {
Zoom *z = _zooms[i];
z->tree.RemoveAll();
for (int j = 0; j < z->tiles.size(); j++) {
Tile &tile = z->tiles[j];
RectC llrect(Coordinates(ic2dc(tile.left), ic2dc(tile.top)),
Coordinates(ic2dc(tile.right), ic2dc(tile.bottom)));
RectD rect(_projection.ll2xy(llrect.topLeft()),
_projection.ll2xy(llrect.bottomRight()));
if (j == 0) {
ReferencePoint tl(PointD(0, 0), rect.topLeft());
ReferencePoint br(PointD(tile.width, tile.height),
rect.bottomRight());
z->transform = Transform(tl, br);
}
QRectF trect(z->transform.proj2img(rect.topLeft()),
z->transform.proj2img(rect.bottomRight()));
tile.pos = trect.topLeft();
qreal min[2], max[2];
min[0] = trect.left();
min[1] = trect.top();
max[0] = trect.right();
max[1] = trect.bottom();
z->tree.Insert(min, max, &tile);
}
}
}
Map *JNXMap::create(const QString &path, const Projection &proj, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;
return new JNXMap(path, proj); return new JNXMap(path);
} }

View File

@ -15,11 +15,11 @@ public:
Q_OBJECT Q_OBJECT
public: public:
JNXMap(const QString &fileName, const Projection &proj, QObject *parent = 0); JNXMap(const QString &fileName, QObject *parent = 0);
~JNXMap(); ~JNXMap();
QRectF bounds(); QRectF bounds();
RectC llBounds() {return _bounds;} RectC llBounds(const Projection &) {return _bounds;}
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
void setZoom(int zoom) {_zoom = zoom;} void setZoom(int zoom) {_zoom = zoom;}
@ -30,19 +30,16 @@ public:
QPointF ll2xy(const Coordinates &c); QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p); Coordinates xy2ll(const QPointF &p);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setInputProjection(const Projection &projection);
void setDevicePixelRatio(qreal /*deviceRatio*/, qreal mapRatio)
{_mapRatio = mapRatio;}
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &proj, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
struct Tile { struct Tile {
@ -54,15 +51,37 @@ private:
QPointF pos; QPointF pos;
}; };
struct Level {
quint32 count;
quint32 offset;
quint32 scale;
};
struct Zoom { struct Zoom {
Zoom() {}
Zoom(const Level &level) : level(level) {}
Level level;
Transform transform; Transform transform;
QVector<Tile> tiles; QVector<Tile> tiles;
RTree<Tile*, qreal, 2> tree; RTree<Tile*, qreal, 2> tree;
}; };
struct Ctx {
QPainter *painter;
QFile *file;
qreal ratio;
Ctx(QPainter *painter, QFile *file, qreal ratio)
: painter(painter), file(file), ratio(ratio) {}
};
template<class T> bool readValue(T &val); template<class T> bool readValue(T &val);
bool readString(QByteArray &ba); bool readString(QByteArray &ba);
bool readHeader();
bool readTiles(); bool readTiles();
void clearTiles();
static bool cb(Tile *tile, void *context); static bool cb(Tile *tile, void *context);
static QPixmap pixmap(const Tile *tile, QFile *file); static QPixmap pixmap(const Tile *tile, QFile *file);

View File

@ -13,14 +13,12 @@
#include <QFileInfo> #include <QFileInfo>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QImage>
#include <QImageReader>
#include <QBuffer> #include <QBuffer>
#include <QImageReader>
#include <QPainter> #include <QPainter>
#include <QPixmapCache>
#include <private/qzipreader_p.h> #include <private/qzipreader_p.h>
#include"common/util.h" #include "common/util.h"
#include "pcs.h"
#include "image.h"
#include "kmzmap.h" #include "kmzmap.h"
@ -29,162 +27,140 @@
#define TL(m) ((m).bbox().topLeft()) #define TL(m) ((m).bbox().topLeft())
#define BR(m) ((m).bbox().bottomRight()) #define BR(m) ((m).bbox().bottomRight())
bool KMZMap::resCmp(const Tile &m1, const Tile &m2)
KMZMap::Overlay::Overlay(const QString &path, const QSize &size,
const RectC &bbox, double rotation, const Projection *proj, qreal ratio)
: _path(path), _size(size), _bbox(bbox), _rotation(rotation), _img(0),
_proj(proj), _ratio(ratio)
{ {
ReferencePoint tl(PointD(0, 0), _proj->ll2xy(bbox.topLeft())); return m1.resolution() > m2.resolution();
ReferencePoint br(PointD(size.width(), size.height()),
_proj->ll2xy(bbox.bottomRight()));
QTransform t;
t.rotate(-rotation);
QRectF b(0, 0, size.width(), size.height());
QPolygonF ma = t.map(b);
_bounds = ma.boundingRect();
_transform = Transform(tl, br);
} }
qreal KMZMap::Overlay::resolution(const QRectF &rect) const bool KMZMap::xCmp(const Tile &m1, const Tile &m2)
{
qreal cy = rect.center().y();
QPointF cl(rect.left(), cy);
QPointF cr(rect.right(), cy);
qreal ds = xy2ll(cl).distanceTo(xy2ll(cr));
qreal ps = QLineF(cl, cr).length();
return ds/ps;
}
void KMZMap::Overlay::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
if (_img) {
QRectF rr(rect.topLeft() / _ratio, rect.size());
if (_rotation) {
painter->save();
painter->rotate(-_rotation);
_img->draw(painter, rr, flags);
painter->restore();
} else
_img->draw(painter, rr, flags);
}
//painter->setPen(Qt::red);
//painter->drawRect(_bounds);
}
void KMZMap::Overlay::load(QZipReader *zip)
{
if (!_img) {
QByteArray ba(zip->fileData(_path));
_img = new Image(QImage::fromData(ba));
_img->setDevicePixelRatio(_ratio);
}
}
void KMZMap::Overlay::unload()
{
delete _img;
_img = 0;
}
void KMZMap::Overlay::setProjection(const Projection *proj)
{
_proj = proj;
ReferencePoint tl(PointD(0, 0), _proj->ll2xy(_bbox.topLeft()));
ReferencePoint br(PointD(_size.width(), _size.height()),
_proj->ll2xy(_bbox.bottomRight()));
QTransform t;
t.rotate(-_rotation);
QRectF b(0, 0, _size.width(), _size.height());
QPolygonF ma = t.map(b);
_bounds = ma.boundingRect();
_transform = Transform(tl, br);
}
void KMZMap::Overlay::setDevicePixelRatio(qreal ratio)
{
_ratio = ratio;
if (_img)
_img->setDevicePixelRatio(_ratio);
}
bool KMZMap::resCmp(const Overlay &m1, const Overlay &m2)
{
qreal r1, r2;
r1 = m1.resolution(m1.bounds());
r2 = m2.resolution(m2.bounds());
return r1 > r2;
}
bool KMZMap::xCmp(const Overlay &m1, const Overlay &m2)
{ {
return TL(m1).lon() < TL(m2).lon(); return TL(m1).lon() < TL(m2).lon();
} }
bool KMZMap::yCmp(const Overlay &m1, const Overlay &m2) bool KMZMap::yCmp(const Tile &m1, const Tile &m2)
{ {
return TL(m1).lat() > TL(m2).lat(); return TL(m1).lat() > TL(m2).lat();
} }
KMZMap::Tile::Tile(const Overlay &overlay, QZipReader &zip)
: _overlay(overlay)
{
QByteArray ba(zip.fileData(overlay.path()));
QBuffer img(&ba);
QImageReader ir(&img);
_size = ir.size();
}
void KMZMap::Tile::configure(const Projection &proj)
{
ReferencePoint tl(PointD(0, 0), proj.ll2xy(bbox().topLeft()));
ReferencePoint br(PointD(_size.width(), _size.height()),
proj.ll2xy(bbox().bottomRight()));
_transform = Transform(tl, br);
}
QRectF KMZMap::Tile::bounds() const
{
QTransform t;
t.rotate(-rotation());
QRectF b(0, 0, _size.width(), _size.height());
QPolygonF ma(t.map(b));
return ma.boundingRect();
}
qreal KMZMap::Tile::resolution() const
{
QRectF d(0, 0, _size.width(), _size.height());
qreal dy = d.center().y();
QPointF dl(d.left(), dy);
QPointF dr(d.right(), dy);
double cy = bbox().center().lat();
Coordinates cl(bbox().left(), cy);
Coordinates cr(bbox().right(), cy);
qreal ds = cl.distanceTo(cr);
qreal ps = QLineF(dl, dr).length();
return ds/ps;
}
bool KMZMap::createTiles(const QList<Overlay> &overlays, QZipReader &zip)
{
if (overlays.isEmpty()) {
_errorString = "No usable overlay found";
return false;
}
_tiles.reserve(overlays.size());
for (int i = 0; i < overlays.size(); i++) {
const Overlay &ol = overlays.at(i);
Tile tile(ol, zip);
if (tile.isValid())
_tiles.append(tile);
else {
_errorString = ol.path() + ": invalid/missing overlay image";
return false;
}
}
return true;
}
void KMZMap::computeLLBounds()
{
for (int i = 0; i < _tiles.size(); i++)
_llbounds |= _tiles.at(i).bbox();
}
void KMZMap::computeZooms() void KMZMap::computeZooms()
{ {
std::sort(_maps.begin(), _maps.end(), resCmp); std::sort(_tiles.begin(), _tiles.end(), resCmp);
_zooms.append(Zoom(0, _maps.count() - 1)); _zooms.append(Zoom(0, _tiles.count() - 1));
for (int i = 1; i < _maps.count(); i++) { for (int i = 1; i < _tiles.count(); i++) {
qreal last = _maps.at(i-1).resolution(_maps.at(i).bounds()); qreal last = _tiles.at(i-1).resolution();
qreal cur = _maps.at(i).resolution(_maps.at(i).bounds()); qreal cur = _tiles.at(i).resolution();
if (cur < last * ZOOM_THRESHOLD) { if (cur < last * ZOOM_THRESHOLD) {
_zooms.last().last = i-1; _zooms.last().last = i-1;
_zooms.append(Zoom(i, _maps.count() - 1)); _zooms.append(Zoom(i, _tiles.count() - 1));
} }
} }
} }
void KMZMap::computeBounds() void KMZMap::computeBounds()
{ {
QVector<QPointF> offsets(_maps.count()); QVector<QPointF> offsets(_tiles.count());
for (int z = 0; z < _zooms.count(); z++) { for (int z = 0; z < _zooms.count(); z++) {
QList<Overlay> m; QList<Tile> m;
for (int i = _zooms.at(z).first; i <= _zooms.at(z).last; i++) for (int i = _zooms.at(z).first; i <= _zooms.at(z).last; i++)
m.append(_maps.at(i)); m.append(_tiles.at(i));
std::sort(m.begin(), m.end(), xCmp); std::sort(m.begin(), m.end(), xCmp);
offsets[_maps.indexOf(m.first())].setX(m.first().bounds().left()); offsets[_tiles.indexOf(m.first())].setX(m.first().bounds().left());
for (int i = 1; i < m.size(); i++) { for (int i = 1; i < m.size(); i++) {
qreal w = m.first().ll2xy(TL(m.at(i))).x(); qreal w = ll2xy(TL(m.at(i)), m.first().transform()).x();
offsets[_maps.indexOf(m.at(i))].setX(w + m.at(i).bounds().left()); offsets[_tiles.indexOf(m.at(i))].setX(w + m.at(i).bounds().left());
} }
std::sort(m.begin(), m.end(), yCmp); std::sort(m.begin(), m.end(), yCmp);
offsets[_maps.indexOf(m.first())].setY(m.first().bounds().top()); offsets[_tiles.indexOf(m.first())].setY(m.first().bounds().top());
for (int i = 1; i < m.size(); i++) { for (int i = 1; i < m.size(); i++) {
qreal h = m.first().ll2xy(TL(m.at(i))).y(); qreal h = ll2xy(TL(m.at(i)), m.first().transform()).y();
offsets[_maps.indexOf(m.at(i))].setY(h + m.at(i).bounds().top()); offsets[_tiles.indexOf(m.at(i))].setY(h + m.at(i).bounds().top());
} }
} }
_adjust = 0; _adjust = 0;
_bounds = QVector<Bounds>(_maps.count()); _bounds = QVector<Bounds>(_tiles.count());
for (int i = 0; i < _maps.count(); i++) { for (int i = 0; i < _tiles.count(); i++) {
QRectF xy(offsets.at(i), _maps.at(i).bounds().size()); QRectF xy(offsets.at(i), _tiles.at(i).bounds().size() / _mapRatio);
_bounds[i] = Bounds(_maps.at(i).bbox(), xy); _bounds[i] = Bounds(_tiles.at(i).bbox(), xy);
_adjust = qMin(qMin(_maps.at(i).bounds().left(), _adjust = qMin(qMin(_tiles.at(i).bounds().left(),
_maps.at(i).bounds().top()), _adjust); _tiles.at(i).bounds().top()), _adjust);
} }
_adjust = -_adjust; _adjust = -_adjust;
} }
@ -236,7 +212,7 @@ RectC KMZMap::latLonBox(QXmlStreamReader &reader, double *rotation)
return RectC(Coordinates(left, top), Coordinates(right, bottom)); return RectC(Coordinates(left, top), Coordinates(right, bottom));
} }
void KMZMap::groundOverlay(QXmlStreamReader &reader, QZipReader &zip) void KMZMap::groundOverlay(QXmlStreamReader &reader, QList<Overlay> &overlays)
{ {
QString image; QString image;
RectC rect; RectC rect;
@ -251,93 +227,87 @@ void KMZMap::groundOverlay(QXmlStreamReader &reader, QZipReader &zip)
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
if (rect.isValid()) { if (rect.isValid())
QByteArray ba(zip.fileData(image)); overlays.append(Overlay(image, rect, rotation));
QBuffer img(&ba); else
QImageReader ir(&img);
QSize size(ir.size());
if (size.isValid())
_maps.append(Overlay(image, size, rect, rotation, &_projection,
_ratio));
else
reader.raiseError(image + ": Invalid image file");
} else
reader.raiseError("Invalid LatLonBox"); reader.raiseError("Invalid LatLonBox");
} }
void KMZMap::document(QXmlStreamReader &reader, QZipReader &zip) void KMZMap::document(QXmlStreamReader &reader, QList<Overlay> &overlays)
{ {
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Document")) if (reader.name() == QLatin1String("Document"))
document(reader, zip); document(reader, overlays);
else if (reader.name() == QLatin1String("GroundOverlay")) else if (reader.name() == QLatin1String("GroundOverlay"))
groundOverlay(reader, zip); groundOverlay(reader, overlays);
else if (reader.name() == QLatin1String("Folder")) else if (reader.name() == QLatin1String("Folder"))
folder(reader, zip); folder(reader, overlays);
else else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
void KMZMap::folder(QXmlStreamReader &reader, QZipReader &zip) void KMZMap::folder(QXmlStreamReader &reader, QList<Overlay> &overlays)
{ {
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("GroundOverlay")) if (reader.name() == QLatin1String("GroundOverlay"))
groundOverlay(reader, zip); groundOverlay(reader, overlays);
else if (reader.name() == QLatin1String("Folder")) else if (reader.name() == QLatin1String("Folder"))
folder(reader, zip); folder(reader, overlays);
else else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
void KMZMap::kml(QXmlStreamReader &reader, QZipReader &zip) void KMZMap::kml(QXmlStreamReader &reader, QList<Overlay> &overlays)
{ {
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Document")) if (reader.name() == QLatin1String("Document"))
document(reader, zip); document(reader, overlays);
else if (reader.name() == QLatin1String("GroundOverlay")) else if (reader.name() == QLatin1String("GroundOverlay"))
groundOverlay(reader, zip); groundOverlay(reader, overlays);
else if (reader.name() == QLatin1String("Folder")) else if (reader.name() == QLatin1String("Folder"))
folder(reader, zip); folder(reader, overlays);
else else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
KMZMap::KMZMap(const QString &fileName, const Projection &proj, QObject *parent) KMZMap::KMZMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _zoom(0), _mapIndex(-1), _zip(0), _projection(proj), : Map(fileName, parent), _zoom(0), _mapIndex(-1), _zip(0), _mapRatio(1.0),
_ratio(1.0), _valid(false) _valid(false)
{ {
QZipReader zip(fileName, QIODevice::ReadOnly); QZipReader zip(fileName, QIODevice::ReadOnly);
QByteArray xml(zip.fileData("doc.kml")); QByteArray xml(zip.fileData("doc.kml"));
QXmlStreamReader reader(xml); QXmlStreamReader reader(xml);
QList<Overlay> overlays;
if (reader.readNextStartElement()) { if (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("kml")) if (reader.name() == QLatin1String("kml"))
kml(reader, zip); kml(reader, overlays);
else else
reader.raiseError("Not a KMZ file"); reader.raiseError("Not a KMZ file");
} }
if (reader.error()) { if (reader.error()) {
_errorString = "doc.kml:" + QString::number(reader.lineNumber()) + ": " _errorString = "doc.kml:" + QString::number(reader.lineNumber()) + ": "
+ reader.errorString(); + reader.errorString();
return; return;
} }
if (_maps.isEmpty()) {
_errorString = "No usable GroundOverlay found";
return;
}
if (!createTiles(overlays, zip))
return;
computeLLBounds();
computeZooms(); computeZooms();
computeBounds();
_valid = true; _valid = true;
} }
KMZMap::~KMZMap()
{
delete _zip;
}
QRectF KMZMap::bounds() QRectF KMZMap::bounds()
{ {
QRectF rect; QRectF rect;
@ -364,8 +334,9 @@ int KMZMap::zoomFit(const QSize &size, const RectC &br)
if (!_bounds.at(i).ll.contains(br.center())) if (!_bounds.at(i).ll.contains(br.center()))
continue; continue;
QRect sbr = QRectF(_maps.at(i).ll2xy(br.topLeft()), QRect sbr = QRectF(ll2xy(br.topLeft(), _tiles.at(i).transform()),
_maps.at(i).ll2xy(br.bottomRight())).toRect().normalized(); ll2xy(br.bottomRight(), _tiles.at(i).transform()))
.toRect().normalized();
if (sbr.size().width() > size.width() if (sbr.size().width() > size.width()
|| sbr.size().height() > size.height()) || sbr.size().height() > size.height())
@ -413,10 +384,10 @@ QPointF KMZMap::ll2xy(const Coordinates &c)
} }
} }
QPointF p = _maps.at(_mapIndex).ll2xy(c); QPointF p = ll2xy(c, _tiles.at(_mapIndex).transform());
if (_maps.at(_mapIndex).rotation()) { if (_tiles.at(_mapIndex).rotation()) {
QTransform matrix; QTransform matrix;
matrix.rotate(-_maps.at(_mapIndex).rotation()); matrix.rotate(-_tiles.at(_mapIndex).rotation());
return matrix.map(p) + _bounds.at(_mapIndex).xy.topLeft(); return matrix.map(p) + _bounds.at(_mapIndex).xy.topLeft();
} else } else
return p + _bounds.at(_mapIndex).xy.topLeft(); return p + _bounds.at(_mapIndex).xy.topLeft();
@ -434,90 +405,89 @@ Coordinates KMZMap::xy2ll(const QPointF &p)
} }
QPointF p2 = p - _bounds.at(idx).xy.topLeft(); QPointF p2 = p - _bounds.at(idx).xy.topLeft();
if (_maps.at(idx).rotation()) { if (_tiles.at(idx).rotation()) {
QTransform matrix; QTransform matrix;
matrix.rotate(_maps.at(idx).rotation()); matrix.rotate(_tiles.at(idx).rotation());
return _maps.at(idx).xy2ll(matrix.map(p2)); return xy2ll(matrix.map(p2), _tiles.at(idx).transform());
} else } else
return _maps.at(idx).xy2ll(p2); return xy2ll(p2, _tiles.at(idx).transform());
} }
void KMZMap::draw(QPainter *painter, const QRectF &rect, Flags flags) void KMZMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{ {
QRectF er = rect.adjusted(-_adjust * _ratio, -_adjust * _ratio, Q_UNUSED(flags);
_adjust * _ratio, _adjust * _ratio);
QRectF er = rect.adjusted(-_adjust * _mapRatio, -_adjust * _mapRatio,
_adjust * _mapRatio, _adjust * _mapRatio);
for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).last; i++) { for (int i = _zooms.at(_zoom).first; i <= _zooms.at(_zoom).last; i++) {
QRectF ir = er.intersected(_bounds.at(i).xy); QRectF ir = er.intersected(_bounds.at(i).xy);
if (!ir.isNull()) if (!ir.isNull())
draw(painter, ir, i, flags); draw(painter, ir, i);
} }
} }
void KMZMap::load() void KMZMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(out);
_projection = in;
_mapRatio = hidpi ? deviceRatio : 1.0;
for (int i = 0; i < _tiles.size(); i++)
_tiles[i].configure(_projection);
computeBounds();
Q_ASSERT(!_zip); Q_ASSERT(!_zip);
_zip = new QZipReader(path(), QIODevice::ReadOnly); _zip = new QZipReader(path(), QIODevice::ReadOnly);
} }
void KMZMap::unload() void KMZMap::unload()
{ {
for (int i = 0; i < _maps.count(); i++) _bounds.clear();
_maps[i].unload();
delete _zip; delete _zip;
_zip = 0; _zip = 0;
} }
void KMZMap::setInputProjection(const Projection &projection) void KMZMap::draw(QPainter *painter, const QRectF &rect, int mapIndex)
{ {
if (!projection.isValid() || projection == _projection) const Tile &map = _tiles.at(mapIndex);
return;
_projection = projection;
for (int i = 0; i < _maps.size(); i++)
_maps[i].setProjection(&_projection);
_bounds.clear();
computeBounds();
}
void KMZMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
{
Q_UNUSED(deviceRatio);
if (mapRatio == _ratio)
return;
_ratio = mapRatio;
for (int i = 0; i < _maps.size(); i++)
_maps[i].setDevicePixelRatio(_ratio);
_bounds.clear();
computeBounds();
}
void KMZMap::draw(QPainter *painter, const QRectF &rect, int mapIndex,
Flags flags)
{
Overlay &map = _maps[mapIndex];
const QPointF offset = _bounds.at(mapIndex).xy.topLeft(); const QPointF offset = _bounds.at(mapIndex).xy.topLeft();
QRectF pr = QRectF(rect.topLeft() - offset, rect.size()); QRectF pr = QRectF(rect.topLeft() - offset, rect.size());
QRectF sr(pr.topLeft() * _mapRatio, pr.size() * _mapRatio);
map.load(_zip); QString key(path() + "/" + map.path());
QPixmap pm;
painter->save(); painter->save();
painter->translate(offset); painter->translate(offset);
map.draw(painter, pr, flags); if (map.rotation())
painter->rotate(-map.rotation());
if (QPixmapCache::find(key, &pm)) {
pm.setDevicePixelRatio(_mapRatio);
painter->drawPixmap(pr.topLeft(), pm, sr);
} else {
QByteArray ba(_zip->fileData(map.path()));
QImage img(QImage::fromData(ba));
pm = QPixmap::fromImage(img);
pm.setDevicePixelRatio(_mapRatio);
painter->drawPixmap(pr.topLeft(), pm, sr);
QPixmapCache::insert(key, pm);
}
//painter->setPen(Qt::red);
//painter->drawRect(map.bounds());
painter->restore(); painter->restore();
} }
Map *KMZMap::create(const QString &path, const Projection &proj, bool *isDir) Map *KMZMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;
return new KMZMap(path, proj); return new KMZMap(path);
} }

View File

@ -1,6 +1,7 @@
#ifndef KMZMAP_H #ifndef KMZMAP_H
#define KMZMAP_H #define KMZMAP_H
#include <QImage>
#include "projection.h" #include "projection.h"
#include "transform.h" #include "transform.h"
#include "rectd.h" #include "rectd.h"
@ -8,15 +9,16 @@
class QXmlStreamReader; class QXmlStreamReader;
class QZipReader; class QZipReader;
class Image;
class KMZMap : public Map class KMZMap : public Map
{ {
Q_OBJECT Q_OBJECT
public: public:
KMZMap(const QString &fileName, const Projection &proj, QObject *parent = 0); KMZMap(const QString &fileName, QObject *parent = 0);
~KMZMap();
RectC llBounds(const Projection &) {return _llbounds;}
QRectF bounds(); QRectF bounds();
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
@ -30,54 +32,52 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setInputProjection(const Projection &projection);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &proj, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
class Overlay { class Overlay {
public: public:
Overlay(const QString &path, const QSize &size, const RectC &bbox, Overlay(const QString &path, const RectC &bbox, double rotation)
double rotation, const Projection *proj, qreal ratio); : _path(path), _bbox(bbox), _rotation(rotation) {}
bool operator==(const Overlay &other) const
{return _path == other._path;}
QPointF ll2xy(const Coordinates &c) const
{return QPointF(_transform.proj2img(_proj->ll2xy(c))) / _ratio;}
Coordinates xy2ll(const QPointF &p) const
{return _proj->xy2ll(_transform.img2proj(p * _ratio));}
const QString &path() const {return _path;} const QString &path() const {return _path;}
const RectC &bbox() const {return _bbox;} const RectC &bbox() const {return _bbox;}
const QRectF &bounds() const {return _bounds;}
qreal resolution(const QRectF &rect) const;
qreal rotation() const {return _rotation;} qreal rotation() const {return _rotation;}
void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(QZipReader *zip);
void unload();
void setProjection(const Projection *proj);
void setDevicePixelRatio(qreal ratio);
private: private:
QString _path; QString _path;
QSize _size;
QRectF _bounds;
RectC _bbox; RectC _bbox;
qreal _rotation; qreal _rotation;
Image *_img; };
const Projection *_proj;
class Tile {
public:
Tile(const Overlay &overlay, QZipReader &zip);
bool operator==(const Tile &other) const
{return _overlay.path() == other._overlay.path();}
bool isValid() const {return _size.isValid();}
const QString &path() const {return _overlay.path();}
qreal rotation() const {return _overlay.rotation();}
const RectC &bbox() const {return _overlay.bbox();}
const Transform &transform() const {return _transform;}
QRectF bounds() const;
qreal resolution() const;
void configure(const Projection &proj);
private:
Overlay _overlay;
QSize _size;
Transform _transform; Transform _transform;
qreal _ratio;
}; };
struct Zoom { struct Zoom {
@ -96,23 +96,32 @@ private:
Bounds(const RectC &ll, const QRectF &xy) : ll(ll), xy(xy) {} Bounds(const RectC &ll, const QRectF &xy) : ll(ll), xy(xy) {}
}; };
void kml(QXmlStreamReader &reader, QZipReader &zip); void kml(QXmlStreamReader &reader, QList<Overlay> &overlays);
void document(QXmlStreamReader &reader, QZipReader &zip); void document(QXmlStreamReader &reader, QList<Overlay> &overlays);
void folder(QXmlStreamReader &reader, QZipReader &zip); void folder(QXmlStreamReader &reader, QList<Overlay> &overlays);
void groundOverlay(QXmlStreamReader &reader, QZipReader &zip); void groundOverlay(QXmlStreamReader &reader, QList<Overlay> &overlays);
RectC latLonBox(QXmlStreamReader &reader, double *rotation); RectC latLonBox(QXmlStreamReader &reader, double *rotation);
QString icon(QXmlStreamReader &reader); QString icon(QXmlStreamReader &reader);
double number(QXmlStreamReader &reader); double number(QXmlStreamReader &reader);
void draw(QPainter *painter, const QRectF &rect, int mapIndex, Flags flags); void draw(QPainter *painter, const QRectF &rect, int mapIndex);
bool createTiles(const QList<Overlay> &overlays, QZipReader &zip);
void computeZooms(); void computeZooms();
void computeBounds(); void computeBounds();
void computeLLBounds();
static bool resCmp(const Overlay &m1, const Overlay &m2); QPointF ll2xy(const Coordinates &c, const Transform &transform) const
static bool xCmp(const Overlay &m1, const Overlay &m2); {return QPointF(transform.proj2img(_projection.ll2xy(c))) / _mapRatio;}
static bool yCmp(const Overlay &m1, const Overlay &m2); Coordinates xy2ll(const QPointF &p, const Transform &transform) const
{return _projection.xy2ll(transform.img2proj(p * _mapRatio));}
QList<Overlay> _maps; static bool resCmp(const Tile &m1, const Tile &m2);
static bool xCmp(const Tile &m1, const Tile &m2);
static bool yCmp(const Tile &m1, const Tile &m2);
RectC _llbounds;
QList<Tile> _tiles;
QVector<Zoom> _zooms; QVector<Zoom> _zooms;
QVector<Bounds> _bounds; QVector<Bounds> _bounds;
int _zoom; int _zoom;
@ -120,7 +129,7 @@ private:
QZipReader *_zip; QZipReader *_zip;
qreal _adjust; qreal _adjust;
Projection _projection; Projection _projection;
qreal _ratio; qreal _mapRatio;
bool _valid; bool _valid;
QString _errorString; QString _errorString;

View File

@ -5,40 +5,36 @@
#define SAMPLES 100 #define SAMPLES 100
void Map::growLeft(const QPointF &p, RectC &rect) static void growLeft(const Coordinates &c, RectC &rect)
{ {
Coordinates c(xy2ll(p));
if (c.lon() < rect.left()) if (c.lon() < rect.left())
rect.setLeft(c.lon()); rect.setLeft(c.lon());
} }
void Map::growRight(const QPointF &p, RectC &rect) static void growRight(const Coordinates &c, RectC &rect)
{ {
Coordinates c(xy2ll(p));
if (c.lon() > rect.right()) if (c.lon() > rect.right())
rect.setRight(c.lon()); rect.setRight(c.lon());
} }
void Map::growTop(const QPointF &p, RectC &rect) static void growTop(const Coordinates &c, RectC &rect)
{ {
Coordinates c(xy2ll(p));
if (c.lat() > rect.top()) if (c.lat() > rect.top())
rect.setTop(c.lat()); rect.setTop(c.lat());
} }
void Map::growBottom(const QPointF &p, RectC &rect) static void growBottom(const Coordinates &c, RectC &rect)
{ {
Coordinates c(xy2ll(p));
if (c.lat() < rect.bottom()) if (c.lat() < rect.bottom())
rect.setBottom(c.lat()); rect.setBottom(c.lat());
} }
RectC Map::llBounds() RectC Map::llBounds(const Projection &proj)
{ {
Q_UNUSED(proj);
/* We use bounds() and xy2ll() here as this fallback implementation is
used ONLY for maps providing those functions since map creation. */
QRectF b(bounds()); QRectF b(bounds());
double dx = b.width() / SAMPLES; double dx = b.width() / SAMPLES;
double dy = b.height() / SAMPLES; double dy = b.height() / SAMPLES;
@ -49,14 +45,14 @@ RectC Map::llBounds()
for (int i = 0; i <= SAMPLES; i++) { for (int i = 0; i <= SAMPLES; i++) {
double x = b.left() + i * dx; double x = b.left() + i * dx;
growBottom(QPointF(x, b.bottom()), rect); growBottom(xy2ll(QPointF(x, b.bottom())), rect);
growTop(QPointF(x, b.top()), rect); growTop(xy2ll(QPointF(x, b.top())), rect);
} }
for (int i = 0; i <= SAMPLES; i++) { for (int i = 0; i <= SAMPLES; i++) {
double y = b.top() + i * dy; double y = b.top() + i * dy;
growLeft(QPointF(b.left(), y), rect); growLeft(xy2ll(QPointF(b.left(), y)), rect);
growRight(QPointF(b.right(), y), rect); growRight(xy2ll(QPointF(b.right(), y)), rect);
} }
return rect; return rect;

View File

@ -28,10 +28,19 @@ public:
: QObject(parent), _path(path) {} : QObject(parent), _path(path) {}
virtual ~Map() {} virtual ~Map() {}
/* Functions available since map creation */
const QString &path() const {return _path;} const QString &path() const {return _path;}
virtual QString name() const {return Util::file2name(path());} virtual QString name() const {return Util::file2name(path());}
virtual RectC llBounds(const Projection &proj);
virtual bool isValid() const {return true;}
virtual bool isReady() const {return true;}
virtual QString errorString() const {return QString();}
/* Functions that shall be called after load() */
virtual void load(const Projection &, const Projection &, qreal, bool) {}
virtual void unload() {}
virtual RectC llBounds();
virtual QRectF bounds() = 0; virtual QRectF bounds() = 0;
virtual qreal resolution(const QRectF &rect); virtual qreal resolution(const QRectF &rect);
@ -47,26 +56,12 @@ public:
virtual void draw(QPainter *painter, const QRectF &rect, Flags flags) = 0; virtual void draw(QPainter *painter, const QRectF &rect, Flags flags) = 0;
virtual void clearCache() {} virtual void clearCache() {}
virtual void load() {}
virtual void unload() {}
virtual void setDevicePixelRatio(qreal, qreal) {}
virtual void setOutputProjection(const Projection &) {}
virtual void setInputProjection(const Projection &) {}
virtual bool isValid() const {return true;}
virtual bool isReady() const {return true;}
virtual QString errorString() const {return QString();}
signals: signals:
void tilesLoaded(); void tilesLoaded();
void mapLoaded(); void mapLoaded();
private: private:
void growLeft(const QPointF &p, RectC &rect);
void growRight(const QPointF &p, RectC &rect);
void growTop(const QPointF &p, RectC &rect);
void growBottom(const QPointF &p, RectC &rect);
QString _path; QString _path;
}; };

View File

@ -62,7 +62,7 @@ MapList::ParserMap MapList::parsers()
MapList::ParserMap MapList::_parsers = parsers(); MapList::ParserMap MapList::_parsers = parsers();
Map *MapList::loadFile(const QString &path, const Projection &proj, bool *isDir) Map *MapList::loadFile(const QString &path, bool *isDir)
{ {
ParserMap::iterator it; ParserMap::iterator it;
QFileInfo fi(path); QFileInfo fi(path);
@ -73,7 +73,7 @@ Map *MapList::loadFile(const QString &path, const Projection &proj, bool *isDir)
if ((it = _parsers.find(suffix)) != _parsers.end()) { if ((it = _parsers.find(suffix)) != _parsers.end()) {
while (it != _parsers.end() && it.key() == suffix) { while (it != _parsers.end() && it.key() == suffix) {
delete map; delete map;
map = it.value()(path, proj, isDir); map = it.value()(path, isDir);
if (map->isValid()) if (map->isValid())
return map; return map;
else else
@ -82,7 +82,7 @@ Map *MapList::loadFile(const QString &path, const Projection &proj, bool *isDir)
} }
} else { } else {
for (it = _parsers.begin(); it != _parsers.end(); it++) { for (it = _parsers.begin(); it != _parsers.end(); it++) {
map = it.value()(path, proj, isDir); map = it.value()(path, isDir);
if (map->isValid()) if (map->isValid())
return map; return map;
else { else {
@ -100,8 +100,7 @@ Map *MapList::loadFile(const QString &path, const Projection &proj, bool *isDir)
return map ? map : new InvalidMap(path, "Unknown file format"); return map ? map : new InvalidMap(path, "Unknown file format");
} }
TreeNode<Map *> MapList::loadDir(const QString &path, const Projection &proj, TreeNode<Map*> MapList::loadDir(const QString &path, TreeNode<Map*> *parent)
TreeNode<Map *> *parent)
{ {
QDir md(path); QDir md(path);
md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); md.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
@ -118,12 +117,12 @@ TreeNode<Map *> MapList::loadDir(const QString &path, const Projection &proj,
QString suffix = fi.suffix().toLower(); QString suffix = fi.suffix().toLower();
if (fi.isDir()) { if (fi.isDir()) {
TreeNode<Map*> child(loadDir(fi.absoluteFilePath(), proj, &tree)); TreeNode<Map*> child(loadDir(fi.absoluteFilePath(), &tree));
if (!child.isEmpty()) if (!child.isEmpty())
tree.addChild(child); tree.addChild(child);
} else if (filter().contains("*." + suffix)) { } else if (filter().contains("*." + suffix)) {
bool isDir = false; bool isDir = false;
Map *map = loadFile(fi.absoluteFilePath(), proj, &isDir); Map *map = loadFile(fi.absoluteFilePath(), &isDir);
if (isDir) { if (isDir) {
if (parent) if (parent)
parent->addItem(map); parent->addItem(map);
@ -138,13 +137,13 @@ TreeNode<Map *> MapList::loadDir(const QString &path, const Projection &proj,
return tree; return tree;
} }
TreeNode<Map *> MapList::loadMaps(const QString &path, const Projection &proj) TreeNode<Map *> MapList::loadMaps(const QString &path)
{ {
if (QFileInfo(path).isDir()) if (QFileInfo(path).isDir())
return loadDir(path, proj); return loadDir(path);
else { else {
TreeNode<Map*> tree; TreeNode<Map*> tree;
tree.addItem(loadFile(path, proj)); tree.addItem(loadFile(path));
return tree; return tree;
} }
} }

View File

@ -10,18 +10,16 @@ class Projection;
class MapList class MapList
{ {
public: public:
static TreeNode<Map*> loadMaps(const QString &path, const Projection &proj); static TreeNode<Map*> loadMaps(const QString &path);
static QString formats(); static QString formats();
static QStringList filter(); static QStringList filter();
private: private:
typedef Map*(*ParserCb)(const QString &, const Projection &, bool *isDir); typedef Map*(*ParserCb)(const QString &, bool *isDir);
typedef QMultiMap<QString, ParserCb> ParserMap; typedef QMultiMap<QString, ParserCb> ParserMap;
static Map *loadFile(const QString &path, const Projection &proj, static Map *loadFile(const QString &path, bool *isDir = 0);
bool *isDir = 0); static TreeNode<Map*> loadDir(const QString &path, TreeNode<Map*> *parent = 0);
static TreeNode<Map*> loadDir(const QString &path, const Projection &proj,
TreeNode<Map *> *parent = 0);
static ParserMap parsers(); static ParserMap parsers();
static ParserMap _parsers; static ParserMap _parsers;

View File

@ -23,17 +23,21 @@ using namespace Mapsforge;
static void copyPaths(const RectC &rect, const QList<MapData::Path> *src, static void copyPaths(const RectC &rect, const QList<MapData::Path> *src,
QList<MapData::Path> *dst) QList<MapData::Path> *dst)
{ {
for (int i = 0; i < src->size(); i++) for (int i = 0; i < src->size(); i++) {
if (rect.intersects(src->at(i).poly.boundingRect())) const MapData::Path &path = src->at(i);
dst->append(src->at(i)); if (rect.intersects(path.poly.boundingRect()))
dst->append(path);
}
} }
static void copyPoints(const RectC &rect, const QList<MapData::Point> *src, static void copyPoints(const RectC &rect, const QList<MapData::Point> *src,
QList<MapData::Point> *dst) QList<MapData::Point> *dst)
{ {
for (int i = 0; i < src->size(); i++) for (int i = 0; i < src->size(); i++) {
if (rect.contains(src->at(i).coordinates)) const MapData::Point &point = src->at(i);
dst->append(src->at(i)); if (rect.contains(point.coordinates))
dst->append(point);
}
} }
static double distance(const Coordinates &c1, const Coordinates &c2) static double distance(const Coordinates &c1, const Coordinates &c2)
@ -208,13 +212,15 @@ bool MapData::readTags(SubFile &subfile, int count,
bool MapData::readSubFiles() bool MapData::readSubFiles()
{ {
QDataStream stream(&_file); /* both _pointFile and _pathFile can be used here */
QDataStream stream(&_pointFile);
for (int i = 0; i < _subFiles.size(); i++) { for (int i = 0; i < _subFiles.size(); i++) {
const SubFileInfo &f = _subFiles.at(i); const SubFileInfo &f = _subFiles.at(i);
quint64 offset, nextOffset; quint64 offset, nextOffset;
stream.device()->seek(f.offset); if (!stream.device()->seek(f.offset))
return false;
QPoint tl(OSM::ll2tile(_bounds.topLeft(), f.base)); QPoint tl(OSM::ll2tile(_bounds.topLeft(), f.base));
QPoint br(OSM::ll2tile(_bounds.bottomRight(), f.base)); QPoint br(OSM::ll2tile(_bounds.bottomRight(), f.base));
@ -359,7 +365,7 @@ bool MapData::readMapInfo(SubFile &hdr, QByteArray &projection, bool &debugMap)
return true; return true;
} }
bool MapData::readHeader() bool MapData::readHeader(QFile &file)
{ {
char magic[MAGIC_SIZE]; char magic[MAGIC_SIZE];
quint32 hdrSize; quint32 hdrSize;
@ -367,18 +373,18 @@ bool MapData::readHeader()
bool debugMap; bool debugMap;
if (_file.read(magic, MAGIC_SIZE) < (qint64)MAGIC_SIZE if (file.read(magic, MAGIC_SIZE) < (qint64)MAGIC_SIZE
|| memcmp(magic, MAGIC, MAGIC_SIZE)) { || memcmp(magic, MAGIC, MAGIC_SIZE)) {
_errorString = "Not a Mapsforge map"; _errorString = "Not a Mapsforge map";
return false; return false;
} }
if (_file.read((char*)&hdrSize, sizeof(hdrSize)) < (qint64)sizeof(hdrSize)) { if (file.read((char*)&hdrSize, sizeof(hdrSize)) < (qint64)sizeof(hdrSize)) {
_errorString = "Unexpected EOF"; _errorString = "Unexpected EOF";
return false; return false;
} }
SubFile hdr(_file, MAGIC_SIZE, qFromBigEndian(hdrSize)); SubFile hdr(file, MAGIC_SIZE, qFromBigEndian(hdrSize));
if (!readMapInfo(hdr, projection, debugMap)) { if (!readMapInfo(hdr, projection, debugMap)) {
_errorString = "Error reading map info"; _errorString = "Error reading map info";
@ -407,18 +413,19 @@ bool MapData::readHeader()
return true; return true;
} }
MapData::MapData(const QString &fileName) : _file(fileName), _valid(false) MapData::MapData(const QString &fileName)
: _pointFile(fileName), _pathFile(fileName), _valid(false)
{ {
if (!_file.open(QFile::ReadOnly | QIODevice::Unbuffered)) { QFile file(fileName);
_errorString = _file.errorString();
if (!file.open(QFile::ReadOnly | QIODevice::Unbuffered)) {
_errorString = file.errorString();
return; return;
} }
if (!readHeader()) if (!readHeader(file))
return; return;
_file.close();
_pathCache.setMaxCost(256); _pathCache.setMaxCost(256);
_pointCache.setMaxCost(256); _pointCache.setMaxCost(256);
@ -444,13 +451,16 @@ RectC MapData::bounds() const
void MapData::load() void MapData::load()
{ {
if (_file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)) _pointFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
readSubFiles(); _pathFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
readSubFiles();
} }
void MapData::clear() void MapData::clear()
{ {
_file.close(); _pointFile.close();
_pathFile.close();
_pathCache.clear(); _pathCache.clear();
_pointCache.clear(); _pointCache.clear();
@ -497,6 +507,9 @@ int MapData::level(int zoom) const
void MapData::points(const RectC &rect, int zoom, QList<Point> *list) void MapData::points(const RectC &rect, int zoom, QList<Point> *list)
{ {
if (!rect.isValid())
return;
int l(level(zoom)); int l(level(zoom));
PointCTX ctx(this, rect, zoom, list); PointCTX ctx(this, rect, zoom, list);
double min[2], max[2]; double min[2], max[2];
@ -513,6 +526,9 @@ void MapData::points(const VectorTile *tile, const RectC &rect, int zoom,
QList<Point> *list) QList<Point> *list)
{ {
Key key(tile, zoom); Key key(tile, zoom);
_pointLock.lock();
QList<Point> *cached = _pointCache.object(key); QList<Point> *cached = _pointCache.object(key);
if (!cached) { if (!cached) {
@ -524,18 +540,24 @@ void MapData::points(const VectorTile *tile, const RectC &rect, int zoom,
delete p; delete p;
} else } else
copyPoints(rect, cached, list); copyPoints(rect, cached, list);
_pointLock.unlock();
} }
void MapData::paths(const RectC &rect, int zoom, QList<Path> *list) void MapData::paths(const RectC &searchRect, const RectC &boundsRect, int zoom,
QList<Path> *list)
{ {
if (!searchRect.isValid())
return;
int l(level(zoom)); int l(level(zoom));
PathCTX ctx(this, rect, zoom, list); PathCTX ctx(this, boundsRect, zoom, list);
double min[2], max[2]; double min[2], max[2];
min[0] = rect.left(); min[0] = searchRect.left();
min[1] = rect.bottom(); min[1] = searchRect.bottom();
max[0] = rect.right(); max[0] = searchRect.right();
max[1] = rect.top(); max[1] = searchRect.top();
_tiles.at(l)->Search(min, max, pathCb, &ctx); _tiles.at(l)->Search(min, max, pathCb, &ctx);
} }
@ -544,6 +566,9 @@ void MapData::paths(const VectorTile *tile, const RectC &rect, int zoom,
QList<Path> *list) QList<Path> *list)
{ {
Key key(tile, zoom); Key key(tile, zoom);
_pathLock.lock();
QList<Path> *cached = _pathCache.object(key); QList<Path> *cached = _pathCache.object(key);
if (!cached) { if (!cached) {
@ -555,12 +580,14 @@ void MapData::paths(const VectorTile *tile, const RectC &rect, int zoom,
delete p; delete p;
} else } else
copyPaths(rect, cached, list); copyPaths(rect, cached, list);
_pathLock.unlock();
} }
bool MapData::readPaths(const VectorTile *tile, int zoom, QList<Path> *list) bool MapData::readPaths(const VectorTile *tile, int zoom, QList<Path> *list)
{ {
const SubFileInfo &info = _subFiles.at(level(zoom)); const SubFileInfo &info = _subFiles.at(level(zoom));
SubFile subfile(_file, info.offset, info.size); SubFile subfile(_pathFile, info.offset, info.size);
int rows = info.max - info.min + 1; int rows = info.max - info.min + 1;
QVector<unsigned> paths(rows); QVector<unsigned> paths(rows);
quint32 blocks, unused, val, cnt = 0; quint32 blocks, unused, val, cnt = 0;
@ -584,8 +611,10 @@ bool MapData::readPaths(const VectorTile *tile, int zoom, QList<Path> *list)
if (!subfile.seek(subfile.pos() + val)) if (!subfile.seek(subfile.pos() + val))
return false; return false;
paths.reserve(paths[zoom - info.min]);
for (unsigned i = 0; i < paths[zoom - info.min]; i++) { for (unsigned i = 0; i < paths[zoom - info.min]; i++) {
Path p(subfile.offset() + subfile.pos()); Path p;
qint32 lon = 0, lat = 0; qint32 lon = 0, lat = 0;
if (!(subfile.readVUInt32(unused) && subfile.readUInt16(bitmap) if (!(subfile.readVUInt32(unused) && subfile.readUInt16(bitmap)
@ -644,7 +673,7 @@ bool MapData::readPaths(const VectorTile *tile, int zoom, QList<Path> *list)
bool MapData::readPoints(const VectorTile *tile, int zoom, QList<Point> *list) bool MapData::readPoints(const VectorTile *tile, int zoom, QList<Point> *list)
{ {
const SubFileInfo &info = _subFiles.at(level(zoom)); const SubFileInfo &info = _subFiles.at(level(zoom));
SubFile subfile(_file, info.offset, info.size); SubFile subfile(_pointFile, info.offset, info.size);
int rows = info.max - info.min + 1; int rows = info.max - info.min + 1;
QVector<unsigned> points(rows); QVector<unsigned> points(rows);
quint32 val, unused, cnt = 0; quint32 val, unused, cnt = 0;
@ -665,6 +694,8 @@ bool MapData::readPoints(const VectorTile *tile, int zoom, QList<Point> *list)
if (!subfile.readVUInt32(unused)) if (!subfile.readVUInt32(unused))
return false; return false;
list->reserve(points[zoom - info.min]);
for (unsigned i = 0; i < points[zoom - info.min]; i++) { for (unsigned i = 0; i < points[zoom - info.min]; i++) {
qint32 lat, lon; qint32 lat, lon;

View File

@ -3,6 +3,7 @@
#include <QFile> #include <QFile>
#include <QCache> #include <QCache>
#include <QMutex>
#include "common/hash.h" #include "common/hash.h"
#include "common/rectc.h" #include "common/rectc.h"
#include "common/rtree.h" #include "common/rtree.h"
@ -36,10 +37,7 @@ public:
}; };
struct Point { struct Point {
Point(const Coordinates &c) : coordinates(c) Point(const Coordinates &c) : id(qHash(c)), coordinates(c) {}
{
id = (quint64)qHash(QPair<double, double>(c.lon(), c.lat()));
}
quint64 id; quint64 id;
Coordinates coordinates; Coordinates coordinates;
@ -48,9 +46,6 @@ public:
}; };
struct Path { struct Path {
Path(quint64 id) : id(id) {}
quint64 id;
Polygon poly; Polygon poly;
QVector<Tag> tags; QVector<Tag> tags;
Coordinates labelPos; Coordinates labelPos;
@ -59,8 +54,6 @@ public:
bool operator<(const Path &other) const bool operator<(const Path &other) const
{return layer < other.layer;} {return layer < other.layer;}
bool operator==(const Path &other) const
{return (id == other.id);}
}; };
RectC bounds() const; RectC bounds() const;
@ -69,7 +62,8 @@ public:
int tileSize() const {return _tileSize;} int tileSize() const {return _tileSize;}
void points(const RectC &rect, int zoom, QList<Point> *list); void points(const RectC &rect, int zoom, QList<Point> *list);
void paths(const RectC &rect, int zoom, QList<Path> *set); void paths(const RectC &searchRect, const RectC &boundsRect, int zoom,
QList<Path> *set);
unsigned tagId(const QByteArray &name) const {return _keys.value(name);} unsigned tagId(const QByteArray &name) const {return _keys.value(name);}
void load(); void load();
@ -146,7 +140,7 @@ private:
bool readTagInfo(SubFile &hdr); bool readTagInfo(SubFile &hdr);
bool readTagInfo(SubFile &hdr, QVector<TagSource> &tags); bool readTagInfo(SubFile &hdr, QVector<TagSource> &tags);
bool readMapInfo(SubFile &hdr, QByteArray &projection, bool &debugMap); bool readMapInfo(SubFile &hdr, QByteArray &projection, bool &debugMap);
bool readHeader(); bool readHeader(QFile &file);
bool readSubFiles(); bool readSubFiles();
void clearTiles(); void clearTiles();
@ -165,7 +159,7 @@ private:
friend HASH_T qHash(const MapData::Key &key); friend HASH_T qHash(const MapData::Key &key);
QFile _file; QFile _pointFile, _pathFile;
RectC _bounds; RectC _bounds;
quint16 _tileSize; quint16 _tileSize;
QVector<SubFileInfo> _subFiles; QVector<SubFileInfo> _subFiles;
@ -175,6 +169,7 @@ private:
QCache<Key, QList<Path> > _pathCache; QCache<Key, QList<Path> > _pathCache;
QCache<Key, QList<Point> > _pointCache; QCache<Key, QList<Point> > _pointCache;
QMutex _pathLock, _pointLock;
bool _valid; bool _valid;
QString _errorString; QString _errorString;
@ -190,11 +185,6 @@ inline HASH_T qHash(const MapData::Tag &tag)
return ::qHash(tag.key) ^ ::qHash(tag.value); return ::qHash(tag.key) ^ ::qHash(tag.value);
} }
inline HASH_T qHash(const MapData::Path &path)
{
return ::qHash(path.id);
}
} }
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG

View File

@ -1,10 +1,17 @@
#include <cmath>
#include <QPainter> #include <QPainter>
#include <QCache> #include <QCache>
#include "common/programpaths.h" #include "map/rectd.h"
#include "rastertile.h" #include "rastertile.h"
using namespace Mapsforge; using namespace Mapsforge;
#define TEXT_EXTENT 160
#define PATHS_EXTENT 20
#define SEARCH_EXTENT -0.5
static double limit = cos(deg2rad(170));
static qreal area(const QPainterPath &polygon) static qreal area(const QPainterPath &polygon)
{ {
qreal area = 0; qreal area = 0;
@ -52,44 +59,89 @@ static const QColor *haloColor(const Style::TextRender *ti)
? &ti->strokeColor() : 0; ? &ti->strokeColor() : 0;
} }
void RasterTile::processPointLabels(QList<TextItem*> &textItems) static QPainterPath parallelPath(const QPainterPath &p, double dy)
{
int n = p.elementCount() - 1;
QVector<QPointF> u(n);
QPainterPath h;
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
h.reserve(p.elementCount());
#endif // QT 5.13
for (int k = 0; k < n; k++) {
qreal c = p.elementAt(k + 1).x - p.elementAt(k).x;
qreal s = p.elementAt(k + 1).y - p.elementAt(k).y;
qreal l = sqrt(c * c + s * s);
u[k] = (l == 0) ? QPointF(0, 0) : QPointF(c / l, s / l);
if (k == 0)
continue;
if (u.at(k).x() * u.at(k-1).x() + u.at(k).y() * u.at(k-1).y() < limit)
return p;
}
h.moveTo(QPointF(p.elementAt(0).x - dy * u.at(0).y(),
p.elementAt(0).y + dy * u.at(0).x()));
for (int k = 1; k < n; k++) {
qreal l = dy / (1 + u.at(k).x() * u.at(k-1).x()
+ u.at(k).y() * u.at(k-1).y());
QPainterPath::Element e(p.elementAt(k));
h.lineTo(QPointF(e.x - l * (u.at(k).y() + u.at(k-1).y()),
e.y + l * (u.at(k).x() + u.at(k-1).x())));
}
h.lineTo(QPointF(p.elementAt(n).x - dy * u.at(n-1).y(),
p.elementAt(n).y + dy * u.at(n-1).x()));
return h;
}
void RasterTile::processPointLabels(const QList<MapData::Point> &points,
QList<TextItem*> &textItems) const
{ {
QList<const Style::TextRender*> labels(_style->pointLabels(_zoom)); QList<const Style::TextRender*> labels(_style->pointLabels(_zoom));
QList<const Style::Symbol*> symbols(_style->pointSymbols(_zoom)); QList<const Style::Symbol*> symbols(_style->pointSymbols(_zoom));
QList<PainterPoint> points; QList<PointText> items;
for (int i = 0; i < _points.size(); i++) { for (int i = 0; i < points.size(); i++) {
const MapData::Point &point = _points.at(i); const MapData::Point &point = points.at(i);
const QByteArray *lbl = 0;
const Style::TextRender *ti = 0; const Style::TextRender *ti = 0;
const Style::Symbol *si = 0; const Style::Symbol *si = 0;
const QByteArray *lbl = 0;
for (int j = 0; j < symbols.size(); j++) {
const Style::Symbol *ri = symbols.at(j);
if (ri->rule().match(point.tags))
if (!si || si->priority() < ri->priority())
si = ri;
}
for (int j = 0; j < labels.size(); j++) { for (int j = 0; j < labels.size(); j++) {
const Style::TextRender *ri = labels.at(j); const Style::TextRender *ri = labels.at(j);
if (ri->rule().match(point.tags)) { if (ri->rule().match(point.tags)) {
if ((lbl = label(ri->key(), point.tags))) { if ((lbl = label(ri->key(), point.tags))) {
if (si && si->id() != ri->symbolId())
continue;
ti = ri; ti = ri;
break; break;
} }
} }
} }
for (int j = 0; j < symbols.size(); j++) {
const Style::Symbol *ri = symbols.at(j);
if (ri->rule().match(point.tags)) {
si = ri;
break;
}
}
if (ti || si) if (ti || si)
points.append(PainterPoint(&point, lbl, si, ti)); items.append(PointText(&point, lbl, si, ti));
} }
std::sort(points.begin(), points.end()); std::sort(items.begin(), items.end());
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < items.size(); i++) {
const PainterPoint &p = points.at(i); const PointText &p = items.at(i);
const QImage *img = p.si ? &p.si->img() : 0; const QImage *img = p.si ? &p.si->img() : 0;
const QFont *font = p.ti ? &p.ti->font() : 0; const QFont *font = p.ti ? &p.ti->font() : 0;
const QColor *color = p.ti ? &p.ti->fillColor() : 0; const QColor *color = p.ti ? &p.ti->fillColor() : 0;
@ -104,14 +156,15 @@ void RasterTile::processPointLabels(QList<TextItem*> &textItems)
} }
} }
void RasterTile::processAreaLabels(QList<TextItem*> &textItems, void RasterTile::processAreaLabels(const QVector<PainterPath> &paths,
QVector<PainterPath> &paths) QList<TextItem*> &textItems) const
{ {
QList<const Style::TextRender*> labels(_style->areaLabels(_zoom)); QList<const Style::TextRender*> labels(_style->areaLabels(_zoom));
QList<const Style::Symbol*> symbols(_style->areaSymbols(_zoom)); QList<const Style::Symbol*> symbols(_style->areaSymbols(_zoom));
QList<PathText> items;
for (int i = 0; i < paths.size(); i++) { for (int i = 0; i < paths.size(); i++) {
PainterPath &path = paths[i]; const PainterPath &path = paths.at(i);
const Style::TextRender *ti = 0; const Style::TextRender *ti = 0;
const Style::Symbol *si = 0; const Style::Symbol *si = 0;
const QByteArray *lbl = 0; const QByteArray *lbl = 0;
@ -119,6 +172,69 @@ void RasterTile::processAreaLabels(QList<TextItem*> &textItems,
if (!path.path->closed) if (!path.path->closed)
continue; continue;
for (int j = 0; j < symbols.size(); j++) {
const Style::Symbol *ri = symbols.at(j);
if (ri->rule().match(path.path->closed, path.path->tags))
if (!si || si->priority() < ri->priority())
si = ri;
}
for (int j = 0; j < labels.size(); j++) {
const Style::TextRender *ri = labels.at(j);
if (ri->rule().match(path.path->closed, path.path->tags)) {
if ((lbl = label(ri->key(), path.path->tags))) {
if (si && si->id() != ri->symbolId())
continue;
ti = ri;
break;
}
}
}
if (ti || si)
items.append(PathText(&path, lbl, si, ti));
}
std::sort(items.begin(), items.end());
for (int i = 0; i < items.size(); i++) {
const PathText &p = items.at(i);
const QImage *img = p.si ? &p.si->img() : 0;
const QFont *font = p.ti ? &p.ti->font() : 0;
const QColor *color = p.ti ? &p.ti->fillColor() : 0;
const QColor *hColor = p.ti ? haloColor(p.ti) : 0;
QPointF pos = p.p->path->labelPos.isNull()
? centroid(p.p->pp) : ll2xy(p.p->path->labelPos);
PointItem *item = new PointItem(pos.toPoint(), p.lbl, font, img, color,
hColor);
if (item->isValid() && _rect.contains(item->boundingRect().toRect())
&& !item->collides(textItems))
textItems.append(item);
else
delete item;
}
}
void RasterTile::processLineLabels(const QVector<PainterPath> &paths,
QList<TextItem*> &textItems) const
{
QList<const Style::TextRender*> labels(_style->pathLabels(_zoom));
QList<const Style::Symbol*> symbols(_style->lineSymbols(_zoom));
QList<PathText> items;
QSet<QByteArray> set;
for (int i = 0; i < paths.size(); i++) {
const PainterPath &path = paths.at(i);
const Style::TextRender *ti = 0;
const Style::Symbol *si = 0;
const QByteArray *lbl = 0;
if (path.path->closed)
continue;
for (int j = 0; j < labels.size(); j++) { for (int j = 0; j < labels.size(); j++) {
const Style::TextRender *ri = labels.at(j); const Style::TextRender *ri = labels.at(j);
if (ri->rule().match(path.path->closed, path.path->tags)) { if (ri->rule().match(path.path->closed, path.path->tags)) {
@ -130,61 +246,50 @@ void RasterTile::processAreaLabels(QList<TextItem*> &textItems,
for (int j = 0; j < symbols.size(); j++) { for (int j = 0; j < symbols.size(); j++) {
const Style::Symbol *ri = symbols.at(j); const Style::Symbol *ri = symbols.at(j);
if (ri->rule().match(path.path->tags)) { if (ri->rule().match(path.path->closed, path.path->tags)) {
si = ri; si = ri;
break; break;
} }
} }
if (!ti && !si) if (ti || si)
continue; items.append(PathText(&path, lbl, si, ti));
const QImage *img = si ? &si->img() : 0;
const QFont *font = ti ? &ti->font() : 0;
const QColor *color = ti ? &ti->fillColor() : 0;
const QColor *hColor = ti ? haloColor(ti) : 0;
QPointF pos = path.path->labelPos.isNull()
? centroid(path.pp) : ll2xy(path.path->labelPos);
PointItem *item = new PointItem(pos.toPoint(), lbl, font, img, color,
hColor);
if (item->isValid() && _rect.contains(item->boundingRect().toRect())
&& !item->collides(textItems))
textItems.append(item);
else
delete item;
} }
}
void RasterTile::processLineLabels(QList<TextItem*> &textItems, std::sort(items.begin(), items.end());
QVector<PainterPath> &paths)
{
QList<const Style::TextRender*> instructions(_style->pathLabels(_zoom));
QSet<QByteArray> set;
for (int i = 0; i < instructions.size(); i++) { for (int i = 0; i < items.size(); i++) {
const Style::TextRender *ri = instructions.at(i); const PathText &p = items.at(i);
const QImage *img = p.si ? &p.si->img() : 0;
const QFont *font = p.ti ? &p.ti->font() : 0;
const QColor *color = p.ti ? &p.ti->fillColor() : 0;
const QColor *hColor = p.ti ? haloColor(p.ti) : 0;
bool rotate = p.si ? p.si->rotate() : false;
bool limit = false;
for (int i = 0; i < paths.size(); i++) { if (p.ti) {
PainterPath &path = paths[i]; limit = (p.ti->key() == ID_ELE || p.ti->key() == ID_REF);
const QByteArray *lbl = label(ri->key(), path.path->tags); if (limit && set.contains(*p.lbl))
if (!lbl)
continue;
if (!ri->rule().match(path.path->closed, path.path->tags))
continue;
bool limit = (ri->key() == ID_ELE || ri->key() == ID_REF);
if (limit && set.contains(*lbl))
continue; continue;
}
PathItem *item = new PathItem(path.pp, lbl, _rect, &ri->font(), PathItem *item = new PathItem(p.p->pp, p.lbl, img, _rect, font, color,
&ri->fillColor(), haloColor(ri)); hColor, rotate);
if (item->isValid() && !item->collides(textItems)) { if (item->isValid() && !item->collides(textItems)) {
textItems.append(item); textItems.append(item);
if (limit) if (limit)
set.insert(*lbl); set.insert(*p.lbl);
} else } else {
delete item; delete item;
if (img && p.lbl) {
PathItem *item = new PathItem(p.p->pp, 0, img, _rect, 0, 0, 0,
rotate);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
} }
} }
} }
@ -200,17 +305,17 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const
{ {
QPainterPath path; QPainterPath path;
if (curve) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
int size = 0; int size = 0;
for (int i = 0; i < polygon.size(); i++) for (int i = 0; i < polygon.size(); i++)
size += polygon.at(i).size(); size += polygon.at(i).size();
path.reserve(size); path.reserve(size);
#endif // QT 5.13 #endif // QT 5.13
for (int i = 0; i < polygon.size(); i++) { for (int i = 0; i < polygon.size(); i++) {
const QVector<Coordinates> &subpath = polygon.at(i); const QVector<Coordinates> &subpath = polygon.at(i);
if (curve) {
QPointF p1(ll2xy(subpath.first())); QPointF p1(ll2xy(subpath.first()));
QPointF p2(0, 0); QPointF p2(0, 0);
QPointF p3(0, 0); QPointF p3(0, 0);
@ -223,25 +328,31 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const
p1 = p3; p1 = p3;
} }
path.quadTo(p2, p3); path.quadTo(p2, p3);
} else { }
path.moveTo(ll2xy(subpath.first())); } else {
for (int j = 1; j < subpath.size(); j++) for (int i = 0; i < polygon.size(); i++) {
path.lineTo(ll2xy(subpath.at(j))); const QVector<Coordinates> &subpath = polygon.at(i);
QVector<QPointF> p(subpath.size());
for (int j = 0; j < subpath.size(); j++)
p[j] = ll2xy(subpath.at(j));
path.addPolygon(p);
} }
} }
return path; return path;
} }
void RasterTile::pathInstructions(QVector<PainterPath> &paths, void RasterTile::pathInstructions(const QList<MapData::Path> &paths,
QVector<RasterTile::RenderInstruction> &instructions) QVector<PainterPath> &painterPaths,
QVector<RasterTile::RenderInstruction> &instructions) const
{ {
QCache<PathKey, QList<const Style::PathRender *> > cache(8192); QCache<PathKey, QList<const Style::PathRender *> > cache(8192);
QList<const Style::PathRender*> *ri; QList<const Style::PathRender*> *ri;
for (int i = 0; i < _paths.size(); i++) { for (int i = 0; i < paths.size(); i++) {
const MapData::Path &path = _paths.at(i); const MapData::Path &path = paths.at(i);
PainterPath &rp = paths[i]; PainterPath &rp = painterPaths[i];
PathKey key(_zoom, path.closed, path.tags); PathKey key(_zoom, path.closed, path.tags);
rp.path = &path; rp.path = &path;
@ -259,14 +370,14 @@ void RasterTile::pathInstructions(QVector<PainterPath> &paths,
} }
} }
void RasterTile::circleInstructions( void RasterTile::circleInstructions(const QList<MapData::Point> &points,
QVector<RasterTile::RenderInstruction> &instructions) QVector<RasterTile::RenderInstruction> &instructions) const
{ {
QCache<PointKey, QList<const Style::CircleRender *> > cache(8192); QCache<PointKey, QList<const Style::CircleRender *> > cache(8192);
QList<const Style::CircleRender*> *ri; QList<const Style::CircleRender*> *ri;
for (int i = 0; i < _points.size(); i++) { for (int i = 0; i < points.size(); i++) {
const MapData::Point &point = _points.at(i); const MapData::Point &point = points.at(i);
PointKey key(_zoom, point.tags); PointKey key(_zoom, point.tags);
if (!(ri = cache.object(key))) { if (!(ri = cache.object(key))) {
@ -282,11 +393,12 @@ void RasterTile::circleInstructions(
} }
} }
void RasterTile::drawPaths(QPainter *painter, QVector<PainterPath> &paths) void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
const QList<MapData::Point> &points, QVector<PainterPath> &painterPaths)
{ {
QVector<RenderInstruction> instructions; QVector<RenderInstruction> instructions;
pathInstructions(paths, instructions); pathInstructions(paths, painterPaths, instructions);
circleInstructions(instructions); circleInstructions(points, instructions);
std::sort(instructions.begin(), instructions.end()); std::sort(instructions.begin(), instructions.end());
for (int i = 0; i < instructions.size(); i++) { for (int i = 0; i < instructions.size(); i++) {
@ -295,13 +407,18 @@ void RasterTile::drawPaths(QPainter *painter, QVector<PainterPath> &paths)
if (path) { if (path) {
const Style::PathRender *ri = is.pathRender(); const Style::PathRender *ri = is.pathRender();
qreal dy = ri->dy(_zoom);
if (!path->pp.elementCount()) if (!path->pp.elementCount())
path->pp = painterPath(path->path->poly, ri->curve()); path->pp = painterPath(path->path->poly, ri->curve());
painter->setPen(ri->pen(_zoom)); painter->setPen(ri->pen(_zoom));
painter->setBrush(ri->brush()); painter->setBrush(ri->brush());
painter->drawPath(path->pp);
if (dy != 0)
painter->drawPath(parallelPath(path->pp, dy));
else
painter->drawPath(path->pp);
} else { } else {
const Style::CircleRender *ri = is.circleRender(); const Style::CircleRender *ri = is.circleRender();
qreal radius = ri->radius(_zoom); qreal radius = ri->radius(_zoom);
@ -313,28 +430,61 @@ void RasterTile::drawPaths(QPainter *painter, QVector<PainterPath> &paths)
} }
} }
void RasterTile::fetchData(QList<MapData::Path> &paths,
QList<MapData::Point> &points) const
{
QPoint ttl(_rect.topLeft());
QRectF pathRect(QPointF(ttl.x() - PATHS_EXTENT, ttl.y() - PATHS_EXTENT),
QPointF(ttl.x() + _rect.width() + PATHS_EXTENT, ttl.y() + _rect.height()
+ PATHS_EXTENT));
QRectF searchRect(QPointF(ttl.x() - SEARCH_EXTENT, ttl.y() - SEARCH_EXTENT),
QPointF(ttl.x() + _rect.width() + SEARCH_EXTENT, ttl.y() + _rect.height()
+ SEARCH_EXTENT));
RectD pathRectD(_transform.img2proj(pathRect.topLeft()),
_transform.img2proj(pathRect.bottomRight()));
RectD searchRectD(_transform.img2proj(searchRect.topLeft()),
_transform.img2proj(searchRect.bottomRight()));
_data->paths(searchRectD.toRectC(_proj, 20), pathRectD.toRectC(_proj, 20),
_zoom, &paths);
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
+ TEXT_EXTENT));
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data->points(pointRectD.toRectC(_proj, 20), _zoom, &points);
}
void RasterTile::render() void RasterTile::render()
{ {
QList<MapData::Path> paths;
QList<MapData::Point> points;
fetchData(paths, points);
QList<TextItem*> textItems; QList<TextItem*> textItems;
QVector<PainterPath> renderPaths(_paths.size()); QVector<PainterPath> renderPaths(paths.size());
_pixmap.setDevicePixelRatio(_ratio); _pixmap.setDevicePixelRatio(_ratio);
_pixmap.fill(Qt::transparent); _pixmap.fill(Qt::transparent);
QPainter painter(&_pixmap); QPainter painter(&_pixmap);
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.translate(-_rect.x(), -_rect.y()); painter.translate(-_rect.x(), -_rect.y());
drawPaths(&painter, renderPaths); drawPaths(&painter, paths, points, renderPaths);
processPointLabels(textItems); processPointLabels(points, textItems);
processAreaLabels(textItems, renderPaths); processAreaLabels(renderPaths, textItems);
processLineLabels(textItems, renderPaths); processLineLabels(renderPaths, textItems);
drawTextItems(&painter, textItems); drawTextItems(&painter, textItems);
//painter.setPen(Qt::red); //painter.setPen(Qt::red);
//painter.setBrush(Qt::NoBrush); //painter.setBrush(Qt::NoBrush);
//painter.drawRect(QRect(_rect.topLeft(), _pixmap.size())); //painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(_rect);
qDeleteAll(textItems); qDeleteAll(textItems);

View File

@ -15,11 +15,10 @@ class RasterTile
{ {
public: public:
RasterTile(const Projection &proj, const Transform &transform, RasterTile(const Projection &proj, const Transform &transform,
const Style *style, int zoom, const QRect &rect, qreal ratio, const Style *style, MapData *data, int zoom, const QRect &rect,
const QList<MapData::Path> &paths, const QList<MapData::Point> &points) qreal ratio) : _proj(proj), _transform(transform), _style(style),
: _proj(proj), _transform(transform), _style(style), _data(data), _zoom(zoom), _rect(rect), _ratio(ratio),
_zoom(zoom), _rect(rect), _ratio(ratio), _pixmap(rect.width() * ratio, _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {}
rect.height() * ratio), _paths(paths), _points(points), _valid(false) {}
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
QPoint xy() const {return _rect.topLeft();} QPoint xy() const {return _rect.topLeft();}
@ -36,15 +35,15 @@ private:
const MapData::Path *path; const MapData::Path *path;
}; };
struct PainterPoint { struct PointText {
PainterPoint(const MapData::Point *p, const QByteArray *lbl, PointText(const MapData::Point *p, const QByteArray *lbl,
const Style::Symbol *si, const Style::TextRender *ti) const Style::Symbol *si, const Style::TextRender *ti)
: p(p), lbl(lbl), ti(ti), si(si) : p(p), lbl(lbl), ti(ti), si(si)
{ {
Q_ASSERT(si || ti); Q_ASSERT(si || ti);
} }
bool operator<(const PainterPoint &other) const bool operator<(const PointText &other) const
{ {
if (priority() == other.priority()) if (priority() == other.priority())
return p->id < other.p->id; return p->id < other.p->id;
@ -59,6 +58,26 @@ private:
const Style::Symbol *si; const Style::Symbol *si;
}; };
struct PathText {
PathText(const PainterPath *p, const QByteArray *lbl,
const Style::Symbol *si, const Style::TextRender *ti)
: p(p), lbl(lbl), ti(ti), si(si)
{
Q_ASSERT(si || ti);
}
bool operator<(const PathText &other) const
{
return (priority() > other.priority());
}
int priority() const {return si ? si->priority() : ti->priority();}
const PainterPath *p;
const QByteArray *lbl;
const Style::TextRender *ti;
const Style::Symbol *si;
};
class RenderInstruction class RenderInstruction
{ {
public: public:
@ -138,40 +157,44 @@ private:
{ {
public: public:
PathItem(const QPainterPath &line, const QByteArray *label, PathItem(const QPainterPath &line, const QByteArray *label,
const QRect &tileRect, const QFont *font, const QColor *color, const QImage *img, const QRect &tileRect, const QFont *font,
const QColor *haloColor) : TextPathItem(line, const QColor *color, const QColor *haloColor, bool rotate)
label ? new QString(*label) : 0, tileRect, font, color, haloColor) {} : TextPathItem(line, label ? new QString(*label) : 0, tileRect, font,
color, haloColor, img, rotate) {}
~PathItem() {delete _text;} ~PathItem() {delete _text;}
}; };
friend HASH_T qHash(const RasterTile::PathKey &key); friend HASH_T qHash(const RasterTile::PathKey &key);
friend HASH_T qHash(const RasterTile::PointKey &key); friend HASH_T qHash(const RasterTile::PointKey &key);
void pathInstructions(QVector<PainterPath> &paths, void fetchData(QList<MapData::Path> &paths,
QVector<RasterTile::RenderInstruction> &instructions); QList<MapData::Point> &points) const;
void circleInstructions(QVector<RasterTile::RenderInstruction> &instructions); void pathInstructions(const QList<MapData::Path> &paths,
QVector<PainterPath> &painterPaths,
QVector<RasterTile::RenderInstruction> &instructions) const;
void circleInstructions(const QList<MapData::Point> &points,
QVector<RasterTile::RenderInstruction> &instructions) const;
QPointF ll2xy(const Coordinates &c) const QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));} {return _transform.proj2img(_proj.ll2xy(c));}
void processPointLabels(QList<TextItem*> &textItems); void processPointLabels(const QList<MapData::Point> &points,
void processAreaLabels(QList<TextItem*> &textItems, QList<TextItem*> &textItems) const;
QVector<PainterPath> &paths); void processAreaLabels(const QVector<PainterPath> &paths,
void processLineLabels(QList<TextItem*> &textItems, QList<TextItem*> &textItems) const;
QVector<PainterPath> &paths); void processLineLabels(const QVector<PainterPath> &paths,
QList<TextItem*> &textItems) const;
QPainterPath painterPath(const Polygon &polygon, bool curve) const; QPainterPath painterPath(const Polygon &polygon, bool curve) const;
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems); void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
void drawPaths(QPainter *painter, QVector<PainterPath> &paths); void drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
const QList<MapData::Point> &points, QVector<PainterPath> &painterPaths);
Projection _proj; Projection _proj;
Transform _transform; Transform _transform;
const Style *_style; const Style *_style;
MapData *_data;
int _zoom; int _zoom;
QRect _rect; QRect _rect;
qreal _ratio; qreal _ratio;
QPixmap _pixmap; QPixmap _pixmap;
QList<MapData::Path> _paths;
QList<MapData::Point> _points;
bool _valid; bool _valid;
}; };

View File

@ -17,18 +17,26 @@ static QString resourcePath(const QString &src, const QString &dir)
return dir + "/" + url.toLocalFile(); return dir + "/" + url.toLocalFile();
} }
static QImage image(const QString &path, int width, int height, qreal ratio) static QImage image(const QString &path, int width, int height, int percent,
qreal ratio)
{ {
QImageReader ir(path, "svg"); QImageReader ir(path, "svg");
if (ir.canRead()) { if (ir.canRead()) {
QSize s(ir.size());
if (!height && !width) { if (!height && !width) {
height = 20; height = 20;
width = 20; width = 20;
} else if (!width) { } else if (!width) {
width = ir.size().height() / (ir.size().height() / (double)height); width = s.height() / (s.height() / (double)height);
} else if (!height) } else if (!height)
height = ir.size().width() / (ir.size().width() / (double)width); height = s.width() / (s.width() / (double)width);
if (percent != 100) {
width *= percent / 100.0;
height *= percent / 100.0;
}
ir.setScaledSize(QSize(width * ratio, height * ratio)); ir.setScaledSize(QSize(width * ratio, height * ratio));
QImage img(ir.read()); QImage img(ir.read());
@ -69,6 +77,42 @@ static QList<QByteArray> valList(const QList<QByteArray> &in)
return out; return out;
} }
const Style::Menu::Layer *Style::Menu::findLayer(const QString &id) const
{
for (int i = 0; i < _layers.size(); i++)
if (_layers.at(i).id() == id)
return &_layers.at(i);
qWarning("%s: layer not found", qPrintable(id));
return 0;
}
void Style::Menu::addCats(const Layer *layer, QSet<QString> &cats) const
{
if (!layer)
return;
if (!layer->parent().isNull())
addCats(findLayer(layer->parent()), cats);
for (int i = 0; i < layer->cats().size(); i++)
cats.insert(layer->cats().at(i));
for (int i = 0; i < layer->overlays().size(); i++) {
const Layer *overlay = findLayer(layer->overlays().at(i));
if (overlay && overlay->enabled())
addCats(overlay, cats);
}
}
QSet<QString> Style::Menu::cats() const
{
QSet<QString> c;
addCats(findLayer(_defaultvalue), c);
return c;
}
Style::Rule::Filter::Filter(const MapData &data, const QList<QByteArray> &keys, Style::Rule::Filter::Filter(const MapData &data, const QList<QByteArray> &keys,
const QList<QByteArray> &vals) : _neg(false) const QList<QByteArray> &vals) : _neg(false)
{ {
@ -139,7 +183,7 @@ void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
const QXmlStreamAttributes &attr = reader.attributes(); const QXmlStreamAttributes &attr = reader.attributes();
QString file; QString file;
QColor fillColor; QColor fillColor;
int height = 0, width = 0; int height = 0, width = 0, percent = 100;
bool ok; bool ok;
ri._area = true; ri._area = true;
@ -154,6 +198,13 @@ void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
return; return;
} }
} }
if (attr.hasAttribute("scale")) {
QString scale(attr.value("scale").toString());
if (scale == "all")
ri._scale = PathRender::Scale::All;
else if (scale == "none")
ri._scale = PathRender::Scale::None;
}
if (attr.hasAttribute("src")) if (attr.hasAttribute("src"))
file = resourcePath(attr.value("src").toString(), dir); file = resourcePath(attr.value("src").toString(), dir);
if (attr.hasAttribute("symbol-height")) { if (attr.hasAttribute("symbol-height")) {
@ -170,9 +221,16 @@ void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
return; return;
} }
} }
if (attr.hasAttribute("symbol-percent")) {
percent = attr.value("symbol-percent").toInt(&ok);
if (!ok || percent < 0) {
reader.raiseError("invalid symbol-percent value");
return;
}
}
if (!file.isNull()) if (!file.isNull())
ri._brush = QBrush(image(file, width, height, ratio)); ri._brush = QBrush(image(file, width, height, percent, ratio));
else if (fillColor.isValid()) else if (fillColor.isValid())
ri._brush = QBrush(fillColor); ri._brush = QBrush(fillColor);
@ -188,6 +246,8 @@ void Style::line(QXmlStreamReader &reader, const Rule &rule)
const QXmlStreamAttributes &attr = reader.attributes(); const QXmlStreamAttributes &attr = reader.attributes();
bool ok; bool ok;
ri._brush = Qt::NoBrush;
if (attr.hasAttribute("stroke")) if (attr.hasAttribute("stroke"))
ri._strokeColor = QColor(attr.value("stroke").toString()); ri._strokeColor = QColor(attr.value("stroke").toString());
if (attr.hasAttribute("stroke-width")) { if (attr.hasAttribute("stroke-width")) {
@ -226,11 +286,25 @@ void Style::line(QXmlStreamReader &reader, const Rule &rule)
else if (join == "bevel") else if (join == "bevel")
ri._strokeJoin = Qt::BevelJoin; ri._strokeJoin = Qt::BevelJoin;
} }
if (attr.hasAttribute("scale")) {
QString scale(attr.value("scale").toString());
if (scale == "all")
ri._scale = PathRender::Scale::All;
else if (scale == "none")
ri._scale = PathRender::Scale::None;
}
if (attr.hasAttribute("curve")) { if (attr.hasAttribute("curve")) {
QString curve(attr.value("curve").toString()); QString curve(attr.value("curve").toString());
if (curve == "cubic") if (curve == "cubic")
ri._curve = true; ri._curve = true;
} }
if (attr.hasAttribute("dy")) {
ri._dy = attr.value("dy").toDouble(&ok);
if (!ok) {
reader.raiseError("invalid dy value");
return;
}
}
if (ri.rule()._type == Rule::AnyType || ri.rule()._type == Rule::WayType) if (ri.rule()._type == Rule::AnyType || ri.rule()._type == Rule::WayType)
_paths.append(ri); _paths.append(ri);
@ -347,6 +421,8 @@ void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule
return; return;
} }
} }
if (attr.hasAttribute("symbol-id"))
ri._symbolId = attr.value("symbol-id").toString();
ri._font.setFamily(fontFamily); ri._font.setFamily(fontFamily);
ri._font.setPixelSize(fontSize); ri._font.setPixelSize(fontSize);
@ -362,12 +438,12 @@ void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule
} }
void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
const Rule &rule) const Rule &rule, QList<Symbol> &list)
{ {
Symbol ri(rule); Symbol ri(rule);
const QXmlStreamAttributes &attr = reader.attributes(); const QXmlStreamAttributes &attr = reader.attributes();
QString file; QString file;
int height = 0, width = 0; int height = 0, width = 0, percent = 100;
bool ok; bool ok;
if (attr.hasAttribute("src")) if (attr.hasAttribute("src"))
@ -390,6 +466,13 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
return; return;
} }
} }
if (attr.hasAttribute("symbol-percent")) {
percent = attr.value("symbol-percent").toInt(&ok);
if (!ok || percent < 0) {
reader.raiseError("invalid symbol-percent value");
return;
}
}
if (attr.hasAttribute("priority")) { if (attr.hasAttribute("priority")) {
ri._priority = attr.value("priority").toInt(&ok); ri._priority = attr.value("priority").toInt(&ok);
if (!ok) { if (!ok) {
@ -397,10 +480,16 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
return; return;
} }
} }
if (attr.hasAttribute("rotate")) {
if (attr.value("rotate").toString() == "false")
ri._rotate = false;
}
if (attr.hasAttribute("id"))
ri._id = attr.value("id").toString();
ri._img = image(file, width, height, ratio); ri._img = image(file, width, height, percent, ratio);
_symbols.append(ri); list.append(ri);
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
@ -472,42 +561,73 @@ void Style::rule(QXmlStreamReader &reader, const QString &dir,
text(reader, data, r, list); text(reader, data, r, list);
} }
else if (reader.name() == QLatin1String("symbol")) else if (reader.name() == QLatin1String("symbol"))
symbol(reader, dir, ratio, r); symbol(reader, dir, ratio, r, _symbols);
else if (reader.name() == QLatin1String("lineSymbol"))
symbol(reader, dir, ratio, r, _lineSymbols);
else else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
void Style::cat(QXmlStreamReader &reader, QSet<QString> &cats) QString Style::cat(QXmlStreamReader &reader)
{ {
const QXmlStreamAttributes &attr = reader.attributes(); const QXmlStreamAttributes &attr = reader.attributes();
cats.insert(attr.value("id").toString()); if (!attr.hasAttribute("id")) {
reader.raiseError("Missing id attribute");
return QString();
}
QString id(attr.value("id").toString());
reader.skipCurrentElement(); reader.skipCurrentElement();
return id;
} }
void Style::layer(QXmlStreamReader &reader, QSet<QString> &cats) Style::Menu::Layer Style::layer(QXmlStreamReader &reader)
{ {
const QXmlStreamAttributes &attr = reader.attributes(); const QXmlStreamAttributes &attr = reader.attributes();
bool enabled = (attr.value("enabled").toString() == "true"); if (!attr.hasAttribute("id")) {
reader.raiseError("Missing id attribute");
return Menu::Layer();
}
Menu::Layer l(attr.value("id").toString(),
attr.value("enabled").toString() == "true");
if (attr.hasAttribute("parent"))
l.setParent(attr.value("parent").toString());
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (enabled && reader.name() == QLatin1String("cat")) if (reader.name() == QLatin1String("cat"))
cat(reader, cats); l.addCat(cat(reader));
else if (reader.name() == QLatin1String("overlay"))
l.addOverlay(cat(reader));
else else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
return l;
} }
void Style::stylemenu(QXmlStreamReader &reader, QSet<QString> &cats) Style::Menu Style::stylemenu(QXmlStreamReader &reader)
{ {
const QXmlStreamAttributes &attr = reader.attributes();
if (!attr.hasAttribute("defaultvalue")) {
reader.raiseError("Missing defaultvalue attribute");
return Menu();
}
Style::Menu menu(attr.value("defaultvalue").toString());
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("layer")) if (reader.name() == QLatin1String("layer"))
layer(reader, cats); menu.addLayer(layer(reader));
else else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
return menu;
} }
void Style::rendertheme(QXmlStreamReader &reader, const QString &dir, void Style::rendertheme(QXmlStreamReader &reader, const QString &dir,
@ -519,9 +639,10 @@ void Style::rendertheme(QXmlStreamReader &reader, const QString &dir,
while (reader.readNextStartElement()) { while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("rule")) if (reader.name() == QLatin1String("rule"))
rule(reader, dir, data, ratio, cats, r); rule(reader, dir, data, ratio, cats, r);
else if (reader.name() == QLatin1String("stylemenu")) else if (reader.name() == QLatin1String("stylemenu")) {
stylemenu(reader, cats); Menu menu(stylemenu(reader));
else cats = menu.cats();
} else
reader.skipCurrentElement(); reader.skipCurrentElement();
} }
} }
@ -629,7 +750,7 @@ QList<const Style::Symbol*> Style::pointSymbols(int zoom) const
for (int i = 0; i < _symbols.size(); i++) { for (int i = 0; i < _symbols.size(); i++) {
const Symbol &symbol = _symbols.at(i); const Symbol &symbol = _symbols.at(i);
const Rule & rule = symbol.rule(); const Rule &rule = symbol.rule();
if (rule._zooms.contains(zoom) && (rule._type == Rule::AnyType if (rule._zooms.contains(zoom) && (rule._type == Rule::AnyType
|| rule._type == Rule::NodeType)) || rule._type == Rule::NodeType))
list.append(&symbol); list.append(&symbol);
@ -638,6 +759,19 @@ QList<const Style::Symbol*> Style::pointSymbols(int zoom) const
return list; return list;
} }
QList<const Style::Symbol*> Style::lineSymbols(int zoom) const
{
QList<const Symbol*> list;
for (int i = 0; i < _lineSymbols.size(); i++) {
const Symbol &symbol = _lineSymbols.at(i);
if (symbol.rule()._zooms.contains(zoom))
list.append(&symbol);
}
return list;
}
QList<const Style::Symbol*> Style::areaSymbols(int zoom) const QList<const Style::Symbol*> Style::areaSymbols(int zoom) const
{ {
QList<const Symbol*> list; QList<const Symbol*> list;
@ -656,14 +790,18 @@ QList<const Style::Symbol*> Style::areaSymbols(int zoom) const
QPen Style::PathRender::pen(int zoom) const QPen Style::PathRender::pen(int zoom) const
{ {
if (_strokeColor.isValid()) { if (_strokeColor.isValid()) {
qreal width = (zoom >= 12) qreal width = (_scale > None && zoom >= 12)
? pow(1.5, zoom - 12) * _strokeWidth : _strokeWidth; ? pow(1.5, zoom - 12) * _strokeWidth : _strokeWidth;
QPen p(QBrush(_strokeColor), width, Qt::SolidLine, _strokeCap, QPen p(QBrush(_strokeColor), width, Qt::SolidLine, _strokeCap,
_strokeJoin); _strokeJoin);
if (!_strokeDasharray.isEmpty()) { if (!_strokeDasharray.isEmpty()) {
QVector<qreal>pattern(_strokeDasharray); QVector<qreal>pattern(_strokeDasharray);
for (int i = 0; i < _strokeDasharray.size(); i++) for (int i = 0; i < _strokeDasharray.size(); i++) {
if (_scale > Stroke && zoom >= 12)
pattern[i] = (pow(1.5, zoom - 12) * pattern[i]);
// QPainter pattern is specified in units of the pens width!
pattern[i] /= width; pattern[i] /= width;
}
p.setDashPattern(pattern); p.setDashPattern(pattern);
} }
return p; return p;
@ -671,6 +809,11 @@ QPen Style::PathRender::pen(int zoom) const
return Qt::NoPen; return Qt::NoPen;
} }
qreal Style::PathRender::dy(int zoom) const
{
return (_scale && zoom >= 12) ? pow(1.5, zoom - 12) * _dy : _dy;
}
qreal Style::CircleRender::radius(int zoom) const qreal Style::CircleRender::radius(int zoom) const
{ {
return (_scale && zoom >= 12) ? pow(1.5, zoom - 12) * _radius : _radius; return (_scale && zoom >= 12) ? pow(1.5, zoom - 12) * _radius : _radius;

View File

@ -136,17 +136,21 @@ public:
public: public:
PathRender(const Rule &rule, int zOrder) : Render(rule), PathRender(const Rule &rule, int zOrder) : Render(rule),
_zOrder(zOrder), _strokeWidth(0), _strokeCap(Qt::RoundCap), _zOrder(zOrder), _strokeWidth(0), _strokeCap(Qt::RoundCap),
_strokeJoin(Qt::RoundJoin), _area(false), _curve(false) {} _strokeJoin(Qt::RoundJoin), _area(false), _curve(false),
_scale(Stroke), _dy(0) {}
int zOrder() const {return _zOrder;} int zOrder() const {return _zOrder;}
QPen pen(int zoom) const; QPen pen(int zoom) const;
const QBrush &brush() const {return _brush;} const QBrush &brush() const {return _brush;}
bool area() const {return _area;} bool area() const {return _area;}
bool curve() const {return _curve;} bool curve() const {return _curve;}
qreal dy(int zoom) const;
private: private:
friend class Style; friend class Style;
enum Scale {None, Stroke, All};
int _zOrder; int _zOrder;
QColor _strokeColor; QColor _strokeColor;
qreal _strokeWidth; qreal _strokeWidth;
@ -155,6 +159,8 @@ public:
Qt::PenJoinStyle _strokeJoin; Qt::PenJoinStyle _strokeJoin;
QBrush _brush; QBrush _brush;
bool _area, _curve; bool _area, _curve;
Scale _scale;
qreal _dy;
}; };
class CircleRender : public Render class CircleRender : public Render
@ -186,6 +192,7 @@ public:
: Render(rule), _priority(0), _fillColor(Qt::black), : Render(rule), _priority(0), _fillColor(Qt::black),
_strokeColor(Qt::black), _strokeWidth(0) {} _strokeColor(Qt::black), _strokeWidth(0) {}
const QString &symbolId() const {return _symbolId;}
const QFont &font() const {return _font;} const QFont &font() const {return _font;}
const QColor &fillColor() const {return _fillColor;} const QColor &fillColor() const {return _fillColor;}
const QColor &strokeColor() const {return _strokeColor;} const QColor &strokeColor() const {return _strokeColor;}
@ -196,6 +203,7 @@ public:
private: private:
friend class Style; friend class Style;
QString _symbolId;
int _priority; int _priority;
QColor _fillColor, _strokeColor; QColor _fillColor, _strokeColor;
qreal _strokeWidth; qreal _strokeWidth;
@ -206,15 +214,20 @@ public:
class Symbol : public Render class Symbol : public Render
{ {
public: public:
Symbol(const Rule &rule) : Render(rule), _priority(0) {} Symbol(const Rule &rule)
: Render(rule), _priority(0), _rotate(true) {}
const QString &id() const {return _id;}
const QImage &img() const {return _img;} const QImage &img() const {return _img;}
bool rotate() const {return _rotate;}
int priority() const {return _priority;} int priority() const {return _priority;}
private: private:
friend class Style; friend class Style;
QString _id;
int _priority; int _priority;
bool _rotate;
QImage _img; QImage _img;
}; };
@ -230,19 +243,60 @@ public:
QList<const TextRender*> areaLabels(int zoom) const; QList<const TextRender*> areaLabels(int zoom) const;
QList<const Symbol*> pointSymbols(int zoom) const; QList<const Symbol*> pointSymbols(int zoom) const;
QList<const Symbol*> areaSymbols(int zoom) const; QList<const Symbol*> areaSymbols(int zoom) const;
QList<const Symbol*> lineSymbols(int zoom) const;
private: private:
class Menu {
public:
class Layer {
public:
Layer() : _enabled(false) {}
Layer(const QString &id, bool enabled)
: _id(id), _enabled(enabled) {}
const QStringList &cats() const {return _cats;}
const QStringList &overlays() const {return _overlays;}
const QString &id() const {return _id;}
const QString &parent() const {return _parent;}
bool enabled() const {return _enabled;}
void setParent(const QString &parent) {_parent = parent;}
void addCat(const QString &cat) {_cats.append(cat);}
void addOverlay(const QString &overlay) {_overlays.append(overlay);}
private:
QStringList _cats;
QStringList _overlays;
QString _id;
QString _parent;
bool _enabled;
};
Menu() {}
Menu(const QString &defaultValue) : _defaultvalue(defaultValue) {}
void addLayer(const Layer &layer) {_layers.append(layer);}
QSet<QString> cats() const;
private:
const Layer *findLayer(const QString &id) const;
void addCats(const Layer *layer, QSet<QString> &cats) const;
QString _defaultvalue;
QList<Layer> _layers;
};
QList<PathRender> _paths; QList<PathRender> _paths;
QList<CircleRender> _circles; QList<CircleRender> _circles;
QList<TextRender> _pathLabels, _pointLabels, _areaLabels; QList<TextRender> _pathLabels, _pointLabels, _areaLabels;
QList<Symbol> _symbols; QList<Symbol> _symbols, _lineSymbols;
bool loadXml(const QString &path, const MapData &data, qreal ratio); bool loadXml(const QString &path, const MapData &data, qreal ratio);
void rendertheme(QXmlStreamReader &reader, const QString &dir, void rendertheme(QXmlStreamReader &reader, const QString &dir,
const MapData &data, qreal ratio); const MapData &data, qreal ratio);
void layer(QXmlStreamReader &reader, QSet<QString> &cats); Menu::Layer layer(QXmlStreamReader &reader);
void stylemenu(QXmlStreamReader &reader, QSet<QString> &cats); Menu stylemenu(QXmlStreamReader &reader);
void cat(QXmlStreamReader &reader, QSet<QString> &cats); QString cat(QXmlStreamReader &reader);
void rule(QXmlStreamReader &reader, const QString &dir, const MapData &data, void rule(QXmlStreamReader &reader, const QString &dir, const MapData &data,
qreal ratio, const QSet<QString> &cats, const Rule &parent); qreal ratio, const QSet<QString> &cats, const Rule &parent);
void area(QXmlStreamReader &reader, const QString &dir, qreal ratio, void area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
@ -252,7 +306,7 @@ private:
void text(QXmlStreamReader &reader, const MapData &data, const Rule &rule, void text(QXmlStreamReader &reader, const MapData &data, const Rule &rule,
QList<QList<TextRender> *> &lists); QList<QList<TextRender> *> &lists);
void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
const Rule &rule); const Rule &rule, QList<Symbol> &list);
}; };
} }

View File

@ -14,7 +14,6 @@ public:
: _file(file), _offset(offset), _size(size), _pos(-1), : _file(file), _offset(offset), _size(size), _pos(-1),
_blockNum(-1), _blockPos(-1) {} _blockNum(-1), _blockPos(-1) {}
quint64 offset() const {return _offset;}
quint64 pos() const {return _pos;} quint64 pos() const {return _pos;}
bool seek(quint64 pos); bool seek(quint64 pos);

View File

@ -2,29 +2,36 @@
#include <QPixmapCache> #include <QPixmapCache>
#include "common/wgs84.h" #include "common/wgs84.h"
#include "common/util.h" #include "common/util.h"
#include "pcs.h"
#include "rectd.h" #include "rectd.h"
#include "pcs.h"
#include "mapsforgemap.h" #include "mapsforgemap.h"
using namespace Mapsforge; using namespace Mapsforge;
#define TEXT_EXTENT 160
MapsforgeMap::MapsforgeMap(const QString &fileName, QObject *parent) MapsforgeMap::MapsforgeMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _data(fileName), _zoom(0), : Map(fileName, parent), _data(fileName), _zoom(0),
_projection(PCS::pcs(3857)), _tileRatio(1.0) _projection(PCS::pcs(3857)), _tileRatio(1.0)
{ {
if (_data.isValid()) { if (_data.isValid())
_zoom = _data.zooms().min(); _zoom = _data.zooms().min();
updateTransform();
}
} }
void MapsforgeMap::load() void MapsforgeMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(hidpi);
_tileRatio = deviceRatio;
_projection = out;
_data.load(); _data.load();
_style.load(_data, _tileRatio); _style.load(_data, _tileRatio);
updateTransform();
QPixmapCache::clear();
} }
void MapsforgeMap::unload() void MapsforgeMap::unload()
@ -160,18 +167,19 @@ void MapsforgeMap::cancelJobs(bool wait)
void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags) void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{ {
QPointF tl(floor(rect.left() / _data.tileSize()) * _data.tileSize(), int tileSize = (_data.tileSize() < 384)
floor(rect.top() / _data.tileSize()) * _data.tileSize()); ? _data.tileSize() << 1 : _data.tileSize();
QPointF tl(floor(rect.left() / tileSize) * tileSize,
floor(rect.top() / tileSize) * tileSize);
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y()); QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / _data.tileSize()); int width = ceil(s.width() / tileSize);
int height = ceil(s.height() / _data.tileSize()); int height = ceil(s.height() / tileSize);
QList<RasterTile> tiles; QList<RasterTile> tiles;
for (int i = 0; i < width; i++) { for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) { for (int j = 0; j < height; j++) {
QPoint ttl(tl.x() + i * _data.tileSize(), tl.y() + j QPoint ttl(tl.x() + i * tileSize, tl.y() + j * tileSize);
* _data.tileSize());
if (isRunning(_zoom, ttl)) if (isRunning(_zoom, ttl))
continue; continue;
@ -179,32 +187,8 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
if (QPixmapCache::find(key(_zoom, ttl), &pm)) if (QPixmapCache::find(key(_zoom, ttl), &pm))
painter->drawPixmap(ttl, pm); painter->drawPixmap(ttl, pm);
else { else {
QList<MapData::Path> paths; tiles.append(RasterTile(_projection, _transform, &_style, &_data,
QList<MapData::Point> points; _zoom, QRect(ttl, QSize(tileSize, tileSize)), _tileRatio));
/* Add a "sub-pixel" margin to assure the tile areas do not
overlap on the border lines. This prevents areas overlap
artifacts at least when using the EPSG:3857 projection. */
QRectF pathRect(QPointF(ttl.x() + 0.5, ttl.y() + 0.5),
QPointF(ttl.x() + _data.tileSize() - 0.5, ttl.y()
+ _data.tileSize() - 0.5));
pathRect &= _bounds;
RectD pathRectD(_transform.img2proj(pathRect.topLeft()),
_transform.img2proj(pathRect.bottomRight()));
_data.paths(pathRectD.toRectC(_projection, 20), _zoom, &paths);
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y()
- TEXT_EXTENT), QPointF(ttl.x() + _data.tileSize()
+ TEXT_EXTENT, ttl.y() + _data.tileSize() + TEXT_EXTENT));
pointRect &= _bounds;
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data.points(pointRectD.toRectC(_projection, 20), _zoom,
&points);
tiles.append(RasterTile(_projection, _transform, &_style, _zoom,
QRect(ttl, QSize(_data.tileSize(), _data.tileSize())),
_tileRatio, paths, points));
} }
} }
} }
@ -225,24 +209,7 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
} }
} }
void MapsforgeMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) Map *MapsforgeMap::create(const QString &path, bool *isMap)
{
Q_UNUSED(mapRatio);
_tileRatio = deviceRatio;
}
void MapsforgeMap::setOutputProjection(const Projection &projection)
{
if (!projection.isValid() || projection == _projection)
return;
_projection = projection;
updateTransform();
QPixmapCache::clear();
}
Map *MapsforgeMap::create(const QString &path, const Projection &, bool *isMap)
{ {
if (isMap) if (isMap)
*isMap = false; *isMap = false;

View File

@ -52,7 +52,7 @@ public:
MapsforgeMap(const QString &fileName, QObject *parent = 0); MapsforgeMap(const QString &fileName, QObject *parent = 0);
QRectF bounds() {return _bounds;} QRectF bounds() {return _bounds;}
RectC llBounds() {return _data.bounds();} RectC llBounds(const Projection &) {return _data.bounds();}
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
void setZoom(int zoom); void setZoom(int zoom);
@ -60,10 +60,9 @@ public:
int zoomIn(); int zoomIn();
int zoomOut(); int zoomOut();
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setOutputProjection(const Projection &projection);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
QPointF ll2xy(const Coordinates &c) QPointF ll2xy(const Coordinates &c)
{return _transform.proj2img(_projection.ll2xy(c));} {return _transform.proj2img(_projection.ll2xy(c));}
@ -75,7 +74,7 @@ public:
bool isValid() const {return _data.isValid();} bool isValid() const {return _data.isValid();}
QString errorString() const {return _data.errorString();} QString errorString() const {return _data.errorString();}
static Map *create(const QString &path, const Projection &, bool *isMap); static Map *create(const QString &path, bool *isMap);
private slots: private slots:
void jobFinished(MapsforgeMapJob *job); void jobFinished(MapsforgeMapJob *job);

View File

@ -189,14 +189,22 @@ void MapSource::map(QXmlStreamReader &reader, Config &config)
else else
config.dimensions.append(KV<QString, QString> config.dimensions.append(KV<QString, QString>
(attr.value("id").toString(), reader.readElementText())); (attr.value("id").toString(), reader.readElementText()));
} else if (reader.name() == QLatin1String("header")) {
QXmlStreamAttributes attr = reader.attributes();
if (!attr.hasAttribute("name"))
reader.raiseError("Missing header name");
else
config.headers.append(HTTPHeader(
attr.value("name").toString().toLatin1(),
reader.readElementText().toLatin1()));
} else if (reader.name() == QLatin1String("crs")) { } else if (reader.name() == QLatin1String("crs")) {
config.coordinateSystem = coordinateSystem(reader); config.coordinateSystem = coordinateSystem(reader);
config.crs = reader.readElementText(); config.crs = reader.readElementText();
} else if (reader.name() == QLatin1String("authorization")) { } else if (reader.name() == QLatin1String("authorization")) {
QXmlStreamAttributes attr = reader.attributes(); QXmlStreamAttributes attr = reader.attributes();
config.authorization = Authorization( Authorization auth(attr.value("username").toString(),
attr.value("username").toString(),
attr.value("password").toString()); attr.value("password").toString());
config.headers.append(auth.header());
reader.skipCurrentElement(); reader.skipCurrentElement();
} else if (reader.name() == QLatin1String("tile")) { } else if (reader.name() == QLatin1String("tile")) {
tile(reader, config); tile(reader, config);
@ -206,9 +214,8 @@ void MapSource::map(QXmlStreamReader &reader, Config &config)
} }
} }
Map *MapSource::create(const QString &path, const Projection &proj, bool *isDir) Map *MapSource::create(const QString &path, bool *isDir)
{ {
Q_UNUSED(proj);
Config config; Config config;
QFile file(path); QFile file(path);
@ -253,24 +260,24 @@ Map *MapSource::create(const QString &path, const Projection &proj, bool *isDir)
case WMTS: case WMTS:
return new WMTSMap(path, config.name, WMTS::Setup(config.url, return new WMTSMap(path, config.name, WMTS::Setup(config.url,
config.layer, config.set, config.style, config.format, config.rest, config.layer, config.set, config.style, config.format, config.rest,
config.coordinateSystem, config.dimensions, config.authorization), config.coordinateSystem, config.dimensions, config.headers),
config.tileRatio); config.tileRatio);
case WMS: case WMS:
return new WMSMap(path, config.name, WMS::Setup(config.url, return new WMSMap(path, config.name, WMS::Setup(config.url,
config.layer, config.style, config.format, config.crs, config.layer, config.style, config.format, config.crs,
config.coordinateSystem, config.dimensions, config.authorization), config.coordinateSystem, config.dimensions, config.headers),
config.tileSize); config.tileSize);
case TMS: case TMS:
return new OnlineMap(path, config.name, config.url, config.zooms, return new OnlineMap(path, config.name, config.url, config.zooms,
config.bounds, config.tileRatio, config.authorization, config.bounds, config.tileRatio, config.headers,
config.tileSize, config.scalable, true, false); config.tileSize, config.scalable, true, false);
case OSM: case OSM:
return new OnlineMap(path, config.name, config.url, config.zooms, return new OnlineMap(path, config.name, config.url, config.zooms,
config.bounds, config.tileRatio, config.authorization, config.bounds, config.tileRatio, config.headers,
config.tileSize, config.scalable, false, false); config.tileSize, config.scalable, false, false);
case QuadTiles: case QuadTiles:
return new OnlineMap(path, config.name, config.url, config.zooms, return new OnlineMap(path, config.name, config.url, config.zooms,
config.bounds, config.tileRatio, config.authorization, config.bounds, config.tileRatio, config.headers,
config.tileSize, config.scalable, false, true); config.tileSize, config.scalable, false, true);
default: default:
return new InvalidMap(path, "Invalid map type"); return new InvalidMap(path, "Invalid map type");

View File

@ -15,7 +15,7 @@ class Projection;
class MapSource class MapSource
{ {
public: public:
static Map *create(const QString &path, const Projection &proj, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
enum Type { enum Type {
@ -40,7 +40,7 @@ private:
CoordinateSystem coordinateSystem; CoordinateSystem coordinateSystem;
bool rest; bool rest;
QList<KV<QString, QString> > dimensions; QList<KV<QString, QString> > dimensions;
Authorization authorization; QList<HTTPHeader> headers;
qreal tileRatio; qreal tileRatio;
int tileSize; int tileSize;
bool scalable; bool scalable;

View File

@ -165,8 +165,19 @@ MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent)
_valid = true; _valid = true;
} }
void MBTilesMap::load() void MBTilesMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
if (_scalable) {
_scaledSize = _tileSize * deviceRatio;
_tileRatio = deviceRatio;
}
_db.open(); _db.open();
} }
@ -218,16 +229,6 @@ int MBTilesMap::zoomOut()
return _zi; return _zi;
} }
void MBTilesMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
{
_mapRatio = mapRatio;
if (_scalable) {
_scaledSize = _tileSize * deviceRatio;
_tileRatio = deviceRatio;
}
}
qreal MBTilesMap::coordinatesRatio() const qreal MBTilesMap::coordinatesRatio() const
{ {
return _mapRatio > 1.0 ? _mapRatio / _tileRatio : 1.0; return _mapRatio > 1.0 ? _mapRatio / _tileRatio : 1.0;
@ -337,7 +338,7 @@ Coordinates MBTilesMap::xy2ll(const QPointF &p)
* coordinatesRatio()); * coordinatesRatio());
} }
Map *MBTilesMap::create(const QString &path, const Projection &, bool *isDir) Map *MBTilesMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -13,7 +13,7 @@ public:
QString name() const {return _name;} QString name() const {return _name;}
QRectF bounds(); QRectF bounds();
RectC llBounds() {return _bounds;} RectC llBounds(const Projection &) {return _bounds;}
qreal resolution(const QRectF &rect); qreal resolution(const QRectF &rect);
int zoom() const {return _zi;} int zoom() const {return _zi;}
@ -27,14 +27,14 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
qreal tileSize() const; qreal tileSize() const;

View File

@ -10,17 +10,16 @@
OnlineMap::OnlineMap(const QString &fileName, const QString &name, OnlineMap::OnlineMap(const QString &fileName, const QString &name,
const QString &url, const Range &zooms, const RectC &bounds, qreal tileRatio, const QString &url, const Range &zooms, const RectC &bounds, qreal tileRatio,
const Authorization &authorization, int tileSize, bool scalable, bool invertY, const QList<HTTPHeader> &headers, int tileSize, bool scalable, bool invertY,
bool quadTiles, QObject *parent) bool quadTiles, QObject *parent)
: Map(fileName, parent), _name(name), _zooms(zooms), _bounds(bounds), : Map(fileName, parent), _name(name), _zooms(zooms), _bounds(bounds),
_zoom(_zooms.max()), _mapRatio(1.0), _tileRatio(tileRatio), _zoom(_zooms.max()), _tileSize(tileSize), _mapRatio(1.0),
_tileSize(tileSize), _scalable(scalable), _invertY(invertY) _tileRatio(tileRatio), _scalable(scalable), _invertY(invertY)
{ {
_tileLoader = new TileLoader(QDir(ProgramPaths::tilesDir()).filePath(_name), _tileLoader = new TileLoader(QDir(ProgramPaths::tilesDir()).filePath(_name),
this); this);
_tileLoader->setUrl(url); _tileLoader->setUrl(url, quadTiles ? TileLoader::QuadTiles : TileLoader::XYZ);
_tileLoader->setAuthorization(authorization); _tileLoader->setHeaders(headers);
_tileLoader->setQuadTiles(quadTiles);
connect(_tileLoader, &TileLoader::finished, this, &OnlineMap::tilesLoaded); connect(_tileLoader, &TileLoader::finished, this, &OnlineMap::tilesLoaded);
} }
@ -70,9 +69,13 @@ int OnlineMap::zoomOut()
return _zoom; return _zoom;
} }
void OnlineMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) void OnlineMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
_mapRatio = mapRatio; Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
if (_scalable) { if (_scalable) {
_tileLoader->setScaledSize(_tileSize * deviceRatio); _tileLoader->setScaledSize(_tileSize * deviceRatio);

View File

@ -13,13 +13,13 @@ class OnlineMap : public Map
public: public:
OnlineMap(const QString &fileName, const QString &name, const QString &url, OnlineMap(const QString &fileName, const QString &name, const QString &url,
const Range &zooms, const RectC &bounds, qreal tileRatio, const Range &zooms, const RectC &bounds, qreal tileRatio,
const Authorization &authorization, int tileSize, bool scalable, const QList<HTTPHeader> &headers, int tileSize, bool scalable,
bool invertY, bool quadTiles, QObject *parent = 0); bool invertY, bool quadTiles, QObject *parent = 0);
QString name() const {return _name;} QString name() const {return _name;}
QRectF bounds(); QRectF bounds();
RectC llBounds() {return _bounds;} RectC llBounds(const Projection &) {return _bounds;}
qreal resolution(const QRectF &rect); qreal resolution(const QRectF &rect);
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
@ -33,7 +33,8 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void clearCache() {_tileLoader->clearCache();} void clearCache() {_tileLoader->clearCache();}
private: private:
@ -47,8 +48,8 @@ private:
Range _zooms; Range _zooms;
RectC _bounds; RectC _bounds;
int _zoom; int _zoom;
qreal _mapRatio, _tileRatio;
int _tileSize; int _tileSize;
qreal _mapRatio, _tileRatio;
bool _scalable; bool _scalable;
bool _invertY; bool _invertY;
}; };

View File

@ -487,8 +487,14 @@ int OruxMap::zoomOut()
return _zoom; return _zoom;
} }
void OruxMap::load() void OruxMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
if (_db.isValid()) if (_db.isValid())
_db.open(); _db.open();
} }
@ -499,12 +505,6 @@ void OruxMap::unload()
_db.close(); _db.close();
} }
void OruxMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
{
Q_UNUSED(deviceRatio);
_mapRatio = mapRatio;
}
QPixmap OruxMap::tile(const Zoom &z, int x, int y) const QPixmap OruxMap::tile(const Zoom &z, int x, int y) const
{ {
if (_db.isValid()) { if (_db.isValid()) {
@ -590,7 +590,7 @@ Coordinates OruxMap::xy2ll(const QPointF &p)
return z.projection.xy2ll(z.transform.img2proj(p * _mapRatio)); return z.projection.xy2ll(z.transform.img2proj(p * _mapRatio));
} }
Map *OruxMap::create(const QString &path, const Projection &, bool *isDir) Map *OruxMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = true; *isDir = true;

View File

@ -33,14 +33,14 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void load(const Projection &in, const Projection &out, qreal deviceRatio,
void load(); bool hidpi);
void unload(); void unload();
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
struct Zoom { struct Zoom {

View File

@ -152,8 +152,13 @@ OsmdroidMap::OsmdroidMap(const QString &fileName, QObject *parent)
_valid = true; _valid = true;
} }
void OsmdroidMap::load() void OsmdroidMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
_db.open(); _db.open();
} }
@ -208,12 +213,6 @@ int OsmdroidMap::zoomOut()
return _zoom; return _zoom;
} }
void OsmdroidMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
{
Q_UNUSED(deviceRatio);
_mapRatio = mapRatio;
}
qreal OsmdroidMap::tileSize() const qreal OsmdroidMap::tileSize() const
{ {
return (_tileSize / _mapRatio); return (_tileSize / _mapRatio);
@ -310,7 +309,7 @@ Coordinates OsmdroidMap::xy2ll(const QPointF &p)
return OSM::m2ll(QPointF(p.x() * scale, -p.y() * scale) * _mapRatio); return OSM::m2ll(QPointF(p.x() * scale, -p.y() * scale) * _mapRatio);
} }
Map *OsmdroidMap::create(const QString &path, const Projection &, bool *isDir) Map *OsmdroidMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -11,7 +11,7 @@ public:
OsmdroidMap(const QString &fileName, QObject *parent = 0); OsmdroidMap(const QString &fileName, QObject *parent = 0);
QRectF bounds(); QRectF bounds();
RectC llBounds() {return _bounds;} RectC llBounds(const Projection &) {return _bounds;}
qreal resolution(const QRectF &rect); qreal resolution(const QRectF &rect);
int zoom() const {return _zoom;} int zoom() const {return _zoom;}
@ -25,14 +25,14 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
int limitZoom(int zoom) const; int limitZoom(int zoom) const;

View File

@ -182,6 +182,9 @@ bool OZF::open()
if (!_file.open(QIODevice::ReadOnly)) if (!_file.open(QIODevice::ReadOnly))
return false; return false;
if (!_zooms.isEmpty())
return true;
if (!readHeaders()) { if (!readHeaders()) {
qWarning("%s: Invalid header", qPrintable(_file.fileName())); qWarning("%s: Invalid header", qPrintable(_file.fileName()));
_file.close(); _file.close();
@ -232,7 +235,6 @@ QPixmap OZF::tile(int zoom, int x, int y)
QSize OZF::size(int zoom) const QSize OZF::size(int zoom) const
{ {
Q_ASSERT(_file.isOpen());
Q_ASSERT(0 <= zoom && zoom < _zooms.count()); Q_ASSERT(0 <= zoom && zoom < _zooms.count());
return _zooms.at(zoom).size; return _zooms.at(zoom).size;

View File

@ -16,6 +16,7 @@ public:
_file(name) {} _file(name) {}
bool open(); bool open();
void close() {_file.close();}
QString fileName() const {return _file.fileName();} QString fileName() const {return _file.fileName();}
bool isOpen() const {return _file.isOpen();} bool isOpen() const {return _file.isOpen();}

View File

@ -50,6 +50,8 @@ OziMap::OziMap(const QString &fileName, bool TAR, QObject *parent)
if (!setTileInfo(_tar->files())) if (!setTileInfo(_tar->files()))
return; return;
_tar->close();
} else { } else {
QFile file(fileName); QFile file(fileName);
MapFile mf(file); MapFile mf(file);
@ -105,6 +107,16 @@ OziMap::OziMap(const QString &fileName, Tar &tar, QObject *parent)
_transform = mf.transform(); _transform = mf.transform();
_tar = new Tar(fi.absolutePath() + "/" + fi.completeBaseName() + ".tar"); _tar = new Tar(fi.absolutePath() + "/" + fi.completeBaseName() + ".tar");
if (!_tar->open()) {
_errorString = _tar->fileName() + ": error reading tar file";
return;
}
if (!setTileInfo(_tar->files())) {
_errorString = _tar->fileName() + ": " + _errorString;
return;
}
_tar->close();
_valid = true; _valid = true;
} }
@ -144,6 +156,7 @@ bool OziMap::setImageInfo(const QString &path)
return false; return false;
} }
_scale = _ozf->scale(_zoom); _scale = _ozf->scale(_zoom);
_ozf->close();
} else { } else {
QImageReader ir(_map.path); QImageReader ir(_map.path);
if (!ir.canRead()) { if (!ir.canRead()) {
@ -192,28 +205,42 @@ bool OziMap::setTileInfo(const QStringList &tiles, const QString &path)
return false; return false;
} }
void OziMap::load() void OziMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
if (_tar && !_tar->isOpen()) { Q_UNUSED(in);
if (!_tar->open()) { Q_UNUSED(out);
qWarning("%s: error loading tar file",
qPrintable(_tar->fileName()));
return;
}
if (!setTileInfo(_tar->files()))
qWarning("%s: %s", qPrintable(_tar->fileName()),
qPrintable(_errorString));
return;
}
if (!_tile.isValid() && !_ozf && !_img) _mapRatio = hidpi ? deviceRatio : 1.0;
if (_tar) {
Q_ASSERT(!_tar->isOpen());
if (!_tar->open())
return;
}
if (_ozf) {
Q_ASSERT(!_ozf->isOpen());
if (!_ozf->open())
return;
}
if (!_tile.isValid() && !_ozf) {
Q_ASSERT(!_img);
_img = new Image(_map.path); _img = new Image(_map.path);
if (_img)
_img->setDevicePixelRatio(_mapRatio);
}
} }
void OziMap::unload() void OziMap::unload()
{ {
delete _img; delete _img;
_img = 0; _img = 0;
if (_tar && _tar->isOpen())
_tar->close();
if (_ozf && _ozf->isOpen())
_ozf->close();
} }
void OziMap::drawTiled(QPainter *painter, const QRectF &rect) const void OziMap::drawTiled(QPainter *painter, const QRectF &rect) const
@ -368,16 +395,7 @@ void OziMap::rescale(int zoom)
_scale = _ozf->scale(zoom); _scale = _ozf->scale(zoom);
} }
void OziMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) Map *OziMap::createTAR(const QString &path, bool *isDir)
{
Q_UNUSED(deviceRatio);
_mapRatio = mapRatio;
if (_img)
_img->setDevicePixelRatio(_mapRatio);
}
Map *OziMap::createTAR(const QString &path, const Projection &, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;
@ -385,7 +403,7 @@ Map *OziMap::createTAR(const QString &path, const Projection &, bool *isDir)
return new OziMap(path, true); return new OziMap(path, true);
} }
Map *OziMap::createMAP(const QString &path, const Projection &, bool *isDir) Map *OziMap::createMAP(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -33,8 +33,8 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void load(const Projection &in, const Projection &out, qreal deviceRatio,
void load(); bool hidpi);
void unload(); void unload();
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
@ -47,8 +47,8 @@ public:
QPointF pp2xy(const PointD &p) const QPointF pp2xy(const PointD &p) const
{return _transform.proj2img(p) / _mapRatio;} {return _transform.proj2img(p) / _mapRatio;}
static Map *createTAR(const QString &path, const Projection &, bool *isDir); static Map *createTAR(const QString &path, bool *isDir);
static Map *createMAP(const QString &path, const Projection &, bool *isDir); static Map *createMAP(const QString &path, bool *isDir);
private: private:
struct ImageInfo { struct ImageInfo {

View File

@ -354,8 +354,13 @@ QCTMap::QCTMap(const QString &fileName, QObject *parent)
_valid = true; _valid = true;
} }
void QCTMap::load() void QCTMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
_file.open(QIODevice::ReadOnly); _file.open(QIODevice::ReadOnly);
} }
@ -486,7 +491,7 @@ void QCTMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
} }
} }
Map *QCTMap::create(const QString &path, const Projection &, bool *isDir) Map *QCTMap::create(const QString &path, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -21,16 +21,14 @@ public:
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
void load(); void load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi);
void unload(); void unload();
void setDevicePixelRatio(qreal /*deviceRatio*/, qreal mapRatio)
{_mapRatio = mapRatio;}
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
bool readName(QDataStream &stream); bool readName(QDataStream &stream);

View File

@ -355,8 +355,13 @@ Coordinates RMap::xy2ll(const QPointF &p)
p.y() / scale.y()) * _mapRatio)); p.y() / scale.y()) * _mapRatio));
} }
void RMap::load() void RMap::load(const Projection &in, const Projection &out, qreal deviceRatio,
bool hidpi)
{ {
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
_file.open(QIODevice::ReadOnly); _file.open(QIODevice::ReadOnly);
} }
@ -455,13 +460,7 @@ void RMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
} }
} }
void RMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) Map *RMap::create(const QString &path, bool *isDir)
{
Q_UNUSED(deviceRatio);
_mapRatio = mapRatio;
}
Map *RMap::create(const QString &path, const Projection &, bool *isDir)
{ {
if (isDir) if (isDir)
*isDir = false; *isDir = false;

View File

@ -25,8 +25,8 @@ public:
QPointF ll2xy(const Coordinates &c); QPointF ll2xy(const Coordinates &c);
Coordinates xy2ll(const QPointF &p); Coordinates xy2ll(const QPointF &p);
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); void load(const Projection &in, const Projection &out, qreal deviceRatio,
void load(); bool hidpi);
void unload(); void unload();
void draw(QPainter *painter, const QRectF &rect, Flags flags); void draw(QPainter *painter, const QRectF &rect, Flags flags);
@ -34,7 +34,7 @@ public:
bool isValid() const {return _valid;} bool isValid() const {return _valid;}
QString errorString() const {return _errorString;} QString errorString() const {return _errorString;}
static Map *create(const QString &path, const Projection &, bool *isDir); static Map *create(const QString &path, bool *isDir);
private: private:
struct Header { struct Header {

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