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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "gdal_unit_test.h"
30 : #include "cpl_string.h"
31 : #include "ogr_geometry.h"
32 : #include "gtest_include.h"
33 :
34 : #include <vector>
35 :
36 : static std::unique_ptr<OGRGeometry>
37 33 : organizePolygons(std::vector<OGRGeometry *> &polygons,
38 : const std::string &method)
39 : {
40 66 : CPLStringList options;
41 33 : options.AddNameValue("METHOD", method.c_str());
42 :
43 : return std::unique_ptr<OGRGeometry>(OGRGeometryFactory::organizePolygons(
44 33 : polygons.data(), static_cast<int>(polygons.size()), nullptr,
45 99 : (const char **)options.List()));
46 : }
47 :
48 70 : static OGRGeometry *readWKT(const std::string &wkt)
49 : {
50 : OGRGeometry *g;
51 70 : auto err = OGRGeometryFactory::createFromWkt(wkt.c_str(), nullptr, &g);
52 :
53 70 : if (err != OGRERR_NONE)
54 : {
55 0 : throw std::runtime_error("Failed to parse WKT");
56 : }
57 :
58 70 : return g;
59 : }
60 :
61 : class OrganizePolygonsTest : public testing::TestWithParam<std::string>
62 : {
63 : };
64 :
65 88 : INSTANTIATE_TEST_SUITE_P(
66 : test_gdal, OrganizePolygonsTest,
67 : ::testing::Values("DEFAULT", "ONLY_CCW", "SKIP"),
68 : [](const ::testing::TestParamInfo<std::string> ¶m_info) -> std::string
69 : { return param_info.param; });
70 :
71 7 : TEST_P(OrganizePolygonsTest, EmptyInputVector)
72 : {
73 3 : std::vector<OGRGeometry *> polygons;
74 :
75 3 : const auto &method = GetParam();
76 3 : auto result = organizePolygons(polygons, method);
77 :
78 3 : ASSERT_NE(result, nullptr);
79 3 : ASSERT_EQ(result->getGeometryType(), wkbPolygon);
80 3 : ASSERT_TRUE(result->IsEmpty());
81 : }
82 :
83 7 : TEST_P(OrganizePolygonsTest, SinglePolygonInput)
84 : {
85 3 : std::vector<OGRGeometry *> polygons;
86 3 : polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 0))"));
87 :
88 3 : std::unique_ptr<OGRGeometry> expected(polygons.front()->clone());
89 :
90 3 : const auto &method = GetParam();
91 3 : auto result = organizePolygons(polygons, method);
92 :
93 3 : ASSERT_NE(result, nullptr);
94 3 : ASSERT_EQ(result->getGeometryType(), wkbPolygon);
95 3 : ASSERT_TRUE(result->Equals(expected.get()));
96 : }
97 :
98 7 : TEST_P(OrganizePolygonsTest, SingleCurvePolygonInput)
99 : {
100 3 : std::vector<OGRGeometry *> polygons;
101 3 : polygons.push_back(readWKT("CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0))"));
102 :
103 3 : std::unique_ptr<OGRGeometry> expected(polygons.front()->clone());
104 :
105 3 : const auto &method = GetParam();
106 3 : auto result = organizePolygons(polygons, method);
107 :
108 3 : ASSERT_NE(result, nullptr);
109 3 : ASSERT_EQ(result->getGeometryType(), wkbCurvePolygon);
110 3 : ASSERT_TRUE(result->Equals(expected.get()));
111 : }
112 :
113 7 : TEST_P(OrganizePolygonsTest, SinglePointInput)
114 : {
115 3 : std::vector<OGRGeometry *> polygons;
116 3 : polygons.push_back(readWKT("POINT (0 0)"));
117 :
118 3 : const auto &method = GetParam();
119 3 : auto result = organizePolygons(polygons, method);
120 :
121 3 : ASSERT_NE(result, nullptr);
122 3 : ASSERT_EQ(result->getGeometryType(), wkbPolygon);
123 3 : ASSERT_TRUE(result->IsEmpty());
124 : }
125 :
126 7 : TEST_P(OrganizePolygonsTest, MixedPolygonCurvePolygonInput)
127 : {
128 3 : std::vector<OGRGeometry *> polygons;
129 3 : polygons.push_back(
130 3 : readWKT("POLYGON ((10 10, 20 10, 20 20, 20 10, 10 10))"));
131 3 : polygons.push_back(readWKT("CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0))"));
132 :
133 3 : const auto &method = GetParam();
134 3 : auto result = organizePolygons(polygons, method);
135 :
136 3 : ASSERT_NE(result, nullptr);
137 3 : ASSERT_EQ(result->getGeometryType(), wkbMultiSurface);
138 :
139 : std::unique_ptr<OGRGeometry> expected(
140 : readWKT("MULTISURFACE ("
141 : "POLYGON ((10 10, 20 10, 20 20, 20 10, 10 10)),"
142 6 : "CURVEPOLYGON ((0 0, 1 0, 1 1, 0 0)))"));
143 :
144 3 : ASSERT_TRUE(result->Equals(expected.get()));
145 : }
146 :
147 7 : TEST_P(OrganizePolygonsTest, MixedPolygonPointInput)
148 : {
149 3 : std::vector<OGRGeometry *> polygons;
150 3 : polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 0))"));
151 3 : polygons.push_back(readWKT("POINT (2 2)"));
152 :
153 3 : std::unique_ptr<OGRGeometry> expected(polygons[0]->clone());
154 :
155 3 : const auto &method = GetParam();
156 3 : auto result = organizePolygons(polygons, method);
157 :
158 3 : ASSERT_NE(result, nullptr);
159 3 : ASSERT_TRUE(result->Equals(expected.get()));
160 : }
161 :
162 7 : TEST_P(OrganizePolygonsTest, CWPolygonCCWHole)
163 : {
164 3 : std::vector<OGRGeometry *> polygons;
165 3 : polygons.push_back(readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))"));
166 3 : polygons.push_back(readWKT("POLYGON ((1 1, 2 1, 2 2, 1 2, 1 1))"));
167 :
168 3 : const auto &method = GetParam();
169 3 : auto result = organizePolygons(polygons, method);
170 :
171 3 : ASSERT_NE(result, nullptr);
172 :
173 0 : std::unique_ptr<OGRGeometry> expected;
174 3 : if (method == "SKIP")
175 : {
176 1 : expected.reset(readWKT("MULTIPOLYGON (((0 0, 0 10, 10 10, 10 0, 0 0)), "
177 : "((1 1, 2 1, 2 2, 1 2, 1 1)))"));
178 : }
179 : else
180 : {
181 2 : expected.reset(readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, "
182 : "2 1, 2 2, 1 2, 1 1))"));
183 : }
184 :
185 3 : ASSERT_TRUE(result->Equals(expected.get()));
186 : }
187 :
188 7 : TEST_P(OrganizePolygonsTest, CWPolygonCCWLakeCWIslandInLake)
189 : {
190 3 : std::vector<OGRGeometry *> polygons;
191 3 : polygons.push_back(
192 3 : readWKT("POLYGON ((0 0, 0 100, 100 100, 100 0, 0 0))")); // CW
193 3 : polygons.push_back(
194 3 : readWKT("POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10))")); // CCW
195 3 : polygons.push_back(
196 3 : readWKT("POLYGON ((15 15, 15 16, 16 16, 16 15, 15 15))")); // CW
197 :
198 3 : const auto &method = GetParam();
199 3 : auto result = organizePolygons(polygons, method);
200 :
201 3 : ASSERT_NE(result, nullptr);
202 :
203 3 : if (method != "SKIP")
204 : {
205 : std::unique_ptr<OGRGeometry> expected(
206 : readWKT("MULTIPOLYGON ("
207 : "((0 0, 0 100, 100 100, 100 0, 0 0), (10 10, 20 10, 20 20, "
208 : "10 20, 10 10)),"
209 4 : "((15 15, 15 16, 16 16, 16 15, 15 15)))"));
210 2 : ASSERT_TRUE(result->Equals(expected.get()));
211 : }
212 : }
213 :
214 7 : TEST_P(OrganizePolygonsTest, AdjacentCCWPolygons)
215 : {
216 3 : std::vector<OGRGeometry *> polygons;
217 3 : polygons.push_back(readWKT("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))")); // CCW
218 3 : polygons.push_back(readWKT("POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))")); // CCW
219 :
220 3 : const auto &method = GetParam();
221 3 : auto result = organizePolygons(polygons, method);
222 :
223 3 : ASSERT_NE(result, nullptr);
224 :
225 : std::unique_ptr<OGRGeometry> expected(
226 : readWKT("MULTIPOLYGON("
227 : "((0 0, 1 0, 1 1, 0 1, 0 0)), "
228 6 : "((1 0, 2 0, 2 1, 1 1, 1 0)))"));
229 3 : ASSERT_TRUE(result->Equals(expected.get()));
230 : }
231 :
232 7 : TEST_P(OrganizePolygonsTest, HoleAlongEdge)
233 : {
234 3 : std::vector<OGRGeometry *> polygons;
235 3 : polygons.push_back(
236 3 : readWKT("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")); // CW
237 3 : polygons.push_back(readWKT("POLYGON ((0 2, 1 2, 1 3, 0 3, 0 2))")); // CCW
238 :
239 3 : const auto &method = GetParam();
240 3 : auto result = organizePolygons(polygons, method);
241 :
242 3 : ASSERT_NE(result, nullptr);
243 :
244 3 : if (method != "SKIP")
245 : {
246 : std::unique_ptr<OGRGeometry> expected(
247 : readWKT("POLYGON("
248 : "(0 0, 0 10, 10 10, 10 0, 0 0), "
249 4 : "(0 2, 1 2, 1 3, 0 3, 0 2))"));
250 2 : ASSERT_TRUE(result->Equals(expected.get()));
251 : }
252 : }
253 :
254 7 : TEST_P(OrganizePolygonsTest, CrossingCCWPolygons)
255 : {
256 3 : std::vector<OGRGeometry *> polygons;
257 3 : polygons.push_back(readWKT("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"));
258 3 : polygons.push_back(readWKT("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))"));
259 :
260 3 : const auto &method = GetParam();
261 3 : auto result = organizePolygons(polygons, method);
262 :
263 3 : ASSERT_NE(result, nullptr);
264 :
265 : std::unique_ptr<OGRGeometry> expected(
266 : readWKT("MULTIPOLYGON("
267 : "((0 0, 10 0, 10 10, 0 10, 0 0)), "
268 6 : "((5 5, 15 5, 15 15, 5 15, 5 5)))"));
269 3 : ASSERT_TRUE(result->Equals(expected.get()));
270 : }
|