Line data Source code
1 : /**********************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Test OGRGeometryFactory::organizePolygons
5 : * Author: Daniel Baston <dbaston at gmail.com>
6 : *
7 : **********************************************************************
8 : * Copyright (c) 2023, ISciences LLC
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdal_unit_test.h"
14 : #include "cpl_string.h"
15 : #include "ogr_geometry.h"
16 : #include "gtest_include.h"
17 :
18 : #include <vector>
19 :
20 : #if defined(__clang__)
21 : #pragma clang diagnostic push
22 : #pragma clang diagnostic ignored "-Wweak-vtables"
23 : #endif
24 :
25 : static std::unique_ptr<OGRGeometry>
26 33 : organizePolygons(std::vector<OGRGeometry *> &polygons,
27 : const std::string &method)
28 : {
29 66 : CPLStringList options;
30 33 : options.AddNameValue("METHOD", method.c_str());
31 :
32 : return std::unique_ptr<OGRGeometry>(OGRGeometryFactory::organizePolygons(
33 33 : polygons.data(), static_cast<int>(polygons.size()), nullptr,
34 99 : (const char **)options.List()));
35 : }
36 :
37 70 : static OGRGeometry *readWKT(const std::string &wkt)
38 : {
39 : OGRGeometry *g;
40 70 : auto err = OGRGeometryFactory::createFromWkt(wkt.c_str(), nullptr, &g);
41 :
42 70 : if (err != OGRERR_NONE)
43 : {
44 0 : throw std::runtime_error("Failed to parse WKT");
45 : }
46 :
47 70 : return g;
48 : }
49 :
50 : class OrganizePolygonsTest : public testing::TestWithParam<std::string>
51 : {
52 : };
53 :
54 88 : INSTANTIATE_TEST_SUITE_P(
55 : test_gdal, OrganizePolygonsTest,
56 : ::testing::Values("DEFAULT", "ONLY_CCW", "SKIP"),
57 : [](const ::testing::TestParamInfo<std::string> ¶m_info) -> std::string
58 : { return param_info.param; });
59 :
60 7 : TEST_P(OrganizePolygonsTest, EmptyInputVector)
61 : {
62 3 : std::vector<OGRGeometry *> polygons;
63 :
64 3 : const auto &method = GetParam();
65 3 : auto result = organizePolygons(polygons, method);
66 :
67 3 : ASSERT_NE(result, nullptr);
68 3 : ASSERT_EQ(result->getGeometryType(), wkbPolygon);
69 3 : ASSERT_TRUE(result->IsEmpty());
70 : }
71 :
72 7 : TEST_P(OrganizePolygonsTest, SinglePolygonInput)
73 : {
74 3 : std::vector<OGRGeometry *> polygons;
75 3 : polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 0))"));
76 :
77 3 : std::unique_ptr<OGRGeometry> expected(polygons.front()->clone());
78 :
79 3 : const auto &method = GetParam();
80 3 : auto result = organizePolygons(polygons, method);
81 :
82 3 : ASSERT_NE(result, nullptr);
83 3 : ASSERT_EQ(result->getGeometryType(), wkbPolygon);
84 3 : ASSERT_TRUE(result->Equals(expected.get()));
85 : }
86 :
87 7 : TEST_P(OrganizePolygonsTest, SingleCurvePolygonInput)
88 : {
89 3 : std::vector<OGRGeometry *> polygons;
90 3 : polygons.push_back(readWKT("CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0))"));
91 :
92 3 : std::unique_ptr<OGRGeometry> expected(polygons.front()->clone());
93 :
94 3 : const auto &method = GetParam();
95 3 : auto result = organizePolygons(polygons, method);
96 :
97 3 : ASSERT_NE(result, nullptr);
98 3 : ASSERT_EQ(result->getGeometryType(), wkbCurvePolygon);
99 3 : ASSERT_TRUE(result->Equals(expected.get()));
100 : }
101 :
102 7 : TEST_P(OrganizePolygonsTest, SinglePointInput)
103 : {
104 3 : std::vector<OGRGeometry *> polygons;
105 3 : polygons.push_back(readWKT("POINT (0 0)"));
106 :
107 3 : const auto &method = GetParam();
108 3 : auto result = organizePolygons(polygons, method);
109 :
110 3 : ASSERT_NE(result, nullptr);
111 3 : ASSERT_EQ(result->getGeometryType(), wkbPolygon);
112 3 : ASSERT_TRUE(result->IsEmpty());
113 : }
114 :
115 7 : TEST_P(OrganizePolygonsTest, MixedPolygonCurvePolygonInput)
116 : {
117 3 : std::vector<OGRGeometry *> polygons;
118 3 : polygons.push_back(
119 3 : readWKT("POLYGON ((10 10, 20 10, 20 20, 20 10, 10 10))"));
120 3 : polygons.push_back(readWKT("CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0))"));
121 :
122 3 : const auto &method = GetParam();
123 3 : auto result = organizePolygons(polygons, method);
124 :
125 3 : ASSERT_NE(result, nullptr);
126 3 : ASSERT_EQ(result->getGeometryType(), wkbMultiSurface);
127 :
128 : std::unique_ptr<OGRGeometry> expected(
129 : readWKT("MULTISURFACE ("
130 : "POLYGON ((10 10, 20 10, 20 20, 20 10, 10 10)),"
131 6 : "CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0)))"));
132 :
133 3 : ASSERT_TRUE(result->Equals(expected.get()));
134 : }
135 :
136 7 : TEST_P(OrganizePolygonsTest, MixedPolygonPointInput)
137 : {
138 3 : std::vector<OGRGeometry *> polygons;
139 3 : polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 0))"));
140 3 : polygons.push_back(readWKT("POINT (2 2)"));
141 :
142 3 : std::unique_ptr<OGRGeometry> expected(polygons[0]->clone());
143 :
144 3 : const auto &method = GetParam();
145 3 : auto result = organizePolygons(polygons, method);
146 :
147 3 : ASSERT_NE(result, nullptr);
148 3 : ASSERT_TRUE(result->Equals(expected.get()));
149 : }
150 :
151 7 : TEST_P(OrganizePolygonsTest, CWPolygonCCWHole)
152 : {
153 3 : std::vector<OGRGeometry *> polygons;
154 3 : polygons.push_back(readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))"));
155 3 : polygons.push_back(readWKT("POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))"));
156 :
157 3 : const auto &method = GetParam();
158 3 : auto result = organizePolygons(polygons, method);
159 :
160 3 : ASSERT_NE(result, nullptr);
161 :
162 0 : std::unique_ptr<OGRGeometry> expected;
163 3 : if (method == "SKIP")
164 : {
165 1 : expected.reset(readWKT("MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0)), "
166 : "((1 1, 2 1, 2 2, 1 2, 1 1)))"));
167 : }
168 : else
169 : {
170 2 : expected.reset(readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, "
171 : "2 1, 2 2, 1 2, 1 1))"));
172 : }
173 :
174 3 : ASSERT_TRUE(result->Equals(expected.get()));
175 : }
176 :
177 7 : TEST_P(OrganizePolygonsTest, CWPolygonCCWLakeCWIslandInLake)
178 : {
179 3 : std::vector<OGRGeometry *> polygons;
180 3 : polygons.push_back(
181 3 : readWKT("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))")); // CW
182 3 : polygons.push_back(
183 3 : readWKT("POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))")); // CCW
184 3 : polygons.push_back(
185 3 : readWKT("POLYGON ((15 15, 15 16, 16 16, 16 15, 15 15))")); // CW
186 :
187 3 : const auto &method = GetParam();
188 3 : auto result = organizePolygons(polygons, method);
189 :
190 3 : ASSERT_NE(result, nullptr);
191 :
192 3 : if (method != "SKIP")
193 : {
194 : std::unique_ptr<OGRGeometry> expected(
195 : readWKT("MULTIPOLYGON ("
196 : "((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 20 10, 20 20, "
197 : "10 20, 10 10)),"
198 4 : "((15 15, 15 16, 16 16, 16 15, 15 15)))"));
199 2 : ASSERT_TRUE(result->Equals(expected.get()));
200 : }
201 : }
202 :
203 7 : TEST_P(OrganizePolygonsTest, AdjacentCCWPolygons)
204 : {
205 3 : std::vector<OGRGeometry *> polygons;
206 3 : polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")); // CCW
207 3 : polygons.push_back(readWKT("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))")); // CCW
208 :
209 3 : const auto &method = GetParam();
210 3 : auto result = organizePolygons(polygons, method);
211 :
212 3 : ASSERT_NE(result, nullptr);
213 :
214 : std::unique_ptr<OGRGeometry> expected(
215 : readWKT("MULTIPOLYGON("
216 : "((0 0, 1 0, 1 1, 0 1, 0 0)), "
217 6 : "((1 0, 2 0, 2 1, 1 1, 1 0)))"));
218 3 : ASSERT_TRUE(result->Equals(expected.get()));
219 : }
220 :
221 7 : TEST_P(OrganizePolygonsTest, HoleAlongEdge)
222 : {
223 3 : std::vector<OGRGeometry *> polygons;
224 3 : polygons.push_back(
225 3 : readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")); // CW
226 3 : polygons.push_back(readWKT("POLYGON ((0 2, 1 2, 1 3, 0 3, 0 2))")); // CCW
227 :
228 3 : const auto &method = GetParam();
229 3 : auto result = organizePolygons(polygons, method);
230 :
231 3 : ASSERT_NE(result, nullptr);
232 :
233 3 : if (method != "SKIP")
234 : {
235 : std::unique_ptr<OGRGeometry> expected(
236 : readWKT("POLYGON("
237 : "(0 0, 0 10, 10 10, 10 0, 0 0), "
238 4 : "(0 2, 1 2, 1 3, 0 3, 0 2))"));
239 2 : ASSERT_TRUE(result->Equals(expected.get()));
240 : }
241 : }
242 :
243 7 : TEST_P(OrganizePolygonsTest, CrossingCCWPolygons)
244 : {
245 3 : std::vector<OGRGeometry *> polygons;
246 3 : polygons.push_back(readWKT("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"));
247 3 : polygons.push_back(readWKT("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"));
248 :
249 3 : const auto &method = GetParam();
250 3 : auto result = organizePolygons(polygons, method);
251 :
252 3 : ASSERT_NE(result, nullptr);
253 :
254 : std::unique_ptr<OGRGeometry> expected(
255 : readWKT("MULTIPOLYGON("
256 : "((0 0, 10 0, 10 10, 0 10, 0 0)), "
257 6 : "((5 5, 15 5, 15 15, 5 15, 5 5)))"));
258 3 : ASSERT_TRUE(result->Equals(expected.get()));
259 : }
260 :
261 : #if defined(__clang__)
262 : #pragma clang diagnostic pop
263 : #endif
|