2018-10-29 00:11:23 +01:00
|
|
|
#include <QPainter>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
|
|
|
#include <QJsonArray>
|
2018-11-22 21:57:21 +01:00
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QDir>
|
2018-10-29 00:11:23 +01:00
|
|
|
#include <QDebug>
|
|
|
|
#include "text.h"
|
|
|
|
#include "color.h"
|
2018-11-06 22:35:30 +01:00
|
|
|
#include "font.h"
|
2018-10-29 00:11:23 +01:00
|
|
|
#include "tile.h"
|
|
|
|
#include "style.h"
|
|
|
|
|
|
|
|
|
2018-11-13 00:07:51 +01:00
|
|
|
static void jsonColor(const QJsonObject &json, const char *name, FunctionC &var)
|
|
|
|
{
|
|
|
|
if (json.contains(name)) {
|
|
|
|
const QJsonValue value = json[name];
|
|
|
|
if (value.isString())
|
|
|
|
var = FunctionC(Color::fromJsonString(value.toString()));
|
|
|
|
else if (value.isObject())
|
|
|
|
var = FunctionC(value.toObject());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void jsonFloat(const QJsonObject &json, const char *name, FunctionF &var)
|
|
|
|
{
|
|
|
|
if (json.contains(name)) {
|
|
|
|
const QJsonValue value = json[name];
|
|
|
|
if (value.isDouble())
|
|
|
|
var = FunctionF(value.toDouble());
|
|
|
|
else if (value.isObject())
|
|
|
|
var = FunctionF(value.toObject());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void jsonBool(const QJsonObject &json, const char *name, FunctionB &var)
|
|
|
|
{
|
|
|
|
if (json.contains(name)) {
|
|
|
|
QJsonValue value = json[name];
|
|
|
|
if (value.isBool())
|
|
|
|
var = FunctionB(value.toBool());
|
|
|
|
else if (value.isObject())
|
|
|
|
var = FunctionB(value.toObject());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 23:35:28 +01:00
|
|
|
static void jsonString(const QJsonObject &json, const char *name, FunctionS &var)
|
|
|
|
{
|
|
|
|
if (json.contains(name)) {
|
|
|
|
QJsonValue value = json[name];
|
|
|
|
if (value.isString())
|
|
|
|
var = FunctionS(value.toString());
|
|
|
|
else if (value.isObject())
|
|
|
|
var = FunctionS(value.toObject());
|
|
|
|
}
|
|
|
|
}
|
2018-11-13 00:07:51 +01:00
|
|
|
|
2018-10-29 00:11:23 +01:00
|
|
|
Style::Layer::Filter::Filter(const QJsonArray &json)
|
|
|
|
: _type(Unknown), _not(false)
|
|
|
|
{
|
2018-10-30 22:08:33 +01:00
|
|
|
#define INVALID_FILTER(json) \
|
|
|
|
{qWarning() << json << ": invalid filter"; return;}
|
|
|
|
|
2018-10-29 00:11:23 +01:00
|
|
|
if (json.isEmpty())
|
2018-10-30 22:08:33 +01:00
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
|
|
|
|
QString type = json.at(0).toString();
|
|
|
|
|
|
|
|
if (type == "==") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() != 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = EQ;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(),
|
|
|
|
json.at(2).toVariant());
|
|
|
|
} else if (type == "!=") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() != 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = NE;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(),
|
|
|
|
json.at(2).toVariant());
|
|
|
|
} else if (type == "<") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() != 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = LT;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(),
|
|
|
|
json.at(2).toVariant());
|
|
|
|
} else if (type == "<=") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() != 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = LE;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(),
|
|
|
|
json.at(2).toVariant());
|
|
|
|
} else if (type == ">") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() != 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = GT;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(),
|
|
|
|
json.at(2).toVariant());
|
|
|
|
} else if (type == ">=") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() != 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = GE;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(),
|
|
|
|
json.at(2).toVariant());
|
|
|
|
} else if (type == "all") {
|
|
|
|
_type = All;
|
|
|
|
for (int i = 1; i < json.size(); i++)
|
|
|
|
_filters.append(Filter(json.at(i).toArray()));
|
|
|
|
} else if (type == "any") {
|
|
|
|
_type = Any;
|
|
|
|
for (int i = 1; i < json.size(); i++)
|
|
|
|
_filters.append(Filter(json.at(i).toArray()));
|
|
|
|
} else if (type == "in") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() < 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = In;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(), QVariant());
|
|
|
|
for (int i = 2; i < json.size(); i++)
|
|
|
|
_set.insert(json.at(i).toString());
|
|
|
|
} else if (type == "!in") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() < 3)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = In;
|
|
|
|
_not = true;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(), QVariant());
|
|
|
|
for (int i = 2; i < json.size(); i++)
|
|
|
|
_set.insert(json.at(i).toString());
|
|
|
|
} else if (type == "has") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() < 2)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = Has;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(), QVariant());
|
|
|
|
} else if (type == "!has") {
|
2018-10-30 22:08:33 +01:00
|
|
|
if (json.size() < 2)
|
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
_type = Has;
|
|
|
|
_not = true;
|
|
|
|
_kv = QPair<QString, QVariant>(json.at(1).toString(), QVariant());
|
|
|
|
} else
|
2018-10-30 22:08:33 +01:00
|
|
|
INVALID_FILTER(json);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-10 19:34:45 +01:00
|
|
|
bool Style::Layer::Filter::match(const QVariantHash &tags) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
|
|
|
switch (_type) {
|
|
|
|
case None:
|
|
|
|
return true;
|
|
|
|
case EQ:
|
|
|
|
return tags.value(_kv.first) == _kv.second;
|
|
|
|
case NE:
|
|
|
|
return tags.value(_kv.first) != _kv.second;
|
|
|
|
case GT:
|
|
|
|
return tags.value(_kv.first) > _kv.second;
|
|
|
|
case GE:
|
|
|
|
return tags.value(_kv.first) >= _kv.second;
|
|
|
|
case LT:
|
|
|
|
return tags.value(_kv.first) < _kv.second;
|
|
|
|
case LE:
|
|
|
|
return tags.value(_kv.first) <= _kv.second;
|
|
|
|
case In:
|
|
|
|
return _set.contains(tags.value(_kv.first).toString()) ^ _not;
|
|
|
|
case Has:
|
|
|
|
return tags.contains(_kv.first) ^ _not;
|
|
|
|
case All:
|
|
|
|
for (int i = 0; i < _filters.size(); i++) {
|
|
|
|
if (!_filters.at(i).match(tags))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case Any:
|
|
|
|
for (int i = 0; i < _filters.size(); i++) {
|
|
|
|
if (_filters.at(i).match(tags))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-23 23:35:28 +01:00
|
|
|
QString Style::Layer::Template::value(int zoom, const QVariantHash &tags) const
|
2018-11-22 21:57:21 +01:00
|
|
|
{
|
2018-11-23 23:35:28 +01:00
|
|
|
QRegExp rx = QRegExp("\\{[^\\}]*\\}");
|
|
|
|
QString text(_field.value(zoom));
|
|
|
|
QStringList keys;
|
2018-11-22 21:57:21 +01:00
|
|
|
int pos = 0;
|
|
|
|
|
2018-11-23 23:35:28 +01:00
|
|
|
while ((pos = rx.indexIn(text, pos)) != -1) {
|
|
|
|
QString match = rx.capturedTexts().first();
|
|
|
|
keys.append(match.mid(1, match.size() - 2));
|
|
|
|
pos += rx.matchedLength();
|
2018-11-22 21:57:21 +01:00
|
|
|
}
|
2018-11-23 23:35:28 +01:00
|
|
|
for (int i = 0; i < keys.size(); i++) {
|
|
|
|
const QString &key = keys.at(i);
|
2018-11-22 21:57:21 +01:00
|
|
|
const QVariant val = tags.value(key);
|
|
|
|
text.replace(QString("{%1}").arg(key), val.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2018-10-29 00:11:23 +01:00
|
|
|
Style::Layer::Paint::Paint(const QJsonObject &json)
|
2018-11-06 22:35:30 +01:00
|
|
|
: _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0)
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-13 00:07:51 +01:00
|
|
|
// fill
|
|
|
|
jsonFloat(json, "fill-opacity", _fillOpacity);
|
|
|
|
jsonColor(json, "fill-color", _fillColor);
|
|
|
|
_fillOutlineColor = _fillColor;
|
|
|
|
jsonColor(json, "fill-outline-color", _fillOutlineColor);
|
|
|
|
jsonBool(json, "fill-antialias",_fillAntialias);
|
2018-11-23 23:35:28 +01:00
|
|
|
jsonString(json, "fill-pattern", _fillPattern);
|
2018-11-03 19:16:08 +01:00
|
|
|
if (json.contains("fill-pattern")) {
|
|
|
|
_fillColor = FunctionC(QColor());
|
|
|
|
_fillOutlineColor = FunctionC(QColor());
|
|
|
|
}
|
|
|
|
|
2018-11-13 00:07:51 +01:00
|
|
|
// line
|
|
|
|
jsonColor(json, "line-color", _lineColor);
|
|
|
|
jsonFloat(json, "line-width", _lineWidth);
|
|
|
|
jsonFloat(json, "line-opacity", _lineOpacity);
|
2018-10-29 00:11:23 +01:00
|
|
|
if (json.contains("line-dasharray") && json["line-dasharray"].isArray()) {
|
|
|
|
QJsonArray array = json["line-dasharray"].toArray();
|
|
|
|
for (int i = 0; i < array.size(); i++)
|
|
|
|
_lineDasharray.append(array.at(i).toDouble());
|
|
|
|
}
|
|
|
|
|
2018-11-13 00:07:51 +01:00
|
|
|
// background
|
|
|
|
jsonColor(json, "background-color", _backgroundColor);
|
|
|
|
|
|
|
|
// text
|
|
|
|
jsonColor(json, "text-color", _textColor);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QPen Style::Layer::Paint::pen(Type type, int zoom) const
|
|
|
|
{
|
|
|
|
QPen pen(Qt::NoPen);
|
|
|
|
qreal width;
|
2018-10-31 00:47:23 +01:00
|
|
|
QColor color;
|
2018-10-29 00:11:23 +01:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case Line:
|
|
|
|
width = _lineWidth.value(zoom);
|
2018-10-31 00:47:23 +01:00
|
|
|
color = _lineColor.value(zoom);
|
|
|
|
if (color.isValid() && width > 0) {
|
|
|
|
pen = QPen(color, width);
|
2018-10-29 00:11:23 +01:00
|
|
|
if (!_lineDasharray.isEmpty())
|
|
|
|
pen.setDashPattern(_lineDasharray);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Fill:
|
2018-10-31 00:47:23 +01:00
|
|
|
color = _fillOutlineColor.value(zoom);
|
|
|
|
if (color.isValid())
|
|
|
|
pen = QPen(color);
|
2018-10-29 00:11:23 +01:00
|
|
|
break;
|
|
|
|
case Symbol:
|
2018-10-31 00:47:23 +01:00
|
|
|
color = _textColor.value(zoom);
|
|
|
|
if (color.isValid())
|
|
|
|
pen = QPen(color);
|
2018-10-29 00:11:23 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pen;
|
|
|
|
}
|
|
|
|
|
2018-11-23 23:35:28 +01:00
|
|
|
QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-10-31 00:47:23 +01:00
|
|
|
QColor color;
|
2018-11-23 23:35:28 +01:00
|
|
|
QBrush brush(Qt::NoBrush);
|
|
|
|
QString pattern;
|
2018-10-31 00:47:23 +01:00
|
|
|
|
2018-10-29 00:11:23 +01:00
|
|
|
switch (type) {
|
|
|
|
case Fill:
|
2018-10-31 00:47:23 +01:00
|
|
|
color = _fillColor.value(zoom);
|
2018-11-23 23:35:28 +01:00
|
|
|
if (color.isValid())
|
|
|
|
brush = QBrush(color);
|
|
|
|
pattern = _fillPattern.value(zoom);
|
|
|
|
if (!pattern.isNull())
|
|
|
|
brush.setTextureImage(sprites.icon(pattern));
|
|
|
|
break;
|
2018-10-29 00:11:23 +01:00
|
|
|
case Background:
|
2018-10-31 00:47:23 +01:00
|
|
|
color = _backgroundColor.value(zoom);
|
2018-11-23 23:35:28 +01:00
|
|
|
if (color.isValid())
|
|
|
|
brush = QBrush(color);
|
|
|
|
pattern = _fillPattern.value(zoom);
|
|
|
|
if (!pattern.isNull())
|
|
|
|
brush.setTextureImage(sprites.icon(pattern));
|
|
|
|
break;
|
2018-10-29 00:11:23 +01:00
|
|
|
default:
|
2018-11-23 23:35:28 +01:00
|
|
|
break;
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
2018-11-23 23:35:28 +01:00
|
|
|
|
|
|
|
return brush;
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal Style::Layer::Paint::opacity(Type type, int zoom) const
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case Fill:
|
|
|
|
return _fillOpacity.value(zoom);
|
|
|
|
case Line:
|
|
|
|
return _lineOpacity.value(zoom);
|
|
|
|
default:
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 22:35:30 +01:00
|
|
|
bool Style::Layer::Paint::antialias(Layer::Type type, int zoom) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case Fill:
|
2018-11-06 22:35:30 +01:00
|
|
|
return _fillAntialias.value(zoom);
|
2018-10-29 00:11:23 +01:00
|
|
|
case Line:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Style::Layer::Layout::Layout(const QJsonObject &json)
|
2018-11-04 23:54:34 +01:00
|
|
|
: _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _lineCap(Qt::FlatCap),
|
2018-11-19 23:09:34 +01:00
|
|
|
_lineJoin(Qt::MiterJoin), _font("Open Sans"), _capitalize(false),
|
2018-11-22 21:57:21 +01:00
|
|
|
_viewportAlignment(false), _textAnchor(Text::Center)
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-13 18:24:51 +01:00
|
|
|
// line
|
2018-11-13 00:07:51 +01:00
|
|
|
if (json.contains("line-cap") && json["line-cap"].isString()) {
|
|
|
|
if (json["line-cap"].toString() == "round")
|
|
|
|
_lineCap = Qt::RoundCap;
|
|
|
|
else if (json["line-cap"].toString() == "square")
|
|
|
|
_lineCap = Qt::SquareCap;
|
|
|
|
}
|
|
|
|
if (json.contains("line-join") && json["line-join"].isString()) {
|
|
|
|
if (json["line-join"].toString() == "bevel")
|
|
|
|
_lineJoin = Qt::BevelJoin;
|
|
|
|
else if (json["line-join"].toString() == "round")
|
|
|
|
_lineJoin = Qt::RoundJoin;
|
|
|
|
}
|
|
|
|
|
2018-11-13 18:24:51 +01:00
|
|
|
// text
|
2018-11-22 21:57:21 +01:00
|
|
|
if (json.contains("text-field") && json["text-field"].isString())
|
|
|
|
_text = Template(json["text-field"].toString());
|
2018-10-29 00:11:23 +01:00
|
|
|
|
2018-11-13 00:07:51 +01:00
|
|
|
jsonFloat(json, "text-size", _textSize);
|
|
|
|
jsonFloat(json, "text-max-width", _textMaxWidth);
|
|
|
|
jsonFloat(json, "text-max-angle", _textMaxAngle);
|
2018-11-06 22:35:30 +01:00
|
|
|
if (json.contains("text-font") && json["text-font"].isArray())
|
|
|
|
_font = Font::fromJsonArray(json["text-font"].toArray());
|
2018-10-29 18:42:04 +01:00
|
|
|
if (json.contains("text-transform") && json["text-transform"].isString())
|
|
|
|
_capitalize = json["text-transform"].toString() == "uppercase";
|
2018-11-19 23:09:34 +01:00
|
|
|
if (json.contains("text-rotation-alignment")
|
|
|
|
&& json["text-rotation-alignment"].isString())
|
|
|
|
if (json["text-rotation-alignment"].toString() == "viewport")
|
|
|
|
_viewportAlignment = true;
|
2018-11-22 21:57:21 +01:00
|
|
|
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());
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QFont Style::Layer::Layout::font(int zoom) const
|
|
|
|
{
|
2018-11-06 22:35:30 +01:00
|
|
|
QFont font(_font);
|
2018-11-06 23:51:44 +01:00
|
|
|
font.setPixelSize(_textSize.value(zoom));
|
2018-10-29 00:11:23 +01:00
|
|
|
|
|
|
|
return font;
|
|
|
|
}
|
|
|
|
|
|
|
|
Style::Layer::Layer(const QJsonObject &json)
|
|
|
|
: _type(Unknown), _minZoom(-1), _maxZoom(-1)
|
|
|
|
{
|
|
|
|
// type
|
|
|
|
QString type = json["type"].toString();
|
|
|
|
if (type == "fill")
|
|
|
|
_type = Fill;
|
|
|
|
else if (type == "line")
|
|
|
|
_type = Line;
|
|
|
|
else if (type == "background")
|
|
|
|
_type = Background;
|
|
|
|
else if (type == "symbol")
|
|
|
|
_type = Symbol;
|
|
|
|
|
|
|
|
// source-layer
|
|
|
|
_sourceLayer = json["source-layer"].toString();
|
|
|
|
|
|
|
|
// zooms
|
|
|
|
if (json.contains("minzoom") && json["minzoom"].isDouble())
|
|
|
|
_minZoom = json["minzoom"].toInt();
|
|
|
|
if (json.contains("maxzoom") && json["maxzoom"].isDouble())
|
|
|
|
_maxZoom = json["maxzoom"].toInt();
|
|
|
|
|
|
|
|
// filter
|
|
|
|
if (json.contains("filter") && json["filter"].isArray())
|
|
|
|
_filter = Filter(json["filter"].toArray());
|
|
|
|
|
|
|
|
// layout
|
|
|
|
if (json.contains("layout") && json["layout"].isObject())
|
|
|
|
_layout = Layout(json["layout"].toObject());
|
|
|
|
|
|
|
|
// paint
|
|
|
|
if (json.contains("paint") && json["paint"].isObject())
|
|
|
|
_paint = Paint(json["paint"].toObject());
|
|
|
|
}
|
|
|
|
|
2018-11-10 19:34:45 +01:00
|
|
|
bool Style::Layer::match(int zoom, const QVariantHash &tags) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
|
|
|
if (zoom >= 0) {
|
|
|
|
if (_minZoom > 0 && zoom < _minZoom)
|
|
|
|
return false;
|
|
|
|
if (_maxZoom > 0 && zoom > _maxZoom)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return _filter.match(tags);
|
|
|
|
}
|
|
|
|
|
2018-11-23 23:35:28 +01:00
|
|
|
void Style::Layer::setPathPainter(Tile &tile, const Sprites &sprites) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
QPen pen(_paint.pen(_type, tile.zoom()));
|
2018-11-23 23:35:28 +01:00
|
|
|
QBrush brush(_paint.brush(_type, tile.zoom(), sprites));
|
2018-10-31 00:47:23 +01:00
|
|
|
|
2018-10-29 00:11:23 +01:00
|
|
|
pen.setJoinStyle(_layout.lineJoin());
|
|
|
|
pen.setCapStyle(_layout.lineCap());
|
|
|
|
|
2018-10-31 00:47:23 +01:00
|
|
|
QPainter &p = tile.painter();
|
2018-11-22 21:57:21 +01:00
|
|
|
p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, tile.zoom()));
|
2018-10-29 22:35:05 +01:00
|
|
|
p.setPen(pen);
|
2018-10-31 00:47:23 +01:00
|
|
|
p.setBrush(brush);
|
2018-11-22 21:57:21 +01:00
|
|
|
p.setOpacity(_paint.opacity(_type, tile.zoom()));
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
void Style::Layer::setSymbolPainter(Tile &tile) const
|
2018-11-13 18:24:51 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
QPen pen(_paint.pen(_type, tile.zoom()));
|
|
|
|
QFont font(_layout.font(tile.zoom()));
|
2018-11-13 18:24:51 +01:00
|
|
|
|
|
|
|
QPainter &p = tile.painter();
|
|
|
|
p.setPen(pen);
|
|
|
|
p.setFont(font);
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
void Style::Layer::setTextProperties(Tile &tile) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
Text::Properties prop;
|
|
|
|
prop.maxWidth = _layout.maxTextWidth(tile.zoom());
|
|
|
|
prop.maxAngle = _layout.maxTextAngle(tile.zoom());
|
|
|
|
prop.anchor = _layout.textAnchor();
|
2018-11-03 21:05:28 +01:00
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
tile.text().setProperties(prop);
|
|
|
|
}
|
2018-10-29 00:11:23 +01:00
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path,
|
|
|
|
const QVariantHash &tags, const Sprites &sprites) const
|
|
|
|
{
|
2018-11-23 23:35:28 +01:00
|
|
|
QString text = _layout.text(tile.zoom(), tags);
|
2018-11-03 21:05:28 +01:00
|
|
|
QString tt(text.trimmed());
|
|
|
|
if (tt.isEmpty())
|
|
|
|
return;
|
2018-11-22 21:57:21 +01:00
|
|
|
if (_layout.capitalize())
|
|
|
|
tt = tt.toUpper();
|
|
|
|
|
2018-11-23 23:35:28 +01:00
|
|
|
QString icon = _layout.icon(tile.zoom(), tags);
|
2018-11-03 21:05:28 +01:00
|
|
|
|
2018-11-19 23:09:34 +01:00
|
|
|
if (_layout.viewportAlignment())
|
2018-11-22 21:57:21 +01:00
|
|
|
tile.text().addLabel(tt, path.elementAt(0), tile.painter(), false,
|
|
|
|
sprites.icon(icon));
|
2018-11-19 23:09:34 +01:00
|
|
|
else if (path.elementCount() == 1 && path.elementAt(0).isMoveTo())
|
2018-11-22 21:57:21 +01:00
|
|
|
tile.text().addLabel(tt, path.elementAt(0), tile.painter(), true,
|
|
|
|
sprites.icon(icon));
|
2018-10-29 00:11:23 +01:00
|
|
|
else
|
2018-11-22 21:57:21 +01:00
|
|
|
tile.text().addLabel(tt, path, tile.painter());
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Style::load(const QString &fileName)
|
|
|
|
{
|
|
|
|
QFile file(fileName);
|
2018-11-01 18:15:05 +01:00
|
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
|
|
qCritical() << fileName << ": error opening file";
|
2018-10-29 00:11:23 +01:00
|
|
|
return false;
|
2018-11-01 18:15:05 +01:00
|
|
|
}
|
2018-10-29 00:11:23 +01:00
|
|
|
QByteArray ba(file.readAll());
|
|
|
|
file.close();
|
|
|
|
|
2018-11-01 18:15:05 +01:00
|
|
|
QJsonParseError error;
|
|
|
|
QJsonDocument doc(QJsonDocument::fromJson(ba, &error));
|
|
|
|
if (doc.isNull()) {
|
|
|
|
qCritical() << fileName << ":" << error.errorString();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QJsonObject json(doc.object());
|
2018-10-29 00:11:23 +01:00
|
|
|
if (json.contains("layers") && json["layers"].isArray()) {
|
|
|
|
QJsonArray layers = json["layers"].toArray();
|
|
|
|
for (int i = 0; i < layers.size(); i++)
|
|
|
|
if (layers[i].isObject())
|
2018-11-22 21:57:21 +01:00
|
|
|
_layers.append(Layer(layers[i].toObject()));
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
for (int i = 0; i < _layers.size(); i++)
|
|
|
|
_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";
|
|
|
|
}
|
2018-10-29 00:11:23 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
bool Style::match(int zoom, int layer, const QVariantHash &tags) const
|
|
|
|
{
|
|
|
|
return _layers.at(layer).match(zoom, tags);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Style::setTextProperties(Tile &tile, int layer) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
const Layer &sl = _layers.at(layer);
|
|
|
|
sl.setTextProperties(tile);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
void Style::setPainter(Tile &tile, int layer) const
|
2018-11-13 01:01:36 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
const Layer &sl = _layers.at(layer);
|
2018-11-13 01:01:36 +01:00
|
|
|
|
|
|
|
if (sl.isPath())
|
2018-11-23 23:35:28 +01:00
|
|
|
sl.setPathPainter(tile, _sprites);
|
2018-11-13 18:24:51 +01:00
|
|
|
else if (sl.isSymbol())
|
2018-11-22 21:57:21 +01:00
|
|
|
sl.setSymbolPainter(tile);
|
2018-11-13 01:01:36 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
void Style::drawFeature(Tile &tile, int layer, const QPainterPath &path,
|
|
|
|
const QVariantHash &tags) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
const Layer &sl = _layers.at(layer);
|
2018-10-29 00:11:23 +01:00
|
|
|
|
|
|
|
if (sl.isPath())
|
2018-11-13 01:01:36 +01:00
|
|
|
tile.painter().drawPath(path);
|
2018-10-29 00:11:23 +01:00
|
|
|
else if (sl.isSymbol())
|
2018-11-22 21:57:21 +01:00
|
|
|
sl.addSymbol(tile, path, tags, _sprites);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
void Style::drawBackground(Tile &tile) const
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-12 22:05:55 +01:00
|
|
|
QRectF rect(QPointF(0, 0), tile.size());
|
2018-10-29 18:42:04 +01:00
|
|
|
QPainterPath path;
|
2018-10-31 17:40:43 +01:00
|
|
|
path.addRect(rect);
|
2018-10-29 00:11:23 +01:00
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
if (_layers.isEmpty()) {
|
2018-10-29 22:35:05 +01:00
|
|
|
tile.painter().setBrush(Qt::lightGray);
|
2018-10-31 17:40:43 +01:00
|
|
|
tile.painter().setPen(Qt::NoPen);
|
|
|
|
tile.painter().drawRect(rect);
|
2018-11-22 21:57:21 +01:00
|
|
|
} else if (_layers.first().isBackground()) {
|
2018-11-23 23:35:28 +01:00
|
|
|
_layers.first().setPathPainter(tile, _sprites);
|
2018-11-13 01:01:36 +01:00
|
|
|
tile.painter().drawPath(path);
|
|
|
|
}
|
2018-11-17 22:22:46 +01:00
|
|
|
|
|
|
|
//tile.painter().setPen(Qt::red);
|
|
|
|
//tile.painter().drawRect(rect);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|