• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.13
/alg/gdal_rpc.cpp
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

38
#ifdef USE_NEON_OPTIMIZATIONS
39
#define USE_SSE2
40
#elif defined(__x86_64) || defined(_M_X64)
41
#define USE_SSE2
42
#endif
43

44
#ifdef USE_SSE2
45
#include "gdalsse_priv.h"
46
#define USE_SSE2_OPTIM
47
#endif
48

49
#include "ogr_api.h"
50
#include "ogr_geometry.h"
51
#include "ogr_spatialref.h"
52
#include "ogr_srs_api.h"
53
#include "gdalresamplingkernels.h"
54

55
// #define DEBUG_VERBOSE_EXTRACT_DEM
56

57
CPL_C_START
58
CPLXMLNode *GDALSerializeRPCTransformer(void *pTransformArg);
59
void *GDALDeserializeRPCTransformer(CPLXMLNode *psTree);
60
CPL_C_END
61

62
constexpr int MAX_ABS_VALUE_WARNINGS = 20;
63
constexpr double DEFAULT_PIX_ERR_THRESHOLD = 0.1;
64

65
/************************************************************************/
66
/*                            RPCInfoToMD()                             */
67
/*                                                                      */
68
/*      Turn an RPCInfo structure back into its metadata format.        */
69
/************************************************************************/
70

71
char **RPCInfoV1ToMD(GDALRPCInfoV1 *psRPCInfo)
×
72

73
{
74
    GDALRPCInfoV2 sRPCInfo;
75
    memcpy(&sRPCInfo, psRPCInfo, sizeof(GDALRPCInfoV1));
×
76
    sRPCInfo.dfERR_BIAS = std::numeric_limits<double>::quiet_NaN();
×
77
    sRPCInfo.dfERR_RAND = std::numeric_limits<double>::quiet_NaN();
×
78
    return RPCInfoV2ToMD(&sRPCInfo);
×
79
}
80

81
char **RPCInfoV2ToMD(GDALRPCInfoV2 *psRPCInfo)
1✔
82

83
{
84
    char **papszMD = nullptr;
1✔
85
    CPLString osField, osMultiField;
2✔
86

87
    if (!std::isnan(psRPCInfo->dfERR_BIAS))
1✔
88
    {
89
        osField.Printf("%.15g", psRPCInfo->dfERR_BIAS);
1✔
90
        papszMD = CSLSetNameValue(papszMD, RPC_ERR_BIAS, osField);
1✔
91
    }
92

93
    if (!std::isnan(psRPCInfo->dfERR_RAND))
1✔
94
    {
95
        osField.Printf("%.15g", psRPCInfo->dfERR_RAND);
1✔
96
        papszMD = CSLSetNameValue(papszMD, RPC_ERR_RAND, osField);
1✔
97
    }
98

99
    osField.Printf("%.15g", psRPCInfo->dfLINE_OFF);
1✔
100
    papszMD = CSLSetNameValue(papszMD, RPC_LINE_OFF, osField);
1✔
101

102
    osField.Printf("%.15g", psRPCInfo->dfSAMP_OFF);
1✔
103
    papszMD = CSLSetNameValue(papszMD, RPC_SAMP_OFF, osField);
1✔
104

105
    osField.Printf("%.15g", psRPCInfo->dfLAT_OFF);
1✔
106
    papszMD = CSLSetNameValue(papszMD, RPC_LAT_OFF, osField);
1✔
107

108
    osField.Printf("%.15g", psRPCInfo->dfLONG_OFF);
1✔
109
    papszMD = CSLSetNameValue(papszMD, RPC_LONG_OFF, osField);
1✔
110

111
    osField.Printf("%.15g", psRPCInfo->dfHEIGHT_OFF);
1✔
112
    papszMD = CSLSetNameValue(papszMD, RPC_HEIGHT_OFF, osField);
1✔
113

114
    osField.Printf("%.15g", psRPCInfo->dfLINE_SCALE);
1✔
115
    papszMD = CSLSetNameValue(papszMD, RPC_LINE_SCALE, osField);
1✔
116

117
    osField.Printf("%.15g", psRPCInfo->dfSAMP_SCALE);
1✔
118
    papszMD = CSLSetNameValue(papszMD, RPC_SAMP_SCALE, osField);
1✔
119

120
    osField.Printf("%.15g", psRPCInfo->dfLAT_SCALE);
1✔
121
    papszMD = CSLSetNameValue(papszMD, RPC_LAT_SCALE, osField);
1✔
122

123
    osField.Printf("%.15g", psRPCInfo->dfLONG_SCALE);
1✔
124
    papszMD = CSLSetNameValue(papszMD, RPC_LONG_SCALE, osField);
1✔
125

126
    osField.Printf("%.15g", psRPCInfo->dfHEIGHT_SCALE);
1✔
127
    papszMD = CSLSetNameValue(papszMD, RPC_HEIGHT_SCALE, osField);
1✔
128

129
    osField.Printf("%.15g", psRPCInfo->dfMIN_LONG);
1✔
130
    papszMD = CSLSetNameValue(papszMD, RPC_MIN_LONG, osField);
1✔
131

132
    osField.Printf("%.15g", psRPCInfo->dfMIN_LAT);
1✔
133
    papszMD = CSLSetNameValue(papszMD, RPC_MIN_LAT, osField);
1✔
134

135
    osField.Printf("%.15g", psRPCInfo->dfMAX_LONG);
1✔
136
    papszMD = CSLSetNameValue(papszMD, RPC_MAX_LONG, osField);
1✔
137

138
    osField.Printf("%.15g", psRPCInfo->dfMAX_LAT);
1✔
139
    papszMD = CSLSetNameValue(papszMD, RPC_MAX_LAT, osField);
1✔
140

141
    for (int i = 0; i < 20; i++)
21✔
142
    {
143
        osField.Printf("%.15g", psRPCInfo->adfLINE_NUM_COEFF[i]);
20✔
144
        if (i > 0)
20✔
145
            osMultiField += " ";
19✔
146
        else
147
            osMultiField = "";
1✔
148
        osMultiField += osField;
20✔
149
    }
150
    papszMD = CSLSetNameValue(papszMD, "LINE_NUM_COEFF", osMultiField);
1✔
151

152
    for (int i = 0; i < 20; i++)
21✔
153
    {
154
        osField.Printf("%.15g", psRPCInfo->adfLINE_DEN_COEFF[i]);
20✔
155
        if (i > 0)
20✔
156
            osMultiField += " ";
19✔
157
        else
158
            osMultiField = "";
1✔
159
        osMultiField += osField;
20✔
160
    }
161
    papszMD = CSLSetNameValue(papszMD, "LINE_DEN_COEFF", osMultiField);
1✔
162

163
    for (int i = 0; i < 20; i++)
21✔
164
    {
165
        osField.Printf("%.15g", psRPCInfo->adfSAMP_NUM_COEFF[i]);
20✔
166
        if (i > 0)
20✔
167
            osMultiField += " ";
19✔
168
        else
169
            osMultiField = "";
1✔
170
        osMultiField += osField;
20✔
171
    }
172
    papszMD = CSLSetNameValue(papszMD, "SAMP_NUM_COEFF", osMultiField);
1✔
173

174
    for (int i = 0; i < 20; i++)
21✔
175
    {
176
        osField.Printf("%.15g", psRPCInfo->adfSAMP_DEN_COEFF[i]);
20✔
177
        if (i > 0)
20✔
178
            osMultiField += " ";
19✔
179
        else
180
            osMultiField = "";
1✔
181
        osMultiField += osField;
20✔
182
    }
183
    papszMD = CSLSetNameValue(papszMD, "SAMP_DEN_COEFF", osMultiField);
1✔
184

185
    return papszMD;
2✔
186
}
187

188
/************************************************************************/
189
/*                          RPCComputeTerms()                           */
190
/************************************************************************/
191

192
static void RPCComputeTerms(double dfLong, double dfLat, double dfHeight,
461,258✔
193
                            double *padfTerms)
194

195
{
196
    padfTerms[0] = 1.0;
461,258✔
197
    padfTerms[1] = dfLong;
461,258✔
198
    padfTerms[2] = dfLat;
461,258✔
199
    padfTerms[3] = dfHeight;
461,258✔
200
    padfTerms[4] = dfLong * dfLat;
461,258✔
201
    padfTerms[5] = dfLong * dfHeight;
461,258✔
202
    padfTerms[6] = dfLat * dfHeight;
461,258✔
203
    padfTerms[7] = dfLong * dfLong;
461,258✔
204
    padfTerms[8] = dfLat * dfLat;
461,258✔
205
    padfTerms[9] = dfHeight * dfHeight;
461,258✔
206

207
    padfTerms[10] = dfLong * dfLat * dfHeight;
461,258✔
208
    padfTerms[11] = dfLong * dfLong * dfLong;
461,258✔
209
    padfTerms[12] = dfLong * dfLat * dfLat;
461,258✔
210
    padfTerms[13] = dfLong * dfHeight * dfHeight;
461,258✔
211
    padfTerms[14] = dfLong * dfLong * dfLat;
461,258✔
212
    padfTerms[15] = dfLat * dfLat * dfLat;
461,258✔
213
    padfTerms[16] = dfLat * dfHeight * dfHeight;
461,258✔
214
    padfTerms[17] = dfLong * dfLong * dfHeight;
461,258✔
215
    padfTerms[18] = dfLat * dfLat * dfHeight;
461,258✔
216
    padfTerms[19] = dfHeight * dfHeight * dfHeight;
461,258✔
217
}
461,258✔
218

219
/************************************************************************/
220
/* ==================================================================== */
221
/*                           GDALRPCTransformer                         */
222
/* ==================================================================== */
223
/************************************************************************/
224

225
/*! DEM Resampling Algorithm */
226
typedef enum
227
{
228
    /*! Nearest neighbour (select on one input pixel) */ DRA_NearestNeighbour =
229
        0,
230
    /*! Bilinear (2x2 kernel) */ DRA_Bilinear = 1,
231
    /*! Cubic Convolution Approximation (4x4 kernel) */ DRA_CubicSpline = 2
232
} DEMResampleAlg;
233

234
typedef struct
235
{
236

237
    GDALTransformerInfo sTI;
238

239
    GDALRPCInfoV2 sRPC;
240

241
    double adfPLToLatLongGeoTransform[6];
242
    double dfRefZ;
243

244
    int bReversed;
245

246
    double dfPixErrThreshold;
247

248
    double dfHeightOffset;
249

250
    double dfHeightScale;
251

252
    char *pszDEMPath;
253

254
    DEMResampleAlg eResampleAlg;
255

256
    int bHasDEMMissingValue;
257
    double dfDEMMissingValue;
258
    char *pszDEMSRS;
259
    int bApplyDEMVDatumShift;
260

261
    GDALDataset *poDS;
262
    // the key is (nYBlock << 32) | nXBlock)
263
    lru11::Cache<uint64_t, std::shared_ptr<std::vector<double>>> *poCacheDEM;
264

265
    OGRCoordinateTransformation *poCT;
266

267
    int nMaxIterations;
268

269
    double adfDEMGeoTransform[6];
270
    double adfDEMReverseGeoTransform[6];
271

272
#ifdef USE_SSE2_OPTIM
273
    double adfDoubles[20 * 4 + 1];
274
    // LINE_NUM_COEFF, LINE_DEN_COEFF, SAMP_NUM_COEFF and then SAMP_DEN_COEFF.
275
    double *padfCoeffs;
276
#endif
277

278
    bool bRPCInverseVerbose;
279
    char *pszRPCInverseLog;
280

281
    char *pszRPCFootprint;
282
    OGRGeometry *poRPCFootprintGeom;
283
    OGRPreparedGeometry *poRPCFootprintPreparedGeom;
284

285
} GDALRPCTransformInfo;
286

287
static bool GDALRPCOpenDEM(GDALRPCTransformInfo *psTransform);
288

289
/************************************************************************/
290
/*                            RPCEvaluate()                             */
291
/************************************************************************/
292
#ifdef USE_SSE2_OPTIM
293

294
static void RPCEvaluate4(const double *padfTerms, const double *padfCoefs,
461,258✔
295
                         double &dfSum1, double &dfSum2, double &dfSum3,
296
                         double &dfSum4)
297

298
{
299
    XMMReg2Double sum1 = XMMReg2Double::Zero();
461,258✔
300
    XMMReg2Double sum2 = XMMReg2Double::Zero();
461,258✔
301
    XMMReg2Double sum3 = XMMReg2Double::Zero();
461,258✔
302
    XMMReg2Double sum4 = XMMReg2Double::Zero();
461,258✔
303
    for (int i = 0; i < 20; i += 2)
5,073,840✔
304
    {
305
        const XMMReg2Double terms =
306
            XMMReg2Double::Load2ValAligned(padfTerms + i);
4,612,580✔
307

308
        // LINE_NUM_COEFF.
309
        const XMMReg2Double coefs1 =
310
            XMMReg2Double::Load2ValAligned(padfCoefs + i);
4,612,580✔
311

312
        // LINE_DEN_COEFF.
313
        const XMMReg2Double coefs2 =
314
            XMMReg2Double::Load2ValAligned(padfCoefs + i + 20);
4,612,580✔
315

316
        // SAMP_NUM_COEFF.
317
        const XMMReg2Double coefs3 =
318
            XMMReg2Double::Load2ValAligned(padfCoefs + i + 40);
4,612,580✔
319

320
        // SAMP_DEN_COEFF.
321
        const XMMReg2Double coefs4 =
322
            XMMReg2Double::Load2ValAligned(padfCoefs + i + 60);
4,612,580✔
323

324
        sum1 += terms * coefs1;
4,612,580✔
325
        sum2 += terms * coefs2;
4,612,580✔
326
        sum3 += terms * coefs3;
4,612,580✔
327
        sum4 += terms * coefs4;
4,612,580✔
328
    }
329
    dfSum1 = sum1.GetHorizSum();
461,258✔
330
    dfSum2 = sum2.GetHorizSum();
461,258✔
331
    dfSum3 = sum3.GetHorizSum();
461,258✔
332
    dfSum4 = sum4.GetHorizSum();
461,258✔
333
}
461,258✔
334

335
#else
336

337
static double RPCEvaluate(const double *padfTerms, const double *padfCoefs)
338

339
{
340
    double dfSum1 = 0.0;
341
    double dfSum2 = 0.0;
342

343
    for (int i = 0; i < 20; i += 2)
344
    {
345
        dfSum1 += padfTerms[i] * padfCoefs[i];
346
        dfSum2 += padfTerms[i + 1] * padfCoefs[i + 1];
347
    }
348

349
    return dfSum1 + dfSum2;
350
}
351

352
#endif
353

354
/************************************************************************/
355
/*                         RPCTransformPoint()                          */
356
/************************************************************************/
357

358
static void RPCTransformPoint(const GDALRPCTransformInfo *psRPCTransformInfo,
461,258✔
359
                              double dfLong, double dfLat, double dfHeight,
360
                              double *pdfPixel, double *pdfLine)
361

362
{
363
    double adfTermsWithMargin[20 + 1] = {};
461,258✔
364
    // Make padfTerms aligned on 16-byte boundary for SSE2 aligned loads.
365
    double *padfTerms =
461,258✔
366
        adfTermsWithMargin +
461,258✔
367
        (reinterpret_cast<GUIntptr_t>(adfTermsWithMargin) % 16) / 8;
368

369
    // Avoid dateline issues.
370
    double diffLong = dfLong - psRPCTransformInfo->sRPC.dfLONG_OFF;
461,258✔
371
    if (diffLong < -270)
461,258✔
372
    {
373
        diffLong += 360;
2✔
374
    }
375
    else if (diffLong > 270)
461,256✔
376
    {
377
        diffLong -= 360;
6✔
378
    }
379

380
    const double dfNormalizedLong =
461,258✔
381
        diffLong / psRPCTransformInfo->sRPC.dfLONG_SCALE;
461,258✔
382
    const double dfNormalizedLat =
461,258✔
383
        (dfLat - psRPCTransformInfo->sRPC.dfLAT_OFF) /
461,258✔
384
        psRPCTransformInfo->sRPC.dfLAT_SCALE;
461,258✔
385
    const double dfNormalizedHeight =
461,258✔
386
        (dfHeight - psRPCTransformInfo->sRPC.dfHEIGHT_OFF) /
461,258✔
387
        psRPCTransformInfo->sRPC.dfHEIGHT_SCALE;
461,258✔
388

389
    // The absolute values of the 3 above normalized values are supposed to be
390
    // below 1. Warn (as debug message) if it is not the case. We allow for some
391
    // margin above 1 (1.5, somewhat arbitrary chosen) before warning.
392
    static int nCountWarningsAboutAboveOneNormalizedValues = 0;
393
    if (nCountWarningsAboutAboveOneNormalizedValues < MAX_ABS_VALUE_WARNINGS)
461,258✔
394
    {
395
        bool bWarned = false;
100,540✔
396
        if (fabs(dfNormalizedLong) > 1.5)
100,540✔
397
        {
398
            bWarned = true;
31✔
399
            CPLDebug(
31✔
400
                "RPC",
401
                "Normalized %s for (lon,lat,height)=(%f,%f,%f) is %f, "
402
                "i.e. with an absolute value of > 1, which may cause numeric "
403
                "stability problems",
404
                "longitude", dfLong, dfLat, dfHeight, dfNormalizedLong);
405
        }
406
        if (fabs(dfNormalizedLat) > 1.5)
100,540✔
407
        {
408
            bWarned = true;
20✔
409
            CPLDebug(
20✔
410
                "RPC",
411
                "Normalized %s for (lon,lat,height)=(%f,%f,%f) is %f, "
412
                "ie with an absolute value of > 1, which may cause numeric "
413
                "stability problems",
414
                "latitude", dfLong, dfLat, dfHeight, dfNormalizedLat);
415
        }
416
        if (fabs(dfNormalizedHeight) > 1.5)
100,540✔
417
        {
418
            bWarned = true;
39✔
419
            CPLDebug(
39✔
420
                "RPC",
421
                "Normalized %s for (lon,lat,height)=(%f,%f,%f) is %f, "
422
                "i.e. with an absolute value of > 1, which may cause numeric "
423
                "stability problems",
424
                "height", dfLong, dfLat, dfHeight, dfNormalizedHeight);
425
        }
426
        if (bWarned)
100,540✔
427
        {
428
            // Limit the number of warnings.
429
            nCountWarningsAboutAboveOneNormalizedValues++;
60✔
430
            if (nCountWarningsAboutAboveOneNormalizedValues ==
60✔
431
                MAX_ABS_VALUE_WARNINGS)
432
            {
433
                CPLDebug("RPC", "No more such debug warnings will be emitted");
3✔
434
            }
435
        }
436
    }
437

438
    RPCComputeTerms(dfNormalizedLong, dfNormalizedLat, dfNormalizedHeight,
461,258✔
439
                    padfTerms);
440

441
#ifdef USE_SSE2_OPTIM
442
    double dfSampNum = 0.0;
461,258✔
443
    double dfSampDen = 0.0;
461,258✔
444
    double dfLineNum = 0.0;
461,258✔
445
    double dfLineDen = 0.0;
461,258✔
446
    RPCEvaluate4(padfTerms, psRPCTransformInfo->padfCoeffs, dfLineNum,
461,258✔
447
                 dfLineDen, dfSampNum, dfSampDen);
448
    const double dfResultX = dfSampNum / dfSampDen;
461,258✔
449
    const double dfResultY = dfLineNum / dfLineDen;
461,258✔
450
#else
451
    const double dfResultX =
452
        RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfSAMP_NUM_COEFF) /
453
        RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfSAMP_DEN_COEFF);
454

455
    const double dfResultY =
456
        RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfLINE_NUM_COEFF) /
457
        RPCEvaluate(padfTerms, psRPCTransformInfo->sRPC.adfLINE_DEN_COEFF);
458
#endif
459

460
    // RPCs are using the center of upper left pixel = 0,0 convention
461
    // convert to top left corner = 0,0 convention used in GDAL.
462
    *pdfPixel = dfResultX * psRPCTransformInfo->sRPC.dfSAMP_SCALE +
461,258✔
463
                psRPCTransformInfo->sRPC.dfSAMP_OFF + 0.5;
461,258✔
464
    *pdfLine = dfResultY * psRPCTransformInfo->sRPC.dfLINE_SCALE +
461,258✔
465
               psRPCTransformInfo->sRPC.dfLINE_OFF + 0.5;
461,258✔
466
}
461,258✔
467

468
/************************************************************************/
469
/*                     GDALSerializeRPCDEMResample()                    */
470
/************************************************************************/
471

472
static const char *GDALSerializeRPCDEMResample(DEMResampleAlg eResampleAlg)
×
473
{
474
    switch (eResampleAlg)
×
475
    {
476
        case DRA_NearestNeighbour:
×
477
            return "near";
×
478
        case DRA_CubicSpline:
×
479
            return "cubic";
×
480
        default:
×
481
        case DRA_Bilinear:
482
            return "bilinear";
×
483
    }
484
}
485

486
/************************************************************************/
487
/*                   GDALCreateSimilarRPCTransformer()                  */
488
/************************************************************************/
489

490
static void *GDALCreateSimilarRPCTransformer(void *hTransformArg,
1✔
491
                                             double dfRatioX, double dfRatioY)
492
{
493
    VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarRPCTransformer",
1✔
494
                      nullptr);
495

496
    GDALRPCTransformInfo *psInfo =
1✔
497
        static_cast<GDALRPCTransformInfo *>(hTransformArg);
498

499
    GDALRPCInfoV2 sRPC;
500
    memcpy(&sRPC, &(psInfo->sRPC), sizeof(GDALRPCInfoV2));
1✔
501

502
    if (dfRatioX != 1.0 || dfRatioY != 1.0)
1✔
503
    {
504
        sRPC.dfLINE_OFF /= dfRatioY;
1✔
505
        sRPC.dfLINE_SCALE /= dfRatioY;
1✔
506
        sRPC.dfSAMP_OFF /= dfRatioX;
1✔
507
        sRPC.dfSAMP_SCALE /= dfRatioX;
1✔
508
    }
509

510
    char **papszOptions = nullptr;
1✔
511
    papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT",
1✔
512
                                   CPLSPrintf("%.17g", psInfo->dfHeightOffset));
513
    papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT_SCALE",
1✔
514
                                   CPLSPrintf("%.17g", psInfo->dfHeightScale));
515
    if (psInfo->pszDEMPath != nullptr)
1✔
516
    {
517
        papszOptions =
518
            CSLSetNameValue(papszOptions, "RPC_DEM", psInfo->pszDEMPath);
×
519
        papszOptions =
520
            CSLSetNameValue(papszOptions, "RPC_DEMINTERPOLATION",
×
521
                            GDALSerializeRPCDEMResample(psInfo->eResampleAlg));
522
        if (psInfo->bHasDEMMissingValue)
×
523
            papszOptions =
524
                CSLSetNameValue(papszOptions, "RPC_DEM_MISSING_VALUE",
×
525
                                CPLSPrintf("%.17g", psInfo->dfDEMMissingValue));
526
        papszOptions =
527
            CSLSetNameValue(papszOptions, "RPC_DEM_APPLY_VDATUM_SHIFT",
×
528
                            (psInfo->bApplyDEMVDatumShift) ? "TRUE" : "FALSE");
×
529
    }
530
    papszOptions = CSLSetNameValue(papszOptions, "RPC_MAX_ITERATIONS",
1✔
531
                                   CPLSPrintf("%d", psInfo->nMaxIterations));
532

533
    GDALRPCTransformInfo *psNewInfo =
534
        static_cast<GDALRPCTransformInfo *>(GDALCreateRPCTransformerV2(
1✔
535
            &sRPC, psInfo->bReversed, psInfo->dfPixErrThreshold, papszOptions));
536
    CSLDestroy(papszOptions);
1✔
537

538
    return psNewInfo;
1✔
539
}
540

541
/************************************************************************/
542
/*                      GDALRPCGetHeightAtLongLat()                     */
543
/************************************************************************/
544

545
static int GDALRPCGetDEMHeight(GDALRPCTransformInfo *psTransform,
546
                               const double dfXIn, const double dfYIn,
547
                               double *pdfDEMH);
548

549
static bool GDALRPCGetHeightAtLongLat(GDALRPCTransformInfo *psTransform,
305,227✔
550
                                      const double dfXIn, const double dfYIn,
551
                                      double *pdfHeight,
552
                                      double *pdfDEMPixel = nullptr,
553
                                      double *pdfDEMLine = nullptr)
554
{
555
    double dfVDatumShift = 0.0;
305,227✔
556
    double dfDEMH = 0.0;
305,227✔
557
    if (psTransform->poDS)
305,227✔
558
    {
559
        double dfX = 0.0;
297,367✔
560
        double dfY = 0.0;
297,367✔
561
        double dfXTemp = dfXIn;
297,367✔
562
        double dfYTemp = dfYIn;
297,367✔
563
        // Check if dem is not in WGS84 and transform points padfX[i], padfY[i].
564
        if (psTransform->poCT)
297,367✔
565
        {
566
            double dfZ = 0.0;
190,275✔
567
            if (!psTransform->poCT->Transform(1, &dfXTemp, &dfYTemp, &dfZ))
190,275✔
568
            {
569
                return false;
×
570
            }
571

572
            // We must take the opposite since poCT transforms from
573
            // WGS84 to geoid. And we are going to do the reverse:
574
            // take an elevation over the geoid and transforms it to WGS84.
575
            if (psTransform->bApplyDEMVDatumShift)
190,275✔
576
                dfVDatumShift = -dfZ;
190,275✔
577
        }
578

579
        bool bRetried = false;
297,367✔
580
    retry:
297,375✔
581
        GDALApplyGeoTransform(psTransform->adfDEMReverseGeoTransform, dfXTemp,
297,375✔
582
                              dfYTemp, &dfX, &dfY);
583
        if (pdfDEMPixel)
297,375✔
584
            *pdfDEMPixel = dfX;
43,939✔
585
        if (pdfDEMLine)
297,375✔
586
            *pdfDEMLine = dfY;
43,939✔
587

588
        if (!GDALRPCGetDEMHeight(psTransform, dfX, dfY, &dfDEMH))
297,375✔
589
        {
590
            // Try to handle the case where the DEM is in LL WGS84 and spans
591
            // over [-180,180], (or very close to it ), presumably with much
592
            // hole in the middle if using VRT, and the longitude goes beyond
593
            // that interval.
594
            if (!bRetried && psTransform->poCT == nullptr &&
44,858✔
595
                (dfXIn >= 180.0 || dfXIn <= -180.0))
16,677✔
596
            {
597
                const int nRasterXSize = psTransform->poDS->GetRasterXSize();
8✔
598
                const double dfMinDEMLong = psTransform->adfDEMGeoTransform[0];
8✔
599
                const double dfMaxDEMLong =
8✔
600
                    psTransform->adfDEMGeoTransform[0] +
8✔
601
                    nRasterXSize * psTransform->adfDEMGeoTransform[1];
8✔
602
                if (fabs(dfMinDEMLong - -180) < 0.1 &&
8✔
603
                    fabs(dfMaxDEMLong - 180) < 0.1)
8✔
604
                {
605
                    if (dfXIn >= 180)
8✔
606
                    {
607
                        dfXTemp = dfXIn - 360;
4✔
608
                        dfYTemp = dfYIn;
4✔
609
                    }
610
                    else
611
                    {
612
                        dfXTemp = dfXIn + 360;
4✔
613
                        dfYTemp = dfYIn;
4✔
614
                    }
615
                    bRetried = true;
8✔
616
                    goto retry;
8✔
617
                }
618
            }
619

620
            if (psTransform->bHasDEMMissingValue)
44,850✔
621
                dfDEMH = psTransform->dfDEMMissingValue;
11,832✔
622
            else
623
            {
624
                return false;
33,018✔
625
            }
626
        }
627
#ifdef DEBUG_VERBOSE_EXTRACT_DEM
628
        CPLDebug("RPC_DEM", "X=%f, Y=%f -> Z=%f", dfX, dfY, dfDEMH);
629
#endif
630
    }
631

632
    *pdfHeight = dfVDatumShift + (psTransform->dfHeightOffset +
272,209✔
633
                                  dfDEMH * psTransform->dfHeightScale);
272,209✔
634
    return true;
272,209✔
635
}
636

637
/************************************************************************/
638
/*                      GDALCreateRPCTransformer()                      */
639
/************************************************************************/
640

641
void *GDALCreateRPCTransformerV1(GDALRPCInfoV1 *psRPCInfo, int bReversed,
×
642
                                 double dfPixErrThreshold, char **papszOptions)
643

644
{
645
    GDALRPCInfoV2 sRPCInfo;
646
    memcpy(&sRPCInfo, psRPCInfo, sizeof(GDALRPCInfoV1));
×
647
    sRPCInfo.dfERR_BIAS = std::numeric_limits<double>::quiet_NaN();
×
648
    sRPCInfo.dfERR_RAND = std::numeric_limits<double>::quiet_NaN();
×
649
    return GDALCreateRPCTransformerV2(&sRPCInfo, bReversed, dfPixErrThreshold,
×
650
                                      papszOptions);
×
651
}
652

653
/**
654
 * Create an RPC based transformer.
655
 *
656
 * The geometric sensor model describing the physical relationship between
657
 * image coordinates and ground coordinates is known as a Rigorous Projection
658
 * Model. A Rigorous Projection Model expresses the mapping of the image space
659
 * coordinates of rows and columns (r,c) onto the object space reference
660
 * surface geodetic coordinates (long, lat, height).
661
 *
662
 * A RPC supports a generic description of the Rigorous Projection Models. The
663
 * approximation used by GDAL (RPC00) is a set of rational polynomials
664
 * expressing the normalized row and column values, (rn , cn), as a function of
665
 * normalized geodetic latitude, longitude, and height, (P, L, H), given a
666
 * set of normalized polynomial coefficients (LINE_NUM_COEF_n, LINE_DEN_COEF_n,
667
 * SAMP_NUM_COEF_n, SAMP_DEN_COEF_n). Normalized values, rather than actual
668
 * values are used in order to minimize introduction of errors during the
669
 * calculations. The transformation between row and column values (r,c), and
670
 * normalized row and column values (rn, cn), and between the geodetic
671
 * latitude, longitude, and height and normalized geodetic latitude,
672
 * longitude, and height (P, L, H), is defined by a set of normalizing
673
 * translations (offsets) and scales that ensure all values are contained in
674
 * the range -1 to +1.
675
 *
676
 * This function creates a GDALTransformFunc compatible transformer
677
 * for going between image pixel/line and long/lat/height coordinates
678
 * using RPCs.  The RPCs are provided in a GDALRPCInfo structure which is
679
 * normally read from metadata using GDALExtractRPCInfo().
680
 *
681
 * GDAL RPC Metadata has the following entries (also described in GDAL RFC 22
682
 * and the GeoTIFF RPC document http://geotiff.maptools.org/rpc_prop.html .
683
 *
684
 * <ul>
685
 * <li>ERR_BIAS: Error - Bias. The RMS bias error in meters per horizontal axis
686
 * of all points in the image (-1.0 if unknown)
687
 * <li>ERR_RAND: Error - Random. RMS random error in meters per horizontal axis
688
 * of each point in the image (-1.0 if unknown)
689
 * <li>LINE_OFF: Line Offset
690
 * <li>SAMP_OFF: Sample Offset
691
 * <li>LAT_OFF: Geodetic Latitude Offset
692
 * <li>LONG_OFF: Geodetic Longitude Offset
693
 * <li>HEIGHT_OFF: Geodetic Height Offset
694
 * <li>LINE_SCALE: Line Scale
695
 * <li>SAMP_SCALE: Sample Scale
696
 * <li>LAT_SCALE: Geodetic Latitude Scale
697
 * <li>LONG_SCALE: Geodetic Longitude Scale
698
 * <li>HEIGHT_SCALE: Geodetic Height Scale
699

700
 * <li>LINE_NUM_COEFF (1-20): Line Numerator Coefficients. Twenty coefficients
701
 * for the polynomial in the Numerator of the rn equation. (space separated)
702
 * <li>LINE_DEN_COEFF (1-20): Line Denominator Coefficients. Twenty coefficients
703
 * for the polynomial in the Denominator of the rn equation. (space separated)
704
 * <li>SAMP_NUM_COEFF (1-20): Sample Numerator Coefficients. Twenty coefficients
705
 * for the polynomial in the Numerator of the cn equation. (space separated)
706
 * <li>SAMP_DEN_COEFF (1-20): Sample Denominator Coefficients. Twenty
707
 * coefficients for the polynomial in the Denominator of the cn equation. (space
708
 * separated)
709
 * </ul>
710
 *
711
 * Some drivers (such as DIMAP) may also fill a HEIGHT_DEFAULT item that can be
712
 * used by GDALCreateGenImgProjTransformer2() to initialize the below RPC_HEIGHT
713
 * transformer option if none of RPC_HEIGHT and RPC_DEM are specified.
714
 * Otherwise, if none of RPC_HEIGHT and RPC_DEM are specified as transformer
715
 * options and if HEIGHT_DEFAULT is no available, a height of 0 will be used.
716
 *
717
 * The transformer normally maps from pixel/line/height to long/lat/height space
718
 * as a forward transformation though in RPC terms that would be considered
719
 * an inverse transformation (and is solved by iterative approximation using
720
 * long/lat/height to pixel/line transformations).  The default direction can
721
 * be reversed by passing bReversed=TRUE.
722
 *
723
 * The iterative solution of pixel/line
724
 * to lat/long/height is currently run for up to 10 iterations or until
725
 * the apparent error is less than dfPixErrThreshold pixels.  Passing zero
726
 * will not avoid all error, but will cause the operation to run for the maximum
727
 * number of iterations.
728
 *
729
 * Starting with GDAL 2.1, debugging of the RPC inverse transformer can be done
730
 * by setting the RPC_INVERSE_VERBOSE configuration option to YES (in which case
731
 * extra debug information will be displayed in the "RPC" debug category, so
732
 * requiring CPL_DEBUG to be also set) and/or by setting RPC_INVERSE_LOG to a
733
 * filename that will contain the content of iterations (this last option only
734
 * makes sense when debugging point by point, since each time
735
 * RPCInverseTransformPoint() is called, the file is rewritten).
736
 *
737
 * Additional options to the transformer can be supplied in papszOptions.
738
 *
739
 * Options:
740
 *
741
 * <ul>
742
 * <li> RPC_HEIGHT: a fixed height offset to be applied to all points passed
743
 * in.  In this situation the Z passed into the transformation function is
744
 * assumed to be height above ground, and the RPC_HEIGHT is assumed to be
745
 * an average height above sea level for ground in the target scene.</li>
746
 *
747
 * <li> RPC_HEIGHT_SCALE: a factor used to multiply heights above ground.
748
 * Useful when elevation offsets of the DEM are not expressed in meters.</li>
749
 *
750
 * <li> RPC_DEM: the name of a GDAL dataset (a DEM file typically) used to
751
 * extract elevation offsets from. In this situation the Z passed into the
752
 * transformation function is assumed to be height above ground. This option
753
 * should be used in replacement of RPC_HEIGHT to provide a way of defining
754
 * a non uniform ground for the target scene</li>
755
 *
756
 * <li> RPC_DEMINTERPOLATION: the DEM interpolation ("near", "bilinear" or
757
 "cubic").
758
 *      Default is "bilinear".</li>
759
 *
760
 * <li> RPC_DEM_MISSING_VALUE: value of DEM height that must be used in case
761
 * the DEM has nodata value at the sampling point, or if its extent does not
762
 * cover the requested coordinate. When not specified, missing values will cause
763
 * a failed transform.</li>
764
 *
765
 * <li> RPC_DEM_SRS: (GDAL >= 3.2) WKT SRS, or any string recognized by
766
 * OGRSpatialReference::SetFromUserInput(), to be used as an override for DEM
767
 SRS.
768
 * Useful if DEM SRS does not have an explicit vertical component. </li>
769
 *
770
 * <li> RPC_DEM_APPLY_VDATUM_SHIFT: whether the vertical component of a compound
771
 * SRS for the DEM should be used (when it is present). This is useful so as to
772
 * be able to transform the "raw" values from the DEM expressed with respect to
773
 * a geoid to the heights with respect to the WGS84 ellipsoid. When this is
774
 * enabled, the GTIFF_REPORT_COMPD_CS configuration option will be also set
775
 * temporarily so as to get the vertical information from GeoTIFF
776
 * files. Defaults to TRUE. (GDAL >= 2.1.0)</li>
777
 *
778
 * <li> RPC_PIXEL_ERROR_THRESHOLD: overrides the dfPixErrThreshold parameter, ie
779
  the error (measured in pixels) allowed in the
780
 * iterative solution of pixel/line to lat/long computations (the other way
781
 * is always exact given the equations).  (GDAL >= 2.1.0)</li>
782
 *
783
 * <li> RPC_MAX_ITERATIONS: maximum number of iterations allowed in the
784
 * iterative solution of pixel/line to lat/long computations. Default value is
785
 * 10 in the absence of a DEM, or 20 if there is a DEM.  (GDAL >= 2.1.0)</li>
786
 *
787
 * <li> RPC_FOOTPRINT: WKT or GeoJSON polygon (in long / lat coordinate space)
788
 * with a validity footprint for the RPC. Any coordinate transformation that
789
 * goes from or arrive outside this footprint will be considered invalid. This
790
 * is useful in situations where the RPC values become highly unstable outside
791
 * of the area on which they have been computed for, potentially leading to
792
 * undesirable "echoes" / false positives. This requires GDAL to be built
793
 against
794
 * GEOS.</li>
795
 *
796
 * </ul>
797
 *
798
 * @param psRPCInfo Definition of the RPC parameters.
799
 *
800
 * @param bReversed If true "forward" transformation will be lat/long to
801
 * pixel/line instead of the normal pixel/line to lat/long.
802
 *
803
 * @param dfPixErrThreshold the error (measured in pixels) allowed in the
804
 * iterative solution of pixel/line to lat/long computations (the other way
805
 * is always exact given the equations). Starting with GDAL 2.1, this may also
806
 * be set through the RPC_PIXEL_ERROR_THRESHOLD transformer option.
807
 * If a negative or null value is provided, then this defaults to 0.1 pixel.
808
 *
809
 * @param papszOptions Other transformer options (i.e. RPC_HEIGHT=z).
810
 *
811
 * @return transformer callback data (deallocate with GDALDestroyTransformer()).
812
 */
813

814
void *GDALCreateRPCTransformerV2(const GDALRPCInfoV2 *psRPCInfo, int bReversed,
47✔
815
                                 double dfPixErrThreshold,
816
                                 CSLConstList papszOptions)
817

818
{
819
    /* -------------------------------------------------------------------- */
820
    /*      Initialize core info.                                           */
821
    /* -------------------------------------------------------------------- */
822
    GDALRPCTransformInfo *psTransform = static_cast<GDALRPCTransformInfo *>(
823
        CPLCalloc(sizeof(GDALRPCTransformInfo), 1));
47✔
824

825
    memcpy(&(psTransform->sRPC), psRPCInfo, sizeof(GDALRPCInfoV2));
47✔
826
    psTransform->bReversed = bReversed;
47✔
827
    const char *pszPixErrThreshold =
828
        CSLFetchNameValue(papszOptions, "RPC_PIXEL_ERROR_THRESHOLD");
47✔
829
    if (pszPixErrThreshold != nullptr)
47✔
830
        psTransform->dfPixErrThreshold = CPLAtof(pszPixErrThreshold);
1✔
831
    else if (dfPixErrThreshold > 0)
46✔
832
        psTransform->dfPixErrThreshold = dfPixErrThreshold;
1✔
833
    else
834
        psTransform->dfPixErrThreshold = DEFAULT_PIX_ERR_THRESHOLD;
45✔
835
    psTransform->dfHeightOffset = 0.0;
47✔
836
    psTransform->dfHeightScale = 1.0;
47✔
837

838
    memcpy(psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE,
47✔
839
           strlen(GDAL_GTI2_SIGNATURE));
840
    psTransform->sTI.pszClassName = GDAL_RPC_TRANSFORMER_CLASS_NAME;
47✔
841
    psTransform->sTI.pfnTransform = GDALRPCTransform;
47✔
842
    psTransform->sTI.pfnCleanup = GDALDestroyRPCTransformer;
47✔
843
    psTransform->sTI.pfnSerialize = GDALSerializeRPCTransformer;
47✔
844
    psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarRPCTransformer;
47✔
845

846
#ifdef USE_SSE2_OPTIM
847
    // Make sure padfCoeffs is aligned on a 16-byte boundary for SSE2 aligned
848
    // loads.
849
    psTransform->padfCoeffs =
47✔
850
        psTransform->adfDoubles +
47✔
851
        (reinterpret_cast<GUIntptr_t>(psTransform->adfDoubles) % 16) / 8;
47✔
852
    memcpy(psTransform->padfCoeffs, psRPCInfo->adfLINE_NUM_COEFF,
47✔
853
           20 * sizeof(double));
854
    memcpy(psTransform->padfCoeffs + 20, psRPCInfo->adfLINE_DEN_COEFF,
47✔
855
           20 * sizeof(double));
856
    memcpy(psTransform->padfCoeffs + 40, psRPCInfo->adfSAMP_NUM_COEFF,
47✔
857
           20 * sizeof(double));
858
    memcpy(psTransform->padfCoeffs + 60, psRPCInfo->adfSAMP_DEN_COEFF,
47✔
859
           20 * sizeof(double));
860
#endif
861

862
    /* -------------------------------------------------------------------- */
863
    /*      Do we have a "average height" that we want to consider all      */
864
    /*      elevations to be relative to?                                   */
865
    /* -------------------------------------------------------------------- */
866
    const char *pszHeight = CSLFetchNameValue(papszOptions, "RPC_HEIGHT");
47✔
867
    if (pszHeight != nullptr)
47✔
868
        psTransform->dfHeightOffset = CPLAtof(pszHeight);
7✔
869

870
    /* -------------------------------------------------------------------- */
871
    /*                       The "height scale"                             */
872
    /* -------------------------------------------------------------------- */
873
    const char *pszHeightScale =
874
        CSLFetchNameValue(papszOptions, "RPC_HEIGHT_SCALE");
47✔
875
    if (pszHeightScale != nullptr)
47✔
876
        psTransform->dfHeightScale = CPLAtof(pszHeightScale);
7✔
877

878
    /* -------------------------------------------------------------------- */
879
    /*                       The DEM file name                              */
880
    /* -------------------------------------------------------------------- */
881
    const char *pszDEMPath = CSLFetchNameValue(papszOptions, "RPC_DEM");
47✔
882
    if (pszDEMPath != nullptr)
47✔
883
    {
884
        psTransform->pszDEMPath = CPLStrdup(pszDEMPath);
33✔
885
    }
886

887
    /* -------------------------------------------------------------------- */
888
    /*                      The DEM interpolation                           */
889
    /* -------------------------------------------------------------------- */
890
    const char *pszDEMInterpolation =
891
        CSLFetchNameValueDef(papszOptions, "RPC_DEMINTERPOLATION", "bilinear");
47✔
892
    if (EQUAL(pszDEMInterpolation, "near"))
47✔
893
    {
894
        psTransform->eResampleAlg = DRA_NearestNeighbour;
3✔
895
    }
896
    else if (EQUAL(pszDEMInterpolation, "bilinear"))
44✔
897
    {
898
        psTransform->eResampleAlg = DRA_Bilinear;
41✔
899
    }
900
    else if (EQUAL(pszDEMInterpolation, "cubic"))
3✔
901
    {
902
        psTransform->eResampleAlg = DRA_CubicSpline;
3✔
903
    }
904
    else
905
    {
906
        CPLDebug("RPC", "Unknown interpolation %s. Defaulting to bilinear",
×
907
                 pszDEMInterpolation);
908
        psTransform->eResampleAlg = DRA_Bilinear;
×
909
    }
910

911
    /* -------------------------------------------------------------------- */
912
    /*                       The DEM missing value                          */
913
    /* -------------------------------------------------------------------- */
914
    const char *pszDEMMissingValue =
915
        CSLFetchNameValue(papszOptions, "RPC_DEM_MISSING_VALUE");
47✔
916
    if (pszDEMMissingValue != nullptr)
47✔
917
    {
918
        psTransform->bHasDEMMissingValue = TRUE;
6✔
919
        psTransform->dfDEMMissingValue = CPLAtof(pszDEMMissingValue);
6✔
920
    }
921

922
    /* -------------------------------------------------------------------- */
923
    /*                        The DEM SRS override                          */
924
    /* -------------------------------------------------------------------- */
925
    const char *pszDEMSRS = CSLFetchNameValue(papszOptions, "RPC_DEM_SRS");
47✔
926
    if (pszDEMSRS != nullptr)
47✔
927
    {
928
        psTransform->pszDEMSRS = CPLStrdup(pszDEMSRS);
1✔
929
    }
930

931
    /* -------------------------------------------------------------------- */
932
    /*      Whether to apply vdatum shift                                   */
933
    /* -------------------------------------------------------------------- */
934
    psTransform->bApplyDEMVDatumShift =
47✔
935
        CPLFetchBool(papszOptions, "RPC_DEM_APPLY_VDATUM_SHIFT", true);
47✔
936

937
    psTransform->nMaxIterations =
47✔
938
        atoi(CSLFetchNameValueDef(papszOptions, "RPC_MAX_ITERATIONS", "0"));
47✔
939

940
    /* -------------------------------------------------------------------- */
941
    /*      Debug                                                           */
942
    /* -------------------------------------------------------------------- */
943
    psTransform->bRPCInverseVerbose =
47✔
944
        CPLTestBool(CPLGetConfigOption("RPC_INVERSE_VERBOSE", "NO"));
47✔
945
    const char *pszRPCInverseLog =
946
        CPLGetConfigOption("RPC_INVERSE_LOG", nullptr);
47✔
947
    if (pszRPCInverseLog != nullptr)
47✔
948
        psTransform->pszRPCInverseLog = CPLStrdup(pszRPCInverseLog);
1✔
949

950
    /* -------------------------------------------------------------------- */
951
    /*      Footprint                                                       */
952
    /* -------------------------------------------------------------------- */
953
    const char *pszFootprint = CSLFetchNameValue(papszOptions, "RPC_FOOTPRINT");
47✔
954
    if (pszFootprint != nullptr)
47✔
955
    {
956
        psTransform->pszRPCFootprint = CPLStrdup(pszFootprint);
1✔
957
        if (pszFootprint[0] == '{')
1✔
958
        {
959
            psTransform->poRPCFootprintGeom =
×
960
                OGRGeometryFactory::createFromGeoJson(pszFootprint);
×
961
        }
962
        else
963
        {
964
            OGRGeometryFactory::createFromWkt(
1✔
965
                pszFootprint, nullptr, &(psTransform->poRPCFootprintGeom));
966
        }
967
        if (psTransform->poRPCFootprintGeom)
1✔
968
        {
969
            if (OGRHasPreparedGeometrySupport())
1✔
970
            {
971
                psTransform->poRPCFootprintPreparedGeom =
1✔
972
                    OGRCreatePreparedGeometry(
1✔
973
                        OGRGeometry::ToHandle(psTransform->poRPCFootprintGeom));
974
            }
975
            else
976
            {
977
                CPLError(CE_Warning, CPLE_AppDefined,
×
978
                         "GEOS not available. RPC_FOOTPRINT will be ignored");
979
            }
980
        }
981
    }
982

983
    /* -------------------------------------------------------------------- */
984
    /*      Open DEM if needed.                                             */
985
    /* -------------------------------------------------------------------- */
986

987
    if (psTransform->pszDEMPath != nullptr && !GDALRPCOpenDEM(psTransform))
47✔
988
    {
989
        GDALDestroyRPCTransformer(psTransform);
1✔
990
        return nullptr;
1✔
991
    }
992

993
    /* -------------------------------------------------------------------- */
994
    /*      Establish a reference point for calcualating an affine          */
995
    /*      geotransform approximate transformation.                        */
996
    /* -------------------------------------------------------------------- */
997
    double adfGTFromLL[6] = {};
46✔
998
    double dfRefPixel = -1.0;
46✔
999
    double dfRefLine = -1.0;
46✔
1000
    double dfRefLong = 0.0;
46✔
1001
    double dfRefLat = 0.0;
46✔
1002

1003
    if (psRPCInfo->dfMIN_LONG != -180 || psRPCInfo->dfMAX_LONG != 180)
46✔
1004
    {
1005
        dfRefLong = (psRPCInfo->dfMIN_LONG + psRPCInfo->dfMAX_LONG) * 0.5;
1✔
1006
        dfRefLat = (psRPCInfo->dfMIN_LAT + psRPCInfo->dfMAX_LAT) * 0.5;
1✔
1007

1008
        double dfX = dfRefLong;
1✔
1009
        double dfY = dfRefLat;
1✔
1010
        double dfZ = 0.0;
1✔
1011
        int nSuccess = 0;
1✔
1012
        // Try with DEM first.
1013
        if (GDALRPCTransform(psTransform, !(psTransform->bReversed), 1, &dfX,
1✔
1014
                             &dfY, &dfZ, &nSuccess) &&
2✔
1015
            nSuccess)
1✔
1016
        {
1017
            dfRefPixel = dfX;
1✔
1018
            dfRefLine = dfY;
1✔
1019
        }
1020
        else
1021
        {
1022
            RPCTransformPoint(psTransform, dfRefLong, dfRefLat, 0.0,
×
1023
                              &dfRefPixel, &dfRefLine);
1024
        }
1025
    }
1026

1027
    // Try with scale and offset if we don't can't use bounds or
1028
    // the results seem daft.
1029
    if (dfRefPixel < 0.0 || dfRefLine < 0.0 || dfRefPixel > 100000 ||
46✔
1030
        dfRefLine > 100000)
1✔
1031
    {
1032
        dfRefLong = psRPCInfo->dfLONG_OFF;
45✔
1033
        dfRefLat = psRPCInfo->dfLAT_OFF;
45✔
1034

1035
        double dfX = dfRefLong;
45✔
1036
        double dfY = dfRefLat;
45✔
1037
        double dfZ = 0.0;
45✔
1038
        int nSuccess = 0;
45✔
1039
        // Try with DEM first.
1040
        if (GDALRPCTransform(psTransform, !(psTransform->bReversed), 1, &dfX,
45✔
1041
                             &dfY, &dfZ, &nSuccess) &&
74✔
1042
            nSuccess)
29✔
1043
        {
1044
            dfRefPixel = dfX;
29✔
1045
            dfRefLine = dfY;
29✔
1046
        }
1047
        else
1048
        {
1049
            RPCTransformPoint(psTransform, dfRefLong, dfRefLat, 0.0,
16✔
1050
                              &dfRefPixel, &dfRefLine);
1051
        }
1052
    }
1053

1054
    psTransform->dfRefZ = 0.0;
46✔
1055
    GDALRPCGetHeightAtLongLat(psTransform, dfRefLong, dfRefLat,
46✔
1056
                              &psTransform->dfRefZ);
1057

1058
    /* -------------------------------------------------------------------- */
1059
    /*      Transform nearby locations to establish affine direction        */
1060
    /*      vectors.                                                        */
1061
    /* -------------------------------------------------------------------- */
1062
    double dfRefPixelDelta = 0.0;
46✔
1063
    double dfRefLineDelta = 0.0;
46✔
1064
    double dfLLDelta = 0.0001;
46✔
1065

1066
    RPCTransformPoint(psTransform, dfRefLong + dfLLDelta, dfRefLat,
46✔
1067
                      psTransform->dfRefZ, &dfRefPixelDelta, &dfRefLineDelta);
1068
    adfGTFromLL[1] = (dfRefPixelDelta - dfRefPixel) / dfLLDelta;
46✔
1069
    adfGTFromLL[4] = (dfRefLineDelta - dfRefLine) / dfLLDelta;
46✔
1070

1071
    RPCTransformPoint(psTransform, dfRefLong, dfRefLat + dfLLDelta,
46✔
1072
                      psTransform->dfRefZ, &dfRefPixelDelta, &dfRefLineDelta);
1073
    adfGTFromLL[2] = (dfRefPixelDelta - dfRefPixel) / dfLLDelta;
46✔
1074
    adfGTFromLL[5] = (dfRefLineDelta - dfRefLine) / dfLLDelta;
46✔
1075

1076
    adfGTFromLL[0] =
46✔
1077
        dfRefPixel - adfGTFromLL[1] * dfRefLong - adfGTFromLL[2] * dfRefLat;
46✔
1078
    adfGTFromLL[3] =
46✔
1079
        dfRefLine - adfGTFromLL[4] * dfRefLong - adfGTFromLL[5] * dfRefLat;
46✔
1080

1081
    if (!GDALInvGeoTransform(adfGTFromLL,
46✔
1082
                             psTransform->adfPLToLatLongGeoTransform))
46✔
1083
    {
1084
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
×
1085
        GDALDestroyRPCTransformer(psTransform);
×
1086
        return nullptr;
×
1087
    }
1088

1089
    return psTransform;
46✔
1090
}
1091

1092
/************************************************************************/
1093
/*                 GDALDestroyReprojectionTransformer()                 */
1094
/************************************************************************/
1095

1096
/** Destroy RPC transformer */
1097
void GDALDestroyRPCTransformer(void *pTransformAlg)
47✔
1098

1099
{
1100
    if (pTransformAlg == nullptr)
47✔
1101
        return;
×
1102

1103
    GDALRPCTransformInfo *psTransform =
47✔
1104
        static_cast<GDALRPCTransformInfo *>(pTransformAlg);
1105

1106
    CPLFree(psTransform->pszDEMPath);
47✔
1107
    CPLFree(psTransform->pszDEMSRS);
47✔
1108

1109
    if (psTransform->poDS)
47✔
1110
        GDALClose(psTransform->poDS);
32✔
1111
    delete psTransform->poCacheDEM;
47✔
1112
    if (psTransform->poCT)
47✔
1113
        OCTDestroyCoordinateTransformation(
11✔
1114
            reinterpret_cast<OGRCoordinateTransformationH>(psTransform->poCT));
11✔
1115
    CPLFree(psTransform->pszRPCInverseLog);
47✔
1116

1117
    CPLFree(psTransform->pszRPCFootprint);
47✔
1118
    delete psTransform->poRPCFootprintGeom;
47✔
1119
    OGRDestroyPreparedGeometry(psTransform->poRPCFootprintPreparedGeom);
47✔
1120

1121
    CPLFree(pTransformAlg);
47✔
1122
}
1123

1124
/************************************************************************/
1125
/*                      RPCInverseTransformPoint()                      */
1126
/************************************************************************/
1127

1128
static bool RPCInverseTransformPoint(GDALRPCTransformInfo *psTransform,
13,978✔
1129
                                     double dfPixel, double dfLine,
1130
                                     double dfUserHeight, double *pdfLong,
1131
                                     double *pdfLat)
1132

1133
{
1134
    // Memo:
1135
    // Known to work with 40 iterations with DEM on all points (int coord and
1136
    // +0.5,+0.5 shift) of flock1.20160216_041050_0905.tif, especially on (0,0).
1137

1138
    /* -------------------------------------------------------------------- */
1139
    /*      Compute an initial approximation based on linear                */
1140
    /*      interpolation from our reference point.                         */
1141
    /* -------------------------------------------------------------------- */
1142
    double dfResultX = psTransform->adfPLToLatLongGeoTransform[0] +
13,978✔
1143
                       psTransform->adfPLToLatLongGeoTransform[1] * dfPixel +
13,978✔
1144
                       psTransform->adfPLToLatLongGeoTransform[2] * dfLine;
13,978✔
1145

1146
    double dfResultY = psTransform->adfPLToLatLongGeoTransform[3] +
13,978✔
1147
                       psTransform->adfPLToLatLongGeoTransform[4] * dfPixel +
13,978✔
1148
                       psTransform->adfPLToLatLongGeoTransform[5] * dfLine;
13,978✔
1149

1150
    if (psTransform->bRPCInverseVerbose)
13,978✔
1151
    {
1152
        CPLDebug("RPC", "Computing inverse transform for (pixel,line)=(%f,%f)",
1✔
1153
                 dfPixel, dfLine);
1154
    }
1155
    VSILFILE *fpLog = nullptr;
13,978✔
1156
    if (psTransform->pszRPCInverseLog)
13,978✔
1157
    {
1158
        fpLog = VSIFOpenL(
1✔
1159
            CPLResetExtensionSafe(psTransform->pszRPCInverseLog, "csvt")
2✔
1160
                .c_str(),
1161
            "wb");
1162
        if (fpLog != nullptr)
1✔
1163
        {
1164
            VSIFPrintfL(fpLog, "Integer,Real,Real,Real,String,Real,Real\n");
1✔
1165
            VSIFCloseL(fpLog);
1✔
1166
        }
1167
        fpLog = VSIFOpenL(psTransform->pszRPCInverseLog, "wb");
1✔
1168
        if (fpLog != nullptr)
1✔
1169
            VSIFPrintfL(
1✔
1170
                fpLog,
1171
                "iter,long,lat,height,WKT,error_pixel_x,error_pixel_y\n");
1172
    }
1173

1174
    /* -------------------------------------------------------------------- */
1175
    /*      Now iterate, trying to find a closer LL location that will      */
1176
    /*      back transform to the indicated pixel and line.                 */
1177
    /* -------------------------------------------------------------------- */
1178
    double dfPixelDeltaX = 0.0;
13,978✔
1179
    double dfPixelDeltaY = 0.0;
13,978✔
1180
    double dfLastResultX = 0.0;
13,978✔
1181
    double dfLastResultY = 0.0;
13,978✔
1182
    double dfLastPixelDeltaX = 0.0;
13,978✔
1183
    double dfLastPixelDeltaY = 0.0;
13,978✔
1184
    bool bLastPixelDeltaValid = false;
13,978✔
1185
    const int nMaxIterations = (psTransform->nMaxIterations > 0)
27,956✔
1186
                                   ? psTransform->nMaxIterations
26,723✔
1187
                               : (psTransform->poDS != nullptr) ? 20
12,745✔
1188
                                                                : 10;
1189
    int nCountConsecutiveErrorBelow2 = 0;
13,978✔
1190

1191
    int iIter = 0;  // Used after for.
13,978✔
1192
    for (; iIter < nMaxIterations; iIter++)
48,662✔
1193
    {
1194
        double dfBackPixel = 0.0;
48,132✔
1195
        double dfBackLine = 0.0;
48,132✔
1196

1197
        // Update DEMH.
1198
        double dfDEMH = 0.0;
48,132✔
1199
        double dfDEMPixel = 0.0;
48,132✔
1200
        double dfDEMLine = 0.0;
48,132✔
1201
        if (!GDALRPCGetHeightAtLongLat(psTransform, dfResultX, dfResultY,
48,132✔
1202
                                       &dfDEMH, &dfDEMPixel, &dfDEMLine))
1203
        {
1204
            if (psTransform->poDS)
20,576✔
1205
            {
1206
                CPLDebug("RPC", "DEM (pixel, line) = (%g, %g)", dfDEMPixel,
20,576✔
1207
                         dfDEMLine);
1208
            }
1209

1210
            // The first time, the guess might be completely out of the
1211
            // validity of the DEM, so pickup the "reference Z" as the
1212
            // first guess or the closest point of the DEM by snapping to it.
1213
            if (iIter == 0)
20,576✔
1214
            {
1215
                bool bUseRefZ = true;
10,383✔
1216
                if (psTransform->poDS)
10,383✔
1217
                {
1218
                    if (dfDEMPixel >= psTransform->poDS->GetRasterXSize())
10,383✔
1219
                        dfDEMPixel = psTransform->poDS->GetRasterXSize() - 0.5;
4,368✔
1220
                    else if (dfDEMPixel < 0)
6,015✔
1221
                        dfDEMPixel = 0.5;
1,385✔
1222
                    if (dfDEMLine >= psTransform->poDS->GetRasterYSize())
10,383✔
1223
                        dfDEMLine = psTransform->poDS->GetRasterYSize() - 0.5;
21✔
1224
                    else if (dfDEMPixel < 0)
10,362✔
1225
                        dfDEMPixel = 0.5;
×
1226
                    if (GDALRPCGetDEMHeight(psTransform, dfDEMPixel, dfDEMLine,
10,383✔
1227
                                            &dfDEMH))
10,383✔
1228
                    {
1229
                        bUseRefZ = false;
1,931✔
1230
                        CPLDebug("RPC",
1,931✔
1231
                                 "Iteration %d for (pixel, line) = (%g, %g): "
1232
                                 "No elevation value at %.15g %.15g. "
1233
                                 "Using elevation %g at DEM (pixel, line) = "
1234
                                 "(%g, %g) (snapping to boundaries) instead",
1235
                                 iIter, dfPixel, dfLine, dfResultX, dfResultY,
1236
                                 dfDEMH, dfDEMPixel, dfDEMLine);
1237
                    }
1238
                }
1239
                if (bUseRefZ)
10,383✔
1240
                {
1241
                    dfDEMH = psTransform->dfRefZ;
8,452✔
1242
                    CPLDebug("RPC",
8,452✔
1243
                             "Iteration %d for (pixel, line) = (%g, %g): "
1244
                             "No elevation value at %.15g %.15g. "
1245
                             "Using elevation %g of reference point instead",
1246
                             iIter, dfPixel, dfLine, dfResultX, dfResultY,
1247
                             dfDEMH);
1248
                }
1249
            }
1250
            else
1251
            {
1252
                CPLDebug("RPC",
10,193✔
1253
                         "Iteration %d for (pixel, line) = (%g, %g): "
1254
                         "No elevation value at %.15g %.15g. Erroring out",
1255
                         iIter, dfPixel, dfLine, dfResultX, dfResultY);
1256
                if (fpLog)
10,193✔
1257
                    VSIFCloseL(fpLog);
×
1258
                return false;
10,193✔
1259
            }
1260
        }
1261

1262
        RPCTransformPoint(psTransform, dfResultX, dfResultY,
37,939✔
1263
                          dfUserHeight + dfDEMH, &dfBackPixel, &dfBackLine);
1264

1265
        dfPixelDeltaX = dfBackPixel - dfPixel;
37,939✔
1266
        dfPixelDeltaY = dfBackLine - dfLine;
37,939✔
1267

1268
        if (psTransform->bRPCInverseVerbose)
37,939✔
1269
        {
1270
            CPLDebug("RPC",
8✔
1271
                     "Iter %d: dfPixelDeltaX=%.02f, dfPixelDeltaY=%.02f, "
1272
                     "long=%f, lat=%f, height=%f",
1273
                     iIter, dfPixelDeltaX, dfPixelDeltaY, dfResultX, dfResultY,
1274
                     dfUserHeight + dfDEMH);
1275
        }
1276
        if (fpLog != nullptr)
37,939✔
1277
        {
1278
            VSIFPrintfL(fpLog,
8✔
1279
                        "%d,%.12f,%.12f,%f,\"POINT(%.12f %.12f)\",%f,%f\n",
1280
                        iIter, dfResultX, dfResultY, dfUserHeight + dfDEMH,
1281
                        dfResultX, dfResultY, dfPixelDeltaX, dfPixelDeltaY);
1282
        }
1283

1284
        const double dfError =
1285
            std::max(std::abs(dfPixelDeltaX), std::abs(dfPixelDeltaY));
37,939✔
1286
        if (dfError < psTransform->dfPixErrThreshold)
37,939✔
1287
        {
1288
            iIter = -1;
3,255✔
1289
            if (psTransform->bRPCInverseVerbose)
3,255✔
1290
            {
1291
                CPLDebug("RPC", "Converged!");
1✔
1292
            }
1293
            break;
3,255✔
1294
        }
1295
        else if (psTransform->poDS != nullptr && bLastPixelDeltaValid &&
34,684✔
1296
                 dfPixelDeltaX * dfLastPixelDeltaX < 0 &&
21,074✔
1297
                 dfPixelDeltaY * dfLastPixelDeltaY < 0)
108✔
1298
        {
1299
            // When there is a DEM, if the error changes sign, we might
1300
            // oscillate forever, so take a mean position as a new guess.
1301
            if (psTransform->bRPCInverseVerbose)
40✔
1302
            {
1303
                CPLDebug("RPC",
1✔
1304
                         "Oscillation detected. "
1305
                         "Taking mean of 2 previous results as new guess");
1306
            }
1307
            dfResultX = (fabs(dfPixelDeltaX) * dfLastResultX +
40✔
1308
                         fabs(dfLastPixelDeltaX) * dfResultX) /
40✔
1309
                        (fabs(dfPixelDeltaX) + fabs(dfLastPixelDeltaX));
40✔
1310
            dfResultY = (fabs(dfPixelDeltaY) * dfLastResultY +
40✔
1311
                         fabs(dfLastPixelDeltaY) * dfResultY) /
40✔
1312
                        (fabs(dfPixelDeltaY) + fabs(dfLastPixelDeltaY));
40✔
1313
            bLastPixelDeltaValid = false;
40✔
1314
            nCountConsecutiveErrorBelow2 = 0;
40✔
1315
            continue;
40✔
1316
        }
1317

1318
        double dfBoostFactor = 1.0;
34,644✔
1319
        if (psTransform->poDS != nullptr && nCountConsecutiveErrorBelow2 >= 5 &&
34,644✔
1320
            dfError < 2)
1321
        {
1322
            // When there is a DEM, if we remain below a given threshold
1323
            // (somewhat arbitrarily set to 2 pixels) for some time, apply a
1324
            // "boost factor" for the new guessed result, in the hope we will go
1325
            // out of the somewhat current stuck situation.
1326
            dfBoostFactor = 10;
13✔
1327
            if (psTransform->bRPCInverseVerbose)
13✔
1328
            {
1329
                CPLDebug("RPC", "Applying boost factor 10");
1✔
1330
            }
1331
        }
1332

1333
        if (dfError < 2)
34,644✔
1334
            nCountConsecutiveErrorBelow2++;
1,422✔
1335
        else
1336
            nCountConsecutiveErrorBelow2 = 0;
33,222✔
1337

1338
        const double dfNewResultX =
34,644✔
1339
            dfResultX -
34,644✔
1340
            (dfPixelDeltaX * psTransform->adfPLToLatLongGeoTransform[1] *
34,644✔
1341
             dfBoostFactor) -
1342
            (dfPixelDeltaY * psTransform->adfPLToLatLongGeoTransform[2] *
34,644✔
1343
             dfBoostFactor);
1344
        const double dfNewResultY =
34,644✔
1345
            dfResultY -
34,644✔
1346
            (dfPixelDeltaX * psTransform->adfPLToLatLongGeoTransform[4] *
34,644✔
1347
             dfBoostFactor) -
1348
            (dfPixelDeltaY * psTransform->adfPLToLatLongGeoTransform[5] *
34,644✔
1349
             dfBoostFactor);
1350

1351
        dfLastResultX = dfResultX;
34,644✔
1352
        dfLastResultY = dfResultY;
34,644✔
1353
        dfResultX = dfNewResultX;
34,644✔
1354
        dfResultY = dfNewResultY;
34,644✔
1355
        dfLastPixelDeltaX = dfPixelDeltaX;
34,644✔
1356
        dfLastPixelDeltaY = dfPixelDeltaY;
34,644✔
1357
        bLastPixelDeltaValid = true;
34,644✔
1358
    }
1359
    if (fpLog != nullptr)
3,785✔
1360
        VSIFCloseL(fpLog);
1✔
1361

1362
    if (iIter != -1)
3,785✔
1363
    {
1364
        CPLDebug("RPC", "Failed Iterations %d: Got: %.16g,%.16g  Offset=%g,%g",
530✔
1365
                 iIter, dfResultX, dfResultY, dfPixelDeltaX, dfPixelDeltaY);
1366
        return false;
530✔
1367
    }
1368

1369
    *pdfLong = dfResultX;
3,255✔
1370
    *pdfLat = dfResultY;
3,255✔
1371
    return true;
3,255✔
1372
}
1373

1374
/************************************************************************/
1375
/*                        GDALRPCGetDEMHeight()                         */
1376
/************************************************************************/
1377

1378
static int GDALRPCGetDEMHeight(GDALRPCTransformInfo *psTransform,
307,758✔
1379
                               const double dfXIn, const double dfYIn,
1380
                               double *pdfDEMH)
1381
{
1382
    GDALRIOResampleAlg eResample = GDALRIOResampleAlg::GRIORA_NearestNeighbour;
307,758✔
1383
    switch (psTransform->eResampleAlg)
307,758✔
1384
    {
1385
        case DEMResampleAlg::DRA_NearestNeighbour:
14✔
1386
            eResample = GDALRIOResampleAlg::GRIORA_NearestNeighbour;
14✔
1387
            break;
14✔
1388
        case DEMResampleAlg::DRA_Bilinear:
307,730✔
1389
            eResample = GDALRIOResampleAlg::GRIORA_Bilinear;
307,730✔
1390
            break;
307,730✔
1391
        case DEMResampleAlg::DRA_CubicSpline:
14✔
1392
            eResample = GDALRIOResampleAlg::GRIORA_CubicSpline;
14✔
1393
            break;
14✔
1394
    }
1395

1396
    std::unique_ptr<DoublePointsCache> cacheDEM{psTransform->poCacheDEM};
307,758✔
1397
    int res =
1398
        GDALInterpolateAtPoint(psTransform->poDS->GetRasterBand(1), eResample,
307,758✔
1399
                               cacheDEM, dfXIn, dfYIn, pdfDEMH, nullptr);
307,758✔
1400
    psTransform->poCacheDEM = cacheDEM.release();
307,758✔
1401
    return res;
615,516✔
1402
}
1403

1404
/************************************************************************/
1405
/*                           RPCIsValidLongLat()                        */
1406
/************************************************************************/
1407

1408
static bool RPCIsValidLongLat(const GDALRPCTransformInfo *psTransform,
547,887✔
1409
                              double dfLong, double dfLat)
1410
{
1411
    if (!psTransform->poRPCFootprintPreparedGeom)
547,887✔
1412
        return true;
427,774✔
1413

1414
    OGRPoint p(dfLong, dfLat);
240,226✔
1415
    return CPL_TO_BOOL(OGRPreparedGeometryContains(
240,226✔
1416
        psTransform->poRPCFootprintPreparedGeom, OGRGeometry::ToHandle(&p)));
240,226✔
1417
}
1418

1419
/************************************************************************/
1420
/*                    GDALRPCTransformWholeLineWithDEM()                */
1421
/************************************************************************/
1422

1423
static int
1424
GDALRPCTransformWholeLineWithDEM(const GDALRPCTransformInfo *psTransform,
2,192✔
1425
                                 int nPointCount, double *padfX, double *padfY,
1426
                                 double *padfZ, int *panSuccess, int nXLeft,
1427
                                 int nXWidth, int nYTop, int nYHeight)
1428
{
1429
    double *padfDEMBuffer = static_cast<double *>(
1430
        VSI_MALLOC3_VERBOSE(sizeof(double), nXWidth, nYHeight));
2,192✔
1431
    if (padfDEMBuffer == nullptr)
2,192✔
1432
    {
1433
        for (int i = 0; i < nPointCount; i++)
×
1434
            panSuccess[i] = FALSE;
×
1435
        return FALSE;
×
1436
    }
1437
    CPLErr eErr = psTransform->poDS->GetRasterBand(1)->RasterIO(
2,192✔
1438
        GF_Read, nXLeft, nYTop, nXWidth, nYHeight, padfDEMBuffer, nXWidth,
1439
        nYHeight, GDT_Float64, 0, 0, nullptr);
1440
    if (eErr != CE_None)
2,192✔
1441
    {
1442
        for (int i = 0; i < nPointCount; i++)
×
1443
            panSuccess[i] = FALSE;
×
1444
        VSIFree(padfDEMBuffer);
×
1445
        return FALSE;
×
1446
    }
1447

1448
    int bGotNoDataValue = FALSE;
2,192✔
1449
    const double dfNoDataValue =
1450
        psTransform->poDS->GetRasterBand(1)->GetNoDataValue(&bGotNoDataValue);
2,192✔
1451

1452
    // dfY in pixel center convention.
1453
    const double dfY = psTransform->adfDEMReverseGeoTransform[3] +
2,192✔
1454
                       padfY[0] * psTransform->adfDEMReverseGeoTransform[5] -
2,192✔
1455
                       0.5;
1456
    const int nY = static_cast<int>(dfY);
2,192✔
1457
    const double dfDeltaY = dfY - nY;
2,192✔
1458

1459
    int bRet = TRUE;
2,192✔
1460
    for (int i = 0; i < nPointCount; i++)
180,781✔
1461
    {
1462
        if (padfX[i] == HUGE_VAL)
178,589✔
1463
        {
1464
            bRet = FALSE;
×
1465
            panSuccess[i] = FALSE;
×
1466
            continue;
×
1467
        }
1468

1469
        double dfDEMH = 0.0;
178,589✔
1470
        const double dfZ_i = padfZ ? padfZ[i] : 0.0;
178,589✔
1471

1472
        if (psTransform->eResampleAlg == DRA_CubicSpline)
178,589✔
1473
        {
1474
            // dfX in pixel center convention.
1475
            const double dfX =
10✔
1476
                psTransform->adfDEMReverseGeoTransform[0] +
10✔
1477
                padfX[i] * psTransform->adfDEMReverseGeoTransform[1] - 0.5;
10✔
1478
            const int nX = static_cast<int>(dfX);
10✔
1479
            const double dfDeltaX = dfX - nX;
10✔
1480

1481
            const int nXNew = nX - 1;
10✔
1482

1483
            double dfSumH = 0.0;
10✔
1484
            double dfSumWeight = 0.0;
10✔
1485
            for (int k_i = 0; k_i < 4; k_i++)
50✔
1486
            {
1487
                // Loop across the X axis.
1488
                for (int k_j = 0; k_j < 4; k_j++)
200✔
1489
                {
1490
                    // Calculate the weight for the specified pixel according
1491
                    // to the bicubic b-spline kernel we're using for
1492
                    // interpolation.
1493
                    const int dKernIndX = k_j - 1;
160✔
1494
                    const int dKernIndY = k_i - 1;
160✔
1495
                    const double dfPixelWeight =
1496
                        CubicSplineKernel(dKernIndX - dfDeltaX) *
160✔
1497
                        CubicSplineKernel(dKernIndY - dfDeltaY);
160✔
1498

1499
                    // Create a sum of all values
1500
                    // adjusted for the pixel's calculated weight.
1501
                    const double dfElev =
160✔
1502
                        padfDEMBuffer[k_i * nXWidth + nXNew - nXLeft + k_j];
160✔
1503
                    if (bGotNoDataValue &&
160✔
1504
                        ARE_REAL_EQUAL(dfNoDataValue, dfElev))
160✔
1505
                        continue;
×
1506

1507
                    dfSumH += dfElev * dfPixelWeight;
160✔
1508
                    dfSumWeight += dfPixelWeight;
160✔
1509
                }
1510
            }
1511
            if (dfSumWeight == 0.0)
10✔
1512
            {
1513
                if (psTransform->bHasDEMMissingValue)
×
1514
                    dfDEMH = psTransform->dfDEMMissingValue;
×
1515
                else
1516
                {
1517
                    bRet = FALSE;
×
1518
                    panSuccess[i] = FALSE;
×
1519
                    continue;
×
1520
                }
1521
            }
1522
            else
1523
                dfDEMH = dfSumH / dfSumWeight;
10✔
1524
        }
1525
        else if (psTransform->eResampleAlg == DRA_Bilinear)
178,579✔
1526
        {
1527
            // dfX in pixel center convention.
1528
            const double dfX =
178,569✔
1529
                psTransform->adfDEMReverseGeoTransform[0] +
178,569✔
1530
                padfX[i] * psTransform->adfDEMReverseGeoTransform[1] - 0.5;
178,569✔
1531
            const int nX = static_cast<int>(dfX);
178,569✔
1532
            const double dfDeltaX = dfX - nX;
178,569✔
1533

1534
            // Bilinear interpolation.
1535
            double adfElevData[4] = {};
178,569✔
1536
            memcpy(adfElevData, padfDEMBuffer + nX - nXLeft,
178,569✔
1537
                   2 * sizeof(double));
1538
            memcpy(adfElevData + 2, padfDEMBuffer + nXWidth + nX - nXLeft,
178,569✔
1539
                   2 * sizeof(double));
1540

1541
            int bFoundNoDataElev = FALSE;
178,569✔
1542
            if (bGotNoDataValue)
178,569✔
1543
            {
1544
                int k_valid_sample = -1;
46,350✔
1545
                for (int k_i = 0; k_i < 4; k_i++)
231,750✔
1546
                {
1547
                    if (ARE_REAL_EQUAL(dfNoDataValue, adfElevData[k_i]))
185,400✔
1548
                    {
1549
                        bFoundNoDataElev = TRUE;
×
1550
                    }
1551
                    else if (k_valid_sample < 0)
185,400✔
1552
                    {
1553
                        k_valid_sample = k_i;
46,350✔
1554
                    }
1555
                }
1556
                if (bFoundNoDataElev)
46,350✔
1557
                {
1558
                    if (k_valid_sample >= 0)
×
1559
                    {
1560
                        if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
×
1561
                        {
1562
                            bRet = FALSE;
×
1563
                            panSuccess[i] = FALSE;
×
1564
                            padfX[i] = HUGE_VAL;
×
1565
                            padfY[i] = HUGE_VAL;
×
1566
                            continue;
×
1567
                        }
1568
                        dfDEMH = adfElevData[k_valid_sample];
×
1569
                        RPCTransformPoint(
×
1570
                            psTransform, padfX[i], padfY[i],
×
1571
                            dfZ_i + (psTransform->dfHeightOffset + dfDEMH) *
×
1572
                                        psTransform->dfHeightScale,
×
1573
                            padfX + i, padfY + i);
×
1574

1575
                        panSuccess[i] = TRUE;
×
1576
                        continue;
×
1577
                    }
1578
                    else if (psTransform->bHasDEMMissingValue)
×
1579
                    {
1580
                        if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
×
1581
                        {
1582
                            bRet = FALSE;
×
1583
                            panSuccess[i] = FALSE;
×
1584
                            padfX[i] = HUGE_VAL;
×
1585
                            padfY[i] = HUGE_VAL;
×
1586
                            continue;
×
1587
                        }
1588
                        dfDEMH = psTransform->dfDEMMissingValue;
×
1589
                        RPCTransformPoint(
×
1590
                            psTransform, padfX[i], padfY[i],
×
1591
                            dfZ_i + (psTransform->dfHeightOffset + dfDEMH) *
×
1592
                                        psTransform->dfHeightScale,
×
1593
                            padfX + i, padfY + i);
×
1594

1595
                        panSuccess[i] = TRUE;
×
1596
                        continue;
×
1597
                    }
1598
                    else
1599
                    {
1600
                        bRet = FALSE;
×
1601
                        panSuccess[i] = FALSE;
×
1602
                        padfX[i] = HUGE_VAL;
×
1603
                        padfY[i] = HUGE_VAL;
×
1604
                        continue;
×
1605
                    }
1606
                }
1607
            }
1608
            const double dfDeltaX1 = 1.0 - dfDeltaX;
178,569✔
1609
            const double dfDeltaY1 = 1.0 - dfDeltaY;
178,569✔
1610

1611
            const double dfXZ1 =
178,569✔
1612
                adfElevData[0] * dfDeltaX1 + adfElevData[1] * dfDeltaX;
178,569✔
1613
            const double dfXZ2 =
178,569✔
1614
                adfElevData[2] * dfDeltaX1 + adfElevData[3] * dfDeltaX;
178,569✔
1615
            const double dfYZ = dfXZ1 * dfDeltaY1 + dfXZ2 * dfDeltaY;
178,569✔
1616
            dfDEMH = dfYZ;
178,569✔
1617
        }
1618
        else
1619
        {
1620
            const double dfX =
10✔
1621
                psTransform->adfDEMReverseGeoTransform[0] +
10✔
1622
                padfX[i] * psTransform->adfDEMReverseGeoTransform[1];
10✔
1623
            const int nX = int(dfX);
10✔
1624

1625
            dfDEMH = padfDEMBuffer[nX - nXLeft];
10✔
1626
            if (bGotNoDataValue && ARE_REAL_EQUAL(dfNoDataValue, dfDEMH))
10✔
1627
            {
1628
                if (psTransform->bHasDEMMissingValue)
×
1629
                    dfDEMH = psTransform->dfDEMMissingValue;
×
1630
                else
1631
                {
1632
                    bRet = FALSE;
×
1633
                    panSuccess[i] = FALSE;
×
1634
                    padfX[i] = HUGE_VAL;
×
1635
                    padfY[i] = HUGE_VAL;
×
1636
                    continue;
×
1637
                }
1638
            }
1639
        }
1640

1641
        if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
178,589✔
1642
        {
1643
            bRet = FALSE;
×
1644
            panSuccess[i] = FALSE;
×
1645
            padfX[i] = HUGE_VAL;
×
1646
            padfY[i] = HUGE_VAL;
×
1647
            continue;
×
1648
        }
1649
        RPCTransformPoint(psTransform, padfX[i], padfY[i],
178,589✔
1650
                          dfZ_i + (psTransform->dfHeightOffset + dfDEMH) *
178,589✔
1651
                                      psTransform->dfHeightScale,
178,589✔
1652
                          padfX + i, padfY + i);
178,589✔
1653

1654
        panSuccess[i] = TRUE;
178,589✔
1655
    }
1656

1657
    VSIFree(padfDEMBuffer);
2,192✔
1658

1659
    return bRet;
2,192✔
1660
}
1661

1662
/************************************************************************/
1663
/*                           GDALRPCOpenDEM()                           */
1664
/************************************************************************/
1665

1666
static bool GDALRPCOpenDEM(GDALRPCTransformInfo *psTransform)
33✔
1667
{
1668
    CPLAssert(psTransform->pszDEMPath != nullptr);
33✔
1669

1670
    bool bIsValid = false;
33✔
1671

1672
    CPLString osPrevValueConfigOption;
66✔
1673
    if (psTransform->bApplyDEMVDatumShift)
33✔
1674
    {
1675
        osPrevValueConfigOption =
1676
            CPLGetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "");
32✔
1677
        CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS", "YES");
32✔
1678
    }
1679
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
33✔
1680
    psTransform->poDS =
33✔
1681
        GDALDataset::FromHandle(GDALOpen(psTransform->pszDEMPath, GA_ReadOnly));
33✔
1682
    if (psTransform->poDS != nullptr &&
65✔
1683
        psTransform->poDS->GetRasterCount() >= 1)
32✔
1684
    {
1685
        OGRSpatialReference oDEMSRS;
64✔
1686
        if (psTransform->pszDEMSRS != nullptr)
32✔
1687
        {
1688
            oDEMSRS.SetFromUserInput(psTransform->pszDEMSRS);
1✔
1689
            oDEMSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1✔
1690
        }
1691

1692
        auto poDSSpaRefSrc = psTransform->pszDEMSRS != nullptr
32✔
1693
                                 ? &oDEMSRS
32✔
1694
                                 : psTransform->poDS->GetSpatialRef();
31✔
1695
        if (poDSSpaRefSrc)
32✔
1696
        {
1697
            auto poDSSpaRef = poDSSpaRefSrc->Clone();
32✔
1698

1699
            if (!psTransform->bApplyDEMVDatumShift)
32✔
1700
                poDSSpaRef->StripVertical();
1✔
1701

1702
            auto wkt_EPSG_4979 =
32✔
1703
                "GEODCRS[\"WGS 84\",\n"
1704
                "    DATUM[\"World Geodetic System 1984\",\n"
1705
                "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
1706
                "            LENGTHUNIT[\"metre\",1]]],\n"
1707
                "    PRIMEM[\"Greenwich\",0,\n"
1708
                "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1709
                "    CS[ellipsoidal,3],\n"
1710
                "        AXIS[\"geodetic latitude (Lat)\",north,\n"
1711
                "            ORDER[1],\n"
1712
                "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1713
                "        AXIS[\"geodetic longitude (Lon)\",east,\n"
1714
                "            ORDER[2],\n"
1715
                "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1716
                "        AXIS[\"ellipsoidal height (h)\",up,\n"
1717
                "            ORDER[3],\n"
1718
                "            LENGTHUNIT[\"metre\",1]],\n"
1719
                "    AREA[\"World (by country)\"],\n"
1720
                "    BBOX[-90,-180,90,180],\n"
1721
                "    ID[\"EPSG\",4979]]";
1722
            OGRSpatialReference *poWGSSpaRef = new OGRSpatialReference(
1723
                poDSSpaRef->IsCompound() ? wkt_EPSG_4979
32✔
1724
                                         : SRS_WKT_WGS84_LAT_LONG);
32✔
1725
            poWGSSpaRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
32✔
1726

1727
            if (!poWGSSpaRef->IsSame(poDSSpaRef))
32✔
1728
                psTransform->poCT =
12✔
1729
                    OGRCreateCoordinateTransformation(poWGSSpaRef, poDSSpaRef);
12✔
1730

1731
            if (psTransform->poCT != nullptr && !poDSSpaRef->IsCompound())
32✔
1732
            {
1733
                // Empiric attempt to guess if the coordinate transformation
1734
                // to WGS84 is a no-op. For example for NED13 datasets in
1735
                // NAD83.
1736
                double adfX[] = {-179.0, 179.0, 179.0, -179.0, 0.0, 0.0};
10✔
1737
                double adfY[] = {89.0, 89.0, -89.0, -89.0, 0.0, 0.0};
10✔
1738
                double adfZ[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
10✔
1739

1740
                // Also test with a "reference point" from the RPC values.
1741
                double dfRefLong = 0.0;
10✔
1742
                double dfRefLat = 0.0;
10✔
1743
                if (psTransform->sRPC.dfMIN_LONG != -180 ||
10✔
1744
                    psTransform->sRPC.dfMAX_LONG != 180)
10✔
1745
                {
1746
                    dfRefLong = (psTransform->sRPC.dfMIN_LONG +
×
1747
                                 psTransform->sRPC.dfMAX_LONG) *
×
1748
                                0.5;
1749
                    dfRefLat = (psTransform->sRPC.dfMIN_LAT +
×
1750
                                psTransform->sRPC.dfMAX_LAT) *
×
1751
                               0.5;
1752
                }
1753
                else
1754
                {
1755
                    dfRefLong = psTransform->sRPC.dfLONG_OFF;
10✔
1756
                    dfRefLat = psTransform->sRPC.dfLAT_OFF;
10✔
1757
                }
1758
                adfX[5] = dfRefLong;
10✔
1759
                adfY[5] = dfRefLat;
10✔
1760

1761
                if (psTransform->poCT->Transform(6, adfX, adfY, adfZ) &&
10✔
1762
                    fabs(adfX[0] - -179.0) < 1.0e-12 &&
10✔
1763
                    fabs(adfY[0] - 89.0) < 1.0e-12 &&
1✔
1764
                    fabs(adfX[1] - 179.0) < 1.0e-12 &&
1✔
1765
                    fabs(adfY[1] - 89.0) < 1.0e-12 &&
1✔
1766
                    fabs(adfX[2] - 179.0) < 1.0e-12 &&
1✔
1767
                    fabs(adfY[2] - -89.0) < 1.0e-12 &&
1✔
1768
                    fabs(adfX[3] - -179.0) < 1.0e-12 &&
1✔
1769
                    fabs(adfY[3] - -89.0) < 1.0e-12 &&
1✔
1770
                    fabs(adfX[4] - 0.0) < 1.0e-12 &&
1✔
1771
                    fabs(adfY[4] - 0.0) < 1.0e-12 &&
1✔
1772
                    fabs(adfX[5] - dfRefLong) < 1.0e-12 &&
21✔
1773
                    fabs(adfY[5] - dfRefLat) < 1.0e-12)
1✔
1774
                {
1775
                    CPLDebug("RPC",
1✔
1776
                             "Short-circuiting coordinate transformation "
1777
                             "from DEM SRS to WGS 84 due to apparent nop");
1778
                    delete psTransform->poCT;
1✔
1779
                    psTransform->poCT = nullptr;
1✔
1780
                }
1781
            }
1782

1783
            delete poWGSSpaRef;
32✔
1784
            delete poDSSpaRef;
32✔
1785
        }
1786

1787
        if (psTransform->poDS->GetGeoTransform(
96✔
1788
                *reinterpret_cast<GDALGeoTransform *>(
1789
                    psTransform->adfDEMGeoTransform)) == CE_None &&
64✔
1790
            GDALInvGeoTransform(psTransform->adfDEMGeoTransform,
32✔
1791
                                psTransform->adfDEMReverseGeoTransform))
32✔
1792
        {
1793
            bIsValid = true;
32✔
1794
        }
1795
    }
1796

1797
    if (psTransform->bApplyDEMVDatumShift)
33✔
1798
    {
1799
        CPLSetThreadLocalConfigOption("GTIFF_REPORT_COMPD_CS",
32✔
1800
                                      !osPrevValueConfigOption.empty()
32✔
1801
                                          ? osPrevValueConfigOption.c_str()
×
1802
                                          : nullptr);
1803
    }
1804

1805
    return bIsValid;
66✔
1806
}
1807

1808
/************************************************************************/
1809
/*                          GDALRPCTransform()                          */
1810
/************************************************************************/
1811

1812
/** RPC transform */
1813
int GDALRPCTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
9,394✔
1814
                     double *padfX, double *padfY, double *padfZ,
1815
                     int *panSuccess)
1816

1817
{
1818
    VALIDATE_POINTER1(pTransformArg, "GDALRPCTransform", 0);
9,394✔
1819

1820
    GDALRPCTransformInfo *psTransform =
9,394✔
1821
        static_cast<GDALRPCTransformInfo *>(pTransformArg);
1822

1823
    if (psTransform->bReversed)
9,394✔
1824
        bDstToSrc = !bDstToSrc;
×
1825

1826
    /* -------------------------------------------------------------------- */
1827
    /*      The simple case is transforming from lat/long to pixel/line.    */
1828
    /*      Just apply the equations directly.                              */
1829
    /* -------------------------------------------------------------------- */
1830
    if (bDstToSrc)
9,394✔
1831
    {
1832
        // Optimization to avoid doing too many picking in DEM in the particular
1833
        // case where each point to transform is on a single line of the DEM.
1834
        // To make it simple and fast we check that all input latitudes are
1835
        // identical, that the DEM is in WGS84 geodetic and that it has no
1836
        // rotation.  Such case is for example triggered when doing gdalwarp
1837
        // with a target SRS of EPSG:4326 or EPSG:3857.
1838
        if (nPointCount >= 10 && psTransform->poDS != nullptr &&
3,476✔
1839
            psTransform->poCT == nullptr &&
3,449✔
1840
            padfY[0] == padfY[nPointCount - 1] &&
2,886✔
1841
            padfY[0] == padfY[nPointCount / 2] &&
2,826✔
1842
            psTransform->adfDEMReverseGeoTransform[1] > 0.0 &&
2,822✔
1843
            psTransform->adfDEMReverseGeoTransform[2] == 0.0 &&
2,822✔
1844
            psTransform->adfDEMReverseGeoTransform[4] == 0.0 &&
13,955✔
1845
            CPLTestBool(CPLGetConfigOption("GDAL_RPC_DEM_OPTIM", "YES")))
2,822✔
1846
        {
1847
            bool bUseOptimized = true;
2,822✔
1848
            double dfMinX = padfX[0];
2,822✔
1849
            double dfMaxX = padfX[0];
2,822✔
1850
            for (int i = 1; i < nPointCount; i++)
327,074✔
1851
            {
1852
                if (padfY[i] != padfY[0])
324,252✔
1853
                {
1854
                    bUseOptimized = false;
×
1855
                    break;
×
1856
                }
1857
                if (padfX[i] < dfMinX)
324,252✔
1858
                    dfMinX = padfX[i];
×
1859
                if (padfX[i] > dfMaxX)
324,252✔
1860
                    dfMaxX = padfX[i];
324,225✔
1861
            }
1862
            if (bUseOptimized)
2,822✔
1863
            {
1864
                double dfX1 = 0.0;
2,822✔
1865
                double dfY1 = 0.0;
2,822✔
1866
                double dfX2 = 0.0;
2,822✔
1867
                double dfY2 = 0.0;
2,822✔
1868
                GDALApplyGeoTransform(psTransform->adfDEMReverseGeoTransform,
2,822✔
1869
                                      dfMinX, padfY[0], &dfX1, &dfY1);
1870
                GDALApplyGeoTransform(psTransform->adfDEMReverseGeoTransform,
2,822✔
1871
                                      dfMaxX, padfY[0], &dfX2, &dfY2);
1872

1873
                // Convert to center of pixel convention for reading the image
1874
                // data.
1875
                if (psTransform->eResampleAlg != DRA_NearestNeighbour)
2,822✔
1876
                {
1877
                    dfX1 -= 0.5;
2,821✔
1878
                    dfY1 -= 0.5;
2,821✔
1879
                    dfX2 -= 0.5;
2,821✔
1880
                    // dfY2 -= 0.5;
1881
                }
1882
                int nXLeft = static_cast<int>(floor(dfX1));
2,822✔
1883
                int nXRight = static_cast<int>(floor(dfX2));
2,822✔
1884
                int nXWidth = nXRight - nXLeft + 1;
2,822✔
1885
                int nYTop = static_cast<int>(floor(dfY1));
2,822✔
1886
                int nYHeight;
1887
                if (psTransform->eResampleAlg == DRA_CubicSpline)
2,822✔
1888
                {
1889
                    nXLeft--;
1✔
1890
                    nXWidth += 3;
1✔
1891
                    nYTop--;
1✔
1892
                    nYHeight = 4;
1✔
1893
                }
1894
                else if (psTransform->eResampleAlg == DRA_Bilinear)
2,821✔
1895
                {
1896
                    nXWidth++;
2,820✔
1897
                    nYHeight = 2;
2,820✔
1898
                }
1899
                else
1900
                {
1901
                    nYHeight = 1;
1✔
1902
                }
1903
                if (nXLeft >= 0 &&
5,022✔
1904
                    nXLeft + nXWidth <= psTransform->poDS->GetRasterXSize() &&
2,200✔
1905
                    nYTop >= 0 &&
5,022✔
1906
                    nYTop + nYHeight <= psTransform->poDS->GetRasterYSize())
2,200✔
1907
                {
1908
                    static bool bOnce = false;
1909
                    if (!bOnce)
2,192✔
1910
                    {
1911
                        bOnce = true;
2✔
1912
                        CPLDebug("RPC",
2✔
1913
                                 "Using GDALRPCTransformWholeLineWithDEM");
1914
                    }
1915
                    return GDALRPCTransformWholeLineWithDEM(
2,192✔
1916
                        psTransform, nPointCount, padfX, padfY, padfZ,
1917
                        panSuccess, nXLeft, nXWidth, nYTop, nYHeight);
2,192✔
1918
                }
1919
            }
1920
        }
1921

1922
        int bRet = TRUE;
5,465✔
1923
        for (int i = 0; i < nPointCount; i++)
371,508✔
1924
        {
1925
            if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
366,043✔
1926
            {
1927
                bRet = FALSE;
108,994✔
1928
                panSuccess[i] = FALSE;
108,994✔
1929
                padfX[i] = HUGE_VAL;
108,994✔
1930
                padfY[i] = HUGE_VAL;
108,994✔
1931
                continue;
121,421✔
1932
            }
1933
            double dfHeight = 0.0;
257,049✔
1934
            if (!GDALRPCGetHeightAtLongLat(psTransform, padfX[i], padfY[i],
257,049✔
1935
                                           &dfHeight))
1936
            {
1937
                bRet = FALSE;
12,427✔
1938
                panSuccess[i] = FALSE;
12,427✔
1939
                padfX[i] = HUGE_VAL;
12,427✔
1940
                padfY[i] = HUGE_VAL;
12,427✔
1941
                continue;
12,427✔
1942
            }
1943

1944
            RPCTransformPoint(psTransform, padfX[i], padfY[i],
244,622✔
1945
                              (padfZ ? padfZ[i] : 0.0) + dfHeight, padfX + i,
244,622✔
1946
                              padfY + i);
244,622✔
1947
            panSuccess[i] = TRUE;
244,622✔
1948
        }
1949

1950
        return bRet;
5,465✔
1951
    }
1952

1953
    if (padfZ == nullptr)
1,737✔
1954
    {
1955
        CPLError(CE_Failure, CPLE_NotSupported,
×
1956
                 "Z array should be provided for reverse RPC computation");
1957
        for (int i = 0; i < nPointCount; i++)
×
1958
            panSuccess[i] = FALSE;
×
1959
        return FALSE;
×
1960
    }
1961

1962
    /* -------------------------------------------------------------------- */
1963
    /*      Compute the inverse (pixel/line/height to lat/long).  This      */
1964
    /*      function uses an iterative method from an initial linear        */
1965
    /*      approximation.                                                  */
1966
    /* -------------------------------------------------------------------- */
1967
    int bRet = TRUE;
1,737✔
1968
    for (int i = 0; i < nPointCount; i++)
15,715✔
1969
    {
1970
        double dfResultX = 0.0;
13,978✔
1971
        double dfResultY = 0.0;
13,978✔
1972

1973
        if (!RPCInverseTransformPoint(psTransform, padfX[i], padfY[i], padfZ[i],
13,978✔
1974
                                      &dfResultX, &dfResultY))
1975
        {
1976
            bRet = FALSE;
10,723✔
1977
            panSuccess[i] = FALSE;
10,723✔
1978
            padfX[i] = HUGE_VAL;
10,723✔
1979
            padfY[i] = HUGE_VAL;
10,723✔
1980
            continue;
10,723✔
1981
        }
1982
        if (!RPCIsValidLongLat(psTransform, padfX[i], padfY[i]))
3,255✔
1983
        {
1984
            bRet = FALSE;
×
1985
            panSuccess[i] = FALSE;
×
1986
            padfX[i] = HUGE_VAL;
×
1987
            padfY[i] = HUGE_VAL;
×
1988
            continue;
×
1989
        }
1990

1991
        padfX[i] = dfResultX;
3,255✔
1992
        padfY[i] = dfResultY;
3,255✔
1993

1994
        panSuccess[i] = TRUE;
3,255✔
1995
    }
1996

1997
    return bRet;
1,737✔
1998
}
1999

2000
/************************************************************************/
2001
/*                    GDALSerializeRPCTransformer()                     */
2002
/************************************************************************/
2003

2004
CPLXMLNode *GDALSerializeRPCTransformer(void *pTransformArg)
1✔
2005

2006
{
2007
    VALIDATE_POINTER1(pTransformArg, "GDALSerializeRPCTransformer", nullptr);
1✔
2008

2009
    GDALRPCTransformInfo *psInfo =
1✔
2010
        static_cast<GDALRPCTransformInfo *>(pTransformArg);
2011

2012
    CPLXMLNode *psTree =
2013
        CPLCreateXMLNode(nullptr, CXT_Element, "RPCTransformer");
1✔
2014

2015
    /* -------------------------------------------------------------------- */
2016
    /*      Serialize bReversed.                                            */
2017
    /* -------------------------------------------------------------------- */
2018
    CPLCreateXMLElementAndValue(psTree, "Reversed",
1✔
2019
                                CPLString().Printf("%d", psInfo->bReversed));
2✔
2020

2021
    /* -------------------------------------------------------------------- */
2022
    /*      Serialize Height Offset.                                        */
2023
    /* -------------------------------------------------------------------- */
2024
    CPLCreateXMLElementAndValue(
1✔
2025
        psTree, "HeightOffset",
2026
        CPLString().Printf("%.15g", psInfo->dfHeightOffset));
2✔
2027

2028
    /* -------------------------------------------------------------------- */
2029
    /*      Serialize Height Scale.                                         */
2030
    /* -------------------------------------------------------------------- */
2031
    if (psInfo->dfHeightScale != 1.0)
1✔
2032
        CPLCreateXMLElementAndValue(
×
2033
            psTree, "HeightScale",
2034
            CPLString().Printf("%.15g", psInfo->dfHeightScale));
×
2035

2036
    /* -------------------------------------------------------------------- */
2037
    /*      Serialize DEM path.                                             */
2038
    /* -------------------------------------------------------------------- */
2039
    if (psInfo->pszDEMPath != nullptr)
1✔
2040
    {
2041
        CPLCreateXMLElementAndValue(
×
2042
            psTree, "DEMPath", CPLString().Printf("%s", psInfo->pszDEMPath));
×
2043

2044
        /* --------------------------------------------------------------------
2045
         */
2046
        /*      Serialize DEM interpolation */
2047
        /* --------------------------------------------------------------------
2048
         */
2049
        CPLCreateXMLElementAndValue(
×
2050
            psTree, "DEMInterpolation",
2051
            GDALSerializeRPCDEMResample(psInfo->eResampleAlg));
2052

2053
        if (psInfo->bHasDEMMissingValue)
×
2054
        {
2055
            CPLCreateXMLElementAndValue(
×
2056
                psTree, "DEMMissingValue",
2057
                CPLSPrintf("%.17g", psInfo->dfDEMMissingValue));
2058
        }
2059

2060
        CPLCreateXMLElementAndValue(psTree, "DEMApplyVDatumShift",
×
2061
                                    psInfo->bApplyDEMVDatumShift ? "true"
×
2062
                                                                 : "false");
2063

2064
        /* --------------------------------------------------------------------
2065
         */
2066
        /*      Serialize DEM SRS */
2067
        /* --------------------------------------------------------------------
2068
         */
2069
        if (psInfo->pszDEMSRS != nullptr)
×
2070
        {
2071
            CPLCreateXMLElementAndValue(psTree, "DEMSRS", psInfo->pszDEMSRS);
×
2072
        }
2073
    }
2074

2075
    /* -------------------------------------------------------------------- */
2076
    /*      Serialize pixel error threshold.                                */
2077
    /* -------------------------------------------------------------------- */
2078
    CPLCreateXMLElementAndValue(
1✔
2079
        psTree, "PixErrThreshold",
2080
        CPLString().Printf("%.15g", psInfo->dfPixErrThreshold));
2✔
2081

2082
    /* -------------------------------------------------------------------- */
2083
    /*      RPC metadata.                                                   */
2084
    /* -------------------------------------------------------------------- */
2085
    char **papszMD = RPCInfoV2ToMD(&(psInfo->sRPC));
1✔
2086
    CPLXMLNode *psMD = CPLCreateXMLNode(psTree, CXT_Element, "Metadata");
1✔
2087

2088
    for (int i = 0; papszMD != nullptr && papszMD[i] != nullptr; i++)
21✔
2089
    {
2090
        char *pszKey = nullptr;
20✔
2091

2092
        const char *pszRawValue = CPLParseNameValue(papszMD[i], &pszKey);
20✔
2093

2094
        CPLXMLNode *psMDI = CPLCreateXMLNode(psMD, CXT_Element, "MDI");
20✔
2095
        CPLSetXMLValue(psMDI, "#key", pszKey);
20✔
2096
        CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
20✔
2097

2098
        CPLFree(pszKey);
20✔
2099
    }
2100

2101
    CSLDestroy(papszMD);
1✔
2102

2103
    return psTree;
1✔
2104
}
2105

2106
/************************************************************************/
2107
/*                   GDALDeserializeRPCTransformer()                    */
2108
/************************************************************************/
2109

2110
void *GDALDeserializeRPCTransformer(CPLXMLNode *psTree)
×
2111

2112
{
2113
    char **papszOptions = nullptr;
×
2114

2115
    /* -------------------------------------------------------------------- */
2116
    /*      Collect metadata.                                               */
2117
    /* -------------------------------------------------------------------- */
2118
    CPLXMLNode *psMetadata = CPLGetXMLNode(psTree, "Metadata");
×
2119

2120
    if (psMetadata == nullptr || psMetadata->eType != CXT_Element ||
×
2121
        !EQUAL(psMetadata->pszValue, "Metadata"))
×
2122
        return nullptr;
×
2123

2124
    char **papszMD = nullptr;
×
2125
    for (CPLXMLNode *psMDI = psMetadata->psChild; psMDI != nullptr;
×
2126
         psMDI = psMDI->psNext)
×
2127
    {
2128
        if (!EQUAL(psMDI->pszValue, "MDI") || psMDI->eType != CXT_Element ||
×
2129
            psMDI->psChild == nullptr || psMDI->psChild->psNext == nullptr ||
×
2130
            psMDI->psChild->eType != CXT_Attribute ||
×
2131
            psMDI->psChild->psChild == nullptr)
×
2132
            continue;
×
2133

2134
        papszMD = CSLSetNameValue(papszMD, psMDI->psChild->psChild->pszValue,
×
2135
                                  psMDI->psChild->psNext->pszValue);
×
2136
    }
2137

2138
    GDALRPCInfoV2 sRPC;
2139
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
×
2140
    {
2141
        CPLError(CE_Failure, CPLE_AppDefined,
×
2142
                 "Failed to reconstitute RPC transformer.");
2143
        CSLDestroy(papszMD);
×
2144
        return nullptr;
×
2145
    }
2146

2147
    CSLDestroy(papszMD);
×
2148

2149
    /* -------------------------------------------------------------------- */
2150
    /*      Get other flags.                                                */
2151
    /* -------------------------------------------------------------------- */
2152
    const int bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
×
2153

2154
    const double dfPixErrThreshold =
2155
        CPLAtof(CPLGetXMLValue(psTree, "PixErrThreshold",
×
2156
                               CPLSPrintf("%f", DEFAULT_PIX_ERR_THRESHOLD)));
2157

2158
    papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT",
×
2159
                                   CPLGetXMLValue(psTree, "HeightOffset", "0"));
2160
    papszOptions = CSLSetNameValue(papszOptions, "RPC_HEIGHT_SCALE",
×
2161
                                   CPLGetXMLValue(psTree, "HeightScale", "1"));
2162
    const char *pszDEMPath = CPLGetXMLValue(psTree, "DEMPath", nullptr);
×
2163
    if (pszDEMPath != nullptr)
×
2164
        papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM", pszDEMPath);
×
2165

2166
    const char *pszDEMInterpolation =
2167
        CPLGetXMLValue(psTree, "DEMInterpolation", "bilinear");
×
2168
    if (pszDEMInterpolation != nullptr)
×
2169
        papszOptions = CSLSetNameValue(papszOptions, "RPC_DEMINTERPOLATION",
×
2170
                                       pszDEMInterpolation);
2171

2172
    const char *pszDEMMissingValue =
2173
        CPLGetXMLValue(psTree, "DEMMissingValue", nullptr);
×
2174
    if (pszDEMMissingValue != nullptr)
×
2175
        papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM_MISSING_VALUE",
×
2176
                                       pszDEMMissingValue);
2177

2178
    const char *pszDEMApplyVDatumShift =
2179
        CPLGetXMLValue(psTree, "DEMApplyVDatumShift", nullptr);
×
2180
    if (pszDEMApplyVDatumShift != nullptr)
×
2181
        papszOptions = CSLSetNameValue(
×
2182
            papszOptions, "RPC_DEM_APPLY_VDATUM_SHIFT", pszDEMApplyVDatumShift);
2183
    const char *pszDEMSRS = CPLGetXMLValue(psTree, "DEMSRS", nullptr);
×
2184
    if (pszDEMSRS != nullptr)
×
2185
        papszOptions = CSLSetNameValue(papszOptions, "RPC_DEM_SRS", pszDEMSRS);
×
2186

2187
    /* -------------------------------------------------------------------- */
2188
    /*      Generate transformation.                                        */
2189
    /* -------------------------------------------------------------------- */
2190
    void *pResult = GDALCreateRPCTransformerV2(&sRPC, bReversed,
×
2191
                                               dfPixErrThreshold, papszOptions);
2192

2193
    CSLDestroy(papszOptions);
×
2194

2195
    return pResult;
×
2196
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc