LCOV - code coverage report
Current view: top level - alg - gdalsievefilter.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 167 186 89.8 %
Date: 2024-11-21 22:18:42 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  Raster to Polygon Converter
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2008, Frank Warmerdam
       9             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal_alg.h"
      16             : 
      17             : #include <cstring>
      18             : 
      19             : #include <algorithm>
      20             : #include <set>
      21             : #include <vector>
      22             : #include <utility>
      23             : 
      24             : #include "cpl_conv.h"
      25             : #include "cpl_error.h"
      26             : #include "cpl_progress.h"
      27             : #include "cpl_vsi.h"
      28             : #include "gdal.h"
      29             : #include "gdal_alg_priv.h"
      30             : 
      31             : #define MY_MAX_INT 2147483647
      32             : 
      33             : /*
      34             :  * General Plan
      35             :  *
      36             :  * 1) make a pass with the polygon enumerator to build up the
      37             :  *    polygon map array.  Also accumulate polygon size information.
      38             :  *
      39             :  * 2) Identify the polygons that need to be merged.
      40             :  *
      41             :  * 3) Make a pass with the polygon enumerator.  For each "to be merged"
      42             :  *    polygon keep track of its largest neighbour.
      43             :  *
      44             :  * 4) Fix up remappings that would go to polygons smaller than the seive
      45             :  *    size.  Ensure these in term map to the largest neighbour of the
      46             :  *    "to be sieved" polygons.
      47             :  *
      48             :  * 5) Make another pass with the polygon enumerator. This time we remap
      49             :  *    the actual pixel values of all polygons to be merged.
      50             :  */
      51             : 
      52             : /************************************************************************/
      53             : /*                          GPMaskImageData()                           */
      54             : /*                                                                      */
      55             : /*      Mask out image pixels to a special nodata value if the mask     */
      56             : /*      band is zero.                                                   */
      57             : /************************************************************************/
      58             : 
      59         143 : static CPLErr GPMaskImageData(GDALRasterBandH hMaskBand, GByte *pabyMaskLine,
      60             :                               int iY, int nXSize, std::int64_t *panImageLine)
      61             : 
      62             : {
      63         143 :     const CPLErr eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1,
      64             :                                      pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0);
      65         143 :     if (eErr == CE_None)
      66             :     {
      67        1942 :         for (int i = 0; i < nXSize; i++)
      68             :         {
      69        1799 :             if (pabyMaskLine[i] == 0)
      70         248 :                 panImageLine[i] = GP_NODATA_MARKER;
      71             :         }
      72             :     }
      73             : 
      74         143 :     return eErr;
      75             : }
      76             : 
      77             : // TODO: What is "eaches" supposed to be?
      78             : 
      79             : /************************************************************************/
      80             : /*                          CompareNeighbour()                          */
      81             : /*                                                                      */
      82             : /*      Compare two neighbouring polygons, and update eaches            */
      83             : /*      "biggest neighbour" if the other is larger than its current     */
      84             : /*      largest neighbour.                                              */
      85             : /*                                                                      */
      86             : /*      Note that this should end up with each polygon knowing the      */
      87             : /*      id of its largest neighbour.  No attempt is made to             */
      88             : /*      restrict things to small polygons that we will be merging,      */
      89             : /*      nor to exclude assigning "biggest neighbours" that are still    */
      90             : /*      smaller than our sieve threshold.                               */
      91             : /************************************************************************/
      92             : 
      93       22263 : static inline void CompareNeighbour(int nPolyId1, int nPolyId2,
      94             :                                     int *panPolyIdMap,
      95             :                                     std::int64_t * /* panPolyValue */,
      96             :                                     const std::vector<int> &anPolySizes,
      97             :                                     std::vector<int> &anBigNeighbour)
      98             : 
      99             : {
     100             :     // Nodata polygon do not need neighbours, and cannot be neighbours
     101             :     // to valid polygons.
     102       22263 :     if (nPolyId1 < 0 || nPolyId2 < 0)
     103          34 :         return;
     104             : 
     105             :     // Make sure we are working with the final merged polygon ids.
     106       22229 :     nPolyId1 = panPolyIdMap[nPolyId1];
     107       22229 :     nPolyId2 = panPolyIdMap[nPolyId2];
     108             : 
     109       22229 :     if (nPolyId1 == nPolyId2)
     110        7680 :         return;
     111             : 
     112             :     // Nodata polygon do not need neighbours, and cannot be neighbours
     113             :     // to valid polygons.
     114             :     // Should no longer happen with r28826 optimization.
     115             :     // if( panPolyValue[nPolyId1] == GP_NODATA_MARKER
     116             :     //    || panPolyValue[nPolyId2] == GP_NODATA_MARKER )
     117             :     //    return;
     118             : 
     119       25407 :     if (anBigNeighbour[nPolyId1] == -1 ||
     120       10858 :         anPolySizes[anBigNeighbour[nPolyId1]] < anPolySizes[nPolyId2])
     121        3811 :         anBigNeighbour[nPolyId1] = nPolyId2;
     122             : 
     123       29088 :     if (anBigNeighbour[nPolyId2] == -1 ||
     124       14539 :         anPolySizes[anBigNeighbour[nPolyId2]] < anPolySizes[nPolyId1])
     125         175 :         anBigNeighbour[nPolyId2] = nPolyId1;
     126             : }
     127             : 
     128             : /************************************************************************/
     129             : /*                          GDALSieveFilter()                           */
     130             : /************************************************************************/
     131             : 
     132             : /**
     133             :  * Removes small raster polygons.
     134             :  *
     135             :  * The function removes raster polygons smaller than a provided
     136             :  * threshold size (in pixels) and replaces them with the pixel value
     137             :  * of the largest neighbour polygon.
     138             :  *
     139             :  * Polygon are determined (per GDALRasterPolygonEnumerator) as regions of
     140             :  * the raster where the pixels all have the same value, and that are contiguous
     141             :  * (connected).
     142             :  *
     143             :  * Pixels determined to be "nodata" per hMaskBand will not be treated as part
     144             :  * of a polygon regardless of their pixel values.  Nodata areas will never be
     145             :  * changed nor affect polygon sizes.
     146             :  *
     147             :  * Polygons smaller than the threshold with no neighbours that are as large
     148             :  * as the threshold will not be altered.  Polygons surrounded by nodata areas
     149             :  * will therefore not be altered.
     150             :  *
     151             :  * The algorithm makes three passes over the input file to enumerate the
     152             :  * polygons and collect limited information about them.  Memory use is
     153             :  * proportional to the number of polygons (roughly 24 bytes per polygon), but
     154             :  * is not directly related to the size of the raster.  So very large raster
     155             :  * files can be processed effectively if there aren't too many polygons.  But
     156             :  * extremely noisy rasters with many one pixel polygons will end up being
     157             :  * expensive (in memory) to process.
     158             :  *
     159             :  * @param hSrcBand the source raster band to be processed.
     160             :  * @param hMaskBand an optional mask band.  All pixels in the mask band with a
     161             :  * value other than zero will be considered suitable for inclusion in polygons.
     162             :  * @param hDstBand the output raster band.  It may be the same as hSrcBand
     163             :  * to update the source in place.
     164             :  * @param nSizeThreshold raster polygons with sizes smaller than this will
     165             :  * be merged into their largest neighbour.
     166             :  * @param nConnectedness either 4 indicating that diagonal pixels are not
     167             :  * considered directly adjacent for polygon membership purposes or 8
     168             :  * indicating they are.
     169             :  * @param papszOptions algorithm options in name=value list form.  None
     170             :  * currently supported.
     171             :  * @param pfnProgress callback for reporting algorithm progress matching the
     172             :  * GDALProgressFunc() semantics.  May be NULL.
     173             :  * @param pProgressArg callback argument passed to pfnProgress.
     174             :  *
     175             :  * @return CE_None on success or CE_Failure if an error occurs.
     176             :  */
     177             : 
     178          12 : CPLErr CPL_STDCALL GDALSieveFilter(GDALRasterBandH hSrcBand,
     179             :                                    GDALRasterBandH hMaskBand,
     180             :                                    GDALRasterBandH hDstBand, int nSizeThreshold,
     181             :                                    int nConnectedness,
     182             :                                    CPL_UNUSED char **papszOptions,
     183             :                                    GDALProgressFunc pfnProgress,
     184             :                                    void *pProgressArg)
     185             : {
     186          12 :     VALIDATE_POINTER1(hSrcBand, "GDALSieveFilter", CE_Failure);
     187          12 :     VALIDATE_POINTER1(hDstBand, "GDALSieveFilter", CE_Failure);
     188             : 
     189          12 :     if (pfnProgress == nullptr)
     190          10 :         pfnProgress = GDALDummyProgress;
     191             : 
     192             :     /* -------------------------------------------------------------------- */
     193             :     /*      Allocate working buffers.                                       */
     194             :     /* -------------------------------------------------------------------- */
     195          12 :     int nXSize = GDALGetRasterBandXSize(hSrcBand);
     196          12 :     int nYSize = GDALGetRasterBandYSize(hSrcBand);
     197             :     auto panLastLineValKeeper = std::unique_ptr<std::int64_t, VSIFreeReleaser>(
     198             :         static_cast<std::int64_t *>(
     199          24 :             VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize)));
     200             :     auto panThisLineValKeeper = std::unique_ptr<std::int64_t, VSIFreeReleaser>(
     201             :         static_cast<std::int64_t *>(
     202          24 :             VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize)));
     203             :     auto panLastLineIdKeeper = std::unique_ptr<GInt32, VSIFreeReleaser>(
     204          24 :         static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize)));
     205             :     auto panThisLineIdKeeper = std::unique_ptr<GInt32, VSIFreeReleaser>(
     206          24 :         static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize)));
     207             :     auto panThisLineWriteValKeeper =
     208             :         std::unique_ptr<std::int64_t, VSIFreeReleaser>(
     209             :             static_cast<std::int64_t *>(
     210          24 :                 VSI_MALLOC2_VERBOSE(sizeof(std::int64_t), nXSize)));
     211             :     auto pabyMaskLineKeeper = std::unique_ptr<GByte, VSIFreeReleaser>(
     212           6 :         hMaskBand != nullptr ? static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize))
     213          30 :                              : nullptr);
     214             : 
     215          12 :     auto panLastLineVal = panLastLineValKeeper.get();
     216          12 :     auto panThisLineVal = panThisLineValKeeper.get();
     217          12 :     auto panLastLineId = panLastLineIdKeeper.get();
     218          12 :     auto panThisLineId = panThisLineIdKeeper.get();
     219          12 :     auto panThisLineWriteVal = panThisLineWriteValKeeper.get();
     220          12 :     auto pabyMaskLine = pabyMaskLineKeeper.get();
     221             : 
     222          12 :     if (panLastLineVal == nullptr || panThisLineVal == nullptr ||
     223          12 :         panLastLineId == nullptr || panThisLineId == nullptr ||
     224          12 :         panThisLineWriteVal == nullptr ||
     225           6 :         (hMaskBand != nullptr && pabyMaskLine == nullptr))
     226             :     {
     227           0 :         return CE_Failure;
     228             :     }
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      The first pass over the raster is only used to build up the     */
     232             :     /*      polygon id map so we will know in advance what polygons are     */
     233             :     /*      what on the second pass.                                        */
     234             :     /* -------------------------------------------------------------------- */
     235          24 :     GDALRasterPolygonEnumerator oFirstEnum(nConnectedness);
     236          24 :     std::vector<int> anPolySizes;
     237             : 
     238          12 :     CPLErr eErr = CE_None;
     239         211 :     for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
     240             :     {
     241         199 :         eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
     242             :                             nXSize, 1, GDT_Int64, 0, 0);
     243             : 
     244         199 :         if (eErr == CE_None && hMaskBand != nullptr)
     245          61 :             eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
     246             :                                    panThisLineVal);
     247         199 :         if (eErr != CE_None)
     248           0 :             break;
     249             : 
     250         199 :         if (iY == 0)
     251          24 :             eErr = oFirstEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
     252             :                                           panThisLineId, nXSize)
     253          12 :                        ? CE_None
     254             :                        : CE_Failure;
     255             :         else
     256         374 :             eErr = oFirstEnum.ProcessLine(panLastLineVal, panThisLineVal,
     257             :                                           panLastLineId, panThisLineId, nXSize)
     258         187 :                        ? CE_None
     259             :                        : CE_Failure;
     260         199 :         if (eErr != CE_None)
     261           0 :             break;
     262             : 
     263             :         /* --------------------------------------------------------------------
     264             :          */
     265             :         /*      Accumulate polygon sizes. */
     266             :         /* --------------------------------------------------------------------
     267             :          */
     268         199 :         if (oFirstEnum.nNextPolygonId > static_cast<int>(anPolySizes.size()))
     269         169 :             anPolySizes.resize(oFirstEnum.nNextPolygonId);
     270             : 
     271       11658 :         for (int iX = 0; iX < nXSize; iX++)
     272             :         {
     273       11459 :             const int iPoly = panThisLineId[iX];
     274             : 
     275       11459 :             if (iPoly >= 0 && anPolySizes[iPoly] < MY_MAX_INT)
     276       11243 :                 anPolySizes[iPoly] += 1;
     277             :         }
     278             : 
     279             :         /* --------------------------------------------------------------------
     280             :          */
     281             :         /*      swap this/last lines. */
     282             :         /* --------------------------------------------------------------------
     283             :          */
     284         199 :         std::swap(panLastLineVal, panThisLineVal);
     285         199 :         std::swap(panLastLineId, panThisLineId);
     286             : 
     287             :         /* --------------------------------------------------------------------
     288             :          */
     289             :         /*      Report progress, and support interrupts. */
     290             :         /* --------------------------------------------------------------------
     291             :          */
     292         199 :         if (!pfnProgress(0.25 * ((iY + 1) / static_cast<double>(nYSize)), "",
     293             :                          pProgressArg))
     294             :         {
     295           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     296           0 :             eErr = CE_Failure;
     297             :         }
     298             :     }
     299             : 
     300             :     /* -------------------------------------------------------------------- */
     301             :     /*      Make a pass through the maps, ensuring every polygon id         */
     302             :     /*      points to the final id it should use, not an intermediate       */
     303             :     /*      value.                                                          */
     304             :     /* -------------------------------------------------------------------- */
     305          12 :     if (eErr == CE_None)
     306          12 :         oFirstEnum.CompleteMerges();
     307             : 
     308             :     /* -------------------------------------------------------------------- */
     309             :     /*      Check if there are polygons                                     */
     310             :     /* -------------------------------------------------------------------- */
     311          12 :     if (!oFirstEnum.panPolyIdMap || !oFirstEnum.panPolyValue)
     312             :     {
     313             :         // Can happen if all pixels are masked
     314           2 :         if (hSrcBand == hDstBand)
     315             :         {
     316           1 :             pfnProgress(1.0, "", pProgressArg);
     317           1 :             return CE_None;
     318             :         }
     319             :         else
     320             :         {
     321           1 :             return GDALRasterBandCopyWholeRaster(hSrcBand, hDstBand, nullptr,
     322           1 :                                                  pfnProgress, pProgressArg);
     323             :         }
     324             :     }
     325             : 
     326             :     /* -------------------------------------------------------------------- */
     327             :     /*      Push the sizes of merged polygon fragments into the             */
     328             :     /*      merged polygon id's count.                                      */
     329             :     /* -------------------------------------------------------------------- */
     330        7199 :     for (int iPoly = 0; iPoly < oFirstEnum.nNextPolygonId; iPoly++)
     331             :     {
     332        7189 :         if (oFirstEnum.panPolyIdMap[iPoly] != iPoly)
     333             :         {
     334        3488 :             GIntBig nSize = anPolySizes[oFirstEnum.panPolyIdMap[iPoly]];
     335             : 
     336        3488 :             nSize += anPolySizes[iPoly];
     337             : 
     338        3488 :             if (nSize > MY_MAX_INT)
     339           0 :                 nSize = MY_MAX_INT;
     340             : 
     341        3488 :             anPolySizes[oFirstEnum.panPolyIdMap[iPoly]] =
     342             :                 static_cast<int>(nSize);
     343        3488 :             anPolySizes[iPoly] = 0;
     344             :         }
     345             :     }
     346             : 
     347             :     /* -------------------------------------------------------------------- */
     348             :     /*      We will use a new enumerator for the second pass primarily      */
     349             :     /*      so we can preserve the first pass map.                          */
     350             :     /* -------------------------------------------------------------------- */
     351          20 :     GDALRasterPolygonEnumerator oSecondEnum(nConnectedness);
     352             : 
     353          20 :     std::vector<int> anBigNeighbour;
     354             :     try
     355             :     {
     356          10 :         anBigNeighbour.resize(anPolySizes.size(), -1);
     357             :     }
     358           0 :     catch (const std::exception &)
     359             :     {
     360           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s: Out of memory",
     361             :                  __FUNCTION__);
     362           0 :         return CE_Failure;
     363             :     }
     364             : 
     365             :     /* ==================================================================== */
     366             :     /*      Second pass ... identify the largest neighbour for each         */
     367             :     /*      polygon.                                                        */
     368             :     /* ==================================================================== */
     369         189 :     for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
     370             :     {
     371             :         /* --------------------------------------------------------------------
     372             :          */
     373             :         /*      Read the image data. */
     374             :         /* --------------------------------------------------------------------
     375             :          */
     376         179 :         eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
     377             :                             nXSize, 1, GDT_Int64, 0, 0);
     378             : 
     379         179 :         if (eErr == CE_None && hMaskBand != nullptr)
     380          41 :             eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
     381             :                                    panThisLineVal);
     382             : 
     383         179 :         if (eErr != CE_None)
     384           0 :             continue;
     385             : 
     386             :         /* --------------------------------------------------------------------
     387             :          */
     388             :         /*      Determine what polygon the various pixels belong to (redoing */
     389             :         /*      the same thing done in the first pass above). */
     390             :         /* --------------------------------------------------------------------
     391             :          */
     392         179 :         if (iY == 0)
     393          20 :             eErr = oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
     394             :                                            panThisLineId, nXSize)
     395          10 :                        ? CE_None
     396             :                        : CE_Failure;
     397             :         else
     398         338 :             eErr = oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
     399             :                                            panLastLineId, panThisLineId, nXSize)
     400         169 :                        ? CE_None
     401             :                        : CE_Failure;
     402             : 
     403         179 :         if (eErr != CE_None)
     404           0 :             continue;
     405             : 
     406             :         /* --------------------------------------------------------------------
     407             :          */
     408             :         /*      Check our neighbours, and update our biggest neighbour map */
     409             :         /*      as appropriate. */
     410             :         /* --------------------------------------------------------------------
     411             :          */
     412       11438 :         for (int iX = 0; iX < nXSize; iX++)
     413             :         {
     414       11259 :             if (iY > 0)
     415             :             {
     416       11087 :                 CompareNeighbour(panThisLineId[iX], panLastLineId[iX],
     417             :                                  oFirstEnum.panPolyIdMap,
     418             :                                  oFirstEnum.panPolyValue, anPolySizes,
     419             :                                  anBigNeighbour);
     420             : 
     421       11087 :                 if (iX > 0 && nConnectedness == 8)
     422          48 :                     CompareNeighbour(panThisLineId[iX], panLastLineId[iX - 1],
     423             :                                      oFirstEnum.panPolyIdMap,
     424             :                                      oFirstEnum.panPolyValue, anPolySizes,
     425             :                                      anBigNeighbour);
     426             : 
     427       11087 :                 if (iX < nXSize - 1 && nConnectedness == 8)
     428          48 :                     CompareNeighbour(panThisLineId[iX], panLastLineId[iX + 1],
     429             :                                      oFirstEnum.panPolyIdMap,
     430             :                                      oFirstEnum.panPolyValue, anPolySizes,
     431             :                                      anBigNeighbour);
     432             :             }
     433             : 
     434       11259 :             if (iX > 0)
     435       11080 :                 CompareNeighbour(panThisLineId[iX], panThisLineId[iX - 1],
     436             :                                  oFirstEnum.panPolyIdMap,
     437             :                                  oFirstEnum.panPolyValue, anPolySizes,
     438             :                                  anBigNeighbour);
     439             : 
     440             :             // We don't need to compare to next pixel or next line
     441             :             // since they will be compared to us.
     442             :         }
     443             : 
     444             :         /* --------------------------------------------------------------------
     445             :          */
     446             :         /*      Swap pixel value, and polygon id lines to be ready for the */
     447             :         /*      next line. */
     448             :         /* --------------------------------------------------------------------
     449             :          */
     450         179 :         std::swap(panLastLineVal, panThisLineVal);
     451         179 :         std::swap(panLastLineId, panThisLineId);
     452             : 
     453             :         /* --------------------------------------------------------------------
     454             :          */
     455             :         /*      Report progress, and support interrupts. */
     456             :         /* --------------------------------------------------------------------
     457             :          */
     458         179 :         if (!pfnProgress(0.25 + 0.25 * ((iY + 1) / static_cast<double>(nYSize)),
     459             :                          "", pProgressArg))
     460             :         {
     461           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     462           0 :             eErr = CE_Failure;
     463             :         }
     464             :     }
     465             : 
     466             :     /* -------------------------------------------------------------------- */
     467             :     /*      If our biggest neighbour is still smaller than the              */
     468             :     /*      threshold, then try tracking to that polygons biggest           */
     469             :     /*      neighbour, and so forth.                                        */
     470             :     /* -------------------------------------------------------------------- */
     471          10 :     int nFailedMerges = 0;
     472          10 :     int nIsolatedSmall = 0;
     473          10 :     int nSieveTargets = 0;
     474             : 
     475        7199 :     for (int iPoly = 0; iPoly < static_cast<int>(anPolySizes.size()); iPoly++)
     476             :     {
     477        7189 :         if (oFirstEnum.panPolyIdMap[iPoly] != iPoly)
     478        3817 :             continue;
     479             : 
     480             :         // Ignore nodata polygons.
     481        3701 :         if (oFirstEnum.panPolyValue[iPoly] == GP_NODATA_MARKER)
     482           0 :             continue;
     483             : 
     484             :         // Don't try to merge polygons larger than the threshold.
     485        3701 :         if (anPolySizes[iPoly] >= nSizeThreshold)
     486             :         {
     487         309 :             anBigNeighbour[iPoly] = -1;
     488         309 :             continue;
     489             :         }
     490             : 
     491        3392 :         nSieveTargets++;
     492             : 
     493             :         // if we have no neighbours but we are small, what shall we do?
     494        3392 :         if (anBigNeighbour[iPoly] == -1)
     495             :         {
     496           0 :             nIsolatedSmall++;
     497           0 :             continue;
     498             :         }
     499             : 
     500        3392 :         std::set<int> oSetVisitedPoly;
     501        3392 :         oSetVisitedPoly.insert(iPoly);
     502             : 
     503             :         // Walk through our neighbours until we find a polygon large enough.
     504        3392 :         int iFinalId = iPoly;
     505        3392 :         bool bFoundBigEnoughPoly = false;
     506             :         while (true)
     507             :         {
     508        3420 :             iFinalId = anBigNeighbour[iFinalId];
     509        3420 :             if (iFinalId < 0)
     510             :             {
     511          19 :                 break;
     512             :             }
     513             :             // If the biggest neighbour is larger than the threshold
     514             :             // then we are golden.
     515        3401 :             if (anPolySizes[iFinalId] >= nSizeThreshold)
     516             :             {
     517        3372 :                 bFoundBigEnoughPoly = true;
     518        3372 :                 break;
     519             :             }
     520             :             // Check that we don't cycle on an already visited polygon.
     521          29 :             if (oSetVisitedPoly.find(iFinalId) != oSetVisitedPoly.end())
     522           1 :                 break;
     523          28 :             oSetVisitedPoly.insert(iFinalId);
     524             :         }
     525             : 
     526        3392 :         if (!bFoundBigEnoughPoly)
     527             :         {
     528          20 :             nFailedMerges++;
     529          20 :             anBigNeighbour[iPoly] = -1;
     530          20 :             continue;
     531             :         }
     532             : 
     533             :         // Map the whole intermediate chain to it.
     534        3372 :         int iPolyCur = iPoly;
     535        3380 :         while (anBigNeighbour[iPolyCur] != iFinalId)
     536             :         {
     537           8 :             int iNextPoly = anBigNeighbour[iPolyCur];
     538           8 :             anBigNeighbour[iPolyCur] = iFinalId;
     539           8 :             iPolyCur = iNextPoly;
     540             :         }
     541             :     }
     542             : 
     543          10 :     CPLDebug("GDALSieveFilter",
     544             :              "Small Polygons: %d, Isolated: %d, Unmergable: %d", nSieveTargets,
     545             :              nIsolatedSmall, nFailedMerges);
     546             : 
     547             :     /* ==================================================================== */
     548             :     /*      Make a third pass over the image, actually applying the         */
     549             :     /*      merges.  We reuse the second enumerator but preserve the        */
     550             :     /*      "final maps" from the first.                                    */
     551             :     /* ==================================================================== */
     552          10 :     oSecondEnum.Clear();
     553             : 
     554         189 :     for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
     555             :     {
     556             :         /* --------------------------------------------------------------------
     557             :          */
     558             :         /*      Read the image data. */
     559             :         /* --------------------------------------------------------------------
     560             :          */
     561         179 :         eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
     562             :                             nXSize, 1, GDT_Int64, 0, 0);
     563             : 
     564         179 :         memcpy(panThisLineWriteVal, panThisLineVal,
     565         179 :                sizeof(panThisLineVal[0]) * nXSize);
     566             : 
     567         179 :         if (eErr == CE_None && hMaskBand != nullptr)
     568          41 :             eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
     569             :                                    panThisLineVal);
     570             : 
     571         179 :         if (eErr != CE_None)
     572           0 :             continue;
     573             : 
     574             :         /* --------------------------------------------------------------------
     575             :          */
     576             :         /*      Determine what polygon the various pixels belong to (redoing */
     577             :         /*      the same thing done in the first pass above). */
     578             :         /* --------------------------------------------------------------------
     579             :          */
     580         179 :         if (iY == 0)
     581          10 :             oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
     582             :                                     panThisLineId, nXSize);
     583             :         else
     584         169 :             oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
     585             :                                     panLastLineId, panThisLineId, nXSize);
     586             : 
     587             :         /* --------------------------------------------------------------------
     588             :          */
     589             :         /*      Reprocess the actual pixel values according to the polygon */
     590             :         /*      merging, and write out this line of image data. */
     591             :         /* --------------------------------------------------------------------
     592             :          */
     593       11438 :         for (int iX = 0; iX < nXSize; iX++)
     594             :         {
     595       11259 :             int iThisPoly = panThisLineId[iX];
     596       11259 :             if (iThisPoly >= 0)
     597             :             {
     598       11243 :                 iThisPoly = oFirstEnum.panPolyIdMap[iThisPoly];
     599             : 
     600       11243 :                 if (anBigNeighbour[iThisPoly] != -1)
     601             :                 {
     602        3377 :                     panThisLineWriteVal[iX] =
     603        3377 :                         oFirstEnum.panPolyValue[anBigNeighbour[iThisPoly]];
     604             :                 }
     605             :             }
     606             :         }
     607             : 
     608             :         /* --------------------------------------------------------------------
     609             :          */
     610             :         /*      Write the update data out. */
     611             :         /* --------------------------------------------------------------------
     612             :          */
     613         179 :         eErr = GDALRasterIO(hDstBand, GF_Write, 0, iY, nXSize, 1,
     614             :                             panThisLineWriteVal, nXSize, 1, GDT_Int64, 0, 0);
     615             : 
     616             :         /* --------------------------------------------------------------------
     617             :          */
     618             :         /*      Swap pixel value, and polygon id lines to be ready for the */
     619             :         /*      next line. */
     620             :         /* --------------------------------------------------------------------
     621             :          */
     622         179 :         std::swap(panLastLineVal, panThisLineVal);
     623         179 :         std::swap(panLastLineId, panThisLineId);
     624             : 
     625             :         /* --------------------------------------------------------------------
     626             :          */
     627             :         /*      Report progress, and support interrupts. */
     628             :         /* --------------------------------------------------------------------
     629             :          */
     630         358 :         if (eErr == CE_None &&
     631         179 :             !pfnProgress(0.5 + 0.5 * ((iY + 1) / static_cast<double>(nYSize)),
     632             :                          "", pProgressArg))
     633             :         {
     634           0 :             CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
     635           0 :             eErr = CE_Failure;
     636             :         }
     637             :     }
     638             : 
     639          10 :     return eErr;
     640             : }

Generated by: LCOV version 1.14