1 /******************************************************************************
2  *
3  * Project:  Contour Generation
4  * Purpose:  Core algorithm implementation for contour line generation.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
9  * Copyright (c) 2003, Applied Coherent Technology Corporation, www.actgate.com
10  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
11  * Copyright (c) 2018, Oslandia <infos at oslandia dot com>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "level_generator.h"
33 #include "polygon_ring_appender.h"
34 #include "utility.h"
35 #include "contour_generator.h"
36 #include "segment_merger.h"
37 
38 #include "gdal.h"
39 #include "gdal_alg.h"
40 #include "cpl_conv.h"
41 #include "cpl_string.h"
42 #include "ogr_api.h"
43 #include "ogr_srs_api.h"
44 #include "ogr_geometry.h"
45 
OGRPolygonContourWriter(double dfLevelMin,double dfLevelMax,const OGRMultiPolygon & multipoly,void * pInfo)46 static CPLErr OGRPolygonContourWriter( double dfLevelMin, double dfLevelMax,
47                                 const OGRMultiPolygon& multipoly,
48                                 void *pInfo )
49 
50 {
51     OGRContourWriterInfo *poInfo = static_cast<OGRContourWriterInfo *>(pInfo);
52 
53     OGRFeatureDefnH hFDefn =
54         OGR_L_GetLayerDefn( static_cast<OGRLayerH>(poInfo->hLayer) );
55 
56     OGRFeatureH hFeat = OGR_F_Create( hFDefn );
57 
58     if( poInfo->nIDField != -1 )
59         OGR_F_SetFieldInteger( hFeat, poInfo->nIDField, poInfo->nNextID++ );
60 
61     if( poInfo->nElevFieldMin != -1 )
62         OGR_F_SetFieldDouble( hFeat, poInfo->nElevFieldMin, dfLevelMin );
63 
64     if( poInfo->nElevFieldMax != -1 )
65         OGR_F_SetFieldDouble( hFeat, poInfo->nElevFieldMax, dfLevelMax );
66 
67     const bool bHasZ = wkbHasZ(OGR_FD_GetGeomType(hFDefn));
68     OGRGeometryH hGeom = OGR_G_CreateGeometry(
69         bHasZ ? wkbMultiPolygon25D : wkbMultiPolygon );
70 
71     for ( int iPart = 0; iPart < multipoly.getNumGeometries(); iPart++ )
72     {
73         OGRPolygon* poNewPoly = new OGRPolygon();
74         const OGRPolygon* poPolygon = static_cast<const OGRPolygon*>(multipoly.getGeometryRef(iPart));
75 
76         for ( int iRing = 0; iRing < poPolygon->getNumInteriorRings() + 1; iRing++ )
77         {
78             const OGRLinearRing* poRing = iRing == 0 ?
79                 poPolygon->getExteriorRing()
80                 : poPolygon->getInteriorRing(iRing - 1);
81 
82             OGRLinearRing* poNewRing = new OGRLinearRing();
83             for ( int iPoint = 0; iPoint < poRing->getNumPoints(); iPoint++ )
84             {
85                 const double dfX = poInfo->adfGeoTransform[0]
86                     + poInfo->adfGeoTransform[1] * poRing->getX(iPoint)
87                     + poInfo->adfGeoTransform[2] * poRing->getY(iPoint);
88                 const double dfY = poInfo->adfGeoTransform[3]
89                     + poInfo->adfGeoTransform[4] * poRing->getX(iPoint)
90                     + poInfo->adfGeoTransform[5] * poRing->getY(iPoint);
91                 if( bHasZ )
92                     OGR_G_SetPoint( OGRGeometry::ToHandle( poNewRing ), iPoint, dfX, dfY, dfLevelMax );
93                 else
94                     OGR_G_SetPoint_2D( OGRGeometry::ToHandle( poNewRing ), iPoint, dfX, dfY );
95             }
96             poNewPoly->addRingDirectly( poNewRing );
97         }
98         OGR_G_AddGeometryDirectly( hGeom, OGRGeometry::ToHandle( poNewPoly ) );
99     }
100 
101     OGR_F_SetGeometryDirectly( hFeat, hGeom );
102 
103     const OGRErr eErr =
104         OGR_L_CreateFeature(static_cast<OGRLayerH>(poInfo->hLayer), hFeat);
105     OGR_F_Destroy( hFeat );
106 
107     return eErr == OGRERR_NONE ? CE_None : CE_Failure;
108 }
109 
110 struct PolygonContourWriter
111 {
112     CPL_DISALLOW_COPY_ASSIGN(PolygonContourWriter)
113 
PolygonContourWriterPolygonContourWriter114     explicit PolygonContourWriter( OGRContourWriterInfo* poInfo, double minLevel ) : poInfo_( poInfo ), previousLevel_( minLevel ) {}
115 
startPolygonPolygonContourWriter116     void startPolygon( double level )
117     {
118         previousLevel_ = currentLevel_;
119         currentGeometry_.reset( new OGRMultiPolygon() );
120         currentLevel_ = level;
121     }
endPolygonPolygonContourWriter122     void endPolygon()
123     {
124         if ( currentGeometry_ && currentPart_ )
125         {
126             currentGeometry_->addGeometryDirectly(currentPart_);
127         }
128 
129         OGRPolygonContourWriter( previousLevel_, currentLevel_, *currentGeometry_, poInfo_ );
130 
131         currentGeometry_.reset( nullptr );
132         currentPart_ = nullptr;
133     }
134 
addPartPolygonContourWriter135     void addPart( const marching_squares::LineString& ring )
136     {
137         if ( currentGeometry_ && currentPart_ )
138         {
139             currentGeometry_->addGeometryDirectly(currentPart_);
140         }
141 
142         OGRLinearRing* poNewRing = new OGRLinearRing();
143         poNewRing->setNumPoints( int(ring.size()) );
144         int iPoint = 0;
145         for ( const auto& p : ring )
146         {
147             poNewRing->setPoint( iPoint, p.x, p.y );
148             iPoint++;
149         }
150         currentPart_ = new OGRPolygon();
151         currentPart_->addRingDirectly(poNewRing);
152     }
addInteriorRingPolygonContourWriter153     void addInteriorRing( const marching_squares::LineString& ring )
154     {
155         OGRLinearRing* poNewRing = new OGRLinearRing();
156         for ( const auto& p : ring )
157         {
158             poNewRing->addPoint( p.x, p.y );
159         }
160         currentPart_->addRingDirectly(poNewRing);
161     }
162 
163     std::unique_ptr<OGRMultiPolygon> currentGeometry_ = {};
164     OGRPolygon* currentPart_ = nullptr;
165     OGRContourWriterInfo* poInfo_ = nullptr;
166     double currentLevel_ = 0;
167     double previousLevel_;
168 };
169 
170 struct GDALRingAppender
171 {
172     CPL_DISALLOW_COPY_ASSIGN(GDALRingAppender)
173 
GDALRingAppenderGDALRingAppender174     GDALRingAppender(GDALContourWriter write, void *data)
175     : write_( write )
176     , data_( data )
177     {}
178 
addLineGDALRingAppender179     void addLine( double level, marching_squares::LineString& ls, bool /*closed*/ )
180     {
181         const size_t sz = ls.size();
182         std::vector<double> xs( sz ), ys ( sz );
183         size_t i = 0;
184         for ( const auto& pt : ls ) {
185             xs[i] = pt.x;
186             ys[i] = pt.y;
187             i++;
188         }
189 
190         if ( write_(level, int(sz), &xs[0], &ys[0], data_) != CE_None )
191             CPLError( CE_Failure, CPLE_AppDefined, "cannot write linestring" );
192     }
193 
194 private:
195     GDALContourWriter write_;
196     void *data_;
197 };
198 
199 /************************************************************************/
200 /* ==================================================================== */
201 /*                   Additional C Callable Functions                    */
202 /* ==================================================================== */
203 /************************************************************************/
204 
205 /************************************************************************/
206 /*                          OGRContourWriter()                          */
207 /************************************************************************/
208 
OGRContourWriter(double dfLevel,int nPoints,double * padfX,double * padfY,void * pInfo)209 CPLErr OGRContourWriter( double dfLevel,
210                          int nPoints, double *padfX, double *padfY,
211                          void *pInfo )
212 
213 {
214     OGRContourWriterInfo *poInfo = static_cast<OGRContourWriterInfo *>(pInfo);
215 
216     OGRFeatureDefnH hFDefn =
217         OGR_L_GetLayerDefn( static_cast<OGRLayerH>(poInfo->hLayer) );
218 
219     OGRFeatureH hFeat = OGR_F_Create( hFDefn );
220 
221     if( poInfo->nIDField != -1 )
222         OGR_F_SetFieldInteger( hFeat, poInfo->nIDField, poInfo->nNextID++ );
223 
224     if( poInfo->nElevField != -1 )
225         OGR_F_SetFieldDouble( hFeat, poInfo->nElevField, dfLevel );
226 
227     const bool bHasZ = wkbHasZ(OGR_FD_GetGeomType(hFDefn));
228     OGRGeometryH hGeom = OGR_G_CreateGeometry(
229         bHasZ ? wkbLineString25D : wkbLineString );
230 
231     for( int iPoint = nPoints - 1; iPoint >= 0; iPoint-- )
232     {
233         const double dfX = poInfo->adfGeoTransform[0]
234                         + poInfo->adfGeoTransform[1] * padfX[iPoint]
235                         + poInfo->adfGeoTransform[2] * padfY[iPoint];
236         const double dfY = poInfo->adfGeoTransform[3]
237                         + poInfo->adfGeoTransform[4] * padfX[iPoint]
238                         + poInfo->adfGeoTransform[5] * padfY[iPoint];
239         if( bHasZ )
240             OGR_G_SetPoint( hGeom, iPoint, dfX, dfY, dfLevel );
241         else
242             OGR_G_SetPoint_2D( hGeom, iPoint, dfX, dfY );
243     }
244 
245     OGR_F_SetGeometryDirectly( hFeat, hGeom );
246 
247     const OGRErr eErr =
248         OGR_L_CreateFeature(static_cast<OGRLayerH>(poInfo->hLayer), hFeat);
249     OGR_F_Destroy( hFeat );
250 
251     return eErr == OGRERR_NONE ? CE_None : CE_Failure;
252 }
253 
254 /************************************************************************/
255 /*                        GDALContourGenerate()                         */
256 /************************************************************************/
257 
258 /**
259  * Create vector contours from raster DEM.
260  *
261  * This function is kept for compatibility reason and will call the new
262  * variant GDALContourGenerateEx that is more extensible and provide more
263  * options.
264  *
265  * Details about the algorithm are also given in the documentation of the
266  * new GDALContourenerateEx function.
267  *
268  * @param hBand The band to read raster data from.  The whole band will be
269  * processed.
270  *
271  * @param dfContourInterval The elevation interval between contours generated.
272  *
273  * @param dfContourBase The "base" relative to which contour intervals are
274  * applied.  This is normally zero, but could be different.  To generate 10m
275  * contours at 5, 15, 25, ... the ContourBase would be 5.
276  *
277  * @param  nFixedLevelCount The number of fixed levels. If this is greater than
278  * zero, then fixed levels will be used, and ContourInterval and ContourBase
279  * are ignored.
280  *
281  * @param padfFixedLevels The list of fixed contour levels at which contours
282  * should be generated.  It will contain FixedLevelCount entries, and may be
283  * NULL if fixed levels are disabled (FixedLevelCount = 0).
284  *
285  * @param bUseNoData If TRUE the dfNoDataValue will be used.
286  *
287  * @param dfNoDataValue The value to use as a "nodata" value. That is, a
288  * pixel value which should be ignored in generating contours as if the value
289  * of the pixel were not known.
290  *
291  * @param hLayer The layer to which new contour vectors will be written.
292  * Each contour will have a LINESTRING geometry attached to it.   This
293  * is really of type OGRLayerH, but void * is used to avoid pulling the
294  * ogr_api.h file in here.
295  *
296  * @param iIDField If not -1 this will be used as a field index to indicate
297  * where a unique id should be written for each feature (contour) written.
298  *
299  * @param iElevField If not -1 this will be used as a field index to indicate
300  * where the elevation value of the contour should be written.
301  *
302  * @param pfnProgress A GDALProgressFunc that may be used to report progress
303  * to the user, or to interrupt the algorithm.  May be NULL if not required.
304  *
305  * @param pProgressArg The callback data for the pfnProgress function.
306  *
307  * @return CE_None on success or CE_Failure if an error occurs.
308  */
309 
GDALContourGenerate(GDALRasterBandH hBand,double dfContourInterval,double dfContourBase,int nFixedLevelCount,double * padfFixedLevels,int bUseNoData,double dfNoDataValue,void * hLayer,int iIDField,int iElevField,GDALProgressFunc pfnProgress,void * pProgressArg)310 CPLErr GDALContourGenerate( GDALRasterBandH hBand,
311                                     double dfContourInterval, double dfContourBase,
312                                     int nFixedLevelCount, double *padfFixedLevels,
313                                     int bUseNoData, double dfNoDataValue,
314                                     void *hLayer, int iIDField, int iElevField,
315                                     GDALProgressFunc pfnProgress, void *pProgressArg )
316 {
317     char** options = nullptr;
318     if ( nFixedLevelCount > 0 ) {
319         std::string values = "FIXED_LEVELS=";
320         for ( int i = 0; i < nFixedLevelCount; i++ ) {
321             const int sz = 32;
322             char* newValue = new char[sz+1];
323             if ( i == nFixedLevelCount - 1 ) {
324                 CPLsnprintf( newValue, sz+1, "%f", padfFixedLevels[i] );
325             }
326             else {
327                 CPLsnprintf( newValue, sz+1, "%f,", padfFixedLevels[i] );
328             }
329             values = values + std::string( newValue );
330             delete[] newValue;
331         }
332         options = CSLAddString( options, values.c_str() );
333     }
334     else if ( dfContourInterval != 0.0 ) {
335         options = CSLAppendPrintf( options, "LEVEL_INTERVAL=%f", dfContourInterval );
336     }
337 
338     if ( dfContourBase != 0.0 ) {
339         options = CSLAppendPrintf( options, "LEVEL_BASE=%f", dfContourBase );
340     }
341 
342     if ( bUseNoData ) {
343         options = CSLAppendPrintf( options, "NODATA=%.19g", dfNoDataValue );
344     }
345     if ( iIDField != -1 ) {
346         options = CSLAppendPrintf( options, "ID_FIELD=%d", iIDField );
347     }
348     if ( iElevField != -1 ) {
349         options = CSLAppendPrintf( options, "ELEV_FIELD=%d", iElevField );
350     }
351 
352     CPLErr err =  GDALContourGenerateEx( hBand, hLayer, options, pfnProgress, pProgressArg );
353     CSLDestroy( options );
354 
355     return err;
356 }
357 
358 /**
359  * Create vector contours from raster DEM.
360  *
361  * This algorithm is an implementation of "Marching squares" [1] that will
362  * generate contour vectors for the input raster band on the requested set
363  * of contour levels.  The vector contours are written to the passed in OGR
364  * vector layer. Also, a NODATA value may be specified to identify pixels
365  * that should not be considered in contour line generation.
366  *
367  * The gdal/apps/gdal_contour.cpp mainline can be used as an example of
368  * how to use this function.
369  *
370  * [1] see https://en.wikipedia.org/wiki/Marching_squares
371  *
372  * ALGORITHM RULES
373 
374 For contouring purposes raster pixel values are assumed to represent a point
375 value at the center of the corresponding pixel region.  For the purpose of
376 contour generation we virtually connect each pixel center to the values to
377 the left, right, top and bottom.  We assume that the pixel value is linearly
378 interpolated between the pixel centers along each line, and determine where
379 (if any) contour lines will appear along these line segments.  Then the
380 contour crossings are connected.
381 
382 This means that contour lines' nodes will not actually be on pixel edges, but
383 rather along vertical and horizontal lines connecting the pixel centers.
384 
385 \verbatim
386 General Case:
387 
388       5 |                  | 3
389      -- + ---------------- + --
390         |                  |
391         |                  |
392         |                  |
393         |                  |
394      10 +                  |
395         |\                 |
396         | \                |
397      -- + -+-------------- + --
398      12 |  10              | 1
399 
400 Saddle Point:
401 
402       5 |                  | 12
403      -- + -------------+-- + --
404         |               \  |
405         |                 \|
406         |                  +
407         |                  |
408         +                  |
409         |\                 |
410         | \                |
411      -- + -+-------------- + --
412      12 |                  | 1
413 
414 or:
415 
416       5 |                  | 12
417      -- + -------------+-- + --
418         |          __/     |
419         |      ___/        |
420         |  ___/          __+
421         | /           __/  |
422         +'         __/     |
423         |       __/        |
424         |   ,__/           |
425      -- + -+-------------- + --
426      12 |                  | 1
427 \endverbatim
428 
429 Nodata:
430 
431 In the "nodata" case we treat the whole nodata pixel as a no-mans land.
432 We extend the corner pixels near the nodata out to half way and then
433 construct extra lines from those points to the center which is assigned
434 an averaged value from the two nearby points (in this case (12+3+5)/3).
435 
436 \verbatim
437       5 |                  | 3
438      -- + ---------------- + --
439         |                  |
440         |                  |
441         |      6.7         |
442         |        +---------+ 3
443      10 +___     |
444         |   \____+ 10
445         |        |
446      -- + -------+        +
447      12 |       12           (nodata)
448 
449 \endverbatim
450 
451  *
452  * @param hBand The band to read raster data from.  The whole band will be
453  * processed.
454  *
455  * @param hLayer The layer to which new contour vectors will be written.
456  * Each contour will have a LINESTRING geometry attached to it
457  * (or POLYGON if POLYGONIZE=YES). This is really of type OGRLayerH, but
458  * void * is used to avoid pulling the ogr_api.h file in here.
459  *
460  * @param pfnProgress A GDALProgressFunc that may be used to report progress
461  * to the user, or to interrupt the algorithm.  May be NULL if not required.
462  *
463  * @param pProgressArg The callback data for the pfnProgress function.
464  *
465  * @param options List of options
466  *
467  * Options:
468  *
469  *   LEVEL_INTERVAL=f
470  *
471  * The elevation interval between contours generated.
472  *
473  *   LEVEL_BASE=f
474  *
475  * The "base" relative to which contour intervals are
476  * applied.  This is normally zero, but could be different.  To generate 10m
477  * contours at 5, 15, 25, ... the LEVEL_BASE would be 5.
478  *
479  *   LEVEL_EXP_BASE=f
480  *
481  * If greater than 0, contour levels are generated on an
482  * exponential scale. Levels will then be generated by LEVEL_EXP_BASE^k
483  * where k is a positive integer.
484  *
485  *   FIXED_LEVELS=f[,f]*
486  *
487  * The list of fixed contour levels at which contours should be generated.
488  * This option has precedence on LEVEL_INTERVAL
489  *
490  *   NODATA=f
491  *
492  * The value to use as a "nodata" value. That is, a pixel value which
493  * should be ignored in generating contours as if the value of the pixel
494  * were not known.
495  *
496  *   ID_FIELD=d
497  *
498  * This will be used as a field index to indicate where a unique id should
499  * be written for each feature (contour) written.
500  *
501  *   ELEV_FIELD=d
502  *
503  * This will be used as a field index to indicate where the elevation value
504  * of the contour should be written. Only used in line contouring mode.
505  *
506  *   ELEV_FIELD_MIN=d
507  *
508  * This will be used as a field index to indicate where the minimum elevation value
509  * of the polygon contour should be written. Only used in polygonal contouring mode.
510  *
511  *   ELEV_FIELD_MAX=d
512  *
513  * This will be used as a field index to indicate where the maximum elevation value
514  * of the polygon contour should be written. Only used in polygonal contouring mode.
515  *
516  *   POLYGONIZE=YES|NO
517  *
518  * If YES, contour polygons will be created, rather than polygon lines.
519  *
520  *
521  * @return CE_None on success or CE_Failure if an error occurs.
522  */
GDALContourGenerateEx(GDALRasterBandH hBand,void * hLayer,CSLConstList options,GDALProgressFunc pfnProgress,void * pProgressArg)523 CPLErr GDALContourGenerateEx( GDALRasterBandH hBand, void *hLayer,
524                                       CSLConstList options,
525                                       GDALProgressFunc pfnProgress, void *pProgressArg )
526 {
527     VALIDATE_POINTER1( hBand, "GDALContourGenerateEx", CE_Failure );
528 
529     if( pfnProgress == nullptr )
530         pfnProgress = GDALDummyProgress;
531 
532     double contourInterval = 0.0;
533     const char* opt = CSLFetchNameValue( options, "LEVEL_INTERVAL" );
534     if ( opt ) {
535         contourInterval = CPLAtof( opt );
536     }
537 
538     double contourBase = 0.0;
539     opt = CSLFetchNameValue( options, "LEVEL_BASE" );
540     if ( opt ) {
541         contourBase = CPLAtof( opt );
542     }
543 
544     double expBase = 0.0;
545     opt = CSLFetchNameValue( options, "LEVEL_EXP_BASE" );
546     if ( opt ) {
547         expBase = CPLAtof( opt );
548     }
549 
550     std::vector<double> fixedLevels;
551     opt = CSLFetchNameValue( options, "FIXED_LEVELS" );
552     if ( opt ) {
553         char** values = CSLTokenizeStringComplex( opt, ",", FALSE, FALSE );
554         fixedLevels.resize( CSLCount( values ) );
555         for ( size_t i = 0; i < fixedLevels.size(); i++ ) {
556             fixedLevels[i] = CPLAtof(values[i]);
557         }
558         CSLDestroy( values );
559     }
560 
561     bool useNoData = false;
562     double noDataValue = 0.0;
563     opt = CSLFetchNameValue( options, "NODATA" );
564     if ( opt ) {
565         useNoData = true;
566         noDataValue = CPLAtof( opt );
567         if( GDALGetRasterDataType(hBand) == GDT_Float32 )
568         {
569             int bClamped = FALSE;
570             int bRounded = FALSE;
571             noDataValue = GDALAdjustValueToDataType(GDT_Float32,
572                                                     noDataValue,
573                                                     &bClamped, &bRounded );
574         }
575     }
576 
577     int idField = -1;
578     opt = CSLFetchNameValue( options, "ID_FIELD" );
579     if ( opt ) {
580         idField = atoi( opt );
581     }
582 
583     int elevField = -1;
584     opt = CSLFetchNameValue( options, "ELEV_FIELD" );
585     if ( opt ) {
586         elevField = atoi( opt );
587     }
588 
589     int elevFieldMin = -1;
590     opt = CSLFetchNameValue( options, "ELEV_FIELD_MIN" );
591     if ( opt ) {
592         elevFieldMin = atoi( opt );
593     }
594 
595     int elevFieldMax = -1;
596     opt = CSLFetchNameValue( options, "ELEV_FIELD_MAX" );
597     if ( opt ) {
598         elevFieldMax = atoi( opt );
599     }
600 
601     bool polygonize = CPLFetchBool( options, "POLYGONIZE", false );
602 
603     using namespace marching_squares;
604 
605     OGRContourWriterInfo oCWI;
606     oCWI.hLayer = static_cast<OGRLayerH>(hLayer);
607     oCWI.nElevField = elevField;
608     oCWI.nElevFieldMin = elevFieldMin;
609     oCWI.nElevFieldMax = elevFieldMax;
610     oCWI.nIDField = idField;
611     oCWI.adfGeoTransform[0] = 0.0;
612     oCWI.adfGeoTransform[1] = 1.0;
613     oCWI.adfGeoTransform[2] = 0.0;
614     oCWI.adfGeoTransform[3] = 0.0;
615     oCWI.adfGeoTransform[4] = 0.0;
616     oCWI.adfGeoTransform[5] = 1.0;
617     GDALDatasetH hSrcDS = GDALGetBandDataset( hBand );
618     if( hSrcDS != nullptr )
619         GDALGetGeoTransform( hSrcDS, oCWI.adfGeoTransform );
620     oCWI.nNextID = 0;
621 
622     bool ok = false;
623     try
624     {
625         if ( polygonize )
626         {
627             int bSuccess;
628             PolygonContourWriter w( &oCWI, GDALGetRasterMinimum( hBand, &bSuccess ) );
629             typedef PolygonRingAppender<PolygonContourWriter> RingAppender;
630             RingAppender appender( w );
631             if ( ! fixedLevels.empty() ) {
632                 FixedLevelRangeIterator levels( &fixedLevels[0], fixedLevels.size(), GDALGetRasterMaximum( hBand, &bSuccess ) );
633                 SegmentMerger<RingAppender, FixedLevelRangeIterator> writer(appender, levels, /* polygonize */ true);
634                 ContourGeneratorFromRaster<decltype(writer), FixedLevelRangeIterator> cg( hBand, useNoData, noDataValue, writer, levels );
635                 ok = cg.process( pfnProgress, pProgressArg );
636             }
637             else if ( expBase > 0.0 ) {
638                 ExponentialLevelRangeIterator levels( expBase );
639                 SegmentMerger<RingAppender, ExponentialLevelRangeIterator> writer(appender, levels, /* polygonize */ true);
640                 ContourGeneratorFromRaster<decltype(writer), ExponentialLevelRangeIterator> cg( hBand, useNoData, noDataValue, writer, levels );
641                 ok = cg.process( pfnProgress, pProgressArg );
642             }
643             else {
644                 IntervalLevelRangeIterator levels( contourBase, contourInterval );
645                 SegmentMerger<RingAppender, IntervalLevelRangeIterator> writer(appender, levels, /* polygonize */ true);
646                 ContourGeneratorFromRaster<decltype(writer), IntervalLevelRangeIterator> cg( hBand, useNoData, noDataValue, writer, levels );
647                 ok = cg.process( pfnProgress, pProgressArg );
648             }
649         }
650         else
651         {
652             GDALRingAppender appender(OGRContourWriter, &oCWI);
653             if ( ! fixedLevels.empty() ) {
654                 FixedLevelRangeIterator levels( &fixedLevels[0], fixedLevels.size() );
655                 SegmentMerger<GDALRingAppender, FixedLevelRangeIterator> writer(appender, levels, /* polygonize */ false);
656                 ContourGeneratorFromRaster<decltype(writer), FixedLevelRangeIterator> cg( hBand, useNoData, noDataValue, writer, levels );
657                 ok = cg.process( pfnProgress, pProgressArg );
658             }
659             else if ( expBase > 0.0 ) {
660                 ExponentialLevelRangeIterator levels( expBase );
661                 SegmentMerger<GDALRingAppender, ExponentialLevelRangeIterator> writer(appender, levels, /* polygonize */ false);
662                 ContourGeneratorFromRaster<decltype(writer), ExponentialLevelRangeIterator> cg( hBand, useNoData, noDataValue, writer, levels );
663                 ok = cg.process( pfnProgress, pProgressArg );
664             }
665             else {
666                 IntervalLevelRangeIterator levels( contourBase, contourInterval );
667                 SegmentMerger<GDALRingAppender, IntervalLevelRangeIterator> writer(appender, levels, /* polygonize */ false);
668                 ContourGeneratorFromRaster<decltype(writer), IntervalLevelRangeIterator> cg( hBand, useNoData, noDataValue, writer, levels );
669                 ok = cg.process( pfnProgress, pProgressArg );
670             }
671         }
672     }
673     catch (const std::exception & e)
674     {
675         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
676         return CE_Failure;
677     }
678     return ok ? CE_None : CE_Failure;
679 }
680 
681 /************************************************************************/
682 /*                           GDAL_CG_Create()                           */
683 /************************************************************************/
684 
685 namespace marching_squares {
686 
687 // Opaque type used by the C API
688 struct ContourGeneratorOpaque
689 {
690     typedef SegmentMerger<GDALRingAppender, IntervalLevelRangeIterator> SegmentMergerT;
691     typedef ContourGenerator<SegmentMergerT, IntervalLevelRangeIterator> ContourGeneratorT;
692 
ContourGeneratorOpaquemarching_squares::ContourGeneratorOpaque693     ContourGeneratorOpaque( int nWidth, int nHeight, int bNoDataSet, double dfNoDataValue,
694                             double dfContourInterval, double dfContourBase,
695                             GDALContourWriter pfnWriter, void *pCBData )
696         : levels( dfContourBase, dfContourInterval )
697         , writer( pfnWriter, pCBData )
698         , merger( writer, levels, /* polygonize */ false )
699         , contourGenerator( nWidth, nHeight, bNoDataSet != 0, dfNoDataValue, merger, levels )
700     {}
701 
702     IntervalLevelRangeIterator levels;
703     GDALRingAppender writer;
704     SegmentMergerT merger;
705     ContourGeneratorT contourGenerator;
706 };
707 
708 }
709 
710 /** Create contour generator */
711 GDALContourGeneratorH
GDAL_CG_Create(int nWidth,int nHeight,int bNoDataSet,double dfNoDataValue,double dfContourInterval,double dfContourBase,GDALContourWriter pfnWriter,void * pCBData)712 GDAL_CG_Create( int nWidth, int nHeight, int bNoDataSet, double dfNoDataValue,
713                 double dfContourInterval, double dfContourBase,
714                 GDALContourWriter pfnWriter, void *pCBData )
715 
716 {
717     auto cg = new marching_squares::ContourGeneratorOpaque( nWidth,
718                                                             nHeight,
719                                                             bNoDataSet,
720                                                             dfNoDataValue,
721                                                             dfContourInterval,
722                                                             dfContourBase,
723                                                             pfnWriter,
724                                                             pCBData );
725 
726     return reinterpret_cast<GDALContourGeneratorH>(cg);
727 }
728 
729 /************************************************************************/
730 /*                          GDAL_CG_FeedLine()                          */
731 /************************************************************************/
732 
733 /** Feed a line to the contour generator */
GDAL_CG_FeedLine(GDALContourGeneratorH hCG,double * padfScanline)734 CPLErr GDAL_CG_FeedLine( GDALContourGeneratorH hCG, double *padfScanline )
735 
736 {
737     VALIDATE_POINTER1( hCG, "GDAL_CG_FeedLine", CE_Failure );
738     return reinterpret_cast<marching_squares::ContourGeneratorOpaque*>(hCG)->contourGenerator.feedLine( padfScanline );
739 }
740 
741 /************************************************************************/
742 /*                          GDAL_CG_Destroy()                           */
743 /************************************************************************/
744 
745 /** Destroy contour generator */
GDAL_CG_Destroy(GDALContourGeneratorH hCG)746 void GDAL_CG_Destroy( GDALContourGeneratorH hCG )
747 
748 {
749     delete reinterpret_cast<marching_squares::ContourGeneratorOpaque*>(hCG);
750 }
751