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