CPLErr CPL_STDCALL GDALFPolygonize( GDALRasterBandH hSrcBand, GDALRasterBandH hMaskBand, OGRLayerH hOutLayer, int iPixValField, char **papszOptions, GDALProgressFunc pfnProgress, void * pProgressArg ) { #ifndef OGR_ENABLED CPLError(CE_Failure, CPLE_NotSupported, "GDALFPolygonize() unimplemented in a non OGR build"); return CE_Failure; #else VALIDATE_POINTER1( hSrcBand, "GDALFPolygonize", CE_Failure ); VALIDATE_POINTER1( hOutLayer, "GDALFPolygonize", CE_Failure ); if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; int nConnectedness = CSLFetchNameValue( papszOptions, "8CONNECTED" ) ? 8 : 4; /* -------------------------------------------------------------------- */ /* Confirm our output layer will support feature creation. */ /* -------------------------------------------------------------------- */ if( !OGR_L_TestCapability( hOutLayer, OLCSequentialWrite ) ) { CPLError( CE_Failure, CPLE_AppDefined, "Output feature layer does not appear to support creation\n" "of features in GDALFPolygonize()." ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Allocate working buffers. */ /* -------------------------------------------------------------------- */ CPLErr eErr = CE_None; int nXSize = GDALGetRasterBandXSize( hSrcBand ); int nYSize = GDALGetRasterBandYSize( hSrcBand ); float *pafLastLineVal = (float *) VSIMalloc2(sizeof(float),nXSize + 2); float *pafThisLineVal = (float *) VSIMalloc2(sizeof(float),nXSize + 2); GInt32 *panLastLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); GInt32 *panThisLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32),nXSize + 2); GByte *pabyMaskLine = (hMaskBand != NULL) ? (GByte *) VSIMalloc(nXSize) : NULL; if (pafLastLineVal == NULL || pafThisLineVal == NULL || panLastLineId == NULL || panThisLineId == NULL || (hMaskBand != NULL && pabyMaskLine == NULL)) { CPLError(CE_Failure, CPLE_OutOfMemory, "Could not allocate enough memory for temporary buffers"); CPLFree( panThisLineId ); CPLFree( panLastLineId ); CPLFree( pafThisLineVal ); CPLFree( pafLastLineVal ); CPLFree( pabyMaskLine ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* Get the geotransform, if there is one, so we can convert the */ /* vectors into georeferenced coordinates. */ /* -------------------------------------------------------------------- */ GDALDatasetH hSrcDS = GDALGetBandDataset( hSrcBand ); double adfGeoTransform[6] = { 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 }; if( hSrcDS ) GDALGetGeoTransform( hSrcDS, adfGeoTransform ); /* -------------------------------------------------------------------- */ /* The first pass over the raster is only used to build up the */ /* polygon id map so we will know in advance what polygons are */ /* what on the second pass. */ /* -------------------------------------------------------------------- */ int iY; GDALRasterFPolygonEnumerator oFirstEnum(nConnectedness); for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) { eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, pafThisLineVal, nXSize, 1, GDT_Float32, 0, 0 ); if( eErr == CE_None && hMaskBand != NULL ) eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, pafThisLineVal ); if( iY == 0 ) oFirstEnum.ProcessLine( NULL, pafThisLineVal, NULL, panThisLineId, nXSize ); else oFirstEnum.ProcessLine( pafLastLineVal, pafThisLineVal, panLastLineId, panThisLineId, nXSize ); // swap lines float * pafTmp = pafLastLineVal; pafLastLineVal = pafThisLineVal; pafThisLineVal = pafTmp; GInt32 * panTmp = panThisLineId; panThisLineId = panLastLineId; panLastLineId = panTmp; /* -------------------------------------------------------------------- */ /* Report progress, and support interrupts. */ /* -------------------------------------------------------------------- */ if( eErr == CE_None && !pfnProgress( 0.10 * ((iY+1) / (double) nYSize), "", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; } } /* -------------------------------------------------------------------- */ /* Make a pass through the maps, ensuring every polygon id */ /* points to the final id it should use, not an intermediate */ /* value. */ /* -------------------------------------------------------------------- */ oFirstEnum.CompleteMerges(); /* -------------------------------------------------------------------- */ /* Initialize ids to -1 to serve as a nodata value for the */ /* previous line, and past the beginning and end of the */ /* scanlines. */ /* -------------------------------------------------------------------- */ int iX; panThisLineId[0] = -1; panThisLineId[nXSize+1] = -1; for( iX = 0; iX < nXSize+2; iX++ ) panLastLineId[iX] = -1; /* -------------------------------------------------------------------- */ /* We will use a new enumerator for the second pass primariliy */ /* so we can preserve the first pass map. */ /* -------------------------------------------------------------------- */ GDALRasterFPolygonEnumerator oSecondEnum(nConnectedness); RPolygonF **papoPoly = (RPolygonF **) CPLCalloc(sizeof(RPolygonF*),oFirstEnum.nNextPolygonId); /* ==================================================================== */ /* Second pass during which we will actually collect polygon */ /* edges as geometries. */ /* ==================================================================== */ for( iY = 0; eErr == CE_None && iY < nYSize+1; iY++ ) { /* -------------------------------------------------------------------- */ /* Read the image data. */ /* -------------------------------------------------------------------- */ if( iY < nYSize ) { eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, pafThisLineVal, nXSize, 1, GDT_Float32, 0, 0 ); if( eErr == CE_None && hMaskBand != NULL ) eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, pafThisLineVal ); } if( eErr != CE_None ) continue; /* -------------------------------------------------------------------- */ /* Determine what polygon the various pixels belong to (redoing */ /* the same thing done in the first pass above). */ /* -------------------------------------------------------------------- */ if( iY == nYSize ) { for( iX = 0; iX < nXSize+2; iX++ ) panThisLineId[iX] = -1; } else if( iY == 0 ) oSecondEnum.ProcessLine( NULL, pafThisLineVal, NULL, panThisLineId+1, nXSize ); else oSecondEnum.ProcessLine( pafLastLineVal, pafThisLineVal, panLastLineId+1, panThisLineId+1, nXSize ); /* -------------------------------------------------------------------- */ /* Add polygon edges to our polygon list for the pixel */ /* boundaries within and above this line. */ /* -------------------------------------------------------------------- */ for( iX = 0; iX < nXSize+1; iX++ ) { AddEdges( panThisLineId, panLastLineId, oFirstEnum.panPolyIdMap, oFirstEnum.pafPolyValue, papoPoly, iX, iY ); } /* -------------------------------------------------------------------- */ /* Periodically we scan out polygons and write out those that */ /* haven't been added to on the last line as we can be sure */ /* they are complete. */ /* -------------------------------------------------------------------- */ if( iY % 8 == 7 ) { for( iX = 0; eErr == CE_None && iX < oSecondEnum.nNextPolygonId; iX++ ) { if( papoPoly[iX] && papoPoly[iX]->nLastLineUpdated < iY-1 ) { if( hMaskBand == NULL || !GDALFloatEquals(papoPoly[iX]->fPolyValue, GP_NODATA_MARKER) ) { eErr = EmitPolygonToLayer( hOutLayer, iPixValField, papoPoly[iX], adfGeoTransform ); } delete papoPoly[iX]; papoPoly[iX] = NULL; } } } /* -------------------------------------------------------------------- */ /* Swap pixel value, and polygon id lines to be ready for the */ /* next line. */ /* -------------------------------------------------------------------- */ float *pafTmp = pafLastLineVal; pafLastLineVal = pafThisLineVal; pafThisLineVal = pafTmp; GInt32 *panTmp = panThisLineId; panThisLineId = panLastLineId; panLastLineId = panTmp; /* -------------------------------------------------------------------- */ /* Report progress, and support interrupts. */ /* -------------------------------------------------------------------- */ if( eErr == CE_None && !pfnProgress( 0.10 + 0.90 * ((iY+1) / (double) nYSize), "", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; } } /* -------------------------------------------------------------------- */ /* Make a cleanup pass for all unflushed polygons. */ /* -------------------------------------------------------------------- */ for( iX = 0; eErr == CE_None && iX < oSecondEnum.nNextPolygonId; iX++ ) { if( papoPoly[iX] ) { if( hMaskBand == NULL || !GDALFloatEquals(papoPoly[iX]->fPolyValue, GP_NODATA_MARKER) ) { eErr = EmitPolygonToLayer( hOutLayer, iPixValField, papoPoly[iX], adfGeoTransform ); } delete papoPoly[iX]; papoPoly[iX] = NULL; } } /* -------------------------------------------------------------------- */ /* Cleanup */ /* -------------------------------------------------------------------- */ CPLFree( panThisLineId ); CPLFree( panLastLineId ); CPLFree( pafThisLineVal ); CPLFree( pafLastLineVal ); CPLFree( pabyMaskLine ); CPLFree( papoPoly ); return eErr; #endif // OGR_ENABLED }
CPLErr CPL_STDCALL GDALSieveFilter( GDALRasterBandH hSrcBand, GDALRasterBandH hMaskBand, GDALRasterBandH hDstBand, int nSizeThreshold, int nConnectedness, char **papszOptions, GDALProgressFunc pfnProgress, void * pProgressArg ) { VALIDATE_POINTER1( hSrcBand, "GDALSieveFilter", CE_Failure ); VALIDATE_POINTER1( hDstBand, "GDALSieveFilter", CE_Failure ); if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; /* -------------------------------------------------------------------- */ /* Allocate working buffers. */ /* -------------------------------------------------------------------- */ CPLErr eErr = CE_None; int nXSize = GDALGetRasterBandXSize( hSrcBand ); int nYSize = GDALGetRasterBandYSize( hSrcBand ); GInt32 *panLastLineVal = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); GInt32 *panThisLineVal = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); GInt32 *panLastLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); GInt32 *panThisLineId = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); GInt32 *panThisLineWriteVal = (GInt32 *) VSIMalloc2(sizeof(GInt32), nXSize); GByte *pabyMaskLine = (hMaskBand != NULL) ? (GByte *) VSIMalloc(nXSize) : NULL; if (panLastLineVal == NULL || panThisLineVal == NULL || panLastLineId == NULL || panThisLineId == NULL || panThisLineWriteVal == NULL || (hMaskBand != NULL && pabyMaskLine == NULL)) { CPLError(CE_Failure, CPLE_OutOfMemory, "Could not allocate enough memory for temporary buffers"); CPLFree( panThisLineId ); CPLFree( panLastLineId ); CPLFree( panThisLineVal ); CPLFree( panLastLineVal ); CPLFree( panThisLineWriteVal ); CPLFree( pabyMaskLine ); return CE_Failure; } /* -------------------------------------------------------------------- */ /* The first pass over the raster is only used to build up the */ /* polygon id map so we will know in advance what polygons are */ /* what on the second pass. */ /* -------------------------------------------------------------------- */ int iY, iX, iPoly; GDALRasterPolygonEnumerator oFirstEnum( nConnectedness ); std::vector<int> anPolySizes; for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) { eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); if( eErr == CE_None && hMaskBand != NULL ) eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); if( iY == 0 ) oFirstEnum.ProcessLine( NULL, panThisLineVal, NULL, panThisLineId, nXSize ); else oFirstEnum.ProcessLine( panLastLineVal, panThisLineVal, panLastLineId, panThisLineId, nXSize ); /* -------------------------------------------------------------------- */ /* Accumulate polygon sizes. */ /* -------------------------------------------------------------------- */ if( oFirstEnum.nNextPolygonId > (int) anPolySizes.size() ) anPolySizes.resize( oFirstEnum.nNextPolygonId ); for( iX = 0; iX < nXSize; iX++ ) { iPoly = panThisLineId[iX]; CPLAssert( iPoly >= 0 ); anPolySizes[iPoly] += 1; } /* -------------------------------------------------------------------- */ /* swap this/last lines. */ /* -------------------------------------------------------------------- */ GInt32 *panTmp = panLastLineVal; panLastLineVal = panThisLineVal; panThisLineVal = panTmp; panTmp = panThisLineId; panThisLineId = panLastLineId; panLastLineId = panTmp; /* -------------------------------------------------------------------- */ /* Report progress, and support interrupts. */ /* -------------------------------------------------------------------- */ if( eErr == CE_None && !pfnProgress( 0.25 * ((iY+1) / (double) nYSize), "", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; } } /* -------------------------------------------------------------------- */ /* Make a pass through the maps, ensuring every polygon id */ /* points to the final id it should use, not an intermediate */ /* value. */ /* -------------------------------------------------------------------- */ oFirstEnum.CompleteMerges(); /* -------------------------------------------------------------------- */ /* Push the sizes of merged polygon fragments into the the */ /* merged polygon id's count. */ /* -------------------------------------------------------------------- */ for( iPoly = 0; iPoly < oFirstEnum.nNextPolygonId; iPoly++ ) { if( oFirstEnum.panPolyIdMap[iPoly] != iPoly ) { anPolySizes[oFirstEnum.panPolyIdMap[iPoly]] += anPolySizes[iPoly]; anPolySizes[iPoly] = 0; } } /* -------------------------------------------------------------------- */ /* We will use a new enumerator for the second pass primariliy */ /* so we can preserve the first pass map. */ /* -------------------------------------------------------------------- */ GDALRasterPolygonEnumerator oSecondEnum( nConnectedness ); std::vector<int> anBigNeighbour; anBigNeighbour.resize( anPolySizes.size() ); for( iPoly = 0; iPoly < (int) anPolySizes.size(); iPoly++ ) anBigNeighbour[iPoly] = -1; /* ==================================================================== */ /* Second pass ... identify the largest neighbour for each */ /* polygon. */ /* ==================================================================== */ for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) { /* -------------------------------------------------------------------- */ /* Read the image data. */ /* -------------------------------------------------------------------- */ eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); if( eErr == CE_None && hMaskBand != NULL ) eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); if( eErr != CE_None ) continue; /* -------------------------------------------------------------------- */ /* Determine what polygon the various pixels belong to (redoing */ /* the same thing done in the first pass above). */ /* -------------------------------------------------------------------- */ if( iY == 0 ) oSecondEnum.ProcessLine( NULL, panThisLineVal, NULL, panThisLineId, nXSize ); else oSecondEnum.ProcessLine( panLastLineVal, panThisLineVal, panLastLineId, panThisLineId, nXSize ); /* -------------------------------------------------------------------- */ /* Check our neighbours, and update our biggest neighbour map */ /* as appropriate. */ /* -------------------------------------------------------------------- */ for( iX = 0; iX < nXSize; iX++ ) { if( iY > 0 ) { CompareNeighbour( panThisLineId[iX], panLastLineId[iX], oFirstEnum.panPolyIdMap, oFirstEnum.panPolyValue, anPolySizes, anBigNeighbour ); if( iX > 0 && nConnectedness == 8 ) CompareNeighbour( panThisLineId[iX], panLastLineId[iX-1], oFirstEnum.panPolyIdMap, oFirstEnum.panPolyValue, anPolySizes, anBigNeighbour ); if( iX < nXSize-1 && nConnectedness == 8 ) CompareNeighbour( panThisLineId[iX], panLastLineId[iX+1], oFirstEnum.panPolyIdMap, oFirstEnum.panPolyValue, anPolySizes, anBigNeighbour ); } if( iX > 0 ) CompareNeighbour( panThisLineId[iX], panThisLineId[iX-1], oFirstEnum.panPolyIdMap, oFirstEnum.panPolyValue, anPolySizes, anBigNeighbour ); // We don't need to compare to next pixel or next line // since they will be compared to us. } /* -------------------------------------------------------------------- */ /* Swap pixel value, and polygon id lines to be ready for the */ /* next line. */ /* -------------------------------------------------------------------- */ GInt32 *panTmp = panLastLineVal; panLastLineVal = panThisLineVal; panThisLineVal = panTmp; panTmp = panThisLineId; panThisLineId = panLastLineId; panLastLineId = panTmp; /* -------------------------------------------------------------------- */ /* Report progress, and support interrupts. */ /* -------------------------------------------------------------------- */ if( eErr == CE_None && !pfnProgress( 0.25 + 0.25 * ((iY+1) / (double) nYSize), "", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; } } /* -------------------------------------------------------------------- */ /* If our biggest neighbour is still smaller than the */ /* threshold, then try tracking to that polygons biggest */ /* neighbour, and so forth. */ /* -------------------------------------------------------------------- */ int nFailedMerges = 0; int nIsolatedSmall = 0; int nSieveTargets = 0; for( iPoly = 0; iPoly < (int) anPolySizes.size(); iPoly++ ) { if( oFirstEnum.panPolyIdMap[iPoly] != iPoly ) continue; // Ignore nodata polygons. if( oFirstEnum.panPolyValue[iPoly] == GP_NODATA_MARKER ) continue; // Don't try to merge polygons larger than the threshold. if( anPolySizes[iPoly] >= nSizeThreshold ) { anBigNeighbour[iPoly] = -1; continue; } nSieveTargets++; // if we have no neighbours but we are small, what shall we do? if( anBigNeighbour[iPoly] == -1 ) { nIsolatedSmall++; continue; } // If our biggest neighbour is larger than the threshold // then we are golden. if( anPolySizes[anBigNeighbour[iPoly]] >= nSizeThreshold ) continue; #ifdef notdef // Will our neighbours biggest neighbour do? // Eventually we need something sort of recursive here with // loop detection. if( anPolySizes[anBigNeighbour[anBigNeighbour[iPoly]]] >= nSizeThreshold ) { anBigNeighbour[iPoly] = anBigNeighbour[anBigNeighbour[iPoly]]; continue; } #endif nFailedMerges++; anBigNeighbour[iPoly] = -1; } CPLDebug( "GDALSieveFilter", "Small Polygons: %d, Isolated: %d, Unmergable: %d", nSieveTargets, nIsolatedSmall, nFailedMerges ); /* ==================================================================== */ /* Make a third pass over the image, actually applying the */ /* merges. We reuse the second enumerator but preserve the */ /* "final maps" from the first. */ /* ==================================================================== */ oSecondEnum.Clear(); for( iY = 0; eErr == CE_None && iY < nYSize; iY++ ) { /* -------------------------------------------------------------------- */ /* Read the image data. */ /* -------------------------------------------------------------------- */ eErr = GDALRasterIO( hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal, nXSize, 1, GDT_Int32, 0, 0 ); memcpy( panThisLineWriteVal, panThisLineVal, 4 * nXSize ); if( eErr == CE_None && hMaskBand != NULL ) eErr = GPMaskImageData( hMaskBand, pabyMaskLine, iY, nXSize, panThisLineVal ); if( eErr != CE_None ) continue; /* -------------------------------------------------------------------- */ /* Determine what polygon the various pixels belong to (redoing */ /* the same thing done in the first pass above). */ /* -------------------------------------------------------------------- */ if( iY == 0 ) oSecondEnum.ProcessLine( NULL, panThisLineVal, NULL, panThisLineId, nXSize ); else oSecondEnum.ProcessLine( panLastLineVal, panThisLineVal, panLastLineId, panThisLineId, nXSize ); /* -------------------------------------------------------------------- */ /* Reprocess the actual pixel values according to the polygon */ /* merging, and write out this line of image data. */ /* -------------------------------------------------------------------- */ for( iX = 0; iX < nXSize; iX++ ) { int iThisPoly = oFirstEnum.panPolyIdMap[panThisLineId[iX]]; if( anBigNeighbour[iThisPoly] != -1 ) { panThisLineWriteVal[iX] = oFirstEnum.panPolyValue[ anBigNeighbour[iThisPoly]]; } } /* -------------------------------------------------------------------- */ /* Write the update data out. */ /* -------------------------------------------------------------------- */ eErr = GDALRasterIO( hDstBand, GF_Write, 0, iY, nXSize, 1, panThisLineWriteVal, nXSize, 1, GDT_Int32, 0, 0 ); /* -------------------------------------------------------------------- */ /* Swap pixel value, and polygon id lines to be ready for the */ /* next line. */ /* -------------------------------------------------------------------- */ GInt32 *panTmp = panLastLineVal; panLastLineVal = panThisLineVal; panThisLineVal = panTmp; panTmp = panThisLineId; panThisLineId = panLastLineId; panLastLineId = panTmp; /* -------------------------------------------------------------------- */ /* Report progress, and support interrupts. */ /* -------------------------------------------------------------------- */ if( eErr == CE_None && !pfnProgress( 0.5 + 0.5 * ((iY+1) / (double) nYSize), "", pProgressArg ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; } } /* -------------------------------------------------------------------- */ /* Cleanup */ /* -------------------------------------------------------------------- */ CPLFree( panThisLineId ); CPLFree( panLastLineId ); CPLFree( panThisLineVal ); CPLFree( panLastLineVal ); CPLFree( panThisLineWriteVal ); CPLFree( pabyMaskLine ); return eErr; }