Line data Source code
1 : ///////////////////////////////////////////////////////////////////////////////
2 : //
3 : // Project: C++ Test Suite for GDAL/OGR
4 : // Purpose: Test coordinate transformations. Ported from osr/osr_ct.py.
5 : // Author: Mateusz Loskot <mateusz@loskot.net>
6 : //
7 : ///////////////////////////////////////////////////////////////////////////////
8 : // Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
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
21 : * OR 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 :
31 : #include "cpl_conv.h"
32 : #include "cpl_error.h"
33 : #include "ogr_api.h"
34 : #include "ogr_srs_api.h"
35 : #include "ogr_spatialref.h"
36 :
37 : #include <algorithm>
38 : #include <cmath>
39 : #include <string>
40 :
41 : #include "gtest_include.h"
42 :
43 : namespace
44 : {
45 :
46 : // Common fixture with test data
47 : struct test_osr_ct : public ::testing::Test
48 : {
49 : OGRErr err_ = OGRERR_NONE;
50 : OGRSpatialReferenceH srs_utm_ = nullptr;
51 : OGRSpatialReferenceH srs_ll_ = nullptr;
52 : OGRCoordinateTransformationH ct_ = nullptr;
53 :
54 9 : void SetUp() override
55 : {
56 9 : srs_utm_ = OSRNewSpatialReference(nullptr);
57 9 : srs_ll_ = OSRNewSpatialReference(nullptr);
58 9 : OSRSetAxisMappingStrategy(srs_utm_, OAMS_TRADITIONAL_GIS_ORDER);
59 9 : OSRSetAxisMappingStrategy(srs_ll_, OAMS_TRADITIONAL_GIS_ORDER);
60 9 : }
61 :
62 9 : void TearDown() override
63 : {
64 9 : OSRDestroySpatialReference(srs_utm_);
65 9 : srs_utm_ = nullptr;
66 9 : OSRDestroySpatialReference(srs_ll_);
67 9 : srs_ll_ = nullptr;
68 9 : OCTDestroyCoordinateTransformation(ct_);
69 9 : ct_ = nullptr;
70 9 : }
71 : };
72 :
73 4 : TEST_F(test_osr_ct, basic)
74 : {
75 1 : err_ = OSRSetUTM(srs_utm_, 11, TRUE);
76 1 : ASSERT_EQ(err_, OGRERR_NONE);
77 :
78 1 : err_ = OSRSetWellKnownGeogCS(srs_utm_, "WGS84");
79 1 : ASSERT_EQ(err_, OGRERR_NONE);
80 :
81 1 : err_ = OSRSetWellKnownGeogCS(srs_ll_, "WGS84");
82 1 : ASSERT_EQ(err_, OGRERR_NONE);
83 :
84 1 : ct_ = OCTNewCoordinateTransformation(srs_ll_, srs_utm_);
85 1 : ASSERT_TRUE(nullptr != ct_);
86 : }
87 :
88 : // Actually perform a simple LL to UTM conversion
89 4 : TEST_F(test_osr_ct, LL_to_UTM)
90 : {
91 1 : err_ = OSRSetUTM(srs_utm_, 11, TRUE);
92 1 : ASSERT_EQ(err_, OGRERR_NONE);
93 :
94 1 : err_ = OSRSetWellKnownGeogCS(srs_utm_, "WGS84");
95 1 : ASSERT_EQ(err_, OGRERR_NONE);
96 :
97 1 : err_ = OSRSetWellKnownGeogCS(srs_ll_, "WGS84");
98 1 : ASSERT_EQ(err_, OGRERR_NONE);
99 :
100 1 : ct_ = OCTNewCoordinateTransformation(srs_ll_, srs_utm_);
101 1 : ASSERT_TRUE(nullptr != ct_);
102 :
103 1 : const int size = 1;
104 1 : double x[size] = {-117.5};
105 1 : double y[size] = {32.0};
106 1 : double z[size] = {0.0};
107 :
108 1 : ASSERT_EQ(OCTTransform(ct_, size, x, y, z), TRUE);
109 :
110 1 : EXPECT_NEAR(x[0], 452772.06, 0.01);
111 1 : EXPECT_NEAR(y[0], 3540544.89, 0.01);
112 1 : EXPECT_NEAR(z[0], 0.0, 0.01);
113 : }
114 :
115 : // Transform an OGR geometry.
116 : // This is mostly aimed at ensuring that the OGRCoordinateTransformation
117 : // target SRS isn't deleted till the output geometry which also
118 : // uses it is deleted.
119 4 : TEST_F(test_osr_ct, OGR_G_Transform)
120 : {
121 1 : err_ = OSRSetUTM(srs_utm_, 11, TRUE);
122 1 : ASSERT_EQ(err_, OGRERR_NONE);
123 :
124 1 : err_ = OSRSetWellKnownGeogCS(srs_utm_, "WGS84");
125 1 : ASSERT_EQ(err_, OGRERR_NONE);
126 :
127 1 : err_ = OSRSetWellKnownGeogCS(srs_ll_, "WGS84");
128 1 : ASSERT_EQ(err_, OGRERR_NONE);
129 :
130 1 : ct_ = OCTNewCoordinateTransformation(srs_ll_, srs_utm_);
131 1 : ASSERT_TRUE(nullptr != ct_);
132 :
133 1 : const char *wkt = "POINT(-117.5 32.0)";
134 1 : OGRGeometryH geom = nullptr;
135 1 : err_ = OGR_G_CreateFromWkt((char **)&wkt, nullptr, &geom);
136 1 : EXPECT_EQ(OGRERR_NONE, err_);
137 1 : EXPECT_TRUE(nullptr != geom);
138 1 : if (geom)
139 : {
140 1 : err_ = OGR_G_Transform(geom, ct_);
141 1 : ASSERT_EQ(err_, OGRERR_NONE);
142 :
143 1 : OGRSpatialReferenceH srs = nullptr;
144 1 : srs = OGR_G_GetSpatialReference(geom);
145 :
146 1 : char *wktSrs = nullptr;
147 1 : err_ = OSRExportToPrettyWkt(srs, &wktSrs, FALSE);
148 1 : EXPECT_TRUE(nullptr != wktSrs);
149 1 : if (wktSrs)
150 : {
151 2 : std::string pretty(wktSrs);
152 2 : EXPECT_EQ(pretty.substr(0, 6), std::string("PROJCS"));
153 : }
154 1 : CPLFree(wktSrs);
155 1 : OGR_G_DestroyGeometry(geom);
156 : }
157 : }
158 :
159 : // Test OGRCoordinateTransformation::GetInverse()
160 4 : TEST_F(test_osr_ct, GetInverse)
161 : {
162 1 : OGRSpatialReference oSRSSource;
163 1 : oSRSSource.SetAxisMappingStrategy(OAMS_AUTHORITY_COMPLIANT);
164 1 : oSRSSource.importFromEPSG(4267);
165 :
166 1 : OGRSpatialReference oSRSTarget;
167 1 : oSRSTarget.SetAxisMappingStrategy(OAMS_AUTHORITY_COMPLIANT);
168 1 : oSRSTarget.importFromEPSG(4269);
169 :
170 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
171 1 : OGRCreateCoordinateTransformation(&oSRSSource, &oSRSTarget));
172 1 : ASSERT_TRUE(poCT != nullptr);
173 1 : ASSERT_TRUE(poCT->GetSourceCS() != nullptr);
174 1 : ASSERT_TRUE(poCT->GetSourceCS()->IsSame(&oSRSSource));
175 1 : ASSERT_TRUE(poCT->GetTargetCS() != nullptr);
176 1 : ASSERT_TRUE(poCT->GetTargetCS()->IsSame(&oSRSTarget));
177 :
178 : auto poInverse =
179 1 : std::unique_ptr<OGRCoordinateTransformation>(poCT->GetInverse());
180 1 : ASSERT_TRUE(poInverse != nullptr);
181 1 : ASSERT_TRUE(poInverse->GetSourceCS() != nullptr);
182 1 : ASSERT_TRUE(poInverse->GetSourceCS()->IsSame(&oSRSTarget));
183 1 : ASSERT_TRUE(poInverse->GetTargetCS() != nullptr);
184 1 : ASSERT_TRUE(poInverse->GetTargetCS()->IsSame(&oSRSSource));
185 :
186 1 : double x = 40;
187 1 : double y = -100;
188 1 : ASSERT_TRUE(poCT->Transform(1, &x, &y));
189 : // Check that the transformed point is different but not too far
190 1 : EXPECT_TRUE(fabs(x - 40) > 1e-10);
191 1 : EXPECT_TRUE(fabs(y - -100) > 1e-10);
192 1 : EXPECT_NEAR(x, 40, 1e-3);
193 1 : EXPECT_NEAR(y, -100, 1e-3);
194 1 : const double xTransformed = x;
195 1 : const double yTransformed = y;
196 :
197 1 : poCT.reset();
198 :
199 : // Check that the transformed point with the inverse transformation
200 : // matches the source
201 1 : ASSERT_TRUE(poInverse->Transform(1, &x, &y));
202 1 : EXPECT_NEAR(x, 40, 1e-8);
203 1 : EXPECT_NEAR(y, -100, 1e-8);
204 :
205 : auto poInvOfInv =
206 1 : std::unique_ptr<OGRCoordinateTransformation>(poInverse->GetInverse());
207 1 : ASSERT_TRUE(poInvOfInv != nullptr);
208 1 : ASSERT_TRUE(poInvOfInv->GetSourceCS() != nullptr);
209 1 : ASSERT_TRUE(poInvOfInv->GetSourceCS()->IsSame(&oSRSSource));
210 1 : ASSERT_TRUE(poInvOfInv->GetTargetCS() != nullptr);
211 1 : ASSERT_TRUE(poInvOfInv->GetTargetCS()->IsSame(&oSRSTarget));
212 1 : ASSERT_TRUE(poInvOfInv->Transform(1, &x, &y));
213 : // Check that the transformed point is different but not too far
214 1 : EXPECT_NEAR(x, xTransformed, 1e-8);
215 1 : EXPECT_NEAR(y, yTransformed, 1e-8);
216 : }
217 :
218 : // Test OGRCoordinateTransformation::GetInverse() with a specified coordinate
219 : // operation
220 4 : TEST_F(test_osr_ct, GetInverse_with_ct)
221 : {
222 1 : OGRCoordinateTransformationOptions options;
223 1 : options.SetCoordinateOperation("+proj=affine +xoff=10", false);
224 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
225 1 : OGRCreateCoordinateTransformation(nullptr, nullptr, options));
226 1 : ASSERT_TRUE(poCT != nullptr);
227 :
228 : auto poInverse =
229 1 : std::unique_ptr<OGRCoordinateTransformation>(poCT->GetInverse());
230 1 : ASSERT_TRUE(poInverse != nullptr);
231 1 : ASSERT_TRUE(poInverse->GetSourceCS() == nullptr);
232 1 : ASSERT_TRUE(poInverse->GetTargetCS() == nullptr);
233 :
234 1 : poCT.reset();
235 :
236 1 : double x = 100;
237 1 : double y = 200;
238 1 : ASSERT_TRUE(poInverse->Transform(1, &x, &y));
239 1 : EXPECT_NEAR(x, 90, 1e-12);
240 1 : EXPECT_NEAR(y, 200.0, 1e-12);
241 : }
242 :
243 : // Test OGRCoordinateTransformation::Clone()
244 3 : static void test_clone(OGRCoordinateTransformation *poCT,
245 : OGRSpatialReference *poSRSSource,
246 : OGRSpatialReference *poSRSTarget, const double xSrc,
247 : const double ySrc)
248 : {
249 3 : ASSERT_TRUE(poCT != nullptr);
250 3 : ASSERT_TRUE((poCT->GetSourceCS() == nullptr) == (poSRSSource == nullptr));
251 3 : if (poSRSSource != nullptr)
252 : {
253 2 : ASSERT_TRUE(poCT->GetSourceCS()->IsSame(poSRSSource));
254 : }
255 3 : ASSERT_TRUE((poCT->GetTargetCS() == nullptr) == (poSRSTarget == nullptr));
256 3 : if (poSRSTarget != nullptr)
257 : {
258 2 : ASSERT_TRUE(poCT->GetTargetCS()->IsSame(poSRSTarget));
259 : }
260 3 : double x = xSrc;
261 3 : double y = ySrc;
262 3 : ASSERT_TRUE(poCT->Transform(1, &x, &y));
263 3 : const double xTransformed = x;
264 3 : const double yTransformed = y;
265 :
266 3 : auto poClone = std::unique_ptr<OGRCoordinateTransformation>(poCT->Clone());
267 3 : ASSERT_TRUE(poClone != nullptr);
268 3 : ASSERT_TRUE((poClone->GetSourceCS() == nullptr) ==
269 : (poSRSSource == nullptr));
270 3 : if (poSRSSource != nullptr)
271 : {
272 2 : ASSERT_TRUE(poClone->GetSourceCS()->IsSame(poSRSSource));
273 : }
274 3 : ASSERT_TRUE((poClone->GetTargetCS() == nullptr) ==
275 : (poSRSTarget == nullptr));
276 3 : if (poSRSTarget != nullptr)
277 : {
278 2 : ASSERT_TRUE(poClone->GetTargetCS()->IsSame(poSRSTarget));
279 : }
280 3 : x = xSrc;
281 3 : y = ySrc;
282 3 : ASSERT_TRUE(poClone->Transform(1, &x, &y));
283 3 : EXPECT_NEAR(x, xTransformed, 1e-15);
284 3 : EXPECT_NEAR(y, yTransformed, 1e-15);
285 : }
286 :
287 : // Test OGRCoordinateTransformation::Clone() with usual case
288 4 : TEST_F(test_osr_ct, Clone)
289 : {
290 2 : OGRSpatialReference oSRSSource;
291 1 : oSRSSource.importFromEPSG(4267);
292 1 : oSRSSource.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
293 :
294 2 : OGRSpatialReference oSRSTarget;
295 1 : oSRSTarget.importFromEPSG(4269);
296 1 : oSRSTarget.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
297 :
298 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
299 2 : OGRCreateCoordinateTransformation(&oSRSSource, &oSRSTarget));
300 :
301 1 : test_clone(poCT.get(), &oSRSSource, &oSRSTarget, 44, -60);
302 1 : }
303 :
304 : // Test OGRCoordinateTransformation::Clone() with a specified coordinate
305 : // operation
306 4 : TEST_F(test_osr_ct, Clone_with_ct)
307 : {
308 2 : OGRCoordinateTransformationOptions options;
309 1 : options.SetCoordinateOperation("+proj=affine +xoff=10", false);
310 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
311 2 : OGRCreateCoordinateTransformation(nullptr, nullptr, options));
312 :
313 1 : test_clone(poCT.get(), nullptr, nullptr, 90, 200);
314 1 : }
315 :
316 : // Test OGRCoordinateTransformation::Clone() with WebMercator->WGS84 special
317 : // case
318 4 : TEST_F(test_osr_ct, Clone_WebMercator_to_WGS84)
319 : {
320 2 : OGRSpatialReference oSRSSource;
321 1 : oSRSSource.importFromEPSG(3857);
322 1 : oSRSSource.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
323 :
324 2 : OGRSpatialReference oSRSTarget;
325 1 : oSRSTarget.SetWellKnownGeogCS("WGS84");
326 1 : oSRSTarget.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
327 :
328 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
329 2 : OGRCreateCoordinateTransformation(&oSRSSource, &oSRSTarget));
330 :
331 1 : test_clone(poCT.get(), &oSRSSource, &oSRSTarget, 44, -60);
332 1 : }
333 :
334 : // Test OGRCoordinateTransformation in pure "C" API
335 : // OCTClone/OCTGetSourceCS/OCTGetTargetCS/OCTGetInverse
336 4 : TEST_F(test_osr_ct, OGRCoordinateTransformation_C_API)
337 : {
338 1 : OGRSpatialReferenceH hSource = OSRNewSpatialReference(nullptr);
339 1 : OGRSpatialReferenceH hTarget = OSRNewSpatialReference(nullptr);
340 1 : EXPECT_TRUE(hSource != nullptr);
341 1 : EXPECT_TRUE(hTarget != nullptr);
342 1 : if (hSource && hTarget)
343 : {
344 1 : EXPECT_TRUE(OGRERR_NONE == OSRImportFromEPSG(hSource, 32637));
345 1 : EXPECT_TRUE(OGRERR_NONE == OSRSetWellKnownGeogCS(hTarget, "WGS84"));
346 : OGRCoordinateTransformationH hTransform =
347 1 : OCTNewCoordinateTransformation(hSource, hTarget);
348 1 : EXPECT_TRUE(hTransform != nullptr);
349 1 : if (hTransform)
350 : {
351 1 : OGRCoordinateTransformationH hClone = OCTClone(hTransform);
352 1 : EXPECT_TRUE(hClone != nullptr);
353 :
354 : OGRCoordinateTransformationH hInvTransform =
355 1 : OCTGetInverse(hTransform);
356 1 : EXPECT_TRUE(hInvTransform != nullptr);
357 1 : if (hClone && hInvTransform)
358 : {
359 : OGRSpatialReferenceH hSourceInternal =
360 1 : OCTGetSourceCS(hTransform);
361 1 : EXPECT_TRUE(hSourceInternal != nullptr);
362 : OGRSpatialReferenceH hTargetInternal =
363 1 : OCTGetTargetCS(hTransform);
364 1 : EXPECT_TRUE(hTargetInternal != nullptr);
365 :
366 1 : if (hSourceInternal)
367 : {
368 1 : EXPECT_TRUE(OSRIsSame(hSource, hSourceInternal));
369 : }
370 1 : if (hTargetInternal)
371 : {
372 1 : EXPECT_TRUE(OSRIsSame(hTarget, hTargetInternal));
373 : }
374 : }
375 :
376 1 : OCTDestroyCoordinateTransformation(hInvTransform);
377 1 : OCTDestroyCoordinateTransformation(hClone);
378 : }
379 1 : OCTDestroyCoordinateTransformation(hTransform);
380 : }
381 1 : OSRDestroySpatialReference(hSource);
382 1 : OSRDestroySpatialReference(hTarget);
383 1 : }
384 : } // namespace
|