mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-01-19 04:02:09 +01:00
Added support for lineSymbols in Mapsforge maps
This commit is contained in:
parent
1746eddb8d
commit
1233d20a21
@ -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);
|
||||
}
|
||||
|
@ -207,33 +207,58 @@ void RasterTile::processAreaLabels(const QVector<PainterPath> &paths,
|
||||
void RasterTile::processLineLabels(const QVector<PainterPath> &paths,
|
||||
QList<TextItem*> &textItems) const
|
||||
{
|
||||
QList<const Style::TextRender*> instructions(_style->pathLabels(_zoom));
|
||||
QList<const Style::TextRender*> labels(_style->pathLabels(_zoom));
|
||||
QList<const Style::Symbol*> symbols(_style->lineSymbols(_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++) {
|
||||
const PainterPath &path = paths.at(i);
|
||||
const Style::TextRender *ti = 0;
|
||||
const Style::Symbol *si = 0;
|
||||
const QByteArray *lbl = 0;
|
||||
bool limit = false;
|
||||
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
const PainterPath &path = paths.at(i);
|
||||
const QByteArray *lbl = label(ri->key(), path.path->tags);
|
||||
if (path.path->closed)
|
||||
continue;
|
||||
|
||||
if (!lbl)
|
||||
continue;
|
||||
if (!ri->rule().match(path.path->closed, path.path->tags))
|
||||
continue;
|
||||
bool limit = (ri->key() == ID_ELE || ri->key() == ID_REF);
|
||||
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)))
|
||||
ti = ri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < symbols.size(); j++) {
|
||||
const Style::Symbol *ri = symbols.at(j);
|
||||
if (ri->rule().match(path.path->tags)) {
|
||||
si = ri;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ti && !si)
|
||||
continue;
|
||||
if (ti) {
|
||||
limit = (ti->key() == ID_ELE || ti->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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
PathItem *item = new PathItem(path.pp, lbl, img, _rect, font, color,
|
||||
hColor);
|
||||
if (item->isValid() && !item->collides(textItems)) {
|
||||
textItems.append(item);
|
||||
if (limit)
|
||||
set.insert(*lbl);
|
||||
} else
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,6 +434,7 @@ 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);
|
||||
@ -420,7 +446,8 @@ void RasterTile::render()
|
||||
|
||||
//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);
|
||||
|
||||
|
@ -137,9 +137,10 @@ 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) : TextPathItem(line,
|
||||
label ? new QString(*label) : 0, tileRect, font, color, haloColor,
|
||||
img) {}
|
||||
~PathItem() {delete _text;}
|
||||
};
|
||||
|
||||
|
@ -419,7 +419,7 @@ 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();
|
||||
@ -457,7 +457,7 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
|
||||
|
||||
ri._img = image(file, width, height, ratio);
|
||||
|
||||
_symbols.append(ri);
|
||||
list.append(ri);
|
||||
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
@ -529,7 +529,9 @@ 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();
|
||||
}
|
||||
@ -716,7 +718,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);
|
||||
@ -725,6 +727,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;
|
||||
|
@ -236,6 +236,7 @@ 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 {
|
||||
@ -282,6 +283,7 @@ private:
|
||||
QList<CircleRender> _circles;
|
||||
QList<TextRender> _pathLabels, _pointLabels, _areaLabels;
|
||||
QList<Symbol> _symbols;
|
||||
QList<Symbol> _lineSymbols;
|
||||
|
||||
bool loadXml(const QString &path, const MapData &data, qreal ratio);
|
||||
void rendertheme(QXmlStreamReader &reader, const QString &dir,
|
||||
@ -298,7 +300,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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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,128 @@ 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;
|
||||
textWidth = _text->size() * cw + _img->width() + PADDING;
|
||||
} else if (_text) {
|
||||
cw = _font->pixelSize() * CHAR_RATIO;
|
||||
mw = _font->pixelSize() / 2;
|
||||
textWidth = _text->size() * cw;
|
||||
} else {
|
||||
cw = _img->width();
|
||||
mw = _img->height() / 2;
|
||||
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) : TextItem(label), _font(font),
|
||||
_color(color), _haloColor(haloColor), _img(img), _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) : TextItem(label), _font(font),
|
||||
_color(color), _haloColor(haloColor), _img(img), _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));
|
||||
|
||||
QTransform t = painter->transform();
|
||||
if (_reverse)
|
||||
painter->drawImage(QPointF(0, -s.height()/2),
|
||||
_img->transformed(QTransform().rotate(180.0)));
|
||||
else
|
||||
painter->drawImage(QPointF(0, -s.height()/2), *_img);
|
||||
|
||||
painter->setFont(*_font);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
if (_haloColor) {
|
||||
painter->setPen(*_haloColor);
|
||||
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);
|
||||
|
||||
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->drawPath(_shape);
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ 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);
|
||||
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 isValid() const {return !_path.isEmpty();}
|
||||
|
||||
@ -23,12 +23,16 @@ 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;
|
||||
const QImage *_img;
|
||||
QPainterPath _path;
|
||||
QRectF _rect;
|
||||
QPainterPath _shape;
|
||||
bool _reverse;
|
||||
};
|
||||
|
||||
#endif // TEXTPATHITEM_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user