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

Compare commits

..

24 Commits

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
28 changed files with 643 additions and 260 deletions

View File

@ -1,23 +1,23 @@
version: 13.4.{build}
version: 13.5.{build}
configuration:
- Release
image:
- Visual Studio 2019
- Visual Studio 2022
environment:
NSISDIR: C:\Program Files (x86)\NSIS
OPENSSLDIR: C:\OpenSSL-v111-Win64\bin
matrix:
- QTDIR: C:\Qt\5.15\msvc2019_64
- QTDIR: C:\Qt\6.4\msvc2019_64
- QTDIR: C:\Qt\6.5\msvc2019_64
NSISDEF: /DQT6
install:
- cmd: |-
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:
- cmd: |-

View File

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

View File

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

View File

@ -55,6 +55,11 @@
<file alias="transform-move_32@2x.png">icons/GUI/transform-move_32@2x.png</file>
</qresource>
<!-- Common map stuff -->
<qresource prefix="/map">
<file alias="arrow.png">icons/map/arrow.png</file>
</qresource>
<!-- POIs (IMG & ENC style) -->
<qresource prefix="/POI">
<file alias="airfield-11.png">icons/map/POI/airfield-11.png</file>

BIN
icons/map/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

View File

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

View File

@ -2,6 +2,7 @@
#include <QGraphicsScene>
#include <QEvent>
#include <QMouseEvent>
#include <QGestureEvent>
#include <QScrollBar>
#include <QGraphicsSimpleTextItem>
#include <QPalette>
@ -37,6 +38,8 @@ GraphView::GraphView(QWidget *parent)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setBackgroundBrush(QBrush(palette().brush(QPalette::Base)));
viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
grabGesture(Qt::PinchGesture);
_xAxis = new AxisItem(AxisItem::X);
_xAxis->setZValue(1.0);
@ -381,6 +384,27 @@ void GraphView::wheelEvent(QWheelEvent *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)
{
QRectF viewRect(mapToScene(rect()).boundingRect());
@ -577,3 +601,19 @@ void GraphView::changeEvent(QEvent *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 QGraphicsSimpleTextItem;
class GraphicsScene;
class QGestureEvent;
class QPinchGesture;
class GraphView : public QGraphicsView
{
@ -59,6 +61,7 @@ protected:
void wheelEvent(QWheelEvent *e);
void changeEvent(QEvent *e);
void paintEvent(QPaintEvent *e);
bool event(QEvent *event);
const QString &yLabel() const {return _yLabel;}
const QString &yUnits() const {return _yUnits;}
@ -94,6 +97,8 @@ private:
void removeItem(QGraphicsItem *item);
void addItem(QGraphicsItem *item);
bool singleGraph() const;
bool gestureEvent(QGestureEvent *event);
void pinchGesture(QPinchGesture *gesture);
GraphicsScene *_scene;

View File

@ -25,6 +25,8 @@ class MapData
{
public:
struct Poly {
Poly() : oneway(false) {}
/* QPointF insted of Coordinates for performance reasons (no need 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
@ -34,6 +36,7 @@ public:
Raster raster;
quint32 type;
RectC boundingRect;
bool oneway;
bool operator<(const Poly &other) const
{return type > other.type;}

View File

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

View File

@ -25,6 +25,12 @@ static const QColor shieldBgColor1("#dd3e3e");
static const QColor shieldBgColor2("#379947");
static const QColor shieldBgColor3("#4a7fc1");
static const QImage *arrow()
{
static QImage img(":/map/arrow.png");
return &img;
}
static QFont pixelSizeFont(int pixelSize)
{
QFont f;
@ -327,14 +333,25 @@ void RasterTile::processStreetNames(const QList<MapData::Poly> &lines,
const QFont *fnt = font(style.textFontSize(), Style::Small);
const QColor *color = style.textColor().isValid()
? &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,
&poly.label.text(), _rect, fnt, color, Style::isContourLine(poly.type)
? 0 : &haloColor);
&poly.label.text(), _rect, fnt, color, hColor, img);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
else {
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.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)
? ((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()),

View File

@ -23,17 +23,21 @@ using namespace Mapsforge;
static void copyPaths(const RectC &rect, const QList<MapData::Path> *src,
QList<MapData::Path> *dst)
{
for (int i = 0; i < src->size(); i++)
if (rect.intersects(src->at(i).poly.boundingRect()))
dst->append(src->at(i));
for (int i = 0; i < src->size(); i++) {
const MapData::Path &path = src->at(i);
if (rect.intersects(path.poly.boundingRect()))
dst->append(path);
}
}
static void copyPoints(const RectC &rect, const QList<MapData::Point> *src,
QList<MapData::Point> *dst)
{
for (int i = 0; i < src->size(); i++)
if (rect.contains(src->at(i).coordinates))
dst->append(src->at(i));
for (int i = 0; i < src->size(); i++) {
const MapData::Point &point = src->at(i);
if (rect.contains(point.coordinates))
dst->append(point);
}
}
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();
}
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;
int l(level(zoom));
PathCTX ctx(this, rect, zoom, list);
PathCTX ctx(this, boundsRect, zoom, list);
double min[2], max[2];
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
min[0] = searchRect.left();
min[1] = searchRect.bottom();
max[0] = searchRect.right();
max[1] = searchRect.top();
_tiles.at(l)->Search(min, max, pathCb, &ctx);
}

View File

@ -62,7 +62,8 @@ public:
int tileSize() const {return _tileSize;}
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);}
void load();

View File

@ -1,13 +1,14 @@
#include <cmath>
#include <QPainter>
#include <QCache>
#include "common/programpaths.h"
#include "map/rectd.h"
#include "rastertile.h"
using namespace Mapsforge;
#define TEXT_EXTENT 160
#define PATHS_EXTENT 20
#define SEARCH_EXTENT -0.5
static double limit = cos(deg2rad(170));
@ -100,44 +101,47 @@ static QPainterPath parallelPath(const QPainterPath &p, double dy)
}
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::Symbol*> symbols(_style->pointSymbols(_zoom));
QList<PainterPoint> painterPoints;
QList<PointText> items;
for (int i = 0; i < points.size(); i++) {
const MapData::Point &point = points.at(i);
const QByteArray *lbl = 0;
const Style::TextRender *ti = 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++) {
const Style::TextRender *ri = labels.at(j);
if (ri->rule().match(point.tags)) {
if ((lbl = label(ri->key(), point.tags))) {
if (si && si->id() != ri->symbolId())
continue;
ti = ri;
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)
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++) {
const PainterPoint &p = painterPoints.at(i);
for (int i = 0; i < items.size(); i++) {
const PointText &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;
@ -152,14 +156,15 @@ void RasterTile::processPointLabels(const QList<MapData::Point> &points,
}
}
void RasterTile::processAreaLabels(QList<TextItem*> &textItems,
QVector<PainterPath> &paths)
void RasterTile::processAreaLabels(const QVector<PainterPath> &paths,
QList<TextItem*> &textItems) const
{
QList<const Style::TextRender*> labels(_style->areaLabels(_zoom));
QList<const Style::Symbol*> symbols(_style->areaSymbols(_zoom));
QList<PathText> items;
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::Symbol *si = 0;
const QByteArray *lbl = 0;
@ -167,6 +172,69 @@ void RasterTile::processAreaLabels(QList<TextItem*> &textItems,
if (!path.path->closed)
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++) {
const Style::TextRender *ri = labels.at(j);
if (ri->rule().match(path.path->closed, path.path->tags)) {
@ -178,61 +246,50 @@ void RasterTile::processAreaLabels(QList<TextItem*> &textItems,
for (int j = 0; j < symbols.size(); 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;
break;
}
}
if (!ti && !si)
continue;
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;
if (ti || si)
items.append(PathText(&path, lbl, si, ti));
}
}
void RasterTile::processLineLabels(QList<TextItem*> &textItems,
QVector<PainterPath> &paths)
{
QList<const Style::TextRender*> instructions(_style->pathLabels(_zoom));
QSet<QByteArray> set;
std::sort(items.begin(), items.end());
for (int i = 0; i < instructions.size(); i++) {
const Style::TextRender *ri = instructions.at(i);
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;
bool rotate = p.si ? p.si->rotate() : false;
bool limit = false;
for (int i = 0; i < paths.size(); i++) {
PainterPath &path = paths[i];
const QByteArray *lbl = label(ri->key(), path.path->tags);
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))
if (p.ti) {
limit = (p.ti->key() == ID_ELE || p.ti->key() == ID_REF);
if (limit && set.contains(*p.lbl))
continue;
}
PathItem *item = new PathItem(path.pp, lbl, _rect, &ri->font(),
&ri->fillColor(), haloColor(ri));
if (item->isValid() && !item->collides(textItems)) {
textItems.append(item);
if (limit)
set.insert(*lbl);
} else
delete item;
PathItem *item = new PathItem(p.p->pp, p.lbl, img, _rect, font, color,
hColor, rotate);
if (item->isValid() && !item->collides(textItems)) {
textItems.append(item);
if (limit)
set.insert(*p.lbl);
} else {
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;
}
}
}
}
@ -288,7 +345,7 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const
void RasterTile::pathInstructions(const QList<MapData::Path> &paths,
QVector<PainterPath> &painterPaths,
QVector<RasterTile::RenderInstruction> &instructions)
QVector<RasterTile::RenderInstruction> &instructions) const
{
QCache<PathKey, QList<const Style::PathRender *> > cache(8192);
QList<const Style::PathRender*> *ri;
@ -314,7 +371,7 @@ void RasterTile::pathInstructions(const QList<MapData::Path> &paths,
}
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);
QList<const Style::CircleRender*> *ri;
@ -357,6 +414,7 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
painter->setPen(ri->pen(_zoom));
painter->setBrush(ri->brush());
if (dy != 0)
painter->drawPath(parallelPath(path->pp, dy));
else
@ -373,18 +431,22 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
}
void RasterTile::fetchData(QList<MapData::Path> &paths,
QList<MapData::Point> &points)
QList<MapData::Point> &points) const
{
QPoint ttl(_rect.topLeft());
/* 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() + _rect.width() - 0.5, ttl.y() + _rect.height() - 0.5));
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()));
_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),
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
@ -409,18 +471,20 @@ void RasterTile::render()
QPainter painter(&_pixmap);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.translate(-_rect.x(), -_rect.y());
drawPaths(&painter, paths, points, renderPaths);
processPointLabels(points, textItems);
processAreaLabels(textItems, renderPaths);
processLineLabels(textItems, renderPaths);
processAreaLabels(renderPaths, textItems);
processLineLabels(renderPaths, textItems);
drawTextItems(&painter, textItems);
//painter.setPen(Qt::red);
//painter.setBrush(Qt::NoBrush);
//painter.drawRect(QRect(_rect.topLeft(), _pixmap.size()));
//painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(_rect);
qDeleteAll(textItems);

View File

@ -35,15 +35,15 @@ private:
const MapData::Path *path;
};
struct PainterPoint {
PainterPoint(const MapData::Point *p, const QByteArray *lbl,
struct PointText {
PointText(const MapData::Point *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 PainterPoint &other) const
bool operator<(const PointText &other) const
{
if (priority() == other.priority())
return p->id < other.p->id;
@ -58,6 +58,26 @@ private:
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
{
public:
@ -137,29 +157,31 @@ private:
{
public:
PathItem(const QPainterPath &line, const QByteArray *label,
const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor) : TextPathItem(line,
label ? new QString(*label) : 0, tileRect, font, color, haloColor) {}
const QImage *img, const QRect &tileRect, const QFont *font,
const QColor *color, const QColor *haloColor, bool rotate)
: TextPathItem(line, label ? new QString(*label) : 0, tileRect, font,
color, haloColor, img, rotate) {}
~PathItem() {delete _text;}
};
friend HASH_T qHash(const RasterTile::PathKey &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,
QVector<PainterPath> &painterPaths,
QVector<RasterTile::RenderInstruction> &instructions);
QVector<RasterTile::RenderInstruction> &instructions) const;
void circleInstructions(const QList<MapData::Point> &points,
QVector<RasterTile::RenderInstruction> &instructions);
QVector<RasterTile::RenderInstruction> &instructions) const;
QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));}
void processPointLabels(const QList<MapData::Point> &points,
QList<TextItem*> &textItems);
void processAreaLabels(QList<TextItem*> &textItems,
QVector<PainterPath> &paths);
void processLineLabels(QList<TextItem*> &textItems,
QVector<PainterPath> &paths);
QList<TextItem*> &textItems) const;
void processAreaLabels(const QVector<PainterPath> &paths,
QList<TextItem*> &textItems) const;
void processLineLabels(const QVector<PainterPath> &paths,
QList<TextItem*> &textItems) const;
QPainterPath painterPath(const Polygon &polygon, bool curve) const;
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
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();
}
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");
if (ir.canRead()) {
QSize s(ir.size());
if (!height && !width) {
height = 20;
width = 20;
} else if (!width) {
width = ir.size().height() / (ir.size().height() / (double)height);
width = s.height() / (s.height() / (double)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));
QImage img(ir.read());
@ -69,6 +77,42 @@ static QList<QByteArray> valList(const QList<QByteArray> &in)
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,
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();
QString file;
QColor fillColor;
int height = 0, width = 0;
int height = 0, width = 0, percent = 100;
bool ok;
ri._area = true;
@ -177,9 +221,16 @@ void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
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())
ri._brush = QBrush(image(file, width, height, ratio));
ri._brush = QBrush(image(file, width, height, percent, ratio));
else if (fillColor.isValid())
ri._brush = QBrush(fillColor);
@ -195,6 +246,8 @@ void Style::line(QXmlStreamReader &reader, const Rule &rule)
const QXmlStreamAttributes &attr = reader.attributes();
bool ok;
ri._brush = Qt::NoBrush;
if (attr.hasAttribute("stroke"))
ri._strokeColor = QColor(attr.value("stroke").toString());
if (attr.hasAttribute("stroke-width")) {
@ -368,6 +421,8 @@ void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule
return;
}
}
if (attr.hasAttribute("symbol-id"))
ri._symbolId = attr.value("symbol-id").toString();
ri._font.setFamily(fontFamily);
ri._font.setPixelSize(fontSize);
@ -383,12 +438,12 @@ void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule
}
void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
const Rule &rule)
const Rule &rule, QList<Symbol> &list)
{
Symbol ri(rule);
const QXmlStreamAttributes &attr = reader.attributes();
QString file;
int height = 0, width = 0;
int height = 0, width = 0, percent = 100;
bool ok;
if (attr.hasAttribute("src"))
@ -411,6 +466,13 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
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")) {
ri._priority = attr.value("priority").toInt(&ok);
if (!ok) {
@ -418,10 +480,16 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
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();
}
@ -493,42 +561,73 @@ void Style::rule(QXmlStreamReader &reader, const QString &dir,
text(reader, data, r, list);
}
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
reader.skipCurrentElement();
}
}
void Style::cat(QXmlStreamReader &reader, QSet<QString> &cats)
QString Style::cat(QXmlStreamReader &reader)
{
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();
return id;
}
void Style::layer(QXmlStreamReader &reader, QSet<QString> &cats)
Style::Menu::Layer Style::layer(QXmlStreamReader &reader)
{
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()) {
if (enabled && reader.name() == QLatin1String("cat"))
cat(reader, cats);
if (reader.name() == QLatin1String("cat"))
l.addCat(cat(reader));
else if (reader.name() == QLatin1String("overlay"))
l.addOverlay(cat(reader));
else
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()) {
if (reader.name() == QLatin1String("layer"))
layer(reader, cats);
menu.addLayer(layer(reader));
else
reader.skipCurrentElement();
}
return menu;
}
void Style::rendertheme(QXmlStreamReader &reader, const QString &dir,
@ -540,9 +639,10 @@ void Style::rendertheme(QXmlStreamReader &reader, const QString &dir,
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("rule"))
rule(reader, dir, data, ratio, cats, r);
else if (reader.name() == QLatin1String("stylemenu"))
stylemenu(reader, cats);
else
else if (reader.name() == QLatin1String("stylemenu")) {
Menu menu(stylemenu(reader));
cats = menu.cats();
} else
reader.skipCurrentElement();
}
}
@ -650,7 +750,7 @@ QList<const Style::Symbol*> Style::pointSymbols(int zoom) const
for (int i = 0; i < _symbols.size(); 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
|| rule._type == Rule::NodeType))
list.append(&symbol);
@ -659,6 +759,19 @@ QList<const Style::Symbol*> Style::pointSymbols(int zoom) const
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 Symbol*> list;

View File

@ -192,6 +192,7 @@ public:
: Render(rule), _priority(0), _fillColor(Qt::black),
_strokeColor(Qt::black), _strokeWidth(0) {}
const QString &symbolId() const {return _symbolId;}
const QFont &font() const {return _font;}
const QColor &fillColor() const {return _fillColor;}
const QColor &strokeColor() const {return _strokeColor;}
@ -202,6 +203,7 @@ public:
private:
friend class Style;
QString _symbolId;
int _priority;
QColor _fillColor, _strokeColor;
qreal _strokeWidth;
@ -212,15 +214,20 @@ public:
class Symbol : public Render
{
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;}
bool rotate() const {return _rotate;}
int priority() const {return _priority;}
private:
friend class Style;
QString _id;
int _priority;
bool _rotate;
QImage _img;
};
@ -236,19 +243,60 @@ public:
QList<const TextRender*> areaLabels(int zoom) const;
QList<const Symbol*> pointSymbols(int zoom) const;
QList<const Symbol*> areaSymbols(int zoom) const;
QList<const Symbol*> lineSymbols(int zoom) const;
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<CircleRender> _circles;
QList<TextRender> _pathLabels, _pointLabels, _areaLabels;
QList<Symbol> _symbols;
QList<Symbol> _symbols, _lineSymbols;
bool loadXml(const QString &path, const MapData &data, qreal ratio);
void rendertheme(QXmlStreamReader &reader, const QString &dir,
const MapData &data, qreal ratio);
void layer(QXmlStreamReader &reader, QSet<QString> &cats);
void stylemenu(QXmlStreamReader &reader, QSet<QString> &cats);
void cat(QXmlStreamReader &reader, QSet<QString> &cats);
Menu::Layer layer(QXmlStreamReader &reader);
Menu stylemenu(QXmlStreamReader &reader);
QString cat(QXmlStreamReader &reader);
void rule(QXmlStreamReader &reader, const QString &dir, const MapData &data,
qreal ratio, const QSet<QString> &cats, const Rule &parent);
void area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
@ -258,7 +306,7 @@ private:
void text(QXmlStreamReader &reader, const MapData &data, const Rule &rule,
QList<QList<TextRender> *> &lists);
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)
{
QPointF tl(floor(rect.left() / _data.tileSize()) * _data.tileSize(),
floor(rect.top() / _data.tileSize()) * _data.tileSize());
int tileSize = (_data.tileSize() < 384)
? _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());
int width = ceil(s.width() / _data.tileSize());
int height = ceil(s.height() / _data.tileSize());
int width = ceil(s.width() / tileSize);
int height = ceil(s.height() / tileSize);
QList<RasterTile> tiles;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
QPoint ttl(tl.x() + i * _data.tileSize(), tl.y() + j
* _data.tileSize());
QPoint ttl(tl.x() + i * tileSize, tl.y() + j * tileSize);
if (isRunning(_zoom, ttl))
continue;
@ -187,8 +188,7 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
painter->drawPixmap(ttl, pm);
else {
tiles.append(RasterTile(_projection, _transform, &_style, &_data,
_zoom, QRect(ttl, QSize(_data.tileSize(), _data.tileSize())),
_tileRatio));
_zoom, QRect(ttl, QSize(tileSize, tileSize)), _tileRatio));
}
}
}

View File

@ -18,9 +18,8 @@ OnlineMap::OnlineMap(const QString &fileName, const QString &name,
{
_tileLoader = new TileLoader(QDir(ProgramPaths::tilesDir()).filePath(_name),
this);
_tileLoader->setUrl(url);
_tileLoader->setUrl(url, quadTiles ? TileLoader::QuadTiles : TileLoader::XYZ);
_tileLoader->setHeaders(headers);
_tileLoader->setQuadTiles(quadTiles);
connect(_tileLoader, &TileLoader::finished, this, &OnlineMap::tilesLoaded);
}

View File

@ -2,8 +2,9 @@
#include <QPainter>
#include "textpathitem.h"
#define CHAR_RATIO 0.55
#define MAX_TEXT_ANGLE 30
#define PADDING 2
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
#define INTERSECTS intersect
@ -12,6 +13,22 @@
#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)
{
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;
if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p)
== QLineF::BoundedIntersection) {
if (p == p2)
if (p == p2) {
swap(line, p1, p2);
return true;
}
p = p2;
}
if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p)
== QLineF::BoundedIntersection) {
if (p == p2)
if (p == p2) {
swap(line, p1, p2);
return true;
}
p = p2;
}
if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p)
== QLineF::BoundedIntersection) {
if (p == p2)
if (p == p2) {
swap(line, p1, p2);
return true;
}
}
Q_ASSERT(p != p2);
@ -197,6 +220,13 @@ static QList<QLineF> lineString(const QPainterPath &path,
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>
static QPainterPath textPath(const T &path, qreal textWidth,
qreal charWidth, const QRectF &tileRect)
@ -229,109 +259,129 @@ static QPainterPath textPath(const T &path, qreal textWidth,
: 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 angle = l.angle();
return (angle > 90 && angle < 270) ? true : false;
qreal cw, mw, textWidth;
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,
const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor) : TextItem(label), _font(font), _color(color),
_haloColor(haloColor)
const QColor *haloColor, const QImage *img, bool rotate)
: TextItem(label), _font(font), _color(color), _haloColor(haloColor),
_img(img), _rotate(rotate), _reverse(false)
{
qreal cw = font->pixelSize() * 0.6;
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();
init(line, tileRect);
}
TextPathItem::TextPathItem(const QPainterPath &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *haloColor) : TextItem(label), _font(font), _color(color),
_haloColor(haloColor)
const QColor *haloColor, const QImage *img, bool rotate)
: TextItem(label), _font(font), _color(color), _haloColor(haloColor),
_img(img), _rotate(rotate), _reverse(false)
{
qreal cw = font->pixelSize() * 0.6;
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();
init(line, tileRect);
}
void TextPathItem::paint(QPainter *painter) const
{
QFontMetrics fm(*_font);
int textWidth = fm.boundingRect(*_text).width();
if (_img) {
QSizeF s(_img->size() / _img->devicePixelRatioF());
qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth);
qreal percent = (1.0 - factor) / 2.0;
painter->save();
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) {
painter->setPen(*_haloColor);
if (_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++) {
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->drawText(QPoint(0, fm.descent()), _text->at(i));
painter->setTransform(t);
int width = fm.horizontalAdvance(_text->at(i));
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->setRenderHint(QPainter::Antialiasing, false);
//painter->drawPath(_shape);
}

View File

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

View File

@ -109,6 +109,7 @@ void TextPointItem::paint(QPainter *painter) const
}
//painter->setPen(Qt::red);
//painter.setBrush(Qt::NoBrush);
//painter->setRenderHint(QPainter::Antialiasing, false);
//painter->drawRect(_rect);
}

View File

@ -1,12 +1,8 @@
#ifndef TEXTPOINTITEM_H
#define TEXTPOINTITEM_H
#include <QRect>
#include <QString>
#include <QVector>
#include "textitem.h"
class QPainter;
class QFont;
class QImage;
class QColor;
@ -14,7 +10,6 @@ class QColor;
class TextPointItem : public TextItem
{
public:
TextPointItem() : TextItem(0), _font(0), _img(0) {}
TextPointItem(const QPoint &point, const QString *text, const QFont *font,
const QImage *img, const QColor *color, const QColor *haloColor,
const QColor *bgColor = 0, int padding = 0, double rotate = NAN);

View File

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

View File

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

View File

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

View File

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