GDAL
polygonize_polygonizer.cpp
1/******************************************************************************
2 * Project: GDAL
3 * Purpose: Implements The Two-Arm Chains EdgeTracing Algorithm
4 * Author: kikitte.lee
5 *
6 ******************************************************************************
7 * Copyright (c) 2023, kikitte.lee <kikitte.lee@gmail.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 ****************************************************************************/
27
30#include "polygonize_polygonizer.h"
31
32#include <algorithm>
33
34namespace gdal
35{
36namespace polygonizer
37{
38
39RPolygon::~RPolygon()
40{
41 for (auto &arc : oArcs)
42 {
43 delete arc;
44 }
45}
46
47IndexedArc RPolygon::newArc(bool bFollowRighthand)
48{
49 Arc *poArc = new Arc();
50 std::size_t iArcIndex = oArcs.size();
51 oArcs.push_back(poArc);
52 oArcRighthandFollow.push_back(bFollowRighthand);
53 oArcConnections.push_back(iArcIndex);
54 return IndexedArc{poArc, iArcIndex};
55}
56
57void RPolygon::setArcConnection(const IndexedArc &oArc,
58 const IndexedArc &oNextArc)
59{
60 oArcConnections[oArc.iIndex] = oNextArc.iIndex;
61}
62
63void RPolygon::updateBottomRightPos(IndexType iRow, IndexType iCol)
64{
65 iBottomRightRow = iRow;
66 iBottomRightCol = iCol;
67}
68
72static void ProcessArmConnections(TwoArm *poCurrent, TwoArm *poAbove,
73 TwoArm *poLeft)
74{
75 poCurrent->poPolyInside->updateBottomRightPos(poCurrent->iRow,
76 poCurrent->iCol);
77 poCurrent->bSolidVertical = poCurrent->poPolyInside != poLeft->poPolyInside;
78 poCurrent->bSolidHorizontal =
79 poCurrent->poPolyInside != poAbove->poPolyInside;
80 poCurrent->poPolyAbove = poAbove->poPolyInside;
81 poCurrent->poPolyLeft = poLeft->poPolyInside;
82
83 constexpr int BIT_CUR_HORIZ = 0;
84 constexpr int BIT_CUR_VERT = 1;
85 constexpr int BIT_LEFT = 2;
86 constexpr int BIT_ABOVE = 3;
87
88 const int nArmConnectionType =
89 (static_cast<int>(poAbove->bSolidVertical) << BIT_ABOVE) |
90 (static_cast<int>(poLeft->bSolidHorizontal) << BIT_LEFT) |
91 (static_cast<int>(poCurrent->bSolidVertical) << BIT_CUR_VERT) |
92 (static_cast<int>(poCurrent->bSolidHorizontal) << BIT_CUR_HORIZ);
93
94 constexpr int VIRTUAL = 0;
95 constexpr int SOLID = 1;
96
97 constexpr int ABOVE_VIRTUAL = VIRTUAL << BIT_ABOVE;
98 constexpr int ABOVE_SOLID = SOLID << BIT_ABOVE;
99
100 constexpr int LEFT_VIRTUAL = VIRTUAL << BIT_LEFT;
101 constexpr int LEFT_SOLID = SOLID << BIT_LEFT;
102
103 constexpr int CUR_VERT_VIRTUAL = VIRTUAL << BIT_CUR_VERT;
104 constexpr int CUR_VERT_SOLID = SOLID << BIT_CUR_VERT;
105
106 constexpr int CUR_HORIZ_VIRTUAL = VIRTUAL << BIT_CUR_HORIZ;
107 constexpr int CUR_HORIZ_SOLID = SOLID << BIT_CUR_HORIZ;
108
135 switch (nArmConnectionType)
136 {
137 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_VIRTUAL |
138 CUR_HORIZ_VIRTUAL: // 0
139 // nothing to do
140 break;
141
142 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_SOLID |
143 CUR_HORIZ_SOLID: // 3
144 // add inner arcs
145 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
146 poCurrent->oArcHorInner = poCurrent->poPolyInside->newArc(false);
147 poCurrent->poPolyInside->setArcConnection(poCurrent->oArcHorInner,
148 poCurrent->oArcVerInner);
149 poCurrent->oArcVerInner.poArc->emplace_back(
150 Point{poCurrent->iRow, poCurrent->iCol});
151
152 // add outer arcs
153 poCurrent->oArcHorOuter = poAbove->poPolyInside->newArc(true);
154 poCurrent->oArcVerOuter = poAbove->poPolyInside->newArc(false);
155 poAbove->poPolyInside->setArcConnection(poCurrent->oArcVerOuter,
156 poCurrent->oArcHorOuter);
157 poCurrent->oArcHorOuter.poArc->push_back(
158 Point{poCurrent->iRow, poCurrent->iCol});
159
160 break;
161 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_VIRTUAL |
162 CUR_HORIZ_SOLID: // 5
163 // pass arcs
164 poCurrent->oArcHorInner = poLeft->oArcHorInner;
165 poCurrent->oArcHorOuter = poLeft->oArcHorOuter;
166
167 break;
168 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_SOLID |
169 CUR_HORIZ_VIRTUAL: // 6
170 // pass arcs
171 poCurrent->oArcVerInner = poLeft->oArcHorOuter;
172 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
173 poCurrent->oArcVerInner.poArc->push_back(
174 Point{poCurrent->iRow, poCurrent->iCol});
175 poCurrent->oArcVerOuter.poArc->push_back(
176 Point{poCurrent->iRow, poCurrent->iCol});
177
178 break;
179 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_SOLID |
180 CUR_HORIZ_SOLID: // 7
181 // pass arcs
182 poCurrent->oArcHorOuter = poLeft->oArcHorOuter;
183 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
184 poLeft->oArcHorInner.poArc->push_back(
185 Point{poCurrent->iRow, poCurrent->iCol});
186
187 // add inner arcs
188 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
189 poCurrent->oArcHorInner = poCurrent->poPolyInside->newArc(false);
190 poCurrent->poPolyInside->setArcConnection(poCurrent->oArcHorInner,
191 poCurrent->oArcVerInner);
192 poCurrent->oArcVerInner.poArc->push_back(
193 Point{poCurrent->iRow, poCurrent->iCol});
194
195 break;
196 case ABOVE_SOLID | LEFT_VIRTUAL | CUR_VERT_VIRTUAL |
197 CUR_HORIZ_SOLID: // 9
198 // pass arcs
199 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
200 poCurrent->oArcHorInner = poAbove->oArcVerOuter;
201 poCurrent->oArcHorOuter.poArc->push_back(
202 Point{poCurrent->iRow, poCurrent->iCol});
203 poCurrent->oArcHorInner.poArc->push_back(
204 Point{poCurrent->iRow, poCurrent->iCol});
205
206 break;
207 case ABOVE_SOLID | LEFT_VIRTUAL | CUR_VERT_SOLID |
208 CUR_HORIZ_VIRTUAL: // 10
209 // pass arcs
210 poCurrent->oArcVerInner = poAbove->oArcVerInner;
211 poCurrent->oArcVerOuter = poAbove->oArcVerOuter;
212
213 break;
214 case ABOVE_SOLID | LEFT_VIRTUAL | CUR_VERT_SOLID |
215 CUR_HORIZ_SOLID: // 11
216 // pass arcs
217 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
218 poCurrent->oArcVerOuter = poAbove->oArcVerOuter;
219 poCurrent->oArcHorOuter.poArc->push_back(
220 Point{poCurrent->iRow, poCurrent->iCol});
221 // add inner arcs
222 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
223 poCurrent->oArcHorInner = poCurrent->poPolyInside->newArc(false);
224 poCurrent->poPolyInside->setArcConnection(poCurrent->oArcHorInner,
225 poCurrent->oArcVerInner);
226 poCurrent->oArcVerInner.poArc->push_back(
227 Point{poCurrent->iRow, poCurrent->iCol});
228
229 break;
230 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_VIRTUAL |
231 CUR_HORIZ_VIRTUAL: // 12
232 // close arcs
233 poLeft->oArcHorOuter.poArc->push_back(
234 Point{poCurrent->iRow, poCurrent->iCol});
235 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
236 poAbove->oArcVerOuter);
237 // close arcs
238 poAbove->oArcVerInner.poArc->push_back(
239 Point{poCurrent->iRow, poCurrent->iCol});
240 poCurrent->poPolyInside->setArcConnection(poAbove->oArcVerInner,
241 poLeft->oArcHorInner);
242
243 break;
244 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_VIRTUAL |
245 CUR_HORIZ_SOLID: // 13
246 // close arcs
247 poLeft->oArcHorOuter.poArc->push_back(
248 Point{poCurrent->iRow, poCurrent->iCol});
249 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
250 poAbove->oArcVerOuter);
251 // pass arcs
252 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
253 poCurrent->oArcHorInner = poLeft->oArcHorInner;
254 poCurrent->oArcHorOuter.poArc->push_back(
255 Point{poCurrent->iRow, poCurrent->iCol});
256
257 break;
258 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_SOLID |
259 CUR_HORIZ_VIRTUAL: // 14
260 // close arcs
261 poLeft->oArcHorOuter.poArc->push_back(
262 Point{poCurrent->iRow, poCurrent->iCol});
263 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
264 poAbove->oArcVerOuter);
265 // pass arcs
266 poCurrent->oArcVerInner = poAbove->oArcVerInner;
267 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
268 poCurrent->oArcVerOuter.poArc->push_back(
269 Point{poCurrent->iRow, poCurrent->iCol});
270
271 break;
272 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_SOLID | CUR_HORIZ_SOLID: // 15
273 // Tow pixels of the main diagonal belong to the same polygon
274 if (poAbove->poPolyLeft == poCurrent->poPolyInside)
275 {
276 // pass arcs
277 poCurrent->oArcVerInner = poLeft->oArcHorOuter;
278 poCurrent->oArcHorInner = poAbove->oArcVerOuter;
279 poCurrent->oArcVerInner.poArc->push_back(
280 Point{poCurrent->iRow, poCurrent->iCol});
281 poCurrent->oArcHorInner.poArc->push_back(
282 Point{poCurrent->iRow, poCurrent->iCol});
283 }
284 else
285 {
286 // close arcs
287 poLeft->oArcHorOuter.poArc->push_back(
288 Point{poCurrent->iRow, poCurrent->iCol});
289 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
290 poAbove->oArcVerOuter);
291 // add inner arcs
292 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
293 poCurrent->oArcHorInner =
294 poCurrent->poPolyInside->newArc(false);
295 poCurrent->poPolyInside->setArcConnection(
296 poCurrent->oArcHorInner, poCurrent->oArcVerInner);
297 poCurrent->oArcVerInner.poArc->push_back(
298 Point{poCurrent->iRow, poCurrent->iCol});
299 }
300
301 // Tow pixels of the secondary diagonal belong to the same polygon
302 if (poAbove->poPolyInside == poLeft->poPolyInside)
303 {
304 // close arcs
305 poAbove->poPolyInside->setArcConnection(poAbove->oArcVerInner,
306 poLeft->oArcHorInner);
307 poAbove->oArcVerInner.poArc->push_back(
308 Point{poCurrent->iRow, poCurrent->iCol});
309 // add outer arcs
310 poCurrent->oArcHorOuter = poAbove->poPolyInside->newArc(true);
311 poCurrent->oArcVerOuter = poAbove->poPolyInside->newArc(false);
312 poCurrent->oArcHorOuter.poArc->push_back(
313 Point{poCurrent->iRow, poCurrent->iCol});
314 poAbove->poPolyInside->setArcConnection(
315 poCurrent->oArcVerOuter, poCurrent->oArcHorOuter);
316 }
317 else
318 {
319 // pass arcs
320 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
321 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
322 poCurrent->oArcHorOuter.poArc->push_back(
323 Point{poCurrent->iRow, poCurrent->iCol});
324 poCurrent->oArcVerOuter.poArc->push_back(
325 Point{poCurrent->iRow, poCurrent->iCol});
326 }
327
328 break;
329
330 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_VIRTUAL |
331 CUR_HORIZ_SOLID: // 1
332 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_SOLID |
333 CUR_HORIZ_VIRTUAL: // 2
334 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_VIRTUAL |
335 CUR_HORIZ_VIRTUAL: // 4
336 default:
337 // Impossible case
338 CPLAssert(false);
339 break;
340 }
341}
342
343template <typename PolyIdType, typename DataType>
344Polygonizer<PolyIdType, DataType>::Polygonizer(
345 PolyIdType nInvalidPolyId, PolygonReceiver<DataType> *poPolygonReceiver)
346 : nInvalidPolyId_(nInvalidPolyId), poPolygonReceiver_(poPolygonReceiver)
347{
348 poTheOuterPolygon_ = createPolygon(THE_OUTER_POLYGON_ID);
349}
350
351template <typename PolyIdType, typename DataType>
352Polygonizer<PolyIdType, DataType>::~Polygonizer()
353{
354 // cppcheck-suppress constVariableReference
355 for (auto &pair : oPolygonMap_)
356 {
357 delete pair.second;
358 }
359}
360
361template <typename PolyIdType, typename DataType>
362RPolygon *Polygonizer<PolyIdType, DataType>::getPolygon(PolyIdType nPolygonId)
363{
364 if (oPolygonMap_.count(nPolygonId) == 0)
365 {
366 return createPolygon(nPolygonId);
367 }
368 else
369 {
370 return oPolygonMap_[nPolygonId];
371 }
372}
373
374template <typename PolyIdType, typename DataType>
375RPolygon *
376Polygonizer<PolyIdType, DataType>::createPolygon(PolyIdType nPolygonId)
377{
378 auto polygon = new RPolygon();
379 oPolygonMap_[nPolygonId] = polygon;
380 return polygon;
381}
382
383template <typename PolyIdType, typename DataType>
384void Polygonizer<PolyIdType, DataType>::destroyPolygon(PolyIdType nPolygonId)
385{
386 delete oPolygonMap_[nPolygonId];
387 oPolygonMap_.erase(nPolygonId);
388}
389
390template <typename PolyIdType, typename DataType>
391void Polygonizer<PolyIdType, DataType>::processLine(
392 const PolyIdType *panThisLineId, const DataType *panLastLineVal,
393 TwoArm *poThisLineArm, TwoArm *poLastLineArm, const IndexType nCurrentRow,
394 const IndexType nCols)
395{
396 TwoArm *poCurrent, *poAbove, *poLeft;
397
398 poCurrent = poThisLineArm + 1;
399 poCurrent->iRow = nCurrentRow;
400 poCurrent->iCol = 0;
401 poCurrent->poPolyInside = getPolygon(panThisLineId[0]);
402 poAbove = poLastLineArm + 1;
403 poLeft = poThisLineArm;
404 poLeft->poPolyInside = poTheOuterPolygon_;
405 ProcessArmConnections(poCurrent, poAbove, poLeft);
406 for (IndexType col = 1; col < nCols; ++col)
407 {
408 IndexType iArmIndex = col + 1;
409 poCurrent = poThisLineArm + iArmIndex;
410 poCurrent->iRow = nCurrentRow;
411 poCurrent->iCol = col;
412 poCurrent->poPolyInside = getPolygon(panThisLineId[col]);
413 poAbove = poLastLineArm + iArmIndex;
414 poLeft = poThisLineArm + iArmIndex - 1;
415 ProcessArmConnections(poCurrent, poAbove, poLeft);
416 }
417 poCurrent = poThisLineArm + nCols + 1;
418 poCurrent->iRow = nCurrentRow;
419 poCurrent->iCol = nCols;
420 poCurrent->poPolyInside = poTheOuterPolygon_;
421 poAbove = poLastLineArm + nCols + 1;
422 poAbove->poPolyInside = poTheOuterPolygon_;
423 poLeft = poThisLineArm + nCols;
424 ProcessArmConnections(poCurrent, poAbove, poLeft);
425
431 std::vector<PolygonMapEntry> oCompletedPolygons;
432 for (auto &entry : oPolygonMap_)
433 {
434 RPolygon *poPolygon = entry.second;
435
436 if (poPolygon->iBottomRightRow + 1 == nCurrentRow)
437 {
438 oCompletedPolygons.push_back(entry);
439 }
440 }
441 // cppcheck-suppress constVariableReference
442 for (auto &entry : oCompletedPolygons)
443 {
444 PolyIdType nPolyId = entry.first;
445 RPolygon *poPolygon = entry.second;
446
447 // emit valid polygon only
448 if (nPolyId != nInvalidPolyId_)
449 {
450 poPolygonReceiver_->receive(
451 poPolygon, panLastLineVal[poPolygon->iBottomRightCol]);
452 }
453
454 destroyPolygon(nPolyId);
455 }
456}
457
458template <typename DataType>
459OGRPolygonWriter<DataType>::OGRPolygonWriter(OGRLayerH hOutLayer,
460 int iPixValField,
461 double *padfGeoTransform)
462 : PolygonReceiver<DataType>(), hOutLayer_(hOutLayer),
463 iPixValField_(iPixValField), padfGeoTransform_(padfGeoTransform)
464{
465}
466
467template <typename DataType>
468void OGRPolygonWriter<DataType>::receive(RPolygon *poPolygon,
469 DataType nPolygonCellValue)
470{
471 std::vector<bool> oAccessedArc(poPolygon->oArcConnections.size(), false);
472 double *padfGeoTransform = padfGeoTransform_;
473
475
476 auto AddRingToPolygon = [&poPolygon, &oAccessedArc, &hPolygon,
477 padfGeoTransform](std::size_t iFirstArcIndex)
478 {
480
481 auto AddArcToRing =
482 [&poPolygon, &hRing, padfGeoTransform](std::size_t iArcIndex)
483 {
484 auto oArc = poPolygon->oArcs[iArcIndex];
485 bool bArcFollowRighthand =
486 poPolygon->oArcRighthandFollow[iArcIndex];
487 for (std::size_t i = 0; i < oArc->size(); ++i)
488 {
489 const Point &oPixel =
490 (*oArc)[bArcFollowRighthand ? i : (oArc->size() - i - 1)];
491
492 const double dfX = padfGeoTransform[0] +
493 oPixel[1] * padfGeoTransform[1] +
494 oPixel[0] * padfGeoTransform[2];
495 const double dfY = padfGeoTransform[3] +
496 oPixel[1] * padfGeoTransform[4] +
497 oPixel[0] * padfGeoTransform[5];
498
499 OGR_G_AddPoint_2D(hRing, dfX, dfY);
500 }
501 };
502
503 AddArcToRing(iFirstArcIndex);
504
505 std::size_t iArcIndex = iFirstArcIndex;
506 std::size_t iNextArcIndex = poPolygon->oArcConnections[iArcIndex];
507 oAccessedArc[iArcIndex] = true;
508 while (iNextArcIndex != iFirstArcIndex)
509 {
510 AddArcToRing(iNextArcIndex);
511 iArcIndex = iNextArcIndex;
512 iNextArcIndex = poPolygon->oArcConnections[iArcIndex];
513 oAccessedArc[iArcIndex] = true;
514 }
515
516 // close ring manually
517 OGR_G_AddPoint_2D(hRing, OGR_G_GetX(hRing, 0), OGR_G_GetY(hRing, 0));
518
519 OGR_G_AddGeometryDirectly(hPolygon, hRing);
520 };
521
522 std::vector<bool>::iterator ite;
523 while ((ite = std::find_if_not(oAccessedArc.begin(), oAccessedArc.end(),
524 [](bool accessed) { return accessed; })) !=
525 oAccessedArc.end())
526 {
527 AddRingToPolygon(ite - oAccessedArc.begin());
528 }
529
530 // Create the feature object
531 OGRFeatureH hFeat = OGR_F_Create(OGR_L_GetLayerDefn(hOutLayer_));
532
533 OGR_F_SetGeometryDirectly(hFeat, hPolygon);
534
535 if (iPixValField_ >= 0)
536 OGR_F_SetFieldDouble(hFeat, iPixValField_,
537 static_cast<double>(nPolygonCellValue));
538
539 // Write the to the layer.
540 if (OGR_L_CreateFeature(hOutLayer_, hFeat) != OGRERR_NONE)
541 eErr_ = CE_Failure;
542
543 OGR_F_Destroy(hFeat);
544}
545
546} // namespace polygonizer
547} // namespace gdal
548
#define CPLAssert(expr)
Assert on an expression.
Definition cpl_error.h:216
OGRFeatureH OGR_F_Create(OGRFeatureDefnH)
Feature factory.
Definition ogrfeature.cpp:132
void * OGRGeometryH
Opaque type for a geometry.
Definition ogr_api.h:66
void OGR_F_Destroy(OGRFeatureH)
Destroy feature.
Definition ogrfeature.cpp:224
OGRErr OGR_L_CreateFeature(OGRLayerH, OGRFeatureH)
Create and write a new feature within a layer.
Definition ogrlayer.cpp:803
OGRGeometryH OGR_G_CreateGeometry(OGRwkbGeometryType)
Create an empty geometry of desired type.
Definition ogrgeometryfactory.cpp:660
void * OGRLayerH
Opaque type for a layer (OGRLayer)
Definition ogr_api.h:688
OGRFeatureDefnH OGR_L_GetLayerDefn(OGRLayerH)
Fetch the schema information for this layer.
Definition ogrlayer.cpp:1322
void OGR_F_SetFieldDouble(OGRFeatureH, int, double)
Set field to double value.
Definition ogrfeature.cpp:4116
void * OGRFeatureH
Opaque type for a feature (OGRFeature)
Definition ogr_api.h:422
OGRErr OGR_F_SetGeometryDirectly(OGRFeatureH, OGRGeometryH)
Set feature geometry.
Definition ogrfeature.cpp:505
#define OGRERR_NONE
Success.
Definition ogr_core.h:389
@ wkbPolygon
planar 2-dimensional geometric object defined by 1 exterior boundary and 0 or more interior boundarie...
Definition ogr_core.h:422
@ wkbLinearRing
non-standard, just for createGeometry()
Definition ogr_core.h:455