Coverage Report

Created: 2025-06-13 06:18

/src/gdal/alg/rasterfill.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Interpolate in nodata areas.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2008, Frank Warmerdam
9
 * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10
 * Copyright (c) 2015, Sean Gillies <sean@mapbox.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ***************************************************************************/
14
15
#include "cpl_port.h"
16
#include "gdal_alg.h"
17
18
#include <cmath>
19
#include <cstring>
20
21
#include <algorithm>
22
#include <string>
23
#include <utility>
24
25
#include "cpl_conv.h"
26
#include "cpl_error.h"
27
#include "cpl_progress.h"
28
#include "cpl_string.h"
29
#include "cpl_vsi.h"
30
#include "gdal.h"
31
#include "gdal_priv.h"
32
33
/************************************************************************/
34
/*                           GDALFilterLine()                           */
35
/*                                                                      */
36
/*      Apply 3x3 filtering one one scanline with masking for which     */
37
/*      pixels are to be interpolated (ThisFMask) and which window      */
38
/*      pixels are valid to include in the interpolation (TMask).       */
39
/************************************************************************/
40
41
static void GDALFilterLine(const float *pafLastLine, const float *pafThisLine,
42
                           const float *pafNextLine, float *pafOutLine,
43
                           const GByte *pabyLastTMask,
44
                           const GByte *pabyThisTMask,
45
                           const GByte *pabyNextTMask,
46
                           const GByte *pabyThisFMask, int nXSize)
47
48
0
{
49
0
    for (int iX = 0; iX < nXSize; iX++)
50
0
    {
51
0
        if (!pabyThisFMask[iX])
52
0
        {
53
0
            pafOutLine[iX] = pafThisLine[iX];
54
0
            continue;
55
0
        }
56
57
0
        CPLAssert(pabyThisTMask[iX]);
58
59
0
        double dfValSum = 0.0;
60
0
        double dfWeightSum = 0.0;
61
62
        // Previous line.
63
0
        if (pafLastLine != nullptr)
64
0
        {
65
0
            if (iX > 0 && pabyLastTMask[iX - 1])
66
0
            {
67
0
                dfValSum += pafLastLine[iX - 1];
68
0
                dfWeightSum += 1.0;
69
0
            }
70
0
            if (pabyLastTMask[iX])
71
0
            {
72
0
                dfValSum += pafLastLine[iX];
73
0
                dfWeightSum += 1.0;
74
0
            }
75
0
            if (iX < nXSize - 1 && pabyLastTMask[iX + 1])
76
0
            {
77
0
                dfValSum += pafLastLine[iX + 1];
78
0
                dfWeightSum += 1.0;
79
0
            }
80
0
        }
81
82
        // Current Line.
83
0
        if (iX > 0 && pabyThisTMask[iX - 1])
84
0
        {
85
0
            dfValSum += pafThisLine[iX - 1];
86
0
            dfWeightSum += 1.0;
87
0
        }
88
0
        if (pabyThisTMask[iX])
89
0
        {
90
0
            dfValSum += pafThisLine[iX];
91
0
            dfWeightSum += 1.0;
92
0
        }
93
0
        if (iX < nXSize - 1 && pabyThisTMask[iX + 1])
94
0
        {
95
0
            dfValSum += pafThisLine[iX + 1];
96
0
            dfWeightSum += 1.0;
97
0
        }
98
99
        // Next line.
100
0
        if (pafNextLine != nullptr)
101
0
        {
102
0
            if (iX > 0 && pabyNextTMask[iX - 1])
103
0
            {
104
0
                dfValSum += pafNextLine[iX - 1];
105
0
                dfWeightSum += 1.0;
106
0
            }
107
0
            if (pabyNextTMask[iX])
108
0
            {
109
0
                dfValSum += pafNextLine[iX];
110
0
                dfWeightSum += 1.0;
111
0
            }
112
0
            if (iX < nXSize - 1 && pabyNextTMask[iX + 1])
113
0
            {
114
0
                dfValSum += pafNextLine[iX + 1];
115
0
                dfWeightSum += 1.0;
116
0
            }
117
0
        }
118
119
0
        pafOutLine[iX] = static_cast<float>(dfValSum / dfWeightSum);
120
0
    }
121
0
}
122
123
/************************************************************************/
124
/*                          GDALMultiFilter()                           */
125
/*                                                                      */
126
/*      Apply multiple iterations of a 3x3 smoothing filter over a      */
127
/*      band with masking controlling what pixels should be             */
128
/*      filtered (FiltMaskBand non zero) and which pixels can be        */
129
/*      considered valid contributors to the filter                     */
130
/*      (TargetMaskBand non zero).                                      */
131
/*                                                                      */
132
/*      This implementation attempts to apply many iterations in        */
133
/*      one IO pass by managing the filtering over a rolling buffer     */
134
/*      of nIterations+2 scanlines.  While possibly clever this        */
135
/*      makes the algorithm implementation largely                      */
136
/*      incomprehensible.                                               */
137
/************************************************************************/
138
139
static CPLErr GDALMultiFilter(GDALRasterBandH hTargetBand,
140
                              GDALRasterBandH hTargetMaskBand,
141
                              GDALRasterBandH hFiltMaskBand, int nIterations,
142
                              GDALProgressFunc pfnProgress, void *pProgressArg)
143
144
0
{
145
0
    const int nXSize = GDALGetRasterBandXSize(hTargetBand);
146
0
    const int nYSize = GDALGetRasterBandYSize(hTargetBand);
147
148
    /* -------------------------------------------------------------------- */
149
    /*      Report starting progress value.                                 */
150
    /* -------------------------------------------------------------------- */
151
0
    if (!pfnProgress(0.0, "Smoothing Filter...", pProgressArg))
152
0
    {
153
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
154
0
        return CE_Failure;
155
0
    }
156
157
    /* -------------------------------------------------------------------- */
158
    /*      Allocate rotating buffers.                                      */
159
    /* -------------------------------------------------------------------- */
160
0
    const int nBufLines = nIterations + 2;
161
162
0
    GByte *pabyTMaskBuf =
163
0
        static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nXSize, nBufLines));
164
0
    GByte *pabyFMaskBuf =
165
0
        static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nXSize, nBufLines));
166
167
0
    float *paf3PassLineBuf = static_cast<float *>(
168
0
        VSI_MALLOC3_VERBOSE(nXSize, nBufLines, 3 * sizeof(float)));
169
0
    if (pabyTMaskBuf == nullptr || pabyFMaskBuf == nullptr ||
170
0
        paf3PassLineBuf == nullptr)
171
0
    {
172
0
        CPLFree(pabyTMaskBuf);
173
0
        CPLFree(pabyFMaskBuf);
174
0
        CPLFree(paf3PassLineBuf);
175
176
0
        return CE_Failure;
177
0
    }
178
179
    /* -------------------------------------------------------------------- */
180
    /*      Process rotating buffers.                                       */
181
    /* -------------------------------------------------------------------- */
182
183
0
    CPLErr eErr = CE_None;
184
185
0
    int iPassCounter = 0;
186
187
0
    for (int nNewLine = 0;  // Line being loaded (zero based scanline).
188
0
         eErr == CE_None && nNewLine < nYSize + nIterations; nNewLine++)
189
0
    {
190
        /* --------------------------------------------------------------------
191
         */
192
        /*      Rotate pass buffers. */
193
        /* --------------------------------------------------------------------
194
         */
195
0
        iPassCounter = (iPassCounter + 1) % 3;
196
197
0
        float *const pafSLastPass =
198
0
            paf3PassLineBuf + ((iPassCounter + 0) % 3) * nXSize * nBufLines;
199
0
        float *const pafLastPass =
200
0
            paf3PassLineBuf + ((iPassCounter + 1) % 3) * nXSize * nBufLines;
201
0
        float *const pafThisPass =
202
0
            paf3PassLineBuf + ((iPassCounter + 2) % 3) * nXSize * nBufLines;
203
204
        /* --------------------------------------------------------------------
205
         */
206
        /*      Where does the new line go in the rotating buffer? */
207
        /* --------------------------------------------------------------------
208
         */
209
0
        const int iBufOffset = nNewLine % nBufLines;
210
211
        /* --------------------------------------------------------------------
212
         */
213
        /*      Read the new data line if it is't off the bottom of the */
214
        /*      image. */
215
        /* --------------------------------------------------------------------
216
         */
217
0
        if (nNewLine < nYSize)
218
0
        {
219
0
            eErr = GDALRasterIO(hTargetMaskBand, GF_Read, 0, nNewLine, nXSize,
220
0
                                1, pabyTMaskBuf + nXSize * iBufOffset, nXSize,
221
0
                                1, GDT_Byte, 0, 0);
222
223
0
            if (eErr != CE_None)
224
0
                break;
225
226
0
            eErr = GDALRasterIO(hFiltMaskBand, GF_Read, 0, nNewLine, nXSize, 1,
227
0
                                pabyFMaskBuf + nXSize * iBufOffset, nXSize, 1,
228
0
                                GDT_Byte, 0, 0);
229
230
0
            if (eErr != CE_None)
231
0
                break;
232
233
0
            eErr = GDALRasterIO(hTargetBand, GF_Read, 0, nNewLine, nXSize, 1,
234
0
                                pafThisPass + nXSize * iBufOffset, nXSize, 1,
235
0
                                GDT_Float32, 0, 0);
236
237
0
            if (eErr != CE_None)
238
0
                break;
239
0
        }
240
241
        /* --------------------------------------------------------------------
242
         */
243
        /*      Loop over the loaded data, applying the filter to all loaded */
244
        /*      lines with neighbours. */
245
        /* --------------------------------------------------------------------
246
         */
247
0
        for (int iFLine = nNewLine - 1;
248
0
             eErr == CE_None && iFLine >= nNewLine - nIterations; iFLine--)
249
0
        {
250
0
            const int iLastOffset = (iFLine - 1) % nBufLines;
251
0
            const int iThisOffset = (iFLine) % nBufLines;
252
0
            const int iNextOffset = (iFLine + 1) % nBufLines;
253
254
            // Default to preserving the old value.
255
0
            if (iFLine >= 0)
256
0
                memcpy(pafThisPass + iThisOffset * nXSize,
257
0
                       pafLastPass + iThisOffset * nXSize,
258
0
                       sizeof(float) * nXSize);
259
260
            // TODO: Enable first and last line.
261
            // Skip the first and last line.
262
0
            if (iFLine < 1 || iFLine >= nYSize - 1)
263
0
            {
264
0
                continue;
265
0
            }
266
267
0
            GDALFilterLine(pafSLastPass + iLastOffset * nXSize,
268
0
                           pafLastPass + iThisOffset * nXSize,
269
0
                           pafThisPass + iNextOffset * nXSize,
270
0
                           pafThisPass + iThisOffset * nXSize,
271
0
                           pabyTMaskBuf + iLastOffset * nXSize,
272
0
                           pabyTMaskBuf + iThisOffset * nXSize,
273
0
                           pabyTMaskBuf + iNextOffset * nXSize,
274
0
                           pabyFMaskBuf + iThisOffset * nXSize, nXSize);
275
0
        }
276
277
        /* --------------------------------------------------------------------
278
         */
279
        /*      Write out the top data line that will be rolling out of our */
280
        /*      buffer. */
281
        /* --------------------------------------------------------------------
282
         */
283
0
        const int iLineToSave = nNewLine - nIterations;
284
285
0
        if (iLineToSave >= 0 && eErr == CE_None)
286
0
        {
287
0
            const int iBufOffset2 = iLineToSave % nBufLines;
288
289
0
            eErr = GDALRasterIO(hTargetBand, GF_Write, 0, iLineToSave, nXSize,
290
0
                                1, pafThisPass + nXSize * iBufOffset2, nXSize,
291
0
                                1, GDT_Float32, 0, 0);
292
0
        }
293
294
        /* --------------------------------------------------------------------
295
         */
296
        /*      Report progress. */
297
        /* --------------------------------------------------------------------
298
         */
299
0
        if (eErr == CE_None &&
300
0
            !pfnProgress((nNewLine + 1) /
301
0
                             static_cast<double>(nYSize + nIterations),
302
0
                         "Smoothing Filter...", pProgressArg))
303
0
        {
304
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
305
0
            eErr = CE_Failure;
306
0
        }
307
0
    }
308
309
    /* -------------------------------------------------------------------- */
310
    /*      Cleanup                                                         */
311
    /* -------------------------------------------------------------------- */
312
0
    CPLFree(pabyTMaskBuf);
313
0
    CPLFree(pabyFMaskBuf);
314
0
    CPLFree(paf3PassLineBuf);
315
316
0
    return eErr;
317
0
}
318
319
/************************************************************************/
320
/*                             QUAD_CHECK()                             */
321
/*                                                                      */
322
/*      macro for checking whether a point is nearer than the           */
323
/*      existing closest point.                                         */
324
/************************************************************************/
325
326
inline void QUAD_CHECK(double &dfQuadDist, float &fQuadValue, int target_x,
327
                       GUInt32 target_y, int origin_x, int origin_y,
328
                       float fTargetValue, GUInt32 nNoDataVal)
329
0
{
330
0
    if (target_y != nNoDataVal)
331
0
    {
332
0
        const double dfDx =
333
0
            static_cast<double>(target_x) - static_cast<double>(origin_x);
334
0
        const double dfDy =
335
0
            static_cast<double>(target_y) - static_cast<double>(origin_y);
336
0
        double dfDistSq = dfDx * dfDx + dfDy * dfDy;
337
338
0
        if (dfDistSq < dfQuadDist * dfQuadDist)
339
0
        {
340
0
            CPLAssert(dfDistSq > 0.0);
341
0
            dfQuadDist = sqrt(dfDistSq);
342
0
            fQuadValue = fTargetValue;
343
0
        }
344
0
    }
345
0
}
346
347
/************************************************************************/
348
/*                           GDALFillNodata()                           */
349
/************************************************************************/
350
351
/**
352
 * Fill selected raster regions by interpolation from the edges.
353
 *
354
 * This algorithm will interpolate values for all designated
355
 * nodata pixels (marked by zeros in hMaskBand). For each pixel
356
 * a four direction conic search is done to find values to interpolate
357
 * from (using inverse distance weighting by default). Once all values are
358
 * interpolated, zero or more smoothing iterations (3x3 average
359
 * filters on interpolated pixels) are applied to smooth out
360
 * artifacts.
361
 *
362
 * This algorithm is generally suitable for interpolating missing
363
 * regions of fairly continuously varying rasters (such as elevation
364
 * models for instance). It is also suitable for filling small holes
365
 * and cracks in more irregularly varying images (like airphotos). It
366
 * is generally not so great for interpolating a raster from sparse
367
 * point data - see the algorithms defined in gdal_grid.h for that case.
368
 *
369
 * @param hTargetBand the raster band to be modified in place.
370
 * @param hMaskBand a mask band indicating pixels to be interpolated
371
 * (zero valued). If hMaskBand is set to NULL, this method will internally use
372
 * the mask band returned by GDALGetMaskBand(hTargetBand).
373
 * @param dfMaxSearchDist the maximum number of pixels to search in all
374
 * directions to find values to interpolate from.
375
 * @param bDeprecatedOption unused argument, should be zero.
376
 * @param nSmoothingIterations the number of 3x3 smoothing filter passes to
377
 * run (0 or more).
378
 * @param papszOptions additional name=value options in a string list.
379
 * <ul>
380
 * <li>TEMP_FILE_DRIVER=gdal_driver_name. For example MEM.</li>
381
 * <li>NODATA=value (starting with GDAL 2.4).
382
 * Source pixels at that value will be ignored by the interpolator. Warning:
383
 * currently this will not be honored by smoothing passes.</li>
384
 * <li>INTERPOLATION=INV_DIST/NEAREST (GDAL >= 3.9). By default, pixels are
385
 * interpolated using an inverse distance weighting (INV_DIST). It is also
386
 * possible to choose a nearest neighbour (NEAREST) strategy.</li>
387
 * </ul>
388
 * @param pfnProgress the progress function to report completion.
389
 * @param pProgressArg callback data for progress function.
390
 *
391
 * @return CE_None on success or CE_Failure if something goes wrong.
392
 */
393
394
CPLErr CPL_STDCALL GDALFillNodata(GDALRasterBandH hTargetBand,
395
                                  GDALRasterBandH hMaskBand,
396
                                  double dfMaxSearchDist,
397
                                  CPL_UNUSED int bDeprecatedOption,
398
                                  int nSmoothingIterations, char **papszOptions,
399
                                  GDALProgressFunc pfnProgress,
400
                                  void *pProgressArg)
401
402
0
{
403
0
    VALIDATE_POINTER1(hTargetBand, "GDALFillNodata", CE_Failure);
404
405
0
    const int nXSize = GDALGetRasterBandXSize(hTargetBand);
406
0
    const int nYSize = GDALGetRasterBandYSize(hTargetBand);
407
408
0
    if (dfMaxSearchDist == 0.0)
409
0
        dfMaxSearchDist = std::max(nXSize, nYSize) + 1;
410
411
0
    const int nMaxSearchDist = static_cast<int>(floor(dfMaxSearchDist));
412
413
0
    const char *pszInterpolation =
414
0
        CSLFetchNameValueDef(papszOptions, "INTERPOLATION", "INV_DIST");
415
0
    const bool bNearest = EQUAL(pszInterpolation, "NEAREST");
416
0
    if (!EQUAL(pszInterpolation, "INV_DIST") &&
417
0
        !EQUAL(pszInterpolation, "NEAREST"))
418
0
    {
419
0
        CPLError(CE_Failure, CPLE_NotSupported,
420
0
                 "Unsupported interpolation method: %s", pszInterpolation);
421
0
        return CE_Failure;
422
0
    }
423
424
    // Special "x" pixel values identifying pixels as special.
425
0
    GDALDataType eType = GDT_UInt16;
426
0
    GUInt32 nNoDataVal = 65535;
427
428
0
    if (nXSize > 65533 || nYSize > 65533)
429
0
    {
430
0
        eType = GDT_UInt32;
431
0
        nNoDataVal = 4000002;
432
0
    }
433
434
    /* -------------------------------------------------------------------- */
435
    /*      Determine format driver for temp work files.                    */
436
    /* -------------------------------------------------------------------- */
437
0
    CPLString osTmpFileDriver =
438
0
        CSLFetchNameValueDef(papszOptions, "TEMP_FILE_DRIVER", "GTiff");
439
0
    GDALDriverH hDriver = GDALGetDriverByName(osTmpFileDriver.c_str());
440
441
0
    if (hDriver == nullptr)
442
0
    {
443
0
        CPLError(CE_Failure, CPLE_AppDefined,
444
0
                 "TEMP_FILE_DRIVER=%s driver is not registered",
445
0
                 osTmpFileDriver.c_str());
446
0
        return CE_Failure;
447
0
    }
448
449
0
    if (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) == nullptr)
450
0
    {
451
0
        CPLError(CE_Failure, CPLE_AppDefined,
452
0
                 "TEMP_FILE_DRIVER=%s driver is incapable of creating "
453
0
                 "temp work files",
454
0
                 osTmpFileDriver.c_str());
455
0
        return CE_Failure;
456
0
    }
457
458
0
    CPLStringList aosWorkFileOptions;
459
0
    if (osTmpFileDriver == "GTiff")
460
0
    {
461
0
        aosWorkFileOptions.SetNameValue("COMPRESS", "LZW");
462
0
        aosWorkFileOptions.SetNameValue("BIGTIFF", "IF_SAFER");
463
0
    }
464
465
0
    const CPLString osTmpFile = CPLGenerateTempFilenameSafe("");
466
467
0
    std::unique_ptr<GDALDataset> poTmpMaskDS;
468
0
    if (hMaskBand == nullptr)
469
0
    {
470
0
        hMaskBand = GDALGetMaskBand(hTargetBand);
471
0
    }
472
0
    else if (nSmoothingIterations > 0 &&
473
0
             hMaskBand != GDALGetMaskBand(hTargetBand))
474
0
    {
475
        // If doing smoothing operations and the user provided its own
476
        // mask band, we must make a copy of it to be able to update it
477
        // when we fill pixels during the initial pass.
478
0
        const CPLString osMaskTmpFile = osTmpFile + "fill_mask_work.tif";
479
0
        poTmpMaskDS.reset(GDALDataset::FromHandle(
480
0
            GDALCreate(hDriver, osMaskTmpFile, nXSize, nYSize, 1, GDT_Byte,
481
0
                       aosWorkFileOptions.List())));
482
0
        if (poTmpMaskDS == nullptr)
483
0
        {
484
0
            CPLError(CE_Failure, CPLE_AppDefined,
485
0
                     "Could not create poTmpMaskDS work file. Check driver "
486
0
                     "capabilities.");
487
0
            return CE_Failure;
488
0
        }
489
0
        poTmpMaskDS->MarkSuppressOnClose();
490
0
        auto hTmpMaskBand =
491
0
            GDALRasterBand::ToHandle(poTmpMaskDS->GetRasterBand(1));
492
0
        if (GDALRasterBandCopyWholeRaster(hMaskBand, hTmpMaskBand, nullptr,
493
0
                                          nullptr, nullptr) != CE_None)
494
0
        {
495
0
            return CE_Failure;
496
0
        }
497
0
        hMaskBand = hTmpMaskBand;
498
0
    }
499
500
    // If there are smoothing iterations, reserve 10% of the progress for them.
501
0
    const double dfProgressRatio = nSmoothingIterations > 0 ? 0.9 : 1.0;
502
503
0
    const char *pszNoData = CSLFetchNameValue(papszOptions, "NODATA");
504
0
    bool bHasNoData = false;
505
0
    float fNoData = 0.0f;
506
0
    if (pszNoData)
507
0
    {
508
0
        bHasNoData = true;
509
0
        fNoData = static_cast<float>(CPLAtof(pszNoData));
510
0
    }
511
512
    /* -------------------------------------------------------------------- */
513
    /*      Initialize progress counter.                                    */
514
    /* -------------------------------------------------------------------- */
515
0
    if (pfnProgress == nullptr)
516
0
        pfnProgress = GDALDummyProgress;
517
518
0
    if (!pfnProgress(0.0, "Filling...", pProgressArg))
519
0
    {
520
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
521
0
        return CE_Failure;
522
0
    }
523
524
    /* -------------------------------------------------------------------- */
525
    /*      Create a work file to hold the Y "last value" indices.          */
526
    /* -------------------------------------------------------------------- */
527
0
    const CPLString osYTmpFile = osTmpFile + "fill_y_work.tif";
528
529
0
    auto poYDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
530
0
        GDALCreate(hDriver, osYTmpFile, nXSize, nYSize, 1, eType,
531
0
                   aosWorkFileOptions.List())));
532
533
0
    if (poYDS == nullptr)
534
0
    {
535
0
        CPLError(
536
0
            CE_Failure, CPLE_AppDefined,
537
0
            "Could not create Y index work file. Check driver capabilities.");
538
0
        return CE_Failure;
539
0
    }
540
0
    poYDS->MarkSuppressOnClose();
541
542
0
    GDALRasterBandH hYBand =
543
0
        GDALRasterBand::FromHandle(poYDS->GetRasterBand(1));
544
545
    /* -------------------------------------------------------------------- */
546
    /*      Create a work file to hold the pixel value associated with      */
547
    /*      the "last xy value" pixel.                                      */
548
    /* -------------------------------------------------------------------- */
549
0
    const CPLString osValTmpFile = osTmpFile + "fill_val_work.tif";
550
551
0
    auto poValDS =
552
0
        std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALCreate(
553
0
            hDriver, osValTmpFile, nXSize, nYSize, 1,
554
0
            GDALGetRasterDataType(hTargetBand), aosWorkFileOptions.List())));
555
556
0
    if (poValDS == nullptr)
557
0
    {
558
0
        CPLError(
559
0
            CE_Failure, CPLE_AppDefined,
560
0
            "Could not create XY value work file. Check driver capabilities.");
561
0
        return CE_Failure;
562
0
    }
563
0
    poValDS->MarkSuppressOnClose();
564
565
0
    GDALRasterBandH hValBand =
566
0
        GDALRasterBand::FromHandle(poValDS->GetRasterBand(1));
567
568
    /* -------------------------------------------------------------------- */
569
    /*      Create a mask file to make it clear what pixels can be filtered */
570
    /*      on the filtering pass.                                          */
571
    /* -------------------------------------------------------------------- */
572
0
    const CPLString osFiltMaskTmpFile = osTmpFile + "fill_filtmask_work.tif";
573
574
0
    auto poFiltMaskDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
575
0
        GDALCreate(hDriver, osFiltMaskTmpFile, nXSize, nYSize, 1, GDT_Byte,
576
0
                   aosWorkFileOptions.List())));
577
578
0
    if (poFiltMaskDS == nullptr)
579
0
    {
580
0
        CPLError(CE_Failure, CPLE_AppDefined,
581
0
                 "Could not create mask work file. Check driver capabilities.");
582
0
        return CE_Failure;
583
0
    }
584
0
    poFiltMaskDS->MarkSuppressOnClose();
585
586
0
    GDALRasterBandH hFiltMaskBand =
587
0
        GDALRasterBand::FromHandle(poFiltMaskDS->GetRasterBand(1));
588
589
    /* -------------------------------------------------------------------- */
590
    /*      Allocate buffers for last scanline and this scanline.           */
591
    /* -------------------------------------------------------------------- */
592
593
0
    GUInt32 *panLastY =
594
0
        static_cast<GUInt32 *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(GUInt32)));
595
0
    GUInt32 *panThisY =
596
0
        static_cast<GUInt32 *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(GUInt32)));
597
0
    GUInt32 *panTopDownY =
598
0
        static_cast<GUInt32 *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(GUInt32)));
599
0
    float *pafLastValue =
600
0
        static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
601
0
    float *pafThisValue =
602
0
        static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
603
0
    float *pafTopDownValue =
604
0
        static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
605
0
    float *pafScanline =
606
0
        static_cast<float *>(VSI_CALLOC_VERBOSE(nXSize, sizeof(float)));
607
0
    GByte *pabyMask = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nXSize, 1));
608
0
    GByte *pabyFiltMask = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nXSize, 1));
609
610
0
    CPLErr eErr = CE_None;
611
612
0
    if (panLastY == nullptr || panThisY == nullptr || panTopDownY == nullptr ||
613
0
        pafLastValue == nullptr || pafThisValue == nullptr ||
614
0
        pafTopDownValue == nullptr || pafScanline == nullptr ||
615
0
        pabyMask == nullptr || pabyFiltMask == nullptr)
616
0
    {
617
0
        eErr = CE_Failure;
618
0
        goto end;
619
0
    }
620
621
0
    for (int iX = 0; iX < nXSize; iX++)
622
0
    {
623
0
        panLastY[iX] = nNoDataVal;
624
0
    }
625
626
    /* ==================================================================== */
627
    /*      Make first pass from top to bottom collecting the "last         */
628
    /*      known value" for each column and writing it out to the work     */
629
    /*      files.                                                          */
630
    /* ==================================================================== */
631
632
0
    for (int iY = 0; iY < nYSize && eErr == CE_None; iY++)
633
0
    {
634
        /* --------------------------------------------------------------------
635
         */
636
        /*      Read data and mask for this line. */
637
        /* --------------------------------------------------------------------
638
         */
639
0
        eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1, pabyMask,
640
0
                            nXSize, 1, GDT_Byte, 0, 0);
641
642
0
        if (eErr != CE_None)
643
0
            break;
644
645
0
        eErr = GDALRasterIO(hTargetBand, GF_Read, 0, iY, nXSize, 1, pafScanline,
646
0
                            nXSize, 1, GDT_Float32, 0, 0);
647
648
0
        if (eErr != CE_None)
649
0
            break;
650
651
        /* --------------------------------------------------------------------
652
         */
653
        /*      Figure out the most recent pixel for each column. */
654
        /* --------------------------------------------------------------------
655
         */
656
657
0
        for (int iX = 0; iX < nXSize; iX++)
658
0
        {
659
0
            if (pabyMask[iX])
660
0
            {
661
0
                pafThisValue[iX] = pafScanline[iX];
662
0
                panThisY[iX] = iY;
663
0
            }
664
0
            else if (iY <= dfMaxSearchDist + panLastY[iX])
665
0
            {
666
0
                pafThisValue[iX] = pafLastValue[iX];
667
0
                panThisY[iX] = panLastY[iX];
668
0
            }
669
0
            else
670
0
            {
671
0
                panThisY[iX] = nNoDataVal;
672
0
            }
673
0
        }
674
675
        /* --------------------------------------------------------------------
676
         */
677
        /*      Write out best index/value to working files. */
678
        /* --------------------------------------------------------------------
679
         */
680
0
        eErr = GDALRasterIO(hYBand, GF_Write, 0, iY, nXSize, 1, panThisY,
681
0
                            nXSize, 1, GDT_UInt32, 0, 0);
682
0
        if (eErr != CE_None)
683
0
            break;
684
685
0
        eErr = GDALRasterIO(hValBand, GF_Write, 0, iY, nXSize, 1, pafThisValue,
686
0
                            nXSize, 1, GDT_Float32, 0, 0);
687
0
        if (eErr != CE_None)
688
0
            break;
689
690
        /* --------------------------------------------------------------------
691
         */
692
        /*      Flip this/last buffers. */
693
        /* --------------------------------------------------------------------
694
         */
695
0
        std::swap(pafThisValue, pafLastValue);
696
0
        std::swap(panThisY, panLastY);
697
698
        /* --------------------------------------------------------------------
699
         */
700
        /*      report progress. */
701
        /* --------------------------------------------------------------------
702
         */
703
0
        if (!pfnProgress(dfProgressRatio *
704
0
                             (0.5 * (iY + 1) / static_cast<double>(nYSize)),
705
0
                         "Filling...", pProgressArg))
706
0
        {
707
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
708
0
            eErr = CE_Failure;
709
0
        }
710
0
    }
711
712
0
    for (int iX = 0; iX < nXSize; iX++)
713
0
    {
714
0
        panLastY[iX] = nNoDataVal;
715
0
    }
716
717
    /* ==================================================================== */
718
    /*      Now we will do collect similar this/last information from       */
719
    /*      bottom to top and use it in combination with the top to         */
720
    /*      bottom search info to interpolate.                              */
721
    /* ==================================================================== */
722
0
    for (int iY = nYSize - 1; iY >= 0 && eErr == CE_None; iY--)
723
0
    {
724
0
        eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1, pabyMask,
725
0
                            nXSize, 1, GDT_Byte, 0, 0);
726
727
0
        if (eErr != CE_None)
728
0
            break;
729
730
0
        eErr = GDALRasterIO(hTargetBand, GF_Read, 0, iY, nXSize, 1, pafScanline,
731
0
                            nXSize, 1, GDT_Float32, 0, 0);
732
733
0
        if (eErr != CE_None)
734
0
            break;
735
736
        /* --------------------------------------------------------------------
737
         */
738
        /*      Figure out the most recent pixel for each column. */
739
        /* --------------------------------------------------------------------
740
         */
741
742
0
        for (int iX = 0; iX < nXSize; iX++)
743
0
        {
744
0
            if (pabyMask[iX])
745
0
            {
746
0
                pafThisValue[iX] = pafScanline[iX];
747
0
                panThisY[iX] = iY;
748
0
            }
749
0
            else if (panLastY[iX] - iY <= dfMaxSearchDist)
750
0
            {
751
0
                pafThisValue[iX] = pafLastValue[iX];
752
0
                panThisY[iX] = panLastY[iX];
753
0
            }
754
0
            else
755
0
            {
756
0
                panThisY[iX] = nNoDataVal;
757
0
            }
758
0
        }
759
760
        /* --------------------------------------------------------------------
761
         */
762
        /*      Load the last y and corresponding value from the top down pass.
763
         */
764
        /* --------------------------------------------------------------------
765
         */
766
0
        eErr = GDALRasterIO(hYBand, GF_Read, 0, iY, nXSize, 1, panTopDownY,
767
0
                            nXSize, 1, GDT_UInt32, 0, 0);
768
769
0
        if (eErr != CE_None)
770
0
            break;
771
772
0
        eErr = GDALRasterIO(hValBand, GF_Read, 0, iY, nXSize, 1,
773
0
                            pafTopDownValue, nXSize, 1, GDT_Float32, 0, 0);
774
775
0
        if (eErr != CE_None)
776
0
            break;
777
778
        /* --------------------------------------------------------------------
779
         */
780
        /*      Attempt to interpolate any pixels that are nodata. */
781
        /* --------------------------------------------------------------------
782
         */
783
0
        memset(pabyFiltMask, 0, nXSize);
784
0
        for (int iX = 0; iX < nXSize; iX++)
785
0
        {
786
0
            int nThisMaxSearchDist = nMaxSearchDist;
787
788
            // If this was a valid target - no change.
789
0
            if (pabyMask[iX])
790
0
                continue;
791
792
0
            enum Quadrants
793
0
            {
794
0
                QUAD_TOP_LEFT = 0,
795
0
                QUAD_BOTTOM_LEFT = 1,
796
0
                QUAD_TOP_RIGHT = 2,
797
0
                QUAD_BOTTOM_RIGHT = 3,
798
0
            };
799
800
0
            constexpr int QUAD_COUNT = 4;
801
0
            double adfQuadDist[QUAD_COUNT] = {};
802
0
            float afQuadValue[QUAD_COUNT] = {};
803
804
0
            for (int iQuad = 0; iQuad < QUAD_COUNT; iQuad++)
805
0
            {
806
0
                adfQuadDist[iQuad] = dfMaxSearchDist + 1.0;
807
0
                afQuadValue[iQuad] = 0.0;
808
0
            }
809
810
            // Step left and right by one pixel searching for the closest
811
            // target value for each quadrant.
812
0
            for (int iStep = 0; iStep <= nThisMaxSearchDist; iStep++)
813
0
            {
814
0
                const int iLeftX = std::max(0, iX - iStep);
815
0
                const int iRightX = std::min(nXSize - 1, iX + iStep);
816
817
                // Top left includes current line.
818
0
                QUAD_CHECK(adfQuadDist[QUAD_TOP_LEFT],
819
0
                           afQuadValue[QUAD_TOP_LEFT], iLeftX,
820
0
                           panTopDownY[iLeftX], iX, iY, pafTopDownValue[iLeftX],
821
0
                           nNoDataVal);
822
823
                // Bottom left.
824
0
                QUAD_CHECK(adfQuadDist[QUAD_BOTTOM_LEFT],
825
0
                           afQuadValue[QUAD_BOTTOM_LEFT], iLeftX,
826
0
                           panLastY[iLeftX], iX, iY, pafLastValue[iLeftX],
827
0
                           nNoDataVal);
828
829
                // Top right and bottom right do no include center pixel.
830
0
                if (iStep == 0)
831
0
                    continue;
832
833
                // Top right includes current line.
834
0
                QUAD_CHECK(adfQuadDist[QUAD_TOP_RIGHT],
835
0
                           afQuadValue[QUAD_TOP_RIGHT], iRightX,
836
0
                           panTopDownY[iRightX], iX, iY,
837
0
                           pafTopDownValue[iRightX], nNoDataVal);
838
839
                // Bottom right.
840
0
                QUAD_CHECK(adfQuadDist[QUAD_BOTTOM_RIGHT],
841
0
                           afQuadValue[QUAD_BOTTOM_RIGHT], iRightX,
842
0
                           panLastY[iRightX], iX, iY, pafLastValue[iRightX],
843
0
                           nNoDataVal);
844
845
                // Every four steps, recompute maximum distance.
846
0
                if ((iStep & 0x3) == 0)
847
0
                    nThisMaxSearchDist = static_cast<int>(floor(
848
0
                        std::max(std::max(adfQuadDist[0], adfQuadDist[1]),
849
0
                                 std::max(adfQuadDist[2], adfQuadDist[3]))));
850
0
            }
851
852
0
            bool bHasSrcValues = false;
853
0
            if (bNearest)
854
0
            {
855
0
                double dfNearestDist = dfMaxSearchDist + 1;
856
0
                float fNearestValue = 0.0f;
857
858
0
                for (int iQuad = 0; iQuad < QUAD_COUNT; iQuad++)
859
0
                {
860
0
                    if (adfQuadDist[iQuad] < dfNearestDist)
861
0
                    {
862
0
                        bHasSrcValues = true;
863
0
                        if (!bHasNoData || afQuadValue[iQuad] != fNoData)
864
0
                        {
865
0
                            fNearestValue = afQuadValue[iQuad];
866
0
                            dfNearestDist = adfQuadDist[iQuad];
867
0
                        }
868
0
                    }
869
0
                }
870
871
0
                if (bHasSrcValues)
872
0
                {
873
0
                    pabyFiltMask[iX] = 255;
874
0
                    if (dfNearestDist <= dfMaxSearchDist)
875
0
                    {
876
0
                        pabyMask[iX] = 255;
877
0
                        pafScanline[iX] = fNearestValue;
878
0
                    }
879
0
                    else
880
0
                        pafScanline[iX] = fNoData;
881
0
                }
882
0
            }
883
0
            else
884
0
            {
885
0
                double dfWeightSum = 0.0;
886
0
                double dfValueSum = 0.0;
887
888
0
                for (int iQuad = 0; iQuad < QUAD_COUNT; iQuad++)
889
0
                {
890
0
                    if (adfQuadDist[iQuad] <= dfMaxSearchDist)
891
0
                    {
892
0
                        bHasSrcValues = true;
893
0
                        if (!bHasNoData || afQuadValue[iQuad] != fNoData)
894
0
                        {
895
0
                            const double dfWeight = 1.0 / adfQuadDist[iQuad];
896
0
                            dfWeightSum += dfWeight;
897
0
                            dfValueSum += afQuadValue[iQuad] * dfWeight;
898
0
                        }
899
0
                    }
900
0
                }
901
902
0
                if (bHasSrcValues)
903
0
                {
904
0
                    pabyFiltMask[iX] = 255;
905
0
                    if (dfWeightSum > 0.0)
906
0
                    {
907
0
                        pabyMask[iX] = 255;
908
0
                        pafScanline[iX] =
909
0
                            static_cast<float>(dfValueSum / dfWeightSum);
910
0
                    }
911
0
                    else
912
0
                        pafScanline[iX] = fNoData;
913
0
                }
914
0
            }
915
0
        }
916
917
        /* --------------------------------------------------------------------
918
         */
919
        /*      Write out the updated data and mask information. */
920
        /* --------------------------------------------------------------------
921
         */
922
0
        eErr = GDALRasterIO(hTargetBand, GF_Write, 0, iY, nXSize, 1,
923
0
                            pafScanline, nXSize, 1, GDT_Float32, 0, 0);
924
925
0
        if (eErr != CE_None)
926
0
            break;
927
928
0
        if (poTmpMaskDS != nullptr)
929
0
        {
930
            // Update (copy of) mask band when it has been provided by the
931
            // user
932
0
            eErr = GDALRasterIO(hMaskBand, GF_Write, 0, iY, nXSize, 1, pabyMask,
933
0
                                nXSize, 1, GDT_Byte, 0, 0);
934
935
0
            if (eErr != CE_None)
936
0
                break;
937
0
        }
938
939
0
        eErr = GDALRasterIO(hFiltMaskBand, GF_Write, 0, iY, nXSize, 1,
940
0
                            pabyFiltMask, nXSize, 1, GDT_Byte, 0, 0);
941
942
0
        if (eErr != CE_None)
943
0
            break;
944
945
        /* --------------------------------------------------------------------
946
         */
947
        /*      Flip this/last buffers. */
948
        /* --------------------------------------------------------------------
949
         */
950
0
        std::swap(pafThisValue, pafLastValue);
951
0
        std::swap(panThisY, panLastY);
952
953
        /* --------------------------------------------------------------------
954
         */
955
        /*      report progress. */
956
        /* --------------------------------------------------------------------
957
         */
958
0
        if (!pfnProgress(
959
0
                dfProgressRatio *
960
0
                    (0.5 + 0.5 * (nYSize - iY) / static_cast<double>(nYSize)),
961
0
                "Filling...", pProgressArg))
962
0
        {
963
0
            CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
964
0
            eErr = CE_Failure;
965
0
        }
966
0
    }
967
968
    /* ==================================================================== */
969
    /*      Now we will do iterative average filters over the               */
970
    /*      interpolated values to smooth things out and make linear        */
971
    /*      artifacts less obvious.                                         */
972
    /* ==================================================================== */
973
0
    if (eErr == CE_None && nSmoothingIterations > 0)
974
0
    {
975
0
        if (poTmpMaskDS == nullptr)
976
0
        {
977
            // Force masks to be to flushed and recomputed when the user
978
            // didn't pass a user-provided hMaskBand, and we assigned it
979
            // to be the mask band of hTargetBand.
980
0
            GDALFlushRasterCache(hMaskBand);
981
0
        }
982
983
0
        void *pScaledProgress = GDALCreateScaledProgress(
984
0
            dfProgressRatio, 1.0, pfnProgress, pProgressArg);
985
986
0
        eErr = GDALMultiFilter(hTargetBand, hMaskBand, hFiltMaskBand,
987
0
                               nSmoothingIterations, GDALScaledProgress,
988
0
                               pScaledProgress);
989
990
0
        GDALDestroyScaledProgress(pScaledProgress);
991
0
    }
992
993
/* -------------------------------------------------------------------- */
994
/*      Close and clean up temporary files. Free working buffers        */
995
/* -------------------------------------------------------------------- */
996
0
end:
997
0
    CPLFree(panLastY);
998
0
    CPLFree(panThisY);
999
0
    CPLFree(panTopDownY);
1000
0
    CPLFree(pafLastValue);
1001
0
    CPLFree(pafThisValue);
1002
0
    CPLFree(pafTopDownValue);
1003
0
    CPLFree(pafScanline);
1004
0
    CPLFree(pabyMask);
1005
0
    CPLFree(pabyFiltMask);
1006
1007
0
    return eErr;
1008
0
}