324 lines
11 KiB
C++
324 lines
11 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: GDAL
|
|
* Purpose: Raster Polygon Enumerator
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2008, Frank Warmerdam
|
|
* Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
****************************************************************************/
|
|
|
|
#include "cpl_port.h"
|
|
#include "gdal_alg_priv.h"
|
|
|
|
#include <cstddef>
|
|
#include <limits>
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_error.h"
|
|
|
|
/*! @cond Doxygen_Suppress */
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterPolygonEnumeratorT() */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
GDALRasterPolygonEnumeratorT<
|
|
DataType, EqualityTest>::GDALRasterPolygonEnumeratorT(int nConnectednessIn)
|
|
: nConnectedness(nConnectednessIn)
|
|
|
|
{
|
|
CPLAssert(nConnectedness == 4 || nConnectedness == 8);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALRasterPolygonEnumeratorT() */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
GDALRasterPolygonEnumeratorT<DataType,
|
|
EqualityTest>::~GDALRasterPolygonEnumeratorT()
|
|
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Clear() */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
void GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::Clear()
|
|
|
|
{
|
|
CPLFree(panPolyIdMap);
|
|
CPLFree(panPolyValue);
|
|
|
|
panPolyIdMap = nullptr;
|
|
panPolyValue = nullptr;
|
|
|
|
nNextPolygonId = 0;
|
|
nPolyAlloc = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* MergePolygon() */
|
|
/* */
|
|
/* Update the polygon map to indicate the merger of two polygons. */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
void GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::MergePolygon(
|
|
int nSrcId, int nDstIdInit)
|
|
|
|
{
|
|
// Figure out the final dest id.
|
|
int nDstIdFinal = nDstIdInit;
|
|
while (panPolyIdMap[nDstIdFinal] != nDstIdFinal)
|
|
nDstIdFinal = panPolyIdMap[nDstIdFinal];
|
|
|
|
// Map the whole intermediate chain to it.
|
|
int nDstIdCur = nDstIdInit;
|
|
while (panPolyIdMap[nDstIdCur] != nDstIdCur)
|
|
{
|
|
int nNextDstId = panPolyIdMap[nDstIdCur];
|
|
panPolyIdMap[nDstIdCur] = nDstIdFinal;
|
|
nDstIdCur = nNextDstId;
|
|
}
|
|
|
|
// And map the whole source chain to it too (can be done in one pass).
|
|
while (panPolyIdMap[nSrcId] != nSrcId)
|
|
{
|
|
int nNextSrcId = panPolyIdMap[nSrcId];
|
|
panPolyIdMap[nSrcId] = nDstIdFinal;
|
|
nSrcId = nNextSrcId;
|
|
}
|
|
panPolyIdMap[nSrcId] = nDstIdFinal;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* NewPolygon() */
|
|
/* */
|
|
/* Allocate a new polygon id, and reallocate the polygon maps */
|
|
/* if needed. */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
int GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::NewPolygon(
|
|
DataType nValue)
|
|
|
|
{
|
|
if (nNextPolygonId == std::numeric_limits<int>::max())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GDALRasterPolygonEnumeratorT::NewPolygon(): maximum number "
|
|
"of polygons reached");
|
|
return -1;
|
|
}
|
|
if (nNextPolygonId >= nPolyAlloc)
|
|
{
|
|
int nPolyAllocNew;
|
|
if (nPolyAlloc < (std::numeric_limits<int>::max() - 20) / 2)
|
|
nPolyAllocNew = nPolyAlloc * 2 + 20;
|
|
else
|
|
nPolyAllocNew = std::numeric_limits<int>::max();
|
|
#if SIZEOF_VOIDP == 4
|
|
if (nPolyAllocNew >
|
|
static_cast<int>(std::numeric_limits<size_t>::max() /
|
|
sizeof(GInt32)) ||
|
|
nPolyAllocNew >
|
|
static_cast<int>(std::numeric_limits<size_t>::max() /
|
|
sizeof(DataType)))
|
|
{
|
|
CPLError(CE_Failure, CPLE_OutOfMemory,
|
|
"GDALRasterPolygonEnumeratorT::NewPolygon(): too many "
|
|
"polygons");
|
|
return -1;
|
|
}
|
|
#endif
|
|
auto panPolyIdMapNew = static_cast<GInt32 *>(
|
|
VSI_REALLOC_VERBOSE(panPolyIdMap, nPolyAllocNew * sizeof(GInt32)));
|
|
auto panPolyValueNew = static_cast<DataType *>(VSI_REALLOC_VERBOSE(
|
|
panPolyValue, nPolyAllocNew * sizeof(DataType)));
|
|
if (panPolyIdMapNew == nullptr || panPolyValueNew == nullptr)
|
|
{
|
|
VSIFree(panPolyIdMapNew);
|
|
VSIFree(panPolyValueNew);
|
|
return -1;
|
|
}
|
|
panPolyIdMap = panPolyIdMapNew;
|
|
panPolyValue = panPolyValueNew;
|
|
nPolyAlloc = nPolyAllocNew;
|
|
}
|
|
|
|
const int nPolyId = nNextPolygonId;
|
|
panPolyIdMap[nPolyId] = nPolyId;
|
|
panPolyValue[nPolyId] = nValue;
|
|
nNextPolygonId++;
|
|
|
|
return nPolyId;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CompleteMerges() */
|
|
/* */
|
|
/* Make a pass through the maps, ensuring every polygon id */
|
|
/* points to the final id it should use, not an intermediate */
|
|
/* value. */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
void GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::CompleteMerges()
|
|
|
|
{
|
|
int nFinalPolyCount = 0;
|
|
|
|
for (int iPoly = 0; iPoly < nNextPolygonId; iPoly++)
|
|
{
|
|
// Figure out the final id.
|
|
int nId = panPolyIdMap[iPoly];
|
|
while (nId != panPolyIdMap[nId])
|
|
{
|
|
nId = panPolyIdMap[nId];
|
|
}
|
|
|
|
// Then map the whole intermediate chain to it.
|
|
int nIdCur = panPolyIdMap[iPoly];
|
|
panPolyIdMap[iPoly] = nId;
|
|
while (nIdCur != panPolyIdMap[nIdCur])
|
|
{
|
|
int nNextId = panPolyIdMap[nIdCur];
|
|
panPolyIdMap[nIdCur] = nId;
|
|
nIdCur = nNextId;
|
|
}
|
|
|
|
if (panPolyIdMap[iPoly] == iPoly)
|
|
nFinalPolyCount++;
|
|
}
|
|
|
|
CPLDebug("GDALRasterPolygonEnumerator",
|
|
"Counted %d polygon fragments forming %d final polygons.",
|
|
nNextPolygonId, nFinalPolyCount);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ProcessLine() */
|
|
/* */
|
|
/* Assign ids to polygons, one line at a time. */
|
|
/************************************************************************/
|
|
|
|
template <class DataType, class EqualityTest>
|
|
bool GDALRasterPolygonEnumeratorT<DataType, EqualityTest>::ProcessLine(
|
|
DataType *panLastLineVal, DataType *panThisLineVal, GInt32 *panLastLineId,
|
|
GInt32 *panThisLineId, int nXSize)
|
|
|
|
{
|
|
EqualityTest eq;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case for the first line. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (panLastLineVal == nullptr)
|
|
{
|
|
for (int i = 0; i < nXSize; i++)
|
|
{
|
|
if (panThisLineVal[i] == GP_NODATA_MARKER)
|
|
{
|
|
panThisLineId[i] = -1;
|
|
}
|
|
else if (i == 0 ||
|
|
!(eq.operator()(panThisLineVal[i], panThisLineVal[i - 1])))
|
|
{
|
|
panThisLineId[i] = NewPolygon(panThisLineVal[i]);
|
|
if (panThisLineId[i] < 0)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
panThisLineId[i] = panThisLineId[i - 1];
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process each pixel comparing to the previous pixel, and to */
|
|
/* the last line. */
|
|
/* -------------------------------------------------------------------- */
|
|
for (int i = 0; i < nXSize; i++)
|
|
{
|
|
if (panThisLineVal[i] == GP_NODATA_MARKER)
|
|
{
|
|
panThisLineId[i] = -1;
|
|
}
|
|
else if (i > 0 &&
|
|
eq.operator()(panThisLineVal[i], panThisLineVal[i - 1]))
|
|
{
|
|
panThisLineId[i] = panThisLineId[i - 1];
|
|
|
|
if (eq.operator()(panLastLineVal[i], panThisLineVal[i]) &&
|
|
(panPolyIdMap[panLastLineId[i]] !=
|
|
panPolyIdMap[panThisLineId[i]]))
|
|
{
|
|
MergePolygon(panLastLineId[i], panThisLineId[i]);
|
|
}
|
|
|
|
if (nConnectedness == 8 &&
|
|
eq.operator()(panLastLineVal[i - 1], panThisLineVal[i]) &&
|
|
(panPolyIdMap[panLastLineId[i - 1]] !=
|
|
panPolyIdMap[panThisLineId[i]]))
|
|
{
|
|
MergePolygon(panLastLineId[i - 1], panThisLineId[i]);
|
|
}
|
|
|
|
if (nConnectedness == 8 && i < nXSize - 1 &&
|
|
eq.operator()(panLastLineVal[i + 1], panThisLineVal[i]) &&
|
|
(panPolyIdMap[panLastLineId[i + 1]] !=
|
|
panPolyIdMap[panThisLineId[i]]))
|
|
{
|
|
MergePolygon(panLastLineId[i + 1], panThisLineId[i]);
|
|
}
|
|
}
|
|
else if (eq.operator()(panLastLineVal[i], panThisLineVal[i]))
|
|
{
|
|
panThisLineId[i] = panLastLineId[i];
|
|
}
|
|
else if (i > 0 && nConnectedness == 8 &&
|
|
eq.operator()(panLastLineVal[i - 1], panThisLineVal[i]))
|
|
{
|
|
panThisLineId[i] = panLastLineId[i - 1];
|
|
|
|
if (i < nXSize - 1 &&
|
|
eq.operator()(panLastLineVal[i + 1], panThisLineVal[i]) &&
|
|
(panPolyIdMap[panLastLineId[i + 1]] !=
|
|
panPolyIdMap[panThisLineId[i]]))
|
|
{
|
|
MergePolygon(panLastLineId[i + 1], panThisLineId[i]);
|
|
}
|
|
}
|
|
else if (i < nXSize - 1 && nConnectedness == 8 &&
|
|
eq.operator()(panLastLineVal[i + 1], panThisLineVal[i]))
|
|
{
|
|
panThisLineId[i] = panLastLineId[i + 1];
|
|
}
|
|
else
|
|
{
|
|
panThisLineId[i] = NewPolygon(panThisLineVal[i]);
|
|
if (panThisLineId[i] < 0)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template class GDALRasterPolygonEnumeratorT<std::int64_t, IntEqualityTest>;
|
|
|
|
template class GDALRasterPolygonEnumeratorT<float, FloatEqualityTest>;
|
|
|
|
/*! @endcond */
|