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