Skip to content

Commit 0e76254

Browse files
committed
Add dimension() function for geometry types
This adds a dimension() function for all geometry types. The topological dimension is defined as 0 for point geometries, 1 for linear geometries and 2 for areal geometries. For geometry collections it is the largest dimension of any of its members. See https://postgis.net/docs/manual-3.3/ST_Dimension.html It is needed for the implementation of centroid() for geometry collections.
1 parent a8f169f commit 0e76254

10 files changed

Lines changed: 57 additions & 1 deletion

src/geom.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#include "geom.hpp"
1111

12+
#include <algorithm>
13+
#include <numeric>
14+
1215
namespace geom {
1316

1417
bool operator==(polygon_t const &a, polygon_t const &b) noexcept
@@ -21,4 +24,18 @@ bool operator!=(polygon_t const &a, polygon_t const &b) noexcept
2124
return !(a == b);
2225
}
2326

27+
std::size_t dimension(collection_t const &geom)
28+
{
29+
return std::accumulate(geom.cbegin(), geom.cend(), 0ULL,
30+
[](std::size_t max, auto const &member) {
31+
return std::max(max, dimension(member));
32+
});
33+
}
34+
35+
std::size_t dimension(geometry_t const &geom)
36+
{
37+
return geom.visit(
38+
overloaded{[&](auto const &input) { return dimension(input); }});
39+
}
40+
2441
} // namespace geom

src/geom.hpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ using multilinestring_t = multigeometry_t<linestring_t>;
224224
using multipolygon_t = multigeometry_t<polygon_t>;
225225

226226
class geometry_t;
227-
228227
using collection_t = multigeometry_t<geometry_t>;
229228

230229
/**
@@ -363,6 +362,27 @@ class geometry_t
363362

364363
}; // class geometry_t
365364

365+
inline std::size_t dimension(nullgeom_t) noexcept { return 0; }
366+
inline std::size_t dimension(point_t) noexcept { return 0; }
367+
inline std::size_t dimension(linestring_t) noexcept { return 1; }
368+
inline std::size_t dimension(polygon_t) noexcept { return 2; }
369+
inline std::size_t dimension(multipoint_t) noexcept { return 0; }
370+
inline std::size_t dimension(multilinestring_t) noexcept { return 1; }
371+
inline std::size_t dimension(multipolygon_t) noexcept { return 2; }
372+
373+
std::size_t dimension(collection_t const &geom);
374+
375+
/**
376+
* Return the dimension of this geometry. This is:
377+
*
378+
* 0 - for null and point geometries
379+
* 1 - for (multi)linestring geometries
380+
* 2 - for (multi)polygon geometries
381+
*
382+
* For geometry collections this is the largest dimension of all its members.
383+
*/
384+
std::size_t dimension(geometry_t const &geom);
385+
366386
} // namespace geom
367387

368388
// This magic is used for visiting geometries. For an explanation see for

tests/test-geom-collections.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ TEST_CASE("geometry collection with point", "[NoDB]")
2323
c.add_geometry(geom::geometry_t{geom::point_t{1, 1}});
2424

2525
REQUIRE(geometry_type(geom) == "GEOMETRYCOLLECTION");
26+
REQUIRE(dimension(geom) == 0);
2627
REQUIRE(num_geometries(geom) == 1);
2728
REQUIRE(area(geom) == Approx(0.0));
2829
REQUIRE(length(geom) == Approx(0.0));
@@ -40,6 +41,7 @@ TEST_CASE("geometry collection with several geometries", "[NoDB]")
4041
c.add_geometry(geom::geometry_t{geom::point_t{2, 2}});
4142

4243
REQUIRE(geometry_type(geom) == "GEOMETRYCOLLECTION");
44+
REQUIRE(dimension(geom) == 1);
4345
REQUIRE(num_geometries(geom) == 3);
4446
REQUIRE(area(geom) == Approx(0.0));
4547
REQUIRE(length(geom) == Approx(1.41421));
@@ -61,6 +63,7 @@ TEST_CASE("create_collection from OSM data", "[NoDB]")
6163
auto const geom = geom::create_collection(buffer.buffer());
6264

6365
REQUIRE(geometry_type(geom) == "GEOMETRYCOLLECTION");
66+
REQUIRE(dimension(geom) == 1);
6467
REQUIRE(num_geometries(geom) == 3);
6568

6669
auto const &c = geom.get<geom::collection_t>();
@@ -82,6 +85,7 @@ TEST_CASE("create_collection from no OSM data returns null geometry", "[NoDB]")
8285
auto const geom = geom::create_collection(buffer.buffer());
8386

8487
REQUIRE(geometry_type(geom) == "NULL");
88+
REQUIRE(dimension(geom) == 0);
8589
REQUIRE(num_geometries(geom) == 0);
8690
}
8791

tests/test-geom-linestrings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ TEST_CASE("line geometry", "[NoDB]")
4242
{
4343
geom::geometry_t const geom{geom::linestring_t{{1, 1}, {2, 2}}};
4444

45+
REQUIRE(dimension(geom) == 1);
4546
REQUIRE(num_geometries(geom) == 1);
4647
REQUIRE(area(geom) == Approx(0.0));
4748
REQUIRE(length(geom) == Approx(1.41421));
@@ -73,6 +74,7 @@ TEST_CASE("create_linestring from OSM data", "[NoDB]")
7374

7475
REQUIRE(geom.is_linestring());
7576
REQUIRE(geometry_type(geom) == "LINESTRING");
77+
REQUIRE(dimension(geom) == 1);
7678
REQUIRE(num_geometries(geom) == 1);
7779
REQUIRE(area(geom) == Approx(0.0));
7880
REQUIRE(length(geom) == Approx(1.41421));

tests/test-geom-multilinestrings.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ TEST_CASE("create_multilinestring with single line", "[NoDB]")
3030

3131
REQUIRE(geom.is_multilinestring());
3232
REQUIRE(geometry_type(geom) == "MULTILINESTRING");
33+
REQUIRE(dimension(geom) == 1);
3334
REQUIRE(num_geometries(geom) == 1);
3435
REQUIRE(area(geom) == Approx(0.0));
3536
REQUIRE(length(geom) == Approx(1.0));
@@ -74,6 +75,7 @@ TEST_CASE("create_multilinestring with single line forming a ring", "[NoDB]")
7475
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
7576

7677
REQUIRE(geom.is_multilinestring());
78+
REQUIRE(dimension(geom) == 1);
7779
auto const &ml = geom.get<geom::multilinestring_t>();
7880
REQUIRE(ml.num_geometries() == 1);
7981
REQUIRE(ml[0] == expected);
@@ -92,6 +94,7 @@ TEST_CASE("create_multilinestring from two non-joined lines", "[NoDB]")
9294
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
9395

9496
REQUIRE(geom.is_multilinestring());
97+
REQUIRE(dimension(geom) == 1);
9598
auto const &ml = geom.get<geom::multilinestring_t>();
9699
REQUIRE(ml.num_geometries() == 2);
97100
REQUIRE(ml[0] == expected[0]);

tests/test-geom-multipoints.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ TEST_CASE("multipoint_t with a single point", "[NoDB]")
2828

2929
REQUIRE(geom.is_multipoint());
3030
REQUIRE(geometry_type(geom) == "MULTIPOINT");
31+
REQUIRE(dimension(geom) == 0);
3132
REQUIRE(num_geometries(geom) == 1);
3233
REQUIRE(area(geom) == Approx(0.0));
3334
REQUIRE(length(geom) == Approx(0.0));

tests/test-geom-multipolygons.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ TEST_CASE("multipolygon geometry with single outer, no inner", "[NoDB]")
2424
geom::polygon_t{geom::ring_t{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}});
2525

2626
REQUIRE(geometry_type(geom) == "MULTIPOLYGON");
27+
REQUIRE(dimension(geom) == 2);
2728
REQUIRE(num_geometries(geom) == 1);
2829
REQUIRE(area(geom) == Approx(1.0));
2930
REQUIRE(length(geom) == Approx(0.0));
@@ -51,6 +52,7 @@ TEST_CASE("multipolygon geometry with two polygons", "[NoDB]")
5152
mp.add_geometry(std::move(polygon));
5253

5354
REQUIRE(geometry_type(geom) == "MULTIPOLYGON");
55+
REQUIRE(dimension(geom) == 2);
5456
REQUIRE(num_geometries(geom) == 2);
5557
REQUIRE(area(geom) == Approx(9.0));
5658
REQUIRE(length(geom) == Approx(0.0));
@@ -67,6 +69,7 @@ TEST_CASE("create_multipolygon creates simple polygon from OSM data", "[NoDB]")
6769

6870
REQUIRE(geom.is_polygon());
6971
REQUIRE(geometry_type(geom) == "POLYGON");
72+
REQUIRE(dimension(geom) == 2);
7073
REQUIRE(num_geometries(geom) == 1);
7174
REQUIRE(area(geom) == Approx(1.0));
7275
REQUIRE(length(geom) == Approx(0.0));

tests/test-geom-null.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ TEST_CASE("null geometry", "[NoDB]")
1717
{
1818
geom::geometry_t const geom{};
1919

20+
REQUIRE(dimension(geom) == 0);
2021
REQUIRE(num_geometries(geom) == 0);
2122
REQUIRE(area(geom) == 0.0);
2223
REQUIRE(length(geom) == Approx(0.0));

tests/test-geom-points.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ TEST_CASE("create_point from OSM data", "[NoDB]")
5050

5151
REQUIRE(geom.is_point());
5252
REQUIRE(geometry_type(geom) == "POINT");
53+
REQUIRE(dimension(geom) == 0);
5354
REQUIRE(num_geometries(geom) == 1);
5455
REQUIRE(area(geom) == Approx(0.0));
5556
REQUIRE(length(geom) == Approx(0.0));

tests/test-geom-polygons.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ TEST_CASE("polygon geometry without inner", "[NoDB]")
2020
geom::geometry_t const geom{
2121
geom::polygon_t{geom::ring_t{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}}};
2222

23+
REQUIRE(dimension(geom) == 2);
2324
REQUIRE(num_geometries(geom) == 1);
2425
REQUIRE(area(geom) == Approx(1.0));
2526
REQUIRE(length(geom) == Approx(0.0));
@@ -33,6 +34,7 @@ TEST_CASE("polygon geometry without inner (reverse)", "[NoDB]")
3334
geom::geometry_t const geom{
3435
geom::polygon_t{geom::ring_t{{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}}};
3536

37+
REQUIRE(dimension(geom) == 2);
3638
REQUIRE(num_geometries(geom) == 1);
3739
REQUIRE(area(geom) == Approx(1.0));
3840
REQUIRE(length(geom) == Approx(0.0));
@@ -53,6 +55,7 @@ TEST_CASE("geom::polygon_t", "[NoDB]")
5355
REQUIRE(polygon.inners().size() == 1);
5456

5557
geom::geometry_t geom{std::move(polygon)};
58+
REQUIRE(dimension(geom) == 2);
5659
REQUIRE(num_geometries(geom) == 1);
5760
REQUIRE(area(geom) == Approx(8.0));
5861
REQUIRE(length(geom) == Approx(0.0));
@@ -78,6 +81,7 @@ TEST_CASE("create_polygon from OSM data", "[NoDB]")
7881

7982
REQUIRE(geom.is_polygon());
8083
REQUIRE(geometry_type(geom) == "POLYGON");
84+
REQUIRE(dimension(geom) == 2);
8185
REQUIRE(num_geometries(geom) == 1);
8286
REQUIRE(area(geom) == Approx(1.0));
8387
REQUIRE(length(geom) == Approx(0.0));

0 commit comments

Comments
 (0)