2018-10-29 00:11:23 +01:00
|
|
|
#include <QByteArray>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QDebug>
|
2018-11-10 19:34:45 +01:00
|
|
|
#include <QVariantHash>
|
2018-10-29 00:11:23 +01:00
|
|
|
#include "vector_tile.pb.h"
|
|
|
|
#include "style.h"
|
|
|
|
#include "tile.h"
|
|
|
|
#include "pbf.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define MOVE_TO 1
|
|
|
|
#define LINE_TO 2
|
|
|
|
#define CLOSE_PATH 7
|
|
|
|
|
|
|
|
#define POLYGON vector_tile::Tile_GeomType::Tile_GeomType_POLYGON
|
|
|
|
#define LINESTRING vector_tile::Tile_GeomType::Tile_GeomType_LINESTRING
|
|
|
|
#define POINT vector_tile::Tile_GeomType::Tile_GeomType_POINT
|
|
|
|
|
2018-11-07 19:48:40 +01:00
|
|
|
|
2018-10-29 00:11:23 +01:00
|
|
|
static QVariant value(const vector_tile::Tile_Value &val)
|
|
|
|
{
|
|
|
|
if (val.has_bool_value())
|
|
|
|
return QVariant(val.bool_value());
|
|
|
|
else if (val.has_int_value())
|
|
|
|
return QVariant((qlonglong)val.int_value());
|
|
|
|
else if (val.has_sint_value())
|
|
|
|
return QVariant((qlonglong)val.sint_value());
|
|
|
|
else if (val.has_uint_value())
|
|
|
|
return QVariant((qulonglong)val.uint_value());
|
|
|
|
else if (val.has_float_value())
|
|
|
|
return QVariant(val.float_value());
|
|
|
|
else if (val.has_double_value())
|
|
|
|
return QVariant(val.double_value());
|
|
|
|
else if (val.has_string_value())
|
|
|
|
return QVariant(QString::fromStdString(val.string_value()));
|
|
|
|
else
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
2018-11-05 23:13:44 +01:00
|
|
|
class Feature
|
|
|
|
{
|
|
|
|
public:
|
2018-11-10 19:34:45 +01:00
|
|
|
Feature() : _data(0) {}
|
2018-11-05 23:13:44 +01:00
|
|
|
Feature(const vector_tile::Tile_Feature *data, const QVector<QString> *keys,
|
|
|
|
const QVector<QVariant> *values) : _data(data)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < data->tags_size(); i = i + 2)
|
|
|
|
_tags.insert(keys->at(data->tags(i)), values->at(data->tags(i+1)));
|
|
|
|
|
|
|
|
switch (data->type()) {
|
|
|
|
case POLYGON:
|
|
|
|
_tags.insert("$type", QVariant("Polygon"));
|
|
|
|
break;
|
|
|
|
case LINESTRING:
|
|
|
|
_tags.insert("$type", QVariant("LineString"));
|
|
|
|
break;
|
|
|
|
case POINT:
|
|
|
|
_tags.insert("$type", QVariant("Point"));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-10 19:34:45 +01:00
|
|
|
const QVariantHash &tags() const {return _tags;}
|
2018-11-05 23:13:44 +01:00
|
|
|
const vector_tile::Tile_Feature *data() const {return _data;}
|
|
|
|
|
|
|
|
private:
|
2018-11-10 19:34:45 +01:00
|
|
|
QVariantHash _tags;
|
2018-11-05 23:13:44 +01:00
|
|
|
const vector_tile::Tile_Feature *_data;
|
|
|
|
};
|
|
|
|
|
2018-11-10 00:17:00 +01:00
|
|
|
bool cmp(const Feature &f1, const Feature &f2)
|
|
|
|
{
|
|
|
|
return f1.data()->id() < f2.data()->id();
|
|
|
|
}
|
|
|
|
|
2018-11-05 23:13:44 +01:00
|
|
|
class Layer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Layer(const vector_tile::Tile_Layer *data) : _data(data)
|
|
|
|
{
|
|
|
|
QVector<QString> keys;
|
|
|
|
QVector<QVariant> values;
|
|
|
|
|
|
|
|
for (int i = 0; i < data->keys_size(); i++)
|
|
|
|
keys.append(QString::fromStdString(data->keys(i)));
|
|
|
|
for (int i = 0; i < data->values_size(); i++)
|
|
|
|
values.append(value(data->values(i)));
|
|
|
|
|
2018-11-10 19:34:45 +01:00
|
|
|
_features.reserve(data->features_size());
|
2018-11-05 23:13:44 +01:00
|
|
|
for (int i = 0; i < data->features_size(); i++)
|
|
|
|
_features.append(Feature(&(data->features(i)), &keys, &values));
|
2018-11-10 00:17:00 +01:00
|
|
|
qSort(_features.begin(), _features.end(), cmp);
|
2018-11-05 23:13:44 +01:00
|
|
|
}
|
|
|
|
|
2018-11-10 19:34:45 +01:00
|
|
|
const QVector<Feature> &features() const {return _features;}
|
2018-11-05 23:13:44 +01:00
|
|
|
const vector_tile::Tile_Layer *data() const {return _data;}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const vector_tile::Tile_Layer *_data;
|
2018-11-10 19:34:45 +01:00
|
|
|
QVector<Feature> _features;
|
2018-11-05 23:13:44 +01:00
|
|
|
};
|
|
|
|
|
2018-11-07 19:48:40 +01:00
|
|
|
static inline qint32 zigzag32decode(quint32 value)
|
|
|
|
{
|
|
|
|
return static_cast<qint32>((value >> 1u) ^ static_cast<quint32>(
|
|
|
|
-static_cast<qint32>(value & 1u)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline QPoint parameters(quint32 v1, quint32 v2)
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-07 19:48:40 +01:00
|
|
|
return QPoint(zigzag32decode(v1), zigzag32decode(v2));
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 18:24:51 +01:00
|
|
|
static void drawFeature(const Feature &feature, Style *style, int styleLayer,
|
2018-11-12 22:05:55 +01:00
|
|
|
const QSizeF &factor, Tile &tile)
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-22 21:57:21 +01:00
|
|
|
if (!style->match(tile.zoom(), styleLayer, feature.tags()))
|
2018-10-29 00:11:23 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
QPoint cursor;
|
|
|
|
QPainterPath path;
|
|
|
|
|
2018-11-05 23:13:44 +01:00
|
|
|
for (int i = 0; i < feature.data()->geometry_size(); i++) {
|
|
|
|
quint32 g = feature.data()->geometry(i);
|
2018-10-29 00:11:23 +01:00
|
|
|
unsigned cmdId = g & 0x7;
|
|
|
|
unsigned cmdCount = g >> 3;
|
|
|
|
|
|
|
|
if (cmdId == MOVE_TO) {
|
|
|
|
for (unsigned j = 0; j < cmdCount; j++) {
|
2018-11-05 23:13:44 +01:00
|
|
|
QPoint offset = parameters(feature.data()->geometry(i+1),
|
|
|
|
feature.data()->geometry(i+2));
|
2018-10-29 00:11:23 +01:00
|
|
|
i += 2;
|
|
|
|
cursor += offset;
|
2018-11-12 22:05:55 +01:00
|
|
|
path.moveTo(QPointF(cursor.x() * factor.width(),
|
|
|
|
cursor.y() * factor.height()));
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
} else if (cmdId == LINE_TO) {
|
|
|
|
for (unsigned j = 0; j < cmdCount; j++) {
|
2018-11-05 23:13:44 +01:00
|
|
|
QPoint offset = parameters(feature.data()->geometry(i+1),
|
|
|
|
feature.data()->geometry(i+2));
|
2018-10-29 00:11:23 +01:00
|
|
|
i += 2;
|
|
|
|
cursor += offset;
|
2018-11-12 22:05:55 +01:00
|
|
|
path.lineTo(QPointF(cursor.x() * factor.width(),
|
|
|
|
cursor.y() * factor.height()));
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
} else if (cmdId == CLOSE_PATH) {
|
|
|
|
path.closeSubpath();
|
|
|
|
path.moveTo(cursor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-24 18:57:24 +01:00
|
|
|
if (path.elementCount())
|
|
|
|
style->drawFeature(tile, styleLayer, path, feature.tags());
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-05 23:13:44 +01:00
|
|
|
static void drawLayer(const Layer &layer, Style *style, int styleLayer,
|
2018-11-17 21:54:51 +01:00
|
|
|
Tile &tile, const QPointF &scale)
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
2018-11-05 23:13:44 +01:00
|
|
|
if (layer.data()->version() > 2)
|
2018-10-31 00:05:08 +01:00
|
|
|
return;
|
|
|
|
|
2018-11-17 21:54:51 +01:00
|
|
|
QSizeF factor(tile.size().width() / scale.x() / (qreal)layer.data()->extent(),
|
|
|
|
tile.size().height() / scale.y() / (qreal)layer.data()->extent());
|
2018-10-29 00:11:23 +01:00
|
|
|
|
2018-11-25 12:46:38 +01:00
|
|
|
style->setupLayer(tile, styleLayer);
|
2018-11-22 21:57:21 +01:00
|
|
|
|
2018-11-05 23:13:44 +01:00
|
|
|
for (int i = 0; i < layer.features().size(); i++)
|
2018-11-13 18:24:51 +01:00
|
|
|
drawFeature(layer.features().at(i), style, styleLayer, factor, tile);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-17 21:54:51 +01:00
|
|
|
bool PBF::render(const QByteArray &data, int zoom, Style *style,
|
|
|
|
const QPointF &scale, QImage *image)
|
2018-10-29 00:11:23 +01:00
|
|
|
{
|
|
|
|
vector_tile::Tile tile;
|
|
|
|
if (!tile.ParseFromArray(data.constData(), data.size())) {
|
|
|
|
qCritical() << "Invalid tile protocol buffer data";
|
2018-11-12 22:05:55 +01:00
|
|
|
return false;
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-22 21:57:21 +01:00
|
|
|
Tile t(image, zoom, scale);
|
2018-10-29 00:11:23 +01:00
|
|
|
|
|
|
|
style->drawBackground(t);
|
|
|
|
|
2018-11-07 19:48:40 +01:00
|
|
|
// Prepare source layers
|
2018-10-29 00:11:23 +01:00
|
|
|
QMap<QString, Layer> layers;
|
|
|
|
for (int i = 0; i < tile.layers_size(); i++) {
|
|
|
|
const vector_tile::Tile_Layer &layer = tile.layers(i);
|
2018-11-05 23:13:44 +01:00
|
|
|
QString name(QString::fromStdString(layer.name()));
|
|
|
|
if (style->sourceLayers().contains(name))
|
|
|
|
layers.insert(name, Layer(&layer));
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 18:24:51 +01:00
|
|
|
t.painter().save();
|
|
|
|
|
2018-11-07 19:48:40 +01:00
|
|
|
// Process source layers in order of style layers
|
2018-10-29 00:11:23 +01:00
|
|
|
for (int i = 0; i < style->sourceLayers().size(); i++) {
|
|
|
|
QMap<QString, Layer>::const_iterator it = layers.find(
|
|
|
|
style->sourceLayers().at(i));
|
|
|
|
if (it == layers.constEnd())
|
|
|
|
continue;
|
|
|
|
|
2018-11-17 21:54:51 +01:00
|
|
|
drawLayer(*it, style, i, t, scale);
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 18:24:51 +01:00
|
|
|
t.painter().restore();
|
2018-11-13 01:01:36 +01:00
|
|
|
t.text().render(&t.painter());
|
2018-11-12 22:05:55 +01:00
|
|
|
|
|
|
|
return true;
|
2018-10-29 00:11:23 +01:00
|
|
|
}
|