Coverage Report

Created: 2025-08-28 06:57

/src/gdal/alg/gdalgeoloc.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Implements Geolocation array based transformer.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2006, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2021, CLS
11
 * Copyright (c) 2022, Planet Labs
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
#include "cpl_port.h"
17
#include "gdal_alg.h"
18
#include "gdal_alg_priv.h"
19
#include "gdalgeoloc.h"
20
#include "gdalgeolocquadtree.h"
21
22
#include <climits>
23
#include <cmath>
24
#include <cstddef>
25
#include <cstdlib>
26
#include <cstring>
27
28
#include <algorithm>
29
#include <limits>
30
31
#include "cpl_conv.h"
32
#include "cpl_error.h"
33
#include "cpl_minixml.h"
34
#include "cpl_quad_tree.h"
35
#include "cpl_string.h"
36
#include "cpl_vsi.h"
37
#include "gdal.h"
38
#include "gdal_priv.h"
39
#include "memdataset.h"
40
41
constexpr float INVALID_BMXY = -10.0f;
42
43
#include "gdalgeoloc_carray_accessor.h"
44
#include "gdalgeoloc_dataset_accessor.h"
45
46
// #define DEBUG_GEOLOC
47
48
#ifdef DEBUG_GEOLOC
49
#include "ogrsf_frmts.h"
50
#endif
51
52
#ifdef DEBUG_GEOLOC
53
#warning "Remove me before committing"
54
#endif
55
56
CPL_C_START
57
CPLXMLNode *GDALSerializeGeoLocTransformer(void *pTransformArg);
58
void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree);
59
CPL_C_END
60
61
/************************************************************************/
62
/* ==================================================================== */
63
/*                         GDALGeoLocTransformer                        */
64
/* ==================================================================== */
65
/************************************************************************/
66
67
/************************************************************************/
68
/*                           UnshiftGeoX()                              */
69
/************************************************************************/
70
71
// Renormalize longitudes to [-180,180] range
72
static double UnshiftGeoX(const GDALGeoLocTransformInfo *psTransform,
73
                          double dfX)
74
0
{
75
0
    if (!psTransform->bGeographicSRSWithMinus180Plus180LongRange)
76
0
        return dfX;
77
0
    if (dfX > 180 || dfX < -180)
78
0
    {
79
0
        dfX = fmod(dfX + 180.0, 360.0);
80
0
        if (dfX < 0)
81
0
            dfX += 180.0;
82
0
        else
83
0
            dfX -= 180.0;
84
0
        return dfX;
85
0
    }
86
0
    return dfX;
87
0
}
88
89
/************************************************************************/
90
/*                           UpdateMinMax()                             */
91
/************************************************************************/
92
93
inline void UpdateMinMax(GDALGeoLocTransformInfo *psTransform, double dfGeoLocX,
94
                         double dfGeoLocY)
95
0
{
96
0
    if (dfGeoLocX < psTransform->dfMinX)
97
0
    {
98
0
        psTransform->dfMinX = dfGeoLocX;
99
0
        psTransform->dfYAtMinX = dfGeoLocY;
100
0
    }
101
0
    if (dfGeoLocX > psTransform->dfMaxX)
102
0
    {
103
0
        psTransform->dfMaxX = dfGeoLocX;
104
0
        psTransform->dfYAtMaxX = dfGeoLocY;
105
0
    }
106
0
    if (dfGeoLocY < psTransform->dfMinY)
107
0
    {
108
0
        psTransform->dfMinY = dfGeoLocY;
109
0
        psTransform->dfXAtMinY = dfGeoLocX;
110
0
    }
111
0
    if (dfGeoLocY > psTransform->dfMaxY)
112
0
    {
113
0
        psTransform->dfMaxY = dfGeoLocY;
114
0
        psTransform->dfXAtMaxY = dfGeoLocX;
115
0
    }
116
0
}
117
118
/************************************************************************/
119
/*                                Clamp()                               */
120
/************************************************************************/
121
122
inline double Clamp(double v, double minV, double maxV)
123
0
{
124
0
    return std::min(std::max(v, minV), maxV);
125
0
}
126
127
/************************************************************************/
128
/*                    START_ITER_PER_BLOCK()                            */
129
/************************************************************************/
130
131
#define START_ITER_PER_BLOCK(_rasterXSize, _tileXSize, _rasterYSize,           \
132
                             _tileYSize, INIT_YBLOCK, _iXStart, _iXEnd,        \
133
                             _iYStart, _iYEnd)                                 \
134
0
    {                                                                          \
135
0
        const int _nYBlocks = DIV_ROUND_UP(_rasterYSize, _tileYSize);          \
136
0
        const int _nXBlocks = DIV_ROUND_UP(_rasterXSize, _tileXSize);          \
137
0
        for (int _iYBlock = 0; _iYBlock < _nYBlocks; ++_iYBlock)               \
138
0
        {                                                                      \
139
0
            const int _iYStart = _iYBlock * _tileYSize;                        \
140
0
            const int _iYEnd = _iYBlock == _nYBlocks - 1                       \
141
0
                                   ? _rasterYSize                              \
142
0
                                   : _iYStart + _tileYSize;                    \
143
0
            INIT_YBLOCK;                                                       \
144
0
            for (int _iXBlock = 0; _iXBlock < _nXBlocks; ++_iXBlock)           \
145
0
            {                                                                  \
146
0
                const int _iXStart = _iXBlock * _tileXSize;                    \
147
0
                const int _iXEnd = _iXBlock == _nXBlocks - 1                   \
148
0
                                       ? _rasterXSize                          \
149
0
                                       : _iXStart + _tileXSize;
150
151
#define END_ITER_PER_BLOCK                                                     \
152
0
    }                                                                          \
153
0
    }                                                                          \
154
0
    }
155
156
/************************************************************************/
157
/*                    GDALGeoLoc::LoadGeolocFinish()                    */
158
/************************************************************************/
159
160
/*! @cond Doxygen_Suppress */
161
162
template <class Accessors>
163
void GDALGeoLoc<Accessors>::LoadGeolocFinish(
164
    GDALGeoLocTransformInfo *psTransform)
165
0
{
166
0
    auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
167
0
    CSLConstList papszGeolocationInfo = psTransform->papszGeolocationInfo;
168
169
    /* -------------------------------------------------------------------- */
170
    /*      Scan forward map for lat/long extents.                          */
171
    /* -------------------------------------------------------------------- */
172
0
    psTransform->dfMinX = std::numeric_limits<double>::max();
173
0
    psTransform->dfMaxX = -std::numeric_limits<double>::max();
174
0
    psTransform->dfMinY = std::numeric_limits<double>::max();
175
0
    psTransform->dfMaxY = -std::numeric_limits<double>::max();
176
177
0
    constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
178
0
    START_ITER_PER_BLOCK(psTransform->nGeoLocXSize, TILE_SIZE,
179
0
                         psTransform->nGeoLocYSize, TILE_SIZE, (void)0, iXStart,
180
0
                         iXEnd, iYStart, iYEnd)
181
0
    {
182
0
        for (int iY = iYStart; iY < iYEnd; ++iY)
183
0
        {
184
0
            for (int iX = iXStart; iX < iXEnd; ++iX)
185
0
            {
186
0
                double dfX = pAccessors->geolocXAccessor.Get(iX, iY);
187
0
                if (!psTransform->bHasNoData || dfX != psTransform->dfNoDataX)
188
0
                {
189
0
                    dfX = UnshiftGeoX(psTransform, dfX);
190
0
                    UpdateMinMax(psTransform, dfX,
191
0
                                 pAccessors->geolocYAccessor.Get(iX, iY));
192
0
                }
193
0
            }
194
0
        }
195
0
    }
196
0
    END_ITER_PER_BLOCK
197
198
    // Check if the SRS is geographic and the geoloc longitudes are in
199
    // [-180,180]
200
0
    const char *pszSRS = CSLFetchNameValue(papszGeolocationInfo, "SRS");
201
0
    if (!psTransform->bGeographicSRSWithMinus180Plus180LongRange && pszSRS &&
202
0
        psTransform->dfMinX >= -180.0 && psTransform->dfMaxX <= 180.0 &&
203
0
        !psTransform->bSwapXY)
204
0
    {
205
0
        OGRSpatialReference oSRS;
206
0
        psTransform->bGeographicSRSWithMinus180Plus180LongRange =
207
0
            oSRS.importFromWkt(pszSRS) == OGRERR_NONE &&
208
0
            CPL_TO_BOOL(oSRS.IsGeographic());
209
0
    }
210
211
#ifdef DEBUG_GEOLOC
212
    if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
213
    {
214
        auto poDS = std::unique_ptr<GDALDataset>(
215
            GDALDriver::FromHandle(GDALGetDriverByName("ESRI Shapefile"))
216
                ->Create("/tmp/geoloc_poly.shp", 0, 0, 0, GDT_Unknown,
217
                         nullptr));
218
        auto poLayer =
219
            poDS->CreateLayer("geoloc_poly", nullptr, wkbPolygon, nullptr);
220
        auto poLayerDefn = poLayer->GetLayerDefn();
221
        OGRFieldDefn fieldX("x", OFTInteger);
222
        poLayer->CreateField(&fieldX);
223
        OGRFieldDefn fieldY("y", OFTInteger);
224
        poLayer->CreateField(&fieldY);
225
        for (int iY = 0; iY < psTransform->nGeoLocYSize - 1; iY++)
226
        {
227
            for (int iX = 0; iX < psTransform->nGeoLocXSize - 1; iX++)
228
            {
229
                double x0, y0, x1, y1, x2, y2, x3, y3;
230
                if (!PixelLineToXY(psTransform, iX, iY, x0, y0) ||
231
                    !PixelLineToXY(psTransform, iX + 1, iY, x2, y2) ||
232
                    !PixelLineToXY(psTransform, iX, iY + 1, x1, y1) ||
233
                    !PixelLineToXY(psTransform, iX + 1, iY + 1, x3, y3))
234
                {
235
                    break;
236
                }
237
                if (psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
238
                    std::fabs(x0) > 170 && std::fabs(x1) > 170 &&
239
                    std::fabs(x2) > 170 && std::fabs(x3) > 170 &&
240
                    (std::fabs(x1 - x0) > 180 || std::fabs(x2 - x0) > 180 ||
241
                     std::fabs(x3 - x0) > 180))
242
                {
243
                    OGRPolygon *poPoly = new OGRPolygon();
244
                    OGRLinearRing *poRing = new OGRLinearRing();
245
                    poRing->addPoint(x0 > 0 ? x0 : x0 + 360, y0);
246
                    poRing->addPoint(x2 > 0 ? x2 : x2 + 360, y2);
247
                    poRing->addPoint(x3 > 0 ? x3 : x3 + 360, y3);
248
                    poRing->addPoint(x1 > 0 ? x1 : x1 + 360, y1);
249
                    poRing->addPoint(x0 > 0 ? x0 : x0 + 360, y0);
250
                    poPoly->addRingDirectly(poRing);
251
                    auto poFeature = std::make_unique<OGRFeature>(poLayerDefn);
252
                    poFeature->SetField(0, static_cast<int>(iX));
253
                    poFeature->SetField(1, static_cast<int>(iY));
254
                    poFeature->SetGeometryDirectly(poPoly);
255
                    CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature.get()));
256
                    if (x0 > 0)
257
                        x0 -= 360;
258
                    if (x1 > 0)
259
                        x1 -= 360;
260
                    if (x2 > 0)
261
                        x2 -= 360;
262
                    if (x3 > 0)
263
                        x3 -= 360;
264
                }
265
266
                OGRPolygon *poPoly = new OGRPolygon();
267
                OGRLinearRing *poRing = new OGRLinearRing();
268
                poRing->addPoint(x0, y0);
269
                poRing->addPoint(x2, y2);
270
                poRing->addPoint(x3, y3);
271
                poRing->addPoint(x1, y1);
272
                poRing->addPoint(x0, y0);
273
                poPoly->addRingDirectly(poRing);
274
                auto poFeature = std::make_unique<OGRFeature>(poLayerDefn);
275
                poFeature->SetField(0, static_cast<int>(iX));
276
                poFeature->SetField(1, static_cast<int>(iY));
277
                poFeature->SetGeometryDirectly(poPoly);
278
                CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature.get()));
279
            }
280
        }
281
    }
282
#endif
283
284
0
    if (psTransform->bOriginIsTopLeftCorner)
285
0
    {
286
        // Add "virtual" edge at Y=nGeoLocYSize
287
0
        for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
288
0
        {
289
0
            double dfGeoLocX;
290
0
            double dfGeoLocY;
291
0
            if (!PixelLineToXY(psTransform, static_cast<double>(iX),
292
0
                               static_cast<double>(psTransform->nGeoLocYSize),
293
0
                               dfGeoLocX, dfGeoLocY))
294
0
                continue;
295
0
            if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
296
0
                dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
297
0
            UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
298
0
        }
299
300
        // Add "virtual" edge at X=nGeoLocXSize
301
0
        for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
302
0
        {
303
0
            double dfGeoLocX;
304
0
            double dfGeoLocY;
305
0
            if (!PixelLineToXY(psTransform,
306
0
                               static_cast<double>(psTransform->nGeoLocXSize),
307
0
                               static_cast<double>(iY), dfGeoLocX, dfGeoLocY))
308
0
                continue;
309
0
            if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
310
0
                dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
311
0
            UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
312
0
        }
313
0
    }
314
0
    else
315
0
    {
316
        // Extend by half-pixel on 4 edges for pixel-center convention
317
318
0
        for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
319
0
        {
320
0
            double dfGeoLocX;
321
0
            double dfGeoLocY;
322
0
            if (!PixelLineToXY(psTransform, static_cast<double>(iX), -0.5,
323
0
                               dfGeoLocX, dfGeoLocY))
324
0
                continue;
325
0
            if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
326
0
                dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
327
0
            UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
328
0
        }
329
330
0
        for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
331
0
        {
332
0
            double dfGeoLocX;
333
0
            double dfGeoLocY;
334
0
            if (!PixelLineToXY(
335
0
                    psTransform, static_cast<double>(iX),
336
0
                    static_cast<double>(psTransform->nGeoLocYSize - 1 + 0.5),
337
0
                    dfGeoLocX, dfGeoLocY))
338
0
                continue;
339
0
            if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
340
0
                dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
341
0
            UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
342
0
        }
343
344
0
        for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
345
0
        {
346
0
            double dfGeoLocX;
347
0
            double dfGeoLocY;
348
0
            if (!PixelLineToXY(psTransform, -0.5, static_cast<double>(iY),
349
0
                               dfGeoLocX, dfGeoLocY))
350
0
                continue;
351
0
            if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
352
0
                dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
353
0
            UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
354
0
        }
355
356
0
        for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
357
0
        {
358
0
            double dfGeoLocX;
359
0
            double dfGeoLocY;
360
0
            if (!PixelLineToXY(psTransform, psTransform->nGeoLocXSize - 1 + 0.5,
361
0
                               static_cast<double>(iY), dfGeoLocX, dfGeoLocY))
362
0
                continue;
363
0
            if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
364
0
                dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
365
0
            UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
366
0
        }
367
0
    }
368
0
}
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::LoadGeolocFinish(GDALGeoLocTransformInfo*)
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::LoadGeolocFinish(GDALGeoLocTransformInfo*)
369
370
/************************************************************************/
371
/*                     GDALGeoLoc::PixelLineToXY()                      */
372
/************************************************************************/
373
374
/** Interpolate a position expressed as (floating point) pixel/line in the
375
 * geolocation array to the corresponding bilinearly-interpolated georeferenced
376
 * position.
377
 *
378
 * The interpolation assumes infinite extension beyond borders of available
379
 * data based on closest grid square.
380
 *
381
 * @param psTransform Transformation info
382
 * @param dfGeoLocPixel Position along the column/pixel axis of the geolocation
383
 * array
384
 * @param dfGeoLocLine  Position along the row/line axis of the geolocation
385
 * array
386
 * @param[out] dfX      Output X of georeferenced position.
387
 * @param[out] dfY      Output Y of georeferenced position.
388
 * @return true if success
389
 */
390
391
template <class Accessors>
392
bool GDALGeoLoc<Accessors>::PixelLineToXY(
393
    const GDALGeoLocTransformInfo *psTransform, const double dfGeoLocPixel,
394
    const double dfGeoLocLine, double &dfX, double &dfY)
395
0
{
396
0
    int iX = static_cast<int>(
397
0
        std::min(std::max(0.0, dfGeoLocPixel),
398
0
                 static_cast<double>(psTransform->nGeoLocXSize - 1)));
399
0
    int iY = static_cast<int>(
400
0
        std::min(std::max(0.0, dfGeoLocLine),
401
0
                 static_cast<double>(psTransform->nGeoLocYSize - 1)));
402
403
0
    auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
404
405
0
    for (int iAttempt = 0; iAttempt < 2; ++iAttempt)
406
0
    {
407
0
        const double dfGLX_0_0 = pAccessors->geolocXAccessor.Get(iX, iY);
408
0
        const double dfGLY_0_0 = pAccessors->geolocYAccessor.Get(iX, iY);
409
0
        if (psTransform->bHasNoData && dfGLX_0_0 == psTransform->dfNoDataX)
410
0
        {
411
0
            return false;
412
0
        }
413
414
        // This assumes infinite extension beyond borders of available
415
        // data based on closest grid square.
416
0
        if (iX + 1 < psTransform->nGeoLocXSize &&
417
0
            iY + 1 < psTransform->nGeoLocYSize)
418
0
        {
419
0
            const double dfGLX_1_0 =
420
0
                pAccessors->geolocXAccessor.Get(iX + 1, iY);
421
0
            const double dfGLY_1_0 =
422
0
                pAccessors->geolocYAccessor.Get(iX + 1, iY);
423
0
            const double dfGLX_0_1 =
424
0
                pAccessors->geolocXAccessor.Get(iX, iY + 1);
425
0
            const double dfGLY_0_1 =
426
0
                pAccessors->geolocYAccessor.Get(iX, iY + 1);
427
0
            const double dfGLX_1_1 =
428
0
                pAccessors->geolocXAccessor.Get(iX + 1, iY + 1);
429
0
            const double dfGLY_1_1 =
430
0
                pAccessors->geolocYAccessor.Get(iX + 1, iY + 1);
431
0
            if (!psTransform->bHasNoData ||
432
0
                (dfGLX_1_0 != psTransform->dfNoDataX &&
433
0
                 dfGLX_0_1 != psTransform->dfNoDataX &&
434
0
                 dfGLX_1_1 != psTransform->dfNoDataX))
435
0
            {
436
0
                const double dfGLX_1_0_adjusted =
437
0
                    ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_0);
438
0
                const double dfGLX_0_1_adjusted =
439
0
                    ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_0_1);
440
0
                const double dfGLX_1_1_adjusted =
441
0
                    ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_1);
442
0
                dfX = (1 - (dfGeoLocLine - iY)) *
443
0
                          (dfGLX_0_0 + (dfGeoLocPixel - iX) *
444
0
                                           (dfGLX_1_0_adjusted - dfGLX_0_0)) +
445
0
                      (dfGeoLocLine - iY) *
446
0
                          (dfGLX_0_1_adjusted +
447
0
                           (dfGeoLocPixel - iX) *
448
0
                               (dfGLX_1_1_adjusted - dfGLX_0_1_adjusted));
449
0
                dfX = UnshiftGeoX(psTransform, dfX);
450
451
0
                dfY = (1 - (dfGeoLocLine - iY)) *
452
0
                          (dfGLY_0_0 +
453
0
                           (dfGeoLocPixel - iX) * (dfGLY_1_0 - dfGLY_0_0)) +
454
0
                      (dfGeoLocLine - iY) *
455
0
                          (dfGLY_0_1 +
456
0
                           (dfGeoLocPixel - iX) * (dfGLY_1_1 - dfGLY_0_1));
457
0
                break;
458
0
            }
459
0
        }
460
461
0
        if (iX == psTransform->nGeoLocXSize - 1 && iX >= 1 &&
462
0
            iY + 1 < psTransform->nGeoLocYSize)
463
0
        {
464
            // If we are after the right edge, then go one pixel left
465
            // and retry
466
0
            iX--;
467
0
            continue;
468
0
        }
469
0
        else if (iY == psTransform->nGeoLocYSize - 1 && iY >= 1 &&
470
0
                 iX + 1 < psTransform->nGeoLocXSize)
471
0
        {
472
            // If we are after the bottom edge, then go one pixel up
473
            // and retry
474
0
            iY--;
475
0
            continue;
476
0
        }
477
0
        else if (iX == psTransform->nGeoLocXSize - 1 &&
478
0
                 iY == psTransform->nGeoLocYSize - 1 && iX >= 1 && iY >= 1)
479
0
        {
480
            // If we are after the right and bottom edge, then go one pixel left
481
            // and up and retry
482
0
            iX--;
483
0
            iY--;
484
0
            continue;
485
0
        }
486
0
        else if (iX + 1 < psTransform->nGeoLocXSize &&
487
0
                 (!psTransform->bHasNoData ||
488
0
                  pAccessors->geolocXAccessor.Get(iX + 1, iY) !=
489
0
                      psTransform->dfNoDataX))
490
0
        {
491
0
            const double dfGLX_1_0 =
492
0
                pAccessors->geolocXAccessor.Get(iX + 1, iY);
493
0
            const double dfGLY_1_0 =
494
0
                pAccessors->geolocYAccessor.Get(iX + 1, iY);
495
0
            dfX =
496
0
                dfGLX_0_0 +
497
0
                (dfGeoLocPixel - iX) *
498
0
                    (ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_0) - dfGLX_0_0);
499
0
            dfX = UnshiftGeoX(psTransform, dfX);
500
0
            dfY = dfGLY_0_0 + (dfGeoLocPixel - iX) * (dfGLY_1_0 - dfGLY_0_0);
501
0
        }
502
0
        else if (iY + 1 < psTransform->nGeoLocYSize &&
503
0
                 (!psTransform->bHasNoData ||
504
0
                  pAccessors->geolocXAccessor.Get(iX, iY + 1) !=
505
0
                      psTransform->dfNoDataX))
506
0
        {
507
0
            const double dfGLX_0_1 =
508
0
                pAccessors->geolocXAccessor.Get(iX, iY + 1);
509
0
            const double dfGLY_0_1 =
510
0
                pAccessors->geolocYAccessor.Get(iX, iY + 1);
511
0
            dfX =
512
0
                dfGLX_0_0 +
513
0
                (dfGeoLocLine - iY) *
514
0
                    (ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_0_1) - dfGLX_0_0);
515
0
            dfX = UnshiftGeoX(psTransform, dfX);
516
0
            dfY = dfGLY_0_0 + (dfGeoLocLine - iY) * (dfGLY_0_1 - dfGLY_0_0);
517
0
        }
518
0
        else
519
0
        {
520
0
            dfX = UnshiftGeoX(psTransform, dfGLX_0_0);
521
0
            dfY = dfGLY_0_0;
522
0
        }
523
0
        break;
524
0
    }
525
0
    return true;
526
0
}
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::PixelLineToXY(GDALGeoLocTransformInfo const*, double, double, double&, double&)
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::PixelLineToXY(GDALGeoLocTransformInfo const*, double, double, double&, double&)
527
528
template <class Accessors>
529
bool GDALGeoLoc<Accessors>::PixelLineToXY(
530
    const GDALGeoLocTransformInfo *psTransform, const int nGeoLocPixel,
531
    const int nGeoLocLine, double &dfX, double &dfY)
532
0
{
533
0
    if (nGeoLocPixel >= 0 && nGeoLocPixel < psTransform->nGeoLocXSize &&
534
0
        nGeoLocLine >= 0 && nGeoLocLine < psTransform->nGeoLocYSize)
535
0
    {
536
0
        auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
537
0
        const double dfGLX =
538
0
            pAccessors->geolocXAccessor.Get(nGeoLocPixel, nGeoLocLine);
539
0
        const double dfGLY =
540
0
            pAccessors->geolocYAccessor.Get(nGeoLocPixel, nGeoLocLine);
541
0
        if (psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX)
542
0
        {
543
0
            return false;
544
0
        }
545
0
        dfX = UnshiftGeoX(psTransform, dfGLX);
546
0
        dfY = dfGLY;
547
0
        return true;
548
0
    }
549
0
    return PixelLineToXY(psTransform, static_cast<double>(nGeoLocPixel),
550
0
                         static_cast<double>(nGeoLocLine), dfX, dfY);
551
0
}
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::PixelLineToXY(GDALGeoLocTransformInfo const*, int, int, double&, double&)
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::PixelLineToXY(GDALGeoLocTransformInfo const*, int, int, double&, double&)
552
553
/************************************************************************/
554
/*                     GDALGeoLoc::ExtractSquare()                      */
555
/************************************************************************/
556
557
template <class Accessors>
558
bool GDALGeoLoc<Accessors>::ExtractSquare(
559
    const GDALGeoLocTransformInfo *psTransform, int nX, int nY, double &dfX_0_0,
560
    double &dfY_0_0, double &dfX_1_0, double &dfY_1_0, double &dfX_0_1,
561
    double &dfY_0_1, double &dfX_1_1, double &dfY_1_1)
562
0
{
563
0
    return PixelLineToXY(psTransform, nX, nY, dfX_0_0, dfY_0_0) &&
564
0
           PixelLineToXY(psTransform, nX + 1, nY, dfX_1_0, dfY_1_0) &&
565
0
           PixelLineToXY(psTransform, nX, nY + 1, dfX_0_1, dfY_0_1) &&
566
0
           PixelLineToXY(psTransform, nX + 1, nY + 1, dfX_1_1, dfY_1_1);
567
0
}
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::ExtractSquare(GDALGeoLocTransformInfo const*, int, int, double&, double&, double&, double&, double&, double&, double&, double&)
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::ExtractSquare(GDALGeoLocTransformInfo const*, int, int, double&, double&, double&, double&, double&, double&, double&, double&)
568
569
bool GDALGeoLocExtractSquare(const GDALGeoLocTransformInfo *psTransform, int nX,
570
                             int nY, double &dfX_0_0, double &dfY_0_0,
571
                             double &dfX_1_0, double &dfY_1_0, double &dfX_0_1,
572
                             double &dfY_0_1, double &dfX_1_1, double &dfY_1_1)
573
0
{
574
0
    if (psTransform->bUseArray)
575
0
    {
576
0
        return GDALGeoLoc<GDALGeoLocCArrayAccessors>::ExtractSquare(
577
0
            psTransform, nX, nY, dfX_0_0, dfY_0_0, dfX_1_0, dfY_1_0, dfX_0_1,
578
0
            dfY_0_1, dfX_1_1, dfY_1_1);
579
0
    }
580
0
    else
581
0
    {
582
0
        return GDALGeoLoc<GDALGeoLocDatasetAccessors>::ExtractSquare(
583
0
            psTransform, nX, nY, dfX_0_0, dfY_0_0, dfX_1_0, dfY_1_0, dfX_0_1,
584
0
            dfY_0_1, dfX_1_1, dfY_1_1);
585
0
    }
586
0
}
587
588
/************************************************************************/
589
/*                        GDALGeoLocTransform()                         */
590
/************************************************************************/
591
592
template <class Accessors>
593
int GDALGeoLoc<Accessors>::Transform(void *pTransformArg, int bDstToSrc,
594
                                     int nPointCount, double *padfX,
595
                                     double *padfY, double * /* padfZ */,
596
                                     int *panSuccess)
597
0
{
598
0
    int bSuccess = TRUE;
599
0
    GDALGeoLocTransformInfo *psTransform =
600
0
        static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
601
602
0
    if (psTransform->bReversed)
603
0
        bDstToSrc = !bDstToSrc;
604
605
0
    const double dfGeorefConventionOffset =
606
0
        psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
607
608
    /* -------------------------------------------------------------------- */
609
    /*      Do original pixel line to target geox/geoy.                     */
610
    /* -------------------------------------------------------------------- */
611
0
    if (!bDstToSrc)
612
0
    {
613
0
        for (int i = 0; i < nPointCount; i++)
614
0
        {
615
0
            if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
616
0
            {
617
0
                bSuccess = FALSE;
618
0
                panSuccess[i] = FALSE;
619
0
                continue;
620
0
            }
621
622
0
            const double dfGeoLocPixel =
623
0
                (padfX[i] - psTransform->dfPIXEL_OFFSET) /
624
0
                    psTransform->dfPIXEL_STEP -
625
0
                dfGeorefConventionOffset;
626
0
            const double dfGeoLocLine =
627
0
                (padfY[i] - psTransform->dfLINE_OFFSET) /
628
0
                    psTransform->dfLINE_STEP -
629
0
                dfGeorefConventionOffset;
630
631
0
            if (!PixelLineToXY(psTransform, dfGeoLocPixel, dfGeoLocLine,
632
0
                               padfX[i], padfY[i]))
633
0
            {
634
0
                bSuccess = FALSE;
635
0
                panSuccess[i] = FALSE;
636
0
                padfX[i] = HUGE_VAL;
637
0
                padfY[i] = HUGE_VAL;
638
0
                continue;
639
0
            }
640
641
0
            if (psTransform->bSwapXY)
642
0
            {
643
0
                std::swap(padfX[i], padfY[i]);
644
0
            }
645
646
0
            panSuccess[i] = TRUE;
647
0
        }
648
0
    }
649
650
    /* -------------------------------------------------------------------- */
651
    /*      geox/geoy to pixel/line using backmap.                          */
652
    /* -------------------------------------------------------------------- */
653
0
    else
654
0
    {
655
0
        if (psTransform->hQuadTree)
656
0
        {
657
0
            GDALGeoLocInverseTransformQuadtree(psTransform, nPointCount, padfX,
658
0
                                               padfY, panSuccess);
659
0
            return TRUE;
660
0
        }
661
662
0
        const bool bGeolocMaxAccuracy = CPLTestBool(
663
0
            CPLGetConfigOption("GDAL_GEOLOC_USE_MAX_ACCURACY", "YES"));
664
665
        // Keep those objects in this outer scope, so they are reused, to
666
        // save memory allocations.
667
0
        OGRPoint oPoint;
668
0
        OGRLinearRing oRing;
669
0
        oRing.setNumPoints(5);
670
671
0
        auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
672
673
0
        for (int i = 0; i < nPointCount; i++)
674
0
        {
675
0
            if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
676
0
            {
677
0
                bSuccess = FALSE;
678
0
                panSuccess[i] = FALSE;
679
0
                continue;
680
0
            }
681
682
0
            if (psTransform->bSwapXY)
683
0
            {
684
0
                std::swap(padfX[i], padfY[i]);
685
0
            }
686
687
0
            const double dfGeoX = padfX[i];
688
0
            const double dfGeoY = padfY[i];
689
690
0
            const double dfBMX =
691
0
                ((padfX[i] - psTransform->adfBackMapGeoTransform[0]) /
692
0
                 psTransform->adfBackMapGeoTransform[1]);
693
0
            const double dfBMY =
694
0
                ((padfY[i] - psTransform->adfBackMapGeoTransform[3]) /
695
0
                 psTransform->adfBackMapGeoTransform[5]);
696
697
0
            if (!(dfBMX >= 0 && dfBMY >= 0 &&
698
0
                  dfBMX + 1 < psTransform->nBackMapWidth &&
699
0
                  dfBMY + 1 < psTransform->nBackMapHeight))
700
0
            {
701
0
                bSuccess = FALSE;
702
0
                panSuccess[i] = FALSE;
703
0
                padfX[i] = HUGE_VAL;
704
0
                padfY[i] = HUGE_VAL;
705
0
                continue;
706
0
            }
707
708
0
            const int iBMX = static_cast<int>(dfBMX);
709
0
            const int iBMY = static_cast<int>(dfBMY);
710
711
0
            const auto fBMX_0_0 = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
712
0
            const auto fBMY_0_0 = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
713
0
            if (fBMX_0_0 == INVALID_BMXY)
714
0
            {
715
0
                bSuccess = FALSE;
716
0
                panSuccess[i] = FALSE;
717
0
                padfX[i] = HUGE_VAL;
718
0
                padfY[i] = HUGE_VAL;
719
0
                continue;
720
0
            }
721
722
0
            const auto fBMX_1_0 =
723
0
                pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY);
724
0
            const auto fBMY_1_0 =
725
0
                pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY);
726
0
            const auto fBMX_0_1 =
727
0
                pAccessors->backMapXAccessor.Get(iBMX, iBMY + 1);
728
0
            const auto fBMY_0_1 =
729
0
                pAccessors->backMapYAccessor.Get(iBMX, iBMY + 1);
730
0
            const auto fBMX_1_1 =
731
0
                pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY + 1);
732
0
            const auto fBMY_1_1 =
733
0
                pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY + 1);
734
0
            if (fBMX_1_0 != INVALID_BMXY && fBMX_0_1 != INVALID_BMXY &&
735
0
                fBMX_1_1 != INVALID_BMXY)
736
0
            {
737
0
                padfX[i] =
738
0
                    (1 - (dfBMY - iBMY)) *
739
0
                        (fBMX_0_0 + (dfBMX - iBMX) * (fBMX_1_0 - fBMX_0_0)) +
740
0
                    (dfBMY - iBMY) *
741
0
                        (fBMX_0_1 + (dfBMX - iBMX) * (fBMX_1_1 - fBMX_0_1));
742
0
                padfY[i] =
743
0
                    (1 - (dfBMY - iBMY)) *
744
0
                        (fBMY_0_0 + (dfBMX - iBMX) * (fBMY_1_0 - fBMY_0_0)) +
745
0
                    (dfBMY - iBMY) *
746
0
                        (fBMY_0_1 + (dfBMX - iBMX) * (fBMY_1_1 - fBMY_0_1));
747
0
            }
748
0
            else if (fBMX_1_0 != INVALID_BMXY)
749
0
            {
750
0
                padfX[i] = fBMX_0_0 + (dfBMX - iBMX) * (fBMX_1_0 - fBMX_0_0);
751
0
                padfY[i] = fBMY_0_0 + (dfBMX - iBMX) * (fBMY_1_0 - fBMY_0_0);
752
0
            }
753
0
            else if (fBMX_0_1 != INVALID_BMXY)
754
0
            {
755
0
                padfX[i] = fBMX_0_0 + (dfBMY - iBMY) * (fBMX_0_1 - fBMX_0_0);
756
0
                padfY[i] = fBMY_0_0 + (dfBMY - iBMY) * (fBMY_0_1 - fBMY_0_0);
757
0
            }
758
0
            else
759
0
            {
760
0
                padfX[i] = fBMX_0_0;
761
0
                padfY[i] = fBMY_0_0;
762
0
            }
763
764
0
            const double dfGeoLocPixel =
765
0
                (padfX[i] - psTransform->dfPIXEL_OFFSET) /
766
0
                    psTransform->dfPIXEL_STEP -
767
0
                dfGeorefConventionOffset;
768
0
            const double dfGeoLocLine =
769
0
                (padfY[i] - psTransform->dfLINE_OFFSET) /
770
0
                    psTransform->dfLINE_STEP -
771
0
                dfGeorefConventionOffset;
772
#if 0
773
            CPLDebug("GEOLOC", "%f %f %f %f", padfX[i], padfY[i], dfGeoLocPixel, dfGeoLocLine);
774
            if( !psTransform->bOriginIsTopLeftCorner )
775
            {
776
                if( dfGeoLocPixel + dfGeorefConventionOffset > psTransform->nGeoLocXSize-1 ||
777
                    dfGeoLocLine + dfGeorefConventionOffset > psTransform->nGeoLocYSize-1 )
778
                {
779
                    panSuccess[i] = FALSE;
780
                    padfX[i] = HUGE_VAL;
781
                    padfY[i] = HUGE_VAL;
782
                    continue;
783
                }
784
            }
785
#endif
786
0
            if (!bGeolocMaxAccuracy)
787
0
            {
788
0
                panSuccess[i] = TRUE;
789
0
                continue;
790
0
            }
791
792
            // Now that we have an approximate solution, identify a matching
793
            // cell in the geolocation array, where we can use inverse bilinear
794
            // interpolation to find the exact solution.
795
796
            // NOTE: if the geolocation array is an affine transformation,
797
            // the approximate solution should match the exact one, if the
798
            // backmap has correctly been built.
799
800
0
            oPoint.setX(dfGeoX);
801
0
            oPoint.setY(dfGeoY);
802
            // The thresholds and radius are rather empirical and have been
803
            // tuned on the product
804
            // S5P_TEST_L2__NO2____20190509T220707_20190509T234837_08137_01_010400_20200220T091343.nc
805
            // that includes the north pole.
806
            // Amended with the test case of
807
            // https://github.com/OSGeo/gdal/issues/5823
808
0
            const int nSearchRadius =
809
0
                psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
810
0
                        fabs(dfGeoY) >= 85
811
0
                    ? 5
812
0
                    : 3;
813
0
            const int nGeoLocPixel =
814
0
                static_cast<int>(std::floor(dfGeoLocPixel));
815
0
            const int nGeoLocLine = static_cast<int>(std::floor(dfGeoLocLine));
816
817
0
            bool bDone = false;
818
            // Using the above approximate nGeoLocPixel, nGeoLocLine, try to
819
            // find a forward cell that includes (dfGeoX, dfGeoY), with an
820
            // increasing search radius, up to nSearchRadius.
821
0
            for (int r = 0; !bDone && r <= nSearchRadius; r++)
822
0
            {
823
0
                for (int iter = 0; !bDone && iter < (r == 0 ? 1 : 8 * r);
824
0
                     ++iter)
825
0
                {
826
                    // For r=1, the below formulas will give the following
827
                    // offsets:
828
                    // (-1,1), (0,1), (1,1), (1,0), (1,-1), (0,-1), (1,-1)
829
0
                    const int sx = (r == 0)         ? 0
830
0
                                   : (iter < 2 * r) ? -r + iter
831
0
                                   : (iter < 4 * r) ? r
832
0
                                   : (iter < 6 * r) ? r - (iter - 4 * r)
833
0
                                                    : -r;
834
0
                    const int sy = (r == 0)         ? 0
835
0
                                   : (iter < 2 * r) ? r
836
0
                                   : (iter < 4 * r) ? r - (iter - 2 * r)
837
0
                                   : (iter < 6 * r) ? -r
838
0
                                                    : -r + (iter - 6 * r);
839
0
                    if (nGeoLocPixel >=
840
0
                            static_cast<int>(psTransform->nGeoLocXSize) - sx ||
841
0
                        nGeoLocLine >=
842
0
                            static_cast<int>(psTransform->nGeoLocYSize) - sy)
843
0
                    {
844
0
                        continue;
845
0
                    }
846
0
                    const int iX = nGeoLocPixel + sx;
847
0
                    const int iY = nGeoLocLine + sy;
848
0
                    if (iX >= -1 || iY >= -1)
849
0
                    {
850
0
                        double x0, y0, x1, y1, x2, y2, x3, y3;
851
852
0
                        if (!PixelLineToXY(psTransform, iX, iY, x0, y0) ||
853
0
                            !PixelLineToXY(psTransform, iX + 1, iY, x2, y2) ||
854
0
                            !PixelLineToXY(psTransform, iX, iY + 1, x1, y1) ||
855
0
                            !PixelLineToXY(psTransform, iX + 1, iY + 1, x3, y3))
856
0
                        {
857
0
                            continue;
858
0
                        }
859
860
0
                        int nIters = 1;
861
                        // For a bounding box crossing the anti-meridian, check
862
                        // both around -180 and +180 deg.
863
0
                        if (psTransform
864
0
                                ->bGeographicSRSWithMinus180Plus180LongRange &&
865
0
                            std::fabs(x0) > 170 && std::fabs(x1) > 170 &&
866
0
                            std::fabs(x2) > 170 && std::fabs(x3) > 170 &&
867
0
                            (std::fabs(x1 - x0) > 180 ||
868
0
                             std::fabs(x2 - x0) > 180 ||
869
0
                             std::fabs(x3 - x0) > 180))
870
0
                        {
871
0
                            nIters = 2;
872
0
                            if (x0 > 0)
873
0
                                x0 -= 360;
874
0
                            if (x1 > 0)
875
0
                                x1 -= 360;
876
0
                            if (x2 > 0)
877
0
                                x2 -= 360;
878
0
                            if (x3 > 0)
879
0
                                x3 -= 360;
880
0
                        }
881
0
                        for (int iIter = 0; !bDone && iIter < nIters; ++iIter)
882
0
                        {
883
0
                            if (iIter == 1)
884
0
                            {
885
0
                                x0 += 360;
886
0
                                x1 += 360;
887
0
                                x2 += 360;
888
0
                                x3 += 360;
889
0
                            }
890
0
                            oRing.setPoint(0, x0, y0);
891
0
                            oRing.setPoint(1, x2, y2);
892
0
                            oRing.setPoint(2, x3, y3);
893
0
                            oRing.setPoint(3, x1, y1);
894
0
                            oRing.setPoint(4, x0, y0);
895
0
                            if (oRing.isPointInRing(&oPoint) ||
896
0
                                oRing.isPointOnRingBoundary(&oPoint))
897
0
                            {
898
0
                                double dfX = static_cast<double>(iX);
899
0
                                double dfY = static_cast<double>(iY);
900
0
                                GDALInverseBilinearInterpolation(
901
0
                                    dfGeoX, dfGeoY, x0, y0, x1, y1, x2, y2, x3,
902
0
                                    y3, dfX, dfY);
903
904
0
                                dfX = (dfX + dfGeorefConventionOffset) *
905
0
                                          psTransform->dfPIXEL_STEP +
906
0
                                      psTransform->dfPIXEL_OFFSET;
907
0
                                dfY = (dfY + dfGeorefConventionOffset) *
908
0
                                          psTransform->dfLINE_STEP +
909
0
                                      psTransform->dfLINE_OFFSET;
910
911
#ifdef DEBUG_GEOLOC_REALLY_VERBOSE
912
                                CPLDebug("GEOLOC",
913
                                         "value before adjustment: %f %f, "
914
                                         "after adjustment: %f %f",
915
                                         padfX[i], padfY[i], dfX, dfY);
916
#endif
917
918
0
                                padfX[i] = dfX;
919
0
                                padfY[i] = dfY;
920
921
0
                                bDone = true;
922
0
                            }
923
0
                        }
924
0
                    }
925
0
                }
926
0
            }
927
0
            if (!bDone)
928
0
            {
929
0
                bSuccess = FALSE;
930
0
                panSuccess[i] = FALSE;
931
0
                padfX[i] = HUGE_VAL;
932
0
                padfY[i] = HUGE_VAL;
933
0
                continue;
934
0
            }
935
936
0
            panSuccess[i] = TRUE;
937
0
        }
938
0
    }
939
940
0
    return bSuccess;
941
0
}
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::Transform(void*, int, int, double*, double*, double*, int*)
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::Transform(void*, int, int, double*, double*, double*, int*)
942
943
/*! @endcond */
944
945
/************************************************************************/
946
/*                  GDALInverseBilinearInterpolation()                  */
947
/************************************************************************/
948
949
// (i,j) before the call should correspond to the input coordinates that give
950
// (x0,y0) as output of the forward interpolation
951
// After the call it will be updated to the input coordinates that give (x,y)
952
// This assumes that (x,y) is within the polygon formed by
953
// (x0, y0), (x2, y2), (x3, y3), (x1, y1), (x0, y0)
954
void GDALInverseBilinearInterpolation(const double x, const double y,
955
                                      const double x0, const double y0,
956
                                      const double x1, const double y1,
957
                                      const double x2, const double y2,
958
                                      const double x3, const double y3,
959
                                      double &i, double &j)
960
0
{
961
    // Exact inverse bilinear interpolation method.
962
    // Maths from https://stackoverflow.com/a/812077
963
964
0
    const double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
965
0
    const double B = (((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3)) +
966
0
                      ((x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2))) /
967
0
                     2;
968
0
    const double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);
969
0
    const double denom = A - 2 * B + C;
970
0
    double s;
971
0
    const double magnitudeOfValues = fabs(A) + fabs(B) + fabs(C);
972
0
    if (fabs(denom) <= 1e-12 * magnitudeOfValues)
973
0
    {
974
        // Happens typically when the x_i,y_i points form a rectangle
975
        // Can also happen when they form a triangle.
976
0
        s = A / (A - C);
977
0
    }
978
0
    else
979
0
    {
980
0
        const double sqrtTerm = sqrt(B * B - A * C);
981
0
        const double s1 = ((A - B) + sqrtTerm) / denom;
982
0
        const double s2 = ((A - B) - sqrtTerm) / denom;
983
0
        if (s1 < 0 || s1 > 1)
984
0
            s = s2;
985
0
        else
986
0
            s = s1;
987
0
    }
988
989
0
    const double t_denom_x = (1 - s) * (x0 - x2) + s * (x1 - x3);
990
0
    if (fabs(t_denom_x) > 1e-12 * magnitudeOfValues)
991
0
    {
992
0
        i += ((1 - s) * (x0 - x) + s * (x1 - x)) / t_denom_x;
993
0
    }
994
0
    else
995
0
    {
996
0
        const double t_denom_y = (1 - s) * (y0 - y2) + s * (y1 - y3);
997
0
        if (fabs(t_denom_y) > 1e-12 * magnitudeOfValues)
998
0
        {
999
0
            i += ((1 - s) * (y0 - y) + s * (y1 - y)) / t_denom_y;
1000
0
        }
1001
0
    }
1002
1003
0
    j += s;
1004
0
}
1005
1006
/************************************************************************/
1007
/*                       GeoLocGenerateBackMap()                        */
1008
/************************************************************************/
1009
1010
/*! @cond Doxygen_Suppress */
1011
1012
template <class Accessors>
1013
bool GDALGeoLoc<Accessors>::GenerateBackMap(
1014
    GDALGeoLocTransformInfo *psTransform)
1015
1016
0
{
1017
0
    CPLDebug("GEOLOC", "Starting backmap generation");
1018
0
    const int nXSize = psTransform->nGeoLocXSize;
1019
0
    const int nYSize = psTransform->nGeoLocYSize;
1020
1021
    /* -------------------------------------------------------------------- */
1022
    /*      Decide on resolution for backmap.  We aim for slightly          */
1023
    /*      higher resolution than the source but we can't easily           */
1024
    /*      establish how much dead space there is in the backmap, so it    */
1025
    /*      is approximate.                                                 */
1026
    /* -------------------------------------------------------------------- */
1027
0
    const double dfTargetPixels =
1028
0
        static_cast<double>(nXSize) * nYSize * psTransform->dfOversampleFactor;
1029
0
    const double dfPixelSizeSquare =
1030
0
        sqrt((psTransform->dfMaxX - psTransform->dfMinX) *
1031
0
             (psTransform->dfMaxY - psTransform->dfMinY) / dfTargetPixels);
1032
0
    if (dfPixelSizeSquare == 0.0)
1033
0
    {
1034
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid pixel size for backmap");
1035
0
        return false;
1036
0
    }
1037
1038
0
    const double dfMinX = psTransform->dfMinX - dfPixelSizeSquare / 2.0;
1039
0
    const double dfMaxX = psTransform->dfMaxX + dfPixelSizeSquare / 2.0;
1040
0
    const double dfMaxY = psTransform->dfMaxY + dfPixelSizeSquare / 2.0;
1041
0
    const double dfMinY = psTransform->dfMinY - dfPixelSizeSquare / 2.0;
1042
0
    const double dfBMXSize = std::ceil((dfMaxX - dfMinX) / dfPixelSizeSquare);
1043
0
    const double dfBMYSize = std::ceil((dfMaxY - dfMinY) / dfPixelSizeSquare);
1044
1045
    // +2 : +1 due to afterwards nBMXSize++, and another +1 as security margin
1046
    // for other computations.
1047
0
    if (!(dfBMXSize > 0 && dfBMXSize + 2 < INT_MAX) ||
1048
0
        !(dfBMYSize > 0 && dfBMYSize + 2 < INT_MAX))
1049
0
    {
1050
0
        CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %f x %f",
1051
0
                 dfBMXSize, dfBMYSize);
1052
0
        return false;
1053
0
    }
1054
1055
0
    int nBMXSize = static_cast<int>(dfBMXSize);
1056
0
    int nBMYSize = static_cast<int>(dfBMYSize);
1057
1058
0
    if (static_cast<size_t>(1 + nBMYSize) >
1059
0
        std::numeric_limits<size_t>::max() / static_cast<size_t>(1 + nBMXSize))
1060
0
    {
1061
0
        CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %f x %f",
1062
0
                 dfBMXSize, dfBMYSize);
1063
0
        return false;
1064
0
    }
1065
1066
0
    const double dfPixelXSize = (dfMaxX - dfMinX) / nBMXSize;
1067
0
    const double dfPixelYSize = (dfMaxY - dfMinY) / nBMYSize;
1068
1069
    // Extra pixel for right-edge and bottom-edge extensions in TOP_LEFT_CORNER
1070
    // convention.
1071
0
    nBMXSize++;
1072
0
    nBMYSize++;
1073
0
    psTransform->nBackMapWidth = nBMXSize;
1074
0
    psTransform->nBackMapHeight = nBMYSize;
1075
1076
0
    psTransform->adfBackMapGeoTransform[0] = dfMinX;
1077
0
    psTransform->adfBackMapGeoTransform[1] = dfPixelXSize;
1078
0
    psTransform->adfBackMapGeoTransform[2] = 0.0;
1079
0
    psTransform->adfBackMapGeoTransform[3] = dfMaxY;
1080
0
    psTransform->adfBackMapGeoTransform[4] = 0.0;
1081
0
    psTransform->adfBackMapGeoTransform[5] = -dfPixelYSize;
1082
1083
    /* -------------------------------------------------------------------- */
1084
    /*      Allocate backmap.                                               */
1085
    /* -------------------------------------------------------------------- */
1086
0
    auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
1087
0
    if (!pAccessors->AllocateBackMap())
1088
0
        return false;
1089
1090
0
    const double dfGeorefConventionOffset =
1091
0
        psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
1092
1093
0
    const auto UpdateBackmap =
1094
0
        [&](int iBMX, int iBMY, double dfX, double dfY, double tempwt)
1095
0
    {
1096
0
        const auto fBMX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1097
0
        const auto fBMY = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1098
0
        const float fUpdatedBMX =
1099
0
            fBMX +
1100
0
            static_cast<float>(tempwt * ((dfX + dfGeorefConventionOffset) *
1101
0
                                             psTransform->dfPIXEL_STEP +
1102
0
                                         psTransform->dfPIXEL_OFFSET));
1103
0
        const float fUpdatedBMY =
1104
0
            fBMY +
1105
0
            static_cast<float>(tempwt * ((dfY + dfGeorefConventionOffset) *
1106
0
                                             psTransform->dfLINE_STEP +
1107
0
                                         psTransform->dfLINE_OFFSET));
1108
0
        const float fUpdatedWeight =
1109
0
            pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) +
1110
0
            static_cast<float>(tempwt);
1111
1112
        // Only update the backmap if the updated averaged value results in a
1113
        // geoloc position that isn't too different from the original one.
1114
        // (there's no guarantee that if padfGeoLocX[i] ~= padfGeoLoc[j],
1115
        //  padfGeoLoc[alpha * i + (1 - alpha) * j] ~= padfGeoLoc[i] )
1116
0
        if (fUpdatedWeight > 0)
1117
0
        {
1118
0
            const float fX = fUpdatedBMX / fUpdatedWeight;
1119
0
            const float fY = fUpdatedBMY / fUpdatedWeight;
1120
0
            const double dfGeoLocPixel =
1121
0
                (fX - psTransform->dfPIXEL_OFFSET) / psTransform->dfPIXEL_STEP -
1122
0
                dfGeorefConventionOffset;
1123
0
            const double dfGeoLocLine =
1124
0
                (fY - psTransform->dfLINE_OFFSET) / psTransform->dfLINE_STEP -
1125
0
                dfGeorefConventionOffset;
1126
0
            int iXAvg = static_cast<int>(std::max(0.0, dfGeoLocPixel));
1127
0
            iXAvg = std::min(iXAvg, psTransform->nGeoLocXSize - 1);
1128
0
            int iYAvg = static_cast<int>(std::max(0.0, dfGeoLocLine));
1129
0
            iYAvg = std::min(iYAvg, psTransform->nGeoLocYSize - 1);
1130
0
            const double dfGLX = pAccessors->geolocXAccessor.Get(iXAvg, iYAvg);
1131
0
            const double dfGLY = pAccessors->geolocYAccessor.Get(iXAvg, iYAvg);
1132
1133
0
            const unsigned iX = static_cast<unsigned>(dfX);
1134
0
            const unsigned iY = static_cast<unsigned>(dfY);
1135
0
            if (!(psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX) &&
1136
0
                ((iX >= static_cast<unsigned>(nXSize - 1) ||
1137
0
                  iY >= static_cast<unsigned>(nYSize - 1)) ||
1138
0
                 (fabs(dfGLX - pAccessors->geolocXAccessor.Get(iX, iY)) <=
1139
0
                      2 * dfPixelXSize &&
1140
0
                  fabs(dfGLY - pAccessors->geolocYAccessor.Get(iX, iY)) <=
1141
0
                      2 * dfPixelYSize)))
1142
0
            {
1143
0
                pAccessors->backMapXAccessor.Set(iBMX, iBMY, fUpdatedBMX);
1144
0
                pAccessors->backMapYAccessor.Set(iBMX, iBMY, fUpdatedBMY);
1145
0
                pAccessors->backMapWeightAccessor.Set(iBMX, iBMY,
1146
0
                                                      fUpdatedWeight);
1147
0
            }
1148
0
        }
1149
0
    };
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::GenerateBackMap(GDALGeoLocTransformInfo*)::{lambda(int, int, double, double, double)#1}::operator()(int, int, double, double, double) const
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::GenerateBackMap(GDALGeoLocTransformInfo*)::{lambda(int, int, double, double, double)#1}::operator()(int, int, double, double, double) const
1150
1151
    // Keep those objects in this outer scope, so they are reused, to
1152
    // save memory allocations.
1153
0
    OGRPoint oPoint;
1154
0
    OGRLinearRing oRing;
1155
0
    oRing.setNumPoints(5);
1156
1157
    /* -------------------------------------------------------------------- */
1158
    /*      Run through the whole geoloc array forward projecting and       */
1159
    /*      pushing into the backmap.                                       */
1160
    /* -------------------------------------------------------------------- */
1161
1162
    // Iterate over the (i,j) pixel space of the geolocation array, in a
1163
    // sufficiently dense way that if the geolocation array expressed an affine
1164
    // transformation, we would hit every node of the backmap.
1165
0
    const double dfStep = 1. / psTransform->dfOversampleFactor;
1166
1167
0
    constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
1168
0
    const int nYBlocks = DIV_ROUND_UP(nYSize, TILE_SIZE);
1169
0
    const int nXBlocks = DIV_ROUND_UP(nXSize, TILE_SIZE);
1170
1171
    // First compute for each block the start end ending floating-point
1172
    // pixel/line values
1173
0
    std::vector<std::pair<double, double>> yStartEnd(nYBlocks + 1);
1174
0
    std::vector<std::pair<double, double>> xStartEnd(nXBlocks + 1);
1175
1176
0
    {
1177
0
        int iYBlockLast = -1;
1178
0
        double dfY = -dfStep;
1179
0
        for (; dfY <= static_cast<double>(nYSize) + 2 * dfStep; dfY += dfStep)
1180
0
        {
1181
0
            const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1182
0
            if (iYBlock != iYBlockLast)
1183
0
            {
1184
0
                CPLAssert(iYBlock == iYBlockLast + 1);
1185
0
                if (iYBlockLast >= 0)
1186
0
                    yStartEnd[iYBlockLast].second = dfY + dfStep / 10;
1187
0
                yStartEnd[iYBlock].first = dfY;
1188
0
                iYBlockLast = iYBlock;
1189
0
            }
1190
0
        }
1191
0
        const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1192
0
        yStartEnd[iYBlock].second = dfY + dfStep / 10;
1193
0
    }
1194
1195
0
    {
1196
0
        int iXBlockLast = -1;
1197
0
        double dfX = -dfStep;
1198
0
        for (; dfX <= static_cast<double>(nXSize) + 2 * dfStep; dfX += dfStep)
1199
0
        {
1200
0
            const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1201
0
            if (iXBlock != iXBlockLast)
1202
0
            {
1203
0
                CPLAssert(iXBlock == iXBlockLast + 1);
1204
0
                if (iXBlockLast >= 0)
1205
0
                    xStartEnd[iXBlockLast].second = dfX + dfStep / 10;
1206
0
                xStartEnd[iXBlock].first = dfX;
1207
0
                iXBlockLast = iXBlock;
1208
0
            }
1209
0
        }
1210
0
        const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1211
0
        xStartEnd[iXBlock].second = dfX + dfStep / 10;
1212
0
    }
1213
1214
0
    for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
1215
0
    {
1216
0
        for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
1217
0
        {
1218
#if 0
1219
        CPLDebug("Process geoloc block (y=%d,x=%d) for y in [%f, %f] and x in [%f, %f]",
1220
                 iYBlock, iXBlock,
1221
                 yStartEnd[iYBlock].first, yStartEnd[iYBlock].second,
1222
                 xStartEnd[iXBlock].first, xStartEnd[iXBlock].second);
1223
#endif
1224
0
            for (double dfY = yStartEnd[iYBlock].first;
1225
0
                 dfY < yStartEnd[iYBlock].second; dfY += dfStep)
1226
0
            {
1227
0
                for (double dfX = xStartEnd[iXBlock].first;
1228
0
                     dfX < xStartEnd[iXBlock].second; dfX += dfStep)
1229
0
                {
1230
                    // Use forward geolocation array interpolation to compute
1231
                    // the georeferenced position corresponding to (dfX, dfY)
1232
0
                    double dfGeoLocX;
1233
0
                    double dfGeoLocY;
1234
0
                    if (!PixelLineToXY(psTransform, dfX, dfY, dfGeoLocX,
1235
0
                                       dfGeoLocY))
1236
0
                        continue;
1237
1238
                    // Compute the floating point coordinates in the pixel space
1239
                    // of the backmap
1240
0
                    const double dBMX = static_cast<double>(
1241
0
                        (dfGeoLocX - dfMinX) / dfPixelXSize);
1242
1243
0
                    const double dBMY = static_cast<double>(
1244
0
                        (dfMaxY - dfGeoLocY) / dfPixelYSize);
1245
1246
                    // Get top left index by truncation
1247
0
                    const int iBMX = static_cast<int>(std::floor(dBMX));
1248
0
                    const int iBMY = static_cast<int>(std::floor(dBMY));
1249
1250
0
                    if (iBMX >= 0 && iBMX < nBMXSize && iBMY >= 0 &&
1251
0
                        iBMY < nBMYSize)
1252
0
                    {
1253
                        // Compute the georeferenced position of the top-left
1254
                        // index of the backmap
1255
0
                        double dfGeoX = dfMinX + iBMX * dfPixelXSize;
1256
0
                        const double dfGeoY = dfMaxY - iBMY * dfPixelYSize;
1257
1258
0
                        bool bMatchingGeoLocCellFound = false;
1259
1260
0
                        const int nOuterIters =
1261
0
                            psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
1262
0
                                    fabs(dfGeoX) >= 180
1263
0
                                ? 2
1264
0
                                : 1;
1265
1266
0
                        for (int iOuterIter = 0; iOuterIter < nOuterIters;
1267
0
                             ++iOuterIter)
1268
0
                        {
1269
0
                            if (iOuterIter == 1 && dfGeoX >= 180)
1270
0
                                dfGeoX -= 360;
1271
0
                            else if (iOuterIter == 1 && dfGeoX <= -180)
1272
0
                                dfGeoX += 360;
1273
1274
                            // Identify a cell (quadrilateral in georeferenced
1275
                            // space) in the geolocation array in which dfGeoX,
1276
                            // dfGeoY falls into.
1277
0
                            oPoint.setX(dfGeoX);
1278
0
                            oPoint.setY(dfGeoY);
1279
0
                            const int nX = static_cast<int>(std::floor(dfX));
1280
0
                            const int nY = static_cast<int>(std::floor(dfY));
1281
0
                            for (int sx = -1;
1282
0
                                 !bMatchingGeoLocCellFound && sx <= 0; sx++)
1283
0
                            {
1284
0
                                for (int sy = -1;
1285
0
                                     !bMatchingGeoLocCellFound && sy <= 0; sy++)
1286
0
                                {
1287
0
                                    const int pixel = nX + sx;
1288
0
                                    const int line = nY + sy;
1289
0
                                    double x0, y0, x1, y1, x2, y2, x3, y3;
1290
0
                                    if (!PixelLineToXY(psTransform, pixel, line,
1291
0
                                                       x0, y0) ||
1292
0
                                        !PixelLineToXY(psTransform, pixel + 1,
1293
0
                                                       line, x2, y2) ||
1294
0
                                        !PixelLineToXY(psTransform, pixel,
1295
0
                                                       line + 1, x1, y1) ||
1296
0
                                        !PixelLineToXY(psTransform, pixel + 1,
1297
0
                                                       line + 1, x3, y3))
1298
0
                                    {
1299
0
                                        break;
1300
0
                                    }
1301
1302
0
                                    int nIters = 1;
1303
0
                                    if (psTransform
1304
0
                                            ->bGeographicSRSWithMinus180Plus180LongRange &&
1305
0
                                        std::fabs(x0) > 170 &&
1306
0
                                        std::fabs(x1) > 170 &&
1307
0
                                        std::fabs(x2) > 170 &&
1308
0
                                        std::fabs(x3) > 170 &&
1309
0
                                        (std::fabs(x1 - x0) > 180 ||
1310
0
                                         std::fabs(x2 - x0) > 180 ||
1311
0
                                         std::fabs(x3 - x0) > 180))
1312
0
                                    {
1313
0
                                        nIters = 2;
1314
0
                                        if (x0 > 0)
1315
0
                                            x0 -= 360;
1316
0
                                        if (x1 > 0)
1317
0
                                            x1 -= 360;
1318
0
                                        if (x2 > 0)
1319
0
                                            x2 -= 360;
1320
0
                                        if (x3 > 0)
1321
0
                                            x3 -= 360;
1322
0
                                    }
1323
0
                                    for (int iIter = 0; iIter < nIters; ++iIter)
1324
0
                                    {
1325
0
                                        if (iIter == 1)
1326
0
                                        {
1327
0
                                            x0 += 360;
1328
0
                                            x1 += 360;
1329
0
                                            x2 += 360;
1330
0
                                            x3 += 360;
1331
0
                                        }
1332
1333
0
                                        oRing.setPoint(0, x0, y0);
1334
0
                                        oRing.setPoint(1, x2, y2);
1335
0
                                        oRing.setPoint(2, x3, y3);
1336
0
                                        oRing.setPoint(3, x1, y1);
1337
0
                                        oRing.setPoint(4, x0, y0);
1338
0
                                        if (oRing.isPointInRing(&oPoint) ||
1339
0
                                            oRing.isPointOnRingBoundary(
1340
0
                                                &oPoint))
1341
0
                                        {
1342
0
                                            bMatchingGeoLocCellFound = true;
1343
0
                                            double dfBMXValue = pixel;
1344
0
                                            double dfBMYValue = line;
1345
0
                                            GDALInverseBilinearInterpolation(
1346
0
                                                dfGeoX, dfGeoY, x0, y0, x1, y1,
1347
0
                                                x2, y2, x3, y3, dfBMXValue,
1348
0
                                                dfBMYValue);
1349
1350
0
                                            dfBMXValue =
1351
0
                                                (dfBMXValue +
1352
0
                                                 dfGeorefConventionOffset) *
1353
0
                                                    psTransform->dfPIXEL_STEP +
1354
0
                                                psTransform->dfPIXEL_OFFSET;
1355
0
                                            dfBMYValue =
1356
0
                                                (dfBMYValue +
1357
0
                                                 dfGeorefConventionOffset) *
1358
0
                                                    psTransform->dfLINE_STEP +
1359
0
                                                psTransform->dfLINE_OFFSET;
1360
1361
0
                                            pAccessors->backMapXAccessor.Set(
1362
0
                                                iBMX, iBMY,
1363
0
                                                static_cast<float>(dfBMXValue));
1364
0
                                            pAccessors->backMapYAccessor.Set(
1365
0
                                                iBMX, iBMY,
1366
0
                                                static_cast<float>(dfBMYValue));
1367
0
                                            pAccessors->backMapWeightAccessor
1368
0
                                                .Set(iBMX, iBMY, 1.0f);
1369
0
                                        }
1370
0
                                    }
1371
0
                                }
1372
0
                            }
1373
0
                        }
1374
0
                        if (bMatchingGeoLocCellFound)
1375
0
                            continue;
1376
0
                    }
1377
1378
                    // We will end up here in non-nominal cases, with nodata,
1379
                    // holes, etc.
1380
1381
                    // Check if the center is in range
1382
0
                    if (iBMX < -1 || iBMY < -1 || iBMX > nBMXSize ||
1383
0
                        iBMY > nBMYSize)
1384
0
                        continue;
1385
1386
0
                    const double fracBMX = dBMX - iBMX;
1387
0
                    const double fracBMY = dBMY - iBMY;
1388
1389
                    // Check logic for top left pixel
1390
0
                    if ((iBMX >= 0) && (iBMY >= 0) && (iBMX < nBMXSize) &&
1391
0
                        (iBMY < nBMYSize) &&
1392
0
                        pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) !=
1393
0
                            1.0f)
1394
0
                    {
1395
0
                        const double tempwt = (1.0 - fracBMX) * (1.0 - fracBMY);
1396
0
                        UpdateBackmap(iBMX, iBMY, dfX, dfY, tempwt);
1397
0
                    }
1398
1399
                    // Check logic for top right pixel
1400
0
                    if ((iBMY >= 0) && (iBMX + 1 < nBMXSize) &&
1401
0
                        (iBMY < nBMYSize) &&
1402
0
                        pAccessors->backMapWeightAccessor.Get(iBMX + 1, iBMY) !=
1403
0
                            1.0f)
1404
0
                    {
1405
0
                        const double tempwt = fracBMX * (1.0 - fracBMY);
1406
0
                        UpdateBackmap(iBMX + 1, iBMY, dfX, dfY, tempwt);
1407
0
                    }
1408
1409
                    // Check logic for bottom right pixel
1410
0
                    if ((iBMX + 1 < nBMXSize) && (iBMY + 1 < nBMYSize) &&
1411
0
                        pAccessors->backMapWeightAccessor.Get(iBMX + 1,
1412
0
                                                              iBMY + 1) != 1.0f)
1413
0
                    {
1414
0
                        const double tempwt = fracBMX * fracBMY;
1415
0
                        UpdateBackmap(iBMX + 1, iBMY + 1, dfX, dfY, tempwt);
1416
0
                    }
1417
1418
                    // Check logic for bottom left pixel
1419
0
                    if ((iBMX >= 0) && (iBMX < nBMXSize) &&
1420
0
                        (iBMY + 1 < nBMYSize) &&
1421
0
                        pAccessors->backMapWeightAccessor.Get(iBMX, iBMY + 1) !=
1422
0
                            1.0f)
1423
0
                    {
1424
0
                        const double tempwt = (1.0 - fracBMX) * fracBMY;
1425
0
                        UpdateBackmap(iBMX, iBMY + 1, dfX, dfY, tempwt);
1426
0
                    }
1427
0
                }
1428
0
            }
1429
0
        }
1430
0
    }
1431
1432
    // Each pixel in the backmap may have multiple entries.
1433
    // We now go in average it out using the weights
1434
0
    START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, (void)0,
1435
0
                         iXStart, iXEnd, iYStart, iYEnd)
1436
0
    {
1437
0
        for (int iY = iYStart; iY < iYEnd; ++iY)
1438
0
        {
1439
0
            for (int iX = iXStart; iX < iXEnd; ++iX)
1440
0
            {
1441
                // Check if pixel was only touch during neighbor scan
1442
                // But no real weight was added as source point matched
1443
                // backmap grid node
1444
0
                const auto weight =
1445
0
                    pAccessors->backMapWeightAccessor.Get(iX, iY);
1446
0
                if (weight > 0)
1447
0
                {
1448
0
                    pAccessors->backMapXAccessor.Set(
1449
0
                        iX, iY,
1450
0
                        pAccessors->backMapXAccessor.Get(iX, iY) / weight);
1451
0
                    pAccessors->backMapYAccessor.Set(
1452
0
                        iX, iY,
1453
0
                        pAccessors->backMapYAccessor.Get(iX, iY) / weight);
1454
0
                }
1455
0
                else
1456
0
                {
1457
0
                    pAccessors->backMapXAccessor.Set(iX, iY, INVALID_BMXY);
1458
0
                    pAccessors->backMapYAccessor.Set(iX, iY, INVALID_BMXY);
1459
0
                }
1460
0
            }
1461
0
        }
1462
0
    }
1463
0
    END_ITER_PER_BLOCK
1464
1465
0
    pAccessors->FreeWghtsBackMap();
1466
1467
    // Fill holes in backmap
1468
0
    auto poBackmapDS = pAccessors->GetBackmapDataset();
1469
1470
0
    pAccessors->FlushBackmapCaches();
1471
1472
#ifdef DEBUG_GEOLOC
1473
    if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1474
    {
1475
        poBackmapDS->SetGeoTransform(psTransform->adfBackMapGeoTransform);
1476
        GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1477
                                 "/tmp/geoloc_before_fill.tif", poBackmapDS,
1478
                                 false, nullptr, nullptr, nullptr));
1479
    }
1480
#endif
1481
1482
0
    constexpr double dfMaxSearchDist = 3.0;
1483
0
    constexpr int nSmoothingIterations = 1;
1484
0
    for (int i = 1; i <= 2; i++)
1485
0
    {
1486
0
        GDALFillNodata(GDALRasterBand::ToHandle(poBackmapDS->GetRasterBand(i)),
1487
0
                       nullptr, dfMaxSearchDist,
1488
0
                       0,  // unused parameter
1489
0
                       nSmoothingIterations, nullptr, nullptr, nullptr);
1490
0
    }
1491
1492
#ifdef DEBUG_GEOLOC
1493
    if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1494
    {
1495
        GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1496
                                 "/tmp/geoloc_after_fill.tif", poBackmapDS,
1497
                                 false, nullptr, nullptr, nullptr));
1498
    }
1499
#endif
1500
1501
    // A final hole filling logic, proceeding line by line, and filling
1502
    // holes when the backmap values surrounding the hole are close enough.
1503
0
    struct LastValidStruct
1504
0
    {
1505
0
        int iX = -1;
1506
0
        float bmX = 0;
1507
0
    };
1508
1509
0
    std::vector<LastValidStruct> lastValid(TILE_SIZE);
1510
0
    const auto reinitLine = [&lastValid]()
1511
0
    {
1512
0
        const size_t nSize = lastValid.size();
1513
0
        lastValid.clear();
1514
0
        lastValid.resize(nSize);
1515
0
    };
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::GenerateBackMap(GDALGeoLocTransformInfo*)::{lambda()#1}::operator()() const
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::GenerateBackMap(GDALGeoLocTransformInfo*)::{lambda()#1}::operator()() const
1516
0
    START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, reinitLine(),
1517
0
                         iXStart, iXEnd, iYStart, iYEnd)
1518
0
    {
1519
0
        const int iYCount = iYEnd - iYStart;
1520
0
        for (int iYIter = 0; iYIter < iYCount; ++iYIter)
1521
0
        {
1522
0
            int iLastValidIX = lastValid[iYIter].iX;
1523
0
            float bmXLastValid = lastValid[iYIter].bmX;
1524
0
            const int iBMY = iYStart + iYIter;
1525
0
            for (int iBMX = iXStart; iBMX < iXEnd; ++iBMX)
1526
0
            {
1527
0
                const float bmX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1528
0
                if (bmX == INVALID_BMXY)
1529
0
                    continue;
1530
0
                if (iLastValidIX != -1 && iBMX > iLastValidIX + 1 &&
1531
0
                    fabs(bmX - bmXLastValid) <= 2)
1532
0
                {
1533
0
                    const float bmY =
1534
0
                        pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1535
0
                    const float bmYLastValid =
1536
0
                        pAccessors->backMapYAccessor.Get(iLastValidIX, iBMY);
1537
0
                    if (fabs(bmY - bmYLastValid) <= 2)
1538
0
                    {
1539
0
                        for (int iBMXInner = iLastValidIX + 1; iBMXInner < iBMX;
1540
0
                             ++iBMXInner)
1541
0
                        {
1542
0
                            const float alpha =
1543
0
                                static_cast<float>(iBMXInner - iLastValidIX) /
1544
0
                                (iBMX - iLastValidIX);
1545
0
                            pAccessors->backMapXAccessor.Set(
1546
0
                                iBMXInner, iBMY,
1547
0
                                (1.0f - alpha) * bmXLastValid + alpha * bmX);
1548
0
                            pAccessors->backMapYAccessor.Set(
1549
0
                                iBMXInner, iBMY,
1550
0
                                (1.0f - alpha) * bmYLastValid + alpha * bmY);
1551
0
                        }
1552
0
                    }
1553
0
                }
1554
0
                iLastValidIX = iBMX;
1555
0
                bmXLastValid = bmX;
1556
0
            }
1557
0
            lastValid[iYIter].iX = iLastValidIX;
1558
0
            lastValid[iYIter].bmX = bmXLastValid;
1559
0
        }
1560
0
    }
1561
0
    END_ITER_PER_BLOCK
1562
1563
#ifdef DEBUG_GEOLOC
1564
    if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1565
    {
1566
        pAccessors->FlushBackmapCaches();
1567
1568
        GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1569
                                 "/tmp/geoloc_after_line_fill.tif", poBackmapDS,
1570
                                 false, nullptr, nullptr, nullptr));
1571
    }
1572
#endif
1573
1574
0
    pAccessors->ReleaseBackmapDataset(poBackmapDS);
1575
0
    CPLDebug("GEOLOC", "Ending backmap generation");
1576
1577
0
    return true;
1578
0
}
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocCArrayAccessors>::GenerateBackMap(GDALGeoLocTransformInfo*)
Unexecuted instantiation: GDALGeoLoc<GDALGeoLocDatasetAccessors>::GenerateBackMap(GDALGeoLocTransformInfo*)
1579
1580
/*! @endcond */
1581
1582
/************************************************************************/
1583
/*                       GDALGeoLocRescale()                            */
1584
/************************************************************************/
1585
1586
static void GDALGeoLocRescale(char **&papszMD, const char *pszItem,
1587
                              double dfRatio, double dfDefaultVal)
1588
0
{
1589
0
    const double dfVal =
1590
0
        dfRatio * CPLAtofM(CSLFetchNameValueDef(
1591
0
                      papszMD, pszItem, CPLSPrintf("%.17g", dfDefaultVal)));
1592
1593
0
    papszMD = CSLSetNameValue(papszMD, pszItem, CPLSPrintf("%.17g", dfVal));
1594
0
}
1595
1596
/************************************************************************/
1597
/*                 GDALCreateSimilarGeoLocTransformer()                 */
1598
/************************************************************************/
1599
1600
static void *GDALCreateSimilarGeoLocTransformer(void *hTransformArg,
1601
                                                double dfRatioX,
1602
                                                double dfRatioY)
1603
0
{
1604
0
    VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarGeoLocTransformer",
1605
0
                      nullptr);
1606
1607
0
    GDALGeoLocTransformInfo *psInfo =
1608
0
        static_cast<GDALGeoLocTransformInfo *>(hTransformArg);
1609
1610
0
    char **papszGeolocationInfo = CSLDuplicate(psInfo->papszGeolocationInfo);
1611
1612
0
    if (dfRatioX != 1.0 || dfRatioY != 1.0)
1613
0
    {
1614
0
        GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_OFFSET", dfRatioX, 0.0);
1615
0
        GDALGeoLocRescale(papszGeolocationInfo, "LINE_OFFSET", dfRatioY, 0.0);
1616
0
        GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_STEP", 1.0 / dfRatioX,
1617
0
                          1.0);
1618
0
        GDALGeoLocRescale(papszGeolocationInfo, "LINE_STEP", 1.0 / dfRatioY,
1619
0
                          1.0);
1620
0
    }
1621
1622
0
    auto psInfoNew =
1623
0
        static_cast<GDALGeoLocTransformInfo *>(GDALCreateGeoLocTransformer(
1624
0
            nullptr, papszGeolocationInfo, psInfo->bReversed));
1625
0
    psInfoNew->dfOversampleFactor = psInfo->dfOversampleFactor;
1626
1627
0
    CSLDestroy(papszGeolocationInfo);
1628
1629
0
    return psInfoNew;
1630
0
}
1631
1632
/************************************************************************/
1633
/*                  GDALCreateGeolocationMetadata()                     */
1634
/************************************************************************/
1635
1636
/** Synthesize the content of a GEOLOCATION metadata domain from a
1637
 *  geolocation dataset.
1638
 *
1639
 *  This is used when doing gdalwarp -to GEOLOC_ARRAY=some.tif
1640
 */
1641
CPLStringList GDALCreateGeolocationMetadata(GDALDatasetH hBaseDS,
1642
                                            const char *pszGeolocationDataset,
1643
                                            bool bIsSource)
1644
0
{
1645
0
    CPLStringList aosMD;
1646
1647
    // Open geolocation dataset
1648
0
    auto poGeolocDS = std::unique_ptr<GDALDataset>(
1649
0
        GDALDataset::Open(pszGeolocationDataset, GDAL_OF_RASTER));
1650
0
    if (poGeolocDS == nullptr)
1651
0
    {
1652
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid dataset: %s",
1653
0
                 pszGeolocationDataset);
1654
0
        return CPLStringList();
1655
0
    }
1656
0
    const int nGeoLocXSize = poGeolocDS->GetRasterXSize();
1657
0
    const int nGeoLocYSize = poGeolocDS->GetRasterYSize();
1658
0
    if (nGeoLocXSize == 0 || nGeoLocYSize == 0)
1659
0
    {
1660
0
        CPLError(CE_Failure, CPLE_AppDefined,
1661
0
                 "Invalid dataset dimension for %s: %dx%d",
1662
0
                 pszGeolocationDataset, nGeoLocXSize, nGeoLocYSize);
1663
0
        return CPLStringList();
1664
0
    }
1665
1666
    // Import the GEOLOCATION metadata from the geolocation dataset, if existing
1667
0
    auto papszGeolocMDFromGeolocDS = poGeolocDS->GetMetadata("GEOLOCATION");
1668
0
    if (papszGeolocMDFromGeolocDS)
1669
0
        aosMD = CSLDuplicate(papszGeolocMDFromGeolocDS);
1670
1671
0
    aosMD.SetNameValue("X_DATASET", pszGeolocationDataset);
1672
0
    aosMD.SetNameValue("Y_DATASET", pszGeolocationDataset);
1673
1674
    // Set X_BAND, Y_BAND to 1, 2 if they are not specified in the initial
1675
    // GEOLOC metadata domain.and the geolocation dataset has 2 bands.
1676
0
    if (aosMD.FetchNameValue("X_BAND") == nullptr &&
1677
0
        aosMD.FetchNameValue("Y_BAND") == nullptr)
1678
0
    {
1679
0
        if (poGeolocDS->GetRasterCount() != 2)
1680
0
        {
1681
0
            CPLError(CE_Failure, CPLE_AppDefined,
1682
0
                     "Expected 2 bands for %s. Got %d", pszGeolocationDataset,
1683
0
                     poGeolocDS->GetRasterCount());
1684
0
            return CPLStringList();
1685
0
        }
1686
0
        aosMD.SetNameValue("X_BAND", "1");
1687
0
        aosMD.SetNameValue("Y_BAND", "2");
1688
0
    }
1689
1690
    // Set the geoloc SRS from the geolocation dataset SRS if there's no
1691
    // explicit one in the initial GEOLOC metadata domain.
1692
0
    if (aosMD.FetchNameValue("SRS") == nullptr)
1693
0
    {
1694
0
        auto poSRS = poGeolocDS->GetSpatialRef();
1695
0
        if (poSRS)
1696
0
        {
1697
0
            char *pszWKT = nullptr;
1698
0
            poSRS->exportToWkt(&pszWKT);
1699
0
            aosMD.SetNameValue("SRS", pszWKT);
1700
0
            CPLFree(pszWKT);
1701
0
        }
1702
0
    }
1703
0
    if (aosMD.FetchNameValue("SRS") == nullptr)
1704
0
    {
1705
0
        aosMD.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);
1706
0
    }
1707
1708
    // Set default values for PIXEL/LINE_OFFSET/STEP if not present.
1709
0
    if (aosMD.FetchNameValue("PIXEL_OFFSET") == nullptr)
1710
0
        aosMD.SetNameValue("PIXEL_OFFSET", "0");
1711
1712
0
    if (aosMD.FetchNameValue("LINE_OFFSET") == nullptr)
1713
0
        aosMD.SetNameValue("LINE_OFFSET", "0");
1714
1715
0
    if (aosMD.FetchNameValue("PIXEL_STEP") == nullptr)
1716
0
    {
1717
0
        aosMD.SetNameValue(
1718
0
            "PIXEL_STEP", CPLSPrintf("%.17g", static_cast<double>(
1719
0
                                                  GDALGetRasterXSize(hBaseDS)) /
1720
0
                                                  nGeoLocXSize));
1721
0
    }
1722
1723
0
    if (aosMD.FetchNameValue("LINE_STEP") == nullptr)
1724
0
    {
1725
0
        aosMD.SetNameValue(
1726
0
            "LINE_STEP", CPLSPrintf("%.17g", static_cast<double>(
1727
0
                                                 GDALGetRasterYSize(hBaseDS)) /
1728
0
                                                 nGeoLocYSize));
1729
0
    }
1730
1731
0
    if (aosMD.FetchNameValue("GEOREFERENCING_CONVENTION") == nullptr)
1732
0
    {
1733
0
        const char *pszConvention =
1734
0
            poGeolocDS->GetMetadataItem("GEOREFERENCING_CONVENTION");
1735
0
        if (pszConvention)
1736
0
            aosMD.SetNameValue("GEOREFERENCING_CONVENTION", pszConvention);
1737
0
    }
1738
1739
0
    std::string osDebugMsg;
1740
0
    osDebugMsg = "Synthesized GEOLOCATION metadata for ";
1741
0
    osDebugMsg += bIsSource ? "source" : "target";
1742
0
    osDebugMsg += ":\n";
1743
0
    for (int i = 0; i < aosMD.size(); ++i)
1744
0
    {
1745
0
        osDebugMsg += "  ";
1746
0
        osDebugMsg += aosMD[i];
1747
0
        osDebugMsg += '\n';
1748
0
    }
1749
0
    CPLDebug("GEOLOC", "%s", osDebugMsg.c_str());
1750
1751
0
    return aosMD;
1752
0
}
1753
1754
/************************************************************************/
1755
/*                    GDALCreateGeoLocTransformer()                     */
1756
/************************************************************************/
1757
1758
void *GDALCreateGeoLocTransformerEx(GDALDatasetH hBaseDS,
1759
                                    CSLConstList papszGeolocationInfo,
1760
                                    int bReversed, const char *pszSourceDataset,
1761
                                    CSLConstList papszTransformOptions)
1762
1763
0
{
1764
1765
0
    if (CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET") == nullptr ||
1766
0
        CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET") == nullptr ||
1767
0
        CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP") == nullptr ||
1768
0
        CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP") == nullptr ||
1769
0
        CSLFetchNameValue(papszGeolocationInfo, "X_BAND") == nullptr ||
1770
0
        CSLFetchNameValue(papszGeolocationInfo, "Y_BAND") == nullptr)
1771
0
    {
1772
0
        CPLError(CE_Failure, CPLE_AppDefined,
1773
0
                 "Missing some geolocation fields in "
1774
0
                 "GDALCreateGeoLocTransformer()");
1775
0
        return nullptr;
1776
0
    }
1777
1778
    /* -------------------------------------------------------------------- */
1779
    /*      Initialize core info.                                           */
1780
    /* -------------------------------------------------------------------- */
1781
0
    GDALGeoLocTransformInfo *psTransform =
1782
0
        static_cast<GDALGeoLocTransformInfo *>(
1783
0
            CPLCalloc(sizeof(GDALGeoLocTransformInfo), 1));
1784
1785
0
    psTransform->bReversed = CPL_TO_BOOL(bReversed);
1786
0
    psTransform->dfOversampleFactor = std::max(
1787
0
        0.1,
1788
0
        std::min(2.0,
1789
0
                 CPLAtof(CSLFetchNameValueDef(
1790
0
                     papszTransformOptions, "GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1791
0
                     CPLGetConfigOption("GDAL_GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1792
0
                                        "1.3")))));
1793
1794
0
    memcpy(psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1795
0
           strlen(GDAL_GTI2_SIGNATURE));
1796
0
    psTransform->sTI.pszClassName = "GDALGeoLocTransformer";
1797
0
    psTransform->sTI.pfnTransform = GDALGeoLocTransform;
1798
0
    psTransform->sTI.pfnCleanup = GDALDestroyGeoLocTransformer;
1799
0
    psTransform->sTI.pfnSerialize = GDALSerializeGeoLocTransformer;
1800
0
    psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarGeoLocTransformer;
1801
1802
0
    psTransform->papszGeolocationInfo = CSLDuplicate(papszGeolocationInfo);
1803
1804
0
    psTransform->bGeographicSRSWithMinus180Plus180LongRange =
1805
0
        CPLTestBool(CSLFetchNameValueDef(
1806
0
            papszTransformOptions,
1807
0
            "GEOLOC_NORMALIZE_LONGITUDE_MINUS_180_PLUS_180", "NO"));
1808
1809
    /* -------------------------------------------------------------------- */
1810
    /*      Pull geolocation info from the options/metadata.                */
1811
    /* -------------------------------------------------------------------- */
1812
0
    psTransform->dfPIXEL_OFFSET =
1813
0
        CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET"));
1814
0
    psTransform->dfLINE_OFFSET =
1815
0
        CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET"));
1816
0
    psTransform->dfPIXEL_STEP =
1817
0
        CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP"));
1818
0
    psTransform->dfLINE_STEP =
1819
0
        CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP"));
1820
1821
0
    psTransform->bOriginIsTopLeftCorner = EQUAL(
1822
0
        CSLFetchNameValueDef(papszGeolocationInfo, "GEOREFERENCING_CONVENTION",
1823
0
                             "TOP_LEFT_CORNER"),
1824
0
        "TOP_LEFT_CORNER");
1825
1826
    /* -------------------------------------------------------------------- */
1827
    /*      Establish access to geolocation dataset(s).                     */
1828
    /* -------------------------------------------------------------------- */
1829
0
    const char *pszDSName =
1830
0
        CSLFetchNameValue(papszGeolocationInfo, "X_DATASET");
1831
0
    if (pszDSName != nullptr)
1832
0
    {
1833
0
        CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1834
0
        if (CPLTestBool(CSLFetchNameValueDef(
1835
0
                papszGeolocationInfo, "X_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1836
0
            (hBaseDS != nullptr || pszSourceDataset))
1837
0
        {
1838
0
            const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1839
0
                CPLGetDirnameSafe(pszSourceDataset
1840
0
                                      ? pszSourceDataset
1841
0
                                      : GDALGetDescription(hBaseDS))
1842
0
                    .c_str(),
1843
0
                pszDSName);
1844
0
            psTransform->hDS_X =
1845
0
                GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1846
0
        }
1847
0
        else
1848
0
        {
1849
0
            psTransform->hDS_X = GDALOpenShared(pszDSName, GA_ReadOnly);
1850
0
        }
1851
0
    }
1852
0
    else
1853
0
    {
1854
0
        psTransform->hDS_X = hBaseDS;
1855
0
        if (hBaseDS)
1856
0
        {
1857
0
            GDALReferenceDataset(psTransform->hDS_X);
1858
0
            psTransform->papszGeolocationInfo =
1859
0
                CSLSetNameValue(psTransform->papszGeolocationInfo, "X_DATASET",
1860
0
                                GDALGetDescription(hBaseDS));
1861
0
        }
1862
0
    }
1863
1864
0
    pszDSName = CSLFetchNameValue(papszGeolocationInfo, "Y_DATASET");
1865
0
    if (pszDSName != nullptr)
1866
0
    {
1867
0
        CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1868
0
        if (CPLTestBool(CSLFetchNameValueDef(
1869
0
                papszGeolocationInfo, "Y_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1870
0
            (hBaseDS != nullptr || pszSourceDataset))
1871
0
        {
1872
0
            const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1873
0
                CPLGetDirnameSafe(pszSourceDataset
1874
0
                                      ? pszSourceDataset
1875
0
                                      : GDALGetDescription(hBaseDS))
1876
0
                    .c_str(),
1877
0
                pszDSName);
1878
0
            psTransform->hDS_Y =
1879
0
                GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1880
0
        }
1881
0
        else
1882
0
        {
1883
0
            psTransform->hDS_Y = GDALOpenShared(pszDSName, GA_ReadOnly);
1884
0
        }
1885
0
    }
1886
0
    else
1887
0
    {
1888
0
        psTransform->hDS_Y = hBaseDS;
1889
0
        if (hBaseDS)
1890
0
        {
1891
0
            GDALReferenceDataset(psTransform->hDS_Y);
1892
0
            psTransform->papszGeolocationInfo =
1893
0
                CSLSetNameValue(psTransform->papszGeolocationInfo, "Y_DATASET",
1894
0
                                GDALGetDescription(hBaseDS));
1895
0
        }
1896
0
    }
1897
1898
0
    if (psTransform->hDS_X == nullptr || psTransform->hDS_Y == nullptr)
1899
0
    {
1900
0
        GDALDestroyGeoLocTransformer(psTransform);
1901
0
        return nullptr;
1902
0
    }
1903
1904
    /* -------------------------------------------------------------------- */
1905
    /*      Get the band handles.                                           */
1906
    /* -------------------------------------------------------------------- */
1907
0
    const int nXBand =
1908
0
        std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "X_BAND")));
1909
0
    psTransform->hBand_X = GDALGetRasterBand(psTransform->hDS_X, nXBand);
1910
1911
0
    psTransform->dfNoDataX = GDALGetRasterNoDataValue(
1912
0
        psTransform->hBand_X, &(psTransform->bHasNoData));
1913
1914
0
    const int nYBand =
1915
0
        std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "Y_BAND")));
1916
0
    psTransform->hBand_Y = GDALGetRasterBand(psTransform->hDS_Y, nYBand);
1917
1918
0
    if (psTransform->hBand_X == nullptr || psTransform->hBand_Y == nullptr)
1919
0
    {
1920
0
        GDALDestroyGeoLocTransformer(psTransform);
1921
0
        return nullptr;
1922
0
    }
1923
1924
0
    psTransform->bSwapXY = CPLTestBool(
1925
0
        CSLFetchNameValueDef(papszGeolocationInfo, "SWAP_XY", "NO"));
1926
1927
    /* -------------------------------------------------------------------- */
1928
    /*     Check that X and Y bands have the same dimensions                */
1929
    /* -------------------------------------------------------------------- */
1930
0
    const int nXSize_XBand = GDALGetRasterXSize(psTransform->hDS_X);
1931
0
    const int nYSize_XBand = GDALGetRasterYSize(psTransform->hDS_X);
1932
0
    const int nXSize_YBand = GDALGetRasterXSize(psTransform->hDS_Y);
1933
0
    const int nYSize_YBand = GDALGetRasterYSize(psTransform->hDS_Y);
1934
0
    if (nYSize_XBand == 1 || nYSize_YBand == 1)
1935
0
    {
1936
0
        if (nYSize_XBand != 1 || nYSize_YBand != 1)
1937
0
        {
1938
0
            CPLError(CE_Failure, CPLE_AppDefined,
1939
0
                     "X_BAND and Y_BAND should have both nYSize == 1");
1940
0
            GDALDestroyGeoLocTransformer(psTransform);
1941
0
            return nullptr;
1942
0
        }
1943
0
    }
1944
0
    else if (nXSize_XBand != nXSize_YBand || nYSize_XBand != nYSize_YBand)
1945
0
    {
1946
0
        CPLError(CE_Failure, CPLE_AppDefined,
1947
0
                 "X_BAND and Y_BAND do not have the same dimensions");
1948
0
        GDALDestroyGeoLocTransformer(psTransform);
1949
0
        return nullptr;
1950
0
    }
1951
1952
0
    if (nXSize_XBand <= 0 || nYSize_XBand <= 0 || nXSize_YBand <= 0 ||
1953
0
        nYSize_YBand <= 0)
1954
0
    {
1955
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid X_BAND / Y_BAND size");
1956
0
        GDALDestroyGeoLocTransformer(psTransform);
1957
0
        return nullptr;
1958
0
    }
1959
1960
    // Is it a regular grid ? That is:
1961
    // The XBAND contains the x coordinates for all lines.
1962
    // The YBAND contains the y coordinates for all columns.
1963
0
    const bool bIsRegularGrid = (nYSize_XBand == 1 && nYSize_YBand == 1);
1964
1965
0
    const int nXSize = nXSize_XBand;
1966
0
    const int nYSize = bIsRegularGrid ? nXSize_YBand : nYSize_XBand;
1967
1968
0
    if (static_cast<size_t>(nXSize) >
1969
0
        std::numeric_limits<size_t>::max() / static_cast<size_t>(nYSize))
1970
0
    {
1971
0
        CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %d x %d", nXSize,
1972
0
                 nYSize);
1973
0
        GDALDestroyGeoLocTransformer(psTransform);
1974
0
        return nullptr;
1975
0
    }
1976
1977
0
    psTransform->nGeoLocXSize = nXSize;
1978
0
    psTransform->nGeoLocYSize = nYSize;
1979
1980
0
    if (hBaseDS && psTransform->dfPIXEL_OFFSET == 0 &&
1981
0
        psTransform->dfLINE_OFFSET == 0 && psTransform->dfPIXEL_STEP == 1 &&
1982
0
        psTransform->dfLINE_STEP == 1)
1983
0
    {
1984
0
        if (GDALGetRasterXSize(hBaseDS) > nXSize ||
1985
0
            GDALGetRasterYSize(hBaseDS) > nYSize)
1986
0
        {
1987
0
            CPLError(CE_Warning, CPLE_AppDefined,
1988
0
                     "Geolocation array is %d x %d large, "
1989
0
                     "whereas dataset is %d x %d large. Result might be "
1990
0
                     "incorrect due to lack of values in geolocation array.",
1991
0
                     nXSize, nYSize, GDALGetRasterXSize(hBaseDS),
1992
0
                     GDALGetRasterYSize(hBaseDS));
1993
0
        }
1994
0
    }
1995
1996
    /* -------------------------------------------------------------------- */
1997
    /*      Load the geolocation array.                                     */
1998
    /* -------------------------------------------------------------------- */
1999
2000
    // The quadtree method is experimental. It simplifies the code
2001
    // significantly, but unfortunately burns more RAM and is slower.
2002
0
    const bool bUseQuadtree =
2003
0
        EQUAL(CPLGetConfigOption("GDAL_GEOLOC_INVERSE_METHOD", "BACKMAP"),
2004
0
              "QUADTREE");
2005
2006
    // Decide if we should C-arrays for geoloc and backmap, or on-disk
2007
    // temporary datasets.
2008
0
    const char *pszUseTempDatasets = CSLFetchNameValueDef(
2009
0
        papszTransformOptions, "GEOLOC_USE_TEMP_DATASETS",
2010
0
        CPLGetConfigOption("GDAL_GEOLOC_USE_TEMP_DATASETS", nullptr));
2011
0
    if (pszUseTempDatasets)
2012
0
        psTransform->bUseArray = !CPLTestBool(pszUseTempDatasets);
2013
0
    else
2014
0
    {
2015
0
        constexpr int MEGAPIXEL_LIMIT = 24;
2016
0
        psTransform->bUseArray =
2017
0
            nXSize < MEGAPIXEL_LIMIT * 1000 * 1000 / nYSize;
2018
0
        if (!psTransform->bUseArray)
2019
0
        {
2020
0
            CPLDebug("GEOLOC",
2021
0
                     "Using temporary GTiff backing to store backmap, because "
2022
0
                     "geoloc arrays require %d megapixels, exceeding the %d "
2023
0
                     "megapixels limit. You can set the "
2024
0
                     "GDAL_GEOLOC_USE_TEMP_DATASETS configuration option to "
2025
0
                     "NO to force RAM storage of backmap",
2026
0
                     static_cast<int>(static_cast<int64_t>(nXSize) * nYSize /
2027
0
                                      (1000 * 1000)),
2028
0
                     MEGAPIXEL_LIMIT);
2029
0
        }
2030
0
    }
2031
2032
0
    if (psTransform->bUseArray)
2033
0
    {
2034
0
        auto pAccessors = new GDALGeoLocCArrayAccessors(psTransform);
2035
0
        psTransform->pAccessors = pAccessors;
2036
0
        if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2037
0
        {
2038
0
            GDALDestroyGeoLocTransformer(psTransform);
2039
0
            return nullptr;
2040
0
        }
2041
0
    }
2042
0
    else
2043
0
    {
2044
0
        auto pAccessors = new GDALGeoLocDatasetAccessors(psTransform);
2045
0
        psTransform->pAccessors = pAccessors;
2046
0
        if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2047
0
        {
2048
0
            GDALDestroyGeoLocTransformer(psTransform);
2049
0
            return nullptr;
2050
0
        }
2051
0
    }
2052
2053
0
    return psTransform;
2054
0
}
2055
2056
/** Create GeoLocation transformer */
2057
void *GDALCreateGeoLocTransformer(GDALDatasetH hBaseDS,
2058
                                  char **papszGeolocationInfo, int bReversed)
2059
2060
0
{
2061
0
    return GDALCreateGeoLocTransformerEx(hBaseDS, papszGeolocationInfo,
2062
0
                                         bReversed, nullptr, nullptr);
2063
0
}
2064
2065
/************************************************************************/
2066
/*                    GDALDestroyGeoLocTransformer()                    */
2067
/************************************************************************/
2068
2069
/** Destroy GeoLocation transformer */
2070
void GDALDestroyGeoLocTransformer(void *pTransformAlg)
2071
2072
0
{
2073
0
    if (pTransformAlg == nullptr)
2074
0
        return;
2075
2076
0
    GDALGeoLocTransformInfo *psTransform =
2077
0
        static_cast<GDALGeoLocTransformInfo *>(pTransformAlg);
2078
2079
0
    CSLDestroy(psTransform->papszGeolocationInfo);
2080
2081
0
    if (psTransform->bUseArray)
2082
0
        delete static_cast<GDALGeoLocCArrayAccessors *>(
2083
0
            psTransform->pAccessors);
2084
0
    else
2085
0
        delete static_cast<GDALGeoLocDatasetAccessors *>(
2086
0
            psTransform->pAccessors);
2087
2088
0
    if (psTransform->hDS_X != nullptr &&
2089
0
        GDALDereferenceDataset(psTransform->hDS_X) == 0)
2090
0
        GDALClose(psTransform->hDS_X);
2091
2092
0
    if (psTransform->hDS_Y != nullptr &&
2093
0
        GDALDereferenceDataset(psTransform->hDS_Y) == 0)
2094
0
        GDALClose(psTransform->hDS_Y);
2095
2096
0
    if (psTransform->hQuadTree != nullptr)
2097
0
        CPLQuadTreeDestroy(psTransform->hQuadTree);
2098
2099
0
    CPLFree(pTransformAlg);
2100
0
}
2101
2102
/** Use GeoLocation transformer */
2103
int GDALGeoLocTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
2104
                        double *padfX, double *padfY, double *padfZ,
2105
                        int *panSuccess)
2106
0
{
2107
0
    GDALGeoLocTransformInfo *psTransform =
2108
0
        static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2109
0
    if (psTransform->bUseArray)
2110
0
    {
2111
0
        return GDALGeoLoc<GDALGeoLocCArrayAccessors>::Transform(
2112
0
            pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2113
0
            panSuccess);
2114
0
    }
2115
0
    else
2116
0
    {
2117
0
        return GDALGeoLoc<GDALGeoLocDatasetAccessors>::Transform(
2118
0
            pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2119
0
            panSuccess);
2120
0
    }
2121
0
}
2122
2123
/************************************************************************/
2124
/*                   GDALSerializeGeoLocTransformer()                   */
2125
/************************************************************************/
2126
2127
CPLXMLNode *GDALSerializeGeoLocTransformer(void *pTransformArg)
2128
2129
0
{
2130
0
    VALIDATE_POINTER1(pTransformArg, "GDALSerializeGeoLocTransformer", nullptr);
2131
2132
0
    GDALGeoLocTransformInfo *psInfo =
2133
0
        static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2134
2135
0
    CPLXMLNode *psTree =
2136
0
        CPLCreateXMLNode(nullptr, CXT_Element, "GeoLocTransformer");
2137
2138
    /* -------------------------------------------------------------------- */
2139
    /*      Serialize bReversed.                                            */
2140
    /* -------------------------------------------------------------------- */
2141
0
    CPLCreateXMLElementAndValue(
2142
0
        psTree, "Reversed",
2143
0
        CPLString().Printf("%d", static_cast<int>(psInfo->bReversed)));
2144
2145
    /* -------------------------------------------------------------------- */
2146
    /*      geoloc metadata.                                                */
2147
    /* -------------------------------------------------------------------- */
2148
0
    char **papszMD = psInfo->papszGeolocationInfo;
2149
0
    CPLXMLNode *psMD = CPLCreateXMLNode(psTree, CXT_Element, "Metadata");
2150
2151
0
    for (int i = 0; papszMD != nullptr && papszMD[i] != nullptr; i++)
2152
0
    {
2153
0
        char *pszKey = nullptr;
2154
0
        const char *pszRawValue = CPLParseNameValue(papszMD[i], &pszKey);
2155
2156
0
        CPLXMLNode *psMDI = CPLCreateXMLNode(psMD, CXT_Element, "MDI");
2157
0
        CPLSetXMLValue(psMDI, "#key", pszKey);
2158
0
        CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
2159
2160
0
        CPLFree(pszKey);
2161
0
    }
2162
2163
0
    return psTree;
2164
0
}
2165
2166
/************************************************************************/
2167
/*                   GDALDeserializeGeoLocTransformer()                 */
2168
/************************************************************************/
2169
2170
void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree)
2171
2172
0
{
2173
    /* -------------------------------------------------------------------- */
2174
    /*      Collect metadata.                                               */
2175
    /* -------------------------------------------------------------------- */
2176
0
    CPLXMLNode *psMetadata = CPLGetXMLNode(psTree, "Metadata");
2177
2178
0
    if (psMetadata == nullptr || psMetadata->eType != CXT_Element ||
2179
0
        !EQUAL(psMetadata->pszValue, "Metadata"))
2180
0
        return nullptr;
2181
2182
0
    char **papszMD = nullptr;
2183
2184
0
    for (CPLXMLNode *psMDI = psMetadata->psChild; psMDI != nullptr;
2185
0
         psMDI = psMDI->psNext)
2186
0
    {
2187
0
        if (!EQUAL(psMDI->pszValue, "MDI") || psMDI->eType != CXT_Element ||
2188
0
            psMDI->psChild == nullptr || psMDI->psChild->psNext == nullptr ||
2189
0
            psMDI->psChild->eType != CXT_Attribute ||
2190
0
            psMDI->psChild->psChild == nullptr)
2191
0
            continue;
2192
2193
0
        papszMD = CSLSetNameValue(papszMD, psMDI->psChild->psChild->pszValue,
2194
0
                                  psMDI->psChild->psNext->pszValue);
2195
0
    }
2196
2197
    /* -------------------------------------------------------------------- */
2198
    /*      Get other flags.                                                */
2199
    /* -------------------------------------------------------------------- */
2200
0
    const int bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
2201
2202
    /* -------------------------------------------------------------------- */
2203
    /*      Generate transformation.                                        */
2204
    /* -------------------------------------------------------------------- */
2205
2206
0
    const char *pszSourceDataset =
2207
0
        CPLGetXMLValue(psTree, "SourceDataset", nullptr);
2208
2209
0
    void *pResult = GDALCreateGeoLocTransformerEx(nullptr, papszMD, bReversed,
2210
0
                                                  pszSourceDataset, nullptr);
2211
2212
    /* -------------------------------------------------------------------- */
2213
    /*      Cleanup GCP copy.                                               */
2214
    /* -------------------------------------------------------------------- */
2215
0
    CSLDestroy(papszMD);
2216
2217
0
    return pResult;
2218
0
}