diff --git a/include/geopackage/wkb/reader.hpp b/include/geopackage/wkb/reader.hpp index 269d197cad..7dc51d13c7 100644 --- a/include/geopackage/wkb/reader.hpp +++ b/include/geopackage/wkb/reader.hpp @@ -1,5 +1,5 @@ -#ifndef NGEN_GEOPACKAGE_WKB_POD_H -#define NGEN_GEOPACKAGE_WKB_POD_H +#ifndef NGEN_GEOPACKAGE_WKB_READER_H +#define NGEN_GEOPACKAGE_WKB_READER_H #include #include @@ -15,104 +15,20 @@ namespace wkb { using byte_t = uint8_t; using byte_vector = std::vector; - -struct point { double x, y; }; -struct linestring { std::vector points; }; -struct polygon { std::vector rings; }; -struct multipoint { std::vector points; }; -struct multilinestring { std::vector lines; }; -struct multipolygon { std::vector polygons; }; - -struct geometry { - using gtype = boost::variant< - point, linestring, polygon, - multipoint, multilinestring, multipolygon - >; - - uint32_t type; - gtype data; -}; - -class wkt : public boost::static_visitor -{ - public: - - /** - * @brief Get the WKT form from WKB structs - * - * @tparam T WKB geometry struct type - * @param g geometry object - * @return std::string @param{g} in WKT form - */ - template - std::string operator()(T& g) const - { - return this->wkt_type(g) + " " + this->wkt_coords(g); - } - - private: - std::string wkt_type(const point&) const { return "POINT"; } - std::string wkt_type(const linestring&) const { return "LINESTRING"; } - std::string wkt_type(const polygon&) const { return "POLYGON"; } - std::string wkt_type(const multipoint&) const { return "MULTIPOINT"; } - std::string wkt_type(const multilinestring&) const { return "MULTILINESTRING"; } - std::string wkt_type(const multipolygon&) const { return "MULTIPOLYGON"; } - - std::string wkt_coords(const point& g) const - { - std::ostringstream out; - out.precision(3); - out << std::fixed << g.x << " " << g.y; - return std::move(out).str(); - } - - std::string wkt_coords(const linestring& g) const - { - return "(" + std::accumulate( - std::next(g.points.begin()), - g.points.end(), - wkt_coords(g.points[0]), - [this](const std::string& a, const point b) { return a + "," + this->wkt_coords(b); } - ) + ")"; - } - - std::string wkt_coords(const multipoint& g) const - { - return "(" + std::accumulate( - std::next(g.points.begin()), - g.points.end(), - wkt_coords(g.points[0]), - [this](const std::string& a, const point b) { return a + "," + this->wkt_coords(b); } - ) + ")"; - } - - std::string wkt_coords(const polygon& g) const - { - std::string output; - for (const auto& gg : g.rings) { - output += "(" + wkt_coords(gg) + ")"; - } - return output; - } - - std::string wkt_coords(const multilinestring& g) const - { - std::string output; - for (const auto& gg : g.lines) { - output += "(" + wkt_coords(gg) + ")"; - } - return output; - } - - std::string wkt_coords(const multipolygon& g) const - { - std::string output; - for (const auto& gg : g.polygons) { - output += "(" + wkt_coords(gg) + ")"; - } - return output; - } -}; +struct wkb_point { double x, y; }; +struct wkb_linestring { std::vector points; }; +struct wkb_polygon { std::vector rings; }; +struct wkb_multipoint { std::vector points; }; +struct wkb_multilinestring { std::vector lines; }; +struct wkb_multipolygon { std::vector polygons; }; +using wkb_geometry = boost::variant< + wkb_point, + wkb_linestring, + wkb_polygon, + wkb_multipoint, + wkb_multilinestring, + wkb_multipolygon +>; /** * @brief @@ -152,80 +68,67 @@ inline void copy_from(byte_vector src, T& index, S& dst, uint8_t order) * @return T parsed WKB in geometry struct */ template -static inline T read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order); +inline T read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order); + +namespace { +template +inline output_t read_wkb_compound_internal(const byte_vector& buffer, int& index, uint8_t order) +{ + uint32_t count; + copy_from(buffer, index, count, order); + std::vector children(count); + for (auto& child : children) { + child = read_wkb_internal(buffer, index, order); + } + return output_t{children}; +} +} // anonymous namespace + +#define READ_WKB_INTERNAL_SIG(output_t) output_t read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) template<> -inline point read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) +inline READ_WKB_INTERNAL_SIG(wkb_point) { double x, y; copy_from(buffer, index, x, order); copy_from(buffer, index, y, order); - return point{x, y}; + return wkb_point{x, y}; }; -template<> -inline linestring read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) +template<> +inline READ_WKB_INTERNAL_SIG(wkb_linestring) { - uint32_t count; - copy_from(buffer, index, count, order); - std::vector children(count); - for (auto& child : children) { - child = read_wkb_internal(buffer, index, order); - } - return linestring{children}; + return read_wkb_compound_internal(buffer, index, order); } -template<> -inline polygon read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) +template<> +inline READ_WKB_INTERNAL_SIG(wkb_polygon) { - uint32_t count; - copy_from(buffer, index, count, order); - std::vector children(count); - for (auto& child : children) { - child = read_wkb_internal(buffer, index, order); - } - - return polygon{children}; + return read_wkb_compound_internal(buffer, index, order); } -template<> -inline multipoint read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) +template<> +inline READ_WKB_INTERNAL_SIG(wkb_multipoint) { - uint32_t count; - copy_from(buffer, index, count, order); - std::vector children(count); - for (auto& child : children) { - child = read_wkb_internal(buffer, index, order); - } - return multipoint{children}; + return read_wkb_compound_internal(buffer, index, order); } -template<> -inline multilinestring read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) +template<> +inline READ_WKB_INTERNAL_SIG(wkb_multilinestring) { - uint32_t count; - copy_from(buffer, index, count, order); - std::vector children(count); - for (auto& child : children) { - child = read_wkb_internal(buffer, index, order); - } - return multilinestring{children}; + return read_wkb_compound_internal(buffer, index, order); } -template<> -inline multipolygon read_wkb_internal(const byte_vector& buffer, int& index, uint8_t order) +template<> +inline READ_WKB_INTERNAL_SIG(wkb_multipolygon) { - uint32_t count; - copy_from(buffer, index, count, order); - std::vector children(count); - for (auto& child : children) { - child = read_wkb_internal(buffer, index, order); - } - return multipolygon{children}; + return read_wkb_compound_internal(buffer, index, order); } +#undef READ_WKB_INTERNAL_SIG + /** - * @brief Read WKB into a naive geometry struct + * @brief Read (known) WKB into a specific geometry struct * * @tparam T geometry struct type (i.e. @code{wkb::point}, @code{wkb::polygon}, etc.) * @param buffer vector of bytes @@ -252,14 +155,16 @@ static inline T read_known_wkb(const byte_vector& buffer) return read_wkb_internal(buffer, index, order); }; -static inline wkb::geometry read_wkb(const byte_vector&buffer) +/** + * @brief Read WKB into a variant geometry struct + * + * @param buffer vector of bytes + * @return wkb::geometry geometry struct containing the parsed WKB values. + */ +static inline wkb::wkb_geometry read_wkb(const byte_vector&buffer) { if (buffer.size() < 5) { - - throw std::runtime_error( - "buffer reached end before encountering WKB\n\tdebug: [" + - std::string(buffer.begin(), buffer.end()) + "]" - ); + throw std::runtime_error("buffer reached end before encountering WKB"); } int index = 0; @@ -269,36 +174,21 @@ static inline wkb::geometry read_wkb(const byte_vector&buffer) uint32_t type; copy_from(buffer, index, type, order); - wkb::geometry g; - g.type = type; + wkb_geometry g; switch(type) { - case 1: - g.data = read_wkb_internal(buffer, index, order); - break; - case 2: - g.data = read_wkb_internal(buffer, index, order); - break; - case 3: - g.data = read_wkb_internal(buffer, index, order); - break; - case 4: - g.data = read_wkb_internal(buffer, index, order); - break; - case 5: - g.data = read_wkb_internal(buffer, index, order); - break; - case 6: - g.data = read_wkb_internal(buffer, index, order); - break; - default: - g.data = point{std::nan("0"), std::nan("0")}; - break; + case 1: g = read_wkb_internal(buffer, index, order); break; + case 2: g = read_wkb_internal(buffer, index, order); break; + case 3: g = read_wkb_internal(buffer, index, order); break; + case 4: g = read_wkb_internal(buffer, index, order); break; + case 5: g = read_wkb_internal(buffer, index, order); break; + case 6: g = read_wkb_internal(buffer, index, order); break; + default: g = wkb_point{std::nan("0"), std::nan("0")}; break; } - return std::move(g); + return g; } } // namespace wkb } // namespace geopackage -#endif // NGEN_GEOPACKAGE_WKB_POD_H \ No newline at end of file +#endif // NGEN_GEOPACKAGE_WKB_READER_H \ No newline at end of file diff --git a/test/geopackage/WKB_Test.cpp b/test/geopackage/WKB_Test.cpp index 867fa34b47..9d3c3d8131 100644 --- a/test/geopackage/WKB_Test.cpp +++ b/test/geopackage/WKB_Test.cpp @@ -1,5 +1,6 @@ #include #include +#include using namespace geopackage; @@ -50,10 +51,10 @@ class WKB_Test : public ::testing::Test TEST_F(WKB_Test, wkb_read_test) { - const wkb::geometry geom = wkb::read_wkb(this->wkb); - EXPECT_EQ(geom.type, 3); + const wkb::wkb_geometry geom = wkb::read_wkb(this->wkb); + EXPECT_EQ(geom.which() + 1, 3); // +1 since variant.which() is 0-based - const wkb::polygon& poly = boost::get(geom.data); + const wkb::wkb_polygon& poly = boost::get(geom); const std::vector> expected_coordinates = { {10.689, -25.092}, {34.595, -20.170}, @@ -67,16 +68,16 @@ TEST_F(WKB_Test, wkb_read_test) EXPECT_NEAR(poly.rings[0].points[i].y, expected_coordinates[i].second, 0.0001); } - const wkb::wkt wkt_visitor; + const wkb::wkt_visitor wkt_v; const auto wkt = "POLYGON ((10.689 -25.092,34.595 -20.170,38.814 -35.639,13.502 -39.155,10.689 -25.092))"; - EXPECT_EQ(boost::apply_visitor(wkt_visitor, geom.data), wkt); - EXPECT_EQ(wkt_visitor(poly), wkt); + EXPECT_EQ(boost::apply_visitor(wkt_v, geom), wkt); + EXPECT_EQ(wkt_v(poly), wkt); } TEST_F(WKB_Test, wkb_endianness_test) { - wkb::point geom_big = wkb::read_known_wkb(this->big_endian); - wkb::point geom_little = wkb::read_known_wkb(this->little_endian); + const auto geom_big = wkb::read_known_wkb(this->big_endian); + const auto geom_little = wkb::read_known_wkb(this->little_endian); EXPECT_NEAR(geom_big.x, geom_little.x, 0.000001); EXPECT_NEAR(geom_big.y, geom_little.y, 0.000001); } \ No newline at end of file