mirror of
https://github.com/tumic0/QtPBFImagePlugin.git
synced 2024-11-24 03:35:54 +01:00
Added support for symbol icons
This commit is contained in:
parent
c48ea8c878
commit
2803f48a21
@ -19,7 +19,8 @@ HEADERS += src/pbfhandler.h \
|
|||||||
src/textpathitem.h \
|
src/textpathitem.h \
|
||||||
src/textpointitem.h \
|
src/textpointitem.h \
|
||||||
src/font.h \
|
src/font.h \
|
||||||
src/textitem.h
|
src/textitem.h \
|
||||||
|
src/sprites.h
|
||||||
SOURCES += src/pbfplugin.cpp \
|
SOURCES += src/pbfplugin.cpp \
|
||||||
src/pbfhandler.cpp \
|
src/pbfhandler.cpp \
|
||||||
src/gzip.cpp \
|
src/gzip.cpp \
|
||||||
@ -30,7 +31,8 @@ SOURCES += src/pbfplugin.cpp \
|
|||||||
src/function.cpp \
|
src/function.cpp \
|
||||||
src/textpathitem.cpp \
|
src/textpathitem.cpp \
|
||||||
src/textpointitem.cpp \
|
src/textpointitem.cpp \
|
||||||
src/font.cpp
|
src/font.cpp \
|
||||||
|
src/sprites.cpp
|
||||||
RESOURCES += pbfplugin.qrc
|
RESOURCES += pbfplugin.qrc
|
||||||
|
|
||||||
unix:!macx{
|
unix:!macx{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>style/style.json</file>
|
<file>style/style.json</file>
|
||||||
|
<file>style/sprite.json</file>
|
||||||
|
<file>style/sprite.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
11
src/pbf.cpp
11
src/pbf.cpp
@ -116,7 +116,7 @@ static inline QPoint parameters(quint32 v1, quint32 v2)
|
|||||||
static void drawFeature(const Feature &feature, Style *style, int styleLayer,
|
static void drawFeature(const Feature &feature, Style *style, int styleLayer,
|
||||||
const QSizeF &factor, Tile &tile)
|
const QSizeF &factor, Tile &tile)
|
||||||
{
|
{
|
||||||
if (!style->match(styleLayer, feature.tags()))
|
if (!style->match(tile.zoom(), styleLayer, feature.tags()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QPoint cursor;
|
QPoint cursor;
|
||||||
@ -151,7 +151,7 @@ static void drawFeature(const Feature &feature, Style *style, int styleLayer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
style->drawFeature(styleLayer, path, feature.tags(), tile);
|
style->drawFeature(tile, styleLayer, path, feature.tags());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawLayer(const Layer &layer, Style *style, int styleLayer,
|
static void drawLayer(const Layer &layer, Style *style, int styleLayer,
|
||||||
@ -163,7 +163,9 @@ static void drawLayer(const Layer &layer, Style *style, int styleLayer,
|
|||||||
QSizeF factor(tile.size().width() / scale.x() / (qreal)layer.data()->extent(),
|
QSizeF factor(tile.size().width() / scale.x() / (qreal)layer.data()->extent(),
|
||||||
tile.size().height() / scale.y() / (qreal)layer.data()->extent());
|
tile.size().height() / scale.y() / (qreal)layer.data()->extent());
|
||||||
|
|
||||||
style->setPainter(styleLayer, tile);
|
style->setPainter(tile, styleLayer);
|
||||||
|
style->setTextProperties(tile, styleLayer);
|
||||||
|
|
||||||
for (int i = 0; i < layer.features().size(); i++)
|
for (int i = 0; i < layer.features().size(); i++)
|
||||||
drawFeature(layer.features().at(i), style, styleLayer, factor, tile);
|
drawFeature(layer.features().at(i), style, styleLayer, factor, tile);
|
||||||
}
|
}
|
||||||
@ -177,9 +179,8 @@ bool PBF::render(const QByteArray &data, int zoom, Style *style,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tile t(image, scale);
|
Tile t(image, zoom, scale);
|
||||||
|
|
||||||
style->setZoom(zoom);
|
|
||||||
style->drawBackground(t);
|
style->drawBackground(t);
|
||||||
|
|
||||||
// Prepare source layers
|
// Prepare source layers
|
||||||
|
91
src/sprites.cpp
Normal file
91
src/sprites.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "sprites.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Loading the sprites atlas image must be deferred until all image plugins
|
||||||
|
are loaded, otherwise reading the image will cause a deadlock!
|
||||||
|
*/
|
||||||
|
static QImage &atlas(const QString &fileName)
|
||||||
|
{
|
||||||
|
static QImage *img = new QImage(fileName);
|
||||||
|
return *img;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sprites::Sprite::Sprite(const QJsonObject &json)
|
||||||
|
{
|
||||||
|
int x, y, width, height;
|
||||||
|
|
||||||
|
if (json.contains("x") && json["x"].isDouble())
|
||||||
|
x = json["x"].toInt();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
if (json.contains("y") && json["y"].isDouble())
|
||||||
|
y = json["y"].toInt();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
if (json.contains("width") && json["width"].isDouble())
|
||||||
|
width = json["width"].toInt();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
if (json.contains("height") && json["height"].isDouble())
|
||||||
|
height = json["height"].toInt();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
_rect = QRect(x, y, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sprites::load(const QString &jsonFile, const QString &imageFile)
|
||||||
|
{
|
||||||
|
_imageFile = imageFile;
|
||||||
|
|
||||||
|
QFile file(jsonFile);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qCritical() << jsonFile << ": error opening file";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray ba(file.readAll());
|
||||||
|
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument doc(QJsonDocument::fromJson(ba, &error));
|
||||||
|
if (doc.isNull()) {
|
||||||
|
qCritical() << jsonFile << ":" << error.errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject json(doc.object());
|
||||||
|
for (QJsonObject::const_iterator it = json.constBegin();
|
||||||
|
it != json.constEnd(); it++) {
|
||||||
|
QJsonValue val(*it);
|
||||||
|
if (val.isObject())
|
||||||
|
_sprites.insert(it.key(), Sprite(val.toObject()));
|
||||||
|
else
|
||||||
|
qWarning() << it.key() << ": invalid sprite definition";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage Sprites::icon(const QString &name) const
|
||||||
|
{
|
||||||
|
if (_imageFile.isNull())
|
||||||
|
return QImage();
|
||||||
|
const QImage &img = atlas(_imageFile);
|
||||||
|
if (img.isNull())
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
QMap<QString, Sprite>::const_iterator it = _sprites.find(name);
|
||||||
|
if (it == _sprites.constEnd())
|
||||||
|
return QImage();
|
||||||
|
|
||||||
|
if (!img.rect().contains(it->rect())) {
|
||||||
|
qWarning() << it->rect() << ": invalid sprite rect";
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
return img.copy(it->rect());
|
||||||
|
}
|
29
src/sprites.h
Normal file
29
src/sprites.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef SPRITES_H
|
||||||
|
#define SPRITES_H
|
||||||
|
|
||||||
|
#include <QRect>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
|
class Sprites
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool load(const QString &jsonFile, const QString &imageFile);
|
||||||
|
|
||||||
|
QImage icon(const QString &name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Sprite {
|
||||||
|
public:
|
||||||
|
Sprite(const QJsonObject &json);
|
||||||
|
const QRect &rect() const {return _rect;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QRect _rect;
|
||||||
|
};
|
||||||
|
|
||||||
|
QMap<QString, Sprite> _sprites;
|
||||||
|
QString _imageFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPRITES_H
|
162
src/style.cpp
162
src/style.cpp
@ -3,6 +3,8 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
#include "color.h"
|
#include "color.h"
|
||||||
@ -168,6 +170,32 @@ bool Style::Layer::Filter::match(const QVariantHash &tags) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Style::Layer::Template::Template(const QString &str) : _field(str)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
while ((pos = _rx.indexIn(_field, pos)) != -1) {
|
||||||
|
QString match = _rx.capturedTexts().first();
|
||||||
|
_keys.append(match.mid(1, match.size() - 2));
|
||||||
|
pos += _rx.matchedLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Style::Layer::Template::value(const QVariantHash &tags) const
|
||||||
|
{
|
||||||
|
QString text(_field);
|
||||||
|
|
||||||
|
for (int i = 0; i < _keys.size(); i++) {
|
||||||
|
const QString &key = _keys.at(i);
|
||||||
|
const QVariant val = tags.value(key);
|
||||||
|
text.replace(QString("{%1}").arg(key), val.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegExp Style::Layer::Template::_rx = QRegExp("\\{[^\\}]*\\}");
|
||||||
|
|
||||||
Style::Layer::Paint::Paint(const QJsonObject &json)
|
Style::Layer::Paint::Paint(const QJsonObject &json)
|
||||||
: _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0)
|
: _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0)
|
||||||
{
|
{
|
||||||
@ -275,7 +303,7 @@ bool Style::Layer::Paint::antialias(Layer::Type type, int zoom) const
|
|||||||
Style::Layer::Layout::Layout(const QJsonObject &json)
|
Style::Layer::Layout::Layout(const QJsonObject &json)
|
||||||
: _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _lineCap(Qt::FlatCap),
|
: _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _lineCap(Qt::FlatCap),
|
||||||
_lineJoin(Qt::MiterJoin), _font("Open Sans"), _capitalize(false),
|
_lineJoin(Qt::MiterJoin), _font("Open Sans"), _capitalize(false),
|
||||||
_viewportAlignment(false)
|
_viewportAlignment(false), _textAnchor(Text::Center)
|
||||||
{
|
{
|
||||||
// line
|
// line
|
||||||
if (json.contains("line-cap") && json["line-cap"].isString()) {
|
if (json.contains("line-cap") && json["line-cap"].isString()) {
|
||||||
@ -292,17 +320,8 @@ Style::Layer::Layout::Layout(const QJsonObject &json)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// text
|
// text
|
||||||
if (!(json.contains("text-field") && json["text-field"].isString()))
|
if (json.contains("text-field") && json["text-field"].isString())
|
||||||
return;
|
_text = Template(json["text-field"].toString());
|
||||||
_textField = json["text-field"].toString();
|
|
||||||
|
|
||||||
QRegExp rx("\\{[^\\}]*\\}");
|
|
||||||
int pos = 0;
|
|
||||||
while ((pos = rx.indexIn(_textField, pos)) != -1) {
|
|
||||||
QString match = rx.capturedTexts().first();
|
|
||||||
_keys.append(match.mid(1, match.size() - 2));
|
|
||||||
pos += rx.matchedLength();
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonFloat(json, "text-size", _textSize);
|
jsonFloat(json, "text-size", _textSize);
|
||||||
jsonFloat(json, "text-max-width", _textMaxWidth);
|
jsonFloat(json, "text-max-width", _textMaxWidth);
|
||||||
@ -311,11 +330,27 @@ Style::Layer::Layout::Layout(const QJsonObject &json)
|
|||||||
_font = Font::fromJsonArray(json["text-font"].toArray());
|
_font = Font::fromJsonArray(json["text-font"].toArray());
|
||||||
if (json.contains("text-transform") && json["text-transform"].isString())
|
if (json.contains("text-transform") && json["text-transform"].isString())
|
||||||
_capitalize = json["text-transform"].toString() == "uppercase";
|
_capitalize = json["text-transform"].toString() == "uppercase";
|
||||||
|
|
||||||
if (json.contains("text-rotation-alignment")
|
if (json.contains("text-rotation-alignment")
|
||||||
&& json["text-rotation-alignment"].isString())
|
&& json["text-rotation-alignment"].isString())
|
||||||
if (json["text-rotation-alignment"].toString() == "viewport")
|
if (json["text-rotation-alignment"].toString() == "viewport")
|
||||||
_viewportAlignment = true;
|
_viewportAlignment = true;
|
||||||
|
if (json.contains("text-anchor") && json["text-anchor"].isString()) {
|
||||||
|
QString anchor(json["text-anchor"].toString());
|
||||||
|
if (anchor == "center")
|
||||||
|
_textAnchor = Text::Center;
|
||||||
|
else if (anchor == "left")
|
||||||
|
_textAnchor = Text::Left;
|
||||||
|
else if (anchor == "right")
|
||||||
|
_textAnchor = Text::Right;
|
||||||
|
else if (anchor == "top")
|
||||||
|
_textAnchor = Text::Top;
|
||||||
|
else if (anchor == "bottom")
|
||||||
|
_textAnchor = Text::Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// icon
|
||||||
|
if (json.contains("icon-image") && json["icon-image"].isString())
|
||||||
|
_icon = Template(json["icon-image"].toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
QFont Style::Layer::Layout::font(int zoom) const
|
QFont Style::Layer::Layout::font(int zoom) const
|
||||||
@ -374,58 +409,61 @@ bool Style::Layer::match(int zoom, const QVariantHash &tags) const
|
|||||||
return _filter.match(tags);
|
return _filter.match(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Style::Layer::setPathPainter(int zoom, Tile &tile) const
|
void Style::Layer::setPathPainter(Tile &tile) const
|
||||||
{
|
{
|
||||||
QPen pen(_paint.pen(_type, zoom));
|
QPen pen(_paint.pen(_type, tile.zoom()));
|
||||||
QBrush brush(_paint.brush(_type, zoom));
|
QBrush brush(_paint.brush(_type, tile.zoom()));
|
||||||
|
|
||||||
pen.setJoinStyle(_layout.lineJoin());
|
pen.setJoinStyle(_layout.lineJoin());
|
||||||
pen.setCapStyle(_layout.lineCap());
|
pen.setCapStyle(_layout.lineCap());
|
||||||
|
|
||||||
QPainter &p = tile.painter();
|
QPainter &p = tile.painter();
|
||||||
p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, zoom));
|
p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, tile.zoom()));
|
||||||
p.setPen(pen);
|
p.setPen(pen);
|
||||||
p.setBrush(brush);
|
p.setBrush(brush);
|
||||||
p.setOpacity(_paint.opacity(_type, zoom));
|
p.setOpacity(_paint.opacity(_type, tile.zoom()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Style::Layer::setSymbolPainter(int zoom, Tile &tile) const
|
void Style::Layer::setSymbolPainter(Tile &tile) const
|
||||||
{
|
{
|
||||||
QPen pen(_paint.pen(_type, zoom));
|
QPen pen(_paint.pen(_type, tile.zoom()));
|
||||||
QFont font(_layout.font(zoom));
|
QFont font(_layout.font(tile.zoom()));
|
||||||
|
|
||||||
QPainter &p = tile.painter();
|
QPainter &p = tile.painter();
|
||||||
p.setPen(pen);
|
p.setPen(pen);
|
||||||
p.setFont(font);
|
p.setFont(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Style::Layer::addSymbol(int zoom, const QPainterPath &path,
|
void Style::Layer::setTextProperties(Tile &tile) const
|
||||||
const QVariantHash &tags, Tile &tile) const
|
|
||||||
{
|
{
|
||||||
if (_layout.keys().isEmpty())
|
Text::Properties prop;
|
||||||
return;
|
prop.maxWidth = _layout.maxTextWidth(tile.zoom());
|
||||||
|
prop.maxAngle = _layout.maxTextAngle(tile.zoom());
|
||||||
|
prop.anchor = _layout.textAnchor();
|
||||||
|
|
||||||
QString text(_layout.field());
|
tile.text().setProperties(prop);
|
||||||
for (int i = 0; i < _layout.keys().size(); i++) {
|
|
||||||
const QString &key = _layout.keys().at(i);
|
|
||||||
const QVariant val = tags.value(key);
|
|
||||||
text.replace(QString("{%1}").arg(key), _layout.capitalize()
|
|
||||||
? val.toString().toUpper() : val.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path,
|
||||||
|
const QVariantHash &tags, const Sprites &sprites) const
|
||||||
|
{
|
||||||
|
QString text = _layout.text().value(tags);
|
||||||
QString tt(text.trimmed());
|
QString tt(text.trimmed());
|
||||||
if (tt.isEmpty())
|
if (tt.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
if (_layout.capitalize())
|
||||||
|
tt = tt.toUpper();
|
||||||
|
|
||||||
|
QString icon = _layout.icon().value(tags);
|
||||||
|
|
||||||
if (_layout.viewportAlignment())
|
if (_layout.viewportAlignment())
|
||||||
tile.text().addLabel(tt, path.elementAt(0), tile.painter(),
|
tile.text().addLabel(tt, path.elementAt(0), tile.painter(), false,
|
||||||
_layout.maxTextWidth(zoom), false);
|
sprites.icon(icon));
|
||||||
else if (path.elementCount() == 1 && path.elementAt(0).isMoveTo())
|
else if (path.elementCount() == 1 && path.elementAt(0).isMoveTo())
|
||||||
tile.text().addLabel(tt, path.elementAt(0), tile.painter(),
|
tile.text().addLabel(tt, path.elementAt(0), tile.painter(), true,
|
||||||
_layout.maxTextWidth(zoom), true);
|
sprites.icon(icon));
|
||||||
else
|
else
|
||||||
tile.text().addLabel(tt, path, tile.painter(),
|
tile.text().addLabel(tt, path, tile.painter());
|
||||||
_layout.maxTextAngle(zoom));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Style::load(const QString &fileName)
|
bool Style::load(const QString &fileName)
|
||||||
@ -450,53 +488,69 @@ bool Style::load(const QString &fileName)
|
|||||||
QJsonArray layers = json["layers"].toArray();
|
QJsonArray layers = json["layers"].toArray();
|
||||||
for (int i = 0; i < layers.size(); i++)
|
for (int i = 0; i < layers.size(); i++)
|
||||||
if (layers[i].isObject())
|
if (layers[i].isObject())
|
||||||
_styles.append(Layer(layers[i].toObject()));
|
_layers.append(Layer(layers[i].toObject()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < _styles.size(); i++)
|
for (int i = 0; i < _layers.size(); i++)
|
||||||
_sourceLayers.append(_styles.at(i).sourceLayer());
|
_sourceLayers.append(_layers.at(i).sourceLayer());
|
||||||
|
|
||||||
|
QDir styleDir = QFileInfo(fileName).absoluteDir();
|
||||||
|
QString spritesJSON(styleDir.filePath("sprite.json"));
|
||||||
|
if (QFileInfo::exists(spritesJSON)) {
|
||||||
|
QString spritesImg(styleDir.filePath("sprite.png"));
|
||||||
|
if (QFileInfo::exists(spritesImg))
|
||||||
|
_sprites.load(spritesJSON, spritesImg);
|
||||||
|
else
|
||||||
|
qCritical() << spritesImg << ": no such file";
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Style::match(int layer, const QVariantHash &tags)
|
bool Style::match(int zoom, int layer, const QVariantHash &tags) const
|
||||||
{
|
{
|
||||||
return _styles.at(layer).match(_zoom, tags);
|
return _layers.at(layer).match(zoom, tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Style::setPainter(int layer, Tile &tile)
|
void Style::setTextProperties(Tile &tile, int layer) const
|
||||||
{
|
{
|
||||||
const Layer &sl = _styles.at(layer);
|
const Layer &sl = _layers.at(layer);
|
||||||
|
sl.setTextProperties(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Style::setPainter(Tile &tile, int layer) const
|
||||||
|
{
|
||||||
|
const Layer &sl = _layers.at(layer);
|
||||||
|
|
||||||
if (sl.isPath())
|
if (sl.isPath())
|
||||||
sl.setPathPainter(_zoom, tile);
|
sl.setPathPainter(tile);
|
||||||
else if (sl.isSymbol())
|
else if (sl.isSymbol())
|
||||||
sl.setSymbolPainter(_zoom, tile);
|
sl.setSymbolPainter(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Style::drawFeature(int layer, const QPainterPath &path,
|
void Style::drawFeature(Tile &tile, int layer, const QPainterPath &path,
|
||||||
const QVariantHash &tags, Tile &tile)
|
const QVariantHash &tags) const
|
||||||
{
|
{
|
||||||
const Layer &sl = _styles.at(layer);
|
const Layer &sl = _layers.at(layer);
|
||||||
|
|
||||||
if (sl.isPath())
|
if (sl.isPath())
|
||||||
tile.painter().drawPath(path);
|
tile.painter().drawPath(path);
|
||||||
else if (sl.isSymbol())
|
else if (sl.isSymbol())
|
||||||
sl.addSymbol(_zoom, path, tags, tile);
|
sl.addSymbol(tile, path, tags, _sprites);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Style::drawBackground(Tile &tile)
|
void Style::drawBackground(Tile &tile) const
|
||||||
{
|
{
|
||||||
QRectF rect(QPointF(0, 0), tile.size());
|
QRectF rect(QPointF(0, 0), tile.size());
|
||||||
QPainterPath path;
|
QPainterPath path;
|
||||||
path.addRect(rect);
|
path.addRect(rect);
|
||||||
|
|
||||||
if (_styles.isEmpty()) {
|
if (_layers.isEmpty()) {
|
||||||
tile.painter().setBrush(Qt::lightGray);
|
tile.painter().setBrush(Qt::lightGray);
|
||||||
tile.painter().setPen(Qt::NoPen);
|
tile.painter().setPen(Qt::NoPen);
|
||||||
tile.painter().drawRect(rect);
|
tile.painter().drawRect(rect);
|
||||||
} else if (_styles.first().isBackground()) {
|
} else if (_layers.first().isBackground()) {
|
||||||
_styles.first().setPathPainter(_zoom, tile);
|
_layers.first().setPathPainter(tile);
|
||||||
tile.painter().drawPath(path);
|
tile.painter().drawPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
src/style.h
56
src/style.h
@ -9,7 +9,9 @@
|
|||||||
#include <QPen>
|
#include <QPen>
|
||||||
#include <QBrush>
|
#include <QBrush>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
|
#include "text.h"
|
||||||
#include "function.h"
|
#include "function.h"
|
||||||
|
#include "sprites.h"
|
||||||
|
|
||||||
|
|
||||||
class QPainter;
|
class QPainter;
|
||||||
@ -23,15 +25,15 @@ public:
|
|||||||
|
|
||||||
bool load(const QString &fileName);
|
bool load(const QString &fileName);
|
||||||
|
|
||||||
void setZoom(int zoom) {_zoom = zoom;}
|
|
||||||
|
|
||||||
const QStringList &sourceLayers() const {return _sourceLayers;}
|
const QStringList &sourceLayers() const {return _sourceLayers;}
|
||||||
bool match(int layer, const QVariantHash &tags);
|
|
||||||
|
|
||||||
void drawBackground(Tile &tile);
|
bool match(int zoom, int layer, const QVariantHash &tags) const;
|
||||||
void setPainter(int layer, Tile &tile);
|
|
||||||
void drawFeature(int layer, const QPainterPath &path,
|
void drawBackground(Tile &tile) const;
|
||||||
const QVariantHash &tags, Tile &tile);
|
void setPainter(Tile &tile, int layer) const;
|
||||||
|
void setTextProperties(Tile &tile, int layer) const;
|
||||||
|
void drawFeature(Tile &tile, int layer, const QPainterPath &path,
|
||||||
|
const QVariantHash &tags) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Layer {
|
class Layer {
|
||||||
@ -45,10 +47,11 @@ private:
|
|||||||
bool isSymbol() const {return (_type == Symbol);}
|
bool isSymbol() const {return (_type == Symbol);}
|
||||||
|
|
||||||
bool match(int zoom, const QVariantHash &tags) const;
|
bool match(int zoom, const QVariantHash &tags) const;
|
||||||
void setPathPainter(int zoom, Tile &tile) const;
|
void setPathPainter(Tile &tile) const;
|
||||||
void setSymbolPainter(int zoom, Tile &tile) const;
|
void setSymbolPainter(Tile &tile) const;
|
||||||
void addSymbol(int zoom, const QPainterPath &path,
|
void setTextProperties(Tile &tile) const;
|
||||||
const QVariantHash &tags, Tile &tile) const;
|
void addSymbol(Tile &tile, const QPainterPath &path,
|
||||||
|
const QVariantHash &tags, const Sprites &sprites) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Type {
|
enum Type {
|
||||||
@ -80,12 +83,25 @@ private:
|
|||||||
QVector<Filter> _filters;
|
QVector<Filter> _filters;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Template {
|
||||||
|
public:
|
||||||
|
Template() {}
|
||||||
|
Template(const QString &str);
|
||||||
|
|
||||||
|
QString value(const QVariantHash &tags) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QRegExp _rx;
|
||||||
|
QStringList _keys;
|
||||||
|
QString _field;
|
||||||
|
};
|
||||||
|
|
||||||
class Layout {
|
class Layout {
|
||||||
public:
|
public:
|
||||||
Layout() : _textSize(16), _textMaxWidth(10), _textMaxAngle(45),
|
Layout() : _textSize(16), _textMaxWidth(10), _textMaxAngle(45),
|
||||||
_lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin),
|
_lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin),
|
||||||
_font("Open Sans"), _capitalize(false), _viewportAlignment(false)
|
_font("Open Sans"), _capitalize(false), _viewportAlignment(false),
|
||||||
{}
|
_textAnchor(Text::Center) {}
|
||||||
Layout(const QJsonObject &json);
|
Layout(const QJsonObject &json);
|
||||||
|
|
||||||
bool capitalize() const {return _capitalize;}
|
bool capitalize() const {return _capitalize;}
|
||||||
@ -93,16 +109,17 @@ private:
|
|||||||
{return _textMaxWidth.value(zoom);}
|
{return _textMaxWidth.value(zoom);}
|
||||||
qreal maxTextAngle(int zoom) const
|
qreal maxTextAngle(int zoom) const
|
||||||
{return _textMaxAngle.value(zoom);}
|
{return _textMaxAngle.value(zoom);}
|
||||||
const QString &field() const {return _textField;}
|
const Template &text() const {return _text;}
|
||||||
const QStringList &keys() const {return _keys;}
|
const Template &icon() const {return _icon;}
|
||||||
QFont font(int zoom) const;
|
QFont font(int zoom) const;
|
||||||
Qt::PenCapStyle lineCap() const {return _lineCap;}
|
Qt::PenCapStyle lineCap() const {return _lineCap;}
|
||||||
Qt::PenJoinStyle lineJoin() const {return _lineJoin;}
|
Qt::PenJoinStyle lineJoin() const {return _lineJoin;}
|
||||||
bool viewportAlignment() const {return _viewportAlignment;}
|
bool viewportAlignment() const {return _viewportAlignment;}
|
||||||
|
Text::Anchor textAnchor() const {return _textAnchor;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList _keys;
|
Template _text;
|
||||||
QString _textField;
|
Template _icon;
|
||||||
FunctionF _textSize;
|
FunctionF _textSize;
|
||||||
FunctionF _textMaxWidth;
|
FunctionF _textMaxWidth;
|
||||||
FunctionF _textMaxAngle;
|
FunctionF _textMaxAngle;
|
||||||
@ -111,6 +128,7 @@ private:
|
|||||||
QFont _font;
|
QFont _font;
|
||||||
bool _capitalize;
|
bool _capitalize;
|
||||||
bool _viewportAlignment;
|
bool _viewportAlignment;
|
||||||
|
Text::Anchor _textAnchor;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Paint {
|
class Paint {
|
||||||
@ -144,9 +162,9 @@ private:
|
|||||||
Paint _paint;
|
Paint _paint;
|
||||||
};
|
};
|
||||||
|
|
||||||
int _zoom;
|
QVector<Layer> _layers;
|
||||||
QVector<Layer> _styles;
|
|
||||||
QStringList _sourceLayers;
|
QStringList _sourceLayers;
|
||||||
|
Sprites _sprites;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // STYLE_H
|
#endif // STYLE_H
|
||||||
|
13
src/text.cpp
13
src/text.cpp
@ -150,28 +150,27 @@ void Text::render(QPainter *painter) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Text::addLabel(const QString &text, const QPointF &pos,
|
void Text::addLabel(const QString &text, const QPointF &pos,
|
||||||
const QPainter &painter, qreal maxTextWidth, bool overlap)
|
const QPainter &painter, bool overlap, const QImage &icon)
|
||||||
{
|
{
|
||||||
if (text.isEmpty())
|
if (text.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TextPointItem *ti;
|
TextPointItem *ti = new TextPointItem(text, pos, painter.font(),
|
||||||
|
_properties, icon);
|
||||||
ti = new TextPointItem(text, pos, painter.font(), maxTextWidth);
|
|
||||||
if (!overlap && !_sceneRect.contains(ti->boundingRect())) {
|
if (!overlap && !_sceneRect.contains(ti->boundingRect())) {
|
||||||
delete ti;
|
delete ti;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ti->setPen(painter.pen());
|
ti->setPen(painter.pen());
|
||||||
addItem(ti);
|
addItem(ti);
|
||||||
|
|
||||||
QList<TextItem*> ci = collidingItems(ti);
|
QList<TextItem*> ci = collidingItems(ti);
|
||||||
for (int i = 0; i < ci.size(); i++)
|
for (int i = 0; i < ci.size(); i++)
|
||||||
ci[i]->setVisible(false);
|
ci[i]->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::addLabel(const QString &text, const QPainterPath &path,
|
void Text::addLabel(const QString &text, const QPainterPath &path,
|
||||||
const QPainter &painter, qreal maxAngle)
|
const QPainter &painter)
|
||||||
{
|
{
|
||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
@ -182,7 +181,7 @@ void Text::addLabel(const QString &text, const QPainterPath &path,
|
|||||||
if (textWidth > path.length())
|
if (textWidth > path.length())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QPainterPath tp(textPath(path, textWidth, maxAngle,
|
QPainterPath tp(textPath(path, textWidth, _properties.maxAngle,
|
||||||
painter.font().pixelSize() / 2, _sceneRect));
|
painter.font().pixelSize() / 2, _sceneRect));
|
||||||
if (tp.isEmpty())
|
if (tp.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
29
src/text.h
29
src/text.h
@ -1,20 +1,40 @@
|
|||||||
#ifndef TEXT_H
|
#ifndef TEXT_H
|
||||||
#define TEXT_H
|
#define TEXT_H
|
||||||
|
|
||||||
#include "textitem.h"
|
#include <QImage>
|
||||||
|
|
||||||
|
class TextItem;
|
||||||
|
|
||||||
class Text
|
class Text
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum Anchor {
|
||||||
|
Center,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
Bottom
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Properties {
|
||||||
|
int maxWidth;
|
||||||
|
int maxAngle;
|
||||||
|
Anchor anchor;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Text(const QSize &size) : _sceneRect(QRectF(QPointF(0, 0), size)) {}
|
Text(const QSize &size) : _sceneRect(QRectF(QPointF(0, 0), size)) {}
|
||||||
~Text();
|
~Text();
|
||||||
|
|
||||||
void render(QPainter *painter) const;
|
void setProperties(const Properties &prop)
|
||||||
|
{_properties = prop;}
|
||||||
|
|
||||||
void addLabel(const QString &text, const QPointF &pos,
|
void addLabel(const QString &text, const QPointF &pos,
|
||||||
const QPainter &painter, qreal maxTextWidth, bool overlap);
|
const QPainter &painter, bool overlap, const QImage &icon);
|
||||||
void addLabel(const QString &text, const QPainterPath &path,
|
void addLabel(const QString &text, const QPainterPath &path,
|
||||||
const QPainter &painter, qreal maxAngle);
|
const QPainter &painter);
|
||||||
|
|
||||||
|
void render(QPainter *painter) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addItem(TextItem *item) {_items.append(item);}
|
void addItem(TextItem *item) {_items.append(item);}
|
||||||
@ -22,6 +42,7 @@ private:
|
|||||||
|
|
||||||
QRectF _sceneRect;
|
QRectF _sceneRect;
|
||||||
QList<TextItem *> _items;
|
QList<TextItem *> _items;
|
||||||
|
Properties _properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TEXT_H
|
#endif // TEXT_H
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
#include "textpointitem.h"
|
#include "textpointitem.h"
|
||||||
|
|
||||||
|
|
||||||
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
|
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
|
||||||
|
|
||||||
#ifdef USE_EXACT_TEXT_RECT
|
static QRectF exactBoundingRect(const QString &str, const QFont &font,
|
||||||
|
|
||||||
static QRectF textBoundingRect(const QString &str, const QFont &font,
|
|
||||||
int maxTextWidth)
|
int maxTextWidth)
|
||||||
{
|
{
|
||||||
QFontMetrics fm(font);
|
QFontMetrics fm(font);
|
||||||
@ -26,8 +24,7 @@ static QRectF textBoundingRect(const QString &str, const QFont &font,
|
|||||||
return br;
|
return br;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // USE_EXACT_TEXT_RECT
|
static QRectF fuzzyBoundingRect(const QString &str, const QFont &font,
|
||||||
static QRectF textBoundingRect(const QString &str, const QFont &font,
|
|
||||||
int maxTextWidth)
|
int maxTextWidth)
|
||||||
{
|
{
|
||||||
int limit = font.pixelSize() * maxTextWidth;
|
int limit = font.pixelSize() * maxTextWidth;
|
||||||
@ -67,23 +64,68 @@ static QRectF textBoundingRect(const QString &str, const QFont &font,
|
|||||||
|
|
||||||
return QRectF(0, 0, width, lines * lh);
|
return QRectF(0, 0, width, lines * lh);
|
||||||
}
|
}
|
||||||
#endif // USE_EXACT_TEXT_RECT
|
|
||||||
|
|
||||||
|
QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const
|
||||||
|
{
|
||||||
|
QRectF iconRect = _icon.isNull() ? QRectF() : _icon.rect();
|
||||||
|
QRectF textRect = brf(text(), _font, _properties.maxWidth);
|
||||||
|
|
||||||
|
switch (_properties.anchor) {
|
||||||
|
case Text::Center:
|
||||||
|
textRect.moveCenter(_pos);
|
||||||
|
break;
|
||||||
|
case Text::Left:
|
||||||
|
textRect.moveTopLeft(_pos - QPointF(-iconRect.width() / 2,
|
||||||
|
textRect.height() / 2));
|
||||||
|
break;
|
||||||
|
case Text::Right:
|
||||||
|
textRect.moveTopRight(_pos - QPointF(iconRect.width() / 2,
|
||||||
|
textRect.height() / 2));
|
||||||
|
break;
|
||||||
|
case Text::Bottom:
|
||||||
|
textRect.moveTopLeft(_pos - QPointF(textRect.width() / 2,
|
||||||
|
iconRect.height() / 2));
|
||||||
|
break;
|
||||||
|
case Text::Top:
|
||||||
|
textRect.moveTopLeft(_pos - QPointF(textRect.width() / 2,
|
||||||
|
-iconRect.height() / 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return textRect;
|
||||||
|
}
|
||||||
|
|
||||||
TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
|
TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
|
||||||
const QFont &font, int maxTextWidth) : TextItem(text), _font(font)
|
const QFont &font, const Text::Properties &prop, const QImage &icon)
|
||||||
|
: TextItem(text), _pos(pos), _font(font), _icon(icon), _properties(prop)
|
||||||
{
|
{
|
||||||
_boundingRect = textBoundingRect(text, font, maxTextWidth);
|
_boundingRect = computeTextRect(fuzzyBoundingRect);
|
||||||
|
|
||||||
|
if (!_icon.isNull()) {
|
||||||
|
QRectF iconRect(_icon.rect());
|
||||||
|
iconRect.moveCenter(pos);
|
||||||
|
_boundingRect |= iconRect;
|
||||||
|
}
|
||||||
|
|
||||||
_boundingRect.moveCenter(pos);
|
|
||||||
_shape.addRect(_boundingRect);
|
_shape.addRect(_boundingRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextPointItem::paint(QPainter *painter) const
|
void TextPointItem::paint(QPainter *painter) const
|
||||||
{
|
{
|
||||||
painter->setFont(_font);
|
|
||||||
painter->setPen(_pen);
|
|
||||||
painter->drawText(_boundingRect, FLAGS, text());
|
|
||||||
|
|
||||||
//painter->setPen(Qt::red);
|
//painter->setPen(Qt::red);
|
||||||
//painter->drawRect(_boundingRect);
|
//painter->drawRect(_boundingRect);
|
||||||
|
|
||||||
|
QRectF textRect;
|
||||||
|
|
||||||
|
if (!_icon.isNull()) {
|
||||||
|
textRect = computeTextRect(exactBoundingRect);
|
||||||
|
painter->drawImage(_pos - QPointF(_icon.width() / 2,
|
||||||
|
_icon.height() / 2), _icon);
|
||||||
|
} else
|
||||||
|
textRect = computeTextRect(fuzzyBoundingRect);
|
||||||
|
|
||||||
|
painter->setFont(_font);
|
||||||
|
painter->setPen(_pen);
|
||||||
|
painter->drawText(textRect, FLAGS, text());
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@
|
|||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include "textitem.h"
|
#include "textitem.h"
|
||||||
|
#include "text.h"
|
||||||
|
|
||||||
class TextPointItem : public TextItem
|
class TextPointItem : public TextItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextPointItem(const QString &text, const QPointF &pos, const QFont &font,
|
TextPointItem(const QString &text, const QPointF &pos, const QFont &font,
|
||||||
int maxTextWidth);
|
const Text::Properties &prop, const QImage &icon);
|
||||||
|
|
||||||
QRectF boundingRect() const {return _boundingRect;}
|
QRectF boundingRect() const {return _boundingRect;}
|
||||||
QPainterPath shape() const {return _shape;}
|
QPainterPath shape() const {return _shape;}
|
||||||
@ -19,10 +20,16 @@ public:
|
|||||||
void setPen(const QPen &pen) {_pen = pen;}
|
void setPen(const QPen &pen) {_pen = pen;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
typedef QRectF (*BoundingRectFunction)(const QString &, const QFont &, int);
|
||||||
|
QRectF computeTextRect(BoundingRectFunction brf) const;
|
||||||
|
|
||||||
|
QPointF _pos;
|
||||||
QPainterPath _shape;
|
QPainterPath _shape;
|
||||||
QRectF _boundingRect;
|
QRectF _boundingRect;
|
||||||
QFont _font;
|
QFont _font;
|
||||||
QPen _pen;
|
QPen _pen;
|
||||||
|
QImage _icon;
|
||||||
|
Text::Properties _properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TEXTPOINTITEM_H
|
#endif // TEXTPOINTITEM_H
|
||||||
|
@ -7,16 +7,18 @@
|
|||||||
|
|
||||||
class Tile {
|
class Tile {
|
||||||
public:
|
public:
|
||||||
Tile(QImage *img, const QPointF &scale)
|
Tile(QImage *img, int zoom, const QPointF &scale)
|
||||||
: _size(img->size()), _text(QSize(img->size().width() / scale.x(),
|
: _zoom(zoom), _size(img->size()), _text(QSize(img->size().width()
|
||||||
img->size().height() / scale.y())), _painter(img)
|
/ scale.x(), img->size().height() / scale.y())), _painter(img)
|
||||||
{_painter.scale(scale.x(), scale.y());}
|
{_painter.scale(scale.x(), scale.y());}
|
||||||
|
|
||||||
|
int zoom() const {return _zoom;}
|
||||||
QSize size() const {return _size;}
|
QSize size() const {return _size;}
|
||||||
Text &text() {return _text;}
|
Text &text() {return _text;}
|
||||||
QPainter &painter() {return _painter;}
|
QPainter &painter() {return _painter;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int _zoom;
|
||||||
QSize _size;
|
QSize _size;
|
||||||
Text _text;
|
Text _text;
|
||||||
QPainter _painter;
|
QPainter _painter;
|
||||||
|
1682
style/sprite.json
Normal file
1682
style/sprite.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
style/sprite.png
Normal file
BIN
style/sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
Loading…
Reference in New Issue
Block a user