Files
MapLibreParticleLab/src/mbgl/map/vector_tile.cpp
2015-11-16 12:25:47 -08:00

209 lines
6.0 KiB
C++

#include <mbgl/map/vector_tile.hpp>
#include <mbgl/map/source.hpp>
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/response.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/thread_context.hpp>
#include <sstream>
namespace mbgl {
Value parseValue(pbf data) {
while (data.next())
{
switch (data.tag)
{
case 1: // string_value
return data.string();
case 2: // float_value
return static_cast<double>(data.float32());
case 3: // double_value
return data.float64();
case 4: // int_value
return data.varint<int64_t>();
case 5: // uint_value
return data.varint<uint64_t>();
case 6: // sint_value
return data.svarint<int64_t>();
case 7: // bool_value
return data.boolean();
default:
data.skip();
break;
}
}
return false;
}
VectorTileFeature::VectorTileFeature(pbf feature_pbf, const VectorTileLayer& layer_)
: layer(layer_) {
while (feature_pbf.next()) {
if (feature_pbf.tag == 1) { // id
id = feature_pbf.varint<uint64_t>();
} else if (feature_pbf.tag == 2) { // tags
tags_pbf = feature_pbf.message();
} else if (feature_pbf.tag == 3) { // type
type = (FeatureType)feature_pbf.varint();
} else if (feature_pbf.tag == 4) { // geometry
geometry_pbf = feature_pbf.message();
} else {
feature_pbf.skip();
}
}
}
mapbox::util::optional<Value> VectorTileFeature::getValue(const std::string& key) const {
auto keyIter = layer.keys.find(key);
if (keyIter == layer.keys.end()) {
return mapbox::util::optional<Value>();
}
pbf tags = tags_pbf;
while (tags) {
uint32_t tag_key = tags.varint();
if (layer.keys.size() <= tag_key) {
throw std::runtime_error("feature referenced out of range key");
}
if (!tags) {
throw std::runtime_error("uneven number of feature tag ids");
}
uint32_t tag_val = tags.varint();
if (layer.values.size() <= tag_val) {
throw std::runtime_error("feature referenced out of range value");
}
if (tag_key == keyIter->second) {
return layer.values[tag_val];
}
}
return mapbox::util::optional<Value>();
}
GeometryCollection VectorTileFeature::getGeometries() const {
pbf data(geometry_pbf);
uint8_t cmd = 1;
uint32_t length = 0;
int32_t x = 0;
int32_t y = 0;
GeometryCollection lines;
lines.emplace_back();
std::vector<Coordinate>* line = &lines.back();
while (data.data < data.end) {
if (length == 0) {
uint32_t cmd_length = data.varint();
cmd = cmd_length & 0x7;
length = cmd_length >> 3;
}
--length;
if (cmd == 1 || cmd == 2) {
x += data.svarint();
y += data.svarint();
if (cmd == 1 && !line->empty()) { // moveTo
lines.emplace_back();
line = &lines.back();
}
line->emplace_back(x, y);
} else if (cmd == 7) { // closePolygon
if (!line->empty()) {
line->push_back((*line)[0]);
}
} else {
throw std::runtime_error("unknown command");
}
}
return lines;
}
VectorTile::VectorTile(std::shared_ptr<const std::string> data_)
: data(data_) {
}
util::ptr<GeometryTileLayer> VectorTile::getLayer(const std::string& name) const {
if (!parsed) {
parsed = true;
pbf tile_pbf(reinterpret_cast<const unsigned char *>(data->c_str()), data->size());
while (tile_pbf.next()) {
if (tile_pbf.tag == 3) { // layer
util::ptr<VectorTileLayer> layer = std::make_shared<VectorTileLayer>(tile_pbf.message());
layers.emplace(layer->name, layer);
} else {
tile_pbf.skip();
}
}
}
auto layer_it = layers.find(name);
if (layer_it != layers.end()) {
return layer_it->second;
}
return nullptr;
}
VectorTileLayer::VectorTileLayer(pbf layer_pbf) {
while (layer_pbf.next()) {
if (layer_pbf.tag == 1) { // name
name = layer_pbf.string();
} else if (layer_pbf.tag == 2) { // feature
features.push_back(layer_pbf.message());
} else if (layer_pbf.tag == 3) { // keys
keys.emplace(layer_pbf.string(), keys.size());
} else if (layer_pbf.tag == 4) { // values
values.emplace_back(std::move(parseValue(layer_pbf.message())));
} else if (layer_pbf.tag == 5) { // extent
extent = layer_pbf.varint();
} else {
layer_pbf.skip();
}
}
}
util::ptr<const GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
return std::make_shared<VectorTileFeature>(features.at(i), *this);
}
VectorTileMonitor::VectorTileMonitor(const SourceInfo& source, const TileID& id, float pixelRatio)
: url(source.tileURL(id, pixelRatio)) {
}
std::unique_ptr<FileRequest> VectorTileMonitor::monitorTile(std::function<void (std::exception_ptr, std::unique_ptr<GeometryTile>)> callback) {
return util::ThreadContext::getFileSource()->request({ Resource::Kind::Tile, url }, [callback, this](Response res) {
if (res.data && data == res.data) {
// We got the same data again. Abort early.
return;
}
if (res.error) {
if (res.error->reason == Response::Error::Reason::NotFound) {
callback(nullptr, nullptr);
return;
} else {
std::stringstream message;
message << "Failed to load [" << url << "]: " << res.error->message;
callback(std::make_exception_ptr(std::runtime_error(message.str())), nullptr);
return;
}
}
data = res.data;
callback(nullptr, std::make_unique<VectorTile>(data));
});
}
}