Skip to content

Commit be022b8

Browse files
authored
Merge pull request #1735 from joto/geom-geometry-n2
Add geometry_n() function to get the nth geometry of a multi geometry
2 parents c57bf45 + dd78822 commit be022b8

10 files changed

+100
-3
lines changed

src/geom-functions.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,61 @@ std::size_t num_geometries(geometry_t const &geom)
5959

6060
namespace {
6161

62+
class geometry_n_visitor
63+
{
64+
public:
65+
geometry_n_visitor(geometry_t *output, std::size_t n)
66+
: m_output(output), m_n(n)
67+
{}
68+
69+
void operator()(nullgeom_t const & /*input*/) const { m_output->reset(); }
70+
71+
void operator()(geom::collection_t const &input) const
72+
{
73+
*m_output = input[m_n - 1];
74+
}
75+
76+
template <typename T>
77+
void operator()(geom::multigeometry_t<T> const &input) const
78+
{
79+
m_output->set<T>() = input[m_n - 1];
80+
}
81+
82+
template <typename T>
83+
void operator()(T const &input) const
84+
{
85+
m_output->set<T>() = input;
86+
}
87+
88+
private:
89+
geometry_t *m_output;
90+
std::size_t m_n;
91+
92+
}; // class geometry_n_visitor
93+
94+
} // anonymous namespace
95+
96+
void geometry_n(geometry_t *output, geometry_t const &input, std::size_t n)
97+
{
98+
auto const num = num_geometries(input);
99+
if (n < 1 || n > num) {
100+
output->reset();
101+
return;
102+
}
103+
104+
input.visit(geometry_n_visitor{output, n});
105+
output->set_srid(input.srid());
106+
}
107+
108+
geometry_t geometry_n(geometry_t const &input, std::size_t n)
109+
{
110+
geom::geometry_t output{};
111+
geometry_n(&output, input, n);
112+
return output;
113+
}
114+
115+
namespace {
116+
62117
void set_to_same_type(geometry_t *output, geometry_t const &input)
63118
{
64119
input.visit(overloaded{[&](auto in) { output->set<decltype(in)>(); }});

src/geom-functions.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,30 @@ std::string_view geometry_type(geometry_t const &geom);
6464
*/
6565
std::size_t num_geometries(geometry_t const &geom);
6666

67+
/**
68+
* Get a copy of the nth geometry out of a (multi)geometry.
69+
*
70+
* For non-multi geometries and n==1 a copy of the single geometry is returned.
71+
* If n is out or range a null geometry is returned.
72+
*
73+
* \param output Pointer to output geometry.
74+
* \param input Input geometry.
75+
* \param n Number of geometry to get (1-based).
76+
*/
77+
void geometry_n(geometry_t *output, geometry_t const &input, std::size_t n);
78+
79+
/**
80+
* Get a copy of the nth geometry out of a (multi)geometry.
81+
*
82+
* For non-multi geometries and n==1 a copy of the single geometry is returned.
83+
* If n is out or range a null geometry is returned.
84+
*
85+
* \param input Input geometry.
86+
* \param n Number of geometry to get (1-based).
87+
* \returns The geometry.
88+
*/
89+
geometry_t geometry_n(geometry_t const &input, std::size_t n);
90+
6791
/**
6892
* Transform a geometry in 4326 into some other projection.
6993
*

tests/test-geom-collections.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ TEST_CASE("geometry collection with point", "[NoDB]")
2626
REQUIRE(num_geometries(geom) == 1);
2727
REQUIRE(area(geom) == Approx(0.0));
2828
REQUIRE_THROWS(centroid(geom));
29+
REQUIRE(geometry_n(geom, 1) == geom::geometry_t{geom::point_t{1, 1}});
2930
}
3031

3132
TEST_CASE("geometry collection with several geometries", "[NoDB]")
@@ -41,6 +42,10 @@ TEST_CASE("geometry collection with several geometries", "[NoDB]")
4142
REQUIRE(num_geometries(geom) == 3);
4243
REQUIRE(area(geom) == Approx(0.0));
4344
REQUIRE_THROWS(centroid(geom));
45+
REQUIRE(geometry_n(geom, 1) == geom::geometry_t{geom::point_t{1, 1}});
46+
REQUIRE(geometry_n(geom, 2) ==
47+
geom::geometry_t{geom::linestring_t{{1, 1}, {2, 2}}});
48+
REQUIRE(geometry_n(geom, 3) == geom::geometry_t{geom::point_t{2, 2}});
4449
}
4550

4651
TEST_CASE("create_collection from OSM data", "[NoDB]")

tests/test-geom-linestrings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ TEST_CASE("line geometry", "[NoDB]")
4646
REQUIRE(area(geom) == Approx(0.0));
4747
REQUIRE(geometry_type(geom) == "LINESTRING");
4848
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{1.5, 1.5}});
49+
REQUIRE(geometry_n(geom, 1) == geom);
4950
}
5051

5152
TEST_CASE("create_linestring from OSM data", "[NoDB]")

tests/test-geom-multilinestrings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ TEST_CASE("create_multilinestring from four lines forming two rings", "[NoDB]")
200200
REQUIRE(ml.num_geometries() == 2);
201201
REQUIRE(ml[0] == expected[0]);
202202
REQUIRE(ml[1] == expected[1]);
203+
REQUIRE(geometry_n(geom, 1).get<geom::linestring_t>() == expected[0]);
204+
REQUIRE(geometry_n(geom, 2).get<geom::linestring_t>() == expected[1]);
203205
}
204206

205207
TEST_CASE("create_multilinestring from Y shape", "[NoDB]")

tests/test-geom-multipoints.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ TEST_CASE("multipoint_t with a single point", "[NoDB]")
3737

3838
TEST_CASE("multipoint_t with several points", "[NoDB]")
3939
{
40-
geom::point_t const p0{1, 1};
41-
geom::point_t const p1{2, 1};
42-
geom::point_t const p2{3, 1};
40+
geom::point_t p0{1, 1};
41+
geom::point_t p1{2, 1};
42+
geom::point_t p2{3, 1};
4343

4444
geom::geometry_t geom{geom::multipoint_t{}};
4545
auto &mp = geom.get<geom::multipoint_t>();
@@ -56,4 +56,8 @@ TEST_CASE("multipoint_t with several points", "[NoDB]")
5656
REQUIRE(mp[0] == p0);
5757
REQUIRE(mp[1] == p1);
5858
REQUIRE(mp[2] == p2);
59+
60+
REQUIRE(geometry_n(geom, 1) == geom::geometry_t{std::move(p0)});
61+
REQUIRE(geometry_n(geom, 2) == geom::geometry_t{std::move(p1)});
62+
REQUIRE(geometry_n(geom, 3) == geom::geometry_t{std::move(p2)});
5963
}

tests/test-geom-multipolygons.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ TEST_CASE("multipolygon geometry with single outer, no inner", "[NoDB]")
2727
REQUIRE(num_geometries(geom) == 1);
2828
REQUIRE(area(geom) == Approx(1.0));
2929
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{0.5, 0.5}});
30+
REQUIRE(geometry_n(geom, 1) ==
31+
geom::geometry_t{geom::polygon_t{
32+
geom::ring_t{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}}});
3033
}
3134

3235
TEST_CASE("multipolygon geometry with two polygons", "[NoDB]")

tests/test-geom-null.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ TEST_CASE("null geometry", "[NoDB]")
2121
REQUIRE(area(geom) == 0.0);
2222
REQUIRE(geometry_type(geom) == "NULL");
2323
REQUIRE(centroid(geom).is_null());
24+
REQUIRE(geometry_n(geom, 1).is_null());
2425
}

tests/test-geom-points.cpp

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

tests/test-geom-polygons.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ TEST_CASE("polygon geometry without inner", "[NoDB]")
2424
REQUIRE(area(geom) == Approx(1.0));
2525
REQUIRE(geometry_type(geom) == "POLYGON");
2626
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{0.5, 0.5}});
27+
REQUIRE(geometry_n(geom, 1) == geom);
2728
}
2829

2930
TEST_CASE("polygon geometry without inner (reverse)", "[NoDB]")

0 commit comments

Comments
 (0)