cf89d56ac Release 1.1.0 6defc33ea Update changelog 8480ce447 Use basic_pbf_builder to make buffer type configurable 25fbe0caf Travis: Disable test that fails with out of memory error f6741c19a Travis: Test on different architectures 598031339 Explicitly return 0 or 1 in example code, don't use std::exit dd8d315ea Remove virtual inheritance in builder f8511cc9e Fix another unused variable 4002414f0 Switch to catch2 for testing 9bb27a31f Avoid unused variable warning b04163da6 Use std::array instead of C array 4701b6a57 Make string non-const so the return doesn't copy 454f7d711 Fix clang-tidy config 6105d7a62 "Modernize" travis config 7fefbf932 Use try/catch in main() b3a62df21 Various fixes based on clang-tidy reports 83f784641 Use uppercase letters for numeric literals 23fd5f5c9 CMake config: Also look for newer clang-tidy versions 25ba7a7f6 Remove unnecessary ref qualifier f1cc1728a Doxygen: Remove config settings not available in newer versions 0a0afc97d Fix doc. 630dc80bc fix docs e022ba387 Call commit on finished features. 5d3f6eaf9 Remember version in layer_builder_impl. Add accessor functions. bc9d5f977 Remove comment that doesn't apply (any more). e1618d063 Update appveyor config to use more recent visual studio version. e6f9e3e62 Remove unnecessary workaround in Appveyor config. e1df3894c Remove xcode6.4 build soon to be removed from travis. 808a7b6a1 Release 1.0.3 d4faa2fb0 catch exceptions in vtzero-stats 291572505 Add overload of copy_properties function using a property_manager. 189caf4b8 Use new copy_id() function in advanced documentation. 184bb33e1 Add feature::for_each_property_indexes() member function. 3e296a36f Bugfix: Wrong iterator was checked in assert. 1115c0910 Update submodule to use newest mvt-fixtures. f32e73893 Explicitly initialize member (needed by older GCCs). 8d37928ca More tests. bf68443be Add copy_properties() helper function to feature builders. 96ed8cbd7 Add copy_id() helper function to builders. b166fcc3a Document that layer class uses mutable. 8609cdf96 Add missing word 3484299f2 Release 1.0.2 6d3dd8940 Bugfix: layer_builder::add_feature() now commits features it adds. 8da72723f Update links in change log. git-subtree-dir: third_party/vtzero git-subtree-split: cf89d56ac22eee0a252aab8d2e87344e4ce73d70
316 lines
11 KiB
C++
316 lines
11 KiB
C++
#ifndef VTZERO_FEATURE_HPP
|
|
#define VTZERO_FEATURE_HPP
|
|
|
|
/*****************************************************************************
|
|
|
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
|
|
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
|
documentation.
|
|
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* @file feature.hpp
|
|
*
|
|
* @brief Contains the feature class.
|
|
*/
|
|
|
|
#include "exception.hpp"
|
|
#include "property.hpp"
|
|
#include "property_value.hpp"
|
|
#include "types.hpp"
|
|
|
|
#include <protozero/pbf_message.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <iterator>
|
|
#include <utility>
|
|
|
|
namespace vtzero {
|
|
|
|
class layer;
|
|
|
|
/**
|
|
* A feature according to spec 4.2.
|
|
*
|
|
* Note that a feature will internally contain a pointer to the layer it
|
|
* came from. The layer has to stay valid as long as the feature is used.
|
|
*/
|
|
class feature {
|
|
|
|
using uint32_it_range = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>;
|
|
|
|
const layer* m_layer = nullptr;
|
|
uint64_t m_id = 0; // defaults to 0, see https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L32
|
|
uint32_it_range m_properties{};
|
|
protozero::pbf_reader::const_uint32_iterator m_property_iterator{};
|
|
std::size_t m_num_properties = 0;
|
|
data_view m_geometry{};
|
|
GeomType m_geometry_type = GeomType::UNKNOWN; // defaults to UNKNOWN, see https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L41
|
|
bool m_has_id = false;
|
|
|
|
public:
|
|
|
|
/**
|
|
* Construct an invalid feature object.
|
|
*/
|
|
feature() = default;
|
|
|
|
/**
|
|
* Construct a feature object.
|
|
*
|
|
* @throws format_exception if the layer data is ill-formed.
|
|
*/
|
|
feature(const layer* layer, const data_view data) :
|
|
m_layer(layer) {
|
|
vtzero_assert(layer);
|
|
vtzero_assert(data.data());
|
|
|
|
protozero::pbf_message<detail::pbf_feature> reader{data};
|
|
|
|
while (reader.next()) {
|
|
switch (reader.tag_and_type()) {
|
|
case protozero::tag_and_type(detail::pbf_feature::id, protozero::pbf_wire_type::varint):
|
|
m_id = reader.get_uint64();
|
|
m_has_id = true;
|
|
break;
|
|
case protozero::tag_and_type(detail::pbf_feature::tags, protozero::pbf_wire_type::length_delimited):
|
|
if (m_properties.begin() != protozero::pbf_reader::const_uint32_iterator{}) {
|
|
throw format_exception{"Feature has more than one tags field"};
|
|
}
|
|
m_properties = reader.get_packed_uint32();
|
|
m_property_iterator = m_properties.begin();
|
|
break;
|
|
case protozero::tag_and_type(detail::pbf_feature::type, protozero::pbf_wire_type::varint): {
|
|
const auto type = reader.get_enum();
|
|
// spec 4.3.4 "Geometry Types"
|
|
if (type < 0 || type > 3) {
|
|
throw format_exception{"Unknown geometry type (spec 4.3.4)"};
|
|
}
|
|
m_geometry_type = static_cast<GeomType>(type);
|
|
}
|
|
break;
|
|
case protozero::tag_and_type(detail::pbf_feature::geometry, protozero::pbf_wire_type::length_delimited):
|
|
if (!m_geometry.empty()) {
|
|
throw format_exception{"Feature has more than one geometry field"};
|
|
}
|
|
m_geometry = reader.get_view();
|
|
break;
|
|
default:
|
|
reader.skip(); // ignore unknown fields
|
|
}
|
|
}
|
|
|
|
// spec 4.2 "A feature MUST contain a geometry field."
|
|
if (m_geometry.empty()) {
|
|
throw format_exception{"Missing geometry field in feature (spec 4.2)"};
|
|
}
|
|
|
|
const auto size = m_properties.size();
|
|
if (size % 2 != 0) {
|
|
throw format_exception{"unpaired property key/value indexes (spec 4.4)"};
|
|
}
|
|
m_num_properties = size / 2;
|
|
}
|
|
|
|
/**
|
|
* Is this a valid feature? Valid features are those not created from
|
|
* the default constructor.
|
|
*
|
|
* Complexity: Constant.
|
|
*/
|
|
bool valid() const noexcept {
|
|
return m_geometry.data() != nullptr;
|
|
}
|
|
|
|
/**
|
|
* Is this a valid feature? Valid features are those not created from
|
|
* the default constructor.
|
|
*
|
|
* Complexity: Constant.
|
|
*/
|
|
explicit operator bool() const noexcept {
|
|
return valid();
|
|
}
|
|
|
|
/**
|
|
* The ID of this feature. According to the spec IDs should be unique
|
|
* in a layer if they are set (spec 4.2).
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* Always returns 0 for invalid features.
|
|
*/
|
|
uint64_t id() const noexcept {
|
|
return m_id;
|
|
}
|
|
|
|
/**
|
|
* Does this feature have an ID?
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* Always returns false for invalid features.
|
|
*/
|
|
bool has_id() const noexcept {
|
|
return m_has_id;
|
|
}
|
|
|
|
/**
|
|
* The geometry type of this feature.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* Always returns GeomType::UNKNOWN for invalid features.
|
|
*/
|
|
GeomType geometry_type() const noexcept {
|
|
return m_geometry_type;
|
|
}
|
|
|
|
/**
|
|
* Get the geometry of this feature.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
*/
|
|
vtzero::geometry geometry() const noexcept {
|
|
vtzero_assert_in_noexcept_function(valid());
|
|
return {m_geometry, m_geometry_type};
|
|
}
|
|
|
|
/**
|
|
* Returns true if this feature doesn't have any properties.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* Always returns true for invalid features.
|
|
*/
|
|
bool empty() const noexcept {
|
|
return m_num_properties == 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of properties in this feature.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* Always returns 0 for invalid features.
|
|
*/
|
|
std::size_t num_properties() const noexcept {
|
|
return m_num_properties;
|
|
}
|
|
|
|
/**
|
|
* Get the next property in this feature.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* @returns The next property or the invalid property if there are no
|
|
* more properties.
|
|
* @throws format_exception if the feature data is ill-formed.
|
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
|
* @pre @code valid() @endcode
|
|
*/
|
|
property next_property();
|
|
|
|
/**
|
|
* Get the indexes into the key/value table for the next property in
|
|
* this feature.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* @returns The next index_value_pair or an invalid index_value_pair
|
|
* if there are no more properties.
|
|
* @throws format_exception if the feature data is ill-formed.
|
|
* @throws out_of_range_exception if the key or value index is not
|
|
* within the range of indexes in the layer key/value table.
|
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
|
* @pre @code valid() @endcode
|
|
*/
|
|
index_value_pair next_property_indexes();
|
|
|
|
/**
|
|
* Reset the property iterator. The next time next_property() or
|
|
* next_property_indexes() is called, it will begin from the first
|
|
* property again.
|
|
*
|
|
* Complexity: Constant.
|
|
*
|
|
* @pre @code valid() @endcode
|
|
*/
|
|
void reset_property() noexcept {
|
|
vtzero_assert_in_noexcept_function(valid());
|
|
m_property_iterator = m_properties.begin();
|
|
}
|
|
|
|
/**
|
|
* Call a function for each property of this feature.
|
|
*
|
|
* @tparam TFunc The type of the function. It must take a single
|
|
* argument of type property&& and return a bool. If the
|
|
* function returns false, the iteration will be stopped.
|
|
* @param func The function to call.
|
|
* @returns true if the iteration was completed and false otherwise.
|
|
* @pre @code valid() @endcode
|
|
*/
|
|
template <typename TFunc>
|
|
bool for_each_property(TFunc&& func) const;
|
|
|
|
/**
|
|
* Call a function for each key/value index of this feature.
|
|
*
|
|
* @tparam TFunc The type of the function. It must take a single
|
|
* argument of type index_value_pair&& and return a bool.
|
|
* If the function returns false, the iteration will be stopped.
|
|
* @param func The function to call.
|
|
* @returns true if the iteration was completed and false otherwise.
|
|
* @pre @code valid() @endcode
|
|
*/
|
|
template <typename TFunc>
|
|
bool for_each_property_indexes(TFunc&& func) const;
|
|
|
|
}; // class feature
|
|
|
|
/**
|
|
* Create some kind of mapping from property keys to property values.
|
|
*
|
|
* This can be used to read all properties into a std::map or similar
|
|
* object.
|
|
*
|
|
* @tparam TMap Map type (std::map, std::unordered_map, ...) Must support
|
|
* the emplace() method.
|
|
* @tparam TKey Key type, usually the key of the map type. The data_view
|
|
* of the property key is converted to this type before
|
|
* adding it to the map.
|
|
* @tparam TValue Value type, usally the value of the map type. The
|
|
* property_value is converted to this type before
|
|
* adding it to the map.
|
|
* @tparam TMapping A struct derived from property_value_mapping with the
|
|
* mapping for vtzero property value types to TValue-constructing
|
|
* types. (See convert_property_value() for details.)
|
|
* @param feature The feature to get the properties from.
|
|
* @returns An object of type TMap with all the properties.
|
|
* @pre @code feature.valid() @endcode
|
|
*/
|
|
template <typename TMap,
|
|
typename TKey = typename TMap::key_type,
|
|
typename TValue = typename TMap::mapped_type,
|
|
typename TMapping = property_value_mapping>
|
|
TMap create_properties_map(const vtzero::feature& feature) {
|
|
TMap map;
|
|
|
|
feature.for_each_property([&map](const property& p) {
|
|
map.emplace(TKey(p.key()), convert_property_value<TValue, TMapping>(p.value()));
|
|
return true;
|
|
});
|
|
|
|
return map;
|
|
}
|
|
|
|
} // namespace vtzero
|
|
|
|
#endif // VTZERO_FEATURE_HPP
|