Skip to content

Commit ddd6d64

Browse files
committed
Add num_geometries(), geometry_type(), and centroid() functions
Tests are split up by geometry type and there are additional tests.
1 parent 49d378b commit ddd6d64

File tree

6 files changed

+192
-31
lines changed

6 files changed

+192
-31
lines changed

src/geom-functions.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* For a full list of authors see the git log.
88
*/
99

10+
#include "geom-boost-adaptor.hpp"
1011
#include "geom-functions.hpp"
1112

1213
#include <algorithm>
@@ -30,6 +31,32 @@ point_t interpolate(point_t p1, point_t p2, double frac) noexcept
3031
frac * (p1.y() - p2.y()) + p2.y()};
3132
}
3233

34+
std::string_view geometry_type(geometry_t const &geom)
35+
{
36+
using namespace std::literals::string_view_literals;
37+
return geom.visit(overloaded{
38+
[&](geom::nullgeom_t const & /*input*/) { return "NULL"sv; },
39+
[&](geom::point_t const & /*input*/) { return "POINT"sv; },
40+
[&](geom::linestring_t const & /*input*/) { return "LINESTRING"sv; },
41+
[&](geom::polygon_t const & /*input*/) { return "POLYGON"sv; },
42+
[&](geom::multipoint_t const & /*input*/) { return "MULTIPOINT"sv; },
43+
[&](geom::multilinestring_t const & /*input*/) {
44+
return "MULTILINESTRING"sv;
45+
},
46+
[&](geom::multipolygon_t const & /*input*/) {
47+
return "MULTIPOLYGON"sv;
48+
},
49+
[&](geom::collection_t const & /*input*/) {
50+
return "GEOMETRYCOLLECTION"sv;
51+
}});
52+
}
53+
54+
std::size_t num_geometries(geometry_t const &geom)
55+
{
56+
return geom.visit(
57+
overloaded{[&](auto const &input) { return input.num_geometries(); }});
58+
}
59+
3360
namespace {
3461

3562
class transform_visitor
@@ -519,4 +546,19 @@ geometry_t line_merge(geometry_t geom)
519546
return output;
520547
}
521548

549+
geometry_t centroid(geometry_t const &geom)
550+
{
551+
geom::geometry_t output{point_t{}, geom.srid()};
552+
auto &center = output.get<point_t>();
553+
554+
geom.visit(overloaded{
555+
[&](geom::nullgeom_t const & /*input*/) { output.reset(); },
556+
[&](geom::collection_t const & /*input*/) {
557+
throw std::runtime_error{"not implemented yet"};
558+
},
559+
[&](auto const &input) { boost::geometry::centroid(input, center); }});
560+
561+
return output;
562+
}
563+
522564
} // namespace geom

src/geom-functions.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "geom.hpp"
1414
#include "reprojection.hpp"
1515

16+
#include <string_view>
1617
#include <utility>
1718
#include <vector>
1819

@@ -51,6 +52,18 @@ void for_each_segment(point_list_t const &list, FUNC&& func)
5152
}
5253
}
5354

55+
/**
56+
* Return the type of a geometry as string: NULL, (MULTI)POINT,
57+
* (MULTI)LINESTRING, (MULTI)POLYGON, or GEOMETRYCOLLECTION.
58+
*/
59+
std::string_view geometry_type(geometry_t const &geom);
60+
61+
/**
62+
* Return the number of geometries in this geometry. For the null geometry
63+
* this is always 0, for other non-multi geometries this is always 1.
64+
*/
65+
std::size_t num_geometries(geometry_t const &geom);
66+
5467
/**
5568
* Transform a geometry in 4326 into some other projection.
5669
*
@@ -105,6 +118,14 @@ std::vector<geometry_t> split_multi(geometry_t geom, bool split_multi = true);
105118
*/
106119
geometry_t line_merge(geometry_t geom);
107120

121+
/**
122+
* Calculate the centroid of a geometry.
123+
*
124+
* \param geom Input geometry.
125+
* \returns Resulting point geometry.
126+
*/
127+
geometry_t centroid(geometry_t const &geom);
128+
108129
} // namespace geom
109130

110131
#endif // OSM2PGSQL_GEOM_FUNCTIONS_HPP

tests/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@ set_test(test-db-copy-thread)
4242
set_test(test-db-copy-mgr)
4343
set_test(test-domain-matcher LABELS NoDB)
4444
set_test(test-expire-tiles LABELS NoDB)
45-
set_test(test-geom LABELS NoDB)
4645
set_test(test-geom-box LABELS NoDB)
46+
set_test(test-geom-lines LABELS NoDB)
47+
set_test(test-geom-null LABELS NoDB)
48+
set_test(test-geom-points LABELS NoDB)
4749
set_test(test-middle)
4850
set_test(test-node-locations LABELS NoDB)
4951
set_test(test-options-database LABELS NoDB)
Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,9 @@
1414
#include "geom-from-osm.hpp"
1515
#include "geom-functions.hpp"
1616
#include "geom.hpp"
17-
#include "reprojection.hpp"
1817

1918
#include <array>
2019

21-
TEST_CASE("geom::distance", "[NoDB]")
22-
{
23-
geom::point_t const p1{10, 10};
24-
geom::point_t const p2{20, 10};
25-
geom::point_t const p3{13, 14};
26-
27-
REQUIRE(geom::distance(p1, p1) == Approx(0.0));
28-
REQUIRE(geom::distance(p1, p2) == Approx(10.0));
29-
REQUIRE(geom::distance(p1, p3) == Approx(5.0));
30-
}
31-
32-
TEST_CASE("geom::interpolate", "[NoDB]")
33-
{
34-
geom::point_t const p1{10, 10};
35-
geom::point_t const p2{20, 10};
36-
37-
auto const i1 = geom::interpolate(p1, p1, 0.5);
38-
REQUIRE(i1.x() == 10);
39-
REQUIRE(i1.y() == 10);
40-
41-
auto const i2 = geom::interpolate(p1, p2, 0.5);
42-
REQUIRE(i2.x() == 15);
43-
REQUIRE(i2.y() == 10);
44-
45-
auto const i3 = geom::interpolate(p2, p1, 0.5);
46-
REQUIRE(i3.x() == 15);
47-
REQUIRE(i3.y() == 10);
48-
}
49-
5020
TEST_CASE("geom::linestring_t", "[NoDB]")
5121
{
5222
geom::linestring_t ls1;
@@ -64,6 +34,18 @@ TEST_CASE("geom::linestring_t", "[NoDB]")
6434
REQUIRE(it->y() == 22);
6535
++it;
6636
REQUIRE(it == ls1.cend());
37+
38+
REQUIRE(ls1.num_geometries() == 1);
39+
}
40+
41+
TEST_CASE("line geometry", "[NoDB]")
42+
{
43+
geom::geometry_t const geom{geom::linestring_t{{1, 1}, {2, 2}}};
44+
45+
REQUIRE(num_geometries(geom) == 1);
46+
REQUIRE(area(geom) == Approx(0.0));
47+
REQUIRE(geometry_type(geom) == "LINESTRING");
48+
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{1.5, 1.5}});
6749
}
6850

6951
TEST_CASE("geom::segmentize w/o split", "[NoDB]")
@@ -73,6 +55,7 @@ TEST_CASE("geom::segmentize w/o split", "[NoDB]")
7355
auto const geom = geom::segmentize(geom::geometry_t{line}, 10.0);
7456

7557
REQUIRE(geom.is_multilinestring());
58+
REQUIRE(num_geometries(geom) == 1);
7659
auto const &ml = geom.get<geom::multilinestring_t>();
7760
REQUIRE(ml.num_geometries() == 1);
7861
REQUIRE(ml[0] == line);
@@ -182,6 +165,9 @@ TEST_CASE("create_multilinestring with single line", "[NoDB]")
182165
geom::line_merge(geom::create_multilinestring(buffer.buffer()));
183166

184167
REQUIRE(geom.is_multilinestring());
168+
REQUIRE(geometry_type(geom) == "MULTILINESTRING");
169+
REQUIRE(num_geometries(geom) == 1);
170+
REQUIRE(area(geom) == Approx(0.0));
185171
auto const &ml = geom.get<geom::multilinestring_t>();
186172
REQUIRE(ml.num_geometries() == 1);
187173
REQUIRE(ml[0] == expected);

tests/test-geom-null.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* SPDX-License-Identifier: GPL-2.0-or-later
3+
*
4+
* This file is part of osm2pgsql (https://osm2pgsql.org/).
5+
*
6+
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
7+
* For a full list of authors see the git log.
8+
*/
9+
10+
#include <catch.hpp>
11+
12+
#include "geom-from-osm.hpp"
13+
#include "geom-functions.hpp"
14+
#include "geom.hpp"
15+
16+
TEST_CASE("null geometry", "[NoDB]")
17+
{
18+
geom::geometry_t const geom{};
19+
20+
REQUIRE(num_geometries(geom) == 0);
21+
REQUIRE(area(geom) == 0.0);
22+
REQUIRE(geometry_type(geom) == "NULL");
23+
REQUIRE(centroid(geom).is_null());
24+
}

tests/test-geom-points.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* SPDX-License-Identifier: GPL-2.0-or-later
3+
*
4+
* This file is part of osm2pgsql (https://osm2pgsql.org/).
5+
*
6+
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
7+
* For a full list of authors see the git log.
8+
*/
9+
10+
#include <catch.hpp>
11+
12+
#include "common-buffer.hpp"
13+
14+
#include "geom-from-osm.hpp"
15+
#include "geom-functions.hpp"
16+
#include "geom.hpp"
17+
18+
TEST_CASE("geom::point_t", "[NoDB]")
19+
{
20+
geom::point_t p;
21+
22+
REQUIRE(p.x() == Approx(0.0));
23+
REQUIRE(p.y() == Approx(0.0));
24+
25+
p.set_x(1.2);
26+
p.set_y(3.4);
27+
28+
REQUIRE(p.x() == Approx(1.2));
29+
REQUIRE(p.y() == Approx(3.4));
30+
31+
REQUIRE(p.num_geometries() == 1);
32+
}
33+
34+
TEST_CASE("geom::point_t from location", "[NoDB]")
35+
{
36+
osmium::Location const location{3.141, 2.718};
37+
geom::point_t const p{location};
38+
39+
REQUIRE(p.x() == Approx(3.141));
40+
REQUIRE(p.y() == Approx(2.718));
41+
REQUIRE(p == geom::point_t{3.141, 2.718});
42+
}
43+
44+
TEST_CASE("create_point from OSM data", "[NoDB]")
45+
{
46+
test_buffer_t buffer;
47+
buffer.add_node("n10 x1.1 y2.2");
48+
49+
auto const geom = geom::create_point(buffer.buffer().get<osmium::Node>(0));
50+
51+
REQUIRE(geom.is_point());
52+
REQUIRE(geometry_type(geom) == "POINT");
53+
REQUIRE(num_geometries(geom) == 1);
54+
REQUIRE(area(geom) == Approx(0.0));
55+
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{1.1, 2.2}});
56+
REQUIRE(geom.get<geom::point_t>() == geom::point_t{1.1, 2.2});
57+
}
58+
59+
TEST_CASE("geom::distance", "[NoDB]")
60+
{
61+
geom::point_t const p1{10, 10};
62+
geom::point_t const p2{20, 10};
63+
geom::point_t const p3{13, 14};
64+
65+
REQUIRE(geom::distance(p1, p1) == Approx(0.0));
66+
REQUIRE(geom::distance(p1, p2) == Approx(10.0));
67+
REQUIRE(geom::distance(p1, p3) == Approx(5.0));
68+
}
69+
70+
TEST_CASE("geom::interpolate", "[NoDB]")
71+
{
72+
geom::point_t const p1{10, 10};
73+
geom::point_t const p2{20, 10};
74+
75+
auto const i1 = geom::interpolate(p1, p1, 0.5);
76+
REQUIRE(i1.x() == 10);
77+
REQUIRE(i1.y() == 10);
78+
79+
auto const i2 = geom::interpolate(p1, p2, 0.5);
80+
REQUIRE(i2.x() == 15);
81+
REQUIRE(i2.y() == 10);
82+
83+
auto const i3 = geom::interpolate(p2, p1, 0.5);
84+
REQUIRE(i3.x() == 15);
85+
REQUIRE(i3.y() == 10);
86+
}

0 commit comments

Comments
 (0)