1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-06 23:52:51 +02:00

Compare commits

..

15 Commits
13.3 ... 13.4

23 changed files with 636 additions and 315 deletions

View File

@ -1,4 +1,4 @@
version: 13.3.{build}
version: 13.4.{build}
configuration:
- Release

View File

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

View File

@ -198,6 +198,9 @@
<file alias="building.png">icons/map/marine/building.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="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>
<!-- Mapsforge rendertheme -->

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
Name "GPXSee"
; Program version
!define VERSION "13.3"
!define VERSION "13.4"
; The file to write
OutFile "GPXSee-${VERSION}_x64.exe"

View File

@ -23,6 +23,7 @@ static QMap<uint,uint> orderMapInit()
map.insert(TYPE(FOGSIG), 0);
map.insert(TYPE(CGUSTA), 1);
map.insert(TYPE(RSCSTA), 1);
map.insert(SUBTYPE(BUAARE, 1), 2);
map.insert(SUBTYPE(BUAARE, 5), 3);
map.insert(SUBTYPE(BUAARE, 4), 4);
@ -264,6 +265,13 @@ MapData::Point::Point(uint type, const Coordinates &c, const QString &label,
if (_label.isEmpty())
_label = sistat(type & 0xFF);
_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)
|| (OBJL == 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();
if ((OBJL == I_RDOCAL && key == COMCHA)
|| (OBJL == RDOCAL && key == COMCHA)

View File

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

View File

@ -10,15 +10,11 @@
using namespace ENC;
#define TEXT_EXTENT 160
#define TSSLPT_SIZE 0.005 /* ll */
#define RDOCAL_SIZE 12 /* px */
#define CURENT_SIZE 12 /* px */
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 struct {
@ -105,67 +101,12 @@ static Coordinates centroid(const QVector<Coordinates> &polygon)
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,
QImage::Format_ARGB32_Premultiplied);
img->fill(Qt::transparent);
QPainter p(img);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(QPen(QColor("#eb49eb"), 1));
uint bt = type>>16;
QPointF arrow[3];
arrow[0] = QPointF(img->width()/2, img->height()/2);
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;
return (bt == RDOCAL || bt == I_RDOCAL || bt == CURENT)
? 90 + param.toDouble() : NAN;
}
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++) {
const QVector<Coordinates> &subpath = polygon.at(i);
path.moveTo(ll2xy(subpath.first()));
for (int j = 1; j < subpath.size(); j++)
path.lineTo(ll2xy(subpath.at(j)));
QVector<QPointF> p(subpath.size());
for (int j = 0; j < subpath.size(); j++)
p[j] = ll2xy(subpath.at(j));
path.addPolygon(p);
}
return path;
@ -352,20 +294,19 @@ void RasterTile::processPoints(QList<MapData::Point*> &points,
const Style::Point &style = s.point(point->type());
const QString *label = point->label().isEmpty() ? 0 : &(point->label());
QImage *rimg = style.img().isNull()
? image(point->type(), point->param()) : 0;
const QImage *img = style.img().isNull() ? rimg : &style.img();
const QImage *img = style.img().isNull() ? 0 : &style.img();
const QFont *fnt = showLabel(img, _data->zooms(), _zoom, point->type())
? font(style.textFontSize()) : 0;
const QColor *color = &style.textColor();
const QColor *hColor = style.haloColor().isValid()
? &style.haloColor() : 0;
double rotate = angle(point->type(), point->param());
if ((!label || !fnt) && !img)
continue;
PointItem *item = new PointItem(pos, label, fnt, img, rimg, color,
hColor);
TextPointItem *item = new TextPointItem(pos, label, fnt, img, color,
hColor, 0, 2, rotate);
if (item->isValid() && !item->collides(textItems)) {
textItems.append(item);
if (lightsMap.contains(point->pos()))

View File

@ -28,19 +28,6 @@ public:
void render();
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,
QList<MapData::Point*> &points);
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, 4)] = _points[SUBTYPE(I_DISMAR, 2)];
_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(RADSTA)] = Point(QImage(":/marine/radar.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)].setTextColor(QColor("#eb49eb"));
_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(I_RDOCAL)] = Point(QImage(":/marine/radio-call.png"));
_points[TYPE(I_RDOCAL)].setTextColor(QColor("#eb49eb"));
_points[TYPE(PYLONS)] = Point(QImage(":/marine/pylon.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(SISTAT)] = Point(QImage(":/marine/pylon.png"));
_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, 11)] = Point(QImage(":/POI/pharmacy-11.png"), Small);

View File

@ -490,5 +490,5 @@ void RasterTile::render()
//painter.setPen(Qt::red);
//painter.setRenderHint(QPainter::Antialiasing, false);
//painter.drawRect(QRect(_xy, _pixmap.size()));
//painter.drawRect(_rect);
}

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,3 +1,4 @@
#include <cmath>
#include <QPainter>
#include <QCache>
#include "common/programpaths.h"
@ -7,6 +8,10 @@
using namespace Mapsforge;
#define TEXT_EXTENT 160
#define PATHS_EXTENT 20
#define SEARCH_EXTENT -0.5
static double limit = cos(deg2rad(170));
static qreal area(const QPainterPath &polygon)
{
@ -55,45 +60,89 @@ static const QColor *haloColor(const Style::TextRender *ti)
? &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,
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;
@ -108,14 +157,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;
@ -123,6 +173,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)) {
@ -134,61 +247,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)
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;
bool rotate = p.si ? p.si->rotate() : false;
bool limit = false;
if (p.ti) {
limit = (p.ti->key() == ID_ELE || p.ti->key() == ID_REF);
if (limit && set.contains(*p.lbl))
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);
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;
PointItem *item = new PointItem(pos.toPoint(), lbl, font, img, color,
hColor);
if (item->isValid() && _rect.contains(item->boundingRect().toRect())
&& !item->collides(textItems))
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;
}
}
void RasterTile::processLineLabels(QList<TextItem*> &textItems,
QVector<PainterPath> &paths)
{
QList<const Style::TextRender*> instructions(_style->pathLabels(_zoom));
QSet<QByteArray> set;
for (int i = 0; i < instructions.size(); i++) {
const Style::TextRender *ri = instructions.at(i);
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))
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;
}
}
}
@ -244,7 +346,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;
@ -270,7 +372,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;
@ -306,12 +408,17 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
if (path) {
const Style::PathRender *ri = is.pathRender();
qreal dy = ri->dy(_zoom);
if (!path->pp.elementCount())
path->pp = painterPath(path->path->poly, ri->curve());
painter->setPen(ri->pen(_zoom));
painter->setBrush(ri->brush());
if (dy != 0)
painter->drawPath(parallelPath(path->pp, dy));
else
painter->drawPath(path->pp);
} else {
const Style::CircleRender *ri = is.circleRender();
@ -325,18 +432,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()
@ -361,18 +472,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")) {
@ -245,6 +298,13 @@ void Style::line(QXmlStreamReader &reader, const Rule &rule)
if (curve == "cubic")
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)
_paths.append(ri);
@ -361,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);
@ -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,
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"))
@ -404,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) {
@ -411,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();
}
@ -486,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,
@ -533,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();
}
}
@ -643,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);
@ -652,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;
@ -689,6 +809,11 @@ QPen Style::PathRender::pen(int zoom) const
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
{
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),
_zOrder(zOrder), _strokeWidth(0), _strokeCap(Qt::RoundCap),
_strokeJoin(Qt::RoundJoin), _area(false), _curve(false),
_scale(Stroke) {}
_scale(Stroke), _dy(0) {}
int zOrder() const {return _zOrder;}
QPen pen(int zoom) const;
const QBrush &brush() const {return _brush;}
bool area() const {return _area;}
bool curve() const {return _curve;}
qreal dy(int zoom) const;
private:
friend class Style;
@ -159,6 +160,7 @@ public:
QBrush _brush;
bool _area, _curve;
Scale _scale;
qreal _dy;
};
class CircleRender : public Render
@ -190,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;}
@ -200,6 +203,7 @@ public:
private:
friend class Style;
QString _symbolId;
int _priority;
QColor _fillColor, _strokeColor;
qreal _strokeWidth;
@ -210,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;
};
@ -234,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,
@ -256,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

@ -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,21 +57,27 @@ 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,65 +259,82 @@ 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
{
if (_img) {
QSizeF s(_img->size() / _img->devicePixelRatioF());
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();
}
if (_text) {
QFontMetrics fm(*_font);
int textWidth = fm.boundingRect(*_text).width();
qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth);
qreal percent = (1.0 - factor) / 2.0;
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);
@ -315,7 +362,7 @@ void TextPathItem::paint(QPainter *painter) const
int width = fm.horizontalAdvance(_text->at(i));
percent += ((qreal)width / (qreal)textWidth) * factor;
}
percent = (1.0 - factor) / 2.0;
percent = ((1.0 - factor) + imgPercent) / 2.0;
}
painter->setPen(_color ? *_color : Qt::black);
@ -331,7 +378,10 @@ void TextPathItem::paint(QPainter *painter) const
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

@ -1,3 +1,4 @@
#include <cmath>
#include <QFont>
#include <QFontMetrics>
#include <QImage>
@ -18,9 +19,9 @@ static void expand(QRectF &rect, int width)
TextPointItem::TextPointItem(const QPoint &point, const QString *text,
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),
_haloColor(haloColor), _bgColor(bgColor)
_haloColor(haloColor), _bgColor(bgColor), _rotate(rotate)
{
if (_text) {
QFontMetrics fm(*_font);
@ -58,8 +59,17 @@ void TextPointItem::paint(QPainter *painter) const
{
if (_img && !_img->isNull()) {
QSizeF s(_img->size() / _img->devicePixelRatioF());
if (std::isnan(_rotate))
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) {
@ -99,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,10 +10,9 @@ 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);
const QColor *bgColor = 0, int padding = 0, double rotate = NAN);
bool isValid() const {return !_rect.isEmpty();}
@ -31,6 +26,7 @@ private:
const QFont *_font;
const QImage *_img;
const QColor *_color, *_haloColor, *_bgColor;
double _rotate;
QRectF _rect, _textRect;
QPainterPath _shape;
};