mirror of
https://github.com/systemed/tilemaker
synced 2025-02-22 06:24:08 +01:00
267 lines
9.4 KiB
C++
267 lines
9.4 KiB
C++
#ifndef VTZERO_BUILDER_IMPL_HPP
|
|
#define VTZERO_BUILDER_IMPL_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 builder_impl.hpp
|
|
*
|
|
* @brief Contains classes internal to the builder.
|
|
*/
|
|
|
|
#include "encoded_property_value.hpp"
|
|
#include "property_value.hpp"
|
|
#include "types.hpp"
|
|
|
|
#include <protozero/basic_pbf_builder.hpp>
|
|
#include <protozero/pbf_message.hpp>
|
|
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
namespace vtzero {
|
|
|
|
namespace detail {
|
|
|
|
class layer_builder_impl {
|
|
|
|
// If this layer is copied from an existing layer, this points
|
|
// to the data of the original layer. For newly built layers,
|
|
// this is empty.
|
|
data_view m_data_view{};
|
|
|
|
// Buffer containing the encoded layer metadata and features
|
|
std::string m_data;
|
|
|
|
// Buffer containing the encoded keys table
|
|
std::string m_keys_data;
|
|
|
|
// Buffer containing the encoded values table
|
|
std::string m_values_data;
|
|
|
|
protozero::pbf_builder<detail::pbf_layer> m_pbf_message_layer{};
|
|
protozero::pbf_builder<detail::pbf_layer> m_pbf_message_keys{};
|
|
protozero::pbf_builder<detail::pbf_layer> m_pbf_message_values{};
|
|
|
|
// The number of features in the layer
|
|
std::size_t m_num_features = 0;
|
|
|
|
// Vector tile spec version
|
|
uint32_t m_version = 0;
|
|
|
|
// The number of keys in the keys table
|
|
uint32_t m_num_keys = 0;
|
|
|
|
// The number of values in the values table
|
|
uint32_t m_num_values = 0;
|
|
|
|
// Below this value, no index will be used to find entries in the
|
|
// key/value tables. This number is based on some initial
|
|
// benchmarking but probably needs some tuning.
|
|
// See also https://github.com/mapbox/vtzero/issues/30
|
|
static constexpr const uint32_t max_entries_flat = 20;
|
|
|
|
using map_type = std::unordered_map<std::string, index_value>;
|
|
map_type m_keys_index;
|
|
map_type m_values_index;
|
|
|
|
static index_value find_in_table(const data_view text, const std::string& data) {
|
|
uint32_t index = 0;
|
|
protozero::pbf_message<detail::pbf_layer> pbf_table{data};
|
|
|
|
while (pbf_table.next()) {
|
|
const auto v = pbf_table.get_view();
|
|
if (v == text) {
|
|
return index_value{index};
|
|
}
|
|
++index;
|
|
}
|
|
|
|
return index_value{};
|
|
}
|
|
|
|
// Read the key or value table and populate an index from its
|
|
// entries. This is done once the table becomes too large to do
|
|
// linear search in it.
|
|
static void populate_index(const std::string& data, map_type& map) {
|
|
uint32_t index = 0;
|
|
protozero::pbf_message<detail::pbf_layer> pbf_table{data};
|
|
|
|
while (pbf_table.next()) {
|
|
map[pbf_table.get_string()] = index++;
|
|
}
|
|
}
|
|
|
|
index_value add_value_without_dup_check(const data_view text) {
|
|
m_pbf_message_values.add_string(detail::pbf_layer::values, text);
|
|
return m_num_values++;
|
|
}
|
|
|
|
index_value add_value(const data_view text) {
|
|
const auto index = find_in_values_table(text);
|
|
if (index.valid()) {
|
|
return index;
|
|
}
|
|
return add_value_without_dup_check(text);
|
|
}
|
|
|
|
index_value find_in_keys_table(const data_view text) {
|
|
if (m_num_keys < max_entries_flat) {
|
|
return find_in_table(text, m_keys_data);
|
|
}
|
|
|
|
if (m_keys_index.empty()) {
|
|
populate_index(m_keys_data, m_keys_index);
|
|
}
|
|
|
|
auto& v = m_keys_index[std::string(text)];
|
|
if (!v.valid()) {
|
|
v = add_key_without_dup_check(text);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
index_value find_in_values_table(const data_view text) {
|
|
if (m_num_values < max_entries_flat) {
|
|
return find_in_table(text, m_values_data);
|
|
}
|
|
|
|
if (m_values_index.empty()) {
|
|
populate_index(m_values_data, m_values_index);
|
|
}
|
|
|
|
auto& v = m_values_index[std::string(text)];
|
|
if (!v.valid()) {
|
|
v = add_value_without_dup_check(text);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
public:
|
|
|
|
// This layer should be a copy of an existing layer
|
|
explicit layer_builder_impl(const data_view data) :
|
|
m_data_view(data) {
|
|
}
|
|
|
|
// This layer is being created from scratch
|
|
template <typename TString>
|
|
layer_builder_impl(TString&& name, uint32_t version, uint32_t extent) :
|
|
m_pbf_message_layer(m_data),
|
|
m_pbf_message_keys(m_keys_data),
|
|
m_pbf_message_values(m_values_data),
|
|
m_version(version) {
|
|
m_pbf_message_layer.add_uint32(detail::pbf_layer::version, version);
|
|
m_pbf_message_layer.add_string(detail::pbf_layer::name, std::forward<TString>(name));
|
|
m_pbf_message_layer.add_uint32(detail::pbf_layer::extent, extent);
|
|
}
|
|
|
|
~layer_builder_impl() noexcept = default;
|
|
|
|
layer_builder_impl(const layer_builder_impl&) = delete;
|
|
layer_builder_impl& operator=(const layer_builder_impl&) = delete;
|
|
|
|
layer_builder_impl(layer_builder_impl&&) = default;
|
|
layer_builder_impl& operator=(layer_builder_impl&&) = default;
|
|
|
|
uint32_t version() const noexcept {
|
|
return m_version;
|
|
}
|
|
|
|
index_value add_key_without_dup_check(const data_view text) {
|
|
m_pbf_message_keys.add_string(detail::pbf_layer::keys, text);
|
|
return m_num_keys++;
|
|
}
|
|
|
|
index_value add_key(const data_view text) {
|
|
const auto index = find_in_keys_table(text);
|
|
if (index.valid()) {
|
|
return index;
|
|
}
|
|
return add_key_without_dup_check(text);
|
|
}
|
|
|
|
index_value add_value_without_dup_check(const property_value value) {
|
|
return add_value_without_dup_check(value.data());
|
|
}
|
|
|
|
index_value add_value_without_dup_check(const encoded_property_value& value) {
|
|
return add_value_without_dup_check(value.data());
|
|
}
|
|
|
|
index_value add_value(const property_value value) {
|
|
return add_value(value.data());
|
|
}
|
|
|
|
index_value add_value(const encoded_property_value& value) {
|
|
return add_value(value.data());
|
|
}
|
|
|
|
const std::string& data() const noexcept {
|
|
return m_data;
|
|
}
|
|
|
|
const std::string& keys_data() const noexcept {
|
|
return m_keys_data;
|
|
}
|
|
|
|
const std::string& values_data() const noexcept {
|
|
return m_values_data;
|
|
}
|
|
|
|
protozero::pbf_builder<detail::pbf_layer>& message() noexcept {
|
|
return m_pbf_message_layer;
|
|
}
|
|
|
|
void increment_feature_count() noexcept {
|
|
++m_num_features;
|
|
}
|
|
|
|
std::size_t estimated_size() const {
|
|
if (m_data_view.data()) {
|
|
// This is a layer created as copy from an existing layer
|
|
constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8;
|
|
return m_data_view.size() + estimated_overhead_for_pbf_encoding;
|
|
}
|
|
|
|
// This is a layer created from scratch
|
|
constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8;
|
|
return data().size() +
|
|
keys_data().size() +
|
|
values_data().size() +
|
|
estimated_overhead_for_pbf_encoding;
|
|
}
|
|
|
|
template <typename TBuffer>
|
|
void build(protozero::basic_pbf_builder<TBuffer, detail::pbf_tile>& pbf_tile_builder) const {
|
|
if (m_data_view.data()) {
|
|
// This is a layer created as copy from an existing layer
|
|
pbf_tile_builder.add_bytes(detail::pbf_tile::layers, m_data_view);
|
|
return;
|
|
}
|
|
|
|
// This is a layer created from scratch
|
|
if (m_num_features > 0) {
|
|
pbf_tile_builder.add_bytes_vectored(detail::pbf_tile::layers,
|
|
data(),
|
|
keys_data(),
|
|
values_data());
|
|
}
|
|
}
|
|
|
|
}; // class layer_builder_impl
|
|
|
|
} // namespace detail
|
|
|
|
} // namespace vtzero
|
|
|
|
#endif // VTZERO_BUILDER_IMPL_HPP
|