1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 05:34:47 +01:00

Improved text layout/rendering on IMG maps

This commit is contained in:
Martin Tůma 2019-06-07 20:33:08 +02:00
parent f2d32b30d3
commit 06e1685f85
11 changed files with 144 additions and 85 deletions

View File

@ -179,7 +179,8 @@ HEADERS += src/common/config.h \
src/map/IMG/style.h \ src/map/IMG/style.h \
src/map/IMG/netfile.h \ src/map/IMG/netfile.h \
src/GUI/limitedcombobox.h \ src/GUI/limitedcombobox.h \
src/GUI/pathtickitem.h src/GUI/pathtickitem.h \
src/map/IMG/textitem.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/common/coordinates.cpp \ src/common/coordinates.cpp \
src/common/rectc.cpp \ src/common/rectc.cpp \
@ -309,7 +310,8 @@ SOURCES += src/main.cpp \
src/map/IMG/vectortile.cpp \ src/map/IMG/vectortile.cpp \
src/map/IMG/style.cpp \ src/map/IMG/style.cpp \
src/map/IMG/netfile.cpp \ src/map/IMG/netfile.cpp \
src/GUI/pathtickitem.cpp src/GUI/pathtickitem.cpp \
src/map/IMG/textitem.cpp
greaterThan(QT_MAJOR_VERSION, 4) { greaterThan(QT_MAJOR_VERSION, 4) {
HEADERS += src/data/geojsonparser.h HEADERS += src/data/geojsonparser.h

View File

@ -75,10 +75,11 @@ void Style::defaultPolygonStyle()
<< TYPE(0x42) << TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46) << TYPE(0x42) << TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46)
<< TYPE(0x47) << TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d) << TYPE(0x47) << TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d)
<< TYPE(0x4e) << TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52) << TYPE(0x4e) << TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52)
<< TYPE(0x14) << TYPE(0x15) << TYPE(0x1e) << TYPE(0x1f) << TYPE(0x04) << TYPE(0x14) << TYPE(0x15) << TYPE(0x16) << TYPE(0x1e) << TYPE(0x1f)
<< TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08) << TYPE(0x09) << TYPE(0x04) << TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08)
<< TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d) << TYPE(0x0e) << TYPE(0x09) << TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d)
<< TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12) << TYPE(0x13); << TYPE(0x0e) << TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12)
<< TYPE(0x13);
} }
void Style::defaultLineStyle() void Style::defaultLineStyle()
@ -962,6 +963,11 @@ bool Style::isSpot(quint32 type)
return (type == TYPE(0x62) || type == TYPE(0x63)); return (type == TYPE(0x62) || type == TYPE(0x63));
} }
bool Style::isSummit(quint32 type)
{
return (type == 0x6616);
}
Style::POIClass Style::poiClass(quint32 type) Style::POIClass Style::poiClass(quint32 type)
{ {
if ((type >= 0x2a00 && type < 0x2b00) || type == 0x2c0a || type == 0x2d02) if ((type >= 0x2a00 && type < 0x2b00) || type == 0x2c0a || type == 0x2d02)

View File

@ -105,6 +105,7 @@ public:
static bool isContourLine(quint32 type); static bool isContourLine(quint32 type);
static bool isSpot(quint32 type); static bool isSpot(quint32 type);
static bool isSummit(quint32 type);
static POIClass poiClass(quint32 type); static POIClass poiClass(quint32 type);
private: private:

17
src/map/IMG/textitem.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "textitem.h"
bool TextItem::collides(const QList<TextItem*> &list) const
{
QRectF r1(boundingRect());
for (int i = 0; i < list.size(); i++) {
const TextItem* other = list.at(i);
QRectF r2(other->boundingRect());
if (!(r1.isEmpty() || r2.isEmpty() || !r1.intersects(r2)))
if (other->shape().intersects(shape()))
return true;
}
return false;
}

22
src/map/IMG/textitem.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef TEXTITEM_H
#define TEXTITEM_H
#include <QList>
#include <QRectF>
#include <QPainterPath>
class QPainter;
class TextItem
{
public:
virtual ~TextItem() {}
virtual QPainterPath shape() const = 0;
virtual QRectF boundingRect() const = 0;
virtual void paint(QPainter *painter) const = 0;
bool collides(const QList<TextItem*> &list) const;
};
#endif // TEXTITEM_H

View File

@ -154,23 +154,6 @@ TextPathItem::TextPathItem(const QPolygonF &line, const QString *label,
_rect = _shape.boundingRect(); _rect = _shape.boundingRect();
} }
bool TextPathItem::collides(const QVector<TextPathItem> &list) const
{
if (_rect.isEmpty())
return false;
for (int i = 0; i < list.size(); i++) {
const TextPathItem &other = list.at(i);
if (other._rect.isEmpty() || !_rect.intersects(other._rect))
continue;
if (other._shape.intersects(_shape))
return true;
}
return false;
}
void TextPathItem::paint(QPainter *painter) const void TextPathItem::paint(QPainter *painter) const
{ {
QFontMetrics fm(*_font); QFontMetrics fm(*_font);

View File

@ -3,9 +3,9 @@
#include <QVector> #include <QVector>
#include <QPainterPath> #include <QPainterPath>
#include "img.h" #include "textitem.h"
class TextPathItem class TextPathItem : public TextItem
{ {
public: public:
TextPathItem() : _text(0), _font(0), _color(0) {} TextPathItem() : _text(0), _font(0), _color(0) {}
@ -13,7 +13,9 @@ public:
const QRect &tileRect, const QFont *font, const QColor *color); const QRect &tileRect, const QFont *font, const QColor *color);
bool isValid() const {return !_path.isEmpty();} bool isValid() const {return !_path.isEmpty();}
bool collides(const QVector<TextPathItem> &list) const;
QPainterPath shape() const {return _shape;}
QRectF boundingRect() const {return _rect;}
void paint(QPainter *painter) const; void paint(QPainter *painter) const;
private: private:

View File

@ -28,15 +28,7 @@ TextPointItem::TextPointItem(const QPoint &point, const QString *text,
_textRect.moveCenter(point); _textRect.moveCenter(point);
_rect = _textRect | iconRect; _rect = _textRect | iconRect;
} _shape.addRect(_rect);
bool TextPointItem::collides(const QVector<TextPointItem> &list) const
{
for (int i = 0; i < list.size(); i++)
if (list.at(i)._rect.intersects(_rect))
return true;
return false;
} }
void TextPointItem::paint(QPainter *painter) const void TextPointItem::paint(QPainter *painter) const

View File

@ -4,20 +4,24 @@
#include <QRect> #include <QRect>
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "textitem.h"
class QPainter; class QPainter;
class QFont; class QFont;
class QImage; class QImage;
class QColor; class QColor;
class TextPointItem class TextPointItem : public TextItem
{ {
public: public:
TextPointItem() : _text(0), _font(0), _img(0) {} TextPointItem() : _text(0), _font(0), _img(0) {}
TextPointItem(const QPoint &point, const QString *text, const QFont *font, TextPointItem(const QPoint &point, const QString *text, const QFont *font,
const QImage *img, const QColor *color); const QImage *img, const QColor *color);
bool collides(const QVector<TextPointItem> &list) const; bool isValid() const {return !_rect.isEmpty();}
QRectF boundingRect() const {return _rect;}
QPainterPath shape() const {return _shape;}
void paint(QPainter *painter) const; void paint(QPainter *painter) const;
private: private:
@ -26,6 +30,7 @@ private:
const QImage *_img; const QImage *_img;
const QColor *_color; const QColor *_color;
QRect _rect, _textRect; QRect _rect, _textRect;
QPainterPath _shape;
}; };
#endif // TEXTPOINTITEM_H #endif // TEXTPOINTITEM_H

View File

@ -24,7 +24,7 @@
#define SMALL_FONT_SIZE 10 #define SMALL_FONT_SIZE 10
#define POI_FONT_SIZE 9 #define POI_FONT_SIZE 9
#define LINE_TEXT_MIN_ZOOM 23 #define LINE_TEXT_MIN_ZOOM 22
class RasterTile class RasterTile
{ {
@ -43,6 +43,12 @@ public:
void load() void load()
{ {
QList<TextItem*> textItems;
_map->processPolygons(_polygons);
_map->processPoints(_points, textItems);
_map->processLines(_lines, _xy, textItems);
_img.fill(Qt::transparent); _img.fill(Qt::transparent);
QPainter painter(&_img); QPainter painter(&_img);
@ -51,8 +57,10 @@ public:
painter.translate(-_xy.x(), -_xy.y()); painter.translate(-_xy.x(), -_xy.y());
_map->drawPolygons(&painter, _polygons); _map->drawPolygons(&painter, _polygons);
_map->drawLines(&painter, _lines, _xy); _map->drawLines(&painter, _lines);
_map->drawPoints(&painter, _points); _map->drawTextItems(&painter, textItems);
qDeleteAll(textItems);
} }
private: private:
@ -189,20 +197,15 @@ Coordinates IMGMap::xy2ll(const QPointF &p)
} }
void IMGMap::drawPolygons(QPainter *painter, QList<IMG::Poly> &polygons) void IMGMap::drawPolygons(QPainter *painter, const QList<IMG::Poly> &polygons)
{ {
for (int n = 0; n < _img.style().drawOrder().size(); n++) { for (int n = 0; n < _img.style().drawOrder().size(); n++) {
for (int i = 0; i < polygons.size(); i++) { for (int i = 0; i < polygons.size(); i++) {
IMG::Poly &poly = polygons[i]; const IMG::Poly &poly = polygons.at(i);
if (poly.type != _img.style().drawOrder().at(n)) if (poly.type != _img.style().drawOrder().at(n))
continue; continue;
const Style::Polygon &style = _img.style().polygon(poly.type); const Style::Polygon &style = _img.style().polygon(poly.type);
for (int j = 0; j < poly.points.size(); j++) {
QPointF &p = poly.points[j];
p = ll2xy(Coordinates(p.x(), p.y()));
}
painter->setPen(style.pen()); painter->setPen(style.pen());
painter->setBrush(style.brush()); painter->setBrush(style.brush());
painter->drawPolygon(poly.points); painter->drawPolygon(poly.points);
@ -210,19 +213,8 @@ void IMGMap::drawPolygons(QPainter *painter, QList<IMG::Poly> &polygons)
} }
} }
void IMGMap::drawLines(QPainter *painter, QList<IMG::Poly> &lines, void IMGMap::drawLines(QPainter *painter, const QList<IMG::Poly> &lines)
const QPoint &tile)
{ {
qStableSort(lines);
for (int i = 0; i < lines.size(); i++) {
IMG::Poly &poly = lines[i];
for (int j = 0; j < poly.points.size(); j++) {
QPointF &p = poly.points[j];
p = ll2xy(Coordinates(p.x(), p.y()));
}
}
painter->setBrush(Qt::NoBrush); painter->setBrush(Qt::NoBrush);
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
@ -247,12 +239,42 @@ void IMGMap::drawLines(QPainter *painter, QList<IMG::Poly> &lines,
painter->drawPolyline(poly.points); painter->drawPolyline(poly.points);
} }
} }
}
void IMGMap::drawTextItems(QPainter *painter, const QList<TextItem*> &textItems)
{
for (int i = 0; i < textItems.size(); i++)
textItems.at(i)->paint(painter);
}
void IMGMap::processPolygons(QList<IMG::Poly> &polygons)
{
for (int i = 0; i < polygons.size(); i++) {
IMG::Poly &poly = polygons[i];
for (int j = 0; j < poly.points.size(); j++) {
QPointF &p = poly.points[j];
p = ll2xy(Coordinates(p.x(), p.y()));
}
}
}
void IMGMap::processLines(QList<IMG::Poly> &lines, const QPoint &tile,
QList<TextItem*> &textItems)
{
qStableSort(lines);
for (int i = 0; i < lines.size(); i++) {
IMG::Poly &poly = lines[i];
for (int j = 0; j < poly.points.size(); j++) {
QPointF &p = poly.points[j];
p = ll2xy(Coordinates(p.x(), p.y()));
}
}
if (_zoom < LINE_TEXT_MIN_ZOOM) if (_zoom < LINE_TEXT_MIN_ZOOM)
return; return;
QVector<TextPathItem> items;
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
IMG::Poly &poly = lines[i]; IMG::Poly &poly = lines[i];
const Style::Line &style = _img.style().line(poly.type); const Style::Line &style = _img.style().line(poly.type);
@ -279,28 +301,26 @@ void IMGMap::drawLines(QPainter *painter, QList<IMG::Poly> &lines,
const QColor *color = style.textColor().isValid() const QColor *color = style.textColor().isValid()
? &style.textColor() : 0; ? &style.textColor() : 0;
TextPathItem item(poly.points, &poly.label, QRect(tile, TextPathItem *item = new TextPathItem(poly.points, &poly.label,
QSize(TILE_SIZE, TILE_SIZE)), font, color); QRect(tile, QSize(TILE_SIZE, TILE_SIZE)), font, color);
if (item.isValid() && !item.collides(items)) if (item->isValid() && !item->collides(textItems))
items.append(item); textItems.append(item);
else
delete item;
} }
for (int i = 0; i < items.size(); i++)
items.at(i).paint(painter);
} }
void IMGMap::drawPoints(QPainter *painter, QList<IMG::Point> &points) void IMGMap::processPoints(QList<IMG::Point> &points,
QList<TextItem*> &textItems)
{ {
qSort(points); qSort(points);
QVector<TextPointItem> items;
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
IMG::Point &point = points[i]; IMG::Point &point = points[i];
const Style::Point &style = _img.style().point(point.type);
int mz = minPOIZoom(Style::poiClass(point.type));
if (point.poi && _zoom < mz) const Style::Point &style = _img.style().point(point.type);
if (point.poi && _zoom < minPOIZoom(Style::poiClass(point.type)))
continue; continue;
const QString *label = point.label.isEmpty() ? 0 : &(point.label); const QString *label = point.label.isEmpty() ? 0 : &(point.label);
@ -334,15 +354,19 @@ void IMGMap::drawPoints(QPainter *painter, QList<IMG::Point> &points)
if (Style::isSpot(point.type)) if (Style::isSpot(point.type))
convertUnits(point.label); convertUnits(point.label);
if (Style::isSummit(point.type) && !point.label.isEmpty()) {
QStringList list = point.label.split(" ");
convertUnits(list.last());
point.label = list.join(" ");
}
TextPointItem item(ll2xy(point.coordinates).toPoint(), label, font, img, TextPointItem *item = new TextPointItem(
color); ll2xy(point.coordinates).toPoint(), label, font, img, color);
if (!item.collides(items)) if (item->isValid() && !item->collides(textItems))
items.append(item); textItems.append(item);
else
delete item;
} }
for (int i = 0; i < items.size(); i++)
items.at(i).paint(painter);
} }
static void render(RasterTile &tile) static void render(RasterTile &tile)

View File

@ -8,6 +8,7 @@
#include "common/range.h" #include "common/range.h"
#include "IMG/img.h" #include "IMG/img.h"
class TextItem;
class IMGMap : public Map class IMGMap : public Map
{ {
@ -41,10 +42,14 @@ private:
Transform transform(int zoom) const; Transform transform(int zoom) const;
void updateTransform(); void updateTransform();
void drawPolygons(QPainter *painter, QList<IMG::Poly> &polygons); void drawPolygons(QPainter *painter, const QList<IMG::Poly> &polygons);
void drawLines(QPainter *painter, QList<IMG::Poly> &lines, void drawLines(QPainter *painter, const QList<IMG::Poly> &lines);
const QPoint &tile); void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
void drawPoints(QPainter *painter, QList<IMG::Point> &points);
void processPolygons(QList<IMG::Poly> &polygons);
void processLines(QList<IMG::Poly> &lines, const QPoint &tile,
QList<TextItem*> &textItems);
void processPoints(QList<IMG::Point> &points, QList<TextItem*> &textItems);
QString _fileName; QString _fileName;
IMG _img; IMG _img;