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 2720 : static CPLErr GPMaskImageData(GDALRasterBandH hMaskBand, GByte *pabyMaskLine,
51 : int iY, int nXSize, DataType *panImageLine)
52 :
53 : {
54 2720 : const CPLErr eErr = GDALRasterIO(hMaskBand, GF_Read, 0, iY, nXSize, 1,
55 : pabyMaskLine, nXSize, 1, GDT_Byte, 0, 0);
56 2720 : if (eErr != CE_None)
57 0 : return eErr;
58 :
59 840682 : for (int i = 0; i < nXSize; i++)
60 : {
61 837962 : if (pabyMaskLine[i] == 0)
62 105692 : panImageLine[i] = GP_NODATA_MARKER;
63 : }
64 :
65 2720 : return CE_None;
66 : }
67 :
68 : /************************************************************************/
69 : /* GDALPolygonizeT() */
70 : /************************************************************************/
71 :
72 : template <class DataType, class EqualityTest>
73 52 : 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 52 : VALIDATE_POINTER1(hSrcBand, "GDALPolygonize", CE_Failure);
81 52 : VALIDATE_POINTER1(hOutLayer, "GDALPolygonize", CE_Failure);
82 :
83 52 : if (pfnProgress == nullptr)
84 15 : pfnProgress = GDALDummyProgress;
85 :
86 52 : const int nConnectedness =
87 52 : CSLFetchNameValue(papszOptions, "8CONNECTED") ? 8 : 4;
88 :
89 : /* -------------------------------------------------------------------- */
90 : /* Confirm our output layer will support feature creation. */
91 : /* -------------------------------------------------------------------- */
92 52 : 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 52 : const int nXSize = GDALGetRasterBandXSize(hSrcBand);
104 52 : const int nYSize = GDALGetRasterBandYSize(hSrcBand);
105 52 : 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 52 : DataType *panLastLineVal =
112 52 : static_cast<DataType *>(VSI_MALLOC2_VERBOSE(sizeof(DataType), nXSize));
113 52 : DataType *panThisLineVal =
114 52 : static_cast<DataType *>(VSI_MALLOC2_VERBOSE(sizeof(DataType), nXSize));
115 52 : GInt32 *panLastLineId =
116 52 : static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
117 52 : GInt32 *panThisLineId =
118 52 : static_cast<GInt32 *>(VSI_MALLOC2_VERBOSE(sizeof(GInt32), nXSize));
119 :
120 52 : GByte *pabyMaskLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
121 :
122 52 : if (panLastLineVal == nullptr || panThisLineVal == nullptr ||
123 52 : 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 52 : double adfGeoTransform[6] = {0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
139 52 : bool bGotGeoTransform = false;
140 : const char *pszDatasetForGeoRef =
141 52 : CSLFetchNameValue(papszOptions, "DATASET_FOR_GEOREF");
142 52 : 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 50 : GDALDatasetH hSrcDS = GDALGetBandDataset(hSrcBand);
155 50 : if (hSrcDS)
156 20 : bGotGeoTransform =
157 20 : GDALGetGeoTransform(hSrcDS, adfGeoTransform) == CE_None;
158 : }
159 52 : if (!bGotGeoTransform)
160 : {
161 30 : adfGeoTransform[0] = 0;
162 30 : adfGeoTransform[1] = 1;
163 30 : adfGeoTransform[2] = 0;
164 30 : adfGeoTransform[3] = 0;
165 30 : adfGeoTransform[4] = 0;
166 30 : 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 104 : GDALRasterPolygonEnumeratorT<DataType, EqualityTest> oFirstEnum(
175 : nConnectedness);
176 :
177 52 : CPLErr eErr = CE_None;
178 :
179 1490 : for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
180 : {
181 1438 : eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1, panThisLineVal,
182 : nXSize, 1, eDT, 0, 0);
183 :
184 1438 : if (eErr == CE_None && hMaskBand != nullptr)
185 1360 : eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
186 : panThisLineVal);
187 :
188 1438 : if (eErr != CE_None)
189 0 : break;
190 :
191 1438 : if (iY == 0)
192 52 : eErr = oFirstEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
193 : panThisLineId, nXSize)
194 52 : ? CE_None
195 : : CE_Failure;
196 : else
197 1386 : eErr = oFirstEnum.ProcessLine(panLastLineVal, panThisLineVal,
198 : panLastLineId, panThisLineId, nXSize)
199 1386 : ? CE_None
200 : : CE_Failure;
201 :
202 1438 : if (eErr != CE_None)
203 0 : break;
204 :
205 : // Swap lines.
206 1438 : std::swap(panLastLineVal, panThisLineVal);
207 1438 : std::swap(panLastLineId, panThisLineId);
208 :
209 : /* --------------------------------------------------------------------
210 : */
211 : /* Report progress, and support interrupts. */
212 : /* --------------------------------------------------------------------
213 : */
214 1438 : 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 52 : if (eErr == CE_None)
228 52 : 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 104 : GDALRasterPolygonEnumeratorT<DataType, EqualityTest> oSecondEnum(
235 : nConnectedness);
236 :
237 104 : OGRPolygonWriter<DataType> oPolygonWriter{hOutLayer, iPixValField,
238 : adfGeoTransform};
239 52 : Polygonizer<GInt32, DataType> oPolygonizer{-1, &oPolygonWriter};
240 52 : TwoArm *paoLastLineArm =
241 52 : static_cast<TwoArm *>(VSI_CALLOC_VERBOSE(sizeof(TwoArm), nXSize + 2));
242 52 : TwoArm *paoThisLineArm =
243 52 : static_cast<TwoArm *>(VSI_CALLOC_VERBOSE(sizeof(TwoArm), nXSize + 2));
244 :
245 52 : if (paoThisLineArm == nullptr || paoLastLineArm == nullptr)
246 : {
247 0 : eErr = CE_Failure;
248 : }
249 : else
250 : {
251 1252 : for (int i = 0; i < nXSize + 2; ++i)
252 : {
253 1200 : 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 1542 : for (int iY = 0; eErr == CE_None && iY < nYSize + 1; iY++)
262 : {
263 : /* --------------------------------------------------------------------
264 : */
265 : /* Read the image data. */
266 : /* --------------------------------------------------------------------
267 : */
268 1490 : if (iY < nYSize)
269 : {
270 1438 : eErr = GDALRasterIO(hSrcBand, GF_Read, 0, iY, nXSize, 1,
271 : panThisLineVal, nXSize, 1, eDT, 0, 0);
272 1438 : if (eErr == CE_None && hMaskBand != nullptr)
273 1360 : eErr = GPMaskImageData(hMaskBand, pabyMaskLine, iY, nXSize,
274 : panThisLineVal);
275 : }
276 :
277 1490 : 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 1490 : if (iY == nYSize)
287 : {
288 1148 : for (int iX = 0; iX < nXSize; iX++)
289 1096 : panThisLineId[iX] =
290 : decltype(oPolygonizer)::THE_OUTER_POLYGON_ID;
291 : }
292 1438 : else if (iY == 0)
293 : {
294 52 : eErr = oSecondEnum.ProcessLine(nullptr, panThisLineVal, nullptr,
295 : panThisLineId, nXSize)
296 52 : ? CE_None
297 : : CE_Failure;
298 : }
299 : else
300 : {
301 1386 : eErr = oSecondEnum.ProcessLine(panLastLineVal, panThisLineVal,
302 : panLastLineId, panThisLineId, nXSize)
303 1386 : ? CE_None
304 : : CE_Failure;
305 : }
306 :
307 1490 : if (eErr != CE_None)
308 0 : continue;
309 :
310 1490 : if (iY < nYSize)
311 : {
312 422103 : 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 420665 : panLastLineId[iX] =
318 420665 : panThisLineId[iX] == -1
319 420665 : ? -1
320 367819 : : oFirstEnum.panPolyIdMap[panThisLineId[iX]];
321 : }
322 :
323 1438 : if (!oPolygonizer.processLine(panLastLineId, panLastLineVal,
324 : paoThisLineArm, paoLastLineArm, iY,
325 : nXSize))
326 : {
327 0 : eErr = CE_Failure;
328 : }
329 : else
330 : {
331 1438 : eErr = oPolygonWriter.getErr();
332 : }
333 : }
334 : else
335 : {
336 52 : if (!oPolygonizer.processLine(panThisLineId, panLastLineVal,
337 : paoThisLineArm, paoLastLineArm, iY,
338 : nXSize))
339 : {
340 0 : eErr = CE_Failure;
341 : }
342 : else
343 : {
344 52 : eErr = oPolygonWriter.getErr();
345 : }
346 : }
347 :
348 1490 : 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 1490 : std::swap(panLastLineVal, panThisLineVal);
358 1490 : std::swap(panLastLineId, panThisLineId);
359 1490 : std::swap(paoThisLineArm, paoLastLineArm);
360 :
361 : /* --------------------------------------------------------------------
362 : */
363 : /* Report progress, and support interrupts. */
364 : /* --------------------------------------------------------------------
365 : */
366 1490 : if (!pfnProgress(0.10 + 0.90 * ((iY + 1) / static_cast<double>(nYSize)),
367 : "", pProgressArg))
368 : {
369 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
370 0 : eErr = CE_Failure;
371 : }
372 : }
373 :
374 : /* -------------------------------------------------------------------- */
375 : /* Cleanup */
376 : /* -------------------------------------------------------------------- */
377 52 : CPLFree(panThisLineId);
378 52 : CPLFree(panLastLineId);
379 52 : CPLFree(panThisLineVal);
380 52 : CPLFree(panLastLineVal);
381 52 : CPLFree(paoThisLineArm);
382 52 : CPLFree(paoLastLineArm);
383 52 : CPLFree(pabyMaskLine);
384 :
385 52 : return eErr;
386 : }
387 :
388 : /******************************************************************************/
389 : /* GDALFloatEquals() */
390 : /* Code from: */
391 : /* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
392 : /******************************************************************************/
393 88 : GBool GDALFloatEquals(float A, float B)
394 : {
395 : // This function will allow maxUlps-1 floats between A and B.
396 88 : const int maxUlps = MAX_ULPS;
397 :
398 : // Make sure maxUlps is non-negative and small enough that the default NAN
399 : // won't compare as equal to anything.
400 : #if MAX_ULPS <= 0 || MAX_ULPS >= 4 * 1024 * 1024
401 : #error "Invalid MAX_ULPS"
402 : #endif
403 :
404 : // This assignation could violate strict aliasing. It causes a warning with
405 : // gcc -O2. Use of memcpy preferred. Credits for Even Rouault. Further info
406 : // at http://trac.osgeo.org/gdal/ticket/4005#comment:6
407 88 : int aInt = 0;
408 88 : memcpy(&aInt, &A, 4);
409 :
410 : // Make aInt lexicographically ordered as a twos-complement int.
411 88 : if (aInt < 0)
412 10 : aInt = INT_MIN - aInt;
413 :
414 : // Make bInt lexicographically ordered as a twos-complement int.
415 88 : int bInt = 0;
416 88 : memcpy(&bInt, &B, 4);
417 :
418 88 : if (bInt < 0)
419 10 : bInt = INT_MIN - bInt;
420 : #ifdef COMPAT_WITH_ICC_CONVERSION_CHECK
421 : const int intDiff =
422 : abs(static_cast<int>(static_cast<GUIntBig>(static_cast<GIntBig>(aInt) -
423 : static_cast<GIntBig>(bInt)) &
424 : 0xFFFFFFFFU));
425 : #else
426 : // To make -ftrapv happy we compute the diff on larger type and
427 : // cast down later.
428 88 : const int intDiff = abs(static_cast<int>(static_cast<GIntBig>(aInt) -
429 : static_cast<GIntBig>(bInt)));
430 : #endif
431 88 : if (intDiff <= maxUlps)
432 30 : return true;
433 58 : return false;
434 : }
435 :
436 : /************************************************************************/
437 : /* GDALPolygonize() */
438 : /************************************************************************/
439 :
440 : /**
441 : * Create polygon coverage from raster data.
442 : *
443 : * This function creates vector polygons for all connected regions of pixels in
444 : * the raster sharing a common pixel value. Optionally each polygon may be
445 : * labeled with the pixel value in an attribute. Optionally a mask band
446 : * can be provided to determine which pixels are eligible for processing.
447 : *
448 : * Note that currently the source pixel band values are read into a
449 : * signed 64bit integer buffer (Int64), so floating point or complex
450 : * bands will be implicitly truncated before processing. If you want to use a
451 : * version using 32bit float buffers, see GDALFPolygonize().
452 : *
453 : * Polygon features will be created on the output layer, with polygon
454 : * geometries representing the polygons. The polygon geometries will be
455 : * in the georeferenced coordinate system of the image (based on the
456 : * geotransform of the source dataset). It is acceptable for the output
457 : * layer to already have features. Note that GDALPolygonize() does not
458 : * set the coordinate system on the output layer. Application code should
459 : * do this when the layer is created, presumably matching the raster
460 : * coordinate system.
461 : *
462 : * The algorithm used attempts to minimize memory use so that very large
463 : * rasters can be processed. However, if the raster has many polygons
464 : * or very large/complex polygons, the memory use for holding polygon
465 : * enumerations and active polygon geometries may grow to be quite large.
466 : *
467 : * The algorithm will generally produce very dense polygon geometries, with
468 : * edges that follow exactly on pixel boundaries for all non-interior pixels.
469 : * For non-thematic raster data (such as satellite images) the result will
470 : * essentially be one small polygon per pixel, and memory and output layer
471 : * sizes will be substantial. The algorithm is primarily intended for
472 : * relatively simple thematic imagery, masks, and classification results.
473 : *
474 : * @param hSrcBand the source raster band to be processed.
475 : * @param hMaskBand an optional mask band. All pixels in the mask band with a
476 : * value other than zero will be considered suitable for collection as
477 : * polygons.
478 : * @param hOutLayer the vector feature layer to which the polygons should
479 : * be written.
480 : * @param iPixValField the attribute field index indicating the feature
481 : * attribute into which the pixel value of the polygon should be written. Or
482 : * -1 to indicate that the pixel value must not be written.
483 : * @param papszOptions a name/value list of additional options
484 : * <ul>
485 : * <li>8CONNECTED=8: May be set to "8" to use 8 connectedness.
486 : * Otherwise 4 connectedness will be applied to the algorithm</li>
487 : * <li>DATASET_FOR_GEOREF=dataset_name: Name of a dataset from which to read
488 : * the geotransform. This useful if hSrcBand has no related dataset, which is
489 : * typical for mask bands.</li>
490 : * </ul>
491 : * @param pfnProgress callback for reporting algorithm progress matching the
492 : * GDALProgressFunc() semantics. May be NULL.
493 : * @param pProgressArg callback argument passed to pfnProgress.
494 : *
495 : * @return CE_None on success or CE_Failure on a failure.
496 : */
497 :
498 51 : CPLErr CPL_STDCALL GDALPolygonize(GDALRasterBandH hSrcBand,
499 : GDALRasterBandH hMaskBand,
500 : OGRLayerH hOutLayer, int iPixValField,
501 : char **papszOptions,
502 : GDALProgressFunc pfnProgress,
503 : void *pProgressArg)
504 :
505 : {
506 51 : return GDALPolygonizeT<std::int64_t, IntEqualityTest>(
507 : hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions, pfnProgress,
508 51 : pProgressArg, GDT_Int64);
509 : }
510 :
511 : /************************************************************************/
512 : /* GDALFPolygonize() */
513 : /************************************************************************/
514 :
515 : /**
516 : * Create polygon coverage from raster data.
517 : *
518 : * This function creates vector polygons for all connected regions of pixels in
519 : * the raster sharing a common pixel value. Optionally each polygon may be
520 : * labeled with the pixel value in an attribute. Optionally a mask band
521 : * can be provided to determine which pixels are eligible for processing.
522 : *
523 : * The source pixel band values are read into a 32bit float buffer. If you want
524 : * to use a (probably faster) version using signed 32bit integer buffer, see
525 : * GDALPolygonize().
526 : *
527 : * Polygon features will be created on the output layer, with polygon
528 : * geometries representing the polygons. The polygon geometries will be
529 : * in the georeferenced coordinate system of the image (based on the
530 : * geotransform of the source dataset). It is acceptable for the output
531 : * layer to already have features. Note that GDALFPolygonize() does not
532 : * set the coordinate system on the output layer. Application code should
533 : * do this when the layer is created, presumably matching the raster
534 : * coordinate system.
535 : *
536 : * The algorithm used attempts to minimize memory use so that very large
537 : * rasters can be processed. However, if the raster has many polygons
538 : * or very large/complex polygons, the memory use for holding polygon
539 : * enumerations and active polygon geometries may grow to be quite large.
540 : *
541 : * The algorithm will generally produce very dense polygon geometries, with
542 : * edges that follow exactly on pixel boundaries for all non-interior pixels.
543 : * For non-thematic raster data (such as satellite images) the result will
544 : * essentially be one small polygon per pixel, and memory and output layer
545 : * sizes will be substantial. The algorithm is primarily intended for
546 : * relatively simple thematic imagery, masks, and classification results.
547 : *
548 : * @param hSrcBand the source raster band to be processed.
549 : * @param hMaskBand an optional mask band. All pixels in the mask band with a
550 : * value other than zero will be considered suitable for collection as
551 : * polygons.
552 : * @param hOutLayer the vector feature layer to which the polygons should
553 : * be written.
554 : * @param iPixValField the attribute field index indicating the feature
555 : * attribute into which the pixel value of the polygon should be written. Or
556 : * -1 to indicate that the pixel value must not be written.
557 : * @param papszOptions a name/value list of additional options
558 : * <ul>
559 : * <li>8CONNECTED=8: May be set to "8" to use 8 connectedness.
560 : * Otherwise 4 connectedness will be applied to the algorithm</li>
561 : * <li>DATASET_FOR_GEOREF=dataset_name: Name of a dataset from which to read
562 : * the geotransform. This useful if hSrcBand has no related dataset, which is
563 : * typical for mask bands.</li>
564 : * </ul>
565 : * @param pfnProgress callback for reporting algorithm progress matching the
566 : * GDALProgressFunc() semantics. May be NULL.
567 : * @param pProgressArg callback argument passed to pfnProgress.
568 : *
569 : * @return CE_None on success or CE_Failure on a failure.
570 : *
571 : * @since GDAL 1.9.0
572 : */
573 :
574 1 : CPLErr CPL_STDCALL GDALFPolygonize(GDALRasterBandH hSrcBand,
575 : GDALRasterBandH hMaskBand,
576 : OGRLayerH hOutLayer, int iPixValField,
577 : char **papszOptions,
578 : GDALProgressFunc pfnProgress,
579 : void *pProgressArg)
580 :
581 : {
582 1 : return GDALPolygonizeT<float, FloatEqualityTest>(
583 : hSrcBand, hMaskBand, hOutLayer, iPixValField, papszOptions, pfnProgress,
584 1 : pProgressArg, GDT_Float32);
585 : }
|