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 * SPDX-License-Identifier: MIT
10 ****************************************************************************/
11
13
14#include "polygonize_polygonizer.h"
15
16#include <algorithm>
17
18namespace gdal
19{
20namespace polygonizer
21{
22IndexedArc RPolygon::newArc(bool bFollowRighthand)
23{
24 const std::size_t iArcIndex = oArcs.size();
25 const auto &oNewArc =
26 oArcs.emplace_back(static_cast<unsigned>(iArcIndex), bFollowRighthand);
27 return IndexedArc{oNewArc.poArc.get(), iArcIndex};
28}
29
30void RPolygon::setArcConnection(const IndexedArc &oArc,
31 const IndexedArc &oNextArc)
32{
33 oArcs[oArc.iIndex].nConnection = static_cast<unsigned>(oNextArc.iIndex);
34}
35
36void RPolygon::updateBottomRightPos(IndexType iRow, IndexType iCol)
37{
38 iBottomRightRow = iRow;
39 iBottomRightCol = iCol;
40}
41
45static void ProcessArmConnections(TwoArm *poCurrent, TwoArm *poAbove,
46 TwoArm *poLeft)
47{
48 poCurrent->poPolyInside->updateBottomRightPos(poCurrent->iRow,
49 poCurrent->iCol);
50 poCurrent->bSolidVertical = poCurrent->poPolyInside != poLeft->poPolyInside;
51 poCurrent->bSolidHorizontal =
52 poCurrent->poPolyInside != poAbove->poPolyInside;
53 poCurrent->poPolyAbove = poAbove->poPolyInside;
54 poCurrent->poPolyLeft = poLeft->poPolyInside;
55
56 constexpr int BIT_CUR_HORIZ = 0;
57 constexpr int BIT_CUR_VERT = 1;
58 constexpr int BIT_LEFT = 2;
59 constexpr int BIT_ABOVE = 3;
60
61 const int nArmConnectionType =
62 (static_cast<int>(poAbove->bSolidVertical) << BIT_ABOVE) |
63 (static_cast<int>(poLeft->bSolidHorizontal) << BIT_LEFT) |
64 (static_cast<int>(poCurrent->bSolidVertical) << BIT_CUR_VERT) |
65 (static_cast<int>(poCurrent->bSolidHorizontal) << BIT_CUR_HORIZ);
66
67 constexpr int VIRTUAL = 0;
68 constexpr int SOLID = 1;
69
70 constexpr int ABOVE_VIRTUAL = VIRTUAL << BIT_ABOVE;
71 constexpr int ABOVE_SOLID = SOLID << BIT_ABOVE;
72
73 constexpr int LEFT_VIRTUAL = VIRTUAL << BIT_LEFT;
74 constexpr int LEFT_SOLID = SOLID << BIT_LEFT;
75
76 constexpr int CUR_VERT_VIRTUAL = VIRTUAL << BIT_CUR_VERT;
77 constexpr int CUR_VERT_SOLID = SOLID << BIT_CUR_VERT;
78
79 constexpr int CUR_HORIZ_VIRTUAL = VIRTUAL << BIT_CUR_HORIZ;
80 constexpr int CUR_HORIZ_SOLID = SOLID << BIT_CUR_HORIZ;
81
107
108 switch (nArmConnectionType)
109 {
110 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_VIRTUAL |
111 CUR_HORIZ_VIRTUAL: // 0
112 // nothing to do
113 break;
114
115 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_SOLID |
116 CUR_HORIZ_SOLID: // 3
117 // add inner arcs
118 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
119 poCurrent->oArcHorInner = poCurrent->poPolyInside->newArc(false);
120 poCurrent->poPolyInside->setArcConnection(poCurrent->oArcHorInner,
121 poCurrent->oArcVerInner);
122 poCurrent->oArcVerInner.poArc->emplace_back(
123 Point{poCurrent->iRow, poCurrent->iCol});
124
125 // add outer arcs
126 poCurrent->oArcHorOuter = poAbove->poPolyInside->newArc(true);
127 poCurrent->oArcVerOuter = poAbove->poPolyInside->newArc(false);
128 poAbove->poPolyInside->setArcConnection(poCurrent->oArcVerOuter,
129 poCurrent->oArcHorOuter);
130 poCurrent->oArcHorOuter.poArc->push_back(
131 Point{poCurrent->iRow, poCurrent->iCol});
132
133 break;
134 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_VIRTUAL |
135 CUR_HORIZ_SOLID: // 5
136 // pass arcs
137 poCurrent->oArcHorInner = poLeft->oArcHorInner;
138 poCurrent->oArcHorOuter = poLeft->oArcHorOuter;
139
140 break;
141 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_SOLID |
142 CUR_HORIZ_VIRTUAL: // 6
143 // pass arcs
144 poCurrent->oArcVerInner = poLeft->oArcHorOuter;
145 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
146 poCurrent->oArcVerInner.poArc->push_back(
147 Point{poCurrent->iRow, poCurrent->iCol});
148 poCurrent->oArcVerOuter.poArc->push_back(
149 Point{poCurrent->iRow, poCurrent->iCol});
150
151 break;
152 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_SOLID |
153 CUR_HORIZ_SOLID: // 7
154 // pass arcs
155 poCurrent->oArcHorOuter = poLeft->oArcHorOuter;
156 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
157 poLeft->oArcHorInner.poArc->push_back(
158 Point{poCurrent->iRow, poCurrent->iCol});
159
160 // add inner arcs
161 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
162 poCurrent->oArcHorInner = poCurrent->poPolyInside->newArc(false);
163 poCurrent->poPolyInside->setArcConnection(poCurrent->oArcHorInner,
164 poCurrent->oArcVerInner);
165 poCurrent->oArcVerInner.poArc->push_back(
166 Point{poCurrent->iRow, poCurrent->iCol});
167
168 break;
169 case ABOVE_SOLID | LEFT_VIRTUAL | CUR_VERT_VIRTUAL |
170 CUR_HORIZ_SOLID: // 9
171 // pass arcs
172 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
173 poCurrent->oArcHorInner = poAbove->oArcVerOuter;
174 poCurrent->oArcHorOuter.poArc->push_back(
175 Point{poCurrent->iRow, poCurrent->iCol});
176 poCurrent->oArcHorInner.poArc->push_back(
177 Point{poCurrent->iRow, poCurrent->iCol});
178
179 break;
180 case ABOVE_SOLID | LEFT_VIRTUAL | CUR_VERT_SOLID |
181 CUR_HORIZ_VIRTUAL: // 10
182 // pass arcs
183 poCurrent->oArcVerInner = poAbove->oArcVerInner;
184 poCurrent->oArcVerOuter = poAbove->oArcVerOuter;
185
186 break;
187 case ABOVE_SOLID | LEFT_VIRTUAL | CUR_VERT_SOLID |
188 CUR_HORIZ_SOLID: // 11
189 // pass arcs
190 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
191 poCurrent->oArcVerOuter = poAbove->oArcVerOuter;
192 poCurrent->oArcHorOuter.poArc->push_back(
193 Point{poCurrent->iRow, poCurrent->iCol});
194 // add inner arcs
195 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
196 poCurrent->oArcHorInner = poCurrent->poPolyInside->newArc(false);
197 poCurrent->poPolyInside->setArcConnection(poCurrent->oArcHorInner,
198 poCurrent->oArcVerInner);
199 poCurrent->oArcVerInner.poArc->push_back(
200 Point{poCurrent->iRow, poCurrent->iCol});
201
202 break;
203 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_VIRTUAL |
204 CUR_HORIZ_VIRTUAL: // 12
205 // close arcs
206 poLeft->oArcHorOuter.poArc->push_back(
207 Point{poCurrent->iRow, poCurrent->iCol});
208 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
209 poAbove->oArcVerOuter);
210 // close arcs
211 poAbove->oArcVerInner.poArc->push_back(
212 Point{poCurrent->iRow, poCurrent->iCol});
213 poCurrent->poPolyInside->setArcConnection(poAbove->oArcVerInner,
214 poLeft->oArcHorInner);
215
216 break;
217 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_VIRTUAL |
218 CUR_HORIZ_SOLID: // 13
219 // close arcs
220 poLeft->oArcHorOuter.poArc->push_back(
221 Point{poCurrent->iRow, poCurrent->iCol});
222 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
223 poAbove->oArcVerOuter);
224 // pass arcs
225 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
226 poCurrent->oArcHorInner = poLeft->oArcHorInner;
227 poCurrent->oArcHorOuter.poArc->push_back(
228 Point{poCurrent->iRow, poCurrent->iCol});
229
230 break;
231 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_SOLID |
232 CUR_HORIZ_VIRTUAL: // 14
233 // close arcs
234 poLeft->oArcHorOuter.poArc->push_back(
235 Point{poCurrent->iRow, poCurrent->iCol});
236 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
237 poAbove->oArcVerOuter);
238 // pass arcs
239 poCurrent->oArcVerInner = poAbove->oArcVerInner;
240 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
241 poCurrent->oArcVerOuter.poArc->push_back(
242 Point{poCurrent->iRow, poCurrent->iCol});
243
244 break;
245 case ABOVE_SOLID | LEFT_SOLID | CUR_VERT_SOLID | CUR_HORIZ_SOLID: // 15
246 // Tow pixels of the main diagonal belong to the same polygon
247 if (poAbove->poPolyLeft == poCurrent->poPolyInside)
248 {
249 // pass arcs
250 poCurrent->oArcVerInner = poLeft->oArcHorOuter;
251 poCurrent->oArcHorInner = poAbove->oArcVerOuter;
252 poCurrent->oArcVerInner.poArc->push_back(
253 Point{poCurrent->iRow, poCurrent->iCol});
254 poCurrent->oArcHorInner.poArc->push_back(
255 Point{poCurrent->iRow, poCurrent->iCol});
256 }
257 else
258 {
259 // close arcs
260 poLeft->oArcHorOuter.poArc->push_back(
261 Point{poCurrent->iRow, poCurrent->iCol});
262 poLeft->poPolyAbove->setArcConnection(poLeft->oArcHorOuter,
263 poAbove->oArcVerOuter);
264 // add inner arcs
265 poCurrent->oArcVerInner = poCurrent->poPolyInside->newArc(true);
266 poCurrent->oArcHorInner =
267 poCurrent->poPolyInside->newArc(false);
268 poCurrent->poPolyInside->setArcConnection(
269 poCurrent->oArcHorInner, poCurrent->oArcVerInner);
270 poCurrent->oArcVerInner.poArc->push_back(
271 Point{poCurrent->iRow, poCurrent->iCol});
272 }
273
274 // Tow pixels of the secondary diagonal belong to the same polygon
275 if (poAbove->poPolyInside == poLeft->poPolyInside)
276 {
277 // close arcs
278 poAbove->poPolyInside->setArcConnection(poAbove->oArcVerInner,
279 poLeft->oArcHorInner);
280 poAbove->oArcVerInner.poArc->push_back(
281 Point{poCurrent->iRow, poCurrent->iCol});
282 // add outer arcs
283 poCurrent->oArcHorOuter = poAbove->poPolyInside->newArc(true);
284 poCurrent->oArcVerOuter = poAbove->poPolyInside->newArc(false);
285 poCurrent->oArcHorOuter.poArc->push_back(
286 Point{poCurrent->iRow, poCurrent->iCol});
287 poAbove->poPolyInside->setArcConnection(
288 poCurrent->oArcVerOuter, poCurrent->oArcHorOuter);
289 }
290 else
291 {
292 // pass arcs
293 poCurrent->oArcHorOuter = poAbove->oArcVerInner;
294 poCurrent->oArcVerOuter = poLeft->oArcHorInner;
295 poCurrent->oArcHorOuter.poArc->push_back(
296 Point{poCurrent->iRow, poCurrent->iCol});
297 poCurrent->oArcVerOuter.poArc->push_back(
298 Point{poCurrent->iRow, poCurrent->iCol});
299 }
300
301 break;
302
303 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_VIRTUAL |
304 CUR_HORIZ_SOLID: // 1
305 case ABOVE_VIRTUAL | LEFT_VIRTUAL | CUR_VERT_SOLID |
306 CUR_HORIZ_VIRTUAL: // 2
307 case ABOVE_VIRTUAL | LEFT_SOLID | CUR_VERT_VIRTUAL |
308 CUR_HORIZ_VIRTUAL: // 4
309 default:
310 // Impossible case
311 CPLAssert(false);
312 break;
313 }
314}
315
316template <typename PolyIdType, typename DataType>
317Polygonizer<PolyIdType, DataType>::Polygonizer(
318 PolyIdType nInvalidPolyId, PolygonReceiver<DataType> *poPolygonReceiver)
319 : nInvalidPolyId_(nInvalidPolyId), poPolygonReceiver_(poPolygonReceiver)
320{
321 poTheOuterPolygon_ = createPolygon(THE_OUTER_POLYGON_ID);
322}
323
324template <typename PolyIdType, typename DataType>
325Polygonizer<PolyIdType, DataType>::~Polygonizer()
326{
327 // cppcheck-suppress constVariableReference
328 for (auto &pair : oPolygonMap_)
329 {
330 delete pair.second;
331 }
332}
333
334template <typename PolyIdType, typename DataType>
335RPolygon *Polygonizer<PolyIdType, DataType>::getPolygon(PolyIdType nPolygonId)
336{
337 const auto oIter = oPolygonMap_.find(nPolygonId);
338 if (oIter == oPolygonMap_.end())
339 {
340 return createPolygon(nPolygonId);
341 }
342 else
343 {
344 return oIter->second;
345 }
346}
347
348template <typename PolyIdType, typename DataType>
349RPolygon *
350Polygonizer<PolyIdType, DataType>::createPolygon(PolyIdType nPolygonId)
351{
352 auto polygon = new RPolygon();
353 oPolygonMap_[nPolygonId] = polygon;
354 return polygon;
355}
356
357template <typename PolyIdType, typename DataType>
358void Polygonizer<PolyIdType, DataType>::destroyPolygon(PolyIdType nPolygonId)
359{
360 const auto oIter = oPolygonMap_.find(nPolygonId);
361 CPLAssert(oIter != oPolygonMap_.end());
362 delete oIter->second;
363 oPolygonMap_.erase(oIter);
364}
365
366template <typename PolyIdType, typename DataType>
367bool Polygonizer<PolyIdType, DataType>::processLine(
368 const PolyIdType *panThisLineId, const DataType *panLastLineVal,
369 TwoArm *poThisLineArm, TwoArm *poLastLineArm, const IndexType nCurrentRow,
370 const IndexType nCols)
371{
372 TwoArm *poCurrent, *poAbove, *poLeft;
373
374 try
375 {
376 poCurrent = poThisLineArm + 1;
377 poCurrent->iRow = nCurrentRow;
378 poCurrent->iCol = 0;
379 poCurrent->poPolyInside = getPolygon(panThisLineId[0]);
380 poAbove = poLastLineArm + 1;
381 poLeft = poThisLineArm;
382 poLeft->poPolyInside = poTheOuterPolygon_;
383 ProcessArmConnections(poCurrent, poAbove, poLeft);
384 for (IndexType col = 1; col < nCols; ++col)
385 {
386 IndexType iArmIndex = col + 1;
387 poCurrent = poThisLineArm + iArmIndex;
388 poCurrent->iRow = nCurrentRow;
389 poCurrent->iCol = col;
390 poCurrent->poPolyInside = getPolygon(panThisLineId[col]);
391 poAbove = poLastLineArm + iArmIndex;
392 poLeft = poThisLineArm + iArmIndex - 1;
393 ProcessArmConnections(poCurrent, poAbove, poLeft);
394 }
395 poCurrent = poThisLineArm + nCols + 1;
396 poCurrent->iRow = nCurrentRow;
397 poCurrent->iCol = nCols;
398 poCurrent->poPolyInside = poTheOuterPolygon_;
399 poAbove = poLastLineArm + nCols + 1;
400 poAbove->poPolyInside = poTheOuterPolygon_;
401 poLeft = poThisLineArm + nCols;
402 ProcessArmConnections(poCurrent, poAbove, poLeft);
403
409 std::vector<PolygonMapEntry> oCompletedPolygons;
410 for (auto &entry : oPolygonMap_)
411 {
412 RPolygon *poPolygon = entry.second;
413
414 if (poPolygon->iBottomRightRow + 1 == nCurrentRow)
415 {
416 oCompletedPolygons.push_back(entry);
417 }
418 }
419 // cppcheck-suppress constVariableReference
420 for (auto &entry : oCompletedPolygons)
421 {
422 PolyIdType nPolyId = entry.first;
423 RPolygon *poPolygon = entry.second;
424
425 // emit valid polygon only
426 if (nPolyId != nInvalidPolyId_)
427 {
428 poPolygonReceiver_->receive(
429 poPolygon, panLastLineVal[poPolygon->iBottomRightCol]);
430 }
431
432 destroyPolygon(nPolyId);
433 }
434 return true;
435 }
436 catch (const std::bad_alloc &)
437 {
438 CPLError(CE_Failure, CPLE_OutOfMemory,
439 "Out of memory in Polygonizer::processLine");
440 return false;
441 }
442}
443
444template <typename DataType>
445OGRPolygonWriter<DataType>::OGRPolygonWriter(OGRLayerH hOutLayer,
446 int iPixValField,
447 const GDALGeoTransform &gt,
448 int nCommitInterval)
449 : PolygonReceiver<DataType>(), poOutLayer_(OGRLayer::FromHandle(hOutLayer)),
450 poOutDS_(poOutLayer_->GetDataset()), iPixValField_(iPixValField), gt_(gt),
451 nCommitInterval_(nCommitInterval)
452{
453 if (nCommitInterval_ != 0)
454 {
455 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
456 if (!(poOutDS_ && poOutDS_->TestCapability(ODsCTransactions) &&
457 poOutDS_->StartTransaction(false) == OGRERR_NONE))
458 {
459 nCommitInterval_ = 0;
460 }
461 }
462 poFeature_ = std::make_unique<OGRFeature>(poOutLayer_->GetLayerDefn());
463 poPolygon_ = new OGRPolygon();
464 poFeature_->SetGeometryDirectly(poPolygon_);
465}
466
467template <typename DataType> OGRPolygonWriter<DataType>::~OGRPolygonWriter()
468{
469 OGRPolygonWriter::Finalize();
470}
471
472template <typename DataType> bool OGRPolygonWriter<DataType>::Finalize()
473{
474 if (nFeaturesWrittenInTransaction_)
475 {
476 nFeaturesWrittenInTransaction_ = 0;
477 if (poOutDS_->CommitTransaction() != OGRERR_NONE)
478 {
479 return false;
480 }
481 }
482 return true;
483}
484
485template <typename DataType>
486void OGRPolygonWriter<DataType>::receive(RPolygon *poPolygon,
487 DataType nPolygonCellValue)
488{
489 std::vector<bool> oAccessedArc(poPolygon->oArcs.size(), false);
490
491 OGRLinearRing *poFirstRing = poPolygon_->getExteriorRing();
492 if (poFirstRing && poPolygon_->getNumInteriorRings() == 0)
493 {
494 poFirstRing->empty();
495 }
496 else
497 {
498 poFirstRing = nullptr;
499 poPolygon_->empty();
500 }
501
502 auto AddRingToPolygon =
503 [this, &poPolygon, &oAccessedArc](std::size_t iFirstArcIndex,
504 OGRLinearRing *poRing)
505 {
506 std::unique_ptr<OGRLinearRing> poNewRing;
507 if (!poRing)
508 {
509 poNewRing = std::make_unique<OGRLinearRing>();
510 poRing = poNewRing.get();
511 }
512
513 auto AddArcToRing = [this, &poPolygon, poRing](std::size_t iArcIndex)
514 {
515 const auto &oArc = poPolygon->oArcs[iArcIndex];
516 const bool bArcFollowRighthand = oArc.bFollowRighthand;
517 const int nArcPointCount = static_cast<int>(oArc.poArc->size());
518 int nDstPointIdx = poRing->getNumPoints();
519 poRing->setNumPoints(nDstPointIdx + nArcPointCount,
520 /* bZeroizeNewContent = */ false);
521 if (poRing->getNumPoints() < nDstPointIdx + nArcPointCount)
522 {
523 return false;
524 }
525 for (int i = 0; i < nArcPointCount; ++i)
526 {
527 const Point &oPixel =
528 (*oArc.poArc)[bArcFollowRighthand
529 ? i
530 : (nArcPointCount - i - 1)];
531
532 const auto oGeoreferenced = gt_.Apply(oPixel[1], oPixel[0]);
533 poRing->setPoint(nDstPointIdx, oGeoreferenced.first,
534 oGeoreferenced.second);
535 ++nDstPointIdx;
536 }
537 return true;
538 };
539
540 if (!AddArcToRing(iFirstArcIndex))
541 {
542 return false;
543 }
544
545 std::size_t iArcIndex = iFirstArcIndex;
546 std::size_t iNextArcIndex = poPolygon->oArcs[iArcIndex].nConnection;
547 oAccessedArc[iArcIndex] = true;
548 while (iNextArcIndex != iFirstArcIndex)
549 {
550 if (!AddArcToRing(iNextArcIndex))
551 {
552 return false;
553 }
554 iArcIndex = iNextArcIndex;
555 iNextArcIndex = poPolygon->oArcs[iArcIndex].nConnection;
556 oAccessedArc[iArcIndex] = true;
557 }
558
559 // close ring manually
560 poRing->closeRings();
561
562 if (poNewRing)
563 poPolygon_->addRingDirectly(poNewRing.release());
564 return true;
565 };
566
567 for (size_t i = 0; i < oAccessedArc.size(); ++i)
568 {
569 if (!oAccessedArc[i])
570 {
571 if (!AddRingToPolygon(i, poFirstRing))
572 {
573 eErr_ = CE_Failure;
574 return;
575 }
576 poFirstRing = nullptr;
577 }
578 }
579
580 // Create the feature object
581 poFeature_->SetFID(OGRNullFID);
582 if (iPixValField_ >= 0)
583 poFeature_->SetField(iPixValField_,
584 static_cast<double>(nPolygonCellValue));
585
586 // Write the to the layer.
587 if (poOutLayer_->CreateFeature(poFeature_.get()) != OGRERR_NONE)
588 eErr_ = CE_Failure;
589
590 else
591 {
592 if (nCommitInterval_ != 0)
593 {
594 ++nFeaturesWrittenInTransaction_;
595 if (nFeaturesWrittenInTransaction_ == nCommitInterval_)
596 {
597 if (poOutDS_->CommitTransaction() != OGRERR_NONE ||
598 poOutDS_->StartTransaction(false) != OGRERR_NONE)
599 {
600 eErr_ = CE_Failure;
601 }
602 nFeaturesWrittenInTransaction_ = 0;
603 }
604 }
605
606 // Shouldn't happen for well behaved drivers, but better check...
607 if (poFeature_->GetGeometryRef() != poPolygon_)
608 {
609 poPolygon_ = new OGRPolygon();
610 poFeature_->SetGeometryDirectly(poPolygon_);
611 }
612 }
613}
614
615} // namespace polygonizer
616} // namespace gdal
617
Class that saves the error state on construction, and restores it on destruction.
Definition cpl_error.h:410
Class that encapsulates a geotransform matrix.
Definition gdal_geotransform.h:42
This class represents a layer of simple features, with access methods.
Definition ogrsf_frmts.h:61
Concrete representation of a closed ring.
Definition ogr_geometry.h:1820
Concrete class representing polygons.
Definition ogr_geometry.h:2635
void empty() override
Clear geometry information.
Definition ogrlinestring.cpp:168
#define CPLAssert(expr)
Assert on an expression.
Definition cpl_error.h:330
#define CPLE_OutOfMemory
Out of memory error.
Definition cpl_error.h:87
void * OGRLayerH
Opaque type for a layer (OGRLayer).
Definition gdal_fwd.h:157
#define OGRNullFID
Special value for a unset FID.
Definition ogr_core.h:830
#define OGRERR_NONE
Success.
Definition ogr_core.h:372
#define ODsCTransactions
Dataset capability for dataset transactions.
Definition ogr_core.h:1032