LCOV - code coverage report
Current view: top level - alg - gdal_rpc.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 742 905 82.0 %
Date: 2024-11-21 22:18:42 Functions: 15 19 78.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Image Warper
       4             :  * Purpose:  Implements a rational polynomial (RPC) based transformer.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal_alg.h"
      16             : 
      17             : #include <cmath>
      18             : #include <cstddef>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : 
      22             : #include <algorithm>
      23             : #include <limits>
      24             : #include <string>
      25             : 
      26             : #include "cpl_conv.h"
      27             : #include "cpl_error.h"
      28             : #include "cpl_mem_cache.h"
      29             : #include "cpl_minixml.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_vsi.h"
      32             : #include "gdal.h"
      33             : #include "gdal_interpolateatpoint.h"
      34             : #include "gdal_mdreader.h"
      35             : #include "gdal_alg_priv.h"
      36             : #include "gdal_priv.h"
      37             : #if defined(__x86_64) || defined(_M_X64)
      38             : #define USE_SSE2_OPTIM
      39             : #include "gdalsse_priv.h"
      40             : #endif
      41             : #include "ogr_api.h"
      42             : #include "ogr_geometry.h"
      43             : #include "ogr_spatialref.h"
      44             : #include "ogr_srs_api.h"
      45             : #include "gdalresamplingkernels.h"
      46             : 
      47             : // #define DEBUG_VERBOSE_EXTRACT_DEM
      48             : 
      49             : CPL_C_START
      50             : CPLXMLNode *GDALSerializeRPCTransformer(void *pTransformArg);
      51             : void *GDALDeserializeRPCTransformer(CPLXMLNode *psTree);
      52             : CPL_C_END
      53             : 
      54             : constexpr int MAX_ABS_VALUE_WARNINGS = 20;
      55             : constexpr double DEFAULT_PIX_ERR_THRESHOLD = 0.1;
      56             : 
      57             : /************************************************************************/
      58             : /*                            RPCInfoToMD()                             */
      59             : /*                                                                      */
      60             : /*      Turn an RPCInfo structure back into its metadata format.        */
      61             : /************************************************************************/
      62             : 
      63           0 : char **RPCInfoV1ToMD(GDALRPCInfoV1 *psRPCInfo)
      64             : 
      65             : {
      66             :     GDALRPCInfoV2 sRPCInfo;
      67           0 :     memcpy(&sRPCInfo, psRPCInfo, sizeof(GDALRPCInfoV1));
      68           0 :     sRPCInfo.dfERR_BIAS = std::numeric_limits<double>::quiet_NaN();
      69           0 :     sRPCInfo.dfERR_RAND = std::numeric_limits<double>::quiet_NaN();
      70           0 :     return RPCInfoV2ToMD(&sRPCInfo);
      71             : }
      72             : 
      73           1 : char **RPCInfoV2ToMD(GDALRPCInfoV2 *psRPCInfo)
      74             : 
      75             : {
      76           1 :     char **papszMD = nullptr;
      77           2 :     CPLString osField, osMultiField;
      78             : 
      79           1 :     if (!std::isnan(psRPCInfo->dfERR_BIAS))
      80             :     {
      81           1 :         osField.Printf("%.15g", psRPCInfo->dfERR_BIAS);
      82           1 :         papszMD = CSLSetNameValue(papszMD, RPC_ERR_BIAS, osField);
      83             :     }
      84             : 
      85           1 :     if (!std::isnan(psRPCInfo->dfERR_RAND))
      86             :     {
      87           1 :         osField.Printf("%.15g", psRPCInfo->dfERR_RAND);
      88           1 :         papszMD = CSLSetNameValue(papszMD, RPC_ERR_RAND, osField);
      89             :     }
      90             : 
      91           1 :     osField.Printf("%.15g", psRPCInfo->dfLINE_OFF);
      92           1 :     papszMD = CSLSetNameValue(papszMD, RPC_LINE_OFF, osField);
      93             : 
      94           1 :     osField.Printf("%.15g", psRPCInfo->dfSAMP_OFF);
      95           1 :     papszMD = CSLSetNameValue(papszMD, RPC_SAMP_OFF, osField);
      96             : 
      97           1 :     osField.Printf("%.15g", psRPCInfo->dfLAT_OFF);
      98           1 :     papszMD = CSLSetNameValue(papszMD, RPC_LAT_OFF, osField);
      99             : 
     100           1 :     osField.Printf("%.15g", psRPCInfo->dfLONG_OFF);
     101           1 :     papszMD = CSLSetNameValue(papszMD, RPC_LONG_OFF, osField);
     102             : 
     103           1 :     osField.Printf("%.15g", psRPCInfo->dfHEIGHT_OFF);
     104           1 :     papszMD = CSLSetNameValue(papszMD, RPC_HEIGHT_OFF, osField);
     105             : 
     106           1 :     osField.Printf("%.15g", psRPCInfo->dfLINE_SCALE);
     107           1 :     papszMD = CSLSetNameValue(papszMD, RPC_LINE_SCALE, osField);
     108             : 
     109           1 :     osField.Printf("%.15g", psRPCInfo->dfSAMP_SCALE);
     110           1 :     papszMD = CSLSetNameValue(papszMD, RPC_SAMP_SCALE, osField);
     111             : 
     112           1 :     osField.Printf("%.15g", psRPCInfo->dfLAT_SCALE);
     113           1 :     papszMD = CSLSetNameValue(papszMD, RPC_LAT_SCALE, osField);
     114             : 
     115           1 :     osField.Printf("%.15g", psRPCInfo->dfLONG_SCALE);
     116           1 :     papszMD = CSLSetNameValue(papszMD, RPC_LONG_SCALE, osField);
     117             : 
     118           1 :     osField.Printf("%.15g", psRPCInfo->dfHEIGHT_SCALE);
     119           1 :     papszMD = CSLSetNameValue(papszMD, RPC_HEIGHT_SCALE, osField);
     120             : 
     121           1 :     osField.Printf("%.15g", psRPCInfo->dfMIN_LONG);
     122           1 :     papszMD = CSLSetNameValue(papszMD, RPC_MIN_LONG, osField);
     123             : 
     124           1 :     osField.Printf("%.15g", psRPCInfo->dfMIN_LAT);
     125           1 :     papszMD = CSLSetNameValue(papszMD, RPC_MIN_LAT, osField);
     126             : 
     127           1 :     osField.Printf("%.15g", psRPCInfo->dfMAX_LONG);
     128           1 :     papszMD = CSLSetNameValue(papszMD, RPC_MAX_LONG, osField);
     129             : 
     130           1 :     osField.Printf("%.15g", psRPCInfo->dfMAX_LAT);
     131           1 :     papszMD = CSLSetNameValue(papszMD, RPC_MAX_LAT, osField);
     132             : 
     133          21 :     for (int i = 0; i < 20; i++)
     134             :     {
     135          20 :         osField.Printf("%.15g", psRPCInfo->adfLINE_NUM_COEFF[i]);
     136          20 :         if (i > 0)
     137          19 :             osMultiField += " ";
     138             :         else
     139           1 :             osMultiField = "";
     140          20 :         osMultiField += osField;
     141             :     }
     142           1 :     papszMD = CSLSetNameValue(papszMD, "LINE_NUM_COEFF", osMultiField);
     143             : 
     144          21 :     for (int i = 0; i < 20; i++)
     145             :     {
     146          20 :         osField.Printf("%.15g", psRPCInfo->adfLINE_DEN_COEFF[i]);
     147          20 :         if (i > 0)
     148          19 :             osMultiField += " ";
     149             :         else
     150           1 :             osMultiField = "";
     151          20 :         osMultiField += osField;
     152             :     }
     153           1 :     papszMD = CSLSetNameValue(papszMD, "LINE_DEN_COEFF", osMultiField);
     154             : 
     155          21 :     for (int i = 0; i < 20; i++)
     156             :     {
     157          20 :         osField.Printf("%.15g", psRPCInfo->adfSAMP_NUM_COEFF[i]);
     158          20 :         if (i > 0)
     159          19 :             osMultiField += " ";
     160             :         else
     161           1 :             osMultiField = "";
     162          20 :         osMultiField += osField;
     163             :     }
     164           1 :     papszMD = CSLSetNameValue(papszMD, "SAMP_NUM_COEFF", osMultiField);
     165             : 
     166          21 :     for (int i = 0; i < 20; i++)
     167             :     {
     168          20 :         osField.Printf("%.15g", psRPCInfo->adfSAMP_DEN_COEFF[i]);
     169          20 :         if (i > 0)
     170          19 :             osMultiField += " ";
     171             :         else
     172           1 :             osMultiField = "";
     173          20 :         osMultiField += osField;
     174             :     }
     175           1 :     papszMD = CSLSetNameValue(papszMD, "SAMP_DEN_COEFF", osMultiField);
     176             : 
     177           2 :     return papszMD;
     178             : }
     179             : 
     180             : /************************************************************************/
     181             : /*                          RPCComputeTerms()                           */
     182             : /************************************************************************/
     183             : 
     184      460269 : static void RPCComputeTerms(double dfLong, double dfLat, double dfHeight,
     185             :                             double *padfTerms)
     186             : 
     187             : {
     188      460269 :     padfTerms[0] = 1.0;
     189      460269 :     padfTerms[1] = dfLong;
     190      460269 :     padfTerms[2] = dfLat;
     191      460269 :     padfTerms[3] = dfHeight;
     192      460269 :     padfTerms[4] = dfLong * dfLat;
     193      460269 :     padfTerms[5] = dfLong * dfHeight;
     194      460269 :     padfTerms[6] = dfLat * dfHeight;
     195      460269 :     padfTerms[7] = dfLong * dfLong;
     196      460269 :     padfTerms[8] = dfLat * dfLat;
     197      460269 :     padfTerms[9] = dfHeight * dfHeight;
     198             : 
     199      460269 :     padfTerms[10] = dfLong * dfLat * dfHeight;
     200      460269 :     padfTerms[11] = dfLong * dfLong * dfLong;
     201      460269 :     padfTerms[12] = dfLong * dfLat * dfLat;
     202      460269 :     padfTerms[13] = dfLong * dfHeight * dfHeight;
     203      460269 :     padfTerms[14] = dfLong * dfLong * dfLat;
     204      460269 :     padfTerms[15] = dfLat * dfLat * dfLat;
     205      460269 :     padfTerms[16] = dfLat * dfHeight * dfHeight;
     206      460269 :     padfTerms[17] = dfLong * dfLong * dfHeight;
     207      460269 :     padfTerms[18] = dfLat * dfLat * dfHeight;
     208      460269 :     padfTerms[19] = dfHeight * dfHeight * dfHeight;
     209      460269 : }
     210             : 
     211             : /************************************************************************/
     212             : /* ==================================================================== */
     213             : /*                           GDALRPCTransformer                         */
     214             : /* ==================================================================== */
     215             : /************************************************************************/
     216             : 
     217             : /*! DEM Resampling Algorithm */
     218             : typedef enum
     219             : {
     220             :     /*! Nearest neighbour (select on one input pixel) */ DRA_NearestNeighbour =
     221             :         0,
     222             :     /*! Bilinear (2x2 kernel) */ DRA_Bilinear = 1,
     223             :     /*! Cubic Convolution Approximation (4x4 kernel) */ DRA_CubicSpline = 2
     224             : } DEMResampleAlg;
     225             : 
     226             : typedef struct
     227             : {
     228             : 
     229             :     GDALTransformerInfo sTI;
     230             : 
     231             :     GDALRPCInfoV2 sRPC;
     232             : 
     233             :     double adfPLToLatLongGeoTransform[6];
     234             :     double dfRefZ;
     235             : 
     236             :     int bReversed;
     237             : 
     238             :     double dfPixErrThreshold;
     239             : 
     240             :     double dfHeightOffset;
     241             : 
     242             :     double dfHeightScale;
     243             : 
     244             :     char *pszDEMPath;
     245             : 
     246             :     DEMResampleAlg eResampleAlg;
     247             : 
     248             :     int bHasDEMMissingValue;
     249             :     double dfDEMMissingValue;
     250             :     char *pszDEMSRS;
     251             :     int bApplyDEMVDatumShift;
     252             : 
     253             :     GDALDataset *poDS;
     254             :     // the key is (nYBlock << 32) | nXBlock)
     255             :     lru11::Cache<uint64_t, std::shared_ptr<std::vector<double>>> *poCacheDEM;
     256             : 
     257             :     OGRCoordinateTransformation *poCT;
     258             : 
     259             :     int nMaxIterations;
     260             : 
     261             :     double adfDEMGeoTransform[6];
     262             :     double adfDEMReverseGeoTransform[6];
     263             : 
     264             : #ifdef USE_SSE2_OPTIM
     265             :     double adfDoubles[20 * 4 + 1];
     266             :     // LINE_NUM_COEFF, LINE_DEN_COEFF, SAMP_NUM_COEFF and then SAMP_DEN_COEFF.
     267             :     double *padfCoeffs;
     268             : #endif
     269             : 
     270             :     bool bRPCInverseVerbose;
     271             :     char *pszRPCInverseLog;
     272             : 
     273             :     char *pszRPCFootprint;
     274             :     OGRGeometry *poRPCFootprintGeom;
     275             :     OGRPreparedGeometry *poRPCFootprintPreparedGeom;
     276             : 
     277             : } GDALRPCTransformInfo;
     278             : 
     279             : static bool GDALRPCOpenDEM(GDALRPCTransformInfo *psTransform);
     280             : 
     281             : /************************************************************************/
     282             : /*                            RPCEvaluate()                             */
     283             : /************************************************************************/
     284             : #ifdef USE_SSE2_OPTIM
     285             : 
     286      460269 : static void RPCEvaluate4(const double *padfTerms, const double *padfCoefs,
     287             :                          double &dfSum1, double &dfSum2, double &dfSum3,
     288             :                          double &dfSum4)
     289             : 
     290             : {
     291      460269 :     XMMReg2Double sum1 = XMMReg2Double::Zero();
     292      460269 :     XMMReg2Double sum2 = XMMReg2Double::Zero();
     293      460269 :     XMMReg2Double sum3 = XMMReg2Double::Zero();
     294      460269 :     XMMReg2Double sum4 = XMMReg2Double::Zero();
     295     5062960 :     for (int i = 0; i < 20; i += 2)
     296             :     {
     297             :         const XMMReg2Double terms =
     298     4602690 :             XMMReg2Double::Load2ValAligned(padfTerms + i);
     299             : 
     300             :         // LINE_NUM_COEFF.
     301             :         const XMMReg2Double coefs1 =
     302     4602690 :             XMMReg2Double::Load2ValAligned(padfCoefs + i);
     303             : 
     304             :         // LINE_DEN_COEFF.
     305             :         const XMMReg2Double coefs2 =
     306     4602690 :             XMMReg2Double::Load2ValAligned(padfCoefs + i + 20);
     307             : 
     308             :         // SAMP_NUM_COEFF.
     309             :         const XMMReg2Double coefs3 =
     310     4602690 :             XMMReg2Double::Load2ValAligned(padfCoefs + i + 40);
     311             : 
     312             :         // SAMP_DEN_COEFF.
     313             :         const XMMReg2Double coefs4 =
     314     4602690 :             XMMReg2Double::Load2ValAligned(padfCoefs + i + 60);
     315             : 
     316     4602690 :         sum1 += terms * coefs1;
     317     4602690 :         sum2 += terms * coefs2;
     318     4602690 :         sum3 += terms * coefs3;
     319     4602690 :         sum4 += terms * coefs4;
     320             :     }
     321      460269 :     dfSum1 = sum1.GetHorizSum();
     322      460269 :     dfSum2 = sum2.GetHorizSum();
     323      460269 :     dfSum3 = sum3.GetHorizSum();
     324      460269 :     dfSum4 = sum4.GetHorizSum();
     325      460269 : }
     326             : 
     327             : #else
     328             : 
     329             : static double RPCEvaluate(const double *padfTerms, const double *padfCoefs)
     330             : 
     331             : {
     332             :     double dfSum1 = 0.0;
     333             :     double dfSum2 = 0.0;
     334             : 
     335             :     for (int i = 0; i < 20; i += 2)
     336             :     {
     337             :         dfSum1 += padfTerms[i] * padfCoefs[i];
     338             :         dfSum2 += padfTerms[i + 1] * padfCoefs[i + 1];
     339             :     }
     340             : 
     341             :     return dfSum1 + dfSum2;
     342             : }
     343             : 
     344             : #endif
     345             : 
     346             : /************************************************************************/
     347             : /*                         RPCTransformPoint()                          */
     348             : /************************************************************************/
     349             : 
     350      460269 : static void RPCTransformPoint(const GDALRPCTransformInfo *psRPCTransformInfo,
     351             :                               double dfLong, double dfLat, double dfHeight,
     352             :                               double *pdfPixel, double *pdfLine)
     353             : 
     354             : {
     355      460269 :     double adfTermsWithMargin[20 + 1] = {};
     356             :     // Make padfTerms aligned on 16-byte boundary for SSE2 aligned loads.
     357      460269 :     double *padfTerms =
     358      460269 :         adfTermsWithMargin +
     359             :         (reinterpret_cast<GUIntptr_t>(adfTermsWithMargin) % 16) / 8;
     360             : 
     361             :     // Avoid dateline issues.
     362      460269 :     double diffLong = dfLong - psRPCTransformInfo->sRPC.dfLONG_OFF;
     363      460269 :     if (diffLong < -270)
     364             :     {
     365           2 :         diffLong += 360;
     366             :     }
     367      460267 :     else if (diffLong > 270)
     368             :     {
     369           6 :         diffLong -= 360;
     370             :     }
     371             : 
     372      460269 :     const double dfNormalizedLong =
     373      460269 :         diffLong / psRPCTransformInfo->sRPC.dfLONG_SCALE;
     374      460269 :     const double dfNormalizedLat =
     375      460269 :         (dfLat - psRPCTransformInfo->sRPC.dfLAT_OFF) /
     376      460269 :         psRPCTransformInfo->sRPC.dfLAT_SCALE;
     377      460269 :     const double dfNormalizedHeight =
     378      460269 :         (dfHeight - psRPCTransformInfo->sRPC.dfHEIGHT_OFF) /
     379      460269 :         psRPCTransformInfo->sRPC.dfHEIGHT_SCALE;
     380             : 
     381             :     // The absolute values of the 3 above normalized values are supposed to be
     382             :     // below 1. Warn (as debug message) if it is not the case. We allow for some
     383             :     // margin above 1 (1.5, somewhat arbitrary chosen) before warning.
     384             :     static int nCountWarningsAboutAboveOneNormalizedValues = 0;
     385      460269 :     if (nCountWarningsAboutAboveOneNormalizedValues < MAX_ABS_VALUE_WARNINGS)
     386             :     {
     387       99804 :         bool bWarned = false;
     388       99804 :         if (fabs(dfNormalizedLong) > 1.5)
     389             :         {
     390          31 :             bWarned = true;
     391          31 :             CPLDebug(
     392             :                 "RPC",
     393             :                 "Normalized %s for (lon,lat,height)=(%f,%f,%f) is %f, "
     394             :                 "i.e. with an absolute value of > 1, which may cause numeric "
     395             :                 "stability problems",
     396             :                 "longitude", dfLong, dfLat, dfHeight, dfNormalizedLong);
     397             :         }
     398       99804 :         if (fabs(dfNormalizedLat) > 1.5)
     399             :         {
     400          21 :             bWarned = true;
     401          21 :             CPLDebug(
     402             :                 "RPC",
     403             :                 "Normalized %s for (lon,lat,height)=(%f,%f,%f) is %f, "
     404             :                 "ie with an absolute value of > 1, which may cause numeric "
     405             :                 "stability problems",
     406             :                 "latitude", dfLong, dfLat, dfHeight, dfNormalizedLat);
     407             :         }
     408       99804 :         if (fabs(dfNormalizedHeight) > 1.5)
     409             :         {
     410          39 :             bWarned = true;
     411          39 :             CPLDebug(
     412             :                 "RPC",
     413             :                 "Normalized %s for (lon,lat,height)=(%f,%f,%f) is %f, "
     414             :                 "i.e. with an absolute value of > 1, which may cause numeric "
     415             :                 "stability problems",
     416             :                 "height", dfLong, dfLat, dfHeight, dfNormalizedHeight);
     417             :         }
     418       99804 :         if (bWarned)
     419             :         {
     420             :             // Limit the number of warnings.
     421          60 :             nCountWarningsAboutAboveOneNormalizedValues++;
     422          60 :             if (nCountWarningsAboutAboveOneNormalizedValues ==
     423             :                 MAX_ABS_VALUE_WARNINGS)
     424             :             {
     425           3 :                 CPLDebug("RPC", "No more such debug warnings will be emitted");
     426             :             }
     427             :         }
     428             :     }
     429             : 
     430      460269 :     RPCComputeTerms(dfNormalizedLong, dfNormalizedLat, dfNormalizedHeight,
     431             :                     padfTerms);
     432             : 
     433             : #ifdef USE_SSE2_OPTIM
     434      460269 :     double dfSampNum = 0.0;
     435      460269 :     double dfSampDen = 0.0;
     436      460269 :     double dfLineNum = 0.0;
     437      460269 :     double dfLineDen = 0.0;
     438      460269 :     RPCEvaluate4(padfTerms, psRPCTransformInfo->padfCoeffs, dfLineNum,
     439             :                  dfLineDen, dfSampNum, dfSampDen);
     440      460269 :     const double dfResultX = dfSampNum / dfSampDen;
     441      460269 :     const double dfResultY = dfLineNum / dfLineDen;
     442             : #else
     443             :     const double dfResultX =
     444             :         RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfSAMP_NUM_COEFF) /
     445             :         RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfSAMP_DEN_COEFF);
     446             : 
     447             :     const double dfResultY =
     448             :         RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfLINE_NUM_COEFF) /
     449             :         RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfLINE_DEN_COEFF);
     450             : #endif
     451             : 
     452             :     // RPCs are using the center of upper left pixel = 0,0 convention
     453             :     // convert to top left corner = 0,0 convention used in GDAL.
     454      460269 :     *pdfPixel = dfResultX * psRPCTransformInfo->sRPC.dfSAMP_SCALE +
     455      460269 :                 psRPCTransformInfo->sRPC.dfSAMP_OFF + 0.5;
     456      460269 :     *pdfLine = dfResultY * psRPCTransformInfo->sRPC.dfLINE_SCALE +
     457      460269 :                psRPCTransformInfo->sRPC.dfLINE_OFF + 0.5;
     458      460269 : }
     459             : 
     460             : /************************************************************************/
     461             : /*                     GDALSerializeRPCDEMResample()                    */
     462             : /************************************************************************/
     463             : 
     464           0 : static const char *GDALSerializeRPCDEMResample(DEMResampleAlg eResampleAlg)
     465             : {
     466           0 :     switch (eResampleAlg)
     467             :     {
     468           0 :         case DRA_NearestNeighbour:
     469           0 :             return "near";
     470           0 :         case DRA_CubicSpline:
     471           0 :             return "cubic";
     472           0 :         default:
     473             :         case DRA_Bilinear:
     474           0 :             return "bilinear";
     475             :     }
     476             : }
     477             : 
     478             : /************************************************************************/
     479             : /*                   GDALCreateSimilarRPCTransformer()                  */
     480             : /************************************************************************/
     481             : 
     482           1 : static void *GDALCreateSimilarRPCTransformer(void *hTransformArg,
     483             :                                              double dfRatioX, double dfRatioY)
     484             : {
     485           1 :     VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarRPCTransformer",
     486             :                       nullptr);
     487             : 
     488           1 :     GDALRPCTransformInfo *psInfo =
     489             :         static_cast<GDALRPCTransformInfo *>(hTransformArg);
     490             : 
     491             :     GDALRPCInfoV2 sRPC;
     492           1 :     memcpy(&sRPC, &(psInfo->sRPC), sizeof(GDALRPCInfoV2));
     493             : 
     494           1 :     if (dfRatioX != 1.0 || dfRatioY != 1.0)
     495             :     {
     496           1 :         sRPC.dfLINE_OFF /= dfRatioY;
     497           1 :         sRPC.dfLINE_SCALE /= dfRatioY;
     498           1 :         sRPC.dfSAMP_OFF /= dfRatioX;
     499           1 :         sRPC.dfSAMP_SCALE /= dfRatioX;
     500             :     }
     501             : 
     502           1 :     char **papszOptions = nullptr;
     503           1 :     papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT",
     504             :                                    CPLSPrintf("%.17g", psInfo->dfHeightOffset));
     505           1 :     papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT_SCALE",
     506             :                                    CPLSPrintf("%.17g", psInfo->dfHeightScale));
     507           1 :     if (psInfo->pszDEMPath != nullptr)
     508             :     {
     509             :         papszOptions =
     510           0 :             CSLSetNameValue(papszOptions, "RPC_DEM", psInfo->pszDEMPath);
     511             :         papszOptions =
     512           0 :             CSLSetNameValue(papszOptions, "RPC_DEMINTERPOLATION",
     513             :                             GDALSerializeRPCDEMResample(psInfo->eResampleAlg));
     514           0 :         if (psInfo->bHasDEMMissingValue)
     515             :             papszOptions =
     516           0 :                 CSLSetNameValue(papszOptions, "RPC_DEM_MISSING_VALUE",
     517             :                                 CPLSPrintf("%.17g", psInfo->dfDEMMissingValue));
     518             :         papszOptions =
     519           0 :             CSLSetNameValue(papszOptions, "RPC_DEM_APPLY_VDATUM_SHIFT",
     520           0 :                             (psInfo->bApplyDEMVDatumShift) ? "TRUE" : "FALSE");
     521             :     }
     522           1 :     papszOptions = CSLSetNameValue(papszOptions, "RPC_MAX_ITERATIONS",
     523             :                                    CPLSPrintf("%d", psInfo->nMaxIterations));
     524             : 
     525             :     GDALRPCTransformInfo *psNewInfo =
     526           1 :         static_cast<GDALRPCTransformInfo *>(GDALCreateRPCTransformerV2(
     527             :             &sRPC, psInfo->bReversed, psInfo->dfPixErrThreshold, papszOptions));
     528           1 :     CSLDestroy(papszOptions);
     529             : 
     530           1 :     return psNewInfo;
     531             : }
     532             : 
     533             : /************************************************************************/
     534             : /*                      GDALRPCGetHeightAtLongLat()                     */
     535             : /************************************************************************/
     536             : 
     537             : static int GDALRPCGetDEMHeight(GDALRPCTransformInfo *psTransform,
     538             :                                const double dfXIn, const double dfYIn,
     539             :                                double *pdfDEMH);
     540             : 
     541      304118 : static bool GDALRPCGetHeightAtLongLat(GDALRPCTransformInfo *psTransform,
     542             :                                       const double dfXIn, const double dfYIn,
     543             :                                       double *pdfHeight,
     544             :                                       double *pdfDEMPixel = nullptr,
     545             :                                       double *pdfDEMLine = nullptr)
     546             : {
     547      304118 :     double dfVDatumShift = 0.0;
     548      304118 :     double dfDEMH = 0.0;
     549      304118 :     if (psTransform->poDS)
     550             :     {
     551      296262 :         double dfX = 0.0;
     552      296262 :         double dfY = 0.0;
     553      296262 :         double dfXTemp = dfXIn;
     554      296262 :         double dfYTemp = dfYIn;
     555             :         // Check if dem is not in WGS84 and transform points padfX[i], padfY[i].
     556      296262 :         if (psTransform->poCT)
     557             :         {
     558      189912 :             double dfZ = 0.0;
     559      189912 :             if (!psTransform->poCT->Transform(1, &dfXTemp, &dfYTemp, &dfZ))
     560             :             {
     561           0 :                 return false;
     562             :             }
     563             : 
     564             :             // We must take the opposite since poCT transforms from
     565             :             // WGS84 to geoid. And we are going to do the reverse:
     566             :             // take an elevation over the geoid and transforms it to WGS84.
     567      189912 :             if (psTransform->bApplyDEMVDatumShift)
     568      189912 :                 dfVDatumShift = -dfZ;
     569             :         }
     570             : 
     571      296262 :         bool bRetried = false;
     572      296270 :     retry:
     573      296270 :         GDALApplyGeoTransform(psTransform->adfDEMReverseGeoTransform, dfXTemp,
     574             :                               dfYTemp, &dfX, &dfY);
     575      296270 :         if (pdfDEMPixel)
     576       43197 :             *pdfDEMPixel = dfX;
     577      296270 :         if (pdfDEMLine)
     578       43197 :             *pdfDEMLine = dfY;
     579             : 
     580      296270 :         if (!GDALRPCGetDEMHeight(psTransform, dfX, dfY, &dfDEMH))
     581             :         {
     582             :             // Try to handle the case where the DEM is in LL WGS84 and spans
     583             :             // over [-180,180], (or very close to it ), presumably with much
     584             :             // hole in the middle if using VRT, and the longitude goes beyond
     585             :             // that interval.
     586       44616 :             if (!bRetried && psTransform->poCT == nullptr &&
     587       16677 :                 (dfXIn >= 180.0 || dfXIn <= -180.0))
     588             :             {
     589           8 :                 const int nRasterXSize = psTransform->poDS->GetRasterXSize();
     590           8 :                 const double dfMinDEMLong = psTransform->adfDEMGeoTransform[0];
     591           8 :                 const double dfMaxDEMLong =
     592           8 :                     psTransform->adfDEMGeoTransform[0] +
     593           8 :                     nRasterXSize * psTransform->adfDEMGeoTransform[1];
     594           8 :                 if (fabs(dfMinDEMLong - -180) < 0.1 &&
     595           8 :                     fabs(dfMaxDEMLong - 180) < 0.1)
     596             :                 {
     597           8 :                     if (dfXIn >= 180)
     598             :                     {
     599           4 :                         dfXTemp = dfXIn - 360;
     600           4 :                         dfYTemp = dfYIn;
     601             :                     }
     602             :                     else
     603             :                     {
     604           4 :                         dfXTemp = dfXIn + 360;
     605           4 :                         dfYTemp = dfYIn;
     606             :                     }
     607           8 :                     bRetried = true;
     608           8 :                     goto retry;
     609             :                 }
     610             :             }
     611             : 
     612       44608 :             if (psTransform->bHasDEMMissingValue)
     613       11832 :                 dfDEMH = psTransform->dfDEMMissingValue;
     614             :             else
     615             :             {
     616       32776 :                 return false;
     617             :             }
     618             :         }
     619             : #ifdef DEBUG_VERBOSE_EXTRACT_DEM
     620             :         CPLDebug("RPC_DEM", "X=%f, Y=%f -> Z=%f", dfX, dfY, dfDEMH);
     621             : #endif
     622             :     }
     623             : 
     624      271342 :     *pdfHeight = dfVDatumShift + (psTransform->dfHeightOffset +
     625      271342 :                                   dfDEMH * psTransform->dfHeightScale);
     626      271342 :     return true;
     627             : }
     628             : 
     629             : /************************************************************************/
     630             : /*                      GDALCreateRPCTransformer()                      */
     631             : /************************************************************************/
     632             : 
     633           0 : void *GDALCreateRPCTransformerV1(GDALRPCInfoV1 *psRPCInfo, int bReversed,
     634             :                                  double dfPixErrThreshold, char **papszOptions)
     635             : 
     636             : {
     637             :     GDALRPCInfoV2 sRPCInfo;
     638           0 :     memcpy(&sRPCInfo, psRPCInfo, sizeof(GDALRPCInfoV1));
     639           0 :     sRPCInfo.dfERR_BIAS = std::numeric_limits<double>::quiet_NaN();
     640           0 :     sRPCInfo.dfERR_RAND = std::numeric_limits<double>::quiet_NaN();
     641           0 :     return GDALCreateRPCTransformerV2(&sRPCInfo, bReversed, dfPixErrThreshold,
     642           0 :                                       papszOptions);
     643             : }
     644             : 
     645             : /**
     646             :  * Create an RPC based transformer.
     647             :  *
     648             :  * The geometric sensor model describing the physical relationship between
     649             :  * image coordinates and ground coordinates is known as a Rigorous Projection
     650             :  * Model. A Rigorous Projection Model expresses the mapping of the image space
     651             :  * coordinates of rows and columns (r,c) onto the object space reference
     652             :  * surface geodetic coordinates (long, lat, height).
     653             :  *
     654             :  * A RPC supports a generic description of the Rigorous Projection Models. The
     655             :  * approximation used by GDAL (RPC00) is a set of rational polynomials
     656             :  * expressing the normalized row and column values, (rn , cn), as a function of
     657             :  * normalized geodetic latitude, longitude, and height, (P, L, H), given a
     658             :  * set of normalized polynomial coefficients (LINE_NUM_COEF_n, LINE_DEN_COEF_n,
     659             :  * SAMP_NUM_COEF_n, SAMP_DEN_COEF_n). Normalized values, rather than actual
     660             :  * values are used in order to minimize introduction of errors during the
     661             :  * calculations. The transformation between row and column values (r,c), and
     662             :  * normalized row and column values (rn, cn), and between the geodetic
     663             :  * latitude, longitude, and height and normalized geodetic latitude,
     664             :  * longitude, and height (P, L, H), is defined by a set of normalizing
     665             :  * translations (offsets) and scales that ensure all values are contained in
     666             :  * the range -1 to +1.
     667             :  *
     668             :  * This function creates a GDALTransformFunc compatible transformer
     669             :  * for going between image pixel/line and long/lat/height coordinates
     670             :  * using RPCs.  The RPCs are provided in a GDALRPCInfo structure which is
     671             :  * normally read from metadata using GDALExtractRPCInfo().
     672             :  *
     673             :  * GDAL RPC Metadata has the following entries (also described in GDAL RFC 22
     674             :  * and the GeoTIFF RPC document http://geotiff.maptools.org/rpc_prop.html .
     675             :  *
     676             :  * <ul>
     677             :  * <li>ERR_BIAS: Error - Bias. The RMS bias error in meters per horizontal axis
     678             :  * of all points in the image (-1.0 if unknown)
     679             :  * <li>ERR_RAND: Error - Random. RMS random error in meters per horizontal axis
     680             :  * of each point in the image (-1.0 if unknown)
     681             :  * <li>LINE_OFF: Line Offset
     682             :  * <li>SAMP_OFF: Sample Offset
     683             :  * <li>LAT_OFF: Geodetic Latitude Offset
     684             :  * <li>LONG_OFF: Geodetic Longitude Offset
     685             :  * <li>HEIGHT_OFF: Geodetic Height Offset
     686             :  * <li>LINE_SCALE: Line Scale
     687             :  * <li>SAMP_SCALE: Sample Scale
     688             :  * <li>LAT_SCALE: Geodetic Latitude Scale
     689             :  * <li>LONG_SCALE: Geodetic Longitude Scale
     690             :  * <li>HEIGHT_SCALE: Geodetic Height Scale
     691             : 
     692             :  * <li>LINE_NUM_COEFF (1-20): Line Numerator Coefficients. Twenty coefficients
     693             :  * for the polynomial in the Numerator of the rn equation. (space separated)
     694             :  * <li>LINE_DEN_COEFF (1-20): Line Denominator Coefficients. Twenty coefficients
     695             :  * for the polynomial in the Denominator of the rn equation. (space separated)
     696             :  * <li>SAMP_NUM_COEFF (1-20): Sample Numerator Coefficients. Twenty coefficients
     697             :  * for the polynomial in the Numerator of the cn equation. (space separated)
     698             :  * <li>SAMP_DEN_COEFF (1-20): Sample Denominator Coefficients. Twenty
     699             :  * coefficients for the polynomial in the Denominator of the cn equation. (space
     700             :  * separated)
     701             :  * </ul>
     702             :  *
     703             :  * The transformer normally maps from pixel/line/height to long/lat/height space
     704             :  * as a forward transformation though in RPC terms that would be considered
     705             :  * an inverse transformation (and is solved by iterative approximation using
     706             :  * long/lat/height to pixel/line transformations).  The default direction can
     707             :  * be reversed by passing bReversed=TRUE.
     708             :  *
     709             :  * The iterative solution of pixel/line
     710             :  * to lat/long/height is currently run for up to 10 iterations or until
     711             :  * the apparent error is less than dfPixErrThreshold pixels.  Passing zero
     712             :  * will not avoid all error, but will cause the operation to run for the maximum
     713             :  * number of iterations.
     714             :  *
     715             :  * Starting with GDAL 2.1, debugging of the RPC inverse transformer can be done
     716             :  * by setting the RPC_INVERSE_VERBOSE configuration option to YES (in which case
     717             :  * extra debug information will be displayed in the "RPC" debug category, so
     718             :  * requiring CPL_DEBUG to be also set) and/or by setting RPC_INVERSE_LOG to a
     719             :  * filename that will contain the content of iterations (this last option only
     720             :  * makes sense when debugging point by point, since each time
     721             :  * RPCInverseTransformPoint() is called, the file is rewritten).
     722             :  *
     723             :  * Additional options to the transformer can be supplied in papszOptions.
     724             :  *
     725             :  * Options:
     726             :  *
     727             :  * <ul>
     728             :  * <li> RPC_HEIGHT: a fixed height offset to be applied to all points passed
     729             :  * in.  In this situation the Z passed into the transformation function is
     730             :  * assumed to be height above ground, and the RPC_HEIGHT is assumed to be
     731             :  * an average height above sea level for ground in the target scene.</li>
     732             :  *
     733             :  * <li> RPC_HEIGHT_SCALE: a factor used to multiply heights above ground.
     734             :  * Useful when elevation offsets of the DEM are not expressed in meters.</li>
     735             :  *
     736             :  * <li> RPC_DEM: the name of a GDAL dataset (a DEM file typically) used to
     737             :  * extract elevation offsets from. In this situation the Z passed into the
     738             :  * transformation function is assumed to be height above ground. This option
     739             :  * should be used in replacement of RPC_HEIGHT to provide a way of defining
     740             :  * a non uniform ground for the target scene</li>
     741             :  *
     742             :  * <li> RPC_DEMINTERPOLATION: the DEM interpolation ("near", "bilinear" or
     743             :  "cubic").
     744             :  *      Default is "bilinear".</li>
     745             :  *
     746             :  * <li> RPC_DEM_MISSING_VALUE: value of DEM height that must be used in case
     747             :  * the DEM has nodata value at the sampling point, or if its extent does not
     748             :  * cover the requested coordinate. When not specified, missing values will cause
     749             :  * a failed transform.</li>
     750             :  *
     751             :  * <li> RPC_DEM_SRS: (GDAL >= 3.2) WKT SRS, or any string recognized by
     752             :  * OGRSpatialReference::SetFromUserInput(), to be used as an override for DEM
     753             :  SRS.
     754             :  * Useful if DEM SRS does not have an explicit vertical component. </li>
     755             :  *
     756             :  * <li> RPC_DEM_APPLY_VDATUM_SHIFT: whether the vertical component of a compound
     757             :  * SRS for the DEM should be used (when it is present). This is useful so as to
     758             :  * be able to transform the "raw" values from the DEM expressed with respect to
     759             :  * a geoid to the heights with respect to the WGS84 ellipsoid. When this is
     760             :  * enabled, the GTIFF_REPORT_COMPD_CS configuration option will be also set
     761             :  * temporarily so as to get the vertical information from GeoTIFF
     762             :  * files. Defaults to TRUE. (GDAL >= 2.1.0)</li>
     763             :  *
     764             :  * <li> RPC_PIXEL_ERROR_THRESHOLD: overrides the dfPixErrThreshold parameter, ie
     765             :   the error (measured in pixels) allowed in the
     766             :  * iterative solution of pixel/line to lat/long computations (the other way
     767             :  * is always exact given the equations).  (GDAL >= 2.1.0)</li>
     768             :  *
     769             :  * <li> RPC_MAX_ITERATIONS: maximum number of iterations allowed in the
     770             :  * iterative solution of pixel/line to lat/long computations. Default value is
     771             :  * 10 in the absence of a DEM, or 20 if there is a DEM.  (GDAL >= 2.1.0)</li>
     772             :  *
     773             :  * <li> RPC_FOOTPRINT: WKT or GeoJSON polygon (in long / lat coordinate space)
     774             :  * with a validity footprint for the RPC. Any coordinate transformation that
     775             :  * goes from or arrive outside this footprint will be considered invalid. This
     776             :  * is useful in situations where the RPC values become highly unstable outside
     777             :  * of the area on which they have been computed for, potentially leading to
     778             :  * undesirable "echoes" / false positives. This requires GDAL to be built
     779             :  against
     780             :  * GEOS.</li>
     781             :  *
     782             :  * </ul>
     783             :  *
     784             :  * @param psRPCInfo Definition of the RPC parameters.
     785             :  *
     786             :  * @param bReversed If true "forward" transformation will be lat/long to
     787             :  * pixel/line instead of the normal pixel/line to lat/long.
     788             :  *
     789             :  * @param dfPixErrThreshold the error (measured in pixels) allowed in the
     790             :  * iterative solution of pixel/line to lat/long computations (the other way
     791             :  * is always exact given the equations). Starting with GDAL 2.1, this may also
     792             :  * be set through the RPC_PIXEL_ERROR_THRESHOLD transformer option.
     793             :  * If a negative or null value is provided, then this defaults to 0.1 pixel.
     794             :  *
     795             :  * @param papszOptions Other transformer options (i.e. RPC_HEIGHT=z).
     796             :  *
     797             :  * @return transformer callback data (deallocate with GDALDestroyTransformer()).
     798             :  */
     799             : 
     800          46 : void *GDALCreateRPCTransformerV2(const GDALRPCInfoV2 *psRPCInfo, int bReversed,
     801             :                                  double dfPixErrThreshold, char **papszOptions)
     802             : 
     803             : {
     804             :     /* -------------------------------------------------------------------- */
     805             :     /*      Initialize core info.                                           */
     806             :     /* -------------------------------------------------------------------- */
     807             :     GDALRPCTransformInfo *psTransform = static_cast<GDALRPCTransformInfo *>(
     808          46 :         CPLCalloc(sizeof(GDALRPCTransformInfo), 1));
     809             : 
     810          46 :     memcpy(&(psTransform->sRPC), psRPCInfo, sizeof(GDALRPCInfoV2));
     811          46 :     psTransform->bReversed = bReversed;
     812             :     const char *pszPixErrThreshold =
     813          46 :         CSLFetchNameValue(papszOptions, "RPC_PIXEL_ERROR_THRESHOLD");
     814          46 :     if (pszPixErrThreshold != nullptr)
     815           1 :         psTransform->dfPixErrThreshold = CPLAtof(pszPixErrThreshold);
     816          45 :     else if (dfPixErrThreshold > 0)
     817           1 :         psTransform->dfPixErrThreshold = dfPixErrThreshold;
     818             :     else
     819          44 :         psTransform->dfPixErrThreshold = DEFAULT_PIX_ERR_THRESHOLD;
     820          46 :     psTransform->dfHeightOffset = 0.0;
     821          46 :     psTransform->dfHeightScale = 1.0;
     822             : 
     823          46 :     memcpy(psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE,
     824             :            strlen(GDAL_GTI2_SIGNATURE));
     825          46 :     psTransform->sTI.pszClassName = GDAL_RPC_TRANSFORMER_CLASS_NAME;
     826          46 :     psTransform->sTI.pfnTransform = GDALRPCTransform;
     827          46 :     psTransform->sTI.pfnCleanup = GDALDestroyRPCTransformer;
     828          46 :     psTransform->sTI.pfnSerialize = GDALSerializeRPCTransformer;
     829          46 :     psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarRPCTransformer;
     830             : 
     831             : #ifdef USE_SSE2_OPTIM
     832             :     // Make sure padfCoeffs is aligned on a 16-byte boundary for SSE2 aligned
     833             :     // loads.
     834          46 :     psTransform->padfCoeffs =
     835          46 :         psTransform->adfDoubles +
     836          46 :         (reinterpret_cast<GUIntptr_t>(psTransform->adfDoubles) % 16) / 8;
     837          46 :     memcpy(psTransform->padfCoeffs, psRPCInfo->adfLINE_NUM_COEFF,
     838             :            20 * sizeof(double));
     839          46 :     memcpy(psTransform->padfCoeffs + 20, psRPCInfo->adfLINE_DEN_COEFF,
     840             :            20 * sizeof(double));
     841          46 :     memcpy(psTransform->padfCoeffs + 40, psRPCInfo->adfSAMP_NUM_COEFF,
     842             :            20 * sizeof(double));
     843          46 :     memcpy(psTransform->padfCoeffs + 60, psRPCInfo->adfSAMP_DEN_COEFF,
     844             :            20 * sizeof(double));
     845             : #endif
     846             : 
     847             :     /* -------------------------------------------------------------------- */
     848             :     /*      Do we have a "average height" that we want to consider all      */
     849             :     /*      elevations to be relative to?                                   */
     850             :     /* -------------------------------------------------------------------- */
     851          46 :     const char *pszHeight = CSLFetchNameValue(papszOptions, "RPC_HEIGHT");
     852          46 :     if (pszHeight != nullptr)
     853           6 :         psTransform->dfHeightOffset = CPLAtof(pszHeight);
     854             : 
     855             :     /* -------------------------------------------------------------------- */
     856             :     /*                       The "height scale"                             */
     857             :     /* -------------------------------------------------------------------- */
     858             :     const char *pszHeightScale =
     859          46 :         CSLFetchNameValue(papszOptions, "RPC_HEIGHT_SCALE");
     860          46 :     if (pszHeightScale != nullptr)
     861           7 :         psTransform->dfHeightScale = CPLAtof(pszHeightScale);
     862             : 
     863             :     /* -------------------------------------------------------------------- */
     864             :     /*                       The DEM file name                              */
     865             :     /* -------------------------------------------------------------------- */
     866          46 :     const char *pszDEMPath = CSLFetchNameValue(papszOptions, "RPC_DEM");
     867          46 :     if (pszDEMPath != nullptr)
     868             :     {
     869          33 :         psTransform->pszDEMPath = CPLStrdup(pszDEMPath);
     870             :     }
     871             : 
     872             :     /* -------------------------------------------------------------------- */
     873             :     /*                      The DEM interpolation                           */
     874             :     /* -------------------------------------------------------------------- */
     875             :     const char *pszDEMInterpolation =
     876          46 :         CSLFetchNameValueDef(papszOptions, "RPC_DEMINTERPOLATION", "bilinear");
     877          46 :     if (EQUAL(pszDEMInterpolation, "near"))
     878             :     {
     879           3 :         psTransform->eResampleAlg = DRA_NearestNeighbour;
     880             :     }
     881          43 :     else if (EQUAL(pszDEMInterpolation, "bilinear"))
     882             :     {
     883          40 :         psTransform->eResampleAlg = DRA_Bilinear;
     884             :     }
     885           3 :     else if (EQUAL(pszDEMInterpolation, "cubic"))
     886             :     {
     887           3 :         psTransform->eResampleAlg = DRA_CubicSpline;
     888             :     }
     889             :     else
     890             :     {
     891           0 :         CPLDebug("RPC", "Unknown interpolation %s. Defaulting to bilinear",
     892             :                  pszDEMInterpolation);
     893           0 :         psTransform->eResampleAlg = DRA_Bilinear;
     894             :     }
     895             : 
     896             :     /* -------------------------------------------------------------------- */
     897             :     /*                       The DEM missing value                          */
     898             :     /* -------------------------------------------------------------------- */
     899             :     const char *pszDEMMissingValue =
     900          46 :         CSLFetchNameValue(papszOptions, "RPC_DEM_MISSING_VALUE");
     901          46 :     if (pszDEMMissingValue != nullptr)
     902             :     {
     903           6 :         psTransform->bHasDEMMissingValue = TRUE;
     904           6 :         psTransform->dfDEMMissingValue = CPLAtof(pszDEMMissingValue);
     905             :     }
     906             : 
     907             :     /* -------------------------------------------------------------------- */
     908             :     /*                        The DEM SRS override                          */
     909             :     /* -------------------------------------------------------------------- */
     910          46 :     const char *pszDEMSRS = CSLFetchNameValue(papszOptions, "RPC_DEM_SRS");
     911          46 :     if (pszDEMSRS != nullptr)
     912             :     {
     913           1 :         psTransform->pszDEMSRS = CPLStrdup(pszDEMSRS);
     914             :     }
     915             : 
     916             :     /* -------------------------------------------------------------------- */
     917             :     /*      Whether to apply vdatum shift                                   */
     918             :     /* -------------------------------------------------------------------- */
     919          46 :     psTransform->bApplyDEMVDatumShift =
     920          46 :         CPLFetchBool(papszOptions, "RPC_DEM_APPLY_VDATUM_SHIFT", true);
     921             : 
     922          46 :     psTransform->nMaxIterations =
     923          46 :         atoi(CSLFetchNameValueDef(papszOptions, "RPC_MAX_ITERATIONS", "0"));
     924             : 
     925             :     /* -------------------------------------------------------------------- */
     926             :     /*      Debug                                                           */
     927             :     /* -------------------------------------------------------------------- */
     928          46 :     psTransform->bRPCInverseVerbose =
     929          46 :         CPLTestBool(CPLGetConfigOption("RPC_INVERSE_VERBOSE", "NO"));
     930             :     const char *pszRPCInverseLog =
     931          46 :         CPLGetConfigOption("RPC_INVERSE_LOG", nullptr);
     932          46 :     if (pszRPCInverseLog != nullptr)
     933           1 :         psTransform->pszRPCInverseLog = CPLStrdup(pszRPCInverseLog);
     934             : 
     935             :     /* -------------------------------------------------------------------- */
     936             :     /*      Footprint                                                       */
     937             :     /* -------------------------------------------------------------------- */
     938          46 :     const char *pszFootprint = CSLFetchNameValue(papszOptions, "RPC_FOOTPRINT");
     939          46 :     if (pszFootprint != nullptr)
     940             :     {
     941           1 :         psTransform->pszRPCFootprint = CPLStrdup(pszFootprint);
     942           1 :         if (pszFootprint[0] == '{')
     943             :         {
     944           0 :             psTransform->poRPCFootprintGeom =
     945           0 :                 OGRGeometryFactory::createFromGeoJson(pszFootprint);
     946             :         }
     947             :         else
     948             :         {
     949           1 :             OGRGeometryFactory::createFromWkt(
     950             :                 pszFootprint, nullptr, &(psTransform->poRPCFootprintGeom));
     951             :         }
     952           1 :         if (psTransform->poRPCFootprintGeom)
     953             :         {
     954           1 :             if (OGRHasPreparedGeometrySupport())
     955             :             {
     956           1 :                 psTransform->poRPCFootprintPreparedGeom =
     957           1 :                     OGRCreatePreparedGeometry(
     958             :                         OGRGeometry::ToHandle(psTransform->poRPCFootprintGeom));
     959             :             }
     960             :             else
     961             :             {
     962           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     963             :                          "GEOS not available. RPC_FOOTPRINT will be ignored");
     964             :             }
     965             :         }
     966             :     }
     967             : 
     968             :     /* -------------------------------------------------------------------- */
     969             :     /*      Open DEM if needed.                                             */
     970             :     /* -------------------------------------------------------------------- */
     971             : 
     972          46 :     if (psTransform->pszDEMPath != nullptr && !GDALRPCOpenDEM(psTransform))
     973             :     {
     974           1 :         GDALDestroyRPCTransformer(psTransform);
     975           1 :         return nullptr;
     976             :     }
     977             : 
     978             :     /* -------------------------------------------------------------------- */
     979             :     /*      Establish a reference point for calcualating an affine          */
     980             :     /*      geotransform approximate transformation.                        */
     981             :     /* -------------------------------------------------------------------- */
     982          45 :     double adfGTFromLL[6] = {};
     983          45 :     double dfRefPixel = -1.0;
     984          45 :     double dfRefLine = -1.0;
     985          45 :     double dfRefLong = 0.0;
     986          45 :     double dfRefLat = 0.0;
     987             : 
     988          45 :     if (psRPCInfo->dfMIN_LONG != -180 || psRPCInfo->dfMAX_LONG != 180)
     989             :     {
     990           1 :         dfRefLong = (psRPCInfo->dfMIN_LONG + psRPCInfo->dfMAX_LONG) * 0.5;
     991           1 :         dfRefLat = (psRPCInfo->dfMIN_LAT + psRPCInfo->dfMAX_LAT) * 0.5;
     992             : 
     993           1 :         double dfX = dfRefLong;
     994           1 :         double dfY = dfRefLat;
     995           1 :         double dfZ = 0.0;
     996           1 :         int nSuccess = 0;
     997             :         // Try with DEM first.
     998           1 :         if (GDALRPCTransform(psTransform, !(psTransform->bReversed), 1, &dfX,
     999           2 :                              &dfY, &dfZ, &nSuccess) &&
    1000           1 :             nSuccess)
    1001             :         {
    1002           1 :             dfRefPixel = dfX;
    1003           1 :             dfRefLine = dfY;
    1004             :         }
    1005             :         else
    1006             :         {
    1007           0 :             RPCTransformPoint(psTransform, dfRefLong, dfRefLat, 0.0,
    1008             :                               &dfRefPixel, &dfRefLine);
    1009             :         }
    1010             :     }
    1011             : 
    1012             :     // Try with scale and offset if we don't can't use bounds or
    1013             :     // the results seem daft.
    1014          45 :     if (dfRefPixel < 0.0 || dfRefLine < 0.0 || dfRefPixel > 100000 ||
    1015           1 :         dfRefLine > 100000)
    1016             :     {
    1017          44 :         dfRefLong = psRPCInfo->dfLONG_OFF;
    1018          44 :         dfRefLat = psRPCInfo->dfLAT_OFF;
    1019             : 
    1020          44 :         double dfX = dfRefLong;
    1021          44 :         double dfY = dfRefLat;
    1022          44 :         double dfZ = 0.0;
    1023          44 :         int nSuccess = 0;
    1024             :         // Try with DEM first.
    1025          44 :         if (GDALRPCTransform(psTransform, !(psTransform->bReversed), 1, &dfX,
    1026          88 :                              &dfY, &dfZ, &nSuccess) &&
    1027          44 :             nSuccess)
    1028             :         {
    1029          28 :             dfRefPixel = dfX;
    1030          28 :             dfRefLine = dfY;
    1031             :         }
    1032             :         else
    1033             :         {
    1034          16 :             RPCTransformPoint(psTransform, dfRefLong, dfRefLat, 0.0,
    1035             :                               &dfRefPixel, &dfRefLine);
    1036             :         }
    1037             :     }
    1038             : 
    1039          45 :     psTransform->dfRefZ = 0.0;
    1040          45 :     GDALRPCGetHeightAtLongLat(psTransform, dfRefLong, dfRefLat,
    1041             :                               &psTransform->dfRefZ);
    1042             : 
    1043             :     /* -------------------------------------------------------------------- */
    1044             :     /*      Transform nearby locations to establish affine direction        */
    1045             :     /*      vectors.                                                        */
    1046             :     /* -------------------------------------------------------------------- */
    1047          45 :     double dfRefPixelDelta = 0.0;
    1048          45 :     double dfRefLineDelta = 0.0;
    1049          45 :     double dfLLDelta = 0.0001;
    1050             : 
    1051          45 :     RPCTransformPoint(psTransform, dfRefLong + dfLLDelta, dfRefLat,
    1052             :                       psTransform->dfRefZ, &dfRefPixelDelta, &dfRefLineDelta);
    1053          45 :     adfGTFromLL[1] = (dfRefPixelDelta - dfRefPixel) / dfLLDelta;
    1054          45 :     adfGTFromLL[4] = (dfRefLineDelta - dfRefLine) / dfLLDelta;
    1055             : 
    1056          45 :     RPCTransformPoint(psTransform, dfRefLong, dfRefLat + dfLLDelta,
    1057             :                       psTransform->dfRefZ, &dfRefPixelDelta, &dfRefLineDelta);
    1058          45 :     adfGTFromLL[2] = (dfRefPixelDelta - dfRefPixel) / dfLLDelta;
    1059          45 :     adfGTFromLL[5] = (dfRefLineDelta - dfRefLine) / dfLLDelta;
    1060             : 
    1061          45 :     adfGTFromLL[0] =
    1062          45 :         dfRefPixel - adfGTFromLL[1] * dfRefLong - adfGTFromLL[2] * dfRefLat;
    1063          45 :     adfGTFromLL[3] =
    1064          45 :         dfRefLine - adfGTFromLL[4] * dfRefLong - adfGTFromLL[5] * dfRefLat;
    1065             : 
    1066          45 :     if (!GDALInvGeoTransform(adfGTFromLL,
    1067          45 :                              psTransform->adfPLToLatLongGeoTransform))
    1068             :     {
    1069           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
    1070           0 :         GDALDestroyRPCTransformer(psTransform);
    1071           0 :         return nullptr;
    1072             :     }
    1073             : 
    1074          45 :     return psTransform;
    1075             : }
    1076             : 
    1077             : /************************************************************************/
    1078             : /*                 GDALDestroyReprojectionTransformer()                 */
    1079             : /************************************************************************/
    1080             : 
    1081             : /** Destroy RPC transformer */
    1082          46 : void GDALDestroyRPCTransformer(void *pTransformAlg)
    1083             : 
    1084             : {
    1085          46 :     if (pTransformAlg == nullptr)
    1086           0 :         return;
    1087             : 
    1088          46 :     GDALRPCTransformInfo *psTransform =
    1089             :         static_cast<GDALRPCTransformInfo *>(pTransformAlg);
    1090             : 
    1091          46 :     CPLFree(psTransform->pszDEMPath);
    1092          46 :     CPLFree(psTransform->pszDEMSRS);
    1093             : 
    1094          46 :     if (psTransform->poDS)
    1095          32 :         GDALClose(psTransform->poDS);
    1096          46 :     delete psTransform->poCacheDEM;
    1097          46 :     if (psTransform->poCT)
    1098          11 :         OCTDestroyCoordinateTransformation(
    1099          11 :             reinterpret_cast<OGRCoordinateTransformationH>(psTransform->poCT));
    1100          46 :     CPLFree(psTransform->pszRPCInverseLog);
    1101             : 
    1102          46 :     CPLFree(psTransform->pszRPCFootprint);
    1103          46 :     delete psTransform->poRPCFootprintGeom;
    1104          46 :     OGRDestroyPreparedGeometry(psTransform->poRPCFootprintPreparedGeom);
    1105             : 
    1106          46 :     CPLFree(pTransformAlg);
    1107             : }
    1108             : 
    1109             : /************************************************************************/
    1110             : /*                      RPCInverseTransformPoint()                      */
    1111             : /************************************************************************/
    1112             : 
    1113       13614 : static bool RPCInverseTransformPoint(GDALRPCTransformInfo *psTransform,
    1114             :                                      double dfPixel, double dfLine,
    1115             :                                      double dfUserHeight, double *pdfLong,
    1116             :                                      double *pdfLat)
    1117             : 
    1118             : {
    1119             :     // Memo:
    1120             :     // Known to work with 40 iterations with DEM on all points (int coord and
    1121             :     // +0.5,+0.5 shift) of flock1.20160216_041050_0905.tif, especially on (0,0).
    1122             : 
    1123             :     /* -------------------------------------------------------------------- */
    1124             :     /*      Compute an initial approximation based on linear                */
    1125             :     /*      interpolation from our reference point.                         */
    1126             :     /* -------------------------------------------------------------------- */
    1127       13614 :     double dfResultX = psTransform->adfPLToLatLongGeoTransform[0] +
    1128       13614 :                        psTransform->adfPLToLatLongGeoTransform[1] * dfPixel +
    1129       13614 :                        psTransform->adfPLToLatLongGeoTransform[2] * dfLine;
    1130             : 
    1131       13614 :     double dfResultY = psTransform->adfPLToLatLongGeoTransform[3] +
    1132       13614 :                        psTransform->adfPLToLatLongGeoTransform[4] * dfPixel +
    1133       13614 :                        psTransform->adfPLToLatLongGeoTransform[5] * dfLine;
    1134             : 
    1135       13614 :     if (psTransform->bRPCInverseVerbose)
    1136             :     {
    1137           1 :         CPLDebug("RPC", "Computing inverse transform for (pixel,line)=(%f,%f)",
    1138             :                  dfPixel, dfLine);
    1139             :     }
    1140       13614 :     VSILFILE *fpLog = nullptr;
    1141       13614 :     if (psTransform->pszRPCInverseLog)
    1142             :     {
    1143           1 :         fpLog = VSIFOpenL(
    1144           1 :             CPLResetExtension(psTransform->pszRPCInverseLog, "csvt"), "wb");
    1145           1 :         if (fpLog != nullptr)
    1146             :         {
    1147           1 :             VSIFPrintfL(fpLog, "Integer,Real,Real,Real,String,Real,Real\n");
    1148           1 :             VSIFCloseL(fpLog);
    1149             :         }
    1150           1 :         fpLog = VSIFOpenL(psTransform->pszRPCInverseLog, "wb");
    1151           1 :         if (fpLog != nullptr)
    1152           1 :             VSIFPrintfL(
    1153             :                 fpLog,
    1154             :                 "iter,long,lat,height,WKT,error_pixel_x,error_pixel_y\n");
    1155             :     }
    1156             : 
    1157             :     /* -------------------------------------------------------------------- */
    1158             :     /*      Now iterate, trying to find a closer LL location that will      */
    1159             :     /*      back transform to the indicated pixel and line.                 */
    1160             :     /* -------------------------------------------------------------------- */
    1161       13614 :     double dfPixelDeltaX = 0.0;
    1162       13614 :     double dfPixelDeltaY = 0.0;
    1163       13614 :     double dfLastResultX = 0.0;
    1164       13614 :     double dfLastResultY = 0.0;
    1165       13614 :     double dfLastPixelDeltaX = 0.0;
    1166       13614 :     double dfLastPixelDeltaY = 0.0;
    1167       13614 :     bool bLastPixelDeltaValid = false;
    1168       27228 :     const int nMaxIterations = (psTransform->nMaxIterations > 0)
    1169       26237 :                                    ? psTransform->nMaxIterations
    1170       12623 :                                : (psTransform->poDS != nullptr) ? 20
    1171             :                                                                 : 10;
    1172       13614 :     int nCountConsecutiveErrorBelow2 = 0;
    1173             : 
    1174       13614 :     int iIter = 0;  // Used after for.
    1175       47918 :     for (; iIter < nMaxIterations; iIter++)
    1176             :     {
    1177       47388 :         double dfBackPixel = 0.0;
    1178       47388 :         double dfBackLine = 0.0;
    1179             : 
    1180             :         // Update DEMH.
    1181       47388 :         double dfDEMH = 0.0;
    1182       47388 :         double dfDEMPixel = 0.0;
    1183       47388 :         double dfDEMLine = 0.0;
    1184       47388 :         if (!GDALRPCGetHeightAtLongLat(psTransform, dfResultX, dfResultY,
    1185             :                                        &dfDEMH, &dfDEMPixel, &dfDEMLine))
    1186             :         {
    1187       20334 :             if (psTransform->poDS)
    1188             :             {
    1189       20334 :                 CPLDebug("RPC", "DEM (pixel, line) = (%g, %g)", dfDEMPixel,
    1190             :                          dfDEMLine);
    1191             :             }
    1192             : 
    1193             :             // The first time, the guess might be completely out of the
    1194             :             // validity of the DEM, so pickup the "reference Z" as the
    1195             :             // first guess or the closest point of the DEM by snapping to it.
    1196       20334 :             if (iIter == 0)
    1197             :             {
    1198       10262 :                 bool bUseRefZ = true;
    1199       10262 :                 if (psTransform->poDS)
    1200             :                 {
    1201       10262 :                     if (dfDEMPixel >= psTransform->poDS->GetRasterXSize())
    1202        4311 :                         dfDEMPixel = psTransform->poDS->GetRasterXSize() - 0.5;
    1203        5951 :                     else if (dfDEMPixel < 0)
    1204        1385 :                         dfDEMPixel = 0.5;
    1205       10262 :                     if (dfDEMLine >= psTransform->poDS->GetRasterYSize())
    1206          21 :                         dfDEMLine = psTransform->poDS->GetRasterYSize() - 0.5;
    1207       10241 :                     else if (dfDEMPixel < 0)
    1208           0 :                         dfDEMPixel = 0.5;
    1209       10262 :                     if (GDALRPCGetDEMHeight(psTransform, dfDEMPixel, dfDEMLine,
    1210       10262 :                                             &dfDEMH))
    1211             :                     {
    1212        1900 :                         bUseRefZ = false;
    1213        1900 :                         CPLDebug("RPC",
    1214             :                                  "Iteration %d for (pixel, line) = (%g, %g): "
    1215             :                                  "No elevation value at %.15g %.15g. "
    1216             :                                  "Using elevation %g at DEM (pixel, line) = "
    1217             :                                  "(%g, %g) (snapping to boundaries) instead",
    1218             :                                  iIter, dfPixel, dfLine, dfResultX, dfResultY,
    1219             :                                  dfDEMH, dfDEMPixel, dfDEMLine);
    1220             :                     }
    1221             :                 }
    1222       10262 :                 if (bUseRefZ)
    1223             :                 {
    1224        8362 :                     dfDEMH = psTransform->dfRefZ;
    1225        8362 :                     CPLDebug("RPC",
    1226             :                              "Iteration %d for (pixel, line) = (%g, %g): "
    1227             :                              "No elevation value at %.15g %.15g. "
    1228             :                              "Using elevation %g of reference point instead",
    1229             :                              iIter, dfPixel, dfLine, dfResultX, dfResultY,
    1230             :                              dfDEMH);
    1231             :                 }
    1232             :             }
    1233             :             else
    1234             :             {
    1235       10072 :                 CPLDebug("RPC",
    1236             :                          "Iteration %d for (pixel, line) = (%g, %g): "
    1237             :                          "No elevation value at %.15g %.15g. Erroring out",
    1238             :                          iIter, dfPixel, dfLine, dfResultX, dfResultY);
    1239       10072 :                 if (fpLog)
    1240           0 :                     VSIFCloseL(fpLog);
    1241       10072 :                 return false;
    1242             :             }
    1243             :         }
    1244             : 
    1245       37316 :         RPCTransformPoint(psTransform, dfResultX, dfResultY,
    1246             :                           dfUserHeight + dfDEMH, &dfBackPixel, &dfBackLine);
    1247             : 
    1248       37316 :         dfPixelDeltaX = dfBackPixel - dfPixel;
    1249       37316 :         dfPixelDeltaY = dfBackLine - dfLine;
    1250             : 
    1251       37316 :         if (psTransform->bRPCInverseVerbose)
    1252             :         {
    1253           8 :             CPLDebug("RPC",
    1254             :                      "Iter %d: dfPixelDeltaX=%.02f, dfPixelDeltaY=%.02f, "
    1255             :                      "long=%f, lat=%f, height=%f",
    1256             :                      iIter, dfPixelDeltaX, dfPixelDeltaY, dfResultX, dfResultY,
    1257             :                      dfUserHeight + dfDEMH);
    1258             :         }
    1259       37316 :         if (fpLog != nullptr)
    1260             :         {
    1261           8 :             VSIFPrintfL(fpLog,
    1262             :                         "%d,%.12f,%.12f,%f,\"POINT(%.12f %.12f)\",%f,%f\n",
    1263             :                         iIter, dfResultX, dfResultY, dfUserHeight + dfDEMH,
    1264             :                         dfResultX, dfResultY, dfPixelDeltaX, dfPixelDeltaY);
    1265             :         }
    1266             : 
    1267             :         const double dfError =
    1268       37316 :             std::max(std::abs(dfPixelDeltaX), std::abs(dfPixelDeltaY));
    1269       37316 :         if (dfError < psTransform->dfPixErrThreshold)
    1270             :         {
    1271        3012 :             iIter = -1;
    1272        3012 :             if (psTransform->bRPCInverseVerbose)
    1273             :             {
    1274           1 :                 CPLDebug("RPC", "Converged!");
    1275             :             }
    1276        3012 :             break;
    1277             :         }
    1278       34304 :         else if (psTransform->poDS != nullptr && bLastPixelDeltaValid &&
    1279       21058 :                  dfPixelDeltaX * dfLastPixelDeltaX < 0 &&
    1280         106 :                  dfPixelDeltaY * dfLastPixelDeltaY < 0)
    1281             :         {
    1282             :             // When there is a DEM, if the error changes sign, we might
    1283             :             // oscillate forever, so take a mean position as a new guess.
    1284          40 :             if (psTransform->bRPCInverseVerbose)
    1285             :             {
    1286           1 :                 CPLDebug("RPC",
    1287             :                          "Oscillation detected. "
    1288             :                          "Taking mean of 2 previous results as new guess");
    1289             :             }
    1290          40 :             dfResultX = (fabs(dfPixelDeltaX) * dfLastResultX +
    1291          40 :                          fabs(dfLastPixelDeltaX) * dfResultX) /
    1292          40 :                         (fabs(dfPixelDeltaX) + fabs(dfLastPixelDeltaX));
    1293          40 :             dfResultY = (fabs(dfPixelDeltaY) * dfLastResultY +
    1294          40 :                          fabs(dfLastPixelDeltaY) * dfResultY) /
    1295          40 :                         (fabs(dfPixelDeltaY) + fabs(dfLastPixelDeltaY));
    1296          40 :             bLastPixelDeltaValid = false;
    1297          40 :             nCountConsecutiveErrorBelow2 = 0;
    1298          40 :             continue;
    1299             :         }
    1300             : 
    1301       34264 :         double dfBoostFactor = 1.0;
    1302       34264 :         if (psTransform->poDS != nullptr && nCountConsecutiveErrorBelow2 >= 5 &&
    1303             :             dfError < 2)
    1304             :         {
    1305             :             // When there is a DEM, if we remain below a given threshold
    1306             :             // (somewhat arbitrarily set to 2 pixels) for some time, apply a
    1307             :             // "boost factor" for the new guessed result, in the hope we will go
    1308             :             // out of the somewhat current stuck situation.
    1309          13 :             dfBoostFactor = 10;
    1310          13 :             if (psTransform->bRPCInverseVerbose)
    1311             :             {
    1312           1 :                 CPLDebug("RPC", "Applying boost factor 10");
    1313             :             }
    1314             :         }
    1315             : 
    1316       34264 :         if (dfError < 2)
    1317        1340 :             nCountConsecutiveErrorBelow2++;
    1318             :         else
    1319       32924 :             nCountConsecutiveErrorBelow2 = 0;
    1320             : 
    1321       34264 :         const double dfNewResultX =
    1322       34264 :             dfResultX -
    1323       34264 :             (dfPixelDeltaX * psTransform->adfPLToLatLongGeoTransform[1] *
    1324             :              dfBoostFactor) -
    1325       34264 :             (dfPixelDeltaY * psTransform->adfPLToLatLongGeoTransform[2] *
    1326             :              dfBoostFactor);
    1327       34264 :         const double dfNewResultY =
    1328       34264 :             dfResultY -
    1329       34264 :             (dfPixelDeltaX * psTransform->adfPLToLatLongGeoTransform[4] *
    1330             :              dfBoostFactor) -
    1331       34264 :             (dfPixelDeltaY * psTransform->adfPLToLatLongGeoTransform[5] *
    1332             :              dfBoostFactor);
    1333             : 
    1334       34264 :         dfLastResultX = dfResultX;
    1335       34264 :         dfLastResultY = dfResultY;
    1336       34264 :         dfResultX = dfNewResultX;
    1337       34264 :         dfResultY = dfNewResultY;
    1338       34264 :         dfLastPixelDeltaX = dfPixelDeltaX;
    1339       34264 :         dfLastPixelDeltaY = dfPixelDeltaY;
    1340       34264 :         bLastPixelDeltaValid = true;
    1341             :     }
    1342        3542 :     if (fpLog != nullptr)
    1343           1 :         VSIFCloseL(fpLog);
    1344             : 
    1345        3542 :     if (iIter != -1)
    1346             :     {
    1347         530 :         CPLDebug("RPC", "Failed Iterations %d: Got: %.16g,%.16g  Offset=%g,%g",
    1348             :                  iIter, dfResultX, dfResultY, dfPixelDeltaX, dfPixelDeltaY);
    1349         530 :         return false;
    1350             :     }
    1351             : 
    1352        3012 :     *pdfLong = dfResultX;
    1353        3012 :     *pdfLat = dfResultY;
    1354        3012 :     return true;
    1355             : }
    1356             : 
    1357             : /************************************************************************/
    1358             : /*                        GDALRPCGetDEMHeight()                         */
    1359             : /************************************************************************/
    1360             : 
    1361      306532 : static int GDALRPCGetDEMHeight(GDALRPCTransformInfo *psTransform,
    1362             :                                const double dfXIn, const double dfYIn,
    1363             :                                double *pdfDEMH)
    1364             : {
    1365      306532 :     GDALRIOResampleAlg eResample = GDALRIOResampleAlg::GRIORA_NearestNeighbour;
    1366      306532 :     switch (psTransform->eResampleAlg)
    1367             :     {
    1368          14 :         case DEMResampleAlg::DRA_NearestNeighbour:
    1369          14 :             eResample = GDALRIOResampleAlg::GRIORA_NearestNeighbour;
    1370          14 :             break;
    1371      306504 :         case DEMResampleAlg::DRA_Bilinear:
    1372      306504 :             eResample = GDALRIOResampleAlg::GRIORA_Bilinear;
    1373      306504 :             break;
    1374          14 :         case DEMResampleAlg::DRA_CubicSpline:
    1375          14 :             eResample = GDALRIOResampleAlg::GRIORA_CubicSpline;
    1376          14 :             break;
    1377             :     }
    1378             : 
    1379      306532 :     std::unique_ptr<DoublePointsCache> cacheDEM{psTransform->poCacheDEM};
    1380             :     int res =
    1381      306532 :         GDALInterpolateAtPoint(psTransform->poDS->GetRasterBand(1), eResample,
    1382      306532 :                                cacheDEM, dfXIn, dfYIn, pdfDEMH, nullptr);
    1383      306532 :     psTransform->poCacheDEM = cacheDEM.release();
    1384      613064 :     return res;
    1385             : }
    1386             : 
    1387             : /************************************************************************/
    1388             : /*                           RPCIsValidLongLat()                        */
    1389             : /************************************************************************/
    1390             : 
    1391      547280 : static bool RPCIsValidLongLat(const GDALRPCTransformInfo *psTransform,
    1392             :                               double dfLong, double dfLat)
    1393             : {
    1394      547280 :     if (!psTransform->poRPCFootprintPreparedGeom)
    1395      427167 :         return true;
    1396             : 
    1397      240226 :     OGRPoint p(dfLong, dfLat);
    1398      240226 :     return CPL_TO_BOOL(OGRPreparedGeometryContains(
    1399      240226 :         psTransform->poRPCFootprintPreparedGeom, OGRGeometry::ToHandle(&p)));
    1400             : }
    1401             : 
    1402             : /************************************************************************/
    1403             : /*                    GDALRPCTransformWholeLineWithDEM()                */
    1404             : /************************************************************************/
    1405             : 
    1406             : static int
    1407        2192 : GDALRPCTransformWholeLineWithDEM(const GDALRPCTransformInfo *psTransform,
    1408             :                                  int nPointCount, double *padfX, double *padfY,
    1409             :                                  double *padfZ, int *panSuccess, int nXLeft,
    1410             :                                  int nXWidth, int nYTop, int nYHeight)
    1411             : {
    1412             :     double *padfDEMBuffer = static_cast<double *>(
    1413        2192 :         VSI_MALLOC3_VERBOSE(sizeof(double), nXWidth, nYHeight));
    1414        2192 :     if (padfDEMBuffer == nullptr)
    1415             :     {
    1416           0 :         for (int i = 0; i < nPointCount; i++)
    1417           0 :             panSuccess[i] = FALSE;
    1418           0 :         return FALSE;
    1419             :     }
    1420        2192 :     CPLErr eErr = psTransform->poDS->GetRasterBand(1)->RasterIO(
    1421             :         GF_Read, nXLeft, nYTop, nXWidth, nYHeight, padfDEMBuffer, nXWidth,
    1422             :         nYHeight, GDT_Float64, 0, 0, nullptr);
    1423        2192 :     if (eErr != CE_None)
    1424             :     {
    1425           0 :         for (int i = 0; i < nPointCount; i++)
    1426           0 :             panSuccess[i] = FALSE;
    1427           0 :         VSIFree(padfDEMBuffer);
    1428           0 :         return FALSE;
    1429             :     }
    1430             : 
    1431        2192 :     int bGotNoDataValue = FALSE;
    1432             :     const double dfNoDataValue =
    1433        2192 :         psTransform->poDS->GetRasterBand(1)->GetNoDataValue(&bGotNoDataValue);
    1434             : 
    1435             :     // dfY in pixel center convention.
    1436        2192 :     const double dfY = psTransform->adfDEMReverseGeoTransform[3] +
    1437        2192 :                        padfY[0] * psTransform->adfDEMReverseGeoTransform[5] -
    1438             :                        0.5;
    1439        2192 :     const int nY = static_cast<int>(dfY);
    1440        2192 :     const double dfDeltaY = dfY - nY;
    1441             : 
    1442      180781 :     for (int i = 0; i < nPointCount; i++)
    1443             :     {
    1444      178589 :         if (padfX[i] == HUGE_VAL)
    1445           0 :             continue;
    1446             : 
    1447      178589 :         double dfDEMH = 0.0;
    1448      178589 :         const double dfZ_i = padfZ ? padfZ[i] : 0.0;
    1449             : 
    1450      178589 :         if (psTransform->eResampleAlg == DRA_CubicSpline)
    1451             :         {
    1452             :             // dfX in pixel center convention.
    1453          10 :             const double dfX =
    1454          10 :                 psTransform->adfDEMReverseGeoTransform[0] +
    1455          10 :                 padfX[i] * psTransform->adfDEMReverseGeoTransform[1] - 0.5;
    1456          10 :             const int nX = static_cast<int>(dfX);
    1457          10 :             const double dfDeltaX = dfX - nX;
    1458             : 
    1459          10 :             const int nXNew = nX - 1;
    1460             : 
    1461          10 :             double dfSumH = 0.0;
    1462          10 :             double dfSumWeight = 0.0;
    1463          50 :             for (int k_i = 0; k_i < 4; k_i++)
    1464             :             {
    1465             :                 // Loop across the X axis.
    1466         200 :                 for (int k_j = 0; k_j < 4; k_j++)
    1467             :                 {
    1468             :                     // Calculate the weight for the specified pixel according
    1469             :                     // to the bicubic b-spline kernel we're using for
    1470             :                     // interpolation.
    1471         160 :                     const int dKernIndX = k_j - 1;
    1472         160 :                     const int dKernIndY = k_i - 1;
    1473             :                     const double dfPixelWeight =
    1474         160 :                         CubicSplineKernel(dKernIndX - dfDeltaX) *
    1475         160 :                         CubicSplineKernel(dKernIndY - dfDeltaY);
    1476             : 
    1477             :                     // Create a sum of all values
    1478             :                     // adjusted for the pixel's calculated weight.
    1479         160 :                     const double dfElev =
    1480         160 :                         padfDEMBuffer[k_i * nXWidth + nXNew - nXLeft + k_j];
    1481         160 :                     if (bGotNoDataValue &&
    1482         160 :                         ARE_REAL_EQUAL(dfNoDataValue, dfElev))
    1483           0 :                         continue;
    1484             : 
    1485         160 :                     dfSumH += dfElev * dfPixelWeight;
    1486         160 :                     dfSumWeight += dfPixelWeight;
    1487             :                 }
    1488             :             }
    1489          10 :             if (dfSumWeight == 0.0)
    1490             :             {
    1491           0 :                 if (psTransform->bHasDEMMissingValue)
    1492           0 :                     dfDEMH = psTransform->dfDEMMissingValue;
    1493             :                 else
    1494             :                 {
    1495           0 :                     panSuccess[i] = FALSE;
    1496           0 :                     continue;
    1497             :                 }
    1498             :             }
    1499             :             else
    1500          10 :                 dfDEMH = dfSumH / dfSumWeight;
    1501             :         }
    1502      178579 :         else if (psTransform->eResampleAlg == DRA_Bilinear)
    1503             :         {
    1504             :             // dfX in pixel center convention.
    1505      178569 :             const double dfX =
    1506      178569 :                 psTransform->adfDEMReverseGeoTransform[0] +
    1507      178569 :                 padfX[i] * psTransform->adfDEMReverseGeoTransform[1] - 0.5;
    1508      178569 :             const int nX = static_cast<int>(dfX);
    1509      178569 :             const double dfDeltaX = dfX - nX;
    1510             : 
    1511             :             // Bilinear interpolation.
    1512      178569 :             double adfElevData[4] = {};
    1513      178569 :             memcpy(adfElevData, padfDEMBuffer + nX - nXLeft,
    1514             :                    2 * sizeof(double));
    1515      178569 :             memcpy(adfElevData + 2, padfDEMBuffer + nXWidth + nX - nXLeft,
    1516             :                    2 * sizeof(double));
    1517             : 
    1518      178569 :             int bFoundNoDataElev = FALSE;
    1519      178569 :             if (bGotNoDataValue)
    1520             :             {
    1521       46350 :                 int k_valid_sample = -1;
    1522      231750 :                 for (int k_i = 0; k_i < 4; k_i++)
    1523             :                 {
    1524      185400 :                     if (ARE_REAL_EQUAL(dfNoDataValue, adfElevData[k_i]))
    1525             :                     {
    1526           0 :                         bFoundNoDataElev = TRUE;
    1527             :                     }
    1528      185400 :                     else if (k_valid_sample < 0)
    1529             :                     {
    1530       46350 :                         k_valid_sample = k_i;
    1531             :                     }
    1532             :                 }
    1533       46350 :                 if (bFoundNoDataElev)
    1534             :                 {
    1535           0 :                     if (k_valid_sample >= 0)
    1536             :                     {
    1537           0 :                         if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
    1538             :                         {
    1539           0 :                             panSuccess[i] = FALSE;
    1540           0 :                             padfX[i] = HUGE_VAL;
    1541           0 :                             padfY[i] = HUGE_VAL;
    1542           0 :                             continue;
    1543             :                         }
    1544           0 :                         dfDEMH = adfElevData[k_valid_sample];
    1545           0 :                         RPCTransformPoint(
    1546           0 :                             psTransform, padfX[i], padfY[i],
    1547           0 :                             dfZ_i + (psTransform->dfHeightOffset + dfDEMH) *
    1548           0 :                                         psTransform->dfHeightScale,
    1549           0 :                             padfX + i, padfY + i);
    1550             : 
    1551           0 :                         panSuccess[i] = TRUE;
    1552           0 :                         continue;
    1553             :                     }
    1554           0 :                     else if (psTransform->bHasDEMMissingValue)
    1555             :                     {
    1556           0 :                         if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
    1557             :                         {
    1558           0 :                             panSuccess[i] = FALSE;
    1559           0 :                             padfX[i] = HUGE_VAL;
    1560           0 :                             padfY[i] = HUGE_VAL;
    1561           0 :                             continue;
    1562             :                         }
    1563           0 :                         dfDEMH = psTransform->dfDEMMissingValue;
    1564           0 :                         RPCTransformPoint(
    1565           0 :                             psTransform, padfX[i], padfY[i],
    1566           0 :                             dfZ_i + (psTransform->dfHeightOffset + dfDEMH) *
    1567           0 :                                         psTransform->dfHeightScale,
    1568           0 :                             padfX + i, padfY + i);
    1569             : 
    1570           0 :                         panSuccess[i] = TRUE;
    1571           0 :                         continue;
    1572             :                     }
    1573             :                     else
    1574             :                     {
    1575           0 :                         panSuccess[i] = FALSE;
    1576           0 :                         padfX[i] = HUGE_VAL;
    1577           0 :                         padfY[i] = HUGE_VAL;
    1578           0 :                         continue;
    1579             :                     }
    1580             :                 }
    1581             :             }
    1582      178569 :             const double dfDeltaX1 = 1.0 - dfDeltaX;
    1583      178569 :             const double dfDeltaY1 = 1.0 - dfDeltaY;
    1584             : 
    1585      178569 :             const double dfXZ1 =
    1586      178569 :                 adfElevData[0] * dfDeltaX1 + adfElevData[1] * dfDeltaX;
    1587      178569 :             const double dfXZ2 =
    1588      178569 :                 adfElevData[2] * dfDeltaX1 + adfElevData[3] * dfDeltaX;
    1589      178569 :             const double dfYZ = dfXZ1 * dfDeltaY1 + dfXZ2 * dfDeltaY;
    1590      178569 :             dfDEMH = dfYZ;
    1591             :         }
    1592             :         else
    1593             :         {
    1594          10 :             const double dfX =
    1595          10 :                 psTransform->adfDEMReverseGeoTransform[0] +
    1596          10 :                 padfX[i] * psTransform->adfDEMReverseGeoTransform[1];
    1597          10 :             const int nX = int(dfX);
    1598             : 
    1599          10 :             dfDEMH = padfDEMBuffer[nX - nXLeft];
    1600          10 :             if (bGotNoDataValue && ARE_REAL_EQUAL(dfNoDataValue, dfDEMH))
    1601             :             {
    1602           0 :                 if (psTransform->bHasDEMMissingValue)
    1603           0 :                     dfDEMH = psTransform->dfDEMMissingValue;
    1604             :                 else
    1605             :                 {
    1606           0 :                     panSuccess[i] = FALSE;
    1607           0 :                     padfX[i] = HUGE_VAL;
    1608           0 :                     padfY[i] = HUGE_VAL;
    1609           0 :                     continue;
    1610             :                 }
    1611             :             }
    1612             :         }
    1613             : 
    1614      178589 :         if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
    1615             :         {
    1616           0 :             panSuccess[i] = FALSE;
    1617           0 :             padfX[i] = HUGE_VAL;
    1618           0 :             padfY[i] = HUGE_VAL;
    1619           0 :             continue;
    1620             :         }
    1621      178589 :         RPCTransformPoint(psTransform, padfX[i], padfY[i],
    1622      178589 :                           dfZ_i + (psTransform->dfHeightOffset + dfDEMH) *
    1623      178589 :                                       psTransform->dfHeightScale,
    1624      178589 :                           padfX + i, padfY + i);
    1625             : 
    1626      178589 :         panSuccess[i] = TRUE;
    1627             :     }
    1628             : 
    1629        2192 :     VSIFree(padfDEMBuffer);
    1630             : 
    1631        2192 :     return TRUE;
    1632             : }
    1633             : 
    1634             : /************************************************************************/
    1635             : /*                           GDALRPCOpenDEM()                           */
    1636             : /************************************************************************/
    1637             : 
    1638          33 : static bool GDALRPCOpenDEM(GDALRPCTransformInfo *psTransform)
    1639             : {
    1640          33 :     CPLAssert(psTransform->pszDEMPath != nullptr);
    1641             : 
    1642          33 :     bool bIsValid = false;
    1643             : 
    1644          66 :     CPLString osPrevValueConfigOption;
    1645          33 :     if (psTransform->bApplyDEMVDatumShift)
    1646             :     {
    1647             :         osPrevValueConfigOption =
    1648          32 :             CPLGetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "");
    1649          32 :         CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
    1650             :     }
    1651          33 :     CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
    1652          33 :     psTransform->poDS =
    1653          33 :         GDALDataset::FromHandle(GDALOpen(psTransform->pszDEMPath, GA_ReadOnly));
    1654          65 :     if (psTransform->poDS != nullptr &&
    1655          32 :         psTransform->poDS->GetRasterCount() >= 1)
    1656             :     {
    1657          64 :         OGRSpatialReference oDEMSRS;
    1658          32 :         if (psTransform->pszDEMSRS != nullptr)
    1659             :         {
    1660           1 :             oDEMSRS.SetFromUserInput(psTransform->pszDEMSRS);
    1661           1 :             oDEMSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1662             :         }
    1663             : 
    1664          32 :         auto poDSSpaRefSrc = psTransform->pszDEMSRS != nullptr
    1665          32 :                                  ? &oDEMSRS
    1666          31 :                                  : psTransform->poDS->GetSpatialRef();
    1667          32 :         if (poDSSpaRefSrc)
    1668             :         {
    1669          32 :             auto poDSSpaRef = poDSSpaRefSrc->Clone();
    1670             : 
    1671          32 :             if (!psTransform->bApplyDEMVDatumShift)
    1672           1 :                 poDSSpaRef->StripVertical();
    1673             : 
    1674          32 :             auto wkt_EPSG_4979 =
    1675             :                 "GEODCRS[\"WGS 84\",\n"
    1676             :                 "    DATUM[\"World Geodetic System 1984\",\n"
    1677             :                 "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
    1678             :                 "            LENGTHUNIT[\"metre\",1]]],\n"
    1679             :                 "    PRIMEM[\"Greenwich\",0,\n"
    1680             :                 "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
    1681             :                 "    CS[ellipsoidal,3],\n"
    1682             :                 "        AXIS[\"geodetic latitude (Lat)\",north,\n"
    1683             :                 "            ORDER[1],\n"
    1684             :                 "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
    1685             :                 "        AXIS[\"geodetic longitude (Lon)\",east,\n"
    1686             :                 "            ORDER[2],\n"
    1687             :                 "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
    1688             :                 "        AXIS[\"ellipsoidal height (h)\",up,\n"
    1689             :                 "            ORDER[3],\n"
    1690             :                 "            LENGTHUNIT[\"metre\",1]],\n"
    1691             :                 "    AREA[\"World (by country)\"],\n"
    1692             :                 "    BBOX[-90,-180,90,180],\n"
    1693             :                 "    ID[\"EPSG\",4979]]";
    1694             :             OGRSpatialReference *poWGSSpaRef = new OGRSpatialReference(
    1695          32 :                 poDSSpaRef->IsCompound() ? wkt_EPSG_4979
    1696          32 :                                          : SRS_WKT_WGS84_LAT_LONG);
    1697          32 :             poWGSSpaRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1698             : 
    1699          32 :             if (!poWGSSpaRef->IsSame(poDSSpaRef))
    1700          12 :                 psTransform->poCT =
    1701          12 :                     OGRCreateCoordinateTransformation(poWGSSpaRef, poDSSpaRef);
    1702             : 
    1703          32 :             if (psTransform->poCT != nullptr && !poDSSpaRef->IsCompound())
    1704             :             {
    1705             :                 // Empiric attempt to guess if the coordinate transformation
    1706             :                 // to WGS84 is a no-op. For example for NED13 datasets in
    1707             :                 // NAD83.
    1708          10 :                 double adfX[] = {-179.0, 179.0, 179.0, -179.0, 0.0, 0.0};
    1709          10 :                 double adfY[] = {89.0, 89.0, -89.0, -89.0, 0.0, 0.0};
    1710          10 :                 double adfZ[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
    1711             : 
    1712             :                 // Also test with a "reference point" from the RPC values.
    1713          10 :                 double dfRefLong = 0.0;
    1714          10 :                 double dfRefLat = 0.0;
    1715          10 :                 if (psTransform->sRPC.dfMIN_LONG != -180 ||
    1716          10 :                     psTransform->sRPC.dfMAX_LONG != 180)
    1717             :                 {
    1718           0 :                     dfRefLong = (psTransform->sRPC.dfMIN_LONG +
    1719           0 :                                  psTransform->sRPC.dfMAX_LONG) *
    1720             :                                 0.5;
    1721           0 :                     dfRefLat = (psTransform->sRPC.dfMIN_LAT +
    1722           0 :                                 psTransform->sRPC.dfMAX_LAT) *
    1723             :                                0.5;
    1724             :                 }
    1725             :                 else
    1726             :                 {
    1727          10 :                     dfRefLong = psTransform->sRPC.dfLONG_OFF;
    1728          10 :                     dfRefLat = psTransform->sRPC.dfLAT_OFF;
    1729             :                 }
    1730          10 :                 adfX[5] = dfRefLong;
    1731          10 :                 adfY[5] = dfRefLat;
    1732             : 
    1733          10 :                 if (psTransform->poCT->Transform(6, adfX, adfY, adfZ) &&
    1734          10 :                     fabs(adfX[0] - -179.0) < 1.0e-12 &&
    1735           1 :                     fabs(adfY[0] - 89.0) < 1.0e-12 &&
    1736           1 :                     fabs(adfX[1] - 179.0) < 1.0e-12 &&
    1737           1 :                     fabs(adfY[1] - 89.0) < 1.0e-12 &&
    1738           1 :                     fabs(adfX[2] - 179.0) < 1.0e-12 &&
    1739           1 :                     fabs(adfY[2] - -89.0) < 1.0e-12 &&
    1740           1 :                     fabs(adfX[3] - -179.0) < 1.0e-12 &&
    1741           1 :                     fabs(adfY[3] - -89.0) < 1.0e-12 &&
    1742           1 :                     fabs(adfX[4] - 0.0) < 1.0e-12 &&
    1743           1 :                     fabs(adfY[4] - 0.0) < 1.0e-12 &&
    1744          21 :                     fabs(adfX[5] - dfRefLong) < 1.0e-12 &&
    1745           1 :                     fabs(adfY[5] - dfRefLat) < 1.0e-12)
    1746             :                 {
    1747           1 :                     CPLDebug("RPC",
    1748             :                              "Short-circuiting coordinate transformation "
    1749             :                              "from DEM SRS to WGS 84 due to apparent nop");
    1750           1 :                     delete psTransform->poCT;
    1751           1 :                     psTransform->poCT = nullptr;
    1752             :                 }
    1753             :             }
    1754             : 
    1755          32 :             delete poWGSSpaRef;
    1756          32 :             delete poDSSpaRef;
    1757             :         }
    1758             : 
    1759          96 :         if (psTransform->poDS->GetGeoTransform(
    1760          64 :                 psTransform->adfDEMGeoTransform) == CE_None &&
    1761          32 :             GDALInvGeoTransform(psTransform->adfDEMGeoTransform,
    1762          32 :                                 psTransform->adfDEMReverseGeoTransform))
    1763             :         {
    1764          32 :             bIsValid = true;
    1765             :         }
    1766             :     }
    1767             : 
    1768          33 :     if (psTransform->bApplyDEMVDatumShift)
    1769             :     {
    1770          32 :         CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
    1771          32 :                                       !osPrevValueConfigOption.empty()
    1772           0 :                                           ? osPrevValueConfigOption.c_str()
    1773             :                                           : nullptr);
    1774             :     }
    1775             : 
    1776          66 :     return bIsValid;
    1777             : }
    1778             : 
    1779             : /************************************************************************/
    1780             : /*                          GDALRPCTransform()                          */
    1781             : /************************************************************************/
    1782             : 
    1783             : /** RPC transform */
    1784        9386 : int GDALRPCTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
    1785             :                      double *padfX, double *padfY, double *padfZ,
    1786             :                      int *panSuccess)
    1787             : 
    1788             : {
    1789        9386 :     VALIDATE_POINTER1(pTransformArg, "GDALRPCTransform", 0);
    1790             : 
    1791        9386 :     GDALRPCTransformInfo *psTransform =
    1792             :         static_cast<GDALRPCTransformInfo *>(pTransformArg);
    1793             : 
    1794        9386 :     if (psTransform->bReversed)
    1795           0 :         bDstToSrc = !bDstToSrc;
    1796             : 
    1797             :     /* -------------------------------------------------------------------- */
    1798             :     /*      The simple case is transforming from lat/long to pixel/line.    */
    1799             :     /*      Just apply the equations directly.                              */
    1800             :     /* -------------------------------------------------------------------- */
    1801        9386 :     if (bDstToSrc)
    1802             :     {
    1803             :         // Optimization to avoid doing too many picking in DEM in the particular
    1804             :         // case where each point to transform is on a single line of the DEM.
    1805             :         // To make it simple and fast we check that all input latitudes are
    1806             :         // identical, that the DEM is in WGS84 geodetic and that it has no
    1807             :         // rotation.  Such case is for example triggered when doing gdalwarp
    1808             :         // with a target SRS of EPSG:4326 or EPSG:3857.
    1809        3473 :         if (nPointCount >= 10 && psTransform->poDS != nullptr &&
    1810        3446 :             psTransform->poCT == nullptr &&
    1811        2884 :             padfY[0] == padfY[nPointCount - 1] &&
    1812        2826 :             padfY[0] == padfY[nPointCount / 2] &&
    1813        2822 :             psTransform->adfDEMReverseGeoTransform[1] > 0.0 &&
    1814        2822 :             psTransform->adfDEMReverseGeoTransform[2] == 0.0 &&
    1815       13948 :             psTransform->adfDEMReverseGeoTransform[4] == 0.0 &&
    1816        2822 :             CPLTestBool(CPLGetConfigOption("GDAL_RPC_DEM_OPTIM", "YES")))
    1817             :         {
    1818        2822 :             bool bUseOptimized = true;
    1819        2822 :             double dfMinX = padfX[0];
    1820        2822 :             double dfMaxX = padfX[0];
    1821      327074 :             for (int i = 1; i < nPointCount; i++)
    1822             :             {
    1823      324252 :                 if (padfY[i] != padfY[0])
    1824             :                 {
    1825           0 :                     bUseOptimized = false;
    1826           0 :                     break;
    1827             :                 }
    1828      324252 :                 if (padfX[i] < dfMinX)
    1829           0 :                     dfMinX = padfX[i];
    1830      324252 :                 if (padfX[i] > dfMaxX)
    1831      324225 :                     dfMaxX = padfX[i];
    1832             :             }
    1833        2822 :             if (bUseOptimized)
    1834             :             {
    1835        2822 :                 double dfX1 = 0.0;
    1836        2822 :                 double dfY1 = 0.0;
    1837        2822 :                 double dfX2 = 0.0;
    1838        2822 :                 double dfY2 = 0.0;
    1839        2822 :                 GDALApplyGeoTransform(psTransform->adfDEMReverseGeoTransform,
    1840             :                                       dfMinX, padfY[0], &dfX1, &dfY1);
    1841        2822 :                 GDALApplyGeoTransform(psTransform->adfDEMReverseGeoTransform,
    1842             :                                       dfMaxX, padfY[0], &dfX2, &dfY2);
    1843             : 
    1844             :                 // Convert to center of pixel convention for reading the image
    1845             :                 // data.
    1846        2822 :                 if (psTransform->eResampleAlg != DRA_NearestNeighbour)
    1847             :                 {
    1848        2821 :                     dfX1 -= 0.5;
    1849        2821 :                     dfY1 -= 0.5;
    1850        2821 :                     dfX2 -= 0.5;
    1851             :                     // dfY2 -= 0.5;
    1852             :                 }
    1853        2822 :                 int nXLeft = static_cast<int>(floor(dfX1));
    1854        2822 :                 int nXRight = static_cast<int>(floor(dfX2));
    1855        2822 :                 int nXWidth = nXRight - nXLeft + 1;
    1856        2822 :                 int nYTop = static_cast<int>(floor(dfY1));
    1857             :                 int nYHeight;
    1858        2822 :                 if (psTransform->eResampleAlg == DRA_CubicSpline)
    1859             :                 {
    1860           1 :                     nXLeft--;
    1861           1 :                     nXWidth += 3;
    1862           1 :                     nYTop--;
    1863           1 :                     nYHeight = 4;
    1864             :                 }
    1865        2821 :                 else if (psTransform->eResampleAlg == DRA_Bilinear)
    1866             :                 {
    1867        2820 :                     nXWidth++;
    1868        2820 :                     nYHeight = 2;
    1869             :                 }
    1870             :                 else
    1871             :                 {
    1872           1 :                     nYHeight = 1;
    1873             :                 }
    1874        5022 :                 if (nXLeft >= 0 &&
    1875        2200 :                     nXLeft + nXWidth <= psTransform->poDS->GetRasterXSize() &&
    1876        5022 :                     nYTop >= 0 &&
    1877        2200 :                     nYTop + nYHeight <= psTransform->poDS->GetRasterYSize())
    1878             :                 {
    1879             :                     static bool bOnce = false;
    1880        2192 :                     if (!bOnce)
    1881             :                     {
    1882           2 :                         bOnce = true;
    1883           2 :                         CPLDebug("RPC",
    1884             :                                  "Using GDALRPCTransformWholeLineWithDEM");
    1885             :                     }
    1886        2192 :                     return GDALRPCTransformWholeLineWithDEM(
    1887             :                         psTransform, nPointCount, padfX, padfY, padfZ,
    1888        2192 :                         panSuccess, nXLeft, nXWidth, nYTop, nYHeight);
    1889             :                 }
    1890             :             }
    1891             :         }
    1892             : 
    1893      371140 :         for (int i = 0; i < nPointCount; i++)
    1894             :         {
    1895      365679 :             if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
    1896             :             {
    1897      108994 :                 panSuccess[i] = FALSE;
    1898      108994 :                 padfX[i] = HUGE_VAL;
    1899      108994 :                 padfY[i] = HUGE_VAL;
    1900      121421 :                 continue;
    1901             :             }
    1902      256685 :             double dfHeight = 0.0;
    1903      256685 :             if (!GDALRPCGetHeightAtLongLat(psTransform, padfX[i], padfY[i],
    1904             :                                            &dfHeight))
    1905             :             {
    1906       12427 :                 panSuccess[i] = FALSE;
    1907       12427 :                 padfX[i] = HUGE_VAL;
    1908       12427 :                 padfY[i] = HUGE_VAL;
    1909       12427 :                 continue;
    1910             :             }
    1911             : 
    1912      244258 :             RPCTransformPoint(psTransform, padfX[i], padfY[i],
    1913      244258 :                               (padfZ ? padfZ[i] : 0.0) + dfHeight, padfX + i,
    1914      244258 :                               padfY + i);
    1915      244258 :             panSuccess[i] = TRUE;
    1916             :         }
    1917             : 
    1918        5461 :         return TRUE;
    1919             :     }
    1920             : 
    1921        1733 :     if (padfZ == nullptr)
    1922             :     {
    1923           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1924             :                  "Z array should be provided for reverse RPC computation");
    1925           0 :         return FALSE;
    1926             :     }
    1927             : 
    1928             :     /* -------------------------------------------------------------------- */
    1929             :     /*      Compute the inverse (pixel/line/height to lat/long).  This      */
    1930             :     /*      function uses an iterative method from an initial linear        */
    1931             :     /*      approximation.                                                  */
    1932             :     /* -------------------------------------------------------------------- */
    1933       15347 :     for (int i = 0; i < nPointCount; i++)
    1934             :     {
    1935       13614 :         double dfResultX = 0.0;
    1936       13614 :         double dfResultY = 0.0;
    1937             : 
    1938       13614 :         if (!RPCInverseTransformPoint(psTransform, padfX[i], padfY[i], padfZ[i],
    1939             :                                       &dfResultX, &dfResultY))
    1940             :         {
    1941       10602 :             panSuccess[i] = FALSE;
    1942       10602 :             padfX[i] = HUGE_VAL;
    1943       10602 :             padfY[i] = HUGE_VAL;
    1944       10602 :             continue;
    1945             :         }
    1946        3012 :         if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
    1947             :         {
    1948           0 :             panSuccess[i] = FALSE;
    1949           0 :             padfX[i] = HUGE_VAL;
    1950           0 :             padfY[i] = HUGE_VAL;
    1951           0 :             continue;
    1952             :         }
    1953             : 
    1954        3012 :         padfX[i] = dfResultX;
    1955        3012 :         padfY[i] = dfResultY;
    1956             : 
    1957        3012 :         panSuccess[i] = TRUE;
    1958             :     }
    1959             : 
    1960        1733 :     return TRUE;
    1961             : }
    1962             : 
    1963             : /************************************************************************/
    1964             : /*                    GDALSerializeRPCTransformer()                     */
    1965             : /************************************************************************/
    1966             : 
    1967           1 : CPLXMLNode *GDALSerializeRPCTransformer(void *pTransformArg)
    1968             : 
    1969             : {
    1970           1 :     VALIDATE_POINTER1(pTransformArg, "GDALSerializeRPCTransformer", nullptr);
    1971             : 
    1972           1 :     GDALRPCTransformInfo *psInfo =
    1973             :         static_cast<GDALRPCTransformInfo *>(pTransformArg);
    1974             : 
    1975             :     CPLXMLNode *psTree =
    1976           1 :         CPLCreateXMLNode(nullptr, CXT_Element, "RPCTransformer");
    1977             : 
    1978             :     /* -------------------------------------------------------------------- */
    1979             :     /*      Serialize bReversed.                                            */
    1980             :     /* -------------------------------------------------------------------- */
    1981           1 :     CPLCreateXMLElementAndValue(psTree, "Reversed",
    1982           2 :                                 CPLString().Printf("%d", psInfo->bReversed));
    1983             : 
    1984             :     /* -------------------------------------------------------------------- */
    1985             :     /*      Serialize Height Offset.                                        */
    1986             :     /* -------------------------------------------------------------------- */
    1987           1 :     CPLCreateXMLElementAndValue(
    1988             :         psTree, "HeightOffset",
    1989           2 :         CPLString().Printf("%.15g", psInfo->dfHeightOffset));
    1990             : 
    1991             :     /* -------------------------------------------------------------------- */
    1992             :     /*      Serialize Height Scale.                                         */
    1993             :     /* -------------------------------------------------------------------- */
    1994           1 :     if (psInfo->dfHeightScale != 1.0)
    1995           0 :         CPLCreateXMLElementAndValue(
    1996             :             psTree, "HeightScale",
    1997           0 :             CPLString().Printf("%.15g", psInfo->dfHeightScale));
    1998             : 
    1999             :     /* -------------------------------------------------------------------- */
    2000             :     /*      Serialize DEM path.                                             */
    2001             :     /* -------------------------------------------------------------------- */
    2002           1 :     if (psInfo->pszDEMPath != nullptr)
    2003             :     {
    2004           0 :         CPLCreateXMLElementAndValue(
    2005           0 :             psTree, "DEMPath", CPLString().Printf("%s", psInfo->pszDEMPath));
    2006             : 
    2007             :         /* --------------------------------------------------------------------
    2008             :          */
    2009             :         /*      Serialize DEM interpolation */
    2010             :         /* --------------------------------------------------------------------
    2011             :          */
    2012           0 :         CPLCreateXMLElementAndValue(
    2013             :             psTree, "DEMInterpolation",
    2014             :             GDALSerializeRPCDEMResample(psInfo->eResampleAlg));
    2015             : 
    2016           0 :         if (psInfo->bHasDEMMissingValue)
    2017             :         {
    2018           0 :             CPLCreateXMLElementAndValue(
    2019             :                 psTree, "DEMMissingValue",
    2020             :                 CPLSPrintf("%.17g", psInfo->dfDEMMissingValue));
    2021             :         }
    2022             : 
    2023           0 :         CPLCreateXMLElementAndValue(psTree, "DEMApplyVDatumShift",
    2024           0 :                                     psInfo->bApplyDEMVDatumShift ? "true"
    2025             :                                                                  : "false");
    2026             : 
    2027             :         /* --------------------------------------------------------------------
    2028             :          */
    2029             :         /*      Serialize DEM SRS */
    2030             :         /* --------------------------------------------------------------------
    2031             :          */
    2032           0 :         if (psInfo->pszDEMSRS != nullptr)
    2033             :         {
    2034           0 :             CPLCreateXMLElementAndValue(psTree, "DEMSRS", psInfo->pszDEMSRS);
    2035             :         }
    2036             :     }
    2037             : 
    2038             :     /* -------------------------------------------------------------------- */
    2039             :     /*      Serialize pixel error threshold.                                */
    2040             :     /* -------------------------------------------------------------------- */
    2041           1 :     CPLCreateXMLElementAndValue(
    2042             :         psTree, "PixErrThreshold",
    2043           2 :         CPLString().Printf("%.15g", psInfo->dfPixErrThreshold));
    2044             : 
    2045             :     /* -------------------------------------------------------------------- */
    2046             :     /*      RPC metadata.                                                   */
    2047             :     /* -------------------------------------------------------------------- */
    2048           1 :     char **papszMD = RPCInfoV2ToMD(&(psInfo->sRPC));
    2049           1 :     CPLXMLNode *psMD = CPLCreateXMLNode(psTree, CXT_Element, "Metadata");
    2050             : 
    2051          21 :     for (int i = 0; papszMD != nullptr && papszMD[i] != nullptr; i++)
    2052             :     {
    2053          20 :         char *pszKey = nullptr;
    2054             : 
    2055          20 :         const char *pszRawValue = CPLParseNameValue(papszMD[i], &pszKey);
    2056             : 
    2057          20 :         CPLXMLNode *psMDI = CPLCreateXMLNode(psMD, CXT_Element, "MDI");
    2058          20 :         CPLSetXMLValue(psMDI, "#key", pszKey);
    2059          20 :         CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
    2060             : 
    2061          20 :         CPLFree(pszKey);
    2062             :     }
    2063             : 
    2064           1 :     CSLDestroy(papszMD);
    2065             : 
    2066           1 :     return psTree;
    2067             : }
    2068             : 
    2069             : /************************************************************************/
    2070             : /*                   GDALDeserializeRPCTransformer()                    */
    2071             : /************************************************************************/
    2072             : 
    2073           0 : void *GDALDeserializeRPCTransformer(CPLXMLNode *psTree)
    2074             : 
    2075             : {
    2076           0 :     char **papszOptions = nullptr;
    2077             : 
    2078             :     /* -------------------------------------------------------------------- */
    2079             :     /*      Collect metadata.                                               */
    2080             :     /* -------------------------------------------------------------------- */
    2081           0 :     CPLXMLNode *psMetadata = CPLGetXMLNode(psTree, "Metadata");
    2082             : 
    2083           0 :     if (psMetadata == nullptr || psMetadata->eType != CXT_Element ||
    2084           0 :         !EQUAL(psMetadata->pszValue, "Metadata"))
    2085           0 :         return nullptr;
    2086             : 
    2087           0 :     char **papszMD = nullptr;
    2088           0 :     for (CPLXMLNode *psMDI = psMetadata->psChild; psMDI != nullptr;
    2089           0 :          psMDI = psMDI->psNext)
    2090             :     {
    2091           0 :         if (!EQUAL(psMDI->pszValue, "MDI") || psMDI->eType != CXT_Element ||
    2092           0 :             psMDI->psChild == nullptr || psMDI->psChild->psNext == nullptr ||
    2093           0 :             psMDI->psChild->eType != CXT_Attribute ||
    2094           0 :             psMDI->psChild->psChild == nullptr)
    2095           0 :             continue;
    2096             : 
    2097           0 :         papszMD = CSLSetNameValue(papszMD, psMDI->psChild->psChild->pszValue,
    2098           0 :                                   psMDI->psChild->psNext->pszValue);
    2099             :     }
    2100             : 
    2101             :     GDALRPCInfoV2 sRPC;
    2102           0 :     if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
    2103             :     {
    2104           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2105             :                  "Failed to reconstitute RPC transformer.");
    2106           0 :         CSLDestroy(papszMD);
    2107           0 :         return nullptr;
    2108             :     }
    2109             : 
    2110           0 :     CSLDestroy(papszMD);
    2111             : 
    2112             :     /* -------------------------------------------------------------------- */
    2113             :     /*      Get other flags.                                                */
    2114             :     /* -------------------------------------------------------------------- */
    2115           0 :     const int bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
    2116             : 
    2117             :     const double dfPixErrThreshold =
    2118           0 :         CPLAtof(CPLGetXMLValue(psTree, "PixErrThreshold",
    2119             :                                CPLSPrintf("%f", DEFAULT_PIX_ERR_THRESHOLD)));
    2120             : 
    2121           0 :     papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT",
    2122             :                                    CPLGetXMLValue(psTree, "HeightOffset", "0"));
    2123           0 :     papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT_SCALE",
    2124             :                                    CPLGetXMLValue(psTree, "HeightScale", "1"));
    2125           0 :     const char *pszDEMPath = CPLGetXMLValue(psTree, "DEMPath", nullptr);
    2126           0 :     if (pszDEMPath != nullptr)
    2127           0 :         papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM", pszDEMPath);
    2128             : 
    2129             :     const char *pszDEMInterpolation =
    2130           0 :         CPLGetXMLValue(psTree, "DEMInterpolation", "bilinear");
    2131           0 :     if (pszDEMInterpolation != nullptr)
    2132           0 :         papszOptions = CSLSetNameValue(papszOptions, "RPC_DEMINTERPOLATION",
    2133             :                                        pszDEMInterpolation);
    2134             : 
    2135             :     const char *pszDEMMissingValue =
    2136           0 :         CPLGetXMLValue(psTree, "DEMMissingValue", nullptr);
    2137           0 :     if (pszDEMMissingValue != nullptr)
    2138           0 :         papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM_MISSING_VALUE",
    2139             :                                        pszDEMMissingValue);
    2140             : 
    2141             :     const char *pszDEMApplyVDatumShift =
    2142           0 :         CPLGetXMLValue(psTree, "DEMApplyVDatumShift", nullptr);
    2143           0 :     if (pszDEMApplyVDatumShift != nullptr)
    2144           0 :         papszOptions = CSLSetNameValue(
    2145             :             papszOptions, "RPC_DEM_APPLY_VDATUM_SHIFT", pszDEMApplyVDatumShift);
    2146           0 :     const char *pszDEMSRS = CPLGetXMLValue(psTree, "DEMSRS", nullptr);
    2147           0 :     if (pszDEMSRS != nullptr)
    2148           0 :         papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM_SRS", pszDEMSRS);
    2149             : 
    2150             :     /* -------------------------------------------------------------------- */
    2151             :     /*      Generate transformation.                                        */
    2152             :     /* -------------------------------------------------------------------- */
    2153           0 :     void *pResult = GDALCreateRPCTransformerV2(&sRPC, bReversed,
    2154             :                                                dfPixErrThreshold, papszOptions);
    2155             : 
    2156           0 :     CSLDestroy(papszOptions);
    2157             : 
    2158           0 :     return pResult;
    2159             : }

Generated by: LCOV version 1.14