mirror of https://gitee.com/openkylin/gdal.git
1443 lines
52 KiB
C++
1443 lines
52 KiB
C++
/* ****************************************************************************
|
|
*
|
|
* Project: GDAL Utilities
|
|
* Purpose: GDAL scattered data gridding (interpolation) tool
|
|
* Author: Andrey Kiselev, dron@ak4719.spb.edu
|
|
*
|
|
* ****************************************************************************
|
|
* Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
|
|
* Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "cpl_port.h"
|
|
#include "gdal_utils.h"
|
|
#include "gdal_utils_priv.h"
|
|
#include "commonutils.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_error.h"
|
|
#include "cpl_progress.h"
|
|
#include "cpl_string.h"
|
|
#include "cpl_vsi.h"
|
|
#include "gdal.h"
|
|
#include "gdal_alg.h"
|
|
#include "gdal_priv.h"
|
|
#include "gdalgrid.h"
|
|
#include "ogr_api.h"
|
|
#include "ogr_core.h"
|
|
#include "ogr_feature.h"
|
|
#include "ogr_geometry.h"
|
|
#include "ogr_spatialref.h"
|
|
#include "ogr_srs_api.h"
|
|
#include "ogrsf_frmts.h"
|
|
|
|
/************************************************************************/
|
|
/* GDALGridOptions */
|
|
/************************************************************************/
|
|
|
|
/** Options for use with GDALGrid(). GDALGridOptions* must be allocated
|
|
* and freed with GDALGridOptionsNew() and GDALGridOptionsFree() respectively.
|
|
*/
|
|
struct GDALGridOptions
|
|
{
|
|
/*! output format. Use the short format name. */
|
|
char *pszFormat;
|
|
|
|
/*! allow or suppress progress monitor and other non-error output */
|
|
bool bQuiet;
|
|
|
|
/*! the progress function to use */
|
|
GDALProgressFunc pfnProgress;
|
|
|
|
/*! pointer to the progress data variable */
|
|
void *pProgressData;
|
|
|
|
char **papszLayers;
|
|
char *pszBurnAttribute;
|
|
double dfIncreaseBurnValue;
|
|
double dfMultiplyBurnValue;
|
|
char *pszWHERE;
|
|
char *pszSQL;
|
|
GDALDataType eOutputType;
|
|
char **papszCreateOptions;
|
|
int nXSize;
|
|
int nYSize;
|
|
double dfXRes;
|
|
double dfYRes;
|
|
double dfXMin;
|
|
double dfXMax;
|
|
double dfYMin;
|
|
double dfYMax;
|
|
bool bIsXExtentSet;
|
|
bool bIsYExtentSet;
|
|
GDALGridAlgorithm eAlgorithm;
|
|
void *pOptions;
|
|
char *pszOutputSRS;
|
|
OGRGeometry *poSpatialFilter;
|
|
bool bClipSrc;
|
|
OGRGeometry *poClipSrc;
|
|
char *pszClipSrcDS;
|
|
char *pszClipSrcSQL;
|
|
char *pszClipSrcLayer;
|
|
char *pszClipSrcWhere;
|
|
bool bNoDataSet;
|
|
double dfNoDataValue;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GetAlgorithmName() */
|
|
/* */
|
|
/* Grids algorithm code into mnemonic name. */
|
|
/************************************************************************/
|
|
|
|
static void PrintAlgorithmAndOptions(GDALGridAlgorithm eAlgorithm,
|
|
void *pOptions)
|
|
{
|
|
switch (eAlgorithm)
|
|
{
|
|
case GGA_InverseDistanceToAPower:
|
|
{
|
|
printf("Algorithm name: \"%s\".\n", szAlgNameInvDist);
|
|
GDALGridInverseDistanceToAPowerOptions *pOptions2 =
|
|
static_cast<GDALGridInverseDistanceToAPowerOptions *>(pOptions);
|
|
CPLprintf("Options are "
|
|
"\"power=%f:smoothing=%f:radius1=%f:radius2=%f:angle=%f"
|
|
":max_points=%u:min_points=%u:nodata=%f\"\n",
|
|
pOptions2->dfPower, pOptions2->dfSmoothing,
|
|
pOptions2->dfRadius1, pOptions2->dfRadius2,
|
|
pOptions2->dfAngle, pOptions2->nMaxPoints,
|
|
pOptions2->nMinPoints, pOptions2->dfNoDataValue);
|
|
break;
|
|
}
|
|
case GGA_InverseDistanceToAPowerNearestNeighbor:
|
|
{
|
|
printf("Algorithm name: \"%s\".\n",
|
|
szAlgNameInvDistNearestNeighbor);
|
|
GDALGridInverseDistanceToAPowerNearestNeighborOptions *pOptions2 =
|
|
static_cast<
|
|
GDALGridInverseDistanceToAPowerNearestNeighborOptions *>(
|
|
pOptions);
|
|
CPLString osStr;
|
|
osStr.Printf("power=%f:smoothing=%f:radius=%f"
|
|
":max_points=%u:min_points=%u:nodata=%f",
|
|
pOptions2->dfPower, pOptions2->dfSmoothing,
|
|
pOptions2->dfRadius, pOptions2->nMaxPoints,
|
|
pOptions2->nMinPoints, pOptions2->dfNoDataValue);
|
|
if (pOptions2->nMinPointsPerQuadrant > 0)
|
|
osStr += CPLSPrintf(":min_points_per_quadrant=%u",
|
|
pOptions2->nMinPointsPerQuadrant);
|
|
if (pOptions2->nMaxPointsPerQuadrant > 0)
|
|
osStr += CPLSPrintf(":max_points_per_quadrant=%u",
|
|
pOptions2->nMaxPointsPerQuadrant);
|
|
printf("Options are: \"%s\n", osStr.c_str()); /* ok */
|
|
break;
|
|
}
|
|
case GGA_MovingAverage:
|
|
{
|
|
printf("Algorithm name: \"%s\".\n", szAlgNameAverage);
|
|
GDALGridMovingAverageOptions *pOptions2 =
|
|
static_cast<GDALGridMovingAverageOptions *>(pOptions);
|
|
CPLString osStr;
|
|
osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
|
|
":nodata=%f",
|
|
pOptions2->dfRadius1, pOptions2->dfRadius2,
|
|
pOptions2->dfAngle, pOptions2->nMinPoints,
|
|
pOptions2->dfNoDataValue);
|
|
if (pOptions2->nMinPointsPerQuadrant > 0)
|
|
osStr += CPLSPrintf(":min_points_per_quadrant=%u",
|
|
pOptions2->nMinPointsPerQuadrant);
|
|
if (pOptions2->nMaxPointsPerQuadrant > 0)
|
|
osStr += CPLSPrintf(":max_points_per_quadrant=%u",
|
|
pOptions2->nMaxPointsPerQuadrant);
|
|
if (pOptions2->nMaxPoints > 0)
|
|
osStr += CPLSPrintf(":max_points=%u", pOptions2->nMaxPoints);
|
|
printf("Options are: \"%s\n", osStr.c_str()); /* ok */
|
|
break;
|
|
}
|
|
case GGA_NearestNeighbor:
|
|
{
|
|
printf("Algorithm name: \"%s\".\n", szAlgNameNearest);
|
|
GDALGridNearestNeighborOptions *pOptions2 =
|
|
static_cast<GDALGridNearestNeighborOptions *>(pOptions);
|
|
CPLprintf("Options are "
|
|
"\"radius1=%f:radius2=%f:angle=%f:nodata=%f\"\n",
|
|
pOptions2->dfRadius1, pOptions2->dfRadius2,
|
|
pOptions2->dfAngle, pOptions2->dfNoDataValue);
|
|
break;
|
|
}
|
|
case GGA_MetricMinimum:
|
|
case GGA_MetricMaximum:
|
|
case GGA_MetricRange:
|
|
case GGA_MetricCount:
|
|
case GGA_MetricAverageDistance:
|
|
case GGA_MetricAverageDistancePts:
|
|
{
|
|
const char *pszAlgName = "";
|
|
switch (eAlgorithm)
|
|
{
|
|
case GGA_MetricMinimum:
|
|
pszAlgName = szAlgNameMinimum;
|
|
break;
|
|
case GGA_MetricMaximum:
|
|
pszAlgName = szAlgNameMaximum;
|
|
break;
|
|
case GGA_MetricRange:
|
|
pszAlgName = szAlgNameRange;
|
|
break;
|
|
case GGA_MetricCount:
|
|
pszAlgName = szAlgNameCount;
|
|
break;
|
|
case GGA_MetricAverageDistance:
|
|
pszAlgName = szAlgNameAverageDistance;
|
|
break;
|
|
case GGA_MetricAverageDistancePts:
|
|
pszAlgName = szAlgNameAverageDistancePts;
|
|
break;
|
|
default:
|
|
CPLAssert(false);
|
|
break;
|
|
}
|
|
printf("Algorithm name: \"%s\".\n", pszAlgName);
|
|
GDALGridDataMetricsOptions *pOptions2 =
|
|
static_cast<GDALGridDataMetricsOptions *>(pOptions);
|
|
CPLString osStr;
|
|
osStr.Printf("radius1=%f:radius2=%f:angle=%f:min_points=%u"
|
|
":nodata=%f",
|
|
pOptions2->dfRadius1, pOptions2->dfRadius2,
|
|
pOptions2->dfAngle, pOptions2->nMinPoints,
|
|
pOptions2->dfNoDataValue);
|
|
if (pOptions2->nMinPointsPerQuadrant > 0)
|
|
osStr += CPLSPrintf(":min_points_per_quadrant=%u",
|
|
pOptions2->nMinPointsPerQuadrant);
|
|
if (pOptions2->nMaxPointsPerQuadrant > 0)
|
|
osStr += CPLSPrintf(":max_points_per_quadrant=%u",
|
|
pOptions2->nMaxPointsPerQuadrant);
|
|
printf("Options are: \"%s\n", osStr.c_str()); /* ok */
|
|
break;
|
|
}
|
|
case GGA_Linear:
|
|
{
|
|
printf("Algorithm name: \"%s\".\n", szAlgNameLinear);
|
|
GDALGridLinearOptions *pOptions2 =
|
|
static_cast<GDALGridLinearOptions *>(pOptions);
|
|
CPLprintf("Options are "
|
|
"\"radius=%f:nodata=%f\"\n",
|
|
pOptions2->dfRadius, pOptions2->dfNoDataValue);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
printf("Algorithm is unknown.\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Extract point coordinates from the geometry reference and set the */
|
|
/* Z value as requested. Test whether we are in the clipped region */
|
|
/* before processing. */
|
|
/************************************************************************/
|
|
|
|
class GDALGridGeometryVisitor final : public OGRDefaultConstGeometryVisitor
|
|
{
|
|
public:
|
|
const OGRGeometry *poClipSrc = nullptr;
|
|
int iBurnField = 0;
|
|
double dfBurnValue = 0;
|
|
double dfIncreaseBurnValue = 0;
|
|
double dfMultiplyBurnValue = 1;
|
|
std::vector<double> adfX{};
|
|
std::vector<double> adfY{};
|
|
std::vector<double> adfZ{};
|
|
|
|
using OGRDefaultConstGeometryVisitor::visit;
|
|
void visit(const OGRPoint *p) override
|
|
{
|
|
if (poClipSrc && !p->Within(poClipSrc))
|
|
return;
|
|
|
|
if (iBurnField < 0 && std::isnan(p->getZ()))
|
|
return;
|
|
|
|
adfX.push_back(p->getX());
|
|
adfY.push_back(p->getY());
|
|
if (iBurnField < 0)
|
|
adfZ.push_back((p->getZ() + dfIncreaseBurnValue) *
|
|
dfMultiplyBurnValue);
|
|
else
|
|
adfZ.push_back((dfBurnValue + dfIncreaseBurnValue) *
|
|
dfMultiplyBurnValue);
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ProcessLayer() */
|
|
/* */
|
|
/* Process all the features in a layer selection, collecting */
|
|
/* geometries and burn values. */
|
|
/************************************************************************/
|
|
|
|
static CPLErr ProcessLayer(OGRLayerH hSrcLayer, GDALDatasetH hDstDS,
|
|
const OGRGeometry *poClipSrc, int nXSize, int nYSize,
|
|
int nBand, bool &bIsXExtentSet, bool &bIsYExtentSet,
|
|
double &dfXMin, double &dfXMax, double &dfYMin,
|
|
double &dfYMax, const char *pszBurnAttribute,
|
|
const double dfIncreaseBurnValue,
|
|
const double dfMultiplyBurnValue, GDALDataType eType,
|
|
GDALGridAlgorithm eAlgorithm, void *pOptions,
|
|
bool bQuiet, GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get field index, and check. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iBurnField = -1;
|
|
|
|
if (pszBurnAttribute)
|
|
{
|
|
iBurnField = OGR_FD_GetFieldIndex(OGR_L_GetLayerDefn(hSrcLayer),
|
|
pszBurnAttribute);
|
|
if (iBurnField == -1)
|
|
{
|
|
printf("Failed to find field %s on layer %s, skipping.\n",
|
|
pszBurnAttribute,
|
|
OGR_FD_GetName(OGR_L_GetLayerDefn(hSrcLayer)));
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Collect the geometries from this layer, and build list of */
|
|
/* values to be interpolated. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALGridGeometryVisitor oVisitor;
|
|
oVisitor.poClipSrc = poClipSrc;
|
|
oVisitor.iBurnField = iBurnField;
|
|
oVisitor.dfIncreaseBurnValue = dfIncreaseBurnValue;
|
|
oVisitor.dfMultiplyBurnValue = dfMultiplyBurnValue;
|
|
|
|
for (auto &&poFeat : OGRLayer::FromHandle(hSrcLayer))
|
|
{
|
|
const OGRGeometry *poGeom = poFeat->GetGeometryRef();
|
|
if (poGeom)
|
|
{
|
|
if (iBurnField >= 0)
|
|
{
|
|
if (!poFeat->IsFieldSetAndNotNull(iBurnField))
|
|
{
|
|
continue;
|
|
}
|
|
oVisitor.dfBurnValue = poFeat->GetFieldAsDouble(iBurnField);
|
|
}
|
|
|
|
poGeom->accept(&oVisitor);
|
|
}
|
|
}
|
|
|
|
if (oVisitor.adfX.empty())
|
|
{
|
|
printf("No point geometry found on layer %s, skipping.\n",
|
|
OGR_FD_GetName(OGR_L_GetLayerDefn(hSrcLayer)));
|
|
return CE_None;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute grid geometry. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (!bIsXExtentSet || !bIsYExtentSet)
|
|
{
|
|
OGREnvelope sEnvelope;
|
|
OGR_L_GetExtent(hSrcLayer, &sEnvelope, TRUE);
|
|
|
|
if (!bIsXExtentSet)
|
|
{
|
|
dfXMin = sEnvelope.MinX;
|
|
dfXMax = sEnvelope.MaxX;
|
|
bIsXExtentSet = true;
|
|
}
|
|
|
|
if (!bIsYExtentSet)
|
|
{
|
|
dfYMin = sEnvelope.MinY;
|
|
dfYMax = sEnvelope.MaxY;
|
|
bIsYExtentSet = true;
|
|
}
|
|
}
|
|
|
|
// Produce north-up images
|
|
if (dfYMin < dfYMax)
|
|
std::swap(dfYMin, dfYMax);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Perform gridding. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
const double dfDeltaX = (dfXMax - dfXMin) / nXSize;
|
|
const double dfDeltaY = (dfYMax - dfYMin) / nYSize;
|
|
|
|
if (!bQuiet)
|
|
{
|
|
printf("Grid data type is \"%s\"\n", GDALGetDataTypeName(eType));
|
|
printf("Grid size = (%d %d).\n", nXSize, nYSize);
|
|
CPLprintf("Corner coordinates = (%f %f)-(%f %f).\n", dfXMin, dfYMin,
|
|
dfXMax, dfYMax);
|
|
CPLprintf("Grid cell size = (%f %f).\n", dfDeltaX, dfDeltaY);
|
|
printf("Source point count = %lu.\n",
|
|
static_cast<unsigned long>(oVisitor.adfX.size()));
|
|
PrintAlgorithmAndOptions(eAlgorithm, pOptions);
|
|
printf("\n");
|
|
}
|
|
|
|
GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, nBand);
|
|
|
|
int nBlockXSize = 0;
|
|
int nBlockYSize = 0;
|
|
const int nDataTypeSize = GDALGetDataTypeSizeBytes(eType);
|
|
|
|
// Try to grow the work buffer up to 16 MB if it is smaller
|
|
GDALGetBlockSize(hBand, &nBlockXSize, &nBlockYSize);
|
|
if (nXSize == 0 || nYSize == 0 || nBlockXSize == 0 || nBlockYSize == 0)
|
|
return CE_Failure;
|
|
|
|
const int nDesiredBufferSize = 16 * 1024 * 1024;
|
|
if (nBlockXSize < nXSize && nBlockYSize < nYSize &&
|
|
nBlockXSize < nDesiredBufferSize / (nBlockYSize * nDataTypeSize))
|
|
{
|
|
const int nNewBlockXSize =
|
|
nDesiredBufferSize / (nBlockYSize * nDataTypeSize);
|
|
nBlockXSize = (nNewBlockXSize / nBlockXSize) * nBlockXSize;
|
|
if (nBlockXSize > nXSize)
|
|
nBlockXSize = nXSize;
|
|
}
|
|
else if (nBlockXSize == nXSize && nBlockYSize < nYSize &&
|
|
nBlockYSize < nDesiredBufferSize / (nXSize * nDataTypeSize))
|
|
{
|
|
const int nNewBlockYSize =
|
|
nDesiredBufferSize / (nXSize * nDataTypeSize);
|
|
nBlockYSize = (nNewBlockYSize / nBlockYSize) * nBlockYSize;
|
|
if (nBlockYSize > nYSize)
|
|
nBlockYSize = nYSize;
|
|
}
|
|
CPLDebug("GDAL_GRID", "Work buffer: %d * %d", nBlockXSize, nBlockYSize);
|
|
|
|
void *pData = VSIMalloc3(nBlockXSize, nBlockYSize, nDataTypeSize);
|
|
if (pData == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate work buffer");
|
|
return CE_Failure;
|
|
}
|
|
|
|
GIntBig nBlock = 0;
|
|
const double dfBlockCount =
|
|
static_cast<double>(DIV_ROUND_UP(nXSize, nBlockXSize)) *
|
|
DIV_ROUND_UP(nYSize, nBlockYSize);
|
|
|
|
GDALGridContext *psContext = GDALGridContextCreate(
|
|
eAlgorithm, pOptions, static_cast<int>(oVisitor.adfX.size()),
|
|
&(oVisitor.adfX[0]), &(oVisitor.adfY[0]), &(oVisitor.adfZ[0]), TRUE);
|
|
if (psContext == nullptr)
|
|
{
|
|
CPLFree(pData);
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLErr eErr = CE_None;
|
|
for (int nYOffset = 0; nYOffset < nYSize && eErr == CE_None;
|
|
nYOffset += nBlockYSize)
|
|
{
|
|
for (int nXOffset = 0; nXOffset < nXSize && eErr == CE_None;
|
|
nXOffset += nBlockXSize)
|
|
{
|
|
void *pScaledProgress = GDALCreateScaledProgress(
|
|
static_cast<double>(nBlock) / dfBlockCount,
|
|
static_cast<double>(nBlock + 1) / dfBlockCount, pfnProgress,
|
|
pProgressData);
|
|
nBlock++;
|
|
|
|
int nXRequest = nBlockXSize;
|
|
if (nXOffset > nXSize - nXRequest)
|
|
nXRequest = nXSize - nXOffset;
|
|
|
|
int nYRequest = nBlockYSize;
|
|
if (nYOffset > nYSize - nYRequest)
|
|
nYRequest = nYSize - nYOffset;
|
|
|
|
eErr = GDALGridContextProcess(
|
|
psContext, dfXMin + dfDeltaX * nXOffset,
|
|
dfXMin + dfDeltaX * (nXOffset + nXRequest),
|
|
dfYMin + dfDeltaY * nYOffset,
|
|
dfYMin + dfDeltaY * (nYOffset + nYRequest), nXRequest,
|
|
nYRequest, eType, pData, GDALScaledProgress, pScaledProgress);
|
|
|
|
if (eErr == CE_None)
|
|
eErr = GDALRasterIO(hBand, GF_Write, nXOffset, nYOffset,
|
|
nXRequest, nYRequest, pData, nXRequest,
|
|
nYRequest, eType, 0, 0);
|
|
|
|
GDALDestroyScaledProgress(pScaledProgress);
|
|
}
|
|
}
|
|
if (eErr == CE_None && pfnProgress)
|
|
pfnProgress(1.0, "", pProgressData);
|
|
|
|
GDALGridContextFree(psContext);
|
|
|
|
CPLFree(pData);
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* LoadGeometry() */
|
|
/* */
|
|
/* Read geometries from the given dataset using specified filters and */
|
|
/* returns a collection of read geometries. */
|
|
/************************************************************************/
|
|
|
|
static OGRGeometryCollection *LoadGeometry(const char *pszDS,
|
|
const char *pszSQL,
|
|
const char *pszLyr,
|
|
const char *pszWhere)
|
|
{
|
|
GDALDataset *poDS = static_cast<GDALDataset *>(
|
|
GDALOpenEx(pszDS, GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
|
|
if (poDS == nullptr)
|
|
return nullptr;
|
|
|
|
OGRLayer *poLyr = nullptr;
|
|
if (pszSQL != nullptr)
|
|
poLyr = poDS->ExecuteSQL(pszSQL, nullptr, nullptr);
|
|
else if (pszLyr != nullptr)
|
|
poLyr = poDS->GetLayerByName(pszLyr);
|
|
else
|
|
poLyr = poDS->GetLayer(0);
|
|
|
|
if (poLyr == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Failed to identify source layer from datasource.");
|
|
GDALClose(poDS);
|
|
return nullptr;
|
|
}
|
|
|
|
if (pszWhere)
|
|
poLyr->SetAttributeFilter(pszWhere);
|
|
|
|
OGRGeometryCollection *poGeom = nullptr;
|
|
for (auto &poFeat : poLyr)
|
|
{
|
|
OGRGeometry *poSrcGeom = poFeat->GetGeometryRef();
|
|
if (poSrcGeom)
|
|
{
|
|
const OGRwkbGeometryType eType =
|
|
wkbFlatten(poSrcGeom->getGeometryType());
|
|
|
|
if (poGeom == nullptr)
|
|
poGeom = new OGRMultiPolygon();
|
|
|
|
if (eType == wkbPolygon)
|
|
{
|
|
poGeom->addGeometry(poSrcGeom);
|
|
}
|
|
else if (eType == wkbMultiPolygon)
|
|
{
|
|
const int nGeomCount = static_cast<OGRMultiPolygon *>(poSrcGeom)
|
|
->getNumGeometries();
|
|
|
|
for (int iGeom = 0; iGeom < nGeomCount; iGeom++)
|
|
{
|
|
poGeom->addGeometry(
|
|
static_cast<OGRMultiPolygon *>(poSrcGeom)
|
|
->getGeometryRef(iGeom));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Geometry not of polygon type.");
|
|
OGRGeometryFactory::destroyGeometry(poGeom);
|
|
if (pszSQL != nullptr)
|
|
poDS->ReleaseResultSet(poLyr);
|
|
GDALClose(poDS);
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pszSQL != nullptr)
|
|
poDS->ReleaseResultSet(poLyr);
|
|
GDALClose(poDS);
|
|
|
|
return poGeom;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGrid() */
|
|
/************************************************************************/
|
|
|
|
/* clang-format off */
|
|
/**
|
|
* Create raster from the scattered data.
|
|
*
|
|
* This is the equivalent of the
|
|
* <a href="/programs/gdal_grid.html">gdal_grid</a> utility.
|
|
*
|
|
* GDALGridOptions* must be allocated and freed with GDALGridOptionsNew()
|
|
* and GDALGridOptionsFree() respectively.
|
|
*
|
|
* @param pszDest the destination dataset path.
|
|
* @param hSrcDataset the source dataset handle.
|
|
* @param psOptionsIn the options struct returned by GDALGridOptionsNew() or
|
|
* NULL.
|
|
* @param pbUsageError pointer to a integer output variable to store if any
|
|
* usage error has occurred or NULL.
|
|
* @return the output dataset (new dataset that must be closed using
|
|
* GDALClose()) or NULL in case of error.
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
/* clang-format on */
|
|
|
|
GDALDatasetH GDALGrid(const char *pszDest, GDALDatasetH hSrcDataset,
|
|
const GDALGridOptions *psOptionsIn, int *pbUsageError)
|
|
|
|
{
|
|
if (hSrcDataset == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
return nullptr;
|
|
}
|
|
if (pszDest == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
return nullptr;
|
|
}
|
|
|
|
GDALGridOptions *psOptionsToFree = nullptr;
|
|
const GDALGridOptions *psOptions = psOptionsIn;
|
|
if (psOptions == nullptr)
|
|
{
|
|
psOptionsToFree = GDALGridOptionsNew(nullptr, nullptr);
|
|
psOptions = psOptionsToFree;
|
|
}
|
|
|
|
GDALDataset *poSrcDS = static_cast<GDALDataset *>(hSrcDataset);
|
|
|
|
if (psOptions->pszSQL == nullptr && psOptions->papszLayers == nullptr &&
|
|
poSrcDS->GetLayerCount() != 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Neither -sql nor -l are specified, but the source dataset "
|
|
"has not one single layer.");
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
|
|
if ((psOptions->nXSize != 0 || psOptions->nYSize != 0) &&
|
|
(psOptions->dfXRes != 0 || psOptions->dfYRes != 0))
|
|
{
|
|
CPLError(CE_Failure, CPLE_IllegalArg,
|
|
"-outsize and -tr options cannot be used at the same time.");
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Find the output driver. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osFormat;
|
|
if (psOptions->pszFormat == nullptr)
|
|
{
|
|
osFormat = GetOutputDriverForRaster(pszDest);
|
|
if (osFormat.empty())
|
|
{
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
osFormat = psOptions->pszFormat;
|
|
}
|
|
|
|
GDALDriverH hDriver = GDALGetDriverByName(osFormat);
|
|
if (hDriver == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Output driver `%s' not recognised.", osFormat.c_str());
|
|
fprintf(stderr, "The following format drivers are configured and "
|
|
"support output:\n");
|
|
for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
|
|
{
|
|
hDriver = GDALGetDriver(iDr);
|
|
|
|
if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) !=
|
|
nullptr &&
|
|
(GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) !=
|
|
nullptr ||
|
|
GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
|
|
nullptr))
|
|
{
|
|
fprintf(stderr, " %s: %s\n", GDALGetDriverShortName(hDriver),
|
|
GDALGetDriverLongName(hDriver));
|
|
}
|
|
}
|
|
printf("\n");
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create target raster file. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nLayerCount = CSLCount(psOptions->papszLayers);
|
|
if (nLayerCount == 0 && psOptions->pszSQL == nullptr)
|
|
nLayerCount = 1; /* due to above check */
|
|
|
|
int nBands = nLayerCount;
|
|
|
|
if (psOptions->pszSQL)
|
|
nBands++;
|
|
|
|
int nXSize;
|
|
int nYSize;
|
|
if (psOptions->dfXRes != 0 && psOptions->dfYRes != 0)
|
|
{
|
|
if ((psOptions->dfXMax == psOptions->dfXMin) ||
|
|
(psOptions->dfYMax == psOptions->dfYMin))
|
|
{
|
|
CPLError(CE_Failure, CPLE_IllegalArg,
|
|
"Invalid txe or tye parameters detected. Please check "
|
|
"your -txe or -tye argument.");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
|
|
double dfXSize = (std::fabs(psOptions->dfXMax - psOptions->dfXMin) +
|
|
(psOptions->dfXRes / 2.0)) /
|
|
psOptions->dfXRes;
|
|
double dfYSize = (std::fabs(psOptions->dfYMax - psOptions->dfYMin) +
|
|
(psOptions->dfYRes / 2.0)) /
|
|
psOptions->dfYRes;
|
|
|
|
if (dfXSize >= 1 && dfXSize <= INT_MAX && dfYSize >= 1 &&
|
|
dfYSize <= INT_MAX)
|
|
{
|
|
nXSize = static_cast<int>(dfXSize);
|
|
nYSize = static_cast<int>(dfYSize);
|
|
}
|
|
else
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_IllegalArg,
|
|
"Invalid output size detected. Please check your -tr argument");
|
|
|
|
if (pbUsageError)
|
|
*pbUsageError = TRUE;
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// FIXME
|
|
nXSize = psOptions->nXSize;
|
|
if (nXSize == 0)
|
|
nXSize = 256;
|
|
nYSize = psOptions->nYSize;
|
|
if (nYSize == 0)
|
|
nYSize = 256;
|
|
}
|
|
|
|
GDALDatasetH hDstDS =
|
|
GDALCreate(hDriver, pszDest, nXSize, nYSize, nBands,
|
|
psOptions->eOutputType, psOptions->papszCreateOptions);
|
|
if (hDstDS == nullptr)
|
|
{
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
return nullptr;
|
|
}
|
|
|
|
if (psOptions->bNoDataSet)
|
|
{
|
|
for (int i = 1; i <= nBands; i++)
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand(hDstDS, i);
|
|
GDALSetRasterNoDataValue(hBand, psOptions->dfNoDataValue);
|
|
}
|
|
}
|
|
|
|
double dfXMin = psOptions->dfXMin;
|
|
double dfYMin = psOptions->dfYMin;
|
|
double dfXMax = psOptions->dfXMax;
|
|
double dfYMax = psOptions->dfYMax;
|
|
bool bIsXExtentSet = psOptions->bIsXExtentSet;
|
|
bool bIsYExtentSet = psOptions->bIsYExtentSet;
|
|
CPLErr eErr = CE_None;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process SQL request. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
if (psOptions->pszSQL != nullptr)
|
|
{
|
|
OGRLayer *poLayer = poSrcDS->ExecuteSQL(
|
|
psOptions->pszSQL, psOptions->poSpatialFilter, nullptr);
|
|
if (poLayer != nullptr)
|
|
{
|
|
// Custom layer will be rasterized in the first band.
|
|
eErr = ProcessLayer(
|
|
OGRLayer::ToHandle(poLayer), hDstDS, psOptions->poSpatialFilter,
|
|
nXSize, nYSize, 1, bIsXExtentSet, bIsYExtentSet, dfXMin, dfXMax,
|
|
dfYMin, dfYMax, psOptions->pszBurnAttribute,
|
|
psOptions->dfIncreaseBurnValue, psOptions->dfMultiplyBurnValue,
|
|
psOptions->eOutputType, psOptions->eAlgorithm,
|
|
psOptions->pOptions, psOptions->bQuiet, psOptions->pfnProgress,
|
|
psOptions->pProgressData);
|
|
|
|
poSrcDS->ReleaseResultSet(poLayer);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Process each layer. */
|
|
/* -------------------------------------------------------------------- */
|
|
char *pszOutputSRS =
|
|
psOptions->pszOutputSRS ? CPLStrdup(psOptions->pszOutputSRS) : nullptr;
|
|
for (int i = 0; i < nLayerCount; i++)
|
|
{
|
|
OGRLayerH hLayer = psOptions->papszLayers == nullptr
|
|
? GDALDatasetGetLayer(hSrcDataset, 0)
|
|
: GDALDatasetGetLayerByName(
|
|
hSrcDataset, psOptions->papszLayers[i]);
|
|
if (hLayer == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Unable to find layer \"%s\", skipping.",
|
|
psOptions->papszLayers && psOptions->papszLayers[i]
|
|
? psOptions->papszLayers[i]
|
|
: "null");
|
|
continue;
|
|
}
|
|
|
|
if (psOptions->pszWHERE)
|
|
{
|
|
if (OGR_L_SetAttributeFilter(hLayer, psOptions->pszWHERE) !=
|
|
OGRERR_NONE)
|
|
break;
|
|
}
|
|
|
|
if (psOptions->poSpatialFilter != nullptr)
|
|
OGR_L_SetSpatialFilter(
|
|
hLayer, OGRGeometry::ToHandle(psOptions->poSpatialFilter));
|
|
|
|
// Fetch the first meaningful SRS definition
|
|
if (!pszOutputSRS)
|
|
{
|
|
OGRSpatialReferenceH hSRS = OGR_L_GetSpatialRef(hLayer);
|
|
if (hSRS)
|
|
OSRExportToWkt(hSRS, &pszOutputSRS);
|
|
}
|
|
|
|
eErr = ProcessLayer(
|
|
hLayer, hDstDS, psOptions->poSpatialFilter, nXSize, nYSize,
|
|
i + 1 + nBands - nLayerCount, bIsXExtentSet, bIsYExtentSet, dfXMin,
|
|
dfXMax, dfYMin, dfYMax, psOptions->pszBurnAttribute,
|
|
psOptions->dfIncreaseBurnValue, psOptions->dfMultiplyBurnValue,
|
|
psOptions->eOutputType, psOptions->eAlgorithm, psOptions->pOptions,
|
|
psOptions->bQuiet, psOptions->pfnProgress,
|
|
psOptions->pProgressData);
|
|
if (eErr != CE_None)
|
|
break;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply geotransformation matrix. */
|
|
/* -------------------------------------------------------------------- */
|
|
double adfGeoTransform[6] = {dfXMin, (dfXMax - dfXMin) / nXSize,
|
|
0.0, dfYMin,
|
|
0.0, (dfYMax - dfYMin) / nYSize};
|
|
GDALSetGeoTransform(hDstDS, adfGeoTransform);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply SRS definition if set. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (pszOutputSRS)
|
|
{
|
|
GDALSetProjection(hDstDS, pszOutputSRS);
|
|
CPLFree(pszOutputSRS);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* End */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALGridOptionsFree(psOptionsToFree);
|
|
|
|
if (eErr != CE_None)
|
|
{
|
|
GDALClose(hDstDS);
|
|
return nullptr;
|
|
}
|
|
|
|
return hDstDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IsNumber() */
|
|
/************************************************************************/
|
|
|
|
static bool IsNumber(const char *pszStr)
|
|
{
|
|
if (*pszStr == '-' || *pszStr == '+')
|
|
pszStr++;
|
|
if (*pszStr == '.')
|
|
pszStr++;
|
|
return *pszStr >= '0' && *pszStr <= '9';
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridOptionsNew() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Allocates a GDALGridOptions struct.
|
|
*
|
|
* @param papszArgv NULL terminated list of options (potentially including
|
|
* filename and open options too), or NULL. The accepted options are the ones of
|
|
* the <a href="/programs/gdal_translate.html">gdal_translate</a> utility.
|
|
* @param psOptionsForBinary (output) may be NULL (and should generally be
|
|
* NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
|
|
* GDALGridOptionsForBinaryNew() prior to this
|
|
* function. Will be filled with potentially present filename, open options,...
|
|
* @return pointer to the allocated GDALGridOptions struct. Must be freed with
|
|
* GDALGridOptionsFree().
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
|
|
GDALGridOptions *
|
|
GDALGridOptionsNew(char **papszArgv,
|
|
GDALGridOptionsForBinary *psOptionsForBinary)
|
|
{
|
|
GDALGridOptions *psOptions =
|
|
static_cast<GDALGridOptions *>(CPLCalloc(1, sizeof(GDALGridOptions)));
|
|
|
|
psOptions->pszFormat = nullptr;
|
|
psOptions->bQuiet = true;
|
|
psOptions->pfnProgress = GDALDummyProgress;
|
|
psOptions->pProgressData = nullptr;
|
|
psOptions->papszLayers = nullptr;
|
|
psOptions->pszBurnAttribute = nullptr;
|
|
psOptions->dfIncreaseBurnValue = 0.0;
|
|
psOptions->dfMultiplyBurnValue = 1.0;
|
|
psOptions->pszWHERE = nullptr;
|
|
psOptions->pszSQL = nullptr;
|
|
psOptions->eOutputType = GDT_Float64;
|
|
psOptions->papszCreateOptions = nullptr;
|
|
psOptions->nXSize = 0;
|
|
psOptions->nYSize = 0;
|
|
psOptions->dfXRes = 0;
|
|
psOptions->dfYRes = 0;
|
|
psOptions->dfXMin = 0.0;
|
|
psOptions->dfXMax = 0.0;
|
|
psOptions->dfYMin = 0.0;
|
|
psOptions->dfYMax = 0.0;
|
|
psOptions->bIsXExtentSet = false;
|
|
psOptions->bIsYExtentSet = false;
|
|
psOptions->eAlgorithm = GGA_InverseDistanceToAPower;
|
|
psOptions->pOptions = nullptr;
|
|
psOptions->pszOutputSRS = nullptr;
|
|
psOptions->poSpatialFilter = nullptr;
|
|
psOptions->poClipSrc = nullptr;
|
|
psOptions->bClipSrc = false;
|
|
psOptions->pszClipSrcDS = nullptr;
|
|
psOptions->pszClipSrcSQL = nullptr;
|
|
psOptions->pszClipSrcLayer = nullptr;
|
|
psOptions->pszClipSrcWhere = nullptr;
|
|
psOptions->bNoDataSet = false;
|
|
psOptions->dfNoDataValue = 0;
|
|
|
|
GDALGridParseAlgorithmAndOptions(szAlgNameInvDist, &psOptions->eAlgorithm,
|
|
&psOptions->pOptions);
|
|
|
|
bool bGotSourceFilename = false;
|
|
bool bGotDestFilename = false;
|
|
/* -------------------------------------------------------------------- */
|
|
/* Handle command line arguments. */
|
|
/* -------------------------------------------------------------------- */
|
|
const int argc = CSLCount(papszArgv);
|
|
for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
|
|
i++)
|
|
{
|
|
if (i < argc - 1 &&
|
|
(EQUAL(papszArgv[i], "-of") || EQUAL(papszArgv[i], "-f")))
|
|
{
|
|
++i;
|
|
CPLFree(psOptions->pszFormat);
|
|
psOptions->pszFormat = CPLStrdup(papszArgv[i]);
|
|
}
|
|
|
|
else if (EQUAL(papszArgv[i], "-q") || EQUAL(papszArgv[i], "-quiet"))
|
|
{
|
|
if (psOptionsForBinary)
|
|
{
|
|
psOptionsForBinary->bQuiet = true;
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"%s switch only supported from gdal_grid binary.",
|
|
papszArgv[i]);
|
|
}
|
|
}
|
|
|
|
else if (EQUAL(papszArgv[i], "-ot") && papszArgv[i + 1])
|
|
{
|
|
for (int iType = 1; iType < GDT_TypeCount; iType++)
|
|
{
|
|
if (GDALGetDataTypeName(static_cast<GDALDataType>(iType)) !=
|
|
nullptr &&
|
|
EQUAL(GDALGetDataTypeName(static_cast<GDALDataType>(iType)),
|
|
papszArgv[i + 1]))
|
|
{
|
|
psOptions->eOutputType = static_cast<GDALDataType>(iType);
|
|
}
|
|
}
|
|
|
|
if (psOptions->eOutputType == GDT_Unknown)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Unknown output pixel type: %s.", papszArgv[i + 1]);
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if (i + 2 < argc && EQUAL(papszArgv[i], "-txe"))
|
|
{
|
|
psOptions->dfXMin = CPLAtof(papszArgv[++i]);
|
|
psOptions->dfXMax = CPLAtof(papszArgv[++i]);
|
|
psOptions->bIsXExtentSet = true;
|
|
}
|
|
|
|
else if (i + 2 < argc && EQUAL(papszArgv[i], "-tye"))
|
|
{
|
|
psOptions->dfYMin = CPLAtof(papszArgv[++i]);
|
|
psOptions->dfYMax = CPLAtof(papszArgv[++i]);
|
|
psOptions->bIsYExtentSet = true;
|
|
}
|
|
|
|
else if (i + 2 < argc && EQUAL(papszArgv[i], "-outsize"))
|
|
{
|
|
CPLAssert(papszArgv[i + 1]);
|
|
CPLAssert(papszArgv[i + 2]);
|
|
psOptions->nXSize = atoi(papszArgv[i + 1]);
|
|
psOptions->nYSize = atoi(papszArgv[i + 2]);
|
|
i += 2;
|
|
}
|
|
|
|
else if (i + 2 < argc && EQUAL(papszArgv[i], "-tr"))
|
|
{
|
|
psOptions->dfXRes = CPLAtofM(papszArgv[++i]);
|
|
psOptions->dfYRes = CPLAtofM(papszArgv[++i]);
|
|
if (psOptions->dfXRes <= 0 || psOptions->dfYRes <= 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_IllegalArg,
|
|
"Wrong value for -tr parameters.");
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-co"))
|
|
{
|
|
psOptions->papszCreateOptions =
|
|
CSLAddString(psOptions->papszCreateOptions, papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-zfield"))
|
|
{
|
|
CPLFree(psOptions->pszBurnAttribute);
|
|
psOptions->pszBurnAttribute = CPLStrdup(papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-z_increase"))
|
|
{
|
|
psOptions->dfIncreaseBurnValue = CPLAtof(papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-z_multiply"))
|
|
{
|
|
psOptions->dfMultiplyBurnValue = CPLAtof(papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-where"))
|
|
{
|
|
CPLFree(psOptions->pszWHERE);
|
|
psOptions->pszWHERE = CPLStrdup(papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-l"))
|
|
{
|
|
psOptions->papszLayers =
|
|
CSLAddString(psOptions->papszLayers, papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-sql"))
|
|
{
|
|
CPLFree(psOptions->pszSQL);
|
|
psOptions->pszSQL = CPLStrdup(papszArgv[++i]);
|
|
}
|
|
|
|
else if (i + 4 < argc && EQUAL(papszArgv[i], "-spat"))
|
|
{
|
|
OGRLinearRing oRing;
|
|
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 1]),
|
|
CPLAtof(papszArgv[i + 2]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 1]),
|
|
CPLAtof(papszArgv[i + 4]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 3]),
|
|
CPLAtof(papszArgv[i + 4]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 3]),
|
|
CPLAtof(papszArgv[i + 2]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 1]),
|
|
CPLAtof(papszArgv[i + 2]));
|
|
|
|
delete psOptions->poSpatialFilter;
|
|
OGRPolygon *poPoly = new OGRPolygon();
|
|
poPoly->addRing(&oRing);
|
|
psOptions->poSpatialFilter = poPoly;
|
|
i += 4;
|
|
}
|
|
|
|
else if (EQUAL(papszArgv[i], "-clipsrc"))
|
|
{
|
|
if (i + 1 >= argc || papszArgv[i + 1] == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_IllegalArg,
|
|
"%s option requires 1 or 4 arguments", papszArgv[i]);
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
|
|
VSIStatBufL sStat;
|
|
psOptions->bClipSrc = true;
|
|
if (IsNumber(papszArgv[i + 1]) && papszArgv[i + 2] != nullptr &&
|
|
papszArgv[i + 3] != nullptr && papszArgv[i + 4] != nullptr)
|
|
{
|
|
OGRLinearRing oRing;
|
|
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 1]),
|
|
CPLAtof(papszArgv[i + 2]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 1]),
|
|
CPLAtof(papszArgv[i + 4]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 3]),
|
|
CPLAtof(papszArgv[i + 4]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 3]),
|
|
CPLAtof(papszArgv[i + 2]));
|
|
oRing.addPoint(CPLAtof(papszArgv[i + 1]),
|
|
CPLAtof(papszArgv[i + 2]));
|
|
|
|
delete psOptions->poClipSrc;
|
|
OGRPolygon *poPoly = static_cast<OGRPolygon *>(
|
|
OGRGeometryFactory::createGeometry(wkbPolygon));
|
|
poPoly->addRing(&oRing);
|
|
psOptions->poClipSrc = poPoly;
|
|
i += 4;
|
|
}
|
|
else if ((STARTS_WITH_CI(papszArgv[i + 1], "POLYGON") ||
|
|
STARTS_WITH_CI(papszArgv[i + 1], "MULTIPOLYGON")) &&
|
|
VSIStatL(papszArgv[i + 1], &sStat) != 0)
|
|
{
|
|
delete psOptions->poClipSrc;
|
|
OGRGeometryFactory::createFromWkt(papszArgv[i + 1], nullptr,
|
|
&psOptions->poClipSrc);
|
|
if (psOptions->poClipSrc == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_IllegalArg,
|
|
"Invalid geometry. Must be a valid POLYGON or "
|
|
"MULTIPOLYGON WKT");
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
i++;
|
|
}
|
|
else if (EQUAL(papszArgv[i + 1], "spat_extent"))
|
|
{
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
CPLFree(psOptions->pszClipSrcDS);
|
|
psOptions->pszClipSrcDS = CPLStrdup(papszArgv[i + 1]);
|
|
i++;
|
|
}
|
|
}
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-clipsrcsql"))
|
|
{
|
|
CPLFree(psOptions->pszClipSrcSQL);
|
|
psOptions->pszClipSrcSQL = CPLStrdup(papszArgv[i + 1]);
|
|
i++;
|
|
}
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-clipsrclayer"))
|
|
{
|
|
CPLFree(psOptions->pszClipSrcLayer);
|
|
psOptions->pszClipSrcLayer = CPLStrdup(papszArgv[i + 1]);
|
|
i++;
|
|
}
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-clipsrcwhere"))
|
|
{
|
|
CPLFree(psOptions->pszClipSrcWhere);
|
|
psOptions->pszClipSrcWhere = CPLStrdup(papszArgv[i + 1]);
|
|
i++;
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-a_srs"))
|
|
{
|
|
OGRSpatialReference oOutputSRS;
|
|
|
|
if (oOutputSRS.SetFromUserInput(papszArgv[i + 1]) != OGRERR_NONE)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Failed to process SRS definition: %s",
|
|
papszArgv[i + 1]);
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
|
|
CPLFree(psOptions->pszOutputSRS);
|
|
oOutputSRS.exportToWkt(&(psOptions->pszOutputSRS));
|
|
i++;
|
|
}
|
|
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-a"))
|
|
{
|
|
const char *pszAlgorithm = papszArgv[++i];
|
|
CPLFree(psOptions->pOptions);
|
|
if (GDALGridParseAlgorithmAndOptions(
|
|
pszAlgorithm, &psOptions->eAlgorithm,
|
|
&psOptions->pOptions) != CE_None)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Failed to process algorithm name and parameters");
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
|
|
char **papszParams = CSLTokenizeString2(pszAlgorithm, ":", FALSE);
|
|
const char *pszNoDataValue =
|
|
CSLFetchNameValue(papszParams, "nodata");
|
|
if (pszNoDataValue != nullptr)
|
|
{
|
|
psOptions->bNoDataSet = true;
|
|
psOptions->dfNoDataValue = CPLAtofM(pszNoDataValue);
|
|
}
|
|
CSLDestroy(papszParams);
|
|
}
|
|
else if (i + 1 < argc && EQUAL(papszArgv[i], "-oo"))
|
|
{
|
|
i++;
|
|
if (psOptionsForBinary)
|
|
{
|
|
psOptionsForBinary->aosOpenOptions.AddString(papszArgv[i]);
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"-oo switch only supported from gdal_grid binary.");
|
|
}
|
|
}
|
|
else if (papszArgv[i][0] == '-')
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "Unknown option name '%s'",
|
|
papszArgv[i]);
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
else if (!bGotSourceFilename)
|
|
{
|
|
bGotSourceFilename = true;
|
|
if (psOptionsForBinary)
|
|
{
|
|
psOptionsForBinary->osSource = papszArgv[i];
|
|
}
|
|
else
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_NotSupported,
|
|
"{source_filename} only supported from gdal_grid binary.");
|
|
}
|
|
}
|
|
else if (!bGotDestFilename)
|
|
{
|
|
bGotDestFilename = true;
|
|
if (psOptionsForBinary)
|
|
{
|
|
psOptionsForBinary->bDestSpecified = true;
|
|
psOptionsForBinary->osDest = papszArgv[i];
|
|
}
|
|
else
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_NotSupported,
|
|
"{dest_filename} only supported from gdal_grid binary.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Too many command options '%s'", papszArgv[i]);
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (psOptions->bClipSrc && psOptions->pszClipSrcDS != nullptr)
|
|
{
|
|
psOptions->poClipSrc = LoadGeometry(
|
|
psOptions->pszClipSrcDS, psOptions->pszClipSrcSQL,
|
|
psOptions->pszClipSrcLayer, psOptions->pszClipSrcWhere);
|
|
if (psOptions->poClipSrc == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot load source clip geometry.");
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
}
|
|
else if (psOptions->bClipSrc && psOptions->poClipSrc == nullptr &&
|
|
!psOptions->poSpatialFilter)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"-clipsrc must be used with -spat option or \n"
|
|
"a bounding box, WKT string or datasource must be "
|
|
"specified.");
|
|
GDALGridOptionsFree(psOptions);
|
|
return nullptr;
|
|
}
|
|
|
|
if (psOptions->poSpatialFilter)
|
|
{
|
|
if (psOptions->poClipSrc)
|
|
{
|
|
OGRGeometry *poTemp =
|
|
psOptions->poSpatialFilter->Intersection(psOptions->poClipSrc);
|
|
if (poTemp)
|
|
{
|
|
delete psOptions->poSpatialFilter;
|
|
psOptions->poSpatialFilter = poTemp;
|
|
}
|
|
|
|
delete psOptions->poClipSrc;
|
|
psOptions->poClipSrc = nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (psOptions->poClipSrc)
|
|
{
|
|
psOptions->poSpatialFilter = psOptions->poClipSrc;
|
|
psOptions->poClipSrc = nullptr;
|
|
}
|
|
}
|
|
|
|
return psOptions;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridOptionsFree() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Frees the GDALGridOptions struct.
|
|
*
|
|
* @param psOptions the options struct for GDALGrid().
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
|
|
void GDALGridOptionsFree(GDALGridOptions *psOptions)
|
|
{
|
|
if (psOptions == nullptr)
|
|
return;
|
|
|
|
CPLFree(psOptions->pszFormat);
|
|
CSLDestroy(psOptions->papszLayers);
|
|
CPLFree(psOptions->pszBurnAttribute);
|
|
CPLFree(psOptions->pszWHERE);
|
|
CPLFree(psOptions->pszSQL);
|
|
CSLDestroy(psOptions->papszCreateOptions);
|
|
CPLFree(psOptions->pOptions);
|
|
CPLFree(psOptions->pszOutputSRS);
|
|
delete psOptions->poSpatialFilter;
|
|
delete psOptions->poClipSrc;
|
|
CPLFree(psOptions->pszClipSrcDS);
|
|
CPLFree(psOptions->pszClipSrcSQL);
|
|
CPLFree(psOptions->pszClipSrcLayer);
|
|
CPLFree(psOptions->pszClipSrcWhere);
|
|
CPLFree(psOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridOptionsSetProgress() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Set a progress function.
|
|
*
|
|
* @param psOptions the options struct for GDALGrid().
|
|
* @param pfnProgress the progress callback.
|
|
* @param pProgressData the user data for the progress callback.
|
|
*
|
|
* @since GDAL 2.1
|
|
*/
|
|
|
|
void GDALGridOptionsSetProgress(GDALGridOptions *psOptions,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
psOptions->pfnProgress = pfnProgress;
|
|
psOptions->pProgressData = pProgressData;
|
|
if (pfnProgress == GDALTermProgress)
|
|
psOptions->bQuiet = false;
|
|
}
|