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
1016 lines
32 KiB
C++
1016 lines
32 KiB
C++
|
|
#include <test.hpp>
|
|
|
|
#include <vtzero/builder.hpp>
|
|
#include <vtzero/geometry.hpp>
|
|
#include <vtzero/vector_tile.hpp>
|
|
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
static std::string open_tile(const std::string& path) {
|
|
const auto* fixtures_dir = std::getenv("FIXTURES_DIR");
|
|
if (fixtures_dir == nullptr) {
|
|
std::cerr << "Set FIXTURES_DIR environment variable to the directory where the mvt fixtures are!\n";
|
|
std::exit(2);
|
|
}
|
|
|
|
std::ifstream stream{std::string{fixtures_dir} + "/" + path,
|
|
std::ios_base::in|std::ios_base::binary};
|
|
if (!stream.is_open()) {
|
|
throw std::runtime_error{"could not open: '" + path + "'"};
|
|
}
|
|
|
|
std::string message{std::istreambuf_iterator<char>(stream.rdbuf()),
|
|
std::istreambuf_iterator<char>()};
|
|
|
|
stream.close();
|
|
return message;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
struct point_handler {
|
|
|
|
std::vector<vtzero::point> data{};
|
|
|
|
void points_begin(uint32_t count) {
|
|
data.reserve(count);
|
|
}
|
|
|
|
void points_point(const vtzero::point point) {
|
|
data.push_back(point);
|
|
}
|
|
|
|
void points_end() const noexcept {
|
|
}
|
|
|
|
}; // struct point_handler
|
|
|
|
struct linestring_handler {
|
|
|
|
std::vector<std::vector<vtzero::point>> data{};
|
|
|
|
void linestring_begin(uint32_t count) {
|
|
data.emplace_back();
|
|
data.back().reserve(count);
|
|
}
|
|
|
|
void linestring_point(const vtzero::point point) {
|
|
data.back().push_back(point);
|
|
}
|
|
|
|
void linestring_end() const noexcept {
|
|
}
|
|
|
|
}; // struct linestring_handler
|
|
|
|
struct polygon_handler {
|
|
|
|
std::vector<std::vector<vtzero::point>> data{};
|
|
|
|
void ring_begin(uint32_t count) {
|
|
data.emplace_back();
|
|
data.back().reserve(count);
|
|
}
|
|
|
|
void ring_point(const vtzero::point point) {
|
|
data.back().push_back(point);
|
|
}
|
|
|
|
void ring_end(vtzero::ring_type /*dummy*/) const noexcept {
|
|
}
|
|
|
|
}; // struct polygon_handler
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
struct geom_handler {
|
|
|
|
std::vector<vtzero::point> point_data{};
|
|
std::vector<std::vector<vtzero::point>> line_data{};
|
|
|
|
void points_begin(uint32_t count) {
|
|
point_data.reserve(count);
|
|
}
|
|
|
|
void points_point(const vtzero::point point) {
|
|
point_data.push_back(point);
|
|
}
|
|
|
|
void points_end() const noexcept {
|
|
}
|
|
|
|
void linestring_begin(uint32_t count) {
|
|
line_data.emplace_back();
|
|
line_data.back().reserve(count);
|
|
}
|
|
|
|
void linestring_point(const vtzero::point point) {
|
|
line_data.back().push_back(point);
|
|
}
|
|
|
|
void linestring_end() const noexcept {
|
|
}
|
|
|
|
void ring_begin(uint32_t count) {
|
|
line_data.emplace_back();
|
|
line_data.back().reserve(count);
|
|
}
|
|
|
|
void ring_point(const vtzero::point point) {
|
|
line_data.back().push_back(point);
|
|
}
|
|
|
|
void ring_end(vtzero::ring_type /*dummy*/) const noexcept {
|
|
}
|
|
|
|
}; // struct geom_handler
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
static vtzero::feature check_layer(vtzero::vector_tile& tile) {
|
|
REQUIRE_FALSE(tile.empty());
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.name() == "hello");
|
|
REQUIRE(layer.version() == 2);
|
|
REQUIRE(layer.extent() == 4096);
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
return layer.next_feature();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
TEST_CASE("MVT test 001: Empty tile") {
|
|
std::string buffer{open_tile("001/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.empty());
|
|
REQUIRE(tile.count_layers() == 0); // NOLINT(readability-container-size-empty)
|
|
}
|
|
|
|
TEST_CASE("MVT test 002: Tile with single point feature without id") {
|
|
std::string buffer{open_tile("002/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE_FALSE(feature.has_id());
|
|
REQUIRE(feature.id() == 0);
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::POINT);
|
|
|
|
point_handler handler;
|
|
vtzero::decode_point_geometry(feature.geometry(), handler);
|
|
|
|
std::vector<vtzero::point> expected = {{25, 17}};
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 003: Tile with single point with missing geometry type") {
|
|
std::string buffer{open_tile("003/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
REQUIRE(feature.has_id());
|
|
REQUIRE(feature.id() == 1);
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::UNKNOWN);
|
|
}
|
|
|
|
TEST_CASE("MVT test 004: Tile with single point with missing geometry") {
|
|
std::string buffer{open_tile("004/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE_THROWS_AS(check_layer(tile), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 005: Tile with single point with broken tags array") {
|
|
std::string buffer{open_tile("005/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE_FALSE(tile.empty());
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE_FALSE(layer.empty());
|
|
|
|
REQUIRE_THROWS_AS(layer.next_feature(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 006: Tile with single point with invalid GeomType") {
|
|
std::string buffer{open_tile("006/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE_FALSE(layer.empty());
|
|
|
|
REQUIRE_THROWS_AS(layer.next_feature(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 007: Layer version as string instead of as an int") {
|
|
std::string buffer{open_tile("007/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
REQUIRE_THROWS_AS(tile.get_layer(0), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 008: Tile layer extent encoded as string") {
|
|
std::string buffer{open_tile("008/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
REQUIRE_THROWS_AS(tile.next_layer(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 009: Tile layer extent missing") {
|
|
std::string buffer{open_tile("009/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
|
|
REQUIRE(layer.name() == "hello");
|
|
REQUIRE(layer.version() == 2);
|
|
REQUIRE(layer.extent() == 4096);
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.id() == 1);
|
|
}
|
|
|
|
TEST_CASE("MVT test 010: Tile layer value is encoded as int, but pretends to be string") {
|
|
std::string buffer{open_tile("010/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE_FALSE(layer.empty());
|
|
|
|
const auto pv = layer.value(0);
|
|
REQUIRE_THROWS_AS(pv.type(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 011: Tile layer value is encoded as unknown type") {
|
|
std::string buffer{open_tile("011/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
const auto layer = tile.next_layer();
|
|
REQUIRE_FALSE(layer.empty());
|
|
|
|
const auto pv = layer.value(0);
|
|
REQUIRE_THROWS_AS(pv.type(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 012: Unknown layer version") {
|
|
std::string buffer{open_tile("012/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
REQUIRE_THROWS_AS(tile.next_layer(), vtzero::version_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 013: Tile with key in table encoded as int") {
|
|
std::string buffer{open_tile("013/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
REQUIRE_THROWS_AS(tile.next_layer(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 014: Tile layer without a name") {
|
|
std::string buffer{open_tile("014/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
REQUIRE_THROWS_AS(tile.next_layer(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 015: Two layers with the same name") {
|
|
std::string buffer{open_tile("015/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 2);
|
|
|
|
while (const auto layer = tile.next_layer()) {
|
|
REQUIRE(layer.name() == "hello");
|
|
}
|
|
|
|
const auto layer = tile.get_layer_by_name("hello");
|
|
REQUIRE(layer.name() == "hello");
|
|
}
|
|
|
|
TEST_CASE("MVT test 016: Valid unknown geometry") {
|
|
std::string buffer{open_tile("016/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::UNKNOWN);
|
|
}
|
|
|
|
TEST_CASE("MVT test 017: Valid point geometry") {
|
|
std::string buffer{open_tile("017/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE(feature.has_id());
|
|
REQUIRE(feature.id() == 1);
|
|
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::POINT);
|
|
|
|
const std::vector<vtzero::point> expected = {{25, 17}};
|
|
|
|
SECTION("decode_point_geometry") {
|
|
point_handler handler;
|
|
vtzero::decode_point_geometry(feature.geometry(), handler);
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
SECTION("decode_geometry") {
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(feature.geometry(), handler);
|
|
REQUIRE(handler.point_data == expected);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MVT test 018: Valid linestring geometry") {
|
|
std::string buffer = open_tile("018/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::LINESTRING);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{2, 2}, {2,10}, {10, 10}}};
|
|
|
|
SECTION("decode_linestring_geometry") {
|
|
linestring_handler handler;
|
|
vtzero::decode_linestring_geometry(feature.geometry(), handler);
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
SECTION("decode_geometry") {
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(feature.geometry(), handler);
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MVT test 019: Valid polygon geometry") {
|
|
std::string buffer = open_tile("019/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::POLYGON);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{3, 6}, {8,12}, {20, 34}, {3, 6}}};
|
|
|
|
SECTION("deocode_polygon_geometry") {
|
|
polygon_handler handler;
|
|
vtzero::decode_polygon_geometry(feature.geometry(), handler);
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
SECTION("deocode_geometry") {
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(feature.geometry(), handler);
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
}
|
|
|
|
TEST_CASE("MVT test 020: Valid multipoint geometry") {
|
|
std::string buffer = open_tile("020/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::POINT);
|
|
|
|
point_handler handler;
|
|
vtzero::decode_point_geometry(feature.geometry(), handler);
|
|
|
|
const std::vector<vtzero::point> expected = {{5, 7}, {3,2}};
|
|
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 021: Valid multilinestring geometry") {
|
|
std::string buffer = open_tile("021/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::LINESTRING);
|
|
|
|
linestring_handler handler;
|
|
vtzero::decode_linestring_geometry(feature.geometry(), handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{2, 2}, {2,10}, {10, 10}}, {{1,1}, {3, 5}}};
|
|
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 022: Valid multipolygon geometry") {
|
|
std::string buffer = open_tile("022/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
const auto feature = check_layer(tile);
|
|
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::POLYGON);
|
|
|
|
polygon_handler handler;
|
|
vtzero::decode_polygon_geometry(feature.geometry(), handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {
|
|
{{0, 0}, {10, 0}, {10, 10}, {0,10}, {0, 0}},
|
|
{{11, 11}, {20, 11}, {20, 20}, {11, 20}, {11, 11}},
|
|
{{13, 13}, {13, 17}, {17, 17}, {17, 13}, {13, 13}}
|
|
};
|
|
|
|
REQUIRE(handler.data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 023: Invalid layer: missing layer name") {
|
|
std::string buffer = open_tile("023/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
REQUIRE_THROWS_AS(tile.next_layer(), vtzero::format_exception);
|
|
REQUIRE_THROWS_AS(tile.get_layer_by_name("foo"), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 024: Missing layer version") {
|
|
std::string buffer = open_tile("024/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
const auto layer = tile.next_layer();
|
|
REQUIRE(layer.version() == 1);
|
|
}
|
|
|
|
TEST_CASE("MVT test 025: Layer without features") {
|
|
std::string buffer = open_tile("025/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
const auto layer = tile.next_layer();
|
|
REQUIRE(layer.empty());
|
|
REQUIRE(layer.num_features() == 0); // NOLINT(readability-container-size-empty)
|
|
}
|
|
|
|
TEST_CASE("MVT test 026: Extra value type") {
|
|
std::string buffer = open_tile("026/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
const auto feature = layer.next_feature();
|
|
REQUIRE(feature.empty());
|
|
|
|
const auto& table = layer.value_table();
|
|
REQUIRE(table.size() == 1);
|
|
|
|
const auto pvv = table[0];
|
|
REQUIRE(pvv.valid());
|
|
REQUIRE_THROWS_AS(pvv.type(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 027: Layer with unused bool property value") {
|
|
std::string buffer{open_tile("027/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
const auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 0); // NOLINT(readability-container-size-empty)
|
|
|
|
const auto& vtab = layer.value_table();
|
|
REQUIRE(vtab.size() == 1);
|
|
REQUIRE(vtab[0].bool_value());
|
|
}
|
|
|
|
TEST_CASE("MVT test 030: Two geometry fields") {
|
|
std::string buffer = open_tile("030/tile.mvt");
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE_FALSE(layer.empty());
|
|
|
|
REQUIRE_THROWS_AS(layer.next_feature(), vtzero::format_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 032: Layer with single feature with string property value") {
|
|
std::string buffer{open_tile("032/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE_FALSE(tile.empty());
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
auto prop = feature.next_property();
|
|
REQUIRE(prop.key() == "key1");
|
|
REQUIRE(prop.value().string_value() == "i am a string value");
|
|
|
|
feature.reset_property();
|
|
auto ii = feature.next_property_indexes();
|
|
REQUIRE(ii);
|
|
REQUIRE(ii.key().value() == 0);
|
|
REQUIRE(ii.value().value() == 0);
|
|
REQUIRE_FALSE(feature.next_property_indexes());
|
|
|
|
int32_t sum = 0;
|
|
int32_t count = 0;
|
|
feature.for_each_property_indexes([&](vtzero::index_value_pair&& ivp) {
|
|
sum += ivp.key().value();
|
|
sum += ivp.value().value();
|
|
++count;
|
|
return true;
|
|
});
|
|
|
|
REQUIRE(sum == 0);
|
|
REQUIRE(count == 1);
|
|
}
|
|
|
|
TEST_CASE("MVT test 033: Layer with single feature with float property value") {
|
|
std::string buffer{open_tile("033/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
const auto prop = feature.next_property();
|
|
REQUIRE(prop.key() == "key1");
|
|
REQUIRE(prop.value().float_value() == Approx(3.1));
|
|
}
|
|
|
|
TEST_CASE("MVT test 034: Layer with single feature with double property value") {
|
|
std::string buffer{open_tile("034/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
const auto prop = feature.next_property();
|
|
REQUIRE(prop.key() == "key1");
|
|
REQUIRE(prop.value().double_value() == Approx(1.23));
|
|
}
|
|
|
|
TEST_CASE("MVT test 035: Layer with single feature with int property value") {
|
|
std::string buffer{open_tile("035/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
const auto prop = feature.next_property();
|
|
REQUIRE(prop.key() == "key1");
|
|
REQUIRE(prop.value().int_value() == 6);
|
|
}
|
|
|
|
TEST_CASE("MVT test 036: Layer with single feature with uint property value") {
|
|
std::string buffer{open_tile("036/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
const auto prop = feature.next_property();
|
|
REQUIRE(prop.key() == "key1");
|
|
REQUIRE(prop.value().uint_value() == 87948);
|
|
}
|
|
|
|
TEST_CASE("MVT test 037: Layer with single feature with sint property value") {
|
|
std::string buffer{open_tile("037/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
const auto prop = feature.next_property();
|
|
REQUIRE(prop.key() == "key1");
|
|
REQUIRE(prop.value().sint_value() == 87948);
|
|
}
|
|
|
|
TEST_CASE("MVT test 038: Layer with all types of property value") {
|
|
std::string buffer{open_tile("038/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
|
|
const auto& vtab = layer.value_table();
|
|
REQUIRE(vtab.size() == 7);
|
|
REQUIRE(vtab[0].string_value() == "ello");
|
|
REQUIRE(vtab[1].bool_value());
|
|
REQUIRE(vtab[2].int_value() == 6);
|
|
REQUIRE(vtab[3].double_value() == Approx(1.23));
|
|
REQUIRE(vtab[4].float_value() == Approx(3.1));
|
|
REQUIRE(vtab[5].sint_value() == -87948);
|
|
REQUIRE(vtab[6].uint_value() == 87948);
|
|
|
|
REQUIRE_THROWS_AS(vtab[0].bool_value(), vtzero::type_exception);
|
|
REQUIRE_THROWS_AS(vtab[0].int_value(), vtzero::type_exception);
|
|
REQUIRE_THROWS_AS(vtab[0].double_value(), vtzero::type_exception);
|
|
REQUIRE_THROWS_AS(vtab[0].float_value(), vtzero::type_exception);
|
|
REQUIRE_THROWS_AS(vtab[0].sint_value(), vtzero::type_exception);
|
|
REQUIRE_THROWS_AS(vtab[0].uint_value(), vtzero::type_exception);
|
|
REQUIRE_THROWS_AS(vtab[1].string_value(), vtzero::type_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 039: Default values are actually encoded in the tile") {
|
|
std::string buffer{open_tile("039/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.version() == 1);
|
|
REQUIRE(layer.name() == "hello");
|
|
REQUIRE(layer.extent() == 4096);
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.id() == 0);
|
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::UNKNOWN);
|
|
REQUIRE(feature.empty());
|
|
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(feature.geometry(), geom_handler{}), vtzero::geometry_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 040: Feature has tags that point to non-existent Key in the layer.") {
|
|
std::string buffer{open_tile("040/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
REQUIRE_THROWS_AS(feature.next_property(), vtzero::out_of_range_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 040: Feature has tags that point to non-existent Key in the layer decoded using next_property_indexes().") {
|
|
std::string buffer{open_tile("040/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
REQUIRE_THROWS_AS(feature.next_property_indexes(), vtzero::out_of_range_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 041: Tags encoded as floats instead of as ints") {
|
|
std::string buffer{open_tile("041/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
auto feature = layer.next_feature();
|
|
REQUIRE_THROWS_AS(feature.next_property(), vtzero::out_of_range_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 042: Feature has tags that point to non-existent Value in the layer.") {
|
|
std::string buffer{open_tile("042/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
|
|
REQUIRE(tile.count_layers() == 1);
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature.num_properties() == 1);
|
|
REQUIRE_THROWS_AS(feature.next_property(), vtzero::out_of_range_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 043: A layer with six points that all share the same key but each has a unique value.") {
|
|
std::string buffer{open_tile("043/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 6);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
REQUIRE(feature.num_properties() == 1);
|
|
|
|
auto property = feature.next_property();
|
|
REQUIRE(property);
|
|
REQUIRE(property.key() == "poi");
|
|
REQUIRE(property.value().string_value() == "swing");
|
|
|
|
feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
property = feature.next_property();
|
|
REQUIRE(property);
|
|
REQUIRE(property.key() == "poi");
|
|
REQUIRE(property.value().string_value() == "water_fountain");
|
|
}
|
|
|
|
TEST_CASE("MVT test 044: Geometry field begins with a ClosePath command, which is invalid") {
|
|
std::string buffer{open_tile("044/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 045: Invalid point geometry that includes a MoveTo command and only half of the xy coordinates") {
|
|
std::string buffer{open_tile("045/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
REQUIRE_THROWS_WITH(vtzero::decode_geometry(geometry, geom_handler{}), "too few points in geometry");
|
|
}
|
|
|
|
TEST_CASE("MVT test 046: Invalid linestring geometry that includes two points in the same position, which is not OGC valid") {
|
|
std::string buffer{open_tile("046/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{2, 2}, {2, 10}, {2, 10}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 047: Invalid polygon with wrong ClosePath count 2 (must be count 1)") {
|
|
std::string buffer{open_tile("047/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
REQUIRE_THROWS_WITH(vtzero::decode_geometry(geometry, geom_handler{}), "ClosePath command count is not 1");
|
|
}
|
|
|
|
TEST_CASE("MVT test 048: Invalid polygon with wrong ClosePath count 0 (must be count 1)") {
|
|
std::string buffer{open_tile("048/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
REQUIRE_THROWS_WITH(vtzero::decode_geometry(geometry, geom_handler{}), "ClosePath command count is not 1");
|
|
}
|
|
|
|
TEST_CASE("MVT test 049: decoding linestring with int32 overflow in x coordinate") {
|
|
std::string buffer{open_tile("049/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{std::numeric_limits<int32_t>::max(), 0}, {std::numeric_limits<int32_t>::min(), 1}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 050: decoding linestring with int32 overflow in y coordinate") {
|
|
std::string buffer{open_tile("050/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{0, std::numeric_limits<int32_t>::min()}, {-1, std::numeric_limits<int32_t>::max()}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 051: multipoint with a huge count value, useful for ensuring no over-allocation errors. Example error message \"count too large\"") {
|
|
std::string buffer{open_tile("051/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
REQUIRE_THROWS_WITH(vtzero::decode_geometry(geometry, geom_handler{}), "count too large");
|
|
}
|
|
|
|
TEST_CASE("MVT test 052: multipoint with not enough points") {
|
|
std::string buffer{open_tile("052/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
}
|
|
|
|
TEST_CASE("MVT test 053: clipped square (exact extent): a polygon that covers the entire tile to the exact boundary") {
|
|
std::string buffer{open_tile("053/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{0, 0}, {4096, 0}, {4096, 4096}, {0, 4096}, {0, 0}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 054: clipped square (one unit buffer): a polygon that covers the entire tile plus a one unit buffer") {
|
|
std::string buffer{open_tile("054/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{-1, -1}, {4097, -1}, {4097, 4097}, {-1, 4097}, {-1, -1}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 055: clipped square (minus one unit buffer): a polygon that almost covers the entire tile minus one unit buffer") {
|
|
std::string buffer{open_tile("055/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{1, 1}, {4095, 1}, {4095, 4095}, {1, 4095}, {1, 1}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 056: clipped square (large buffer): a polygon that covers the entire tile plus a 200 unit buffer") {
|
|
std::string buffer{open_tile("056/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
|
|
geom_handler handler;
|
|
vtzero::decode_geometry(geometry, handler);
|
|
|
|
const std::vector<std::vector<vtzero::point>> expected = {{{-200, -200}, {4296, -200}, {4296, 4296}, {-200, 4296}, {-200, -200}}};
|
|
REQUIRE(handler.line_data == expected);
|
|
}
|
|
|
|
TEST_CASE("MVT test 057: A point fixture with a gigantic MoveTo command. Can be used to test decoders for memory overallocation situations") {
|
|
std::string buffer{open_tile("057/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
REQUIRE_THROWS_WITH(vtzero::decode_geometry(geometry, geom_handler{}), "count too large");
|
|
}
|
|
|
|
TEST_CASE("MVT test 058: A linestring fixture with a gigantic LineTo command") {
|
|
std::string buffer{open_tile("058/tile.mvt")};
|
|
vtzero::vector_tile tile{buffer};
|
|
REQUIRE(tile.count_layers() == 1);
|
|
|
|
auto layer = tile.next_layer();
|
|
REQUIRE(layer.num_features() == 1);
|
|
|
|
auto feature = layer.next_feature();
|
|
REQUIRE(feature);
|
|
|
|
const auto geometry = feature.geometry();
|
|
REQUIRE_THROWS_AS(vtzero::decode_geometry(geometry, geom_handler{}), vtzero::geometry_exception);
|
|
REQUIRE_THROWS_WITH(vtzero::decode_geometry(geometry, geom_handler{}), "count too large");
|
|
}
|
|
|