Line data Source code
1 : /* ****************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: GDAL scattered data gridding (interpolation) tool
5 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
6 : *
7 : * ****************************************************************************
8 : * Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
9 : * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gdal_utils.h"
16 : #include "gdal_utils_priv.h"
17 : #include "commonutils.h"
18 : #include "gdalargumentparser.h"
19 :
20 : #include <cmath>
21 : #include <cstdint>
22 : #include <cstdio>
23 : #include <cstdlib>
24 : #include <algorithm>
25 : #include <vector>
26 :
27 : #include "cpl_conv.h"
28 : #include "cpl_error.h"
29 : #include "cpl_progress.h"
30 : #include "cpl_string.h"
31 : #include "cpl_vsi.h"
32 : #include "gdal.h"
33 : #include "gdal_alg.h"
34 : #include "gdal_priv.h"
35 : #include "gdalgrid.h"
36 : #include "ogr_api.h"
37 : #include "ogr_core.h"
38 : #include "ogr_feature.h"
39 : #include "ogr_geometry.h"
40 : #include "ogr_spatialref.h"
41 : #include "ogr_srs_api.h"
42 : #include "ogrsf_frmts.h"
43 :
44 : /************************************************************************/
45 : /* GDALGridOptions */
46 : /************************************************************************/
47 :
48 : /** Options for use with GDALGrid(). GDALGridOptions* must be allocated
49 : * and freed with GDALGridOptionsNew() and GDALGridOptionsFree() respectively.
50 : */
51 : struct GDALGridOptions
52 : {
53 : /*! output format. Use the short format name. */
54 : std::string osFormat{};
55 :
56 : /*! allow or suppress progress monitor and other non-error output */
57 : bool bQuiet = true;
58 :
59 : /*! the progress function to use */
60 : GDALProgressFunc pfnProgress = GDALDummyProgress;
61 :
62 : /*! pointer to the progress data variable */
63 : void *pProgressData = nullptr;
64 :
65 : CPLStringList aosLayers{};
66 : std::string osBurnAttribute{};
67 : double dfIncreaseBurnValue = 0.0;
68 : double dfMultiplyBurnValue = 1.0;
69 : std::string osWHERE{};
70 : std::string osSQL{};
71 : GDALDataType eOutputType = GDT_Float64;
72 : CPLStringList aosCreateOptions{};
73 : int nXSize = 0;
74 : int nYSize = 0;
75 : double dfXRes = 0;
76 : double dfYRes = 0;
77 : double dfXMin = 0;
78 : double dfXMax = 0;
79 : double dfYMin = 0;
80 : double dfYMax = 0;
81 : bool bIsXExtentSet = false;
82 : bool bIsYExtentSet = false;
83 : GDALGridAlgorithm eAlgorithm = GGA_InverseDistanceToAPower;
84 : std::unique_ptr<void, VSIFreeReleaser> pOptions{};
85 : std::string osOutputSRS{};
86 : std::unique_ptr<OGRGeometry> poSpatialFilter{};
87 : bool bClipSrc = false;
88 : std::unique_ptr<OGRGeometry> poClipSrc{};
89 : std::string osClipSrcDS{};
90 : std::string osClipSrcSQL{};
91 : std::string osClipSrcLayer{};
92 : std::string osClipSrcWhere{};
93 : bool bNoDataSet = false;
94 : double dfNoDataValue = 0;
95 :
96 182 : GDALGridOptions()
97 182 : {
98 182 : void *l_pOptions = nullptr;
99 182 : GDALGridParseAlgorithmAndOptions(szAlgNameInvDist, &eAlgorithm,
100 : &l_pOptions);
101 182 : pOptions.reset(l_pOptions);
102 182 : }
103 :
104 : CPL_DISALLOW_COPY_ASSIGN(GDALGridOptions)
105 : };
106 :
107 : /************************************************************************/
108 : /* GetAlgorithmName() */
109 : /* */
110 : /* Grids algorithm code into mnemonic name. */
111 : /************************************************************************/
112 :
113 54 : static void PrintAlgorithmAndOptions(GDALGridAlgorithm eAlgorithm,
114 : void *pOptions)
115 : {
116 54 : switch (eAlgorithm)
117 : {
118 6 : case GGA_InverseDistanceToAPower:
119 : {
120 6 : printf("Algorithm name: \"%s\".\n", szAlgNameInvDist);
121 6 : GDALGridInverseDistanceToAPowerOptions *pOptions2 =
122 : static_cast<GDALGridInverseDistanceToAPowerOptions *>(pOptions);
123 6 : CPLprintf("Options are "
124 : "\"power=%f:smoothing=%f:radius1=%f:radius2=%f:angle=%f"
125 : ":max_points=%u:min_points=%u:nodata=%f\"\n",
126 : pOptions2->dfPower, pOptions2->dfSmoothing,
127 : pOptions2->dfRadius1, pOptions2->dfRadius2,
128 : pOptions2->dfAngle, pOptions2->nMaxPoints,
129 : pOptions2->nMinPoints, pOptions2->dfNoDataValue);
130 6 : break;
131 : }
132 3 : case GGA_InverseDistanceToAPowerNearestNeighbor:
133 : {
134 3 : printf("Algorithm name: \"%s\".\n",
135 : szAlgNameInvDistNearestNeighbor);
136 3 : GDALGridInverseDistanceToAPowerNearestNeighborOptions *pOptions2 =
137 : static_cast<
138 : GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
139 : pOptions);
140 6 : CPLString osStr;
141 : osStr.Printf("power=%f:smoothing=%f:radius=%f"
142 : ":max_points=%u:min_points=%u:nodata=%f",
143 : pOptions2->dfPower, pOptions2->dfSmoothing,
144 : pOptions2->dfRadius, pOptions2->nMaxPoints,
145 3 : pOptions2->nMinPoints, pOptions2->dfNoDataValue);
146 3 : if (pOptions2->nMinPointsPerQuadrant > 0)
147 : osStr += CPLSPrintf(":min_points_per_quadrant=%u",
148 0 : pOptions2->nMinPointsPerQuadrant);
149 3 : if (pOptions2->nMaxPointsPerQuadrant > 0)
150 : osStr += CPLSPrintf(":max_points_per_quadrant=%u",
151 0 : pOptions2->nMaxPointsPerQuadrant);
152 3 : printf("Options are: \"%s\n", osStr.c_str()); /* ok */
153 3 : break;
154 : }
155 6 : case GGA_MovingAverage:
156 : {
157 6 : printf("Algorithm name: \"%s\".\n", szAlgNameAverage);
158 6 : GDALGridMovingAverageOptions *pOptions2 =
159 : static_cast<GDALGridMovingAverageOptions *>(pOptions);
160 12 : CPLString osStr;
161 : osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
162 : ":nodata=%f",
163 : pOptions2->dfRadius1, pOptions2->dfRadius2,
164 : pOptions2->dfAngle, pOptions2->nMinPoints,
165 6 : pOptions2->dfNoDataValue);
166 6 : if (pOptions2->nMinPointsPerQuadrant > 0)
167 : osStr += CPLSPrintf(":min_points_per_quadrant=%u",
168 0 : pOptions2->nMinPointsPerQuadrant);
169 6 : if (pOptions2->nMaxPointsPerQuadrant > 0)
170 : osStr += CPLSPrintf(":max_points_per_quadrant=%u",
171 0 : pOptions2->nMaxPointsPerQuadrant);
172 6 : if (pOptions2->nMaxPoints > 0)
173 0 : osStr += CPLSPrintf(":max_points=%u", pOptions2->nMaxPoints);
174 6 : printf("Options are: \"%s\n", osStr.c_str()); /* ok */
175 6 : break;
176 : }
177 12 : case GGA_NearestNeighbor:
178 : {
179 12 : printf("Algorithm name: \"%s\".\n", szAlgNameNearest);
180 12 : GDALGridNearestNeighborOptions *pOptions2 =
181 : static_cast<GDALGridNearestNeighborOptions *>(pOptions);
182 12 : CPLprintf("Options are "
183 : "\"radius1=%f:radius2=%f:angle=%f:nodata=%f\"\n",
184 : pOptions2->dfRadius1, pOptions2->dfRadius2,
185 : pOptions2->dfAngle, pOptions2->dfNoDataValue);
186 12 : break;
187 : }
188 26 : case GGA_MetricMinimum:
189 : case GGA_MetricMaximum:
190 : case GGA_MetricRange:
191 : case GGA_MetricCount:
192 : case GGA_MetricAverageDistance:
193 : case GGA_MetricAverageDistancePts:
194 : {
195 26 : const char *pszAlgName = "";
196 26 : CPL_IGNORE_RET_VAL(pszAlgName); // Make CSA happy
197 26 : switch (eAlgorithm)
198 : {
199 6 : case GGA_MetricMinimum:
200 6 : pszAlgName = szAlgNameMinimum;
201 6 : break;
202 6 : case GGA_MetricMaximum:
203 6 : pszAlgName = szAlgNameMaximum;
204 6 : break;
205 3 : case GGA_MetricRange:
206 3 : pszAlgName = szAlgNameRange;
207 3 : break;
208 5 : case GGA_MetricCount:
209 5 : pszAlgName = szAlgNameCount;
210 5 : break;
211 3 : case GGA_MetricAverageDistance:
212 3 : pszAlgName = szAlgNameAverageDistance;
213 3 : break;
214 3 : case GGA_MetricAverageDistancePts:
215 3 : pszAlgName = szAlgNameAverageDistancePts;
216 3 : break;
217 0 : default:
218 0 : CPLAssert(false);
219 : break;
220 : }
221 26 : printf("Algorithm name: \"%s\".\n", pszAlgName);
222 26 : GDALGridDataMetricsOptions *pOptions2 =
223 : static_cast<GDALGridDataMetricsOptions *>(pOptions);
224 52 : CPLString osStr;
225 : osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
226 : ":nodata=%f",
227 : pOptions2->dfRadius1, pOptions2->dfRadius2,
228 : pOptions2->dfAngle, pOptions2->nMinPoints,
229 26 : pOptions2->dfNoDataValue);
230 26 : if (pOptions2->nMinPointsPerQuadrant > 0)
231 : osStr += CPLSPrintf(":min_points_per_quadrant=%u",
232 0 : pOptions2->nMinPointsPerQuadrant);
233 26 : if (pOptions2->nMaxPointsPerQuadrant > 0)
234 : osStr += CPLSPrintf(":max_points_per_quadrant=%u",
235 0 : pOptions2->nMaxPointsPerQuadrant);
236 26 : printf("Options are: \"%s\n", osStr.c_str()); /* ok */
237 26 : break;
238 : }
239 1 : case GGA_Linear:
240 : {
241 1 : printf("Algorithm name: \"%s\".\n", szAlgNameLinear);
242 1 : GDALGridLinearOptions *pOptions2 =
243 : static_cast<GDALGridLinearOptions *>(pOptions);
244 1 : CPLprintf("Options are "
245 : "\"radius=%f:nodata=%f\"\n",
246 : pOptions2->dfRadius, pOptions2->dfNoDataValue);
247 1 : break;
248 : }
249 0 : default:
250 : {
251 0 : printf("Algorithm is unknown.\n");
252 0 : break;
253 : }
254 : }
255 54 : }
256 :
257 : /************************************************************************/
258 : /* Extract point coordinates from the geometry reference and set the */
259 : /* Z value as requested. Test whether we are in the clipped region */
260 : /* before processing. */
261 : /************************************************************************/
262 :
263 : class GDALGridGeometryVisitor final : public OGRDefaultConstGeometryVisitor
264 : {
265 : public:
266 : const OGRGeometry *poClipSrc = nullptr;
267 : int iBurnField = 0;
268 : double dfBurnValue = 0;
269 : double dfIncreaseBurnValue = 0;
270 : double dfMultiplyBurnValue = 1;
271 : std::vector<double> adfX{};
272 : std::vector<double> adfY{};
273 : std::vector<double> adfZ{};
274 :
275 : using OGRDefaultConstGeometryVisitor::visit;
276 :
277 65295 : void visit(const OGRPoint *p) override
278 : {
279 65295 : if (poClipSrc && !p->Within(poClipSrc))
280 20 : return;
281 :
282 65275 : if (iBurnField < 0 && std::isnan(p->getZ()))
283 1 : return;
284 :
285 65274 : adfX.push_back(p->getX());
286 65274 : adfY.push_back(p->getY());
287 65274 : if (iBurnField < 0)
288 130530 : adfZ.push_back((p->getZ() + dfIncreaseBurnValue) *
289 65265 : dfMultiplyBurnValue);
290 : else
291 9 : adfZ.push_back((dfBurnValue + dfIncreaseBurnValue) *
292 9 : dfMultiplyBurnValue);
293 : }
294 : };
295 :
296 : /************************************************************************/
297 : /* ProcessLayer() */
298 : /* */
299 : /* Process all the features in a layer selection, collecting */
300 : /* geometries and burn values. */
301 : /************************************************************************/
302 :
303 177 : static CPLErr ProcessLayer(OGRLayer *poSrcLayer, GDALDataset *poDstDS,
304 : const OGRGeometry *poClipSrc, int nXSize, int nYSize,
305 : int nBand, bool &bIsXExtentSet, bool &bIsYExtentSet,
306 : double &dfXMin, double &dfXMax, double &dfYMin,
307 : double &dfYMax, const std::string &osBurnAttribute,
308 : const double dfIncreaseBurnValue,
309 : const double dfMultiplyBurnValue, GDALDataType eType,
310 : GDALGridAlgorithm eAlgorithm, void *pOptions,
311 : bool bQuiet, GDALProgressFunc pfnProgress,
312 : void *pProgressData)
313 :
314 : {
315 : /* -------------------------------------------------------------------- */
316 : /* Get field index, and check. */
317 : /* -------------------------------------------------------------------- */
318 177 : int iBurnField = -1;
319 :
320 177 : if (!osBurnAttribute.empty())
321 : {
322 : iBurnField =
323 2 : poSrcLayer->GetLayerDefn()->GetFieldIndex(osBurnAttribute.c_str());
324 2 : if (iBurnField == -1)
325 : {
326 0 : printf("Failed to find field %s on layer %s, skipping.\n",
327 0 : osBurnAttribute.c_str(), poSrcLayer->GetName());
328 0 : return CE_Failure;
329 : }
330 : }
331 :
332 : /* -------------------------------------------------------------------- */
333 : /* Collect the geometries from this layer, and build list of */
334 : /* values to be interpolated. */
335 : /* -------------------------------------------------------------------- */
336 354 : GDALGridGeometryVisitor oVisitor;
337 177 : oVisitor.poClipSrc = poClipSrc;
338 177 : oVisitor.iBurnField = iBurnField;
339 177 : oVisitor.dfIncreaseBurnValue = dfIncreaseBurnValue;
340 177 : oVisitor.dfMultiplyBurnValue = dfMultiplyBurnValue;
341 :
342 65323 : for (auto &&poFeat : poSrcLayer)
343 : {
344 65146 : const OGRGeometry *poGeom = poFeat->GetGeometryRef();
345 65146 : if (poGeom)
346 : {
347 65146 : if (iBurnField >= 0)
348 : {
349 10 : if (!poFeat->IsFieldSetAndNotNull(iBurnField))
350 : {
351 1 : continue;
352 : }
353 9 : oVisitor.dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
354 : }
355 :
356 65145 : poGeom->accept(&oVisitor);
357 : }
358 : }
359 :
360 177 : if (oVisitor.adfX.empty())
361 : {
362 0 : printf("No point geometry found on layer %s, skipping.\n",
363 0 : poSrcLayer->GetName());
364 0 : return CE_None;
365 : }
366 :
367 : /* -------------------------------------------------------------------- */
368 : /* Compute grid geometry. */
369 : /* -------------------------------------------------------------------- */
370 177 : if (!bIsXExtentSet || !bIsYExtentSet)
371 : {
372 68 : OGREnvelope sEnvelope;
373 68 : if (poSrcLayer->GetExtent(&sEnvelope, TRUE) == OGRERR_FAILURE)
374 : {
375 0 : return CE_Failure;
376 : }
377 :
378 68 : if (!bIsXExtentSet)
379 : {
380 68 : dfXMin = sEnvelope.MinX;
381 68 : dfXMax = sEnvelope.MaxX;
382 68 : bIsXExtentSet = true;
383 : }
384 :
385 68 : if (!bIsYExtentSet)
386 : {
387 68 : dfYMin = sEnvelope.MinY;
388 68 : dfYMax = sEnvelope.MaxY;
389 68 : bIsYExtentSet = true;
390 : }
391 : }
392 :
393 : // Produce north-up images
394 177 : if (dfYMin < dfYMax)
395 125 : std::swap(dfYMin, dfYMax);
396 :
397 : /* -------------------------------------------------------------------- */
398 : /* Perform gridding. */
399 : /* -------------------------------------------------------------------- */
400 :
401 177 : const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
402 177 : const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
403 :
404 177 : if (!bQuiet)
405 : {
406 54 : printf("Grid data type is \"%s\"\n", GDALGetDataTypeName(eType));
407 54 : printf("Grid size = (%d %d).\n", nXSize, nYSize);
408 54 : CPLprintf("Corner coordinates = (%f %f)-(%f %f).\n", dfXMin, dfYMin,
409 : dfXMax, dfYMax);
410 54 : CPLprintf("Grid cell size = (%f %f).\n", dfDeltaX, dfDeltaY);
411 54 : printf("Source point count = %lu.\n",
412 54 : static_cast<unsigned long>(oVisitor.adfX.size()));
413 54 : PrintAlgorithmAndOptions(eAlgorithm, pOptions);
414 54 : printf("\n");
415 : }
416 :
417 177 : GDALRasterBand *poBand = poDstDS->GetRasterBand(nBand);
418 :
419 177 : int nBlockXSize = 0;
420 177 : int nBlockYSize = 0;
421 177 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
422 :
423 : // Try to grow the work buffer up to 16 MB if it is smaller
424 177 : poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
425 177 : if (nXSize == 0 || nYSize == 0 || nBlockXSize == 0 || nBlockYSize == 0)
426 0 : return CE_Failure;
427 :
428 177 : const int nDesiredBufferSize = 16 * 1024 * 1024;
429 177 : if (nBlockXSize < nXSize && nBlockYSize < nYSize &&
430 0 : nBlockXSize < nDesiredBufferSize / (nBlockYSize * nDataTypeSize))
431 : {
432 0 : const int nNewBlockXSize =
433 0 : nDesiredBufferSize / (nBlockYSize * nDataTypeSize);
434 0 : nBlockXSize = (nNewBlockXSize / nBlockXSize) * nBlockXSize;
435 0 : if (nBlockXSize > nXSize)
436 0 : nBlockXSize = nXSize;
437 : }
438 177 : else if (nBlockXSize == nXSize && nBlockYSize < nYSize &&
439 81 : nBlockYSize < nDesiredBufferSize / (nXSize * nDataTypeSize))
440 : {
441 81 : const int nNewBlockYSize =
442 81 : nDesiredBufferSize / (nXSize * nDataTypeSize);
443 81 : nBlockYSize = (nNewBlockYSize / nBlockYSize) * nBlockYSize;
444 81 : if (nBlockYSize > nYSize)
445 81 : nBlockYSize = nYSize;
446 : }
447 177 : CPLDebug("GDAL_GRID", "Work buffer: %d * %d", nBlockXSize, nBlockYSize);
448 :
449 : std::unique_ptr<void, VSIFreeReleaser> pData(
450 354 : VSIMalloc3(nBlockXSize, nBlockYSize, nDataTypeSize));
451 177 : if (!pData)
452 : {
453 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate work buffer");
454 0 : return CE_Failure;
455 : }
456 :
457 177 : GIntBig nBlock = 0;
458 177 : const double dfBlockCount =
459 177 : static_cast<double>(DIV_ROUND_UP(nXSize, nBlockXSize)) *
460 177 : DIV_ROUND_UP(nYSize, nBlockYSize);
461 :
462 : struct GDALGridContextReleaser
463 : {
464 177 : void operator()(GDALGridContext *psContext)
465 : {
466 177 : GDALGridContextFree(psContext);
467 177 : }
468 : };
469 :
470 : std::unique_ptr<GDALGridContext, GDALGridContextReleaser> psContext(
471 : GDALGridContextCreate(eAlgorithm, pOptions,
472 177 : static_cast<int>(oVisitor.adfX.size()),
473 177 : &(oVisitor.adfX[0]), &(oVisitor.adfY[0]),
474 531 : &(oVisitor.adfZ[0]), TRUE));
475 177 : if (!psContext)
476 : {
477 0 : return CE_Failure;
478 : }
479 :
480 177 : CPLErr eErr = CE_None;
481 354 : for (int nYOffset = 0; nYOffset < nYSize && eErr == CE_None;
482 177 : nYOffset += nBlockYSize)
483 : {
484 354 : for (int nXOffset = 0; nXOffset < nXSize && eErr == CE_None;
485 177 : nXOffset += nBlockXSize)
486 : {
487 : std::unique_ptr<void, GDALScaledProgressReleaser> pScaledProgress(
488 : GDALCreateScaledProgress(
489 177 : static_cast<double>(nBlock) / dfBlockCount,
490 177 : static_cast<double>(nBlock + 1) / dfBlockCount, pfnProgress,
491 354 : pProgressData));
492 177 : nBlock++;
493 :
494 177 : int nXRequest = nBlockXSize;
495 177 : if (nXOffset > nXSize - nXRequest)
496 2 : nXRequest = nXSize - nXOffset;
497 :
498 177 : int nYRequest = nBlockYSize;
499 177 : if (nYOffset > nYSize - nYRequest)
500 2 : nYRequest = nYSize - nYOffset;
501 :
502 354 : eErr = GDALGridContextProcess(
503 177 : psContext.get(), dfXMin + dfDeltaX * nXOffset,
504 177 : dfXMin + dfDeltaX * (nXOffset + nXRequest),
505 177 : dfYMin + dfDeltaY * nYOffset,
506 177 : dfYMin + dfDeltaY * (nYOffset + nYRequest), nXRequest,
507 : nYRequest, eType, pData.get(), GDALScaledProgress,
508 : pScaledProgress.get());
509 :
510 177 : if (eErr == CE_None)
511 177 : eErr = poBand->RasterIO(GF_Write, nXOffset, nYOffset, nXRequest,
512 : nYRequest, pData.get(), nXRequest,
513 : nYRequest, eType, 0, 0, nullptr);
514 : }
515 : }
516 177 : if (eErr == CE_None && pfnProgress)
517 104 : pfnProgress(1.0, "", pProgressData);
518 :
519 177 : return eErr;
520 : }
521 :
522 : /************************************************************************/
523 : /* LoadGeometry() */
524 : /* */
525 : /* Read geometries from the given dataset using specified filters and */
526 : /* returns a collection of read geometries. */
527 : /************************************************************************/
528 :
529 1 : static std::unique_ptr<OGRGeometry> LoadGeometry(const std::string &osDS,
530 : const std::string &osSQL,
531 : const std::string &osLyr,
532 : const std::string &osWhere)
533 : {
534 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
535 2 : osDS.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
536 1 : if (!poDS)
537 0 : return nullptr;
538 :
539 1 : OGRLayer *poLyr = nullptr;
540 1 : if (!osSQL.empty())
541 0 : poLyr = poDS->ExecuteSQL(osSQL.c_str(), nullptr, nullptr);
542 1 : else if (!osLyr.empty())
543 0 : poLyr = poDS->GetLayerByName(osLyr.c_str());
544 : else
545 1 : poLyr = poDS->GetLayer(0);
546 :
547 1 : if (poLyr == nullptr)
548 : {
549 0 : CPLError(CE_Failure, CPLE_AppDefined,
550 : "Failed to identify source layer from datasource.");
551 0 : return nullptr;
552 : }
553 :
554 1 : if (!osWhere.empty())
555 0 : poLyr->SetAttributeFilter(osWhere.c_str());
556 :
557 1 : std::unique_ptr<OGRGeometryCollection> poGeom;
558 2 : for (auto &poFeat : poLyr)
559 : {
560 1 : const OGRGeometry *poSrcGeom = poFeat->GetGeometryRef();
561 1 : if (poSrcGeom)
562 : {
563 : const OGRwkbGeometryType eType =
564 1 : wkbFlatten(poSrcGeom->getGeometryType());
565 :
566 1 : if (!poGeom)
567 1 : poGeom = std::make_unique<OGRMultiPolygon>();
568 :
569 1 : if (eType == wkbPolygon)
570 : {
571 1 : poGeom->addGeometry(poSrcGeom);
572 : }
573 0 : else if (eType == wkbMultiPolygon)
574 : {
575 : const int nGeomCount =
576 0 : poSrcGeom->toMultiPolygon()->getNumGeometries();
577 :
578 0 : for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
579 : {
580 0 : poGeom->addGeometry(
581 0 : poSrcGeom->toMultiPolygon()->getGeometryRef(iGeom));
582 : }
583 : }
584 : else
585 : {
586 0 : CPLError(CE_Failure, CPLE_AppDefined,
587 : "Geometry not of polygon type.");
588 0 : if (!osSQL.empty())
589 0 : poDS->ReleaseResultSet(poLyr);
590 0 : return nullptr;
591 : }
592 : }
593 : }
594 :
595 1 : if (!osSQL.empty())
596 0 : poDS->ReleaseResultSet(poLyr);
597 :
598 1 : return poGeom;
599 : }
600 :
601 : /************************************************************************/
602 : /* GDALGrid() */
603 : /************************************************************************/
604 :
605 : /* clang-format off */
606 : /**
607 : * Create raster from the scattered data.
608 : *
609 : * This is the equivalent of the
610 : * <a href="/programs/gdal_grid.html">gdal_grid</a> utility.
611 : *
612 : * GDALGridOptions* must be allocated and freed with GDALGridOptionsNew()
613 : * and GDALGridOptionsFree() respectively.
614 : *
615 : * @param pszDest the destination dataset path.
616 : * @param hSrcDataset the source dataset handle.
617 : * @param psOptionsIn the options struct returned by GDALGridOptionsNew() or
618 : * NULL.
619 : * @param pbUsageError pointer to a integer output variable to store if any
620 : * usage error has occurred or NULL.
621 : * @return the output dataset (new dataset that must be closed using
622 : * GDALClose()) or NULL in case of error.
623 : *
624 : * @since GDAL 2.1
625 : */
626 : /* clang-format on */
627 :
628 182 : GDALDatasetH GDALGrid(const char *pszDest, GDALDatasetH hSrcDataset,
629 : const GDALGridOptions *psOptionsIn, int *pbUsageError)
630 :
631 : {
632 182 : if (hSrcDataset == nullptr)
633 : {
634 0 : CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
635 :
636 0 : if (pbUsageError)
637 0 : *pbUsageError = TRUE;
638 0 : return nullptr;
639 : }
640 182 : if (pszDest == nullptr)
641 : {
642 0 : CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
643 :
644 0 : if (pbUsageError)
645 0 : *pbUsageError = TRUE;
646 0 : return nullptr;
647 : }
648 :
649 182 : std::unique_ptr<GDALGridOptions> psOptionsToFree;
650 182 : const GDALGridOptions *psOptions = psOptionsIn;
651 182 : if (psOptions == nullptr)
652 : {
653 0 : psOptionsToFree = std::make_unique<GDALGridOptions>();
654 0 : psOptions = psOptionsToFree.get();
655 : }
656 :
657 182 : GDALDataset *poSrcDS = GDALDataset::FromHandle(hSrcDataset);
658 :
659 300 : if (psOptions->osSQL.empty() && psOptions->aosLayers.empty() &&
660 118 : poSrcDS->GetLayerCount() != 1)
661 : {
662 0 : CPLError(CE_Failure, CPLE_NotSupported,
663 : "Neither -sql nor -l are specified, but the source dataset "
664 : "has not one single layer.");
665 0 : if (pbUsageError)
666 0 : *pbUsageError = TRUE;
667 0 : return nullptr;
668 : }
669 :
670 182 : if ((psOptions->nXSize != 0 || psOptions->nYSize != 0) &&
671 106 : (psOptions->dfXRes != 0 || psOptions->dfYRes != 0))
672 : {
673 0 : CPLError(CE_Failure, CPLE_IllegalArg,
674 : "-outsize and -tr options cannot be used at the same time.");
675 0 : return nullptr;
676 : }
677 :
678 : /* -------------------------------------------------------------------- */
679 : /* Find the output driver. */
680 : /* -------------------------------------------------------------------- */
681 364 : std::string osFormat;
682 182 : if (psOptions->osFormat.empty())
683 : {
684 58 : osFormat = GetOutputDriverForRaster(pszDest);
685 58 : if (osFormat.empty())
686 : {
687 0 : return nullptr;
688 : }
689 : }
690 : else
691 : {
692 124 : osFormat = psOptions->osFormat;
693 : }
694 :
695 182 : GDALDriverH hDriver = GDALGetDriverByName(osFormat.c_str());
696 182 : if (hDriver == nullptr)
697 : {
698 0 : CPLError(CE_Failure, CPLE_AppDefined,
699 : "Output driver `%s' not recognised.", osFormat.c_str());
700 0 : fprintf(stderr, "The following format drivers are enabled and "
701 : "support writing:\n");
702 0 : for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
703 : {
704 0 : hDriver = GDALGetDriver(iDr);
705 :
706 0 : if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) !=
707 0 : nullptr &&
708 0 : (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) !=
709 0 : nullptr ||
710 0 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
711 : nullptr))
712 : {
713 0 : fprintf(stderr, " %s: %s\n", GDALGetDriverShortName(hDriver),
714 : GDALGetDriverLongName(hDriver));
715 : }
716 : }
717 0 : printf("\n");
718 0 : return nullptr;
719 : }
720 :
721 : /* -------------------------------------------------------------------- */
722 : /* Create target raster file. */
723 : /* -------------------------------------------------------------------- */
724 182 : int nLayerCount = psOptions->aosLayers.size();
725 182 : if (nLayerCount == 0 && psOptions->osSQL.empty())
726 118 : nLayerCount = 1; /* due to above check */
727 :
728 182 : int nBands = nLayerCount;
729 :
730 182 : if (!psOptions->osSQL.empty())
731 6 : nBands++;
732 :
733 : int nXSize;
734 : int nYSize;
735 182 : if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0)
736 : {
737 2 : double dfXSize = (std::fabs(psOptions->dfXMax - psOptions->dfXMin) +
738 2 : (psOptions->dfXRes / 2.0)) /
739 2 : psOptions->dfXRes;
740 2 : double dfYSize = (std::fabs(psOptions->dfYMax - psOptions->dfYMin) +
741 2 : (psOptions->dfYRes / 2.0)) /
742 2 : psOptions->dfYRes;
743 :
744 2 : if (dfXSize >= 1 && dfXSize <= INT_MAX && dfYSize >= 1 &&
745 : dfYSize <= INT_MAX)
746 : {
747 2 : nXSize = static_cast<int>(dfXSize);
748 2 : nYSize = static_cast<int>(dfYSize);
749 : }
750 : else
751 : {
752 0 : CPLError(
753 : CE_Failure, CPLE_IllegalArg,
754 : "Invalid output size detected. Please check your -tr argument");
755 :
756 0 : if (pbUsageError)
757 0 : *pbUsageError = TRUE;
758 0 : return nullptr;
759 2 : }
760 : }
761 : else
762 : {
763 : // FIXME
764 180 : nXSize = psOptions->nXSize;
765 180 : if (nXSize == 0)
766 74 : nXSize = 256;
767 180 : nYSize = psOptions->nYSize;
768 180 : if (nYSize == 0)
769 74 : nYSize = 256;
770 : }
771 :
772 : std::unique_ptr<GDALDataset> poDstDS(GDALDataset::FromHandle(GDALCreate(
773 182 : hDriver, pszDest, nXSize, nYSize, nBands, psOptions->eOutputType,
774 364 : psOptions->aosCreateOptions.List())));
775 182 : if (!poDstDS)
776 : {
777 0 : return nullptr;
778 : }
779 :
780 182 : if (psOptions->bNoDataSet)
781 : {
782 256 : for (int i = 1; i <= nBands; i++)
783 : {
784 128 : poDstDS->GetRasterBand(i)->SetNoDataValue(psOptions->dfNoDataValue);
785 : }
786 : }
787 :
788 182 : double dfXMin = psOptions->dfXMin;
789 182 : double dfYMin = psOptions->dfYMin;
790 182 : double dfXMax = psOptions->dfXMax;
791 182 : double dfYMax = psOptions->dfYMax;
792 182 : bool bIsXExtentSet = psOptions->bIsXExtentSet;
793 182 : bool bIsYExtentSet = psOptions->bIsYExtentSet;
794 182 : CPLErr eErr = CE_None;
795 :
796 : /* -------------------------------------------------------------------- */
797 : /* Process SQL request. */
798 : /* -------------------------------------------------------------------- */
799 :
800 182 : if (!psOptions->osSQL.empty())
801 : {
802 : OGRLayer *poLayer =
803 6 : poSrcDS->ExecuteSQL(psOptions->osSQL.c_str(),
804 6 : psOptions->poSpatialFilter.get(), nullptr);
805 6 : if (poLayer == nullptr)
806 : {
807 2 : return nullptr;
808 : }
809 :
810 : // Custom layer will be rasterized in the first band.
811 4 : eErr = ProcessLayer(
812 4 : poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
813 : nYSize, 1, bIsXExtentSet, bIsYExtentSet, dfXMin, dfXMax, dfYMin,
814 4 : dfYMax, psOptions->osBurnAttribute, psOptions->dfIncreaseBurnValue,
815 4 : psOptions->dfMultiplyBurnValue, psOptions->eOutputType,
816 4 : psOptions->eAlgorithm, psOptions->pOptions.get(), psOptions->bQuiet,
817 4 : psOptions->pfnProgress, psOptions->pProgressData);
818 :
819 4 : poSrcDS->ReleaseResultSet(poLayer);
820 : }
821 :
822 : /* -------------------------------------------------------------------- */
823 : /* Process each layer. */
824 : /* -------------------------------------------------------------------- */
825 360 : std::string osOutputSRS(psOptions->osOutputSRS);
826 353 : for (int i = 0; i < nLayerCount; i++)
827 : {
828 176 : auto poLayer = psOptions->aosLayers.empty()
829 176 : ? poSrcDS->GetLayer(0)
830 58 : : poSrcDS->GetLayerByName(psOptions->aosLayers[i]);
831 176 : if (!poLayer)
832 : {
833 2 : CPLError(CE_Failure, CPLE_AppDefined,
834 : "Unable to find layer \"%s\".",
835 2 : !psOptions->aosLayers.empty() && psOptions->aosLayers[i]
836 2 : ? psOptions->aosLayers[i]
837 : : "null");
838 2 : eErr = CE_Failure;
839 2 : break;
840 : }
841 :
842 174 : if (!psOptions->osWHERE.empty())
843 : {
844 2 : if (poLayer->SetAttributeFilter(psOptions->osWHERE.c_str()) !=
845 : OGRERR_NONE)
846 : {
847 1 : eErr = CE_Failure;
848 1 : break;
849 : }
850 : }
851 :
852 173 : if (psOptions->poSpatialFilter)
853 4 : poLayer->SetSpatialFilter(psOptions->poSpatialFilter.get());
854 :
855 : // Fetch the first meaningful SRS definition
856 173 : if (osOutputSRS.empty())
857 : {
858 172 : auto poSRS = poLayer->GetSpatialRef();
859 172 : if (poSRS)
860 91 : osOutputSRS = poSRS->exportToWkt();
861 : }
862 :
863 173 : eErr = ProcessLayer(
864 173 : poLayer, poDstDS.get(), psOptions->poSpatialFilter.get(), nXSize,
865 173 : nYSize, i + 1 + nBands - nLayerCount, bIsXExtentSet, bIsYExtentSet,
866 173 : dfXMin, dfXMax, dfYMin, dfYMax, psOptions->osBurnAttribute,
867 173 : psOptions->dfIncreaseBurnValue, psOptions->dfMultiplyBurnValue,
868 173 : psOptions->eOutputType, psOptions->eAlgorithm,
869 173 : psOptions->pOptions.get(), psOptions->bQuiet,
870 173 : psOptions->pfnProgress, psOptions->pProgressData);
871 173 : if (eErr != CE_None)
872 0 : break;
873 : }
874 :
875 : /* -------------------------------------------------------------------- */
876 : /* Apply geotransformation matrix. */
877 : /* -------------------------------------------------------------------- */
878 180 : double adfGeoTransform[6] = {dfXMin, (dfXMax - dfXMin) / nXSize,
879 : 0.0, dfYMin,
880 180 : 0.0, (dfYMax - dfYMin) / nYSize};
881 180 : poDstDS->SetGeoTransform(adfGeoTransform);
882 :
883 : /* -------------------------------------------------------------------- */
884 : /* Apply SRS definition if set. */
885 : /* -------------------------------------------------------------------- */
886 180 : if (!osOutputSRS.empty())
887 : {
888 92 : poDstDS->SetProjection(osOutputSRS.c_str());
889 : }
890 :
891 : /* -------------------------------------------------------------------- */
892 : /* End */
893 : /* -------------------------------------------------------------------- */
894 :
895 180 : if (eErr != CE_None)
896 : {
897 3 : return nullptr;
898 : }
899 :
900 177 : return GDALDataset::ToHandle(poDstDS.release());
901 : }
902 :
903 : /************************************************************************/
904 : /* GDALGridOptionsGetParser() */
905 : /************************************************************************/
906 :
907 : /*! @cond Doxygen_Suppress */
908 :
909 : static std::unique_ptr<GDALArgumentParser>
910 182 : GDALGridOptionsGetParser(GDALGridOptions *psOptions,
911 : GDALGridOptionsForBinary *psOptionsForBinary,
912 : int nCountClipSrc)
913 : {
914 : auto argParser = std::make_unique<GDALArgumentParser>(
915 182 : "gdal_grid", /* bForBinary=*/psOptionsForBinary != nullptr);
916 :
917 182 : argParser->add_description(
918 : _("Creates a regular grid (raster) from the scattered data read from a "
919 182 : "vector datasource."));
920 :
921 182 : argParser->add_epilog(_(
922 : "Available algorithms and parameters with their defaults:\n"
923 : " Inverse distance to a power (default)\n"
924 : " "
925 : "invdist:power=2.0:smoothing=0.0:radius1=0.0:radius2=0.0:angle=0.0:max_"
926 : "points=0:min_points=0:nodata=0.0\n"
927 : " Inverse distance to a power with nearest neighbor search\n"
928 : " "
929 : "invdistnn:power=2.0:radius=1.0:max_points=12:min_points=0:nodata=0\n"
930 : " Moving average\n"
931 : " "
932 : "average:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
933 : " Nearest neighbor\n"
934 : " nearest:radius1=0.0:radius2=0.0:angle=0.0:nodata=0.0\n"
935 : " Various data metrics\n"
936 : " <metric "
937 : "name>:radius1=0.0:radius2=0.0:angle=0.0:min_points=0:nodata=0.0\n"
938 : " possible metrics are:\n"
939 : " minimum\n"
940 : " maximum\n"
941 : " range\n"
942 : " count\n"
943 : " average_distance\n"
944 : " average_distance_pts\n"
945 : " Linear\n"
946 : " linear:radius=-1.0:nodata=0.0\n"
947 : "\n"
948 182 : "For more details, consult https://gdal.org/programs/gdal_grid.html"));
949 :
950 : argParser->add_quiet_argument(
951 182 : psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
952 :
953 182 : argParser->add_output_format_argument(psOptions->osFormat);
954 :
955 182 : argParser->add_output_type_argument(psOptions->eOutputType);
956 :
957 182 : argParser->add_argument("-txe")
958 364 : .metavar("<xmin> <xmax>")
959 182 : .nargs(2)
960 182 : .scan<'g', double>()
961 182 : .help(_("Set georeferenced X extents of output file to be created."));
962 :
963 182 : argParser->add_argument("-tye")
964 364 : .metavar("<ymin> <ymax>")
965 182 : .nargs(2)
966 182 : .scan<'g', double>()
967 182 : .help(_("Set georeferenced Y extents of output file to be created."));
968 :
969 182 : argParser->add_argument("-outsize")
970 364 : .metavar("<xsize> <ysize>")
971 182 : .nargs(2)
972 182 : .scan<'i', int>()
973 182 : .help(_("Set the size of the output file."));
974 :
975 182 : argParser->add_argument("-tr")
976 364 : .metavar("<xres> <yes>")
977 182 : .nargs(2)
978 182 : .scan<'g', double>()
979 182 : .help(_("Set target resolution."));
980 :
981 182 : argParser->add_creation_options_argument(psOptions->aosCreateOptions);
982 :
983 182 : argParser->add_argument("-zfield")
984 364 : .metavar("<field_name>")
985 182 : .store_into(psOptions->osBurnAttribute)
986 182 : .help(_("Field name from which to get Z values."));
987 :
988 182 : argParser->add_argument("-z_increase")
989 364 : .metavar("<increase_value>")
990 182 : .store_into(psOptions->dfIncreaseBurnValue)
991 : .help(_("Addition to the attribute field on the features to be used to "
992 182 : "get a Z value from."));
993 :
994 182 : argParser->add_argument("-z_multiply")
995 364 : .metavar("<multiply_value>")
996 182 : .store_into(psOptions->dfMultiplyBurnValue)
997 182 : .help(_("Multiplication ratio for the Z field.."));
998 :
999 182 : argParser->add_argument("-where")
1000 364 : .metavar("<expression>")
1001 182 : .store_into(psOptions->osWHERE)
1002 : .help(_("Query expression to be applied to select features to process "
1003 182 : "from the input layer(s)."));
1004 :
1005 182 : argParser->add_argument("-l")
1006 364 : .metavar("<layer_name>")
1007 182 : .append()
1008 58 : .action([psOptions](const std::string &s)
1009 240 : { psOptions->aosLayers.AddString(s.c_str()); })
1010 : .help(_("Layer(s) from the datasource that will be used for input "
1011 182 : "features."));
1012 :
1013 182 : argParser->add_argument("-sql")
1014 364 : .metavar("<select_statement>")
1015 182 : .store_into(psOptions->osSQL)
1016 : .help(_("SQL statement to be evaluated to produce a layer of features "
1017 182 : "to be processed."));
1018 :
1019 182 : argParser->add_argument("-spat")
1020 364 : .metavar("<xmin> <ymin> <xmax> <ymax>")
1021 182 : .nargs(4)
1022 182 : .scan<'g', double>()
1023 : .help(_("The area of interest. Only features within the rectangle will "
1024 182 : "be reported."));
1025 :
1026 182 : argParser->add_argument("-clipsrc")
1027 182 : .nargs(nCountClipSrc)
1028 364 : .metavar("[<xmin> <ymin> <xmax> <ymax>]|<WKT>|<datasource>|spat_extent")
1029 182 : .help(_("Clip geometries (in source SRS)."));
1030 :
1031 182 : argParser->add_argument("-clipsrcsql")
1032 364 : .metavar("<sql_statement>")
1033 182 : .store_into(psOptions->osClipSrcSQL)
1034 : .help(_("Select desired geometries from the source clip datasource "
1035 182 : "using an SQL query."));
1036 :
1037 182 : argParser->add_argument("-clipsrclayer")
1038 364 : .metavar("<layername>")
1039 182 : .store_into(psOptions->osClipSrcLayer)
1040 182 : .help(_("Select the named layer from the source clip datasource."));
1041 :
1042 182 : argParser->add_argument("-clipsrcwhere")
1043 364 : .metavar("<expression>")
1044 182 : .store_into(psOptions->osClipSrcWhere)
1045 : .help(_("Restrict desired geometries from the source clip layer based "
1046 182 : "on an attribute query."));
1047 :
1048 182 : argParser->add_argument("-a_srs")
1049 364 : .metavar("<srs_def>")
1050 : .action(
1051 2 : [psOptions](const std::string &osOutputSRSDef)
1052 : {
1053 2 : OGRSpatialReference oOutputSRS;
1054 :
1055 1 : if (oOutputSRS.SetFromUserInput(osOutputSRSDef.c_str()) !=
1056 : OGRERR_NONE)
1057 : {
1058 : throw std::invalid_argument(
1059 0 : std::string("Failed to process SRS definition: ")
1060 0 : .append(osOutputSRSDef));
1061 : }
1062 :
1063 1 : char *pszWKT = nullptr;
1064 1 : oOutputSRS.exportToWkt(&pszWKT);
1065 1 : if (pszWKT)
1066 1 : psOptions->osOutputSRS = pszWKT;
1067 1 : CPLFree(pszWKT);
1068 183 : })
1069 182 : .help(_("Assign an output SRS, but without reprojecting."));
1070 :
1071 182 : argParser->add_argument("-a")
1072 364 : .metavar("<algorithm>[[:<parameter1>=<value1>]...]")
1073 : .action(
1074 656 : [psOptions](const std::string &s)
1075 : {
1076 176 : const char *pszAlgorithm = s.c_str();
1077 176 : void *pOptions = nullptr;
1078 176 : if (GDALGridParseAlgorithmAndOptions(pszAlgorithm,
1079 : &psOptions->eAlgorithm,
1080 176 : &pOptions) != CE_None)
1081 : {
1082 : throw std::invalid_argument(
1083 0 : "Failed to process algorithm name and parameters");
1084 : }
1085 176 : psOptions->pOptions.reset(pOptions);
1086 :
1087 : const CPLStringList aosParams(
1088 352 : CSLTokenizeString2(pszAlgorithm, ":", FALSE));
1089 176 : const char *pszNoDataValue = aosParams.FetchNameValue("nodata");
1090 176 : if (pszNoDataValue != nullptr)
1091 : {
1092 128 : psOptions->bNoDataSet = true;
1093 128 : psOptions->dfNoDataValue = CPLAtofM(pszNoDataValue);
1094 : }
1095 358 : })
1096 : .help(_("Set the interpolation algorithm or data metric name and "
1097 182 : "(optionally) its parameters."));
1098 :
1099 182 : if (psOptionsForBinary)
1100 : {
1101 : argParser->add_open_options_argument(
1102 54 : &(psOptionsForBinary->aosOpenOptions));
1103 : }
1104 :
1105 182 : if (psOptionsForBinary)
1106 : {
1107 54 : argParser->add_argument("src_dataset_name")
1108 108 : .metavar("<src_dataset_name>")
1109 54 : .store_into(psOptionsForBinary->osSource)
1110 54 : .help(_("Input dataset."));
1111 :
1112 54 : argParser->add_argument("dst_dataset_name")
1113 108 : .metavar("<dst_dataset_name>")
1114 54 : .store_into(psOptionsForBinary->osDest)
1115 54 : .help(_("Output dataset."));
1116 : }
1117 :
1118 182 : return argParser;
1119 : }
1120 :
1121 : /*! @endcond */
1122 :
1123 : /************************************************************************/
1124 : /* GDALGridGetParserUsage() */
1125 : /************************************************************************/
1126 :
1127 0 : std::string GDALGridGetParserUsage()
1128 : {
1129 : try
1130 : {
1131 0 : GDALGridOptions sOptions;
1132 0 : GDALGridOptionsForBinary sOptionsForBinary;
1133 : auto argParser =
1134 0 : GDALGridOptionsGetParser(&sOptions, &sOptionsForBinary, 1);
1135 0 : return argParser->usage();
1136 : }
1137 0 : catch (const std::exception &err)
1138 : {
1139 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
1140 0 : err.what());
1141 0 : return std::string();
1142 : }
1143 : }
1144 :
1145 : /************************************************************************/
1146 : /* CHECK_HAS_ENOUGH_ADDITIONAL_ARGS() */
1147 : /************************************************************************/
1148 :
1149 : #ifndef CheckHasEnoughAdditionalArgs_defined
1150 : #define CheckHasEnoughAdditionalArgs_defined
1151 :
1152 3 : static bool CheckHasEnoughAdditionalArgs(CSLConstList papszArgv, int i,
1153 : int nExtraArg, int nArgc)
1154 : {
1155 3 : if (i + nExtraArg >= nArgc)
1156 : {
1157 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1158 0 : "%s option requires %d argument%s", papszArgv[i], nExtraArg,
1159 : nExtraArg == 1 ? "" : "s");
1160 0 : return false;
1161 : }
1162 3 : return true;
1163 : }
1164 : #endif
1165 :
1166 : #define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \
1167 : if (!CheckHasEnoughAdditionalArgs(papszArgv, i, nExtraArg, nArgc)) \
1168 : { \
1169 : return nullptr; \
1170 : }
1171 :
1172 : /************************************************************************/
1173 : /* GDALGridOptionsNew() */
1174 : /************************************************************************/
1175 :
1176 : /**
1177 : * Allocates a GDALGridOptions struct.
1178 : *
1179 : * @param papszArgv NULL terminated list of options (potentially including
1180 : * filename and open options too), or NULL. The accepted options are the ones of
1181 : * the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
1182 : * @param psOptionsForBinary (output) may be NULL (and should generally be
1183 : * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
1184 : * GDALGridOptionsForBinaryNew() prior to this
1185 : * function. Will be filled with potentially present filename, open options,...
1186 : * @return pointer to the allocated GDALGridOptions struct. Must be freed with
1187 : * GDALGridOptionsFree().
1188 : *
1189 : * @since GDAL 2.1
1190 : */
1191 :
1192 : GDALGridOptions *
1193 182 : GDALGridOptionsNew(char **papszArgv,
1194 : GDALGridOptionsForBinary *psOptionsForBinary)
1195 : {
1196 364 : auto psOptions = std::make_unique<GDALGridOptions>();
1197 :
1198 : /* -------------------------------------------------------------------- */
1199 : /* Pre-processing for custom syntax that ArgumentParser does not */
1200 : /* support. */
1201 : /* -------------------------------------------------------------------- */
1202 :
1203 364 : CPLStringList aosArgv;
1204 182 : const int nArgc = CSLCount(papszArgv);
1205 182 : int nCountClipSrc = 0;
1206 2480 : for (int i = 0;
1207 2480 : i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
1208 : {
1209 2298 : if (EQUAL(papszArgv[i], "-clipsrc"))
1210 : {
1211 3 : if (nCountClipSrc)
1212 : {
1213 0 : CPLError(CE_Failure, CPLE_AppDefined, "Duplicate argument %s",
1214 0 : papszArgv[i]);
1215 0 : return nullptr;
1216 : }
1217 : // argparse doesn't handle well variable number of values
1218 : // just before the positional arguments, so we have to detect
1219 : // it manually and set the correct number.
1220 3 : nCountClipSrc = 1;
1221 3 : CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1);
1222 5 : if (CPLGetValueType(papszArgv[i + 1]) != CPL_VALUE_STRING &&
1223 2 : i + 4 < nArgc)
1224 : {
1225 2 : nCountClipSrc = 4;
1226 : }
1227 :
1228 15 : for (int j = 0; j < 1 + nCountClipSrc; ++j)
1229 : {
1230 12 : aosArgv.AddString(papszArgv[i]);
1231 12 : ++i;
1232 : }
1233 3 : --i;
1234 : }
1235 :
1236 : else
1237 : {
1238 2295 : aosArgv.AddString(papszArgv[i]);
1239 : }
1240 : }
1241 :
1242 : try
1243 : {
1244 : auto argParser = GDALGridOptionsGetParser(
1245 364 : psOptions.get(), psOptionsForBinary, nCountClipSrc);
1246 :
1247 182 : argParser->parse_args_without_binary_name(aosArgv.List());
1248 :
1249 294 : if (auto oTXE = argParser->present<std::vector<double>>("-txe"))
1250 : {
1251 112 : psOptions->dfXMin = (*oTXE)[0];
1252 112 : psOptions->dfXMax = (*oTXE)[1];
1253 112 : psOptions->bIsXExtentSet = true;
1254 : }
1255 :
1256 294 : if (auto oTYE = argParser->present<std::vector<double>>("-tye"))
1257 : {
1258 112 : psOptions->dfYMin = (*oTYE)[0];
1259 112 : psOptions->dfYMax = (*oTYE)[1];
1260 112 : psOptions->bIsYExtentSet = true;
1261 : }
1262 :
1263 288 : if (auto oOutsize = argParser->present<std::vector<int>>("-outsize"))
1264 : {
1265 106 : psOptions->nXSize = (*oOutsize)[0];
1266 106 : psOptions->nYSize = (*oOutsize)[1];
1267 : }
1268 :
1269 182 : if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
1270 : {
1271 2 : psOptions->dfXRes = (*adfTargetRes)[0];
1272 2 : psOptions->dfYRes = (*adfTargetRes)[1];
1273 2 : if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
1274 : {
1275 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1276 : "Wrong value for -tr parameters.");
1277 0 : return nullptr;
1278 : }
1279 : }
1280 :
1281 183 : if (auto oSpat = argParser->present<std::vector<double>>("-spat"))
1282 : {
1283 2 : OGRLinearRing oRing;
1284 1 : const double dfMinX = (*oSpat)[0];
1285 1 : const double dfMinY = (*oSpat)[1];
1286 1 : const double dfMaxX = (*oSpat)[2];
1287 1 : const double dfMaxY = (*oSpat)[3];
1288 :
1289 1 : oRing.addPoint(dfMinX, dfMinY);
1290 1 : oRing.addPoint(dfMinX, dfMaxY);
1291 1 : oRing.addPoint(dfMaxX, dfMaxY);
1292 1 : oRing.addPoint(dfMaxX, dfMinY);
1293 1 : oRing.addPoint(dfMinX, dfMinY);
1294 :
1295 2 : auto poPolygon = std::make_unique<OGRPolygon>();
1296 1 : poPolygon->addRing(&oRing);
1297 1 : psOptions->poSpatialFilter = std::move(poPolygon);
1298 : }
1299 :
1300 182 : if (auto oClipSrc =
1301 182 : argParser->present<std::vector<std::string>>("-clipsrc"))
1302 : {
1303 3 : const std::string &osVal = (*oClipSrc)[0];
1304 :
1305 3 : psOptions->poClipSrc.reset();
1306 3 : psOptions->osClipSrcDS.clear();
1307 :
1308 : VSIStatBufL sStat;
1309 3 : psOptions->bClipSrc = true;
1310 3 : if (oClipSrc->size() == 4)
1311 : {
1312 2 : const double dfMinX = CPLAtofM((*oClipSrc)[0].c_str());
1313 2 : const double dfMinY = CPLAtofM((*oClipSrc)[1].c_str());
1314 2 : const double dfMaxX = CPLAtofM((*oClipSrc)[2].c_str());
1315 2 : const double dfMaxY = CPLAtofM((*oClipSrc)[3].c_str());
1316 :
1317 4 : OGRLinearRing oRing;
1318 :
1319 2 : oRing.addPoint(dfMinX, dfMinY);
1320 2 : oRing.addPoint(dfMinX, dfMaxY);
1321 2 : oRing.addPoint(dfMaxX, dfMaxY);
1322 2 : oRing.addPoint(dfMaxX, dfMinY);
1323 2 : oRing.addPoint(dfMinX, dfMinY);
1324 :
1325 4 : auto poPoly = std::make_unique<OGRPolygon>();
1326 2 : poPoly->addRing(&oRing);
1327 2 : psOptions->poClipSrc = std::move(poPoly);
1328 : }
1329 1 : else if ((STARTS_WITH_CI(osVal.c_str(), "POLYGON") ||
1330 1 : STARTS_WITH_CI(osVal.c_str(), "MULTIPOLYGON")) &&
1331 0 : VSIStatL(osVal.c_str(), &sStat) != 0)
1332 : {
1333 0 : psOptions->poClipSrc =
1334 0 : OGRGeometryFactory::createFromWkt(osVal.c_str(), nullptr)
1335 0 : .first;
1336 0 : if (psOptions->poClipSrc == nullptr)
1337 : {
1338 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1339 : "Invalid geometry. Must be a valid POLYGON or "
1340 : "MULTIPOLYGON WKT");
1341 0 : return nullptr;
1342 : }
1343 : }
1344 1 : else if (EQUAL(osVal.c_str(), "spat_extent"))
1345 : {
1346 : // Nothing to do
1347 : }
1348 : else
1349 : {
1350 1 : psOptions->osClipSrcDS = osVal;
1351 : }
1352 : }
1353 :
1354 182 : if (psOptions->bClipSrc && !psOptions->osClipSrcDS.empty())
1355 : {
1356 2 : psOptions->poClipSrc = LoadGeometry(
1357 1 : psOptions->osClipSrcDS, psOptions->osClipSrcSQL,
1358 2 : psOptions->osClipSrcLayer, psOptions->osClipSrcWhere);
1359 1 : if (!psOptions->poClipSrc)
1360 : {
1361 0 : CPLError(CE_Failure, CPLE_AppDefined,
1362 : "Cannot load source clip geometry.");
1363 0 : return nullptr;
1364 : }
1365 : }
1366 181 : else if (psOptions->bClipSrc && !psOptions->poClipSrc &&
1367 0 : !psOptions->poSpatialFilter)
1368 : {
1369 0 : CPLError(CE_Failure, CPLE_AppDefined,
1370 : "-clipsrc must be used with -spat option or \n"
1371 : "a bounding box, WKT string or datasource must be "
1372 : "specified.");
1373 0 : return nullptr;
1374 : }
1375 :
1376 182 : if (psOptions->poSpatialFilter)
1377 : {
1378 1 : if (psOptions->poClipSrc)
1379 : {
1380 : auto poTemp = std::unique_ptr<OGRGeometry>(
1381 0 : psOptions->poSpatialFilter->Intersection(
1382 0 : psOptions->poClipSrc.get()));
1383 0 : if (poTemp)
1384 : {
1385 0 : psOptions->poSpatialFilter = std::move(poTemp);
1386 : }
1387 :
1388 0 : psOptions->poClipSrc.reset();
1389 : }
1390 : }
1391 : else
1392 : {
1393 181 : if (psOptions->poClipSrc)
1394 : {
1395 3 : psOptions->poSpatialFilter = std::move(psOptions->poClipSrc);
1396 : }
1397 : }
1398 :
1399 184 : if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0 &&
1400 2 : !(psOptions->bIsXExtentSet && psOptions->bIsYExtentSet))
1401 : {
1402 0 : CPLError(CE_Failure, CPLE_IllegalArg,
1403 : "-txe ad -tye arguments must be provided when "
1404 : "resolution is provided.");
1405 0 : return nullptr;
1406 : }
1407 :
1408 182 : return psOptions.release();
1409 : }
1410 0 : catch (const std::exception &err)
1411 : {
1412 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
1413 0 : return nullptr;
1414 : }
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* GDALGridOptionsFree() */
1419 : /************************************************************************/
1420 :
1421 : /**
1422 : * Frees the GDALGridOptions struct.
1423 : *
1424 : * @param psOptions the options struct for GDALGrid().
1425 : *
1426 : * @since GDAL 2.1
1427 : */
1428 :
1429 182 : void GDALGridOptionsFree(GDALGridOptions *psOptions)
1430 : {
1431 182 : delete psOptions;
1432 182 : }
1433 :
1434 : /************************************************************************/
1435 : /* GDALGridOptionsSetProgress() */
1436 : /************************************************************************/
1437 :
1438 : /**
1439 : * Set a progress function.
1440 : *
1441 : * @param psOptions the options struct for GDALGrid().
1442 : * @param pfnProgress the progress callback.
1443 : * @param pProgressData the user data for the progress callback.
1444 : *
1445 : * @since GDAL 2.1
1446 : */
1447 :
1448 130 : void GDALGridOptionsSetProgress(GDALGridOptions *psOptions,
1449 : GDALProgressFunc pfnProgress,
1450 : void *pProgressData)
1451 : {
1452 130 : psOptions->pfnProgress = pfnProgress;
1453 130 : psOptions->pProgressData = pProgressData;
1454 130 : if (pfnProgress == GDALTermProgress)
1455 54 : psOptions->bQuiet = false;
1456 130 : }
1457 :
1458 : #undef CHECK_HAS_ENOUGH_ADDITIONAL_ARGS
|