Skip to content

Commit c200088

Browse files
committed
Add WKB geometry collection parser and more WKB tests
1 parent 18662b7 commit c200088

File tree

3 files changed

+105
-54
lines changed

3 files changed

+105
-54
lines changed

src/geom.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <osmium/osm/location.hpp>
2020

21+
#include <algorithm>
2122
#include <cassert>
2223
#include <cmath>
2324
#include <initializer_list>
@@ -136,6 +137,18 @@ class polygon_t
136137

137138
void add_inner_ring(ring_t &&ring) { m_inners.push_back(std::move(ring)); }
138139

140+
friend bool operator==(polygon_t const &a, polygon_t const &b) noexcept
141+
{
142+
return (a.outer() == b.outer()) &&
143+
std::equal(a.inners().cbegin(), a.inners().cend(),
144+
b.inners().cbegin(), b.inners().cend());
145+
}
146+
147+
friend bool operator!=(polygon_t const &a, polygon_t const &b) noexcept
148+
{
149+
return !(a == b);
150+
}
151+
139152
private:
140153
ring_t m_outer;
141154
std::vector<ring_t> m_inners;
@@ -154,6 +167,18 @@ class multigeometry_t : public std::vector<GEOM>
154167

155168
GEOM &add_geometry() { return this->emplace_back(); }
156169

170+
friend bool operator==(multigeometry_t const &a,
171+
multigeometry_t const &b) noexcept
172+
{
173+
return std::equal(a.cbegin(), a.cend(), b.cbegin(), b.cend());
174+
}
175+
176+
friend bool operator!=(multigeometry_t const &a,
177+
multigeometry_t const &b) noexcept
178+
{
179+
return !(a == b);
180+
}
181+
157182
}; // class multigeometry_t
158183

159184
using multipoint_t = multigeometry_t<point_t>;
@@ -247,6 +272,16 @@ class geometry_t
247272
return std::visit(std::forward<V>(visitor), m_geom);
248273
}
249274

275+
friend bool operator==(geometry_t const &a, geometry_t const &b) noexcept
276+
{
277+
return (a.srid() == b.srid()) && (a.m_geom == b.m_geom);
278+
}
279+
280+
friend bool operator!=(geometry_t const &a, geometry_t const &b) noexcept
281+
{
282+
return !(a == b);
283+
}
284+
250285
private:
251286
std::variant<nullgeom_t, point_t, linestring_t, polygon_t, multipoint_t,
252287
multilinestring_t, multipolygon_t, collection_t>

src/wkb.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,8 @@ class make_ewkb_visitor
295295
class ewkb_parser_t
296296
{
297297
public:
298-
explicit ewkb_parser_t(std::string const &wkb)
299-
: m_it(wkb.data()), m_end(wkb.data() + wkb.size()),
300-
m_max_length(wkb.size() / (sizeof(double) * 2))
298+
ewkb_parser_t(char const *it, char const *end)
299+
: m_it(it), m_end(end), m_max_length((end - it) / (sizeof(double) * 2))
301300
{}
302301

303302
geom::geometry_t operator()()
@@ -329,18 +328,19 @@ class ewkb_parser_t
329328
case geometry_type::wkb_multi_polygon:
330329
parse_multi_polygon(&geom);
331330
break;
331+
case geometry_type::wkb_collection:
332+
parse_collection(&geom);
333+
break;
332334
default:
333335
throw std::runtime_error{
334336
"Invalid WKB geometry: Unknown geometry type {}"_format(type)};
335337
}
336338

337-
if (m_it != m_end) {
338-
throw std::runtime_error{"Invalid WKB geometry: Extra data at end"};
339-
}
340-
341339
return geom;
342340
}
343341

342+
char const *next() const noexcept { return m_it; }
343+
344344
private:
345345
void check_bytes(uint32_t bytes) const
346346
{
@@ -518,6 +518,23 @@ class ewkb_parser_t
518518
}
519519
}
520520

521+
void parse_collection(geom::geometry_t *geom)
522+
{
523+
auto &collection = geom->set<geom::collection_t>();
524+
auto const num_geoms = parse_length();
525+
if (num_geoms == 0) {
526+
throw std::runtime_error{
527+
"Invalid WKB geometry: Geometry collection without geometries"};
528+
}
529+
530+
collection.reserve(num_geoms);
531+
for (uint32_t i = 0; i < num_geoms; ++i) {
532+
ewkb_parser_t parser{m_it, m_end};
533+
collection.add_geometry(parser());
534+
m_it = parser.next();
535+
}
536+
}
537+
521538
char const *m_it;
522539
char const *m_end;
523540
uint32_t m_max_length;
@@ -536,8 +553,15 @@ std::string geom_to_ewkb(geom::geometry_t const &geom, bool ensure_multi)
536553

537554
geom::geometry_t ewkb_to_geom(std::string const &wkb)
538555
{
539-
ewkb::ewkb_parser_t parser{wkb};
540-
return parser();
556+
const char *const end = wkb.data() + wkb.size();
557+
ewkb::ewkb_parser_t parser{wkb.data(), end};
558+
auto geom = parser();
559+
560+
if (parser.next() != end) {
561+
throw std::runtime_error{"Invalid WKB geometry: Extra data at end"};
562+
}
563+
564+
return geom;
541565
}
542566

543567
unsigned char decode_hex_char(char c)

tests/test-wkb.cpp

Lines changed: 37 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
TEST_CASE("wkb: nullgeom", "[NoDB]")
1616
{
17-
geom::geometry_t geom{};
17+
geom::geometry_t const geom{};
18+
REQUIRE(geom.is_null());
1819

1920
auto const wkb = geom_to_ewkb(geom);
2021
REQUIRE(wkb.empty());
@@ -25,25 +26,23 @@ TEST_CASE("wkb: nullgeom", "[NoDB]")
2526

2627
TEST_CASE("wkb: point", "[NoDB]")
2728
{
28-
geom::geometry_t geom{geom::point_t{3.14, 2.17}, 42};
29+
geom::geometry_t const geom{geom::point_t{3.14, 2.17}, 42};
2930

3031
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
3132
REQUIRE(result.is_point());
3233
REQUIRE(result.srid() == 42);
33-
34-
REQUIRE(result.get<geom::point_t>() == geom.get<geom::point_t>());
34+
REQUIRE(result == geom);
3535
}
3636

3737
TEST_CASE("wkb: linestring", "[NoDB]")
3838
{
39-
geom::geometry_t geom{
39+
geom::geometry_t const geom{
4040
geom::linestring_t{{1.2, 2.3}, {3.4, 4.5}, {5.6, 6.7}}, 43};
4141

4242
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
4343
REQUIRE(result.is_linestring());
4444
REQUIRE(result.srid() == 43);
45-
46-
REQUIRE(result.get<geom::linestring_t>() == geom.get<geom::linestring_t>());
45+
REQUIRE(result == geom);
4746
}
4847

4948
TEST_CASE("wkb: polygon without inner ring", "[NoDB]")
@@ -56,10 +55,7 @@ TEST_CASE("wkb: polygon without inner ring", "[NoDB]")
5655
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
5756
REQUIRE(result.is_polygon());
5857
REQUIRE(result.srid() == 44);
59-
REQUIRE(result.get<geom::polygon_t>().inners().empty());
60-
61-
REQUIRE(result.get<geom::polygon_t>().outer() ==
62-
geom.get<geom::polygon_t>().outer());
58+
REQUIRE(result == geom);
6359
}
6460

6561
TEST_CASE("wkb: polygon with inner rings", "[NoDB]")
@@ -75,24 +71,19 @@ TEST_CASE("wkb: polygon with inner rings", "[NoDB]")
7571
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
7672
REQUIRE(result.is_polygon());
7773
REQUIRE(result.srid() == 45);
78-
REQUIRE(result.get<geom::polygon_t>().inners().size() == 1);
79-
80-
REQUIRE(result.get<geom::polygon_t>().outer() ==
81-
geom.get<geom::polygon_t>().outer());
82-
REQUIRE(result.get<geom::polygon_t>().inners().front() ==
83-
geom.get<geom::polygon_t>().inners().front());
74+
REQUIRE(result == geom);
8475
}
8576

8677
TEST_CASE("wkb: point as multipoint", "[NoDB]")
8778
{
88-
geom::geometry_t geom{geom::point_t{1.2, 2.3}, 47};
79+
geom::geometry_t const geom{geom::point_t{1.2, 2.3}, 47};
8980

9081
auto const result = ewkb_to_geom(geom_to_ewkb(geom, true));
9182
REQUIRE(result.is_multipoint());
9283
REQUIRE(result.srid() == 47);
84+
9385
auto const &rmp = result.get<geom::multipoint_t>();
9486
REQUIRE(rmp.num_geometries() == 1);
95-
9687
REQUIRE(rmp[0] == geom.get<geom::point_t>());
9788
}
9889

@@ -107,24 +98,20 @@ TEST_CASE("wkb: multipoint", "[NoDB]")
10798
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
10899
REQUIRE(result.is_multipoint());
109100
REQUIRE(result.srid() == 46);
110-
auto const &rmp = result.get<geom::multipoint_t>();
111-
REQUIRE(rmp.num_geometries() == 2);
112-
113-
REQUIRE(rmp[0] == mp[0]);
114-
REQUIRE(rmp[1] == mp[1]);
101+
REQUIRE(result == geom);
115102
}
116103

117104
TEST_CASE("wkb: linestring as multilinestring", "[NoDB]")
118105
{
119-
geom::geometry_t geom{
106+
geom::geometry_t const geom{
120107
geom::linestring_t{{1.2, 2.3}, {3.4, 4.5}, {5.6, 6.7}}, 43};
121108

122109
auto const result = ewkb_to_geom(geom_to_ewkb(geom, true));
123110
REQUIRE(result.is_multilinestring());
124111
REQUIRE(result.srid() == 43);
112+
125113
auto const &rml = result.get<geom::multilinestring_t>();
126114
REQUIRE(rml.num_geometries() == 1);
127-
128115
REQUIRE(rml[0] == geom.get<geom::linestring_t>());
129116
}
130117

@@ -139,11 +126,7 @@ TEST_CASE("wkb: multilinestring", "[NoDB]")
139126
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
140127
REQUIRE(result.is_multilinestring());
141128
REQUIRE(result.srid() == 46);
142-
auto const &rml = result.get<geom::multilinestring_t>();
143-
REQUIRE(rml.num_geometries() == 2);
144-
145-
REQUIRE(rml[0] == ml[0]);
146-
REQUIRE(rml[1] == ml[1]);
129+
REQUIRE(result == geom);
147130
}
148131

149132
TEST_CASE("wkb: polygon as multipolygon", "[NoDB]")
@@ -156,11 +139,10 @@ TEST_CASE("wkb: polygon as multipolygon", "[NoDB]")
156139
auto const result = ewkb_to_geom(geom_to_ewkb(geom, true));
157140
REQUIRE(result.is_multipolygon());
158141
REQUIRE(result.srid() == 44);
142+
159143
auto const &rmp = result.get<geom::multipolygon_t>();
160144
REQUIRE(rmp.num_geometries() == 1);
161-
162-
REQUIRE(rmp[0].outer() == geom.get<geom::polygon_t>().outer());
163-
REQUIRE(rmp[0].inners().empty());
145+
REQUIRE(rmp[0] == geom.get<geom::polygon_t>());
164146
}
165147

166148
TEST_CASE("wkb: multipolygon", "[NoDB]")
@@ -181,22 +163,32 @@ TEST_CASE("wkb: multipolygon", "[NoDB]")
181163
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
182164
REQUIRE(result.is_multipolygon());
183165
REQUIRE(result.srid() == 47);
184-
auto const &rmp = result.get<geom::multipolygon_t>();
185-
REQUIRE(rmp.num_geometries() == 2);
186-
187-
REQUIRE(rmp[0].outer() == mp[0].outer());
188-
REQUIRE(rmp[0].inners().size() == 1);
189-
REQUIRE(rmp[0].inners().front() == mp[0].inners().front());
190-
191-
REQUIRE(rmp[1].outer() == mp[1].outer());
192-
REQUIRE(rmp[1].inners().empty());
166+
REQUIRE(result == geom);
193167
}
194168

195-
TEST_CASE("wkb: invalid", "[NoDB]")
169+
TEST_CASE("wkb: geometrycollection", "[NoDB]")
196170
{
197-
REQUIRE_THROWS(ewkb_to_geom("INVALID"));
171+
geom::geometry_t geom1{geom::point_t{1.0, 2.0}};
172+
geom::geometry_t geom2{geom::linestring_t{{1.2, 2.3}, {3.4, 4.5}}};
173+
geom::geometry_t geom3{geom::multipolygon_t{}};
174+
175+
geom3.get<geom::multipolygon_t>().emplace_back(geom::ring_t{
176+
{4.0, 4.0}, {5.0, 4.0}, {5.0, 5.0}, {4.0, 5.0}, {4.0, 4.0}});
177+
178+
geom::geometry_t geom{geom::collection_t{}, 49};
179+
auto &c = geom.get<geom::collection_t>();
180+
c.add_geometry(std::move(geom1));
181+
c.add_geometry(std::move(geom2));
182+
c.add_geometry(std::move(geom3));
183+
184+
auto const result = ewkb_to_geom(geom_to_ewkb(geom));
185+
REQUIRE(result.is_collection());
186+
REQUIRE(result.srid() == 49);
187+
REQUIRE(result == geom);
198188
}
199189

190+
TEST_CASE("wkb: invalid", "[NoDB]") { REQUIRE_THROWS(ewkb_to_geom("INVALID")); }
191+
200192
TEST_CASE("wkb hex decode of valid hex characters")
201193
{
202194
REQUIRE(decode_hex_char('0') == 0);

0 commit comments

Comments
 (0)