Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Footprint OGR shapes into a GDAL raster.
5 : * Authors: Even Rouault, <even dot rouault at spatialys dot com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys dot com>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_port.h"
30 : #include "gdal_utils.h"
31 : #include "gdal_utils_priv.h"
32 :
33 : #include <array>
34 : #include <cmath>
35 : #include <cstdio>
36 : #include <cstdlib>
37 : #include <cstring>
38 : #include <algorithm>
39 : #include <limits>
40 : #include <vector>
41 :
42 : #include "commonutils.h"
43 : #include "cpl_conv.h"
44 : #include "cpl_error.h"
45 : #include "cpl_progress.h"
46 : #include "cpl_string.h"
47 : #include "cpl_vsi.h"
48 : #include "gdal.h"
49 : #include "gdal_alg.h"
50 : #include "gdal_priv.h"
51 : #include "ogr_api.h"
52 : #include "ogr_core.h"
53 : #include "ogr_mem.h"
54 : #include "ogrsf_frmts.h"
55 : #include "ogr_spatialref.h"
56 :
57 : constexpr const char *DEFAULT_LAYER_NAME = "footprint";
58 :
59 : /************************************************************************/
60 : /* GDALFootprintOptions */
61 : /************************************************************************/
62 :
63 : struct GDALFootprintOptions
64 : {
65 : /*! output format. Use the short format name. */
66 : std::string osFormat{};
67 :
68 : /*! the progress function to use */
69 : GDALProgressFunc pfnProgress = GDALDummyProgress;
70 :
71 : /*! pointer to the progress data variable */
72 : void *pProgressData = nullptr;
73 :
74 : bool bCreateOutput = false;
75 :
76 : std::string osDestLayerName{};
77 :
78 : /*! Layer creation options */
79 : CPLStringList aosLCO{};
80 :
81 : /*! Dataset creation options */
82 : CPLStringList aosDSCO{};
83 :
84 : /*! Overview index: 0 = first overview level */
85 : int nOvrIndex = -1;
86 :
87 : /** Whether output geometry should be in georeferenced coordinates, if
88 : * possible (if explicitly requested, bOutCSGeorefRequested is also set)
89 : * false = in pixel coordinates
90 : */
91 : bool bOutCSGeoref = true;
92 :
93 : /** Whether -t_cs georef has been explicitly set */
94 : bool bOutCSGeorefRequested = false;
95 :
96 : OGRSpatialReference oOutputSRS{};
97 :
98 : bool bSplitPolys = false;
99 :
100 : double dfDensifyDistance = 0;
101 :
102 : double dfSimplifyTolerance = 0;
103 :
104 : bool bConvexHull = false;
105 :
106 : double dfMinRingArea = 0;
107 :
108 : int nMaxPoints = 100;
109 :
110 : /*! Source bands to take into account */
111 : std::vector<int> anBands{};
112 :
113 : /*! Whether to combine bands unioning (true) or intersecting (false) */
114 : bool bCombineBandsUnion = true;
115 :
116 : /*! Field name where to write the path of the raster. Empty if not desired */
117 : std::string osLocationFieldName = "location";
118 :
119 : /*! Whether to force writing absolute paths in location field. */
120 : bool bAbsolutePath = false;
121 :
122 : std::string osSrcNoData;
123 : };
124 :
125 : /************************************************************************/
126 : /* GDALFootprintMaskBand */
127 : /************************************************************************/
128 :
129 : class GDALFootprintMaskBand final : public GDALRasterBand
130 : {
131 : GDALRasterBand *m_poSrcBand = nullptr;
132 :
133 : public:
134 27 : explicit GDALFootprintMaskBand(GDALRasterBand *poSrcBand)
135 27 : : m_poSrcBand(poSrcBand)
136 : {
137 27 : nRasterXSize = m_poSrcBand->GetXSize();
138 27 : nRasterYSize = m_poSrcBand->GetYSize();
139 27 : eDataType = GDT_Byte;
140 27 : m_poSrcBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
141 27 : }
142 :
143 : protected:
144 0 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override
145 : {
146 : int nWindowXSize;
147 : int nWindowYSize;
148 0 : m_poSrcBand->GetActualBlockSize(nBlockXOff, nBlockYOff, &nWindowXSize,
149 : &nWindowYSize);
150 : GDALRasterIOExtraArg sExtraArg;
151 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
152 0 : return IRasterIO(GF_Read, nBlockXOff * nBlockXSize,
153 0 : nBlockYOff * nBlockYSize, nWindowXSize, nWindowYSize,
154 : pData, nWindowXSize, nWindowYSize, GDT_Byte, 1,
155 0 : nBlockXSize, &sExtraArg);
156 : }
157 :
158 1272 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
159 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
160 : GDALDataType eBufType, GSpacing nPixelSpace,
161 : GSpacing nLineSpace,
162 : GDALRasterIOExtraArg *psExtraArg) override
163 : {
164 1272 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
165 636 : eBufType == GDT_Byte && nPixelSpace == 1)
166 : {
167 : // Request when band seen as the mask band for GDALPolygonize()
168 :
169 636 : if (m_poSrcBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
170 : pData, nBufXSize, nBufYSize, eBufType,
171 : nPixelSpace, nLineSpace,
172 636 : psExtraArg) != CE_None)
173 : {
174 0 : return CE_Failure;
175 : }
176 636 : GByte *pabyData = static_cast<GByte *>(pData);
177 1272 : for (int iY = 0; iY < nYSize; ++iY)
178 : {
179 12728 : for (int iX = 0; iX < nXSize; ++iX)
180 : {
181 12092 : if (pabyData[iX])
182 12034 : pabyData[iX] = 1;
183 : }
184 636 : pabyData += nLineSpace;
185 : }
186 :
187 636 : return CE_None;
188 : }
189 :
190 636 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
191 636 : eBufType == GDT_Int64 &&
192 636 : nPixelSpace == static_cast<int>(sizeof(int64_t)) &&
193 636 : (nLineSpace % nPixelSpace) == 0)
194 : {
195 : // Request when band seen as the value band for GDALPolygonize()
196 :
197 636 : if (m_poSrcBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
198 : pData, nBufXSize, nBufYSize, eBufType,
199 : nPixelSpace, nLineSpace,
200 636 : psExtraArg) != CE_None)
201 : {
202 0 : return CE_Failure;
203 : }
204 636 : int64_t *panData = static_cast<int64_t *>(pData);
205 1272 : for (int iY = 0; iY < nYSize; ++iY)
206 : {
207 12728 : for (int iX = 0; iX < nXSize; ++iX)
208 : {
209 12092 : if (panData[iX])
210 12034 : panData[iX] = 1;
211 : }
212 636 : panData += (nLineSpace / nPixelSpace);
213 : }
214 :
215 636 : return CE_None;
216 : }
217 :
218 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
219 : pData, nBufXSize, nBufYSize, eBufType,
220 0 : nPixelSpace, nLineSpace, psExtraArg);
221 : }
222 : };
223 :
224 : /************************************************************************/
225 : /* GDALFootprintCombinedMaskBand */
226 : /************************************************************************/
227 :
228 : class GDALFootprintCombinedMaskBand final : public GDALRasterBand
229 : {
230 : std::vector<GDALRasterBand *> m_apoSrcBands{};
231 :
232 : /** Whether to combine bands unioning (true) or intersecting (false) */
233 : bool m_bUnion = false;
234 :
235 : public:
236 3 : explicit GDALFootprintCombinedMaskBand(
237 : const std::vector<GDALRasterBand *> &apoSrcBands, bool bUnion)
238 3 : : m_apoSrcBands(apoSrcBands), m_bUnion(bUnion)
239 : {
240 3 : nRasterXSize = m_apoSrcBands[0]->GetXSize();
241 3 : nRasterYSize = m_apoSrcBands[0]->GetYSize();
242 3 : eDataType = GDT_Byte;
243 3 : m_apoSrcBands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
244 3 : }
245 :
246 : protected:
247 0 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override
248 : {
249 : int nWindowXSize;
250 : int nWindowYSize;
251 0 : m_apoSrcBands[0]->GetActualBlockSize(nBlockXOff, nBlockYOff,
252 : &nWindowXSize, &nWindowYSize);
253 : GDALRasterIOExtraArg sExtraArg;
254 0 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
255 0 : return IRasterIO(GF_Read, nBlockXOff * nBlockXSize,
256 0 : nBlockYOff * nBlockYSize, nWindowXSize, nWindowYSize,
257 : pData, nWindowXSize, nWindowYSize, GDT_Byte, 1,
258 0 : nBlockXSize, &sExtraArg);
259 : }
260 :
261 12 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
262 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
263 : GDALDataType eBufType, GSpacing nPixelSpace,
264 : GSpacing nLineSpace,
265 : GDALRasterIOExtraArg *psExtraArg) override
266 : {
267 12 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
268 6 : eBufType == GDT_Byte && nPixelSpace == 1)
269 : {
270 : // Request when band seen as the mask band for GDALPolygonize()
271 : {
272 6 : GByte *pabyData = static_cast<GByte *>(pData);
273 12 : for (int iY = 0; iY < nYSize; ++iY)
274 : {
275 6 : memset(pabyData, m_bUnion ? 0 : 1, nXSize);
276 6 : pabyData += nLineSpace;
277 : }
278 : }
279 :
280 12 : std::vector<GByte> abyTmp(static_cast<size_t>(nXSize) * nYSize);
281 18 : for (auto poBand : m_apoSrcBands)
282 : {
283 12 : if (poBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
284 12 : abyTmp.data(), nBufXSize, nBufYSize,
285 : GDT_Byte, 1, nXSize,
286 12 : psExtraArg) != CE_None)
287 : {
288 0 : return CE_Failure;
289 : }
290 12 : GByte *pabyData = static_cast<GByte *>(pData);
291 12 : size_t iTmp = 0;
292 24 : for (int iY = 0; iY < nYSize; ++iY)
293 : {
294 12 : if (m_bUnion)
295 : {
296 16 : for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
297 : {
298 12 : if (abyTmp[iTmp])
299 4 : pabyData[iX] = 1;
300 : }
301 : }
302 : else
303 : {
304 28 : for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
305 : {
306 20 : if (abyTmp[iTmp] == 0)
307 10 : pabyData[iX] = 0;
308 : }
309 : }
310 12 : pabyData += nLineSpace;
311 : }
312 : }
313 :
314 6 : return CE_None;
315 : }
316 :
317 6 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
318 6 : eBufType == GDT_Int64 &&
319 6 : nPixelSpace == static_cast<int>(sizeof(int64_t)) &&
320 6 : (nLineSpace % nPixelSpace) == 0)
321 : {
322 : // Request when band seen as the value band for GDALPolygonize()
323 : {
324 6 : int64_t *panData = static_cast<int64_t *>(pData);
325 12 : for (int iY = 0; iY < nYSize; ++iY)
326 : {
327 6 : if (m_bUnion)
328 : {
329 2 : memset(panData, 0, nXSize * sizeof(int64_t));
330 : }
331 : else
332 : {
333 4 : int64_t nOne = 1;
334 4 : GDALCopyWords(&nOne, GDT_Int64, 0, panData, GDT_Int64,
335 : sizeof(int64_t), nXSize);
336 : }
337 6 : panData += (nLineSpace / nPixelSpace);
338 : }
339 : }
340 :
341 12 : std::vector<GByte> abyTmp(static_cast<size_t>(nXSize) * nYSize);
342 18 : for (auto poBand : m_apoSrcBands)
343 : {
344 12 : if (poBand->RasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize,
345 12 : abyTmp.data(), nBufXSize, nBufYSize,
346 : GDT_Byte, 1, nXSize,
347 12 : psExtraArg) != CE_None)
348 : {
349 0 : return CE_Failure;
350 : }
351 12 : size_t iTmp = 0;
352 12 : int64_t *panData = static_cast<int64_t *>(pData);
353 24 : for (int iY = 0; iY < nYSize; ++iY)
354 : {
355 12 : if (m_bUnion)
356 : {
357 16 : for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
358 : {
359 12 : if (abyTmp[iTmp])
360 4 : panData[iX] = 1;
361 : }
362 : }
363 : else
364 : {
365 28 : for (int iX = 0; iX < nXSize; ++iX, ++iTmp)
366 : {
367 20 : if (abyTmp[iTmp] == 0)
368 10 : panData[iX] = 0;
369 : }
370 : }
371 12 : panData += (nLineSpace / nPixelSpace);
372 : }
373 : }
374 6 : return CE_None;
375 : }
376 :
377 0 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
378 : pData, nBufXSize, nBufYSize, eBufType,
379 0 : nPixelSpace, nLineSpace, psExtraArg);
380 : }
381 : };
382 :
383 : /************************************************************************/
384 : /* GetOutputLayerAndUpdateDstDS() */
385 : /************************************************************************/
386 :
387 : static OGRLayer *
388 43 : GetOutputLayerAndUpdateDstDS(const char *pszDest, GDALDatasetH &hDstDS,
389 : GDALDataset *poSrcDS,
390 : const GDALFootprintOptions *psOptions)
391 : {
392 :
393 43 : if (pszDest == nullptr)
394 3 : pszDest = GDALGetDescription(hDstDS);
395 :
396 : /* -------------------------------------------------------------------- */
397 : /* Create output dataset if needed */
398 : /* -------------------------------------------------------------------- */
399 43 : const bool bCreateOutput = psOptions->bCreateOutput || hDstDS == nullptr;
400 :
401 43 : GDALDriverH hDriver = nullptr;
402 43 : if (bCreateOutput)
403 : {
404 39 : std::string osFormat(psOptions->osFormat);
405 39 : if (osFormat.empty())
406 : {
407 5 : const auto aoDrivers = GetOutputDriversFor(pszDest, GDAL_OF_VECTOR);
408 5 : if (aoDrivers.empty())
409 : {
410 1 : CPLError(CE_Failure, CPLE_AppDefined,
411 : "Cannot guess driver for %s", pszDest);
412 1 : return nullptr;
413 : }
414 : else
415 : {
416 4 : if (aoDrivers.size() > 1)
417 : {
418 1 : CPLError(CE_Warning, CPLE_AppDefined,
419 : "Several drivers matching %s extension. Using %s",
420 1 : CPLGetExtension(pszDest), aoDrivers[0].c_str());
421 : }
422 4 : osFormat = aoDrivers[0];
423 : }
424 : }
425 :
426 : /* ----------------------------------------------------------------- */
427 : /* Find the output driver. */
428 : /* ----------------------------------------------------------------- */
429 38 : hDriver = GDALGetDriverByName(osFormat.c_str());
430 : char **papszDriverMD =
431 38 : hDriver ? GDALGetMetadata(hDriver, nullptr) : nullptr;
432 75 : if (hDriver == nullptr ||
433 37 : !CPLTestBool(CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_VECTOR,
434 75 : "FALSE")) ||
435 36 : !CPLTestBool(
436 : CSLFetchNameValueDef(papszDriverMD, GDAL_DCAP_CREATE, "FALSE")))
437 : {
438 2 : CPLError(CE_Failure, CPLE_NotSupported,
439 : "Output driver `%s' not recognised or does not support "
440 : "direct output file creation.",
441 : osFormat.c_str());
442 2 : return nullptr;
443 : }
444 :
445 36 : hDstDS = GDALCreate(hDriver, pszDest, 0, 0, 0, GDT_Unknown,
446 : psOptions->aosDSCO.List());
447 36 : if (!hDstDS)
448 : {
449 1 : return nullptr;
450 : }
451 : }
452 :
453 : /* -------------------------------------------------------------------- */
454 : /* Open or create target layer. */
455 : /* -------------------------------------------------------------------- */
456 39 : auto poDstDS = GDALDataset::FromHandle(hDstDS);
457 39 : OGRLayer *poLayer = nullptr;
458 39 : if (!psOptions->osDestLayerName.empty())
459 : {
460 3 : poLayer = poDstDS->GetLayerByName(psOptions->osDestLayerName.c_str());
461 3 : if (!poLayer)
462 : {
463 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find layer %s",
464 : psOptions->osDestLayerName.c_str());
465 1 : return nullptr;
466 : }
467 : }
468 37 : else if (poDstDS->GetLayerCount() == 1 && poDstDS->GetDriver() &&
469 1 : EQUAL(poDstDS->GetDriver()->GetDescription(), "ESRI Shapefile"))
470 : {
471 1 : poLayer = poDstDS->GetLayer(0);
472 : }
473 : else
474 : {
475 35 : poLayer = poDstDS->GetLayerByName(DEFAULT_LAYER_NAME);
476 : }
477 38 : if (!poLayer)
478 : {
479 35 : std::string osDestLayerName = psOptions->osDestLayerName;
480 35 : if (osDestLayerName.empty())
481 : {
482 70 : if (poDstDS->GetDriver() &&
483 35 : EQUAL(poDstDS->GetDriver()->GetDescription(), "ESRI Shapefile"))
484 : {
485 3 : osDestLayerName = CPLGetBasename(pszDest);
486 : }
487 : else
488 : {
489 32 : osDestLayerName = DEFAULT_LAYER_NAME;
490 : }
491 : }
492 :
493 0 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS;
494 35 : if (psOptions->bOutCSGeoref)
495 : {
496 20 : if (!psOptions->oOutputSRS.IsEmpty())
497 : {
498 2 : poSRS.reset(psOptions->oOutputSRS.Clone());
499 : }
500 18 : else if (auto poSrcSRS = poSrcDS->GetSpatialRef())
501 : {
502 10 : poSRS.reset(poSrcSRS->Clone());
503 : }
504 : }
505 :
506 105 : poLayer = poDstDS->CreateLayer(
507 35 : osDestLayerName.c_str(), poSRS.get(),
508 35 : psOptions->bSplitPolys ? wkbPolygon : wkbMultiPolygon,
509 : const_cast<char **>(psOptions->aosLCO.List()));
510 :
511 35 : if (!psOptions->osLocationFieldName.empty())
512 : {
513 : OGRFieldDefn oFieldDefn(psOptions->osLocationFieldName.c_str(),
514 34 : OFTString);
515 34 : if (poLayer->CreateField(&oFieldDefn) != OGRERR_NONE)
516 0 : return nullptr;
517 : }
518 : }
519 :
520 38 : return poLayer;
521 : }
522 :
523 : /************************************************************************/
524 : /* GeoTransformCoordinateTransformation */
525 : /************************************************************************/
526 :
527 : class GeoTransformCoordinateTransformation final
528 : : public OGRCoordinateTransformation
529 : {
530 : const std::array<double, 6> m_gt;
531 :
532 : public:
533 16 : explicit GeoTransformCoordinateTransformation(
534 : const std::array<double, 6> >)
535 16 : : m_gt(gt)
536 : {
537 16 : }
538 :
539 0 : const OGRSpatialReference *GetSourceCS() const override
540 : {
541 0 : return nullptr;
542 : }
543 :
544 48 : const OGRSpatialReference *GetTargetCS() const override
545 : {
546 48 : return nullptr;
547 : }
548 :
549 0 : OGRCoordinateTransformation *Clone() const override
550 : {
551 0 : return new GeoTransformCoordinateTransformation(m_gt);
552 : }
553 :
554 0 : OGRCoordinateTransformation *GetInverse() const override
555 : {
556 0 : CPLError(CE_Failure, CPLE_NotSupported,
557 : "GeoTransformCoordinateTransformation::GetInverse() not "
558 : "implemented");
559 0 : return nullptr;
560 : }
561 :
562 16 : int Transform(size_t nCount, double *x, double *y, double * /* z */,
563 : double * /* t */, int *pabSuccess) override
564 : {
565 96 : for (size_t i = 0; i < nCount; ++i)
566 : {
567 80 : const double X = m_gt[0] + x[i] * m_gt[1] + y[i] * m_gt[2];
568 80 : const double Y = m_gt[3] + x[i] * m_gt[4] + y[i] * m_gt[5];
569 80 : x[i] = X;
570 80 : y[i] = Y;
571 80 : if (pabSuccess)
572 80 : pabSuccess[i] = TRUE;
573 : }
574 16 : return TRUE;
575 : }
576 : };
577 :
578 : /************************************************************************/
579 : /* CountPoints() */
580 : /************************************************************************/
581 :
582 73 : static size_t CountPoints(const OGRGeometry *poGeom)
583 : {
584 73 : if (poGeom->getGeometryType() == wkbMultiPolygon)
585 : {
586 25 : size_t n = 0;
587 50 : for (auto *poPoly : poGeom->toMultiPolygon())
588 : {
589 25 : n += CountPoints(poPoly);
590 : }
591 25 : return n;
592 : }
593 48 : else if (poGeom->getGeometryType() == wkbPolygon)
594 : {
595 48 : size_t n = 0;
596 97 : for (auto *poRing : poGeom->toPolygon())
597 : {
598 49 : n += poRing->getNumPoints() - 1;
599 : }
600 48 : return n;
601 : }
602 0 : return 0;
603 : }
604 :
605 : /************************************************************************/
606 : /* GetMinDistanceBetweenTwoPoints() */
607 : /************************************************************************/
608 :
609 3 : static double GetMinDistanceBetweenTwoPoints(const OGRGeometry *poGeom)
610 : {
611 3 : if (poGeom->getGeometryType() == wkbMultiPolygon)
612 : {
613 1 : double v = std::numeric_limits<double>::max();
614 2 : for (auto *poPoly : poGeom->toMultiPolygon())
615 : {
616 1 : v = std::min(v, GetMinDistanceBetweenTwoPoints(poPoly));
617 : }
618 1 : return v;
619 : }
620 2 : else if (poGeom->getGeometryType() == wkbPolygon)
621 : {
622 1 : double v = std::numeric_limits<double>::max();
623 2 : for (auto *poRing : poGeom->toPolygon())
624 : {
625 1 : v = std::min(v, GetMinDistanceBetweenTwoPoints(poRing));
626 : }
627 1 : return v;
628 : }
629 1 : else if (poGeom->getGeometryType() == wkbLineString)
630 : {
631 1 : double v = std::numeric_limits<double>::max();
632 1 : const auto poLS = poGeom->toLineString();
633 1 : const int nNumPoints = poLS->getNumPoints();
634 7 : for (int i = 0; i < nNumPoints - 1; ++i)
635 : {
636 6 : const double x1 = poLS->getX(i);
637 6 : const double y1 = poLS->getY(i);
638 6 : const double x2 = poLS->getX(i + 1);
639 6 : const double y2 = poLS->getY(i + 1);
640 6 : const double d = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
641 6 : if (d > 0)
642 6 : v = std::min(v, d);
643 : }
644 1 : return sqrt(v);
645 : }
646 0 : return 0;
647 : }
648 :
649 : /************************************************************************/
650 : /* GDALFootprintProcess() */
651 : /************************************************************************/
652 :
653 38 : static bool GDALFootprintProcess(GDALDataset *poSrcDS, OGRLayer *poDstLayer,
654 : const GDALFootprintOptions *psOptions)
655 : {
656 38 : std::unique_ptr<OGRCoordinateTransformation> poCT_SRS;
657 38 : const OGRSpatialReference *poDstSRS = poDstLayer->GetSpatialRef();
658 38 : if (!psOptions->oOutputSRS.IsEmpty())
659 2 : poDstSRS = &(psOptions->oOutputSRS);
660 38 : if (poDstSRS)
661 : {
662 12 : auto poSrcSRS = poSrcDS->GetSpatialRef();
663 12 : if (!poSrcSRS)
664 : {
665 1 : CPLError(CE_Failure, CPLE_AppDefined,
666 : "Output layer has CRS, but input is not georeferenced");
667 1 : return false;
668 : }
669 11 : poCT_SRS.reset(OGRCreateCoordinateTransformation(poSrcSRS, poDstSRS));
670 11 : if (!poCT_SRS)
671 0 : return false;
672 : }
673 :
674 74 : std::vector<int> anBands = psOptions->anBands;
675 37 : const int nBandCount = poSrcDS->GetRasterCount();
676 37 : if (anBands.empty())
677 : {
678 83 : for (int i = 1; i <= nBandCount; ++i)
679 47 : anBands.push_back(i);
680 : }
681 :
682 74 : std::vector<GDALRasterBand *> apoSrcMaskBands;
683 : const CPLStringList aosSrcNoData(
684 74 : CSLTokenizeString2(psOptions->osSrcNoData.c_str(), " ", 0));
685 74 : std::vector<double> adfSrcNoData;
686 37 : if (!psOptions->osSrcNoData.empty())
687 : {
688 3 : if (aosSrcNoData.size() != 1 &&
689 1 : static_cast<size_t>(aosSrcNoData.size()) != anBands.size())
690 : {
691 1 : CPLError(CE_Failure, CPLE_AppDefined,
692 : "Number of values in -srcnodata should be 1 or the number "
693 : "of bands");
694 1 : return false;
695 : }
696 2 : for (int i = 0; i < aosSrcNoData.size(); ++i)
697 : {
698 1 : adfSrcNoData.emplace_back(CPLAtof(aosSrcNoData[i]));
699 : }
700 : }
701 36 : bool bGlobalMask = true;
702 72 : std::vector<std::unique_ptr<GDALRasterBand>> apoTmpNoDataMaskBands;
703 76 : for (size_t i = 0; i < anBands.size(); ++i)
704 : {
705 45 : const int nBand = anBands[i];
706 45 : if (nBand <= 0 || nBand > nBandCount)
707 : {
708 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid band number: %d",
709 : nBand);
710 5 : return false;
711 : }
712 44 : auto poBand = poSrcDS->GetRasterBand(nBand);
713 44 : if (!adfSrcNoData.empty())
714 : {
715 1 : bGlobalMask = false;
716 : apoTmpNoDataMaskBands.emplace_back(
717 3 : std::make_unique<GDALNoDataMaskBand>(
718 2 : poBand, adfSrcNoData.size() == 1 ? adfSrcNoData[0]
719 1 : : adfSrcNoData[i]));
720 1 : apoSrcMaskBands.push_back(apoTmpNoDataMaskBands.back().get());
721 : }
722 : else
723 : {
724 : GDALRasterBand *poMaskBand;
725 43 : const int nMaskFlags = poBand->GetMaskFlags();
726 43 : if (poBand->GetColorInterpretation() == GCI_AlphaBand)
727 : {
728 2 : poMaskBand = poBand;
729 : }
730 : else
731 : {
732 41 : if ((nMaskFlags & GMF_PER_DATASET) == 0)
733 : {
734 33 : bGlobalMask = false;
735 : }
736 41 : poMaskBand = poBand->GetMaskBand();
737 : }
738 43 : if (psOptions->nOvrIndex >= 0)
739 : {
740 10 : if (nMaskFlags == GMF_NODATA)
741 : {
742 : // If the mask band is based on nodata, we don't need
743 : // to check the overviews of the mask band, but we
744 : // can take the mask band of the overviews
745 4 : auto poOvrBand = poBand->GetOverview(psOptions->nOvrIndex);
746 4 : if (!poOvrBand)
747 : {
748 2 : if (poBand->GetOverviewCount() == 0)
749 : {
750 1 : CPLError(
751 : CE_Failure, CPLE_AppDefined,
752 : "Overview index %d invalid for this dataset. "
753 : "Bands of this dataset have no "
754 : "precomputed overviews",
755 1 : psOptions->nOvrIndex);
756 : }
757 : else
758 : {
759 1 : CPLError(
760 : CE_Failure, CPLE_AppDefined,
761 : "Overview index %d invalid for this dataset. "
762 : "Value should be in [0,%d] range",
763 1 : psOptions->nOvrIndex,
764 1 : poBand->GetOverviewCount() - 1);
765 : }
766 4 : return false;
767 : }
768 2 : if (poOvrBand->GetMaskFlags() != GMF_NODATA)
769 : {
770 0 : CPLError(CE_Failure, CPLE_AppDefined,
771 : "poOvrBand->GetMaskFlags() != GMF_NODATA");
772 0 : return false;
773 : }
774 2 : poMaskBand = poOvrBand->GetMaskBand();
775 : }
776 : else
777 : {
778 6 : poMaskBand = poMaskBand->GetOverview(psOptions->nOvrIndex);
779 6 : if (!poMaskBand)
780 : {
781 2 : if (poBand->GetMaskBand()->GetOverviewCount() == 0)
782 : {
783 1 : CPLError(
784 : CE_Failure, CPLE_AppDefined,
785 : "Overview index %d invalid for this dataset. "
786 : "Mask bands of this dataset have no "
787 : "precomputed overviews",
788 1 : psOptions->nOvrIndex);
789 : }
790 : else
791 : {
792 1 : CPLError(
793 : CE_Failure, CPLE_AppDefined,
794 : "Overview index %d invalid for this dataset. "
795 : "Value should be in [0,%d] range",
796 1 : psOptions->nOvrIndex,
797 1 : poBand->GetMaskBand()->GetOverviewCount() - 1);
798 : }
799 2 : return false;
800 : }
801 : }
802 : }
803 39 : apoSrcMaskBands.push_back(poMaskBand);
804 : }
805 : }
806 :
807 31 : std::unique_ptr<OGRCoordinateTransformation> poCT_GT;
808 31 : std::array<double, 6> adfGeoTransform{{0.0, 1.0, 0.0, 0.0, 0.0, 1.0}};
809 47 : if (psOptions->bOutCSGeoref &&
810 16 : poSrcDS->GetGeoTransform(adfGeoTransform.data()) == CE_None)
811 : {
812 14 : auto poMaskBand = apoSrcMaskBands[0];
813 28 : adfGeoTransform[1] *=
814 14 : double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize();
815 28 : adfGeoTransform[2] *=
816 14 : double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize();
817 28 : adfGeoTransform[4] *=
818 14 : double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize();
819 28 : adfGeoTransform[5] *=
820 14 : double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize();
821 28 : poCT_GT = std::make_unique<GeoTransformCoordinateTransformation>(
822 14 : adfGeoTransform);
823 : }
824 17 : else if (psOptions->bOutCSGeorefRequested)
825 : {
826 1 : CPLError(CE_Failure, CPLE_AppDefined,
827 : "Georeferenced coordinates requested, but "
828 : "input dataset has no geotransform.");
829 1 : return false;
830 : }
831 16 : else if (psOptions->nOvrIndex >= 0)
832 : {
833 : // Transform from overview pixel coordinates to full resolution
834 : // pixel coordinates
835 2 : auto poMaskBand = apoSrcMaskBands[0];
836 4 : adfGeoTransform[1] =
837 2 : double(poSrcDS->GetRasterXSize()) / poMaskBand->GetXSize();
838 2 : adfGeoTransform[2] = 0;
839 2 : adfGeoTransform[4] = 0;
840 4 : adfGeoTransform[5] =
841 2 : double(poSrcDS->GetRasterYSize()) / poMaskBand->GetYSize();
842 4 : poCT_GT = std::make_unique<GeoTransformCoordinateTransformation>(
843 2 : adfGeoTransform);
844 : }
845 :
846 30 : std::unique_ptr<GDALRasterBand> poMaskForRasterize;
847 30 : if (bGlobalMask || anBands.size() == 1)
848 : {
849 : poMaskForRasterize =
850 27 : std::make_unique<GDALFootprintMaskBand>(apoSrcMaskBands[0]);
851 : }
852 : else
853 : {
854 3 : poMaskForRasterize = std::make_unique<GDALFootprintCombinedMaskBand>(
855 3 : apoSrcMaskBands, psOptions->bCombineBandsUnion);
856 : }
857 :
858 30 : auto hBand = GDALRasterBand::ToHandle(poMaskForRasterize.get());
859 60 : auto poMemLayer = std::make_unique<OGRMemLayer>("", nullptr, wkbUnknown);
860 : const CPLErr eErr =
861 30 : GDALPolygonize(hBand, hBand, OGRLayer::ToHandle(poMemLayer.get()),
862 : /* iPixValField = */ -1,
863 30 : /* papszOptions = */ nullptr, psOptions->pfnProgress,
864 30 : psOptions->pProgressData);
865 30 : if (eErr != CE_None)
866 : {
867 0 : return false;
868 : }
869 :
870 30 : if (!psOptions->bSplitPolys)
871 : {
872 58 : auto poMP = std::make_unique<OGRMultiPolygon>();
873 58 : for (auto &&poFeature : poMemLayer.get())
874 : {
875 : auto poGeom =
876 58 : std::unique_ptr<OGRGeometry>(poFeature->StealGeometry());
877 29 : CPLAssert(poGeom);
878 29 : if (poGeom->getGeometryType() == wkbPolygon)
879 : {
880 29 : poMP->addGeometryDirectly(poGeom.release());
881 : }
882 : }
883 29 : poMemLayer = std::make_unique<OGRMemLayer>("", nullptr, wkbUnknown);
884 : auto poFeature =
885 29 : std::make_unique<OGRFeature>(poMemLayer->GetLayerDefn());
886 29 : poFeature->SetGeometryDirectly(poMP.release());
887 29 : CPL_IGNORE_RET_VAL(poMemLayer->CreateFeature(poFeature.get()));
888 : }
889 :
890 61 : for (auto &&poFeature : poMemLayer.get())
891 : {
892 31 : auto poGeom = std::unique_ptr<OGRGeometry>(poFeature->StealGeometry());
893 31 : CPLAssert(poGeom);
894 31 : if (poGeom->IsEmpty())
895 1 : continue;
896 :
897 : auto poDstFeature =
898 30 : std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
899 30 : poDstFeature->SetFrom(poFeature.get());
900 :
901 30 : if (poCT_GT)
902 : {
903 16 : if (poGeom->transform(poCT_GT.get()) != OGRERR_NONE)
904 0 : return false;
905 : }
906 :
907 30 : if (psOptions->dfDensifyDistance > 0)
908 : {
909 1 : OGREnvelope sEnvelope;
910 1 : poGeom->getEnvelope(&sEnvelope);
911 : // Some sanity check to avoid insane memory allocations
912 1 : if (sEnvelope.MaxX - sEnvelope.MinX >
913 1 : 1e6 * psOptions->dfDensifyDistance ||
914 1 : sEnvelope.MaxY - sEnvelope.MinY >
915 1 : 1e6 * psOptions->dfDensifyDistance)
916 : {
917 0 : CPLError(CE_Failure, CPLE_AppDefined,
918 : "Densification distance too small compared to "
919 : "geometry extent");
920 0 : return false;
921 : }
922 1 : poGeom->segmentize(psOptions->dfDensifyDistance);
923 : }
924 :
925 30 : if (poCT_SRS)
926 : {
927 11 : if (poGeom->transform(poCT_SRS.get()) != OGRERR_NONE)
928 0 : return false;
929 : }
930 :
931 30 : if (psOptions->dfMinRingArea != 0)
932 : {
933 2 : if (poGeom->getGeometryType() == wkbMultiPolygon)
934 : {
935 4 : auto poMP = std::make_unique<OGRMultiPolygon>();
936 4 : for (auto *poPoly : poGeom->toMultiPolygon())
937 : {
938 4 : auto poNewPoly = std::make_unique<OGRPolygon>();
939 4 : for (auto *poRing : poPoly)
940 : {
941 2 : if (poRing->get_Area() >= psOptions->dfMinRingArea)
942 : {
943 1 : poNewPoly->addRing(poRing);
944 : }
945 : }
946 2 : if (!poNewPoly->IsEmpty())
947 1 : poMP->addGeometryDirectly(poNewPoly.release());
948 : }
949 2 : poGeom = std::move(poMP);
950 : }
951 0 : else if (poGeom->getGeometryType() == wkbPolygon)
952 : {
953 0 : auto poNewPoly = std::make_unique<OGRPolygon>();
954 0 : for (auto *poRing : poGeom->toPolygon())
955 : {
956 0 : if (poRing->get_Area() >= psOptions->dfMinRingArea)
957 : {
958 0 : poNewPoly->addRing(poRing);
959 : }
960 : }
961 0 : poGeom = std::move(poNewPoly);
962 : }
963 2 : if (poGeom->IsEmpty())
964 1 : continue;
965 : }
966 :
967 29 : if (psOptions->bConvexHull)
968 : {
969 1 : poGeom.reset(poGeom->ConvexHull());
970 1 : if (!poGeom || poGeom->IsEmpty())
971 0 : continue;
972 : }
973 :
974 29 : if (psOptions->dfSimplifyTolerance != 0)
975 : {
976 1 : poGeom.reset(poGeom->Simplify(psOptions->dfSimplifyTolerance));
977 1 : if (!poGeom || poGeom->IsEmpty())
978 0 : continue;
979 : }
980 :
981 58 : if (psOptions->nMaxPoints > 0 &&
982 29 : CountPoints(poGeom.get()) >
983 29 : static_cast<size_t>(psOptions->nMaxPoints))
984 : {
985 1 : OGREnvelope sEnvelope;
986 1 : poGeom->getEnvelope(&sEnvelope);
987 1 : double tolMin = GetMinDistanceBetweenTwoPoints(poGeom.get());
988 2 : double tolMax = std::max(sEnvelope.MaxY - sEnvelope.MinY,
989 1 : sEnvelope.MaxX - sEnvelope.MinX);
990 21 : for (int i = 0; i < 20; ++i)
991 : {
992 20 : const double tol = (tolMin + tolMax) / 2;
993 : std::unique_ptr<OGRGeometry> poSimplifiedGeom(
994 20 : poGeom->Simplify(tol));
995 20 : if (!poSimplifiedGeom || poSimplifiedGeom->IsEmpty())
996 : {
997 1 : tolMax = tol;
998 1 : continue;
999 : }
1000 19 : const auto nPoints = CountPoints(poSimplifiedGeom.get());
1001 19 : if (nPoints == static_cast<size_t>(psOptions->nMaxPoints))
1002 : {
1003 0 : tolMax = tol;
1004 0 : break;
1005 : }
1006 19 : else if (nPoints < static_cast<size_t>(psOptions->nMaxPoints))
1007 : {
1008 19 : tolMax = tol;
1009 : }
1010 : else
1011 : {
1012 0 : tolMin = tol;
1013 : }
1014 : }
1015 1 : poGeom.reset(poGeom->Simplify(tolMax));
1016 1 : if (!poGeom || poGeom->IsEmpty())
1017 0 : continue;
1018 : }
1019 :
1020 29 : if (!psOptions->bSplitPolys && poGeom->getGeometryType() == wkbPolygon)
1021 3 : poGeom.reset(
1022 : OGRGeometryFactory::forceToMultiPolygon(poGeom.release()));
1023 :
1024 29 : poDstFeature->SetGeometryDirectly(poGeom.release());
1025 :
1026 29 : if (!psOptions->osLocationFieldName.empty())
1027 : {
1028 :
1029 56 : std::string osFilename = poSrcDS->GetDescription();
1030 : // Make sure it is a file before building absolute path name.
1031 : VSIStatBufL sStatBuf;
1032 57 : if (psOptions->bAbsolutePath &&
1033 29 : CPLIsFilenameRelative(osFilename.c_str()) &&
1034 1 : VSIStatL(osFilename.c_str(), &sStatBuf) == 0)
1035 : {
1036 1 : char *pszCurDir = CPLGetCurrentDir();
1037 1 : if (pszCurDir)
1038 : {
1039 : osFilename = CPLProjectRelativeFilename(pszCurDir,
1040 1 : osFilename.c_str());
1041 1 : CPLFree(pszCurDir);
1042 : }
1043 : }
1044 28 : poDstFeature->SetField(psOptions->osLocationFieldName.c_str(),
1045 : osFilename.c_str());
1046 : }
1047 :
1048 29 : if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
1049 : {
1050 0 : return false;
1051 : }
1052 : }
1053 :
1054 30 : return true;
1055 : }
1056 :
1057 : /************************************************************************/
1058 : /* GDALFootprint() */
1059 : /************************************************************************/
1060 :
1061 : /* clang-format off */
1062 : /**
1063 : * Computes the footprint of a raster.
1064 : *
1065 : * This is the equivalent of the
1066 : * <a href="/programs/gdal_footprint.html">gdal_footprint</a> utility.
1067 : *
1068 : * GDALFootprintOptions* must be allocated and freed with
1069 : * GDALFootprintOptionsNew() and GDALFootprintOptionsFree() respectively.
1070 : * pszDest and hDstDS cannot be used at the same time.
1071 : *
1072 : * @param pszDest the vector destination dataset path or NULL.
1073 : * @param hDstDS the vector destination dataset or NULL.
1074 : * @param hSrcDataset the raster source dataset handle.
1075 : * @param psOptionsIn the options struct returned by GDALFootprintOptionsNew()
1076 : * or NULL.
1077 : * @param pbUsageError pointer to a integer output variable to store if any
1078 : * usage error has occurred or NULL.
1079 : * @return the output dataset (new dataset that must be closed using
1080 : * GDALClose(), or hDstDS is not NULL) or NULL in case of error.
1081 : *
1082 : * @since GDAL 3.8
1083 : */
1084 : /* clang-format on */
1085 :
1086 47 : GDALDatasetH GDALFootprint(const char *pszDest, GDALDatasetH hDstDS,
1087 : GDALDatasetH hSrcDataset,
1088 : const GDALFootprintOptions *psOptionsIn,
1089 : int *pbUsageError)
1090 : {
1091 47 : if (pszDest == nullptr && hDstDS == nullptr)
1092 : {
1093 1 : CPLError(CE_Failure, CPLE_AppDefined,
1094 : "pszDest == NULL && hDstDS == NULL");
1095 :
1096 1 : if (pbUsageError)
1097 0 : *pbUsageError = TRUE;
1098 1 : return nullptr;
1099 : }
1100 46 : if (hSrcDataset == nullptr)
1101 : {
1102 1 : CPLError(CE_Failure, CPLE_AppDefined, "hSrcDataset== NULL");
1103 :
1104 1 : if (pbUsageError)
1105 0 : *pbUsageError = TRUE;
1106 1 : return nullptr;
1107 : }
1108 45 : if (hDstDS != nullptr && psOptionsIn && psOptionsIn->bCreateOutput)
1109 : {
1110 1 : CPLError(CE_Failure, CPLE_AppDefined,
1111 : "hDstDS != NULL but options that imply creating a new dataset "
1112 : "have been set.");
1113 :
1114 1 : if (pbUsageError)
1115 0 : *pbUsageError = TRUE;
1116 1 : return nullptr;
1117 : }
1118 :
1119 44 : GDALFootprintOptions *psOptionsToFree = nullptr;
1120 44 : const GDALFootprintOptions *psOptions = psOptionsIn;
1121 44 : if (psOptions == nullptr)
1122 : {
1123 1 : psOptionsToFree = GDALFootprintOptionsNew(nullptr, nullptr);
1124 1 : psOptions = psOptionsToFree;
1125 : }
1126 :
1127 44 : const bool bCloseOutDSOnError = hDstDS == nullptr;
1128 :
1129 44 : auto poSrcDS = GDALDataset::FromHandle(hSrcDataset);
1130 44 : if (poSrcDS->GetRasterCount() == 0)
1131 : {
1132 1 : CPLError(CE_Failure, CPLE_AppDefined,
1133 : "Input dataset has no raster band.%s",
1134 1 : poSrcDS->GetMetadata("SUBDATASETS")
1135 : ? " You need to specify one subdataset."
1136 : : "");
1137 1 : GDALFootprintOptionsFree(psOptionsToFree);
1138 1 : if (bCloseOutDSOnError)
1139 0 : GDALClose(hDstDS);
1140 1 : return nullptr;
1141 : }
1142 :
1143 : auto poLayer =
1144 43 : GetOutputLayerAndUpdateDstDS(pszDest, hDstDS, poSrcDS, psOptions);
1145 43 : if (!poLayer)
1146 : {
1147 5 : GDALFootprintOptionsFree(psOptionsToFree);
1148 5 : if (hDstDS && bCloseOutDSOnError)
1149 0 : GDALClose(hDstDS);
1150 5 : return nullptr;
1151 : }
1152 :
1153 38 : if (!GDALFootprintProcess(poSrcDS, poLayer, psOptions))
1154 : {
1155 8 : GDALFootprintOptionsFree(psOptionsToFree);
1156 8 : if (bCloseOutDSOnError)
1157 7 : GDALClose(hDstDS);
1158 8 : return nullptr;
1159 : }
1160 :
1161 30 : GDALFootprintOptionsFree(psOptionsToFree);
1162 :
1163 30 : return hDstDS;
1164 : }
1165 :
1166 : /************************************************************************/
1167 : /* GDALFootprintOptionsNew() */
1168 : /************************************************************************/
1169 :
1170 : /**
1171 : * Allocates a GDALFootprintOptions struct.
1172 : *
1173 : * @param papszArgv NULL terminated list of options (potentially including
1174 : * filename and open options too), or NULL. The accepted options are the ones of
1175 : * the <a href="/programs/gdal_rasterize.html">gdal_rasterize</a> utility.
1176 : * @param psOptionsForBinary (output) may be NULL (and should generally be
1177 : * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1178 : * GDALFootprintOptionsForBinaryNew() prior to this function. Will be filled
1179 : * with potentially present filename, open options,...
1180 : * @return pointer to the allocated GDALFootprintOptions struct. Must be freed
1181 : * with GDALFootprintOptionsFree().
1182 : *
1183 : * @since GDAL 3.8
1184 : */
1185 :
1186 : GDALFootprintOptions *
1187 48 : GDALFootprintOptionsNew(char **papszArgv,
1188 : GDALFootprintOptionsForBinary *psOptionsForBinary)
1189 : {
1190 96 : auto psOptions = std::make_unique<GDALFootprintOptions>();
1191 :
1192 48 : bool bGotSourceFilename = false;
1193 48 : bool bGotDestFilename = false;
1194 : /* -------------------------------------------------------------------- */
1195 : /* Handle command line arguments. */
1196 : /* -------------------------------------------------------------------- */
1197 48 : const int argc = CSLCount(papszArgv);
1198 188 : for (int i = 0; papszArgv != nullptr && i < argc; i++)
1199 : {
1200 140 : if (i < argc - 1 &&
1201 131 : (EQUAL(papszArgv[i], "-of") || EQUAL(papszArgv[i], "-f")))
1202 : {
1203 37 : ++i;
1204 37 : psOptions->osFormat = papszArgv[i];
1205 37 : psOptions->bCreateOutput = true;
1206 : }
1207 :
1208 103 : else if (EQUAL(papszArgv[i], "-q") || EQUAL(papszArgv[i], "-quiet"))
1209 : {
1210 1 : if (psOptionsForBinary)
1211 : {
1212 1 : psOptionsForBinary->bQuiet = true;
1213 : }
1214 : else
1215 : {
1216 0 : CPLError(CE_Failure, CPLE_NotSupported,
1217 : "%s switch only supported from gdal_footprint binary.",
1218 0 : papszArgv[i]);
1219 0 : return nullptr;
1220 : }
1221 : }
1222 :
1223 102 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-oo"))
1224 : {
1225 0 : i++;
1226 0 : if (psOptionsForBinary)
1227 : {
1228 0 : psOptionsForBinary->aosOpenOptions.AddString(papszArgv[i]);
1229 : }
1230 : else
1231 : {
1232 0 : CPLError(
1233 : CE_Failure, CPLE_NotSupported,
1234 : "-oo switch only supported from gdal_footprint binary.");
1235 : }
1236 : }
1237 :
1238 102 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-t_cs"))
1239 : {
1240 18 : i++;
1241 18 : const std::string osVal(papszArgv[i]);
1242 18 : if (osVal == "georef")
1243 : {
1244 2 : psOptions->bOutCSGeoref = true;
1245 2 : psOptions->bOutCSGeorefRequested = true;
1246 : }
1247 16 : else if (osVal == "pixel")
1248 : {
1249 16 : psOptions->bOutCSGeoref = false;
1250 : }
1251 : else
1252 : {
1253 0 : CPLError(CE_Failure, CPLE_NotSupported,
1254 : "Invalid value for -t_cs");
1255 0 : return nullptr;
1256 18 : }
1257 : }
1258 :
1259 84 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-t_srs"))
1260 : {
1261 3 : i++;
1262 3 : const std::string osVal(papszArgv[i]);
1263 3 : if (psOptions->oOutputSRS.SetFromUserInput(osVal.c_str()) !=
1264 : OGRERR_NONE)
1265 : {
1266 0 : CPLError(CE_Failure, CPLE_AppDefined,
1267 : "Failed to process SRS definition: %s", osVal.c_str());
1268 0 : return nullptr;
1269 : }
1270 3 : psOptions->oOutputSRS.SetAxisMappingStrategy(
1271 3 : OAMS_TRADITIONAL_GIS_ORDER);
1272 : }
1273 :
1274 81 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-b"))
1275 : {
1276 1 : i++;
1277 1 : psOptions->anBands.push_back(atoi(papszArgv[i]));
1278 : }
1279 :
1280 80 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-combine_bands"))
1281 : {
1282 2 : i++;
1283 2 : if (EQUAL(papszArgv[i], "union"))
1284 0 : psOptions->bCombineBandsUnion = true;
1285 2 : else if (EQUAL(papszArgv[i], "intersection"))
1286 2 : psOptions->bCombineBandsUnion = false;
1287 : else
1288 : {
1289 0 : CPLError(CE_Failure, CPLE_NotSupported,
1290 : "Invalid value for -combine_bands");
1291 0 : return nullptr;
1292 : }
1293 : }
1294 :
1295 78 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-srcnodata"))
1296 : {
1297 3 : i++;
1298 3 : psOptions->osSrcNoData = papszArgv[i];
1299 : }
1300 :
1301 75 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-lco"))
1302 : {
1303 1 : i++;
1304 1 : psOptions->aosLCO.AddString(papszArgv[i]);
1305 : }
1306 :
1307 74 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-dsco"))
1308 : {
1309 1 : i++;
1310 1 : psOptions->aosDSCO.AddString(papszArgv[i]);
1311 : }
1312 :
1313 73 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-lyr_name"))
1314 : {
1315 3 : i++;
1316 3 : psOptions->osDestLayerName = papszArgv[i];
1317 : }
1318 :
1319 70 : else if (EQUAL(papszArgv[i], "-split_polys"))
1320 : {
1321 1 : psOptions->bSplitPolys = true;
1322 : }
1323 :
1324 69 : else if (EQUAL(papszArgv[i], "-convex_hull"))
1325 : {
1326 1 : psOptions->bConvexHull = true;
1327 : }
1328 :
1329 68 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-densify"))
1330 : {
1331 1 : i++;
1332 1 : psOptions->dfDensifyDistance = CPLAtof(papszArgv[i]);
1333 : }
1334 :
1335 67 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-simplify"))
1336 : {
1337 1 : i++;
1338 1 : psOptions->dfSimplifyTolerance = CPLAtof(papszArgv[i]);
1339 : }
1340 :
1341 66 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-max_points"))
1342 : {
1343 1 : i++;
1344 1 : if (EQUAL(papszArgv[i], "unlimited"))
1345 : {
1346 0 : psOptions->nMaxPoints = 0;
1347 : }
1348 : else
1349 : {
1350 1 : psOptions->nMaxPoints = atoi(papszArgv[i]);
1351 1 : if (psOptions->nMaxPoints > 0 && psOptions->nMaxPoints < 3)
1352 : {
1353 0 : CPLError(CE_Failure, CPLE_NotSupported,
1354 : "Invalid value for -max_points");
1355 0 : return nullptr;
1356 : }
1357 : }
1358 : }
1359 :
1360 65 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-min_ring_area"))
1361 : {
1362 2 : i++;
1363 2 : psOptions->dfMinRingArea = CPLAtof(papszArgv[i]);
1364 : }
1365 :
1366 63 : else if (EQUAL(papszArgv[i], "-overwrite"))
1367 : {
1368 1 : if (psOptionsForBinary)
1369 : {
1370 1 : psOptionsForBinary->bOverwrite = true;
1371 : }
1372 : else
1373 : {
1374 0 : CPLError(CE_Failure, CPLE_NotSupported,
1375 : "-overwrite switch only supported from gdal_footprint "
1376 : "binary.");
1377 0 : return nullptr;
1378 : }
1379 : }
1380 :
1381 62 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-location_field_name"))
1382 : {
1383 38 : i++;
1384 38 : psOptions->osLocationFieldName = papszArgv[i];
1385 : }
1386 :
1387 24 : else if (EQUAL(papszArgv[i], "-no_location"))
1388 : {
1389 1 : psOptions->osLocationFieldName.clear();
1390 : }
1391 :
1392 23 : else if (EQUAL(papszArgv[i], "-write_absolute_path"))
1393 : {
1394 1 : psOptions->bAbsolutePath = true;
1395 : }
1396 :
1397 22 : else if (i < argc - 1 && EQUAL(papszArgv[i], "-ovr"))
1398 : {
1399 8 : i++;
1400 8 : psOptions->nOvrIndex = atoi(papszArgv[i]);
1401 : }
1402 :
1403 14 : else if (papszArgv[i][0] == '-')
1404 : {
1405 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unknown option name '%s'",
1406 0 : papszArgv[i]);
1407 0 : return nullptr;
1408 : }
1409 14 : else if (!bGotSourceFilename)
1410 : {
1411 7 : bGotSourceFilename = true;
1412 7 : if (psOptionsForBinary)
1413 : {
1414 7 : psOptionsForBinary->osSource = papszArgv[i];
1415 : }
1416 : else
1417 : {
1418 0 : CPLError(CE_Failure, CPLE_NotSupported,
1419 : "{source_filename} only supported from gdal_footprint "
1420 : "binary.");
1421 0 : return nullptr;
1422 : }
1423 : }
1424 7 : else if (!bGotDestFilename)
1425 : {
1426 7 : bGotDestFilename = true;
1427 7 : CPLAssert(psOptionsForBinary);
1428 7 : psOptionsForBinary->bDestSpecified = true;
1429 7 : psOptionsForBinary->osDest = papszArgv[i];
1430 : }
1431 : else
1432 : {
1433 0 : CPLError(CE_Failure, CPLE_NotSupported,
1434 0 : "Too many command options '%s'", papszArgv[i]);
1435 0 : return nullptr;
1436 : }
1437 : }
1438 :
1439 48 : if (!psOptions->bOutCSGeoref && !psOptions->oOutputSRS.IsEmpty())
1440 : {
1441 1 : CPLError(CE_Failure, CPLE_AppDefined,
1442 : "-t_cs pixel and -t_srs are mutually exclusive.");
1443 1 : return nullptr;
1444 : }
1445 :
1446 47 : if (!psOptions->osSrcNoData.empty() && psOptions->nOvrIndex >= 0)
1447 : {
1448 1 : CPLError(CE_Failure, CPLE_AppDefined,
1449 : "-srcnodata and -ovr are mutually exclusive.");
1450 1 : return nullptr;
1451 : }
1452 :
1453 46 : if (psOptionsForBinary)
1454 : {
1455 7 : psOptionsForBinary->bCreateOutput = psOptions->bCreateOutput;
1456 7 : psOptionsForBinary->osFormat = psOptions->osFormat;
1457 7 : psOptionsForBinary->osDestLayerName = psOptions->osDestLayerName;
1458 : }
1459 :
1460 46 : return psOptions.release();
1461 : }
1462 :
1463 : /************************************************************************/
1464 : /* GDALFootprintOptionsFree() */
1465 : /************************************************************************/
1466 :
1467 : /**
1468 : * Frees the GDALFootprintOptions struct.
1469 : *
1470 : * @param psOptions the options struct for GDALFootprint().
1471 : *
1472 : * @since GDAL 3.8
1473 : */
1474 :
1475 88 : void GDALFootprintOptionsFree(GDALFootprintOptions *psOptions)
1476 : {
1477 88 : delete psOptions;
1478 88 : }
1479 :
1480 : /************************************************************************/
1481 : /* GDALFootprintOptionsSetProgress() */
1482 : /************************************************************************/
1483 :
1484 : /**
1485 : * Set a progress function.
1486 : *
1487 : * @param psOptions the options struct for GDALFootprint().
1488 : * @param pfnProgress the progress callback.
1489 : * @param pProgressData the user data for the progress callback.
1490 : *
1491 : * @since GDAL 3.8
1492 : */
1493 :
1494 6 : void GDALFootprintOptionsSetProgress(GDALFootprintOptions *psOptions,
1495 : GDALProgressFunc pfnProgress,
1496 : void *pProgressData)
1497 : {
1498 6 : psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
1499 6 : psOptions->pProgressData = pProgressData;
1500 6 : }
|