Line data Source code
1 : /******************************************************************************
2 : * Project: GDAL
3 : * Purpose: Raster to Polygon Converter
4 : * Author: Frank Warmerdam, warmerdam@pobox.com
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2008, Frank Warmerdam
8 : * Copyright (c) 2009-2020, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "gdal_alg.h"
15 :
16 : #include <stddef.h>
17 : #include <stdio.h>
18 : #include <cstdlib>
19 : #include <string.h>
20 :
21 : #include <algorithm>
22 : #include <limits>
23 : #include <map>
24 : #include <memory>
25 : #include <utility>
26 : #include <vector>
27 :
28 : #include "gdal_alg_priv.h"
29 : #include "gdal.h"
30 : #include "ogr_api.h"
31 : #include "ogr_core.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_error.h"
34 : #include "cpl_progress.h"
35 : #include "cpl_string.h"
36 : #include "cpl_vsi.h"
37 :
38 : #include "polygonize_polygonizer.h"
39 :
40 : using namespace gdal::polygonizer;
41 :
42 : /************************************************************************/
43 : /* GPMaskImageData() */
44 : /* */
45 : /* Mask out image pixels to a special nodata value if the mask */
46 : /* band is zero. */
47 : /************************************************************************/
48 :
49 : template <class DataType>
50 3504 : static CPLErr GPMaskImageData(GDALRasterBandH hMaskBand, GByte *pabyMaskLine,
51 : int iY, int nXSize, DataType *panImageLine)
52 :
53 : {
54 3504 : const CPLErr eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1,
55 : pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0);
56 3504 : if (eErr != CE_None)
57 0 : return eErr;
58 :
59 856136 : for (int i = 0; i < nXSize; i++)
60 : {
61 852632 : if (pabyMaskLine[i] == 0)
62 105872 : panImageLine[i] = GP_NODATA_MARKER;
63 : }
64 :
65 3504 : return CE_None;
66 : }
67 :
68 : /************************************************************************/
69 : /* GDALPolygonizeT() */
70 : /************************************************************************/
71 :
72 : template <class DataType, class EqualityTest>
73 90 : static CPLErr GDALPolygonizeT(GDALRasterBandH hSrcBand,
74 : GDALRasterBandH hMaskBand, OGRLayerH hOutLayer,
75 : int iPixValField, char **papszOptions,
76 : GDALProgressFunc pfnProgress, void *pProgressArg,
77 : GDALDataType eDT)
78 :
79 : {
80 90 : VALIDATE_POINTER1(hSrcBand, "GDALPolygonize", CE_Failure);
81 90 : VALIDATE_POINTER1(hOutLayer, "GDALPolygonize", CE_Failure);
82 :
83 90 : if (pfnProgress == nullptr)
84 23 : pfnProgress = GDALDummyProgress;
85 :
86 90 : const int nConnectedness =
87 90 : CSLFetchNameValue(papszOptions, "8CONNECTED") ? 8 : 4;
88 :
89 : /* -------------------------------------------------------------------- */
90 : /* Confirm our output layer will support feature creation. */
91 : /* -------------------------------------------------------------------- */
92 90 : if (!OGR_L_TestCapability(hOutLayer, OLCSequentialWrite))
93 : {
94 0 : CPLError(CE_Failure, CPLE_AppDefined,
95 : "Output feature layer does not appear to support creation "
96 : "of features in GDALPolygonize().");
97 0 : return CE_Failure;
98 : }
99 :
100 : /* -------------------------------------------------------------------- */
101 : /* Allocate working buffers. */
102 : /* -------------------------------------------------------------------- */
103 90 : const int nXSize = GDALGetRasterBandXSize(hSrcBand);
104 90 : const int nYSize = GDALGetRasterBandYSize(hSrcBand);
105 90 : if (nXSize > std::numeric_limits<int>::max() - 2)
106 : {
107 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too wide raster");
108 0 : return CE_Failure;
109 : }
110 :
111 90 : DataType *panLastLineVal =
112 90 : static_cast<DataType *>(VSI_MALLOC2_VERBOSE(sizeof(DataType), nXSize));
113 90 : DataType *panThisLineVal =
114 90 : static_cast<DataType *>(VSI_MALLOC2_VERBOSE(sizeof(DataType), nXSize));
115 90 : GInt32 *panLastLineId =
116 90 : static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
117 90 : GInt32 *panThisLineId =
118 90 : static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
119 :
120 90 : GByte *pabyMaskLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
121 :
122 90 : if (panLastLineVal == nullptr || panThisLineVal == nullptr ||
123 90 : panLastLineId == nullptr || panThisLineId == nullptr ||
124 : pabyMaskLine == nullptr)
125 : {
126 0 : CPLFree(panThisLineId);
127 0 : CPLFree(panLastLineId);
128 0 : CPLFree(panThisLineVal);
129 0 : CPLFree(panLastLineVal);
130 0 : CPLFree(pabyMaskLine);
131 0 : return CE_Failure;
132 : }
133 :
134 : /* -------------------------------------------------------------------- */
135 : /* Get the geotransform, if there is one, so we can convert the */
136 : /* vectors into georeferenced coordinates. */
137 : /* -------------------------------------------------------------------- */
138 90 : double adfGeoTransform[6] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
139 90 : bool bGotGeoTransform = false;
140 : const char *pszDatasetForGeoRef =
141 90 : CSLFetchNameValue(papszOptions, "DATASET_FOR_GEOREF");
142 90 : if (pszDatasetForGeoRef)
143 : {
144 2 : GDALDatasetH hSrcDS = GDALOpen(pszDatasetForGeoRef, GA_ReadOnly);
145 2 : if (hSrcDS)
146 : {
147 2 : bGotGeoTransform =
148 2 : GDALGetGeoTransform(hSrcDS, adfGeoTransform) == CE_None;
149 2 : GDALClose(hSrcDS);
150 : }
151 : }
152 : else
153 : {
154 88 : GDALDatasetH hSrcDS = GDALGetBandDataset(hSrcBand);
155 88 : if (hSrcDS)
156 29 : bGotGeoTransform =
157 29 : GDALGetGeoTransform(hSrcDS, adfGeoTransform) == CE_None;
158 : }
159 90 : if (!bGotGeoTransform)
160 : {
161 59 : adfGeoTransform[0] = 0;
162 59 : adfGeoTransform[1] = 1;
163 59 : adfGeoTransform[2] = 0;
164 59 : adfGeoTransform[3] = 0;
165 59 : adfGeoTransform[4] = 0;
166 59 : adfGeoTransform[5] = 1;
167 : }
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* The first pass over the raster is only used to build up the */
171 : /* polygon id map so we will know in advance what polygons are */
172 : /* what on the second pass. */
173 : /* -------------------------------------------------------------------- */
174 180 : GDALRasterPolygonEnumeratorT<DataType, EqualityTest> oFirstEnum(
175 : nConnectedness);
176 :
177 90 : CPLErr eErr = CE_None;
178 :
179 1920 : for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
180 : {
181 1830 : eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
182 : nXSize, 1, eDT, 0, 0);
183 :
184 1830 : if (eErr == CE_None && hMaskBand != nullptr)
185 1752 : eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
186 : panThisLineVal);
187 :
188 1830 : if (eErr != CE_None)
189 0 : break;
190 :
191 1830 : if (iY == 0)
192 90 : eErr = oFirstEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
193 : panThisLineId, nXSize)
194 90 : ? CE_None
195 : : CE_Failure;
196 : else
197 1740 : eErr = oFirstEnum.ProcessLine(panLastLineVal, panThisLineVal,
198 : panLastLineId, panThisLineId, nXSize)
199 1740 : ? CE_None
200 : : CE_Failure;
201 :
202 1830 : if (eErr != CE_None)
203 0 : break;
204 :
205 : // Swap lines.
206 1830 : std::swap(panLastLineVal, panThisLineVal);
207 1830 : std::swap(panLastLineId, panThisLineId);
208 :
209 : /* --------------------------------------------------------------------
210 : */
211 : /* Report progress, and support interrupts. */
212 : /* --------------------------------------------------------------------
213 : */
214 1830 : if (!pfnProgress(0.10 * ((iY + 1) / static_cast<double>(nYSize)), "",
215 : pProgressArg))
216 : {
217 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
218 0 : eErr = CE_Failure;
219 : }
220 : }
221 :
222 : /* -------------------------------------------------------------------- */
223 : /* Make a pass through the maps, ensuring every polygon id */
224 : /* points to the final id it should use, not an intermediate */
225 : /* value. */
226 : /* -------------------------------------------------------------------- */
227 90 : if (eErr == CE_None)
228 90 : oFirstEnum.CompleteMerges();
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* We will use a new enumerator for the second pass primarily */
232 : /* so we can preserve the first pass map. */
233 : /* -------------------------------------------------------------------- */
234 180 : GDALRasterPolygonEnumeratorT<DataType, EqualityTest> oSecondEnum(
235 : nConnectedness);
236 :
237 180 : OGRPolygonWriter<DataType> oPolygonWriter{hOutLayer, iPixValField,
238 : adfGeoTransform};
239 90 : Polygonizer<GInt32, DataType> oPolygonizer{-1, &oPolygonWriter};
240 90 : TwoArm *paoLastLineArm =
241 90 : static_cast<TwoArm *>(VSI_CALLOC_VERBOSE(sizeof(TwoArm), nXSize + 2));
242 90 : TwoArm *paoThisLineArm =
243 90 : static_cast<TwoArm *>(VSI_CALLOC_VERBOSE(sizeof(TwoArm), nXSize + 2));
244 :
245 90 : if (paoThisLineArm == nullptr || paoLastLineArm == nullptr)
246 : {
247 0 : eErr = CE_Failure;
248 : }
249 : else
250 : {
251 1773 : for (int i = 0; i < nXSize + 2; ++i)
252 : {
253 1683 : paoLastLineArm[i].poPolyInside = oPolygonizer.getTheOuterPolygon();
254 : }
255 : }
256 :
257 : /* ==================================================================== */
258 : /* Second pass during which we will actually collect polygon */
259 : /* edges as geometries. */
260 : /* ==================================================================== */
261 2010 : for (int iY = 0; eErr == CE_None && iY < nYSize + 1; iY++)
262 : {
263 : /* --------------------------------------------------------------------
264 : */
265 : /* Read the image data. */
266 : /* --------------------------------------------------------------------
267 : */
268 1920 : if (iY < nYSize)
269 : {
270 1830 : eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1,
271 : panThisLineVal, nXSize, 1, eDT, 0, 0);
272 1830 : if (eErr == CE_None && hMaskBand != nullptr)
273 1752 : eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
274 : panThisLineVal);
275 : }
276 :
277 1920 : if (eErr != CE_None)
278 0 : continue;
279 :
280 : /* --------------------------------------------------------------------
281 : */
282 : /* Determine what polygon the various pixels belong to (redoing */
283 : /* the same thing done in the first pass above). */
284 : /* --------------------------------------------------------------------
285 : */
286 1920 : if (iY == nYSize)
287 : {
288 1593 : for (int iX = 0; iX < nXSize; iX++)
289 1503 : panThisLineId[iX] =
290 : decltype(oPolygonizer)::THE_OUTER_POLYGON_ID;
291 : }
292 1830 : else if (iY == 0)
293 : {
294 90 : eErr = oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
295 : panThisLineId, nXSize)
296 90 : ? CE_None
297 : : CE_Failure;
298 : }
299 : else
300 : {
301 1740 : eErr = oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
302 : panLastLineId, panThisLineId, nXSize)
303 1740 : ? CE_None
304 : : CE_Failure;
305 : }
306 :
307 1920 : if (eErr != CE_None)
308 0 : continue;
309 :
310 1920 : if (iY < nYSize)
311 : {
312 429830 : for (int iX = 0; iX < nXSize; iX++)
313 : {
314 : // TODO: maybe we can reserve -1 as the lookup result for -1 polygon id in the panPolyIdMap,
315 : // so the this expression becomes: panLastLineId[iX] = *(oFirstEnum.panPolyIdMap + panThisLineId[iX]).
316 : // This would eliminate the condition checking.
317 428000 : panLastLineId[iX] =
318 428000 : panThisLineId[iX] == -1
319 428000 : ? -1
320 375064 : : oFirstEnum.panPolyIdMap[panThisLineId[iX]];
321 : }
322 :
323 1830 : if (!oPolygonizer.processLine(panLastLineId, panLastLineVal,
324 : paoThisLineArm, paoLastLineArm, iY,
325 : nXSize))
326 : {
327 0 : eErr = CE_Failure;
328 : }
329 : else
330 : {
331 1830 : eErr = oPolygonWriter.getErr();
332 : }
333 : }
334 : else
335 : {
336 90 : if (!oPolygonizer.processLine(panThisLineId, panLastLineVal,
337 : paoThisLineArm, paoLastLineArm, iY,
338 : nXSize))
339 : {
340 0 : eErr = CE_Failure;
341 : }
342 : else
343 : {
344 90 : eErr = oPolygonWriter.getErr();
345 : }
346 : }
347 :
348 1920 : if (eErr != CE_None)
349 0 : continue;
350 :
351 : /* --------------------------------------------------------------------
352 : */
353 : /* Swap pixel value, and polygon id lines to be ready for the */
354 : /* next line. */
355 : /* --------------------------------------------------------------------
356 : */
357 1920 : std::swap(panLastLineVal, panThisLineVal);
358 1920 : std::swap(panLastLineId, panThisLineId);
359 1920 : std::swap(paoThisLineArm, paoLastLineArm);
360 :
361 : /* --------------------------------------------------------------------
362 : */
363 : /* Report progress, and support interrupts. */
364 : /* --------------------------------------------------------------------
365 : */
366 1920 : if (!pfnProgress(
367 1920 : std::min(1.0, 0.10 + 0.90 * ((iY + 1) /
368 1920 : static_cast<double>(nYSize))),
369 : "", pProgressArg))
370 : {
371 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
372 0 : eErr = CE_Failure;
373 : }
374 : }
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Cleanup */
378 : /* -------------------------------------------------------------------- */
379 90 : CPLFree(panThisLineId);
380 90 : CPLFree(panLastLineId);
381 90 : CPLFree(panThisLineVal);
382 90 : CPLFree(panLastLineVal);
383 90 : CPLFree(paoThisLineArm);
384 90 : CPLFree(paoLastLineArm);
385 90 : CPLFree(pabyMaskLine);
386 :
387 90 : return eErr;
388 : }
389 :
390 : /******************************************************************************/
391 : /* GDALFloatEquals() */
392 : /* Code from: */
393 : /* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
394 : /******************************************************************************/
395 1608 : GBool GDALFloatEquals(float A, float B)
396 : {
397 : // This function will allow maxUlps-1 floats between A and B.
398 1608 : const int maxUlps = MAX_ULPS;
399 :
400 : // Make sure maxUlps is non-negative and small enough that the default NAN
401 : // won't compare as equal to anything.
402 : #if MAX_ULPS <= 0 || MAX_ULPS >= 4 * 1024 * 1024
403 : #error "Invalid MAX_ULPS"
404 : #endif
405 :
406 : // This assignation could violate strict aliasing. It causes a warning with
407 : // gcc -O2. Use of memcpy preferred. Credits for Even Rouault. Further info
408 : // at http://trac.osgeo.org/gdal/ticket/4005#comment:6
409 1608 : int aInt = 0;
410 1608 : memcpy(&aInt, &A, 4);
411 :
412 : // Make aInt lexicographically ordered as a twos-complement int.
413 1608 : if (aInt < 0)
414 10 : aInt = INT_MIN - aInt;
415 :
416 : // Make bInt lexicographically ordered as a twos-complement int.
417 1608 : int bInt = 0;
418 1608 : memcpy(&bInt, &B, 4);
419 :
420 1608 : if (bInt < 0)
421 10 : bInt = INT_MIN - bInt;
422 : #ifdef COMPAT_WITH_ICC_CONVERSION_CHECK
423 : const int intDiff =
424 : abs(static_cast<int>(static_cast<GUIntBig>(static_cast<GIntBig>(aInt) -
425 : static_cast<GIntBig>(bInt)) &
426 : 0xFFFFFFFFU));
427 : #else
428 : // To make -ftrapv happy we compute the diff on larger type and
429 : // cast down later.
430 1608 : const int intDiff = abs(static_cast<int>(static_cast<GIntBig>(aInt) -
431 : static_cast<GIntBig>(bInt)));
432 : #endif
433 1608 : if (intDiff <= maxUlps)
434 270 : return true;
435 1338 : return false;
436 : }
437 :
438 : /************************************************************************/
439 : /* GDALPolygonize() */
440 : /************************************************************************/
441 :
442 : /**
443 : * Create polygon coverage from raster data.
444 : *
445 : * This function creates vector polygons for all connected regions of pixels in
446 : * the raster sharing a common pixel value. Optionally each polygon may be
447 : * labeled with the pixel value in an attribute. Optionally a mask band
448 : * can be provided to determine which pixels are eligible for processing.
449 : *
450 : * Note that currently the source pixel band values are read into a
451 : * signed 64bit integer buffer (Int64), so floating point or complex
452 : * bands will be implicitly truncated before processing. If you want to use a
453 : * version using 32bit float buffers, see GDALFPolygonize().
454 : *
455 : * Polygon features will be created on the output layer, with polygon
456 : * geometries representing the polygons. The polygon geometries will be
457 : * in the georeferenced coordinate system of the image (based on the
458 : * geotransform of the source dataset). It is acceptable for the output
459 : * layer to already have features. Note that GDALPolygonize() does not
460 : * set the coordinate system on the output layer. Application code should
461 : * do this when the layer is created, presumably matching the raster
462 : * coordinate system.
463 : *
464 : * The algorithm used attempts to minimize memory use so that very large
465 : * rasters can be processed. However, if the raster has many polygons
466 : * or very large/complex polygons, the memory use for holding polygon
467 : * enumerations and active polygon geometries may grow to be quite large.
468 : *
469 : * The algorithm will generally produce very dense polygon geometries, with
470 : * edges that follow exactly on pixel boundaries for all non-interior pixels.
471 : * For non-thematic raster data (such as satellite images) the result will
472 : * essentially be one small polygon per pixel, and memory and output layer
473 : * sizes will be substantial. The algorithm is primarily intended for
474 : * relatively simple thematic imagery, masks, and classification results.
475 : *
476 : * @param hSrcBand the source raster band to be processed.
477 : * @param hMaskBand an optional mask band. All pixels in the mask band with a
478 : * value other than zero will be considered suitable for collection as
479 : * polygons.
480 : * @param hOutLayer the vector feature layer to which the polygons should
481 : * be written.
482 : * @param iPixValField the attribute field index indicating the feature
483 : * attribute into which the pixel value of the polygon should be written. Or
484 : * -1 to indicate that the pixel value must not be written.
485 : * @param papszOptions a name/value list of additional options
486 : * <ul>
487 : * <li>8CONNECTED=8: May be set to "8" to use 8 connectedness.
488 : * Otherwise 4 connectedness will be applied to the algorithm</li>
489 : * <li>DATASET_FOR_GEOREF=dataset_name: Name of a dataset from which to read
490 : * the geotransform. This useful if hSrcBand has no related dataset, which is
491 : * typical for mask bands.</li>
492 : * </ul>
493 : * @param pfnProgress callback for reporting algorithm progress matching the
494 : * GDALProgressFunc() semantics. May be NULL.
495 : * @param pProgressArg callback argument passed to pfnProgress.
496 : *
497 : * @return CE_None on success or CE_Failure on a failure.
498 : */
499 :
500 88 : CPLErr CPL_STDCALL GDALPolygonize(GDALRasterBandH hSrcBand,
501 : GDALRasterBandH hMaskBand,
502 : OGRLayerH hOutLayer, int iPixValField,
503 : char **papszOptions,
504 : GDALProgressFunc pfnProgress,
505 : void *pProgressArg)
506 :
507 : {
508 88 : return GDALPolygonizeT<std::int64_t, IntEqualityTest>(
509 : hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions, pfnProgress,
510 88 : pProgressArg, GDT_Int64);
511 : }
512 :
513 : /************************************************************************/
514 : /* GDALFPolygonize() */
515 : /************************************************************************/
516 :
517 : /**
518 : * Create polygon coverage from raster data.
519 : *
520 : * This function creates vector polygons for all connected regions of pixels in
521 : * the raster sharing a common pixel value. Optionally each polygon may be
522 : * labeled with the pixel value in an attribute. Optionally a mask band
523 : * can be provided to determine which pixels are eligible for processing.
524 : *
525 : * The source pixel band values are read into a 32bit float buffer. If you want
526 : * to use a (probably faster) version using signed 32bit integer buffer, see
527 : * GDALPolygonize().
528 : *
529 : * Polygon features will be created on the output layer, with polygon
530 : * geometries representing the polygons. The polygon geometries will be
531 : * in the georeferenced coordinate system of the image (based on the
532 : * geotransform of the source dataset). It is acceptable for the output
533 : * layer to already have features. Note that GDALFPolygonize() does not
534 : * set the coordinate system on the output layer. Application code should
535 : * do this when the layer is created, presumably matching the raster
536 : * coordinate system.
537 : *
538 : * The algorithm used attempts to minimize memory use so that very large
539 : * rasters can be processed. However, if the raster has many polygons
540 : * or very large/complex polygons, the memory use for holding polygon
541 : * enumerations and active polygon geometries may grow to be quite large.
542 : *
543 : * The algorithm will generally produce very dense polygon geometries, with
544 : * edges that follow exactly on pixel boundaries for all non-interior pixels.
545 : * For non-thematic raster data (such as satellite images) the result will
546 : * essentially be one small polygon per pixel, and memory and output layer
547 : * sizes will be substantial. The algorithm is primarily intended for
548 : * relatively simple thematic imagery, masks, and classification results.
549 : *
550 : * @param hSrcBand the source raster band to be processed.
551 : * @param hMaskBand an optional mask band. All pixels in the mask band with a
552 : * value other than zero will be considered suitable for collection as
553 : * polygons.
554 : * @param hOutLayer the vector feature layer to which the polygons should
555 : * be written.
556 : * @param iPixValField the attribute field index indicating the feature
557 : * attribute into which the pixel value of the polygon should be written. Or
558 : * -1 to indicate that the pixel value must not be written.
559 : * @param papszOptions a name/value list of additional options
560 : * <ul>
561 : * <li>8CONNECTED=8: May be set to "8" to use 8 connectedness.
562 : * Otherwise 4 connectedness will be applied to the algorithm</li>
563 : * <li>DATASET_FOR_GEOREF=dataset_name: Name of a dataset from which to read
564 : * the geotransform. This useful if hSrcBand has no related dataset, which is
565 : * typical for mask bands.</li>
566 : * </ul>
567 : * @param pfnProgress callback for reporting algorithm progress matching the
568 : * GDALProgressFunc() semantics. May be NULL.
569 : * @param pProgressArg callback argument passed to pfnProgress.
570 : *
571 : * @return CE_None on success or CE_Failure on a failure.
572 : *
573 : * @since GDAL 1.9.0
574 : */
575 :
576 2 : CPLErr CPL_STDCALL GDALFPolygonize(GDALRasterBandH hSrcBand,
577 : GDALRasterBandH hMaskBand,
578 : OGRLayerH hOutLayer, int iPixValField,
579 : char **papszOptions,
580 : GDALProgressFunc pfnProgress,
581 : void *pProgressArg)
582 :
583 : {
584 2 : return GDALPolygonizeT<float, FloatEqualityTest>(
585 : hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions, pfnProgress,
586 2 : pProgressArg, GDT_Float32);
587 : }
|