From b6ca28e159c91facb7d0bcd5bd3c8c4f0b38a31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Sat, 8 Apr 2023 00:34:33 +0200 Subject: [PATCH] Render circles --- src/map/mapsforge/rastertile.cpp | 65 +++++++++++++++++++------- src/map/mapsforge/rastertile.h | 66 +++++++++++++++++++------- src/map/mapsforge/style.cpp | 80 +++++++++++++++++++++++++++++++- src/map/mapsforge/style.h | 27 +++++++++++ 4 files changed, 203 insertions(+), 35 deletions(-) diff --git a/src/map/mapsforge/rastertile.cpp b/src/map/mapsforge/rastertile.cpp index 85d2919f..9c64ac74 100644 --- a/src/map/mapsforge/rastertile.cpp +++ b/src/map/mapsforge/rastertile.cpp @@ -238,18 +238,17 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const return path; } -QVector RasterTile::pathInstructions( - QVector &paths) +void RasterTile::pathInstructions(QVector &paths, + QVector &instructions) { - QCache > cache(8192); - QVector instructions; + QCache > cache(8192); const Style &s = style(_ratio); QList *ri; for (int i = 0; i < _paths.size(); i++) { const MapData::Path &path = _paths.at(i); PainterPath &rp = paths[i]; - Key key(_zoom, path.closed, path.tags); + PathKey key(_zoom, path.closed, path.tags); rp.path = &path; @@ -257,34 +256,66 @@ QVector RasterTile::pathInstructions( ri = new QList(s.paths(_zoom, path.closed, path.tags)); for (int j = 0; j < ri->size(); j++) - instructions.append(PathInstruction(ri->at(j), &rp)); + instructions.append(RenderInstruction(ri->at(j), &rp)); cache.insert(key, ri); } else { for (int j = 0; j < ri->size(); j++) - instructions.append(PathInstruction(ri->at(j), &rp)); + instructions.append(RenderInstruction(ri->at(j), &rp)); } } +} - std::sort(instructions.begin(), instructions.end()); +void RasterTile::circleInstructions( + QVector &instructions) +{ + QCache > cache(8192); + const Style &s = style(_ratio); + QList *ri; - return instructions; + for (int i = 0; i < _points.size(); i++) { + const MapData::Point &point = _points.at(i); + PointKey key(_zoom, point.tags); + + if (!(ri = cache.object(key))) { + ri = new QList(s.circles(_zoom, point.tags)); + for (int j = 0; j < ri->size(); j++) + instructions.append(RenderInstruction(ri->at(j), &point)); + cache.insert(key, ri); + } else { + for (int j = 0; j < ri->size(); j++) + instructions.append(RenderInstruction(ri->at(j), &point)); + } + } } void RasterTile::drawPaths(QPainter *painter, QVector &paths) { - QVector instructions(pathInstructions(paths)); + QVector instructions; + pathInstructions(paths, instructions); + circleInstructions(instructions); + std::sort(instructions.begin(), instructions.end()); for (int i = 0; i < instructions.size(); i++) { - const PathInstruction &is = instructions.at(i); - const Style::PathRender *ri = is.render(); + const RenderInstruction &is = instructions.at(i); PainterPath *path = is.path(); - if (!path->pp.elementCount()) - path->pp = painterPath(path->path->poly, ri->curve()); + if (path) { + const Style::PathRender *ri = is.pathRender(); - painter->setPen(ri->pen(_zoom)); - painter->setBrush(ri->brush()); - painter->drawPath(path->pp); + if (!path->pp.elementCount()) + path->pp = painterPath(path->path->poly, ri->curve()); + + painter->setPen(ri->pen(_zoom)); + painter->setBrush(ri->brush()); + painter->drawPath(path->pp); + } else { + const Style::CircleRender *ri = is.circleRender(); + qreal radius = ri->radius(_zoom); + + painter->setPen(ri->pen()); + painter->setBrush(ri->brush()); + painter->drawEllipse(ll2xy(is.point()->coordinates), radius, radius); + } } } diff --git a/src/map/mapsforge/rastertile.h b/src/map/mapsforge/rastertile.h index d831dc71..d7fb65b1 100644 --- a/src/map/mapsforge/rastertile.h +++ b/src/map/mapsforge/rastertile.h @@ -59,33 +59,47 @@ private: const Style::Symbol *si; }; - class PathInstruction + class RenderInstruction { public: - PathInstruction() : _render(0), _path(0) {} - PathInstruction(const Style::PathRender *render, PainterPath *path) - : _render(render), _path(path) {} + RenderInstruction() : _pathRender(0), _circleRender(0), _path(0), + _point(0) {} + RenderInstruction(const Style::PathRender *render, PainterPath *path) + : _pathRender(render), _circleRender(0), _path(path), _point(0) {} + RenderInstruction(const Style::CircleRender *render, + const MapData::Point *point) : _pathRender(0), _circleRender(render), + _path(0), _point(point) {} - bool operator<(const PathInstruction &other) const + bool operator<(const RenderInstruction &other) const { - if (_path->path->layer == other._path->path->layer) - return _render->zOrder() < other._render->zOrder(); + if (layer() == other.layer()) + return zOrder() < other.zOrder(); else - return (_path->path->layer < other._path->path->layer); + return (layer() < other.layer()); } - const Style::PathRender *render() const {return _render;} + const Style::PathRender *pathRender() const {return _pathRender;} + const Style::CircleRender *circleRender() const {return _circleRender;} PainterPath *path() const {return _path;} + const MapData::Point *point() const {return _point;} private: - const Style::PathRender *_render; + int layer() const {return _path ? _path->path->layer : _point->layer;} + int zOrder() const + { + return _pathRender ? _pathRender->zOrder() : _circleRender->zOrder(); + } + + const Style::PathRender *_pathRender; + const Style::CircleRender *_circleRender; PainterPath *_path; + const MapData::Point *_point; }; - struct Key { - Key(int zoom, bool closed, const QVector &tags) + struct PathKey { + PathKey(int zoom, bool closed, const QVector &tags) : zoom(zoom), closed(closed), tags(tags) {} - bool operator==(const Key &other) const + bool operator==(const PathKey &other) const { return zoom == other.zoom && closed == other.closed && tags == other.tags; @@ -96,6 +110,18 @@ private: const QVector &tags; }; + struct PointKey { + PointKey(int zoom, const QVector &tags) + : zoom(zoom), tags(tags) {} + bool operator==(const PointKey &other) const + { + return zoom == other.zoom && tags == other.tags; + } + + int zoom; + const QVector &tags; + }; + class PointItem : public TextPointItem { public: @@ -116,9 +142,12 @@ private: ~PathItem() {delete _text;} }; - friend HASH_T qHash(const RasterTile::Key &key); + friend HASH_T qHash(const RasterTile::PathKey &key); + friend HASH_T qHash(const RasterTile::PointKey &key); - QVector pathInstructions(QVector &paths); + void pathInstructions(QVector &paths, + QVector &instructions); + void circleInstructions(QVector &instructions); QPointF ll2xy(const Coordinates &c) const {return _transform.proj2img(_proj.ll2xy(c));} void processPointLabels(QList &textItems); @@ -142,7 +171,12 @@ private: bool _valid; }; -inline HASH_T qHash(const RasterTile::Key &key) +inline HASH_T qHash(const RasterTile::PathKey &key) +{ + return ::qHash(key.zoom) ^ ::qHash(key.tags); +} + +inline HASH_T qHash(const RasterTile::PointKey &key) { return ::qHash(key.zoom) ^ ::qHash(key.tags); } diff --git a/src/map/mapsforge/style.cpp b/src/map/mapsforge/style.cpp index 800f97d0..79de8424 100644 --- a/src/map/mapsforge/style.cpp +++ b/src/map/mapsforge/style.cpp @@ -79,10 +79,24 @@ bool Style::Rule::match(int zoom, bool closed, return true; } +bool Style::Rule::match(int zoom, const QVector &tags) const +{ + if (_type && NodeType != _type) + return false; + if (!_zooms.contains(zoom)) + return false; + + for (int i = 0; i < _filters.size(); i++) + if (!_filters.at(i).match(tags)) + return false; + + return true; +} + void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio, const Rule &rule) { - PathRender ri(rule, _paths.size()); + PathRender ri(rule, _paths.size() + _circles.size()); const QXmlStreamAttributes &attr = reader.attributes(); QString file; int height = 0, width = 0; @@ -127,7 +141,7 @@ void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio, void Style::line(QXmlStreamReader &reader, const Rule &rule) { - PathRender ri(rule, _paths.size()); + PathRender ri(rule, _paths.size() + _circles.size()); const QXmlStreamAttributes &attr = reader.attributes(); bool ok; @@ -180,6 +194,49 @@ void Style::line(QXmlStreamReader &reader, const Rule &rule) reader.skipCurrentElement(); } +void Style::circle(QXmlStreamReader &reader, const Rule &rule) +{ + CircleRender ri(rule, _paths.size() + _circles.size()); + const QXmlStreamAttributes &attr = reader.attributes(); + bool ok; + QColor fillColor, strokeColor; + qreal strokeWidth = 0; + + if (attr.hasAttribute("fill")) + fillColor = QColor(attr.value("fill").toString()); + if (attr.hasAttribute("stroke")) + strokeColor = QColor(attr.value("stroke").toString()); + if (attr.hasAttribute("stroke-width")) { + strokeWidth = attr.value("stroke-width").toFloat(&ok); + if (!ok || strokeWidth < 0) { + reader.raiseError("invalid stroke-width value"); + return; + } + } + if (attr.hasAttribute("radius")) { + ri._radius = attr.value("radius").toDouble(&ok); + if (!ok || ri._radius <= 0) { + reader.raiseError("invalid radius value"); + return; + } + } else { + reader.raiseError("missing radius"); + return; + } + if (attr.hasAttribute("scale-radius")) { + if (attr.value("scale-radius").toString() == "true") + ri._scale = true; + } + + ri._pen = (strokeColor.isValid() && strokeWidth > 0) + ? QPen(QBrush(strokeColor), strokeWidth) : Qt::NoPen; + ri._brush = fillColor.isValid() ? QBrush(fillColor) : Qt::NoBrush; + + _circles.append(ri); + + reader.skipCurrentElement(); +} + void Style::text(QXmlStreamReader &reader, const Rule &rule, QList*> &lists) { @@ -351,6 +408,8 @@ void Style::rule(QXmlStreamReader &reader, const QString &dir, qreal ratio, area(reader, dir, ratio, r); else if (reader.name() == QLatin1String("line")) line(reader, r); + else if (reader.name() == QLatin1String("circle")) + circle(reader, r); else if (reader.name() == QLatin1String("pathText")) { QList*> list; list.append(&_pathLabels); @@ -458,6 +517,18 @@ QList Style::paths(int zoom, bool closed, return ri; } +QList Style::circles(int zoom, + const QVector &tags) const +{ + QList ri; + + for (int i = 0; i < _circles.size(); i++) + if (_circles.at(i).rule().match(zoom, tags)) + ri.append(&_circles.at(i)); + + return ri; +} + QList Style::pathLabels(int zoom) const { QList list; @@ -549,3 +620,8 @@ QBrush Style::PathRender::brush() const else return Qt::NoBrush; } + +qreal Style::CircleRender::radius(int zoom) const +{ + return (_scale && zoom >= 12) ? pow(1.5, zoom - 12) * _radius : _radius; +} diff --git a/src/map/mapsforge/style.h b/src/map/mapsforge/style.h index 1f856373..09a54143 100644 --- a/src/map/mapsforge/style.h +++ b/src/map/mapsforge/style.h @@ -33,6 +33,7 @@ public: bool match(bool closed, const QVector &tags) const; bool match(int zoom, bool closed, const QVector &tags) const; + bool match(int zoom, const QVector &tags) const; private: enum Type { @@ -184,6 +185,28 @@ public: bool _area, _curve; }; + class CircleRender : public Render + { + public: + CircleRender(const Rule &rule, int zOrder) : Render(rule), + _zOrder(zOrder), _pen(Qt::NoPen), _brush(Qt::NoBrush), + _scale(false) {} + + int zOrder() const {return _zOrder;} + const QPen &pen() const {return _pen;} + const QBrush &brush() const {return _brush;} + qreal radius(int zoom) const; + + private: + friend class Style; + + int _zOrder; + QPen _pen; + QBrush _brush; + qreal _radius; + bool _scale; + }; + class TextRender : public Render { public: @@ -227,6 +250,8 @@ public: QList paths(int zoom, bool closed, const QVector &tags) const; + QList circles(int zoom, + const QVector &tags) const; QList pathLabels(int zoom) const; QList pointLabels(int zoom) const; QList areaLabels(int zoom) const; @@ -235,6 +260,7 @@ public: private: QList _paths; + QList _circles; QList _pathLabels, _pointLabels, _areaLabels; QList _symbols; @@ -248,6 +274,7 @@ private: void area(QXmlStreamReader &reader, const QString &dir, qreal ratio, const Rule &rule); void line(QXmlStreamReader &reader, const Rule &rule); + void circle(QXmlStreamReader &reader, const Rule &rule); void text(QXmlStreamReader &reader, const Rule &rule, QList *> &lists); void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,