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