1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-27 21:24:47 +01:00

Added support for label shields

This commit is contained in:
Martin Tůma 2019-06-30 20:39:22 +02:00
parent 561fadb4f2
commit 537b1c3716
10 changed files with 298 additions and 85 deletions

View File

@ -180,7 +180,8 @@ HEADERS += src/common/config.h \
src/map/IMG/netfile.h \
src/GUI/limitedcombobox.h \
src/GUI/pathtickitem.h \
src/map/IMG/textitem.h
src/map/IMG/textitem.h \
src/map/IMG/label.h
SOURCES += src/main.cpp \
src/common/coordinates.cpp \
src/common/rectc.cpp \

View File

@ -10,6 +10,7 @@
#include "common/rectc.h"
#include "common/range.h"
#include "style.h"
#include "label.h"
class VectorTile;
class SubFile;
@ -23,7 +24,7 @@ public:
ll2xy() the points in the IMG class as this can not be done in
parallel. */
QVector<QPointF> points;
QString label;
Label label;
quint32 type;
bool operator<(const Poly &other) const
@ -34,7 +35,7 @@ public:
Point() : id(0) {}
Coordinates coordinates;
QString label;
Label label;
quint32 type;
bool poi;
quint64 id;

74
src/map/IMG/label.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef LABEL_H
#define LABEL_H
#include <QString>
#include <QDebug>
class Label {
public:
class Shield
{
public:
enum Type {
None,
USInterstate,
USShield,
USRound,
Hbox,
Box,
Oval
};
Shield() : _type(None) {}
Shield(Type type, const QString &name) : _type(type), _text(name) {}
Type type() const {return _type;}
const QString &text() const {return _text;}
bool isValid() const {return _type > None && !_text.isEmpty();}
bool operator==(const Shield &other) const
{return _type == other._type && _text == other._text;}
private:
Type _type;
QString _text;
};
Label() {}
Label(const QString &text, const Shield &shield = Shield())
: _text(text), _shield(shield) {}
const Shield &shield() const {return _shield;}
const QString &text() const {return _text;}
bool isValid() const {return _shield.isValid() || !_text.isEmpty();}
void setText(const QString &text) {_text = text;}
private:
QString _text;
Shield _shield;
};
inline uint qHash(const Label::Shield &shield)
{
return qHash(shield.text()) ^ shield.type();
}
#ifndef QT_NO_DEBUG
inline QDebug operator<<(QDebug dbg, const Label::Shield &shield)
{
dbg.nospace() << "Shield(" << shield.type() << ", " << shield.text() << ")";
return dbg.space();
}
inline QDebug operator<<(QDebug dbg, const Label &label)
{
dbg.nospace() << "Label(";
if (label.shield().isValid())
dbg << label.shield() << ", ";
dbg << label.text() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG
#endif // LABEL_H

View File

@ -7,7 +7,7 @@ static quint8 NORMAL_CHARS[] = {
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '~', '~', '~', '~', '~',
'X', 'Y', 'Z', '~', '~', '~', ' ', ' ',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '~', '~', '~', '~', '~', '~'
};
@ -48,6 +48,7 @@ static QString capitalize(const QString &str)
return ret;
}
bool LBLFile::init()
{
Handle hdl;
@ -73,45 +74,48 @@ bool LBLFile::init()
return true;
}
QString LBLFile::label6b(Handle &hdl, quint32 offset) const
Label LBLFile::label6b(Handle &hdl, quint32 offset) const
{
QByteArray result;
Label::Shield::Type shieldType = Label::Shield::None;
QByteArray label, shieldLabel;
QByteArray *bap = &label;
Charset curCharSet = Normal;
quint8 b1, b2, b3;
if (!seek(hdl, offset))
return QString();
return Label();
while (true) {
if (!(readByte(hdl, b1) && readByte(hdl, b2) && readByte(hdl, b3)))
return QString();
return Label();
int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F};
for (int cpt = 0; cpt < 4; cpt++) {
if (c[cpt] > 0x2F)
return QString::fromLatin1(result);
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d))
return Label(capitalize(QString::fromLatin1(label)),
Label::Shield(shieldType, shieldLabel));
switch (curCharSet) {
case Normal:
if (c[cpt] == 0x1c)
curCharSet = Symbol;
else if (c[cpt] == 0x1b)
curCharSet = Special;
else if(c[cpt] == 0x1d)
result.append('|');
else if (c[cpt] == 0x1f)
result.append(' ');
else if (c[cpt] == 0x1e)
result.append(' ');
else if (c[cpt] >= 0x2a && c[cpt] <= 0x2f) {
shieldType = (Label::Shield::Type)(c[cpt] - 0x29);
bap = &shieldLabel;
} else if (bap == &shieldLabel
&& NORMAL_CHARS[c[cpt]] == ' ')
bap = &label;
else
result.append(NORMAL_CHARS[c[cpt]]);
bap->append(NORMAL_CHARS[c[cpt]]);
break;
case Symbol:
result.append(SYMBOL_CHARS[c[cpt]]);
bap->append(SYMBOL_CHARS[c[cpt]]);
curCharSet = Normal;
break;
case Special:
result.append(SPECIAL_CHARS[c[cpt]]);
bap->append(SPECIAL_CHARS[c[cpt]]);
curCharSet = Normal;
break;
}
@ -119,30 +123,39 @@ QString LBLFile::label6b(Handle &hdl, quint32 offset) const
}
}
QString LBLFile::label8b(Handle &hdl, quint32 offset) const
Label LBLFile::label8b(Handle &hdl, quint32 offset) const
{
QByteArray result;
Label::Shield::Type shieldType = Label::Shield::None;
QByteArray label, shieldLabel;
QByteArray *bap = &label;
quint8 c;
if (!seek(hdl, offset))
return QString();
return Label();
while (true) {
if (!readByte(hdl, c))
return QString();
if (!c)
return Label();
if (!c || c == 0x1d)
break;
if (c >= 0x1B && c <= 0x1F)
result.append(' ');
else if (c > 0x07)
result.append(c);
if ((c >= 0x1e && c <= 0x1f))
bap->append(' ');
else if (c <= 0x07) {
shieldType = (Label::Shield::Type)c;
bap = &shieldLabel;
} else if (bap == &shieldLabel && QChar(c).isSpace()) {
bap = &label;
} else
bap->append(c);
}
return _codec ? _codec->toUnicode(result) : QString::fromLatin1(result);
return Label(capitalize(_codec ? _codec->toUnicode(label)
: QString::fromLatin1(label)), Label::Shield(shieldType, _codec
? _codec->toUnicode(shieldLabel) : QString::fromLatin1(shieldLabel)));
}
QString LBLFile::label(Handle &hdl, quint32 offset, bool poi)
Label LBLFile::label(Handle &hdl, quint32 offset, bool poi)
{
if (!_init) {
if (!(_init = init()))
@ -163,11 +176,11 @@ QString LBLFile::label(Handle &hdl, quint32 offset, bool poi)
switch (_encoding) {
case 6:
return capitalize(label6b(hdl, labelOffset));
return label6b(hdl, labelOffset);
case 9:
case 10:
return capitalize(label8b(hdl, labelOffset));
return label8b(hdl, labelOffset);
default:
return QString();
return Label();
}
}

View File

@ -2,6 +2,7 @@
#define LBLFILE_H
#include "subfile.h"
#include "label.h"
class QTextCodec;
@ -10,13 +11,13 @@ class LBLFile : public SubFile
public:
LBLFile(IMG *img, quint32 size) : SubFile(img, size), _init(false) {}
QString label(Handle &hdl, quint32 offset, bool poi = false);
Label label(Handle &hdl, quint32 offset, bool poi = false);
private:
bool init();
QString label6b(Handle &hdl, quint32 offset) const;
QString label8b(Handle &hdl, quint32 offset) const;
Label label6b(Handle &hdl, quint32 offset) const;
Label label8b(Handle &hdl, quint32 offset) const;
quint32 _offset;
quint32 _size;

View File

@ -977,7 +977,12 @@ bool Style::isSpot(quint32 type)
bool Style::isSummit(quint32 type)
{
return (type == 0x6616);
return (type == 0x6616);
}
bool Style::isMajorRoad(quint32 type)
{
return (type <= TYPE(0x04));
}
Style::POIClass Style::poiClass(quint32 type)

View File

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

View File

@ -7,29 +7,48 @@
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
#define MAX_TEXT_WIDTH 8
#define MIN_BOX_WIDTH 2
static void expand(QRect &rect, int width)
{
rect.adjust(-(width/2 - rect.width()/2), 0, width/2 - rect.width()/2, 0);
}
TextPointItem::TextPointItem(const QPoint &point, const QString *text,
const QFont *font, const QImage *img, const QColor *color)
: _text(text), _font(font), _img(img), _color(color)
const QFont *font, const QImage *img, const QColor *color,
const QColor *bgColor) : _text(text), _font(font), _img(img), _color(color),
_bgColor(bgColor)
{
QRect iconRect;
if (text) {
QFontMetrics fm(*font);
int limit = font->pixelSize() * MAX_TEXT_WIDTH;
_textRect = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, *text);
_textRect.adjust(0, 0, 1, 1);
}
if (img) {
iconRect = QRect(QPoint(point.x() - img->width()/2, point.y()
- img->height()/2), img->size());
_textRect.moveTopLeft(QPoint(point.x() + img->width(), point.y()
if (_bgColor && _textRect.width() < font->pixelSize() * MIN_BOX_WIDTH)
expand(_textRect, font->pixelSize() * MIN_BOX_WIDTH);
setPos(point);
}
void TextPointItem::setPos(const QPoint &point)
{
QPainterPath shape;
QRect iconRect;
if (_img) {
iconRect = QRect(QPoint(point.x() - _img->width()/2, point.y()
- _img->height()/2), _img->size());
_textRect.moveTopLeft(QPoint(point.x() + _img->width(), point.y()
- _textRect.height()/2));
} else
_textRect.moveCenter(point);
_rect = _textRect | iconRect;
_shape.addRect(_rect);
shape.addRect(_rect);
_shape = shape;
}
void TextPointItem::paint(QPainter *painter) const
@ -54,6 +73,13 @@ void TextPointItem::paint(QPainter *painter) const
painter->drawImage(_textRect.x() - 1, _textRect.y(), img);
painter->drawImage(_textRect.x() + 1, _textRect.y(), img);
if (_bgColor) {
painter->setPen(*_color);
painter->setBrush(*_bgColor);
painter->drawRect(_textRect);
painter->setBrush(Qt::NoBrush);
}
if (_color) {
painter->setFont(*_font);
painter->setPen(*_color);

View File

@ -16,7 +16,7 @@ class TextPointItem : public TextItem
public:
TextPointItem() : _text(0), _font(0), _img(0) {}
TextPointItem(const QPoint &point, const QString *text, const QFont *font,
const QImage *img, const QColor *color);
const QImage *img, const QColor *color, const QColor *bgColor = 0);
bool isValid() const {return !_rect.isEmpty();}
@ -24,11 +24,13 @@ public:
QPainterPath shape() const {return _shape;}
void paint(QPainter *painter) const;
void setPos(const QPoint &point);
private:
const QString *_text;
const QFont *_font;
const QImage *_img;
const QColor *_color;
const QColor *_color, *_bgColor;
QRect _rect, _textRect;
QPainterPath _shape;
};

View File

@ -73,12 +73,18 @@ private:
QList<IMG::Point> _points;
};
static void convertUnits(QString &str)
static QColor shieldColor(Qt::white);
static QColor shieldBgColor1("#dd3e3e");
static QColor shieldBgColor2("#379947");
static QColor shieldBgColor3("#4a7fc1");
static QString convertUnits(const QString &str)
{
bool ok;
int number = str.toInt(&ok);
if (ok)
str = QString::number(qRound(number * 0.3048));
return ok ? QString::number(qRound(number * 0.3048)) : str;
}
static int minPOIZoom(Style::POIClass cl)
@ -123,6 +129,40 @@ FONT(normalFont, NORMAL_FONT_SIZE)
FONT(smallFont, SMALL_FONT_SIZE)
FONT(poiFont, POI_FONT_SIZE)
static const QColor *shieldBgColor(Label::Shield::Type type)
{
switch (type) {
case Label::Shield::USInterstate:
case Label::Shield::Hbox:
return &shieldBgColor1;
case Label::Shield::USShield:
case Label::Shield::Box:
return &shieldBgColor2;
case Label::Shield::USRound:
case Label::Shield::Oval:
return &shieldBgColor3;
default:
return 0;
}
}
static int minShieldZoom(Label::Shield::Type type)
{
switch (type) {
case Label::Shield::USInterstate:
case Label::Shield::Hbox:
return 18;
case Label::Shield::USShield:
case Label::Shield::Box:
return 19;
case Label::Shield::USRound:
case Label::Shield::Oval:
return 20;
default:
return INT_MAX;
}
}
IMGMap::IMGMap(const QString &fileName, QObject *parent)
: Map(parent), _fileName(fileName), _img(fileName),
@ -278,6 +318,8 @@ void IMGMap::processPolygons(QList<IMG::Poly> &polygons)
void IMGMap::processLines(QList<IMG::Poly> &lines, const QPoint &tile,
QList<TextItem*> &textItems)
{
QRect tileRect(tile, QSize(TILE_SIZE, TILE_SIZE));
qStableSort(lines);
for (int i = 0; i < lines.size(); i++) {
@ -288,41 +330,87 @@ void IMGMap::processLines(QList<IMG::Poly> &lines, const QPoint &tile,
}
}
if (_zoom < LINE_TEXT_MIN_ZOOM)
return;
if (_zoom >= LINE_TEXT_MIN_ZOOM) {
for (int i = 0; i < lines.size(); i++) {
IMG::Poly &poly = lines[i];
const Style::Line &style = _img.style().line(poly.type);
for (int i = 0; i < lines.size(); i++) {
IMG::Poly &poly = lines[i];
const Style::Line &style = _img.style().line(poly.type);
if (style.img().isNull() && style.foreground() == Qt::NoPen)
continue;
if (poly.label.text().isEmpty()
|| style.textFontSize() == Style::None)
continue;
if (style.img().isNull() && style.foreground() == Qt::NoPen)
continue;
if (poly.label.isEmpty() || style.textFontSize() == Style::None)
continue;
if (Style::isContourLine(poly.type))
poly.label.setText(convertUnits(poly.label.text()));
if (Style::isContourLine(poly.type))
convertUnits(poly.label);
const QFont *font;
switch (style.textFontSize()) {
case Style::Large:
font = largeFont();
break;
case Style::Small:
font = smallFont();
break;
default:
font = normalFont();
}
const QColor *color = style.textColor().isValid()
? &style.textColor() : 0;
const QFont *font;
switch (style.textFontSize()) {
case Style::Large:
font = largeFont();
break;
case Style::Small:
font = smallFont();
break;
default:
font = normalFont();
TextPathItem *item = new TextPathItem(poly.points,
&poly.label.text(), tileRect, font, color);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
const QColor *color = style.textColor().isValid()
? &style.textColor() : 0;
}
TextPathItem *item = new TextPathItem(poly.points, &poly.label,
QRect(tile, QSize(TILE_SIZE, TILE_SIZE)), font, color);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
for (int type = 1; type < 7; type++) {
if (minShieldZoom((Label::Shield::Type)type) > _zoom)
continue;
QSet<Label::Shield> shields;
for (int i = 0; i < lines.size(); i++) {
const IMG::Poly &poly = lines.at(i);
const Label::Shield &shield = poly.label.shield();
if (shield.type() != type || !Style::isMajorRoad(poly.type))
continue;
if (poly.label.shield().isValid() && !shields.contains(shield)) {
bool valid = false;
int idx = poly.points.size()/2, inc = 0, sign = 1;
TextPointItem *item = new TextPointItem(
poly.points.at(idx).toPoint(), &shield.text(), normalFont(),
0, &shieldColor, shieldBgColor(shield.type()));
while (1) {
if (!item->collides(textItems)
&& tileRect.contains(item->boundingRect().toRect())) {
valid = true;
break;
}
inc++;
sign = (sign < 0) ? 1 : -1;
idx += inc * sign;
if (!(idx >= 0 && idx < poly.points.size()))
break;
item->setPos(poly.points.at(idx).toPoint());
}
if (valid) {
textItems.append(item);
shields.insert(shield);
} else
delete item;
}
}
}
}
@ -339,7 +427,8 @@ void IMGMap::processPoints(QList<IMG::Point> &points,
if (point.poi && _zoom < minPOIZoom(Style::poiClass(point.type)))
continue;
const QString *label = point.label.isEmpty() ? 0 : &(point.label);
const QString *label = point.label.text().isEmpty()
? 0 : &(point.label.text());
const QImage *img = style.img().isNull() ? 0 : &style.img();
const QFont *font = 0;
if (point.poi) {
@ -369,10 +458,10 @@ void IMGMap::processPoints(QList<IMG::Point> &points,
continue;
if (Style::isSpot(point.type))
convertUnits(point.label);
if (Style::isSummit(point.type) && !point.label.isEmpty()) {
QStringList list = point.label.split(" ");
convertUnits(list.last());
point.label.setText(convertUnits(point.label.text()));
if (Style::isSummit(point.type) && !point.label.text().isEmpty()) {
QStringList list = point.label.text().split(" ");
list.last() = convertUnits(list.last());
point.label = list.join(" ");
}