From b65682a82878f2cc3c6e0db919012251db6c254f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Thu, 15 Apr 2021 23:11:47 +0200 Subject: [PATCH] Fixed/improved label rendering --- src/map/mapsforge/mapdata.cpp | 23 ++++--- src/map/mapsforge/mapdata.h | 6 +- src/map/mapsforge/rastertile.cpp | 100 ++++++++++++++++++++++--------- src/map/mapsforge/style.cpp | 13 ++-- src/map/mapsforge/style.h | 6 +- 5 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/map/mapsforge/mapdata.cpp b/src/map/mapsforge/mapdata.cpp index d5756cd3..d8f6e9c9 100644 --- a/src/map/mapsforge/mapdata.cpp +++ b/src/map/mapsforge/mapdata.cpp @@ -545,7 +545,7 @@ bool MapData::readPaths(const VectorTile *tile, int zoom, QList *list) quint32 blocks, unused, val, cnt = 0; quint16 bitmap; quint8 sb, flags; - QByteArray label, houseNumber, reference; + QByteArray name, houseNumber, reference; if (!subfile.seek(tile->offset & OFFSET_MASK)) @@ -579,18 +579,20 @@ bool MapData::readPaths(const VectorTile *tile, int zoom, QList *list) if (!subfile.readByte(flags)) return false; if (flags & 0x80) { - if (!subfile.readString(label)) + if (!subfile.readString(name)) return false; - p.label = label.split('\r').first(); + name = name.split('\r').first(); + p.tags.append(Tag("name", name)); } if (flags & 0x40) { if (!subfile.readString(houseNumber)) return false; + p.tags.append(Tag("addr:housenumber", houseNumber)); } if (flags & 0x20) { if (!subfile.readString(reference)) return false; - p.reference = reference; + p.tags.append(Tag("ref", reference)); } if (flags & 0x10) { if (!(subfile.readVInt32(lat) && subfile.readVInt32(lon))) @@ -626,7 +628,7 @@ bool MapData::readPoints(const VectorTile *tile, int zoom, QList *list) QVector points(rows); quint32 val, unused, cnt = 0; quint8 sb, flags; - QByteArray label, houseNumber; + QByteArray name, houseNumber; if (!subfile.seek(tile->offset & OFFSET_MASK)) @@ -660,18 +662,21 @@ bool MapData::readPoints(const VectorTile *tile, int zoom, QList *list) if (!subfile.readByte(flags)) return false; if (flags & 0x80) { - if (!subfile.readString(label)) + if (!subfile.readString(name)) return false; - p.label = label.split('\r').first(); + name = name.split('\r').first(); + p.tags.append(Tag("name", name)); } if (flags & 0x40) { if (!subfile.readString(houseNumber)) return false; + p.tags.append(Tag("addr:housenumber", houseNumber)); } if (flags & 0x20) { - qint32 unused; - if (!subfile.readVInt32(unused)) + qint32 elevation; + if (!subfile.readVInt32(elevation)) return false; + p.tags.append(Tag("ele", QByteArray::number(elevation))); } setPointId(p); diff --git a/src/map/mapsforge/mapdata.h b/src/map/mapsforge/mapdata.h index 8e9712ae..a65b49ca 100644 --- a/src/map/mapsforge/mapdata.h +++ b/src/map/mapsforge/mapdata.h @@ -43,24 +43,24 @@ public: Point(const Coordinates &c) : coordinates(c) {} Coordinates coordinates; - QString label; QVector tags; int layer; quint64 id; + QString label; + bool operator<(const Point &other) const {return id > other.id;} }; struct Path { Polygon poly; - QString label; - QString reference; QVector tags; Coordinates labelPos; int layer; bool closed; + QString label; QPainterPath path; bool operator<(const Path &other) const diff --git a/src/map/mapsforge/rastertile.cpp b/src/map/mapsforge/rastertile.cpp index 7195657e..f9d657f9 100644 --- a/src/map/mapsforge/rastertile.cpp +++ b/src/map/mapsforge/rastertile.cpp @@ -44,6 +44,41 @@ static QPointF centroid(const QPainterPath &polygon) return QPointF(cx * factor, cy * factor); } +static QString *pointLabel(const Style::TextRender *ri, MapData::Point &point) +{ + for (int i = 0; i < point.tags.size(); i++) { + if (point.tags.at(i).key == ri->key()) { + if (point.tags.at(i).value.isEmpty()) + return 0; + else { + point.label = point.tags.at(i).value; + return &point.label; + } + } + } + + return 0; +} + +static QString *pathLabel(const Style::TextRender *ri, MapData::Path &path, + bool *limit = 0) +{ + for (int i = 0; i < path.tags.size(); i++) { + if (path.tags.at(i).key == ri->key()) { + if (path.tags.at(i).value.isEmpty()) + return 0; + else { + path.label = path.tags.at(i).value; + if (limit) + *limit = (path.tags.at(i).key == "ref"); + return &path.label; + } + } + } + + return 0; +} + void RasterTile::processPoints(QList &textItems) { const Style &s = style(); @@ -51,23 +86,23 @@ void RasterTile::processPoints(QList &textItems) QList symbols(s.symbols(_zoom)); for (int i = 0; i < _points.size(); i++) { - const MapData::Point &point = _points.at(i); - const QString *label = point.label.isEmpty() ? 0 : &(point.label); + MapData::Point &point = _points[i]; + QString *label = 0; const Style::TextRender *ti = 0; const Style::Symbol *si = 0; - if (label) { - for (int i = 0; i < labels.size(); i++) { - const Style::TextRender *ri = labels.at(i); - if (ri->rule().match(point.tags)) { + for (int j = 0; j < labels.size(); j++) { + const Style::TextRender *ri = labels.at(j); + if (ri->rule().match(point.tags)) { + if ((label = pointLabel(ri, point))) { ti = ri; break; } } } - for (int i = 0; i < symbols.size(); i++) { - const Style::Symbol *ri = symbols.at(i); + for (int j = 0; j < symbols.size(); j++) { + const Style::Symbol *ri = symbols.at(j); if (ri->rule().match(point.tags)) { si = ri; break; @@ -102,21 +137,22 @@ void RasterTile::processAreaNames(const QRect &tileRect, const Style::TextRender *ri = instructions.at(i); for (int j = 0; j < _paths.size(); j++) { - const MapData::Path &path = _paths.at(j); + MapData::Path &path = _paths[j]; + QString *label = 0; - if (!path.closed || path.label.isEmpty()) - continue; - if (!path.path.elementCount()) - continue; - if (set.contains(path.label)) + if (!path.closed || !path.path.elementCount()) continue; if (!ri->rule().match(path.closed, path.tags)) continue; + if (!(label = pathLabel(ri, path))) + continue; + if (set.contains(path.label)) + continue; QPointF pos = path.labelPos.isNull() ? centroid(path.path) : ll2xy(path.labelPos); - TextPointItem *item = new TextPointItem(pos.toPoint(), &path.label, + TextPointItem *item = new TextPointItem(pos.toPoint(), label, &ri->font(), 0, &ri->fillColor(), 0, false); if (item->isValid() && tileRect.contains(item->boundingRect().toRect()) && !item->collides(textItems)) { @@ -133,26 +169,32 @@ void RasterTile::processStreetNames(const QRect &tileRect, { const Style &s = style(); QList instructions(s.pathLabels(_zoom)); + QSet set; for (int i = 0; i < instructions.size(); i++) { const Style::TextRender *ri = instructions.at(i); for (int j = 0; j < _paths.size(); j++) { MapData::Path &path = _paths[j]; + QString *label = 0; + bool limit = false; - if (path.label.isEmpty()) - continue; if (!path.path.elementCount()) continue; if (!ri->rule().match(path.closed, path.tags)) continue; + if (!(label = pathLabel(ri, path, &limit))) + continue; + if (limit && set.contains(path.label)) + continue; - TextPathItem *item = new TextPathItem(path.path, - &path.label, tileRect, &ri->font(), &ri->fillColor(), - &ri->strokeColor()); - if (item->isValid() && !item->collides(textItems)) + TextPathItem *item = new TextPathItem(path.path, label, tileRect, + &ri->font(), &ri->fillColor(), &ri->strokeColor()); + if (item->isValid() && !item->collides(textItems)) { textItems.append(item); - else + if (limit) + set.insert(path.label); + } else delete item; } } @@ -185,6 +227,7 @@ QVector RasterTile::pathInstructions() QCache > cache(1024); QVector instructions; const Style &s = style(); + QVector *ri; for (int i = 0 ; i < _paths.size(); i++) { MapData::Path &path = _paths[i]; @@ -192,9 +235,8 @@ QVector RasterTile::pathInstructions() Key key(_zoom, path.closed, path.tags); QVector *cached = cache.object(key); if (!cached) { - QVector *ri - = new QVector(); - s.match(_zoom, path.closed, path.tags, ri); + ri = new QVector(s.paths(_zoom, + path.closed, path.tags)); for (int j = 0; j < ri->size(); j++) instructions.append(PathInstruction(ri->at(j), &path)); cache.insert(key, ri); @@ -212,7 +254,9 @@ QVector RasterTile::pathInstructions() void RasterTile::drawPaths(QPainter *painter) { QVector instructions(pathInstructions()); - const Style::PathRender *lri = 0; + if (instructions.isEmpty()) + return; + const Style::PathRender *lri = instructions.first().render(); QPixmap layer(_pixmap.size()); layer.fill(Qt::transparent); @@ -226,11 +270,11 @@ void RasterTile::drawPaths(QPainter *painter) PathInstruction &is = instructions[i]; const Style::PathRender *ri = is.render(); - if (lri && ri != lri) { + if (ri != lri) { painter->drawPixmap(_xy, layer); lp.fillRect(QRect(_xy, _pixmap.size()), Qt::transparent); + lri = ri; } - lri = ri; if (!is.path()->path.elementCount()) is.path()->path = painterPath(is.path()->poly); diff --git a/src/map/mapsforge/style.cpp b/src/map/mapsforge/style.cpp index bc06e625..61081ac5 100644 --- a/src/map/mapsforge/style.cpp +++ b/src/map/mapsforge/style.cpp @@ -152,6 +152,8 @@ void Style::text(QXmlStreamReader &reader, const Rule &rule, int fontSize = 9; bool bold = false, italic = false; + if (attr.hasAttribute("k")) + ri._key = attr.value("k").toLatin1(); if (attr.hasAttribute("fill")) ri._fillColor = QColor(attr.value("fill").toString()); if (attr.hasAttribute("stroke")) @@ -169,7 +171,6 @@ void Style::text(QXmlStreamReader &reader, const Rule &rule, italic = true; } } - ri._font.setPixelSize(fontSize); ri._font.setBold(bold); ri._font.setItalic(italic); @@ -337,12 +338,16 @@ Style::Style(const QString &path) loadXml(":/mapsforge/default.xml"); } -void Style::match(int zoom, bool closed, const QVector &tags, - QVector *ri) const +QVector Style::paths(int zoom, bool closed, + const QVector &tags) const { + QVector ri; + for (int i = 0; i < _paths.size(); i++) if (_paths.at(i).rule().match(zoom, closed, tags)) - ri->append(&_paths.at(i)); + ri.append(&_paths.at(i)); + + return ri; } QList Style::pathLabels(int zoom) const diff --git a/src/map/mapsforge/style.h b/src/map/mapsforge/style.h index 2f7bd28b..0b784acb 100644 --- a/src/map/mapsforge/style.h +++ b/src/map/mapsforge/style.h @@ -192,12 +192,14 @@ public: const QFont &font() const {return _font;} const QColor &fillColor() const {return _fillColor;} const QColor &strokeColor() const {return _strokeColor;} + const QByteArray &key() const {return _key;} private: friend class Style; QColor _fillColor, _strokeColor; QFont _font; + QByteArray _key; }; class Symbol : public Render @@ -215,8 +217,8 @@ public: Style(const QString &path); - void match(int zoom, bool closed, const QVector &tags, - QVector *ri) const; + QVector paths(int zoom, bool closed, + const QVector &tags) const; QList pathLabels(int zoom) const; QList pointLabels(int zoom) const; QList areaLabels(int zoom) const;