summaryrefslogtreecommitdiff
path: root/src/mbgl/tile/vector_tile_data.cpp
blob: 93dba57b4be7f1a402ef189008c5a72e8ca76bf4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <mbgl/tile/vector_tile_data.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/logging.hpp>

namespace mbgl {

VectorTileFeature::VectorTileFeature(const vtzero::layer& layer_,
                                     const vtzero::feature& feature_)
    : feature(feature_), layer(layer_) {
}

FeatureType VectorTileFeature::getType() const {
    switch (feature.geometry_type()) {
    case vtzero::GeomType::POINT:
        return FeatureType::Point;
    case vtzero::GeomType::LINESTRING:
        return FeatureType::LineString;
    case vtzero::GeomType::POLYGON:
        return FeatureType::Polygon;
    default:
        return FeatureType::Unknown;
    }
}

struct PropertyValueMapping : vtzero::property_value_mapping {
    using float_type = double;
};

optional<Value> VectorTileFeature::getValue(const std::string& key) const {
    optional<Value> result;
    feature.for_each_property([&](vtzero::property&& p) {
        if (p.key() == key) {
            result = vtzero::convert_property_value<Value, PropertyValueMapping>(p.value());
        }
        return !result;
    });
    return result;
}

std::unordered_map<std::string, Value> VectorTileFeature::getProperties() const {
    std::unordered_map<std::string, Value> map;
    feature.for_each_property([&](const vtzero::property& p) {
        map.emplace(std::string(p.key()), vtzero::convert_property_value<Value, PropertyValueMapping>(p.value()));
        return true;
    });
    return map;
}

optional<FeatureIdentifier> VectorTileFeature::getID() const {
    return {feature.id()};
}

// Decode every geometry into a common structure: GeometryCollection, aka
// std::vector<std::vector<mapbox::geometry::Point<int16_t>>>
struct GeometryHandler {
    GeometryHandler(const float scale_) : scale(scale_) {}
    
    void begin(uint32_t count) {
        // cap allocation to 1MB
        constexpr std::uint32_t MAX_LENGTH = (1024 * 1024) / 16;
        if (count > MAX_LENGTH) {
            count = MAX_LENGTH;
        }
        result_.emplace_back();
        result_.back().reserve(count);
    }
    
    void add_point(vtzero::point point) {
        float scaled_x = ::roundf(static_cast<float>(point.x) * scale);
        float scaled_y = ::roundf(static_cast<float>(point.y) * scale);
        static const float max_coord = static_cast<float>(std::numeric_limits<typename GeometryCollection::coordinate_type>::max());
        static const float min_coord = static_cast<float>(std::numeric_limits<typename GeometryCollection::coordinate_type>::min());

        if (scaled_x > max_coord ||
            scaled_x < min_coord ||
            scaled_y > max_coord ||
            scaled_y < min_coord
        ) {
            std::runtime_error("paths outside valid range of coordinate_type");
        } else {
            result_.back().emplace_back(
                static_cast<typename GeometryCollection::coordinate_type>(scaled_x),
                static_cast<typename GeometryCollection::coordinate_type>(scaled_y));
        }
    }
    
    // decode_point_geometry handlers
    void points_begin(uint32_t count) { begin(count); }
    void points_point(vtzero::point point) { add_point(point); }
    void points_end() {}

    // decode_linestring_geometry handlers
    void linestring_begin(uint32_t count) { begin(count); }
    void linestring_point(vtzero::point point) { add_point(point); }
    void linestring_end() {}
    
    // decode_polygon_geometry handlers
    void ring_begin(uint32_t count) { begin(count); }
    void ring_point(vtzero::point point) { add_point(point); }
    void ring_end(vtzero::ring_type) {}

    GeometryCollection result() {
        return result_;
    }
    
    const float scale;
    GeometryCollection result_;
};

GeometryCollection VectorTileFeature::getGeometries() const {
    const float scale = float(util::EXTENT) / layer.extent();
    const vtzero::geometry geometry = feature.geometry();
    try {
        GeometryCollection lines = vtzero::decode_geometry(geometry, GeometryHandler(scale));
        if (layer.version() >= 2 || feature.geometry_type() != vtzero::GeomType::POLYGON) {
            return lines;
        } else {
            return fixupPolygons(lines);
        }
    } catch (const std::exception& e) {
        Log::Warning(Event::ParseTile, std::string("vtzero: ") + e.what());
        return {};
    }
}

VectorTileLayer::VectorTileLayer(std::shared_ptr<const std::string> data_,
                                 const vtzero::layer& layer_)
    : data(std::move(data_)), layer(layer_) {
    layer.for_each_feature([&](vtzero::feature&& f) {
        features.push_back(f);
        return true;
    });
}

std::size_t VectorTileLayer::featureCount() const {
    return layer.num_features();
}

std::unique_ptr<GeometryTileFeature> VectorTileLayer::getFeature(std::size_t i) const {
    return std::make_unique<VectorTileFeature>(layer, features[i]);
}

std::string VectorTileLayer::getName() const {
    return static_cast<std::string>(layer.name());
}

VectorTileData::VectorTileData(std::shared_ptr<const std::string> data_) :
    data(std::move(data_)),
    tile(*data)
{
}

std::unique_ptr<GeometryTileData> VectorTileData::clone() const {
    return std::make_unique<VectorTileData>(data);
}

std::unique_ptr<GeometryTileLayer> VectorTileData::getLayer(const std::string& name) const {
    parseLayers();
    auto it = layers.find(name);
    if (it != layers.end()) {
        return std::make_unique<VectorTileLayer>(data, it->second);
    }
    return nullptr;
}

std::vector<std::string> VectorTileData::layerNames() const {
    parseLayers();
    std::vector<std::string> names;
    names.reserve(layers.size());
    for (const auto& entry : layers) {
        names.emplace_back(entry.first);
    }
    return names;
}

void VectorTileData::parseLayers() const {
    if (!parsed) {
        // We're parsing this lazily so that we can construct VectorTileData objects on the main
        // thread without incurring the overhead of parsing immediately.
        tile.for_each_layer([&](vtzero::layer&& layer) {
            layers[static_cast<std::string>(layer.name())] = std::move(layer);
            return true;
        });
        parsed = true;
    }
}

} // namespace mbgl