/***************************************************************************** Example program for vtzero library. vtzero-check - Check vector tiles for validity *****************************************************************************/ #include "utils.hpp" #include #include #include #include #include class result { int m_return_code = 0; public: void has_warning() noexcept { if (m_return_code < 1) { m_return_code = 1; } } void has_error() noexcept { if (m_return_code < 2) { m_return_code = 2; } } void has_fatal_error() noexcept { if (m_return_code < 3) { m_return_code = 3; } } int return_code() const noexcept { return m_return_code; } } result; class CheckGeomHandler { vtzero::point m_prev_point{}; int m_layer_num; int m_feature_num; int64_t m_extent; bool m_is_first_point = false; int m_count = 0; void print_context() const { std::cerr << " in layer " << m_layer_num << " in feature " << m_feature_num << " in geometry " << m_count << ": "; } void print_error(const char* message) const { result.has_error(); std::cerr << "Error"; print_context(); std::cerr << message << '\n'; } void print_warning(const char* message) const { result.has_warning(); std::cerr << "Warning"; print_context(); std::cerr << message << '\n'; } void check_point_location(const vtzero::point point) const { if (point.x < -m_extent || point.y < -m_extent || point.x > 2 * m_extent || point.y > 2 * m_extent) { print_warning("point waaaay beyond the extent"); } } public: CheckGeomHandler(uint32_t extent, int layer_num, int feature_num) : m_layer_num(layer_num), m_feature_num(feature_num), m_extent(static_cast(extent)) { } // ---------------------------------------------------------------------- void points_begin(const uint32_t /*count*/) const { } void points_point(const vtzero::point point) const { check_point_location(point); } void points_end() const { } // ---------------------------------------------------------------------- void linestring_begin(const uint32_t count) { if (count < 2) { print_error("Not enough points in linestring"); } m_is_first_point = true; } void linestring_point(const vtzero::point point) { if (m_is_first_point) { m_is_first_point = false; } else { if (point == m_prev_point) { print_error("Duplicate point in linestring"); } } m_prev_point = point; check_point_location(point); } void linestring_end() { ++m_count; } // ---------------------------------------------------------------------- void ring_begin(const uint32_t count) { if (count < 4) { print_error("Not enough points in ring"); } m_is_first_point = true; } void ring_point(const vtzero::point point) { if (m_is_first_point) { m_is_first_point = false; } else { if (point == m_prev_point) { print_error("Duplicate point in ring"); } } m_prev_point = point; check_point_location(point); } void ring_end(const vtzero::ring_type rt) { if (rt == vtzero::ring_type::invalid) { print_error("Invalid ring with area 0"); } if (m_count == 0 && rt != vtzero::ring_type::outer) { print_error("First ring isn't an outer ring"); } ++m_count; } }; // class CheckGeomHandler int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " TILE\n"; return 1; } std::string input_file{argv[1]}; const auto data = read_file(input_file); std::set layer_names; vtzero::vector_tile tile{data}; int layer_num = 0; int feature_num = -1; try { while (auto layer = tile.next_layer()) { if (layer.name().empty()) { std::cerr << "Error in layer " << layer_num << ": name is empty (spec 4.1)\n"; result.has_error(); } std::string name(layer.name()); if (layer_names.count(name) > 0) { std::cerr << "Error in layer " << layer_num << ": name is duplicate of previous layer ('" << name << "') (spec 4.1)\n"; result.has_error(); } layer_names.insert(name); feature_num = 0; while (auto feature = layer.next_feature()) { CheckGeomHandler handler{layer.extent(), layer_num, feature_num}; vtzero::decode_geometry(feature.geometry(), handler); ++feature_num; } if (feature_num == 0) { std::cerr << "Warning: No features in layer " << layer_num << " (spec 4.1)\n"; result.has_warning(); } feature_num = -1; ++layer_num; } if (layer_num == 0) { std::cerr << "Warning: No layers in vector tile (spec 4.1)\n"; result.has_warning(); } } catch (const std::exception& e) { std::cerr << "Fatal error in layer " << layer_num; if (feature_num >= 0) { std::cerr << " in feature " << feature_num; } std::cerr << ": " << e.what() << '\n'; result.has_fatal_error(); } return result.return_code(); }