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

Compare commits

..

33 Commits
13.3 ... 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
36 changed files with 754 additions and 353 deletions

View File

@ -1,23 +1,23 @@
version: 13.3.{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.3 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

@ -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.3" !define VERSION "13.5"
; The file to write ; The file to write
OutFile "GPXSee-${VERSION}_x64.exe" OutFile "GPXSee-${VERSION}_x64.exe"

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

@ -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)

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

@ -10,15 +10,11 @@
using namespace ENC; using namespace ENC;
#define TEXT_EXTENT 160 #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 {
@ -105,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)
@ -186,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;
@ -352,20 +294,19 @@ void RasterTile::processPoints(QList<MapData::Point*> &points,
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 QImage *img = style.img().isNull() ? rimg : &style.img();
const QFont *fnt = showLabel(img, _data->zooms(), _zoom, point->type()) const QFont *fnt = showLabel(img, _data->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()))

View File

@ -28,19 +28,6 @@ public:
void render(); void render();
private: private:
class PointItem : public TextPointItem
{
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;
};
void fetchData(QList<MapData::Poly*> &polygons, QList<MapData::Line*> &lines, void fetchData(QList<MapData::Poly*> &polygons, QList<MapData::Line*> &lines,
QList<MapData::Point*> &points); QList<MapData::Point*> &points);
QPointF ll2xy(const Coordinates &c) const QPointF ll2xy(const Coordinates &c) const

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

@ -25,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
@ -34,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;}

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

@ -25,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;
@ -327,14 +333,25 @@ void RasterTile::processStreetNames(const QList<MapData::Poly> &lines,
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;
}
}
} }
} }
@ -490,5 +507,5 @@ void RasterTile::render()
//painter.setPen(Qt::red); //painter.setPen(Qt::red);
//painter.setRenderHint(QPainter::Antialiasing, false); //painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(QRect(_xy, _pixmap.size())); //painter.drawRect(_rect);
} }

View File

@ -292,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()),

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)
@ -540,19 +544,20 @@ void MapData::points(const VectorTile *tile, const RectC &rect, int zoom,
_pointLock.unlock(); _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 (!rect.isValid()) if (!searchRect.isValid())
return; 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);
} }

View File

@ -62,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();

View File

@ -1,12 +1,16 @@
#include <cmath>
#include <QPainter> #include <QPainter>
#include <QCache> #include <QCache>
#include "common/programpaths.h"
#include "map/rectd.h" #include "map/rectd.h"
#include "rastertile.h" #include "rastertile.h"
using namespace Mapsforge; using namespace Mapsforge;
#define TEXT_EXTENT 160 #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)
{ {
@ -55,45 +59,89 @@ static const QColor *haloColor(const Style::TextRender *ti)
? &ti->strokeColor() : 0; ? &ti->strokeColor() : 0;
} }
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, void RasterTile::processPointLabels(const QList<MapData::Point> &points,
QList<TextItem*> &textItems) 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> painterPoints; 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)
painterPoints.append(PainterPoint(&point, lbl, si, ti)); items.append(PointText(&point, lbl, si, ti));
} }
std::sort(painterPoints.begin(), painterPoints.end()); std::sort(items.begin(), items.end());
for (int i = 0; i < painterPoints.size(); i++) { for (int i = 0; i < items.size(); i++) {
const PainterPoint &p = painterPoints.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;
@ -108,14 +156,15 @@ void RasterTile::processPointLabels(const QList<MapData::Point> &points,
} }
} }
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;
@ -123,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)) {
@ -134,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;
}
} }
} }
} }
@ -244,7 +345,7 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const
void RasterTile::pathInstructions(const QList<MapData::Path> &paths, void RasterTile::pathInstructions(const QList<MapData::Path> &paths,
QVector<PainterPath> &painterPaths, QVector<PainterPath> &painterPaths,
QVector<RasterTile::RenderInstruction> &instructions) 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;
@ -270,7 +371,7 @@ void RasterTile::pathInstructions(const QList<MapData::Path> &paths,
} }
void RasterTile::circleInstructions(const QList<MapData::Point> &points, 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;
@ -306,13 +407,18 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &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);
@ -325,18 +431,22 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
} }
void RasterTile::fetchData(QList<MapData::Path> &paths, void RasterTile::fetchData(QList<MapData::Path> &paths,
QList<MapData::Point> &points) QList<MapData::Point> &points) const
{ {
QPoint ttl(_rect.topLeft()); QPoint ttl(_rect.topLeft());
/* Add a "sub-pixel" margin to assure the tile areas do not QRectF pathRect(QPointF(ttl.x() - PATHS_EXTENT, ttl.y() - PATHS_EXTENT),
overlap on the border lines. This prevents areas overlap QPointF(ttl.x() + _rect.width() + PATHS_EXTENT, ttl.y() + _rect.height()
artifacts at least when using the EPSG:3857 projection. */ + PATHS_EXTENT));
QRectF pathRect(QPointF(ttl.x() + 0.5, ttl.y() + 0.5), QRectF searchRect(QPointF(ttl.x() - SEARCH_EXTENT, ttl.y() - SEARCH_EXTENT),
QPointF(ttl.x() + _rect.width() - 0.5, ttl.y() + _rect.height() - 0.5)); QPointF(ttl.x() + _rect.width() + SEARCH_EXTENT, ttl.y() + _rect.height()
+ SEARCH_EXTENT));
RectD pathRectD(_transform.img2proj(pathRect.topLeft()), RectD pathRectD(_transform.img2proj(pathRect.topLeft()),
_transform.img2proj(pathRect.bottomRight())); _transform.img2proj(pathRect.bottomRight()));
_data->paths(pathRectD.toRectC(_proj, 20), _zoom, &paths); 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), QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height() QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
@ -361,18 +471,20 @@ void RasterTile::render()
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, paths, points, renderPaths); drawPaths(&painter, paths, points, renderPaths);
processPointLabels(points, 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

@ -35,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;
@ -58,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:
@ -137,29 +157,31 @@ 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 fetchData(QList<MapData::Path> &paths, QList<MapData::Point> &points); void fetchData(QList<MapData::Path> &paths,
QList<MapData::Point> &points) const;
void pathInstructions(const QList<MapData::Path> &paths, void pathInstructions(const QList<MapData::Path> &paths,
QVector<PainterPath> &painterPaths, QVector<PainterPath> &painterPaths,
QVector<RasterTile::RenderInstruction> &instructions); QVector<RasterTile::RenderInstruction> &instructions) const;
void circleInstructions(const QList<MapData::Point> &points, void circleInstructions(const QList<MapData::Point> &points,
QVector<RasterTile::RenderInstruction> &instructions); 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(const QList<MapData::Point> &points, void processPointLabels(const QList<MapData::Point> &points,
QList<TextItem*> &textItems); QList<TextItem*> &textItems) const;
void processAreaLabels(QList<TextItem*> &textItems, void processAreaLabels(const QVector<PainterPath> &paths,
QVector<PainterPath> &paths); QList<TextItem*> &textItems) const;
void processLineLabels(QList<TextItem*> &textItems, void processLineLabels(const QVector<PainterPath> &paths,
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, const QList<MapData::Path> &paths, void drawPaths(QPainter *painter, const QList<MapData::Path> &paths,

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;
@ -177,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);
@ -195,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")) {
@ -245,6 +298,13 @@ void Style::line(QXmlStreamReader &reader, const Rule &rule)
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);
@ -361,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);
@ -376,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"))
@ -404,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) {
@ -411,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();
} }
@ -486,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,
@ -533,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();
} }
} }
@ -643,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);
@ -652,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;
@ -689,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

@ -137,13 +137,14 @@ 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) {} _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;
@ -159,6 +160,7 @@ public:
QBrush _brush; QBrush _brush;
bool _area, _curve; bool _area, _curve;
Scale _scale; Scale _scale;
qreal _dy;
}; };
class CircleRender : public Render class CircleRender : public Render
@ -190,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;}
@ -200,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;
@ -210,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;
}; };
@ -234,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,
@ -256,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

@ -167,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;
@ -187,8 +188,7 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
painter->drawPixmap(ttl, pm); painter->drawPixmap(ttl, pm);
else { else {
tiles.append(RasterTile(_projection, _transform, &_style, &_data, tiles.append(RasterTile(_projection, _transform, &_style, &_data,
_zoom, QRect(ttl, QSize(_data.tileSize(), _data.tileSize())), _zoom, QRect(ttl, QSize(tileSize, tileSize)), _tileRatio));
_tileRatio));
} }
} }
} }

View File

@ -18,9 +18,8 @@ OnlineMap::OnlineMap(const QString &fileName, const QString &name,
{ {
_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->setHeaders(headers); _tileLoader->setHeaders(headers);
_tileLoader->setQuadTiles(quadTiles);
connect(_tileLoader, &TileLoader::finished, this, &OnlineMap::tilesLoaded); connect(_tileLoader, &TileLoader::finished, this, &OnlineMap::tilesLoaded);
} }

View File

@ -2,8 +2,9 @@
#include <QPainter> #include <QPainter>
#include "textpathitem.h" #include "textpathitem.h"
#define CHAR_RATIO 0.55
#define MAX_TEXT_ANGLE 30 #define MAX_TEXT_ANGLE 30
#define PADDING 2
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
#define INTERSECTS intersect #define INTERSECTS intersect
@ -12,6 +13,22 @@
#endif // QT 5.15 #endif // QT 5.15
static void swap(const QLineF &line, QPointF *p1, QPointF *p2)
{
QPointF lp1(line.p1());
QPointF lp2(line.p2());
if ((lp1.rx() < lp2.rx() && p1->rx() > p2->rx())
|| (lp1.ry() < lp2.ry() && p1->ry() > p2->ry())
|| (lp1.rx() > lp2.rx() && p1->rx() < p2->rx())
|| (lp1.ry() > lp2.ry() && p1->ry() < p2->ry())) {
QPointF tmp(*p2);
*p2 = *p1;
*p1 = tmp;
}
}
static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p) static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p)
{ {
if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p) if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p)
@ -40,20 +57,26 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1,
p = p2; p = p2;
if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p)
== QLineF::BoundedIntersection) { == QLineF::BoundedIntersection) {
if (p == p2) if (p == p2) {
swap(line, p1, p2);
return true; return true;
}
p = p2; p = p2;
} }
if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p)
== QLineF::BoundedIntersection) { == QLineF::BoundedIntersection) {
if (p == p2) if (p == p2) {
swap(line, p1, p2);
return true; return true;
}
p = p2; p = p2;
} }
if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p)
== QLineF::BoundedIntersection) { == QLineF::BoundedIntersection) {
if (p == p2) if (p == p2) {
swap(line, p1, p2);
return true; return true;
}
} }
Q_ASSERT(p != p2); Q_ASSERT(p != p2);
@ -197,6 +220,13 @@ static QList<QLineF> lineString(const QPainterPath &path,
return lines; return lines;
} }
static bool reverse(const QPainterPath &path)
{
QLineF l(path.elementAt(0), path.elementAt(1));
qreal angle = l.angle();
return (angle > 90 && angle < 270) ? true : false;
}
template<class T> template<class T>
static QPainterPath textPath(const T &path, qreal textWidth, static QPainterPath textPath(const T &path, qreal textWidth,
qreal charWidth, const QRectF &tileRect) qreal charWidth, const QRectF &tileRect)
@ -229,109 +259,129 @@ static QPainterPath textPath(const T &path, qreal textWidth,
: QPainterPath(); : QPainterPath();
} }
static bool reverse(const QPainterPath &path) template<class T>
void TextPathItem::init(const T &line, const QRect &tileRect)
{ {
QLineF l(path.elementAt(0), path.elementAt(1)); qreal cw, mw, textWidth;
qreal angle = l.angle();
return (angle > 90 && angle < 270) ? true : false; if (_text && _img) {
cw = _font->pixelSize() * CHAR_RATIO;
mw = _font->pixelSize() / 2.0;
textWidth = _text->size() * cw + _img->width() + PADDING;
} else if (_text) {
cw = _font->pixelSize() * CHAR_RATIO;
mw = _font->pixelSize() / 2.0;
textWidth = _text->size() * cw;
} else {
cw = _img->width();
mw = _img->height() / 2.0;
textWidth = _img->width();
}
_path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw));
if (_path.isEmpty())
return;
if (reverse(_path)) {
_path = _path.toReversed();
_reverse = true;
}
QPainterPathStroker s;
s.setWidth(mw * 2);
s.setCapStyle(Qt::FlatCap);
_shape = s.createStroke(_path).simplified();
_rect = _shape.boundingRect();
} }
TextPathItem::TextPathItem(const QPolygonF &line, const QString *label, TextPathItem::TextPathItem(const QPolygonF &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color, const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor) : TextItem(label), _font(font), _color(color), const QColor *haloColor, const QImage *img, bool rotate)
_haloColor(haloColor) : TextItem(label), _font(font), _color(color), _haloColor(haloColor),
_img(img), _rotate(rotate), _reverse(false)
{ {
qreal cw = font->pixelSize() * 0.6; init(line, tileRect);
qreal textWidth = _text->size() * cw;
qreal mw = font->pixelSize() / 2;
_path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw));
if (_path.isEmpty())
return;
if (reverse(_path))
_path = _path.toReversed();
QPainterPathStroker s;
s.setWidth(font->pixelSize());
s.setCapStyle(Qt::FlatCap);
_shape = s.createStroke(_path).simplified();
_rect = _shape.boundingRect();
} }
TextPathItem::TextPathItem(const QPainterPath &line, const QString *label, TextPathItem::TextPathItem(const QPainterPath &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color, const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor) : TextItem(label), _font(font), _color(color), const QColor *haloColor, const QImage *img, bool rotate)
_haloColor(haloColor) : TextItem(label), _font(font), _color(color), _haloColor(haloColor),
_img(img), _rotate(rotate), _reverse(false)
{ {
qreal cw = font->pixelSize() * 0.6; init(line, tileRect);
qreal textWidth = _text->size() * cw;
qreal mw = font->pixelSize() / 2;
_path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw));
if (_path.isEmpty())
return;
if (reverse(_path))
_path = _path.toReversed();
QPainterPathStroker s;
s.setWidth(font->pixelSize());
s.setCapStyle(Qt::FlatCap);
_shape = s.createStroke(_path).simplified();
_rect = _shape.boundingRect();
} }
void TextPathItem::paint(QPainter *painter) const void TextPathItem::paint(QPainter *painter) const
{ {
QFontMetrics fm(*_font); if (_img) {
int textWidth = fm.boundingRect(*_text).width(); QSizeF s(_img->size() / _img->devicePixelRatioF());
qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth); painter->save();
qreal percent = (1.0 - factor) / 2.0; painter->translate(QPointF(_path.elementAt(0).x, _path.elementAt(0).y));
painter->rotate(360 - _path.angleAtPercent(0));
if (_reverse && _rotate) {
painter->rotate(180);
painter->translate(-s.width(), 0);
}
painter->drawImage(QPointF(0, -s.height()/2), *_img);
painter->restore();
}
QTransform t = painter->transform(); if (_text) {
QFontMetrics fm(*_font);
int textWidth = fm.boundingRect(*_text).width();
int imgWidth = _img ? _img->width() + PADDING : 0;
qreal imgPercent = imgWidth / _path.length();
qreal factor = textWidth / qMax(_path.length(), (qreal)(textWidth));
qreal percent = ((1.0 - factor) + imgPercent) / 2.0;
QTransform t = painter->transform();
painter->setFont(*_font); painter->setFont(*_font);
if (_haloColor) { if (_haloColor) {
painter->setPen(*_haloColor); painter->setPen(*_haloColor);
for (int i = 0; i < _text->size(); i++) {
QPointF point = _path.pointAtPercent(percent);
qreal angle = _path.angleAtPercent(percent);
QChar c = _text->at(i);
painter->translate(point);
painter->rotate(-angle);
painter->drawText(QPoint(-1, fm.descent() - 1), c);
painter->drawText(QPoint(1, fm.descent() + 1), c);
painter->drawText(QPoint(-1, fm.descent() + 1), c);
painter->drawText(QPoint(1, fm.descent() -1), c);
painter->drawText(QPoint(0, fm.descent() - 1), c);
painter->drawText(QPoint(0, fm.descent() + 1), c);
painter->drawText(QPoint(-1, fm.descent()), c);
painter->drawText(QPoint(1, fm.descent()), c);
painter->setTransform(t);
int width = fm.horizontalAdvance(_text->at(i));
percent += ((qreal)width / (qreal)textWidth) * factor;
}
percent = ((1.0 - factor) + imgPercent) / 2.0;
}
painter->setPen(_color ? *_color : Qt::black);
for (int i = 0; i < _text->size(); i++) { for (int i = 0; i < _text->size(); i++) {
QPointF point = _path.pointAtPercent(percent); QPointF point = _path.pointAtPercent(percent);
qreal angle = _path.angleAtPercent(percent); qreal angle = _path.angleAtPercent(percent);
QChar c = _text->at(i);
painter->translate(point); painter->translate(point);
painter->rotate(-angle); painter->rotate(-angle);
painter->drawText(QPoint(-1, fm.descent() - 1), c); painter->drawText(QPoint(0, fm.descent()), _text->at(i));
painter->drawText(QPoint(1, fm.descent() + 1), c);
painter->drawText(QPoint(-1, fm.descent() + 1), c);
painter->drawText(QPoint(1, fm.descent() -1), c);
painter->drawText(QPoint(0, fm.descent() - 1), c);
painter->drawText(QPoint(0, fm.descent() + 1), c);
painter->drawText(QPoint(-1, fm.descent()), c);
painter->drawText(QPoint(1, fm.descent()), c);
painter->setTransform(t); painter->setTransform(t);
int width = fm.horizontalAdvance(_text->at(i)); int width = fm.horizontalAdvance(_text->at(i));
percent += ((qreal)width / (qreal)textWidth) * factor; percent += ((qreal)width / (qreal)textWidth) * factor;
} }
percent = (1.0 - factor) / 2.0;
}
painter->setPen(_color ? *_color : Qt::black);
for (int i = 0; i < _text->size(); i++) {
QPointF point = _path.pointAtPercent(percent);
qreal angle = _path.angleAtPercent(percent);
painter->translate(point);
painter->rotate(-angle);
painter->drawText(QPoint(0, fm.descent()), _text->at(i));
painter->setTransform(t);
int width = fm.horizontalAdvance(_text->at(i));
percent += ((qreal)width / (qreal)textWidth) * factor;
} }
//painter->setBrush(Qt::NoBrush);
//painter->setPen(Qt::red); //painter->setPen(Qt::red);
//painter->setRenderHint(QPainter::Antialiasing, false);
//painter->drawPath(_shape); //painter->drawPath(_shape);
} }

View File

@ -1,20 +1,21 @@
#ifndef TEXTPATHITEM_H #ifndef TEXTPATHITEM_H
#define TEXTPATHITEM_H #define TEXTPATHITEM_H
#include <QVector>
#include <QPainterPath>
#include "textitem.h" #include "textitem.h"
class QFont;
class QImage;
class QColor;
class TextPathItem : public TextItem class TextPathItem : public TextItem
{ {
public: public:
TextPathItem() : TextItem(0), _font(0), _color(0) {}
TextPathItem(const QPolygonF &line, const QString *label, TextPathItem(const QPolygonF &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color, const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor); const QColor *haloColor, const QImage *img = 0, bool rotate = true);
TextPathItem(const QPainterPath &line, const QString *label, TextPathItem(const QPainterPath &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color, const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor); const QColor *haloColor, const QImage *img = 0, bool rotate = true);
bool isValid() const {return !_path.isEmpty();} bool isValid() const {return !_path.isEmpty();}
@ -23,12 +24,14 @@ public:
void paint(QPainter *painter) const; void paint(QPainter *painter) const;
private: private:
template<class T> void init(const T &line, const QRect &tileRect);
const QFont *_font; const QFont *_font;
const QColor *_color; const QColor *_color, *_haloColor;
const QColor *_haloColor; const QImage *_img;
QPainterPath _path;
QRectF _rect; QRectF _rect;
QPainterPath _shape; QPainterPath _path, _shape;
bool _rotate, _reverse;
}; };
#endif // TEXTPATHITEM_H #endif // TEXTPATHITEM_H

View File

@ -1,3 +1,4 @@
#include <cmath>
#include <QFont> #include <QFont>
#include <QFontMetrics> #include <QFontMetrics>
#include <QImage> #include <QImage>
@ -18,9 +19,9 @@ static void expand(QRectF &rect, int width)
TextPointItem::TextPointItem(const QPoint &point, const QString *text, TextPointItem::TextPointItem(const QPoint &point, const QString *text,
const QFont *font, const QImage *img, const QColor *color, const QFont *font, const QImage *img, const QColor *color,
const QColor *haloColor, const QColor *bgColor, int padding) const QColor *haloColor, const QColor *bgColor, int padding, double rotate)
: TextItem(font ? text : 0), _font(font), _img(img), _color(color), : TextItem(font ? text : 0), _font(font), _img(img), _color(color),
_haloColor(haloColor), _bgColor(bgColor) _haloColor(haloColor), _bgColor(bgColor), _rotate(rotate)
{ {
if (_text) { if (_text) {
QFontMetrics fm(*_font); QFontMetrics fm(*_font);
@ -58,8 +59,17 @@ void TextPointItem::paint(QPainter *painter) const
{ {
if (_img && !_img->isNull()) { if (_img && !_img->isNull()) {
QSizeF s(_img->size() / _img->devicePixelRatioF()); QSizeF s(_img->size() / _img->devicePixelRatioF());
painter->drawImage(QPointF(_rect.left(), _rect.center().y() if (std::isnan(_rotate))
- s.height()/2), *_img); painter->drawImage(QPointF(_rect.left(), _rect.center().y()
- s.height()/2), *_img);
else {
painter->save();
painter->translate(QPointF(_rect.left() + s.width()/2,
_rect.center().y()));
painter->rotate(_rotate);
painter->drawImage(QPointF(-s.width()/2, -s.height()/2), *_img);
painter->restore();
}
} }
if (_text) { if (_text) {
@ -99,6 +109,7 @@ void TextPointItem::paint(QPainter *painter) const
} }
//painter->setPen(Qt::red); //painter->setPen(Qt::red);
//painter.setBrush(Qt::NoBrush);
//painter->setRenderHint(QPainter::Antialiasing, false); //painter->setRenderHint(QPainter::Antialiasing, false);
//painter->drawRect(_rect); //painter->drawRect(_rect);
} }

View File

@ -1,12 +1,8 @@
#ifndef TEXTPOINTITEM_H #ifndef TEXTPOINTITEM_H
#define TEXTPOINTITEM_H #define TEXTPOINTITEM_H
#include <QRect>
#include <QString>
#include <QVector>
#include "textitem.h" #include "textitem.h"
class QPainter;
class QFont; class QFont;
class QImage; class QImage;
class QColor; class QColor;
@ -14,10 +10,9 @@ class QColor;
class TextPointItem : public TextItem class TextPointItem : public TextItem
{ {
public: public:
TextPointItem() : TextItem(0), _font(0), _img(0) {}
TextPointItem(const QPoint &point, const QString *text, const QFont *font, TextPointItem(const QPoint &point, const QString *text, const QFont *font,
const QImage *img, const QColor *color, const QColor *haloColor, const QImage *img, const QColor *color, const QColor *haloColor,
const QColor *bgColor = 0, int padding = 0); const QColor *bgColor = 0, int padding = 0, double rotate = NAN);
bool isValid() const {return !_rect.isEmpty();} bool isValid() const {return !_rect.isEmpty();}
@ -31,6 +26,7 @@ private:
const QFont *_font; const QFont *_font;
const QImage *_img; const QImage *_img;
const QColor *_color, *_haloColor, *_bgColor; const QColor *_color, *_haloColor, *_bgColor;
double _rotate;
QRectF _rect, _textRect; QRectF _rect, _textRect;
QPainterPath _shape; QPainterPath _shape;
}; };

View File

@ -52,7 +52,7 @@ static QString quadKey(const QPoint &xy, int zoom)
} }
TileLoader::TileLoader(const QString &dir, QObject *parent) TileLoader::TileLoader(const QString &dir, QObject *parent)
: QObject(parent), _dir(dir), _scaledSize(0), _quadTiles(false) : QObject(parent), _urlType(XYZ), _dir(dir), _scaledSize(0)
{ {
if (!QDir().mkpath(_dir)) if (!QDir().mkpath(_dir))
qWarning("%s: %s", qPrintable(_dir), "Error creating tiles directory"); qWarning("%s: %s", qPrintable(_dir), "Error creating tiles directory");
@ -176,19 +176,21 @@ QUrl TileLoader::tileUrl(const FetchTile &tile) const
{ {
QString url(_url); QString url(_url);
if (!tile.bbox().isNull()) { switch (_urlType) {
QString bbox = QString("%1,%2,%3,%4").arg( case BoundingBox:
QString::number(tile.bbox().left(), 'f', 6), url.replace("$bbox", QString("%1,%2,%3,%4").arg(
QString::number(tile.bbox().bottom(), 'f', 6), QString::number(tile.bbox().left(), 'f', 6),
QString::number(tile.bbox().right(), 'f', 6), QString::number(tile.bbox().bottom(), 'f', 6),
QString::number(tile.bbox().top(), 'f', 6)); QString::number(tile.bbox().right(), 'f', 6),
url.replace("$bbox", bbox); QString::number(tile.bbox().top(), 'f', 6)));
} else if (_quadTiles) { break;
url.replace("$quadkey", quadKey(tile.xy(), tile.zoom().toInt())); case QuadTiles:
} else { url.replace("$quadkey", quadKey(tile.xy(), tile.zoom().toInt()));
url.replace("$z", tile.zoom().toString()); break;
url.replace("$x", QString::number(tile.xy().x())); default:
url.replace("$y", QString::number(tile.xy().y())); url.replace("$z", tile.zoom().toString());
url.replace("$x", QString::number(tile.xy().x()));
url.replace("$y", QString::number(tile.xy().y()));
} }
return QUrl(url); return QUrl(url);

View File

@ -11,12 +11,17 @@ class TileLoader : public QObject
Q_OBJECT Q_OBJECT
public: public:
enum UrlType {
XYZ,
QuadTiles,
BoundingBox
};
TileLoader(const QString &dir, QObject *parent = 0); TileLoader(const QString &dir, QObject *parent = 0);
void setUrl(const QString &url) {_url = url;} void setUrl(const QString &url, UrlType type) {_url = url; _urlType = type;}
void setHeaders(const QList<HTTPHeader> &headers) {_headers = headers;} void setHeaders(const QList<HTTPHeader> &headers) {_headers = headers;}
void setScaledSize(int size); void setScaledSize(int size);
void setQuadTiles(bool quadTiles) {_quadTiles = quadTiles;}
void loadTilesAsync(QVector<FetchTile> &list); void loadTilesAsync(QVector<FetchTile> &list);
void loadTilesSync(QVector<FetchTile> &list); void loadTilesSync(QVector<FetchTile> &list);
@ -31,10 +36,10 @@ private:
Downloader *_downloader; Downloader *_downloader;
QString _url; QString _url;
UrlType _urlType;
QString _dir; QString _dir;
QList<HTTPHeader> _headers; QList<HTTPHeader> _headers;
int _scaledSize; int _scaledSize;
bool _quadTiles;
}; };
#endif // TILELOADER_H #endif // TILELOADER_H

View File

@ -85,7 +85,7 @@ WMSMap::WMSMap(const QString &fileName, const QString &name,
void WMSMap::init() void WMSMap::init()
{ {
_tileLoader->setUrl(tileUrl()); _tileLoader->setUrl(tileUrl(), TileLoader::BoundingBox);
_bounds = RectD(_wms->bbox(), _wms->projection()); _bounds = RectD(_wms->bbox(), _wms->projection());
computeZooms(); computeZooms();
updateTransform(); updateTransform();

View File

@ -31,7 +31,7 @@ WMTSMap::WMTSMap(const QString &fileName, const QString &name,
void WMTSMap::init() void WMTSMap::init()
{ {
_tileLoader->setUrl(_wmts->tileUrl()); _tileLoader->setUrl(_wmts->tileUrl(), TileLoader::XYZ);
_bounds = RectD(_wmts->bbox(), _wmts->projection()); _bounds = RectD(_wmts->bbox(), _wmts->projection());
updateTransform(); updateTransform();
} }