1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-01-31 09:05:14 +01:00

Render circles

This commit is contained in:
Martin Tůma 2023-04-08 00:34:33 +02:00
parent 648627b17f
commit b6ca28e159
4 changed files with 203 additions and 35 deletions

View File

@ -238,18 +238,17 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon, bool curve) const
return path;
}
QVector<RasterTile::PathInstruction> RasterTile::pathInstructions(
QVector<PainterPath> &paths)
void RasterTile::pathInstructions(QVector<PainterPath> &paths,
QVector<RasterTile::RenderInstruction> &instructions)
{
QCache<Key, QList<const Style::PathRender *> > cache(8192);
QVector<PathInstruction> instructions;
QCache<PathKey, QList<const Style::PathRender *> > cache(8192);
const Style &s = style(_ratio);
QList<const Style::PathRender*> *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::PathInstruction> RasterTile::pathInstructions(
ri = new QList<const Style::PathRender*>(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<RasterTile::RenderInstruction> &instructions)
{
QCache<PointKey, QList<const Style::CircleRender *> > cache(8192);
const Style &s = style(_ratio);
QList<const Style::CircleRender*> *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<const Style::CircleRender*>(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<PainterPath> &paths)
{
QVector<PathInstruction> instructions(pathInstructions(paths));
QVector<RenderInstruction> 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);
}
}
}

View File

@ -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<MapData::Tag> &tags)
struct PathKey {
PathKey(int zoom, bool closed, const QVector<MapData::Tag> &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<MapData::Tag> &tags;
};
struct PointKey {
PointKey(int zoom, const QVector<MapData::Tag> &tags)
: zoom(zoom), tags(tags) {}
bool operator==(const PointKey &other) const
{
return zoom == other.zoom && tags == other.tags;
}
int zoom;
const QVector<MapData::Tag> &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<PathInstruction> pathInstructions(QVector<PainterPath> &paths);
void pathInstructions(QVector<PainterPath> &paths,
QVector<RasterTile::RenderInstruction> &instructions);
void circleInstructions(QVector<RasterTile::RenderInstruction> &instructions);
QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));}
void processPointLabels(QList<TextItem*> &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);
}

View File

@ -79,10 +79,24 @@ bool Style::Rule::match(int zoom, bool closed,
return true;
}
bool Style::Rule::match(int zoom, const QVector<MapData::Tag> &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<QList<TextRender>*> &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<QList<TextRender>*> list;
list.append(&_pathLabels);
@ -458,6 +517,18 @@ QList<const Style::PathRender *> Style::paths(int zoom, bool closed,
return ri;
}
QList<const Style::CircleRender *> Style::circles(int zoom,
const QVector<MapData::Tag> &tags) const
{
QList<const CircleRender*> 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<const Style::TextRender*> Style::pathLabels(int zoom) const
{
QList<const TextRender*> 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;
}

View File

@ -33,6 +33,7 @@ public:
bool match(bool closed, const QVector<MapData::Tag> &tags) const;
bool match(int zoom, bool closed,
const QVector<MapData::Tag> &tags) const;
bool match(int zoom, const QVector<MapData::Tag> &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<const PathRender *> paths(int zoom, bool closed,
const QVector<MapData::Tag> &tags) const;
QList<const CircleRender *> circles(int zoom,
const QVector<MapData::Tag> &tags) const;
QList<const TextRender*> pathLabels(int zoom) const;
QList<const TextRender*> pointLabels(int zoom) const;
QList<const TextRender*> areaLabels(int zoom) const;
@ -235,6 +260,7 @@ public:
private:
QList<PathRender> _paths;
QList<CircleRender> _circles;
QList<TextRender> _pathLabels, _pointLabels, _areaLabels;
QList<Symbol> _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<QList<TextRender> *> &lists);
void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,