LCOV - code coverage report
Current view: top level - autotest/cpp - test_osr_ct.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 205 205 100.0 %
Date: 2024-05-13 13:33:37 Functions: 39 39 100.0 %

          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

Generated by: LCOV version 1.14