| 1 | #pragma once |
| 2 | |
| 3 | #include "vector_tile/vector_tile_config.hpp" |
| 4 | #include <mapbox/geometry.hpp> |
| 5 | #include <protozero/pbf_reader.hpp> |
| 6 | |
| 7 | #include <cmath> |
| 8 | #include <cstdint> |
| 9 | #include <map> |
| 10 | #include <functional> // reference_wrapper |
| 11 | #include <string> |
| 12 | #include <stdexcept> |
| 13 | |
| 14 | #include <experimental/optional> |
| 15 | |
| 16 | template <typename T> |
| 17 | using optional = std::experimental::optional<T>; |
| 18 | |
| 19 | namespace mapbox { namespace vector_tile { |
| 20 | |
| 21 | using point_type = mapbox::geometry::point<std::int16_t>; |
| 22 | |
| 23 | class points_array_type : public std::vector<point_type> { |
| 24 | public: |
| 25 | using coordinate_type = point_type::coordinate_type; |
| 26 | template <class... Args> |
| 27 | points_array_type(Args&&... args) : std::vector<point_type>(std::forward<Args>(args)...) {} |
| 28 | }; |
| 29 | |
| 30 | class points_arrays_type : public std::vector<points_array_type> { |
| 31 | public: |
| 32 | using coordinate_type = points_array_type::coordinate_type; |
| 33 | template <class... Args> |
| 34 | points_arrays_type(Args&&... args) : std::vector<points_array_type>(std::forward<Args>(args)...) {} |
| 35 | }; |
| 36 | |
| 37 | class layer; |
| 38 | |
| 39 | class feature { |
| 40 | public: |
| 41 | using properties_type = mapbox::geometry::property_map; |
| 42 | using packed_iterator_type = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>; |
| 43 | |
| 44 | feature(protozero::data_view const&, layer const&); |
| 45 | |
| 46 | GeomType getType() const { return type; } |
| 47 | optional<mapbox::geometry::value> getValue(std::string const&) const; |
| 48 | properties_type getProperties() const; |
| 49 | optional<mapbox::geometry::identifier> const& getID() const; |
| 50 | std::uint32_t getExtent() const; |
| 51 | std::uint32_t getVersion() const; |
| 52 | template <typename GeometryCollectionType> |
| 53 | GeometryCollectionType getGeometries(float scale) const; |
| 54 | |
| 55 | private: |
| 56 | const layer& layer_; |
| 57 | optional<mapbox::geometry::identifier> id; |
| 58 | GeomType type = GeomType::UNKNOWN; |
| 59 | packed_iterator_type tags_iter; |
| 60 | packed_iterator_type geometry_iter; |
| 61 | }; |
| 62 | |
| 63 | class layer { |
| 64 | public: |
| 65 | layer(protozero::data_view const& layer_view); |
| 66 | |
| 67 | std::size_t featureCount() const { return features.size(); } |
| 68 | protozero::data_view const& getFeature(std::size_t) const; |
| 69 | std::string const& getName() const; |
| 70 | std::uint32_t getExtent() const { return extent; } |
| 71 | std::uint32_t getVersion() const { return version; } |
| 72 | |
| 73 | private: |
| 74 | friend class feature; |
| 75 | |
| 76 | std::string name; |
| 77 | std::uint32_t version; |
| 78 | std::uint32_t extent; |
| 79 | std::map<std::string, std::uint32_t> keysMap; |
| 80 | std::vector<std::reference_wrapper<const std::string>> keys; |
| 81 | std::vector<protozero::data_view> values; |
| 82 | std::vector<protozero::data_view> features; |
| 83 | }; |
| 84 | |
| 85 | class buffer { |
| 86 | public: |
| 87 | buffer(std::string const& data); |
| 88 | std::vector<std::string> layerNames() const; |
| 89 | std::map<std::string, const protozero::data_view> getLayers() const { return layers; }; |
| 90 | layer getLayer(const std::string&) const; |
| 91 | |
| 92 | private: |
| 93 | std::map<std::string, const protozero::data_view> layers; |
| 94 | }; |
| 95 | |
| 96 | static mapbox::geometry::value parseValue(protozero::data_view const& value_view) { |
| 97 | mapbox::geometry::value value; |
| 98 | protozero::pbf_reader value_reader(value_view); |
| 99 | while (value_reader.next()) |
| 100 | { |
| 101 | switch (value_reader.tag()) { |
| 102 | case ValueType::STRING: |
| 103 | value = value_reader.get_string(); |
| 104 | break; |
| 105 | case ValueType::FLOAT: |
| 106 | value = static_cast<double>(value_reader.get_float()); |
| 107 | break; |
| 108 | case ValueType::DOUBLE: |
| 109 | value = value_reader.get_double(); |
| 110 | break; |
| 111 | case ValueType::INT: |
| 112 | value = value_reader.get_int64(); |
| 113 | break; |
| 114 | case ValueType::UINT: |
| 115 | value = value_reader.get_uint64(); |
| 116 | break; |
| 117 | case ValueType::SINT: |
| 118 | value = value_reader.get_sint64(); |
| 119 | break; |
| 120 | case ValueType::BOOL: |
| 121 | value = value_reader.get_bool(); |
| 122 | break; |
| 123 | default: |
| 124 | value_reader.skip(); |
| 125 | break; |
| 126 | } |
| 127 | } |
| 128 | return value; |
| 129 | } |
| 130 | |
| 131 | inline feature::feature(protozero::data_view const& feature_view, layer const& l) |
| 132 | : layer_(l), |
| 133 | id(), |
| 134 | type(GeomType::UNKNOWN), |
| 135 | tags_iter(), |
| 136 | geometry_iter() |
| 137 | { |
| 138 | protozero::pbf_reader feature_pbf(feature_view); |
| 139 | while (feature_pbf.next()) { |
| 140 | switch (feature_pbf.tag()) { |
| 141 | case FeatureType::ID: |
| 142 | id = optional<mapbox::geometry::identifier>{ feature_pbf.get_uint64() }; |
| 143 | break; |
| 144 | case FeatureType::TAGS: |
| 145 | tags_iter = feature_pbf.get_packed_uint32(); |
| 146 | break; |
| 147 | case FeatureType::TYPE: |
| 148 | type = static_cast<GeomType>(feature_pbf.get_enum()); |
| 149 | break; |
| 150 | case FeatureType::GEOMETRY: |
| 151 | geometry_iter = feature_pbf.get_packed_uint32(); |
| 152 | break; |
| 153 | default: |
| 154 | feature_pbf.skip(); |
| 155 | break; |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | inline optional<mapbox::geometry::value> feature::getValue(const std::string& key) const { |
| 161 | auto keyIter = layer_.keysMap.find(x: key); |
| 162 | if (keyIter == layer_.keysMap.end()) { |
| 163 | return optional<mapbox::geometry::value>(); |
| 164 | } |
| 165 | |
| 166 | const auto values_count = layer_.values.size(); |
| 167 | const auto keymap_count = layer_.keysMap.size(); |
| 168 | auto start_itr = tags_iter.begin(); |
| 169 | const auto end_itr = tags_iter.end(); |
| 170 | while (start_itr != end_itr) { |
| 171 | std::uint32_t tag_key = static_cast<std::uint32_t>(*start_itr++); |
| 172 | |
| 173 | if (keymap_count <= tag_key) { |
| 174 | throw std::runtime_error("feature referenced out of range key" ); |
| 175 | } |
| 176 | |
| 177 | if (start_itr == end_itr) { |
| 178 | throw std::runtime_error("uneven number of feature tag ids" ); |
| 179 | } |
| 180 | |
| 181 | std::uint32_t tag_val = static_cast<std::uint32_t>(*start_itr++);; |
| 182 | if (values_count <= tag_val) { |
| 183 | throw std::runtime_error("feature referenced out of range value" ); |
| 184 | } |
| 185 | |
| 186 | if (tag_key == keyIter->second) { |
| 187 | return parseValue(value_view: layer_.values[tag_val]); |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | return optional<mapbox::geometry::value>(); |
| 192 | } |
| 193 | |
| 194 | inline feature::properties_type feature::getProperties() const { |
| 195 | auto start_itr = tags_iter.begin(); |
| 196 | const auto end_itr = tags_iter.end(); |
| 197 | properties_type properties; |
| 198 | auto iter_len = std::distance(first: start_itr,last: end_itr); |
| 199 | if (iter_len > 0) { |
| 200 | properties.reserve(n: static_cast<std::size_t>(iter_len/2)); |
| 201 | while (start_itr != end_itr) { |
| 202 | std::uint32_t tag_key = static_cast<std::uint32_t>(*start_itr++); |
| 203 | if (start_itr == end_itr) { |
| 204 | throw std::runtime_error("uneven number of feature tag ids" ); |
| 205 | } |
| 206 | std::uint32_t tag_val = static_cast<std::uint32_t>(*start_itr++); |
| 207 | properties.emplace(args: layer_.keys.at(n: tag_key),args: parseValue(value_view: layer_.values.at(n: tag_val))); |
| 208 | } |
| 209 | } |
| 210 | return properties; |
| 211 | } |
| 212 | |
| 213 | inline optional<mapbox::geometry::identifier> const& feature::getID() const { |
| 214 | return id; |
| 215 | } |
| 216 | |
| 217 | inline std::uint32_t feature::getExtent() const { |
| 218 | return layer_.getExtent(); |
| 219 | } |
| 220 | |
| 221 | inline std::uint32_t feature::getVersion() const { |
| 222 | return layer_.getVersion(); |
| 223 | } |
| 224 | |
| 225 | template <typename GeometryCollectionType> |
| 226 | GeometryCollectionType feature::getGeometries(float scale) const { |
| 227 | std::uint8_t cmd = 1; |
| 228 | std::uint32_t length = 0; |
| 229 | std::int64_t x = 0; |
| 230 | std::int64_t y = 0; |
| 231 | |
| 232 | GeometryCollectionType paths; |
| 233 | |
| 234 | paths.emplace_back(); |
| 235 | |
| 236 | auto start_itr = geometry_iter.begin(); |
| 237 | const auto end_itr = geometry_iter.end(); |
| 238 | bool first = true; |
| 239 | std::uint32_t len_reserve = 0; |
| 240 | std::size_t = 0; |
| 241 | if (type == GeomType::LINESTRING) { |
| 242 | extra_coords = 1; |
| 243 | } else if (type == GeomType::POLYGON) { |
| 244 | extra_coords = 2; |
| 245 | } |
| 246 | bool is_point = type == GeomType::POINT; |
| 247 | |
| 248 | while (start_itr != end_itr) { |
| 249 | if (length == 0) { |
| 250 | std::uint32_t cmd_length = static_cast<std::uint32_t>(*start_itr++); |
| 251 | cmd = cmd_length & 0x7; |
| 252 | length = len_reserve = cmd_length >> 3; |
| 253 | // Prevents the creation of vector tiles that would cause |
| 254 | // a denial of service from massive over allocation. Protection |
| 255 | // limit is based on the assumption of an int64_t point which is |
| 256 | // 16 bytes in size and wanting to have a maximum of 1 MB of memory |
| 257 | // used. |
| 258 | constexpr std::uint32_t MAX_LENGTH = (1024 * 1024) / 16; |
| 259 | if (len_reserve > MAX_LENGTH) { |
| 260 | len_reserve = MAX_LENGTH; |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | --length; |
| 265 | |
| 266 | if (cmd == CommandType::MOVE_TO || cmd == CommandType::LINE_TO) { |
| 267 | |
| 268 | if (is_point) { |
| 269 | if (first && cmd == CommandType::MOVE_TO) { |
| 270 | // note: this invalidates pointers. So we always |
| 271 | // dynamically get the path with paths.back() |
| 272 | paths.reserve(len_reserve); |
| 273 | first = false; |
| 274 | } |
| 275 | } else { |
| 276 | if (first && cmd == CommandType::LINE_TO) { |
| 277 | paths.back().reserve(len_reserve + extra_coords); |
| 278 | first = false; |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | if (cmd == CommandType::MOVE_TO && !paths.back().empty()) { |
| 283 | if (paths.back().size() < paths.back().capacity()) { |
| 284 | // Assuming we had an invalid length before |
| 285 | // lets shrink to fit, just to make sure |
| 286 | // we don't have a large capacity vector |
| 287 | // just wasting memory |
| 288 | paths.back().shrink_to_fit(); |
| 289 | } |
| 290 | paths.emplace_back(); |
| 291 | if (!is_point) { |
| 292 | first = true; |
| 293 | } |
| 294 | } |
| 295 | |
| 296 | x += protozero::decode_zigzag32(value: static_cast<std::uint32_t>(*start_itr++)); |
| 297 | y += protozero::decode_zigzag32(value: static_cast<std::uint32_t>(*start_itr++)); |
| 298 | float px = ::roundf(x: static_cast<float>(x) * scale); |
| 299 | float py = ::roundf(x: static_cast<float>(y) * scale); |
| 300 | static const float max_coord = static_cast<float>(std::numeric_limits<typename GeometryCollectionType::coordinate_type>::max()); |
| 301 | static const float min_coord = static_cast<float>(std::numeric_limits<typename GeometryCollectionType::coordinate_type>::min()); |
| 302 | |
| 303 | if (px > max_coord || |
| 304 | px < min_coord || |
| 305 | py > max_coord || |
| 306 | py < min_coord |
| 307 | ) { |
| 308 | std::runtime_error("paths outside valid range of coordinate_type" ); |
| 309 | } else { |
| 310 | paths.back().emplace_back( |
| 311 | static_cast<typename GeometryCollectionType::coordinate_type>(px), |
| 312 | static_cast<typename GeometryCollectionType::coordinate_type>(py)); |
| 313 | } |
| 314 | } else if (cmd == CommandType::CLOSE) { |
| 315 | if (!paths.back().empty()) { |
| 316 | paths.back().push_back(paths.back()[0]); |
| 317 | } |
| 318 | length = 0; |
| 319 | } else { |
| 320 | throw std::runtime_error("unknown command" ); |
| 321 | } |
| 322 | } |
| 323 | if (paths.size() < paths.capacity()) { |
| 324 | // Assuming we had an invalid length before |
| 325 | // lets shrink to fit, just to make sure |
| 326 | // we don't have a large capacity vector |
| 327 | // just wasting memory |
| 328 | paths.shrink_to_fit(); |
| 329 | } |
| 330 | #if defined(DEBUG) |
| 331 | for (auto const& p : paths) { |
| 332 | assert(p.size() == p.capacity()); |
| 333 | } |
| 334 | #endif |
| 335 | return paths; |
| 336 | } |
| 337 | |
| 338 | inline buffer::buffer(std::string const& data) |
| 339 | : layers() { |
| 340 | protozero::pbf_reader data_reader(data); |
| 341 | while (data_reader.next(next_tag: TileType::LAYERS)) { |
| 342 | const protozero::data_view layer_view = data_reader.get_view(); |
| 343 | protozero::pbf_reader layer_reader(layer_view); |
| 344 | std::string name; |
| 345 | bool has_name = false; |
| 346 | while (layer_reader.next(next_tag: LayerType::NAME)) { |
| 347 | name = layer_reader.get_string(); |
| 348 | has_name = true; |
| 349 | } |
| 350 | if (!has_name) { |
| 351 | throw std::runtime_error("Layer missing name" ); |
| 352 | } |
| 353 | layers.emplace(args&: name, args: layer_view); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | inline std::vector<std::string> buffer::layerNames() const { |
| 358 | std::vector<std::string> names; |
| 359 | names.reserve(n: layers.size()); |
| 360 | for (auto const& layer : layers) { |
| 361 | names.emplace_back(args: layer.first); |
| 362 | } |
| 363 | return names; |
| 364 | } |
| 365 | |
| 366 | inline layer buffer::getLayer(const std::string& name) const { |
| 367 | auto layer_it = layers.find(x: name); |
| 368 | if (layer_it == layers.end()) { |
| 369 | throw std::runtime_error(std::string("no layer by the name of '" )+name+"'" ); |
| 370 | } |
| 371 | return layer(layer_it->second); |
| 372 | } |
| 373 | |
| 374 | inline layer::layer(protozero::data_view const& layer_view) : |
| 375 | name(), |
| 376 | version(1), |
| 377 | extent(4096), |
| 378 | keysMap(), |
| 379 | keys(), |
| 380 | values(), |
| 381 | features() |
| 382 | { |
| 383 | bool has_name = false; |
| 384 | bool has_extent = false; |
| 385 | bool has_version = false; |
| 386 | protozero::pbf_reader layer_pbf(layer_view); |
| 387 | while (layer_pbf.next()) { |
| 388 | switch (layer_pbf.tag()) { |
| 389 | case LayerType::NAME: |
| 390 | { |
| 391 | name = layer_pbf.get_string(); |
| 392 | has_name = true; |
| 393 | } |
| 394 | break; |
| 395 | case LayerType::FEATURES: |
| 396 | { |
| 397 | features.push_back(x: layer_pbf.get_view()); |
| 398 | } |
| 399 | break; |
| 400 | case LayerType::KEYS: |
| 401 | { |
| 402 | // We want to keep the keys in the order of the vector tile |
| 403 | // https://github.com/mapbox/mapbox-gl-native/pull/5183 |
| 404 | auto iter = keysMap.emplace(args: layer_pbf.get_string(), args: keysMap.size()); |
| 405 | keys.emplace_back(args: std::reference_wrapper<const std::string>(iter.first->first)); |
| 406 | } |
| 407 | break; |
| 408 | case LayerType::VALUES: |
| 409 | { |
| 410 | values.emplace_back(args: layer_pbf.get_view()); |
| 411 | } |
| 412 | break; |
| 413 | case LayerType::EXTENT: |
| 414 | { |
| 415 | extent = layer_pbf.get_uint32(); |
| 416 | has_extent = true; |
| 417 | } |
| 418 | break; |
| 419 | case LayerType::VERSION: |
| 420 | { |
| 421 | version = layer_pbf.get_uint32(); |
| 422 | has_version = true; |
| 423 | } |
| 424 | break; |
| 425 | default: |
| 426 | { |
| 427 | layer_pbf.skip(); |
| 428 | } |
| 429 | break; |
| 430 | } |
| 431 | } |
| 432 | if (!has_version || !has_name || !has_extent) { |
| 433 | std::string msg("missing required field:" ); |
| 434 | if (!has_version) { |
| 435 | msg += " version " ; |
| 436 | } |
| 437 | if (!has_extent) { |
| 438 | msg += " extent " ; |
| 439 | } |
| 440 | if (!has_name) { |
| 441 | msg += " name" ; |
| 442 | } |
| 443 | throw std::runtime_error(msg.c_str()); |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | inline protozero::data_view const& layer::getFeature(std::size_t i) const { |
| 448 | return features.at(n: i); |
| 449 | } |
| 450 | |
| 451 | inline std::string const& layer::getName() const { |
| 452 | return name; |
| 453 | } |
| 454 | |
| 455 | }} // namespace mapbox/vector_tile |
| 456 | |