Merge commit '6eb4f090f98f6b17a23c57768c16b7716b6c9cbd' as 'third_party/libosmium'

This commit is contained in:
Patrick Niklaus
2017-08-30 09:30:27 +00:00
434 changed files with 81367 additions and 0 deletions
@@ -0,0 +1 @@
multipolygon.qgs~
+126
View File
@@ -0,0 +1,126 @@
#-----------------------------------------------------------------------------
#
# CMake Config
#
# Libosmium data tests
#
#-----------------------------------------------------------------------------
message(STATUS "Configuring data tests")
if(NOT GDAL_FOUND OR NOT EXPAT_FOUND)
message(STATUS "Sorry, building data tests needs GDAL and Expat")
message(STATUS "Configuring data tests - failed")
return()
endif()
message(STATUS "Looking for osm-testdata")
find_path(OSM_TESTDATA grid/data/all.osm HINT ../../../osm-testdata)
if(OSM_TESTDATA STREQUAL "OSM_TESTDATA-NOTFOUND")
message(STATUS "Looking for osm-testdata - not found (data tests disabled)")
message(STATUS "Configuring data tests - failed")
return()
endif()
message(STATUS "Looking for osm-testdata - found")
#-----------------------------------------------------------------------------
include_directories("include")
include_directories("../include")
#-----------------------------------------------------------------------------
#
# testcases
#
#-----------------------------------------------------------------------------
file(GLOB TESTCASE_CPPS testcases/*.cpp)
add_executable(testdata-testcases testdata-testcases.cpp ${TESTCASE_CPPS})
set_pthread_on_target(testdata-testcases)
target_link_libraries(testdata-testcases
${OSMIUM_XML_LIBRARIES}
)
add_test(NAME testdata-testcases
COMMAND testdata-testcases
)
set_tests_properties(testdata-testcases PROPERTIES
ENVIRONMENT "TESTCASES_DIR=${OSM_TESTDATA}/grid/data"
LABELS "data;fast")
#-----------------------------------------------------------------------------
#
# xml
#
#-----------------------------------------------------------------------------
add_executable(testdata-xml testdata-xml.cpp)
set_pthread_on_target(testdata-xml)
target_link_libraries(testdata-xml
${OSMIUM_XML_LIBRARIES}
)
add_test(NAME testdata-xml
COMMAND testdata-xml
)
set_tests_properties(testdata-xml PROPERTIES
ENVIRONMENT "TESTDIR=${OSM_TESTDATA}/xml/data"
LABELS "data;fast")
#-----------------------------------------------------------------------------
#
# overview
#
#-----------------------------------------------------------------------------
add_executable(testdata-overview testdata-overview.cpp)
set_pthread_on_target(testdata-overview)
target_link_libraries(testdata-overview
${OSMIUM_XML_LIBRARIES}
${GDAL_LIBRARIES}
)
add_test(NAME testdata-overview
COMMAND testdata-overview ${OSM_TESTDATA}/grid/data/all.osm
)
set_tests_properties(testdata-overview PROPERTIES
LABELS "data;slow")
#-----------------------------------------------------------------------------
#
# multipolygon
#
#-----------------------------------------------------------------------------
find_program(RUBY ruby)
if(RUBY)
find_package(Gem COMPONENTS json)
endif()
find_program(SPATIALITE spatialite)
if(RUBY AND GEM_json_FOUND AND SPATIALITE)
add_executable(testdata-multipolygon testdata-multipolygon.cpp)
set_pthread_on_target(testdata-multipolygon)
target_link_libraries(testdata-multipolygon
${OSMIUM_XML_LIBRARIES}
${GDAL_LIBRARIES}
)
add_test(NAME testdata-multipolygon
COMMAND ${CMAKE_COMMAND}
-D OSM_TESTDATA=${OSM_TESTDATA}
-D RUBY=${RUBY}
-P ${CMAKE_CURRENT_SOURCE_DIR}/run-testdata-multipolygon.cmake)
set_tests_properties(testdata-multipolygon PROPERTIES LABELS "data;slow")
configure_file(multipolygon.qgs ${CMAKE_CURRENT_BINARY_DIR}/multipolygon.qgs @ONLY)
else()
message(WARNING "Disabled testdata-multipolygon test because 'ruby' and/or 'json' ruby gem and/or 'spatialite' was not found")
endif()
#-----------------------------------------------------------------------------
message(STATUS "Configuring data tests - done")
#-----------------------------------------------------------------------------
+10
View File
@@ -0,0 +1,10 @@
# OSM Testdata
This directory contains software that can be used with the osm-testdata
repository at https://github.com/osmcode/osm-testdata . To use it, clone
the `osm-testdata` repository in the same directory where you cloned the
`libosmium` repository.
Tests will be built if the CMake option `BUILD_DATA_TESTS` is set and run as
part of the `ctest` run.
@@ -0,0 +1,92 @@
#ifndef CHECK_BASICS_HANDLER_HPP
#define CHECK_BASICS_HANDLER_HPP
#include <iostream>
#include <unordered_set>
#include <osmium/handler.hpp>
#include <osmium/osm.hpp>
/**
* Check some basics of the input data:
*
* 1. Correct number of nodes, ways, and relations
* 2. Correct ID space used by nodes, ways, and relations
* 3. No ID used more than once
*/
class CheckBasicsHandler : public osmium::handler::Handler {
// Lower bound for the id range allowed in this test.
int m_id_range;
// In the beginning these contains the number of nodes, ways, and relations
// supposedly in the data.osm file. They will be decremented on each object
// and have to be 0 at the end.
int m_num_nodes;
int m_num_ways;
int m_num_relations;
// All IDs encountered in the data.osm file will be stored in this set and
// checked for duplicates.
std::unordered_set<osmium::object_id_type> m_ids;
// Check id is in range [min, max] and that it isn't more than once in input.
void id_check(osmium::object_id_type id, osmium::object_id_type min, osmium::object_id_type max) {
if (id < m_id_range + min || id > m_id_range + max) {
std::cerr << " id " << id << " out of range for this test case\n";
exit(1);
}
auto r = m_ids.insert(id);
if (!r.second) {
std::cerr << " id " << id << " contained twice in data.osm\n";
exit(1);
}
}
public:
static const int ids_per_testcase = 1000;
CheckBasicsHandler(int testcase, int nodes, int ways, int relations) :
osmium::handler::Handler(),
m_id_range(testcase * ids_per_testcase),
m_num_nodes(nodes),
m_num_ways(ways),
m_num_relations(relations) {
}
~CheckBasicsHandler() {
if (m_num_nodes != 0) {
std::cerr << " wrong number of nodes in data.osm\n";
exit(1);
}
if (m_num_ways != 0) {
std::cerr << " wrong number of ways in data.osm\n";
exit(1);
}
if (m_num_relations != 0) {
std::cerr << " wrong number of relations in data.osm\n";
exit(1);
}
}
void node(const osmium::Node& node) {
id_check(node.id(), 0, 799);
--m_num_nodes;
}
void way(const osmium::Way& way) {
id_check(way.id(), 800, 899);
--m_num_ways;
}
void relations(const osmium::Relation& relation) {
id_check(relation.id(), 900, 999);
--m_num_relations;
}
}; // CheckBasicsHandler
#endif // CHECK_BASICS_HANDLER_HPP
@@ -0,0 +1,86 @@
#ifndef CHECK_WKT_HANDLER_HPP
#define CHECK_WKT_HANDLER_HPP
#include <cassert>
#include <fstream>
#include <map>
#include <sstream>
#include <string>
#include <osmium/handler.hpp>
#include <osmium/osm.hpp>
#include <osmium/osm/types.hpp>
class CheckWKTHandler : public osmium::handler::Handler {
std::map<osmium::object_id_type, std::string> m_geometries;
osmium::geom::WKTFactory<> m_factory;
void read_wkt_file(const std::string& filename) {
std::ifstream in(filename, std::ifstream::in);
if (in) {
osmium::object_id_type id;
std::string line;
while (std::getline(in, line)) {
size_t pos = line.find_first_of(' ');
if (pos == std::string::npos) {
std::cerr << filename << " not formatted correctly\n";
exit(1);
}
std::string id_str = line.substr(0, pos);
std::istringstream iss(id_str);
iss >> id;
if (m_geometries.find(id) != m_geometries.end()) {
std::cerr << filename + " contains id " << id << "twice\n";
exit(1);
}
m_geometries[id] = line.substr(pos+1);
}
}
}
public:
CheckWKTHandler(const std::string& dirname, int test_id) :
osmium::handler::Handler() {
std::string filename = dirname + "/" + std::to_string(test_id / 100) + "/" + std::to_string(test_id) + "/";
read_wkt_file(filename + "nodes.wkt");
read_wkt_file(filename + "ways.wkt");
}
~CheckWKTHandler() {
if (!m_geometries.empty()) {
for (const auto& geom : m_geometries) {
std::cerr << "geometry id " << geom.first << " not in data.osm.\n";
}
exit(1);
}
}
void node(const osmium::Node& node) {
const std::string wkt = m_geometries[node.id()];
assert(wkt != "" && "Missing geometry for node in nodes.wkt");
std::string this_wkt = m_factory.create_point(node.location());
assert(wkt == this_wkt && "wkt geometries don't match");
m_geometries.erase(node.id());
}
void way(const osmium::Way& way) {
const std::string wkt = m_geometries[way.id()];
assert(wkt != "" && "Missing geometry for way in ways.wkt");
std::string this_wkt = m_factory.create_linestring(way);
assert(wkt == this_wkt && "wkt geometries don't match");
m_geometries.erase(way.id());
}
}; // CheckWKTHandler
#endif // CHECK_WKT_HANDLER_HPP
@@ -0,0 +1,22 @@
#ifndef COMMON_HPP
#define COMMON_HPP
#include <osmium/index/map/dummy.hpp>
#include <osmium/index/map/sparse_mem_array.hpp>
#include <osmium/geom/wkt.hpp>
#include <osmium/handler.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>
#include <osmium/io/xml_input.hpp>
#include <osmium/visitor.hpp>
using index_neg_type = osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location>;
using index_pos_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
using location_handler_type = osmium::handler::NodeLocationsForWays<index_pos_type, index_neg_type>;
#include "check_basics_handler.hpp"
#include "check_wkt_handler.hpp"
#include "testdata-testcases.hpp"
#endif // COMMON_HPP
@@ -0,0 +1,10 @@
#ifndef TESTDATA_TESTCASES_HPP
#define TESTDATA_TESTCASES_HPP
#include <catch.hpp>
#include <string>
extern std::string dirname;
#endif // TESTDATA_TESTCASES_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
#-----------------------------------------------------------------------------
#
# Helper script that runs the 'multipolygon' test.
#
#-----------------------------------------------------------------------------
# Remove files that might be left over from previous run
file(REMOVE multipolygon.db multipolygon-tests.json)
#-----------------------------------------------------------------------------
#
# Create multipolygons from test data.
#
#-----------------------------------------------------------------------------
execute_process(
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/testdata-multipolygon
${OSM_TESTDATA}/grid/data/all.osm
RESULT_VARIABLE _result
OUTPUT_FILE multipolygon.log
ERROR_FILE multipolygon.log
)
if(_result)
message(FATAL_ERROR "Error running testdata-multipolygon command")
endif()
#-----------------------------------------------------------------------------
#
# Compare created multipolygons with reference data.
#
#-----------------------------------------------------------------------------
execute_process(
COMMAND ${RUBY} ${OSM_TESTDATA}/bin/compare-areas.rb
${OSM_TESTDATA}/grid/data/tests.json
multipolygon-tests.json
RESULT_VARIABLE _result
)
if(_result)
message(FATAL_ERROR "Error running compare-areas command")
endif()
#-----------------------------------------------------------------------------
@@ -0,0 +1,46 @@
#include <cmath>
#include <cstring>
#include <stdexcept>
#include "common.hpp"
class TestHandler100 : public osmium::handler::Handler {
public:
TestHandler100() :
osmium::handler::Handler() {
}
void node(const osmium::Node& node) {
constexpr const double epsilon = 0.00000001;
if (node.id() == 100000) {
REQUIRE(node.version() == 1);
REQUIRE(node.timestamp() == osmium::Timestamp{"2014-01-01T00:00:00Z"});
REQUIRE(node.uid() == 1);
REQUIRE(!std::strcmp(node.user(), "test"));
REQUIRE(node.changeset() == 1);
REQUIRE(std::abs(node.location().lon() - 1.02) < epsilon);
REQUIRE(std::abs(node.location().lat() - 1.02) < epsilon);
} else {
throw std::runtime_error{"Unknown ID"};
}
}
}; // class TestHandler100
TEST_CASE("100") {
SECTION("test 100") {
osmium::io::Reader reader{dirname + "/1/100/data.osm"};
CheckBasicsHandler check_basics_handler{100, 1, 0, 0};
CheckWKTHandler check_wkt_handler{dirname, 100};
TestHandler100 test_handler;
osmium::apply(reader, check_basics_handler, check_wkt_handler, test_handler);
}
}
@@ -0,0 +1,47 @@
#include <cmath>
#include <stdexcept>
#include "common.hpp"
class TestHandler101 : public osmium::handler::Handler {
public:
TestHandler101() :
osmium::handler::Handler() {
}
void node(const osmium::Node& node) {
constexpr const double epsilon = 0.00000001;
if (node.id() == 101000) {
REQUIRE(node.version() == 1);
REQUIRE(std::abs(node.location().lon() - 1.12) < epsilon);
REQUIRE(std::abs(node.location().lat() - 1.02) < epsilon);
} else if (node.id() == 101001) {
REQUIRE(node.version() == 1);
REQUIRE(std::abs(node.location().lon() - 1.12) < epsilon);
REQUIRE(std::abs(node.location().lat() - 1.03) < epsilon);
} else if (node.id() == 101002) {
} else if (node.id() == 101003) {
} else {
throw std::runtime_error{"Unknown ID"};
}
}
}; // class TestHandler101
TEST_CASE("101") {
SECTION("test 101") {
osmium::io::Reader reader{dirname + "/1/101/data.osm"};
CheckBasicsHandler check_basics_handler{101, 4, 0, 0};
CheckWKTHandler check_wkt_handler{dirname, 101};
TestHandler101 test_handler;
osmium::apply(reader, check_basics_handler, check_wkt_handler, test_handler);
}
}
@@ -0,0 +1,63 @@
#include <cmath>
#include <cstring>
#include <stdexcept>
#include "common.hpp"
class TestHandler110 : public osmium::handler::Handler {
public:
TestHandler110() :
osmium::handler::Handler() {
}
void node(const osmium::Node& node) {
constexpr const double epsilon = 0.00000001;
if (node.id() == 110000) {
REQUIRE(std::abs(node.location().lon() - 1.02) < epsilon);
REQUIRE(std::abs(node.location().lat() - 1.12) < epsilon);
} else if (node.id() == 110001) {
REQUIRE(std::abs(node.location().lon() - 1.07) < epsilon);
REQUIRE(std::abs(node.location().lat() - 1.13) < epsilon);
} else {
throw std::runtime_error{"Unknown ID"};
}
}
void way(const osmium::Way& way) {
if (way.id() == 110800) {
REQUIRE(way.version() == 1);
REQUIRE(way.nodes().size() == 2);
REQUIRE(!way.is_closed());
const char *test_id = way.tags().get_value_by_key("test:id");
REQUIRE(test_id);
REQUIRE(!std::strcmp(test_id, "110"));
} else {
throw std::runtime_error{"Unknown ID"};
}
}
}; // class TestHandler110
TEST_CASE("110") {
SECTION("test 110") {
osmium::io::Reader reader(dirname + "/1/110/data.osm");
index_pos_type index_pos;
index_neg_type index_neg;
location_handler_type location_handler(index_pos, index_neg);
location_handler.ignore_errors();
CheckBasicsHandler check_basics_handler(110, 2, 1, 0);
CheckWKTHandler check_wkt_handler(dirname, 110);
TestHandler110 test_handler;
osmium::apply(reader, location_handler, check_basics_handler, check_wkt_handler, test_handler);
}
}
@@ -0,0 +1,186 @@
#include <cstring>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <gdalcpp.hpp>
#include <osmium/index/map/sparse_mem_array.hpp>
#include <osmium/area/assembler_legacy.hpp>
#include <osmium/area/multipolygon_collector.hpp>
#include <osmium/area/problem_reporter_ogr.hpp>
#include <osmium/geom/ogr.hpp>
#include <osmium/geom/wkt.hpp>
#include <osmium/handler.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>
#include <osmium/io/xml_input.hpp>
#include <osmium/visitor.hpp>
using index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
struct less_charptr {
bool operator()(const char* a, const char* b) const noexcept {
return std::strcmp(a, b) < 0;
}
}; // less_charptr
using tagmap_type = std::map<const char*, const char*, less_charptr>;
tagmap_type create_map(const osmium::TagList& taglist) {
tagmap_type map;
for (auto& tag : taglist) {
map[tag.key()] = tag.value();
}
return map;
}
class TestHandler : public osmium::handler::Handler {
gdalcpp::Layer m_layer_point;
gdalcpp::Layer m_layer_lines;
gdalcpp::Layer m_layer_mpoly;
osmium::geom::OGRFactory<> m_ogr_factory;
osmium::geom::WKTFactory<> m_wkt_factory;
std::ofstream m_out;
bool m_first_out{true};
public:
explicit TestHandler(gdalcpp::Dataset& dataset) :
m_layer_point(dataset, "points", wkbPoint),
m_layer_lines(dataset, "lines", wkbLineString),
m_layer_mpoly(dataset, "multipolygons", wkbMultiPolygon),
m_out("multipolygon-tests.json") {
m_layer_point.add_field("id", OFTReal, 10);
m_layer_point.add_field("type", OFTString, 30);
m_layer_lines.add_field("id", OFTReal, 10);
m_layer_lines.add_field("type", OFTString, 30);
m_layer_mpoly.add_field("id", OFTReal, 10);
m_layer_mpoly.add_field("from_type", OFTString, 1);
}
~TestHandler() {
m_out << "\n]\n";
}
void node(const osmium::Node& node) {
gdalcpp::Feature feature{m_layer_point, m_ogr_factory.create_point(node)};
feature.set_field("id", static_cast<double>(node.id()));
feature.set_field("type", node.tags().get_value_by_key("type"));
feature.add_to_layer();
}
void way(const osmium::Way& way) {
try {
gdalcpp::Feature feature{m_layer_lines, m_ogr_factory.create_linestring(way)};
feature.set_field("id", static_cast<double>(way.id()));
feature.set_field("type", way.tags().get_value_by_key("type"));
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
}
}
void area(const osmium::Area& area) {
if (m_first_out) {
m_out << "[\n";
m_first_out = false;
} else {
m_out << ",\n";
}
m_out << "{\n \"test_id\": " << (area.orig_id() / 1000) << ",\n \"area_id\": " << area.id() << ",\n \"from_id\": " << area.orig_id() << ",\n \"from_type\": \"" << (area.from_way() ? "way" : "relation") << "\",\n \"wkt\": \"";
try {
const std::string wkt = m_wkt_factory.create_multipolygon(area);
m_out << wkt << "\",\n \"tags\": {";
const auto tagmap = create_map(area.tags());
bool first = true;
for (auto& tag : tagmap) {
if (first) {
first = false;
} else {
m_out << ", ";
}
m_out << '"' << tag.first << "\": \"" << tag.second << '"';
}
m_out << "}\n}";
} catch (const osmium::geometry_error&) {
m_out << "INVALID\"\n}";
}
try {
gdalcpp::Feature feature{m_layer_mpoly, m_ogr_factory.create_multipolygon(area)};
feature.set_field("id", static_cast<double>(area.orig_id()));
std::string from_type;
if (area.from_way()) {
from_type = "w";
} else {
from_type = "r";
}
feature.set_field("from_type", from_type.c_str());
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n";
}
}
}; // class TestHandler
/* ================================================== */
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " INFILE\n";
std::exit(1);
}
const std::string output_format{"SQLite"};
const std::string input_filename{argv[1]};
const std::string output_filename{"multipolygon.db"};
CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}};
osmium::area::ProblemReporterOGR problem_reporter{dataset};
osmium::area::AssemblerLegacy::config_type assembler_config;
assembler_config.problem_reporter = &problem_reporter;
assembler_config.check_roles = true;
assembler_config.create_empty_areas = true;
assembler_config.debug_level = 2;
osmium::area::MultipolygonCollector<osmium::area::AssemblerLegacy> collector{assembler_config};
std::cerr << "Pass 1...\n";
osmium::io::Reader reader1{input_filename};
collector.read_relations(reader1);
reader1.close();
std::cerr << "Pass 1 done\n";
index_type index;
location_handler_type location_handler{index};
location_handler.ignore_errors();
TestHandler test_handler{dataset};
std::cerr << "Pass 2...\n";
osmium::io::Reader reader2{input_filename};
osmium::apply(reader2, location_handler, test_handler, collector.handler([&test_handler](const osmium::memory::Buffer& area_buffer) {
osmium::apply(area_buffer, test_handler);
}));
reader2.close();
std::cerr << "Pass 2 done\n";
}
@@ -0,0 +1,102 @@
/* The code in this file is released into the Public Domain. */
#include <iostream>
#include <string>
#include <gdalcpp.hpp>
#include <osmium/index/map/sparse_mem_array.hpp>
#include <osmium/geom/ogr.hpp>
#include <osmium/handler.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>
#include <osmium/io/xml_input.hpp>
#include <osmium/visitor.hpp>
using index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
class TestOverviewHandler : public osmium::handler::Handler {
gdalcpp::Layer m_layer_nodes;
gdalcpp::Layer m_layer_labels;
gdalcpp::Layer m_layer_ways;
osmium::geom::OGRFactory<> m_factory;
public:
explicit TestOverviewHandler(gdalcpp::Dataset& dataset) :
m_layer_nodes(dataset, "nodes", wkbPoint),
m_layer_labels(dataset, "labels", wkbPoint),
m_layer_ways(dataset, "ways", wkbLineString) {
m_layer_nodes.add_field("id", OFTReal, 10);
m_layer_labels.add_field("id", OFTReal, 10);
m_layer_labels.add_field("label", OFTString, 30);
m_layer_ways.add_field("id", OFTReal, 10);
m_layer_ways.add_field("test", OFTInteger, 3);
}
void node(const osmium::Node& node) {
const char* label = node.tags().get_value_by_key("label");
if (label) {
gdalcpp::Feature feature(m_layer_labels, m_factory.create_point(node));
feature.set_field("id", static_cast<double>(node.id()));
feature.set_field("label", label);
feature.add_to_layer();
} else {
gdalcpp::Feature feature(m_layer_nodes, m_factory.create_point(node));
feature.set_field("id", static_cast<double>(node.id()));
feature.add_to_layer();
}
}
void way(const osmium::Way& way) {
try {
gdalcpp::Feature feature(m_layer_ways, m_factory.create_linestring(way));
feature.set_field("id", static_cast<double>(way.id()));
const char* test = way.tags().get_value_by_key("test");
if (test) {
feature.set_field("test", test);
}
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n";
}
}
};
/* ================================================== */
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " INFILE\n";
std::exit(1);
}
const std::string output_format{"SQLite"};
const std::string input_filename{argv[1]};
const std::string output_filename{"testdata-overview.db"};
::unlink(output_filename.c_str());
CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE");
gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}};
osmium::io::Reader reader{input_filename};
index_type index;
location_handler_type location_handler{index};
location_handler.ignore_errors();
TestOverviewHandler handler{dataset};
osmium::apply(reader, location_handler, handler);
reader.close();
}
@@ -0,0 +1,23 @@
#include <iostream>
#include <string>
#define CATCH_CONFIG_RUNNER
#include "testdata-testcases.hpp"
std::string dirname;
int main(int argc, char* argv[]) {
const char* testcases_dir = getenv("TESTCASES_DIR");
if (testcases_dir) {
dirname = testcases_dir;
std::cerr << "Running tests from '" << dirname << "' (from TESTCASES_DIR environment variable)\n";
} else {
std::cerr << "Please set TESTCASES_DIR environment variable.\n";
std::exit(1);
}
return Catch::Session().run(argc, argv);
}
+514
View File
@@ -0,0 +1,514 @@
/* The code in this file is released into the Public Domain. */
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <cassert>
#include <cstdlib>
#include <future>
#include <iostream>
#include <iterator>
#include <string>
#include <osmium/io/detail/queue_util.hpp>
#include <osmium/io/xml_input.hpp>
#include <osmium/io/gzip_compression.hpp>
#include <osmium/visitor.hpp>
static std::string S_(const char* s) {
return std::string{s};
}
static std::string filename(const char* test_id, const char* suffix = "osm") {
const char* testdir = getenv("TESTDIR");
if (!testdir) {
std::cerr << "You have to set TESTDIR environment variable before running testdata-xml\n";
std::exit(2);
}
std::string f;
f += testdir;
f += "/";
f += test_id;
f += "/data.";
f += suffix;
return f;
}
struct header_buffer_type {
osmium::io::Header header;
osmium::memory::Buffer buffer;
};
// =============================================
// The following helper functions are used to call different parts of the
// Osmium internals used to read and parse XML files. This way those parts
// can be tested individually. These function can not be used in normal
// operations, because they make certain assumptions, for instance that
// file contents fit into small buffers.
static std::string read_file(const char* test_id) {
const int fd = osmium::io::detail::open_for_reading(filename(test_id));
assert(fd >= 0);
std::string input(10000, '\0');
const auto n = ::read(fd, reinterpret_cast<unsigned char*>(const_cast<char*>(input.data())), 10000);
assert(n >= 0);
input.resize(static_cast<std::string::size_type>(n));
close(fd);
return input;
}
static std::string read_gz_file(const char* test_id, const char* suffix) {
const int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix));
assert(fd >= 0);
osmium::io::GzipDecompressor gzip_decompressor{fd};
const std::string input = gzip_decompressor.read();
gzip_decompressor.close();
return input;
}
// cppcheck-suppress passedByValue
static header_buffer_type parse_xml(std::string input) {
osmium::thread::Pool pool;
osmium::io::detail::future_string_queue_type input_queue;
osmium::io::detail::future_buffer_queue_type output_queue;
std::promise<osmium::io::Header> header_promise;
std::future<osmium::io::Header> header_future = header_promise.get_future();
osmium::io::detail::add_to_queue(input_queue, std::move(input));
osmium::io::detail::add_to_queue(input_queue, std::string{});
osmium::io::detail::parser_arguments args = {
pool,
input_queue,
output_queue,
header_promise,
osmium::osm_entity_bits::all,
osmium::io::read_meta::yes
};
osmium::io::detail::XMLParser parser{args};
parser.parse();
header_buffer_type result;
result.header = header_future.get();
std::future<osmium::memory::Buffer> future_buffer;
output_queue.wait_and_pop(future_buffer);
result.buffer = future_buffer.get();
if (result.buffer) {
std::future<osmium::memory::Buffer> future_buffer2;
output_queue.wait_and_pop(future_buffer2);
assert(!future_buffer2.get());
}
return result;
}
static header_buffer_type read_xml(const char* test_id) {
const std::string input = read_file(test_id);
return parse_xml(input);
}
// =============================================
TEST_CASE("Reading OSM XML 100: Direct") {
header_buffer_type r = read_xml("100-correct_but_no_data");
REQUIRE(r.header.get("generator") == "testdata");
REQUIRE(0 == r.buffer.committed());
REQUIRE(! r.buffer);
}
TEST_CASE("Reading OSM XML 100: Using Reader") {
osmium::io::Reader reader{filename("100-correct_but_no_data")};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
osmium::memory::Buffer buffer = reader.read();
REQUIRE(0 == buffer.committed());
REQUIRE(! buffer);
reader.close();
}
TEST_CASE("Reading OSM XML 100: Using Reader asking for header only") {
osmium::io::Reader reader{filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
reader.close();
}
// =============================================
TEST_CASE("Reading OSM XML 101: Direct") {
REQUIRE_THROWS_AS(read_xml("101-missing_version"), osmium::format_version_error);
try {
read_xml("101-missing_version");
} catch (const osmium::format_version_error& e) {
REQUIRE(e.version.empty());
}
}
TEST_CASE("Reading OSM XML 101: Using Reader") {
REQUIRE_THROWS_AS([](){
osmium::io::Reader reader{filename("101-missing_version")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}(), osmium::format_version_error);
}
// =============================================
TEST_CASE("Reading OSM XML 102: Direct") {
REQUIRE_THROWS_AS(read_xml("102-wrong_version"), osmium::format_version_error);
try {
read_xml("102-wrong_version");
} catch (const osmium::format_version_error& e) {
REQUIRE(e.version == "0.1");
}
}
TEST_CASE("Reading OSM XML 102: Using Reader") {
REQUIRE_THROWS_AS([](){
osmium::io::Reader reader{filename("102-wrong_version")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}(), osmium::format_version_error);
}
// =============================================
TEST_CASE("Reading OSM XML 103: Direct") {
REQUIRE_THROWS_AS(read_xml("103-old_version"), osmium::format_version_error);
try {
read_xml("103-old_version");
} catch (const osmium::format_version_error& e) {
REQUIRE(e.version == "0.5");
}
}
TEST_CASE("Reading OSM XML 103: Using Reader") {
REQUIRE_THROWS_AS([](){
osmium::io::Reader reader{filename("103-old_version")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}(), osmium::format_version_error);
}
// =============================================
TEST_CASE("Reading OSM XML 104: Direct") {
REQUIRE_THROWS_AS(read_xml("104-empty_file"), osmium::xml_error);
try {
read_xml("104-empty_file");
} catch (const osmium::xml_error& e) {
REQUIRE(e.line == 1);
REQUIRE(e.column == 0);
}
}
TEST_CASE("Reading OSM XML 104: Using Reader") {
REQUIRE_THROWS_AS([](){
osmium::io::Reader reader{filename("104-empty_file")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}(), osmium::xml_error);
}
// =============================================
TEST_CASE("Reading OSM XML 105: Direct") {
REQUIRE_THROWS_AS(read_xml("105-incomplete_xml_file"), osmium::xml_error);
}
TEST_CASE("Reading OSM XML 105: Using Reader") {
REQUIRE_THROWS_AS([](){
osmium::io::Reader reader{filename("105-incomplete_xml_file")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}(), osmium::xml_error);
}
// =============================================
TEST_CASE("Reading OSM XML 120: Direct") {
std::string data = read_gz_file("120-correct_gzip_file_without_data", "osm.gz");
REQUIRE(data.size() == 102);
header_buffer_type r = parse_xml(data);
REQUIRE(r.header.get("generator") == "testdata");
REQUIRE(0 == r.buffer.committed());
REQUIRE(! r.buffer);
}
TEST_CASE("Reading OSM XML 120: Using Reader") {
osmium::io::Reader reader{filename("120-correct_gzip_file_without_data", "osm.gz")};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
osmium::memory::Buffer buffer = reader.read();
REQUIRE(0 == buffer.committed());
REQUIRE(! buffer);
reader.close();
}
// =============================================
TEST_CASE("Reading OSM XML 121: Direct") {
REQUIRE_THROWS_AS(read_gz_file("121-truncated_gzip_file", "osm.gz"), osmium::gzip_error);
}
TEST_CASE("Reading OSM XML 121: Using Reader") {
// can throw osmium::gzip_error or osmium::xml_error
REQUIRE_THROWS([](){
osmium::io::Reader reader{filename("121-truncated_gzip_file", "osm.gz")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}());
}
// =============================================
TEST_CASE("Reading OSM XML 122: Direct") {
REQUIRE_THROWS_AS(read_xml("122-no_osm_element"), osmium::xml_error);
}
TEST_CASE("Reading OSM XML 122: Using Reader") {
REQUIRE_THROWS_AS([](){
osmium::io::Reader reader{filename("122-no_osm_element")};
const osmium::io::Header header{reader.header()};
osmium::memory::Buffer buffer = reader.read();
reader.close();
}(), osmium::xml_error);
}
// =============================================
TEST_CASE("Reading OSM XML 140: Using Reader") {
osmium::io::Reader reader{filename("140-unicode")};
osmium::memory::Buffer buffer = reader.read();
reader.close();
int count = 0;
for (const auto& node : buffer.select<osmium::Node>()) {
++count;
REQUIRE(node.id() == count);
const osmium::TagList& t = node.tags();
const char* uc = t["unicode_char"];
const auto len = atoi(t["unicode_utf8_length"]);
REQUIRE(len == strlen(uc));
REQUIRE(S_(uc) == t["unicode_xml"]);
// workaround for missing support for u8 string literals on Windows
#if !defined(_MSC_VER)
switch (count) {
case 1:
REQUIRE(S_(uc) == u8"a");
break;
case 2:
REQUIRE(S_(uc) == u8"\u00e4");
break;
case 3:
REQUIRE(S_(uc) == u8"\u30dc");
break;
case 4:
REQUIRE(S_(uc) == u8"\U0001d11e");
break;
case 5:
REQUIRE(S_(uc) == u8"\U0001f6eb");
break;
default:
REQUIRE(false); // should not be here
}
#endif
}
REQUIRE(count == 5);
}
// =============================================
TEST_CASE("Reading OSM XML 141: Using Reader") {
osmium::io::Reader reader{filename("141-entities")};
osmium::memory::Buffer buffer = reader.read();
reader.close();
REQUIRE(buffer.committed() > 0);
REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
const osmium::Node& node = buffer.get<osmium::Node>(0);
const osmium::TagList& tags = node.tags();
REQUIRE(S_(tags["less-than"]) == "<");
REQUIRE(S_(tags["greater-than"]) == ">");
REQUIRE(S_(tags["apostrophe"]) == "'");
REQUIRE(S_(tags["ampersand"]) == "&");
REQUIRE(S_(tags["quote"]) == "\"");
}
// =============================================
TEST_CASE("Reading OSM XML 142: Using Reader to read nodes") {
osmium::io::Reader reader{filename("142-whitespace")};
osmium::memory::Buffer buffer = reader.read();
reader.close();
int count = 0;
for (const auto& node : buffer.select<osmium::Node>()) {
++count;
REQUIRE(node.id() == count);
REQUIRE(node.tags().size() == 1);
const osmium::Tag& tag = *(node.tags().begin());
switch (count) {
case 1:
REQUIRE(S_(node.user()) == "user name");
REQUIRE(S_(tag.key()) == "key with space");
REQUIRE(S_(tag.value()) == "value with space");
break;
case 2:
REQUIRE(S_(node.user()) == "line\nfeed");
REQUIRE(S_(tag.key()) == "key with\nlinefeed");
REQUIRE(S_(tag.value()) == "value with\nlinefeed");
break;
case 3:
REQUIRE(S_(node.user()) == "carriage\rreturn");
REQUIRE(S_(tag.key()) == "key with\rcarriage\rreturn");
REQUIRE(S_(tag.value()) == "value with\rcarriage\rreturn");
break;
case 4:
REQUIRE(S_(node.user()) == "tab\tulator");
REQUIRE(S_(tag.key()) == "key with\ttab");
REQUIRE(S_(tag.value()) == "value with\ttab");
break;
case 5:
REQUIRE(S_(node.user()) == "unencoded linefeed");
REQUIRE(S_(tag.key()) == "key with unencoded linefeed");
REQUIRE(S_(tag.value()) == "value with unencoded linefeed");
break;
default:
REQUIRE(false); // should not be here
}
}
REQUIRE(count == 5);
}
TEST_CASE("Reading OSM XML 142: Using Reader to read relation") {
osmium::io::Reader reader{filename("142-whitespace")};
osmium::memory::Buffer buffer = reader.read();
reader.close();
auto it = buffer.select<osmium::Relation>().begin();
REQUIRE(it != buffer.select<osmium::Relation>().end());
REQUIRE(it->id() == 21);
const auto& members = it->members();
REQUIRE(members.size() == 5);
int count = 0;
for (const auto& member : members) {
++count;
switch (count) {
case 1:
REQUIRE(S_(member.role()) == "role with whitespace");
break;
case 2:
REQUIRE(S_(member.role()) == "role with\nlinefeed");
break;
case 3:
REQUIRE(S_(member.role()) == "role with\rcarriage\rreturn");
break;
case 4:
REQUIRE(S_(member.role()) == "role with\ttab");
break;
case 5:
REQUIRE(S_(member.role()) == "role with unencoded linefeed");
break;
default:
REQUIRE(false); // should not be here
}
}
REQUIRE(count == 5);
}
// =============================================
TEST_CASE("Reading OSM XML 200: Direct") {
header_buffer_type r = read_xml("200-nodes");
REQUIRE(r.header.get("generator") == "testdata");
REQUIRE(r.buffer.committed() > 0);
REQUIRE(r.buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
REQUIRE(r.buffer.get<osmium::Node>(0).id() == 36966060);
REQUIRE(std::distance(r.buffer.begin(), r.buffer.end()) == 3);
}
TEST_CASE("Reading OSM XML 200: Using Reader") {
osmium::io::Reader reader{filename("200-nodes")};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
osmium::memory::Buffer buffer = reader.read();
REQUIRE(buffer.committed() > 0);
REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
REQUIRE(buffer.get<osmium::Node>(0).id() == 36966060);
REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
reader.close();
}
TEST_CASE("Reading OSM XML 200: Using Reader asking for nodes") {
osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::node};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
osmium::memory::Buffer buffer = reader.read();
REQUIRE(buffer.committed() > 0);
REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
REQUIRE(buffer.get<osmium::Node>(0).id() == 36966060);
REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
reader.close();
}
TEST_CASE("Reading OSM XML 200: Using Reader asking for header only") {
osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::nothing};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
REQUIRE_FALSE(reader.read());
REQUIRE_THROWS(reader.read());
reader.close();
}
TEST_CASE("Reading OSM XML 200: Using Reader asking for ways") {
osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::way};
const osmium::io::Header header{reader.header()};
REQUIRE(header.get("generator") == "testdata");
osmium::memory::Buffer buffer = reader.read();
REQUIRE(0 == buffer.committed());
REQUIRE(! buffer);
reader.close();
}