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