#include #include #include #include #include "vector_tile.pb.h" #include "style.h" #include "tile.h" #include "pbf.h" using namespace google::protobuf; #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 struct Layer { Layer(const vector_tile::Tile_Layer *data) : data(data) {} const vector_tile::Tile_Layer *data; QVector keys; QVector values; }; struct Feature { Feature(const vector_tile::Tile_Feature *data, const QVector *keys, const QVector *values) : data(data), keys(keys), values(values) {} const vector_tile::Tile_Feature *data; const QVector *keys; const QVector *values; }; 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(); } static QPoint parameters(quint32 v1, quint32 v2) { return QPoint((v1 >> 1) ^ (-(v1 & 1)), ((v2 >> 1) ^ (-(v2 & 1)))); } static void feature(const Feature &feature, Style *style, int styleLayer, qreal factor, Tile &tile) { QVariantMap tags; for (int i = 0; i < feature.data->tags_size(); i = i + 2) tags.insert(feature.keys->at(feature.data->tags(i)), feature.values->at(feature.data->tags(i+1))); switch (feature.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; } if (!style->match(styleLayer, tags)) return; QPoint cursor; QPainterPath path; for (int i = 0; i < feature.data->geometry_size(); i++) { quint32 g = feature.data->geometry(i); unsigned cmdId = g & 0x7; unsigned cmdCount = g >> 3; if (cmdId == MOVE_TO) { for (unsigned j = 0; j < cmdCount; j++) { QPoint offset = parameters(feature.data->geometry(i+1), feature.data->geometry(i+2)); i += 2; cursor += offset; path.moveTo(QPointF(cursor) / factor); } } else if (cmdId == LINE_TO) { for (unsigned j = 0; j < cmdCount; j++) { QPoint offset = parameters(feature.data->geometry(i+1), feature.data->geometry(i+2)); i += 2; cursor += offset; path.lineTo(QPointF(cursor) / factor); } } else if (cmdId == CLOSE_PATH) { path.closeSubpath(); path.moveTo(cursor); } } style->drawFeature(styleLayer, path, tags, tile); } static void layer(const Layer &layer, Style *style, int styleLayer, Tile &tile) { if (layer.data->version() != 2) return; qreal factor = layer.data->extent() / (qreal)tile.size(); for (int i = 0; i < layer.data->features_size(); i++) feature(Feature(&(layer.data->features(i)), &(layer.keys), &(layer.values)), style, styleLayer, factor, tile); } QImage PBF::image(const QByteArray &data, int zoom, Style *style, int size) { vector_tile::Tile tile; if (!tile.ParseFromArray(data.constData(), data.size())) { qCritical() << "Invalid tile protocol buffer data"; return QImage(); } Tile t(size); style->setZoom(zoom); style->drawBackground(t); QMap layers; for (int i = 0; i < tile.layers_size(); i++) { const vector_tile::Tile_Layer &layer = tile.layers(i); Layer l(&layer); for (int j = 0; j < layer.keys_size(); j++) l.keys.append(QString::fromStdString(layer.keys(j))); for (int j = 0; j < layer.values_size(); j++) l.values.append(value(layer.values(j))); layers.insert(QString::fromStdString(tile.layers(i).name()), l); } // Process data in order of style layers for (int i = 0; i < style->sourceLayers().size(); i++) { QMap::const_iterator it = layers.find( style->sourceLayers().at(i)); if (it == layers.constEnd()) continue; layer(*it, style, i, t); } return t.render(); }