mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-07-16 20:04:23 +02:00
Compare commits
8 Commits
961061b643
...
13.4
Author | SHA1 | Date | |
---|---|---|---|
9afeaf672a | |||
5ddd63e697 | |||
88fa1ed786 | |||
1233d20a21 | |||
1746eddb8d | |||
ecda5103c8 | |||
50b0ff1c56 | |||
2b300fab54 |
Binary file not shown.
Before Width: | Height: | Size: 224 B After Width: | Height: | Size: 214 B |
@ -103,12 +103,10 @@ static Coordinates centroid(const QVector<Coordinates> &polygon)
|
||||
|
||||
static double angle(uint type, const QVariant ¶m)
|
||||
{
|
||||
if (type>>16 == RDOCAL || type>>16 == I_RDOCAL)
|
||||
return 90 + param.toDouble();
|
||||
else if (type>>16 == CURENT)
|
||||
return 90 + param.toDouble();
|
||||
else
|
||||
return NAN;
|
||||
uint bt = type>>16;
|
||||
|
||||
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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -8,6 +8,8 @@
|
||||
using namespace Mapsforge;
|
||||
|
||||
#define TEXT_EXTENT 160
|
||||
#define PATHS_EXTENT 20
|
||||
#define SEARCH_EXTENT -0.5
|
||||
|
||||
static double limit = cos(deg2rad(170));
|
||||
|
||||
@ -100,44 +102,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 +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;
|
||||
@ -167,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)) {
|
||||
@ -178,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)
|
||||
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 +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;
|
||||
@ -314,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;
|
||||
@ -357,6 +415,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 +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()
|
||||
@ -409,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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user