Skip to content

Commit f7ce135

Browse files
authored
Merge pull request #1760 from joto/geom-add-reverse
Add reverse() function to reverse linestring (and other) geometries
2 parents 0899d68 + 8b55943 commit f7ce135

9 files changed

+118
-0
lines changed

src/flex-lua-geom.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,20 @@ static int geom_is_null(lua_State *lua_state)
119119
return 1;
120120
}
121121

122+
static int geom_reverse(lua_State *lua_state)
123+
{
124+
auto const *const input_geometry = unpack_geometry(lua_state);
125+
126+
try {
127+
auto *geom = create_lua_geometry_object(lua_state);
128+
geom::reverse(geom, *input_geometry);
129+
} catch (...) {
130+
return luaL_error(lua_state, "Unknown error in 'reverse()'.\n");
131+
}
132+
133+
return 1;
134+
}
135+
122136
static int geom_line_merge(lua_State *lua_state)
123137
{
124138
auto const *const input_geometry = unpack_geometry(lua_state);
@@ -230,6 +244,7 @@ void init_geometry_class(lua_State *lua_state)
230244
luaX_add_table_func(lua_state, "geometry_type", geom_geometry_type);
231245
luaX_add_table_func(lua_state, "is_null", geom_is_null);
232246
luaX_add_table_func(lua_state, "line_merge", geom_line_merge);
247+
luaX_add_table_func(lua_state, "reverse", geom_reverse);
233248
luaX_add_table_func(lua_state, "num_geometries", geom_num_geometries);
234249
luaX_add_table_func(lua_state, "segmentize", geom_segmentize);
235250
luaX_add_table_func(lua_state, "simplify", geom_simplify);

src/geom-functions.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,62 @@ static void add_nodes_to_linestring(linestring_t *linestring, ITERATOR it,
446446
}
447447
}
448448

449+
/****************************************************************************/
450+
451+
static void reverse(geom::nullgeom_t * /*output*/,
452+
geom::nullgeom_t const & /*input*/) noexcept
453+
{}
454+
455+
static void reverse(geom::point_t *output, geom::point_t const &input) noexcept
456+
{
457+
*output = input;
458+
}
459+
460+
static void reverse(point_list_t *output, point_list_t const &input)
461+
{
462+
output->reserve(input.size());
463+
std::reverse_copy(input.cbegin(), input.cend(),
464+
std::back_inserter(*output));
465+
}
466+
467+
static void reverse(geom::polygon_t *output, geom::polygon_t const &input)
468+
{
469+
reverse(&output->outer(), input.outer());
470+
for (auto const &g : input.inners()) {
471+
reverse(&output->inners().emplace_back(), g);
472+
}
473+
}
474+
475+
template <typename T>
476+
void reverse(geom::multigeometry_t<T> *output,
477+
geom::multigeometry_t<T> const &input)
478+
{
479+
output->reserve(input.num_geometries());
480+
for (auto const &g : input) {
481+
reverse(&output->add_geometry(), g);
482+
}
483+
}
484+
485+
void reverse(geometry_t *output, geometry_t const &input)
486+
{
487+
output->set_srid(input.srid());
488+
489+
input.visit([&](auto const &geom) {
490+
using inner_type =
491+
std::remove_const_t<std::remove_reference_t<decltype(geom)>>;
492+
return reverse(&output->set<inner_type>(), geom);
493+
});
494+
}
495+
496+
geometry_t reverse(geometry_t const &input)
497+
{
498+
geometry_t output;
499+
reverse(&output, input);
500+
return output;
501+
}
502+
503+
/****************************************************************************/
504+
449505
void line_merge(geometry_t *output, geometry_t const &input)
450506
{
451507
if (input.is_linestring()) {

src/geom-functions.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,22 @@ double area(geometry_t const &geom);
158158
*/
159159
std::vector<geometry_t> split_multi(geometry_t &&geom, bool split_multi = true);
160160

161+
/**
162+
* Reverses the order of the vertices in geometry.
163+
*
164+
* \param output Pointer to output geometry.
165+
* \param input Input geometry.
166+
*/
167+
void reverse(geometry_t *output, geometry_t const &input);
168+
169+
/**
170+
* Reverses the order of the vertices in geometry.
171+
*
172+
* \param input Input geometry.
173+
* \returns Result geometry.
174+
*/
175+
geometry_t reverse(geometry_t const &input);
176+
161177
/**
162178
* Merge lines in a multilinestring end-to-end as far as possible.
163179
*

tests/test-geom-linestrings.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ TEST_CASE("line geometry", "[NoDB]")
4949
REQUIRE(geometry_n(geom, 1) == geom);
5050
}
5151

52+
TEST_CASE("reverse line geometry", "[NoDB]")
53+
{
54+
geom::geometry_t const geom{geom::linestring_t{{1, 1}, {2, 2}}};
55+
56+
auto reversed = geom::reverse(geom);
57+
REQUIRE(num_geometries(reversed) == 1);
58+
REQUIRE(geometry_type(reversed) == "LINESTRING");
59+
60+
auto const &line = reversed.get<geom::linestring_t>();
61+
REQUIRE(line.size() == 2);
62+
REQUIRE(line == geom::linestring_t{{2, 2}, {1, 1}});
63+
}
64+
5265
TEST_CASE("create_linestring from OSM data", "[NoDB]")
5366
{
5467
test_buffer_t buffer;

tests/test-geom-multilinestrings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
TEST_CASE("create_multilinestring with single line", "[NoDB]")
2121
{
2222
geom::linestring_t const expected{{1, 1}, {2, 1}};
23+
geom::linestring_t const expected_rev{{2, 1}, {1, 1}};
2324

2425
test_buffer_t buffer;
2526
buffer.add_way("w20 Nn10x1y1,n11x2y1");
@@ -34,6 +35,10 @@ TEST_CASE("create_multilinestring with single line", "[NoDB]")
3435
auto const &ml = geom.get<geom::multilinestring_t>();
3536
REQUIRE(ml.num_geometries() == 1);
3637
REQUIRE(ml[0] == expected);
38+
39+
auto const rev = reverse(geom);
40+
REQUIRE(rev.is_multilinestring());
41+
REQUIRE(rev.get<geom::multilinestring_t>()[0] == expected_rev);
3742
}
3843

3944
TEST_CASE("create_multilinestring with single line and no force_multi",

tests/test-geom-multipoints.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ TEST_CASE("multipoint_t with a single point", "[NoDB]")
3030
REQUIRE(geometry_type(geom) == "MULTIPOINT");
3131
REQUIRE(num_geometries(geom) == 1);
3232
REQUIRE(area(geom) == Approx(0.0));
33+
REQUIRE(reverse(geom) == geom);
3334
REQUIRE(centroid(geom) == geom::geometry_t{std::move(point)});
3435

3536
REQUIRE(mp[0] == expected);
@@ -51,6 +52,7 @@ TEST_CASE("multipoint_t with several points", "[NoDB]")
5152
REQUIRE(geometry_type(geom) == "MULTIPOINT");
5253
REQUIRE(num_geometries(geom) == 3);
5354
REQUIRE(area(geom) == Approx(0.0));
55+
REQUIRE(reverse(geom) == geom);
5456
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{2, 1}});
5557

5658
REQUIRE(mp[0] == p0);

tests/test-geom-null.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ TEST_CASE("null geometry", "[NoDB]")
2222
REQUIRE(geometry_type(geom) == "NULL");
2323
REQUIRE(centroid(geom).is_null());
2424
REQUIRE(geometry_n(geom, 1).is_null());
25+
REQUIRE(reverse(geom).is_null());
2526
}

tests/test-geom-points.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ TEST_CASE("create_point from OSM data", "[NoDB]")
5454
REQUIRE(area(geom) == Approx(0.0));
5555
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{1.1, 2.2}});
5656
REQUIRE(geometry_n(geom, 1) == geom);
57+
REQUIRE(reverse(geom) == geom);
5758
REQUIRE(geom.get<geom::point_t>() == geom::point_t{1.1, 2.2});
5859
}
5960

tests/test-geom-polygons.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ TEST_CASE("geom::polygon_t", "[NoDB]")
6666
REQUIRE(area(geom) == Approx(8.0));
6767
REQUIRE(geometry_type(geom) == "POLYGON");
6868
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{1.5, 1.5}});
69+
70+
auto const geom_rev = reverse(geom);
71+
REQUIRE(geom_rev.is_polygon());
72+
auto const &rev = geom_rev.get<geom::polygon_t>();
73+
REQUIRE(rev.outer() ==
74+
geom::ring_t{{0, 0}, {3, 0}, {3, 3}, {0, 3}, {0, 0}});
75+
REQUIRE(rev.inners().size() == 1);
76+
REQUIRE(rev.inners()[0] ==
77+
geom::ring_t{{1, 1}, {1, 2}, {2, 2}, {2, 1}, {1, 1}});
6978
}
7079

7180
TEST_CASE("create_polygon from OSM data", "[NoDB]")

0 commit comments

Comments
 (0)