gdal/apps/gdalbuildvrt_lib.cpp

2376 lines
85 KiB
C++

/******************************************************************************
*
* Project: GDAL Utilities
* Purpose: Command line application to build VRT datasets from raster products
* or content of SHP tile index
* Author: Even Rouault, <even dot rouault at spatialys dot com>
*
******************************************************************************
* Copyright (c) 2007-2016, Even Rouault <even dot rouault at spatialys dot com>
*
* SPDX-License-Identifier: MIT
****************************************************************************/
#include "ogr_api.h"
#include "ogr_srs_api.h"
#include "cpl_port.h"
#include "gdal_utils.h"
#include "gdal_utils_priv.h"
#include "gdalargumentparser.h"
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "commonutils.h"
#include "cpl_conv.h"
#include "cpl_error.h"
#include "cpl_progress.h"
#include "cpl_string.h"
#include "cpl_vsi.h"
#include "cpl_vsi_virtual.h"
#include "gdal.h"
#include "gdal_vrt.h"
#include "gdal_priv.h"
#include "gdal_proxy.h"
#include "ogr_api.h"
#include "ogr_core.h"
#include "ogr_srs_api.h"
#include "ogr_spatialref.h"
#include "ogrsf_frmts.h"
#include "vrtdataset.h"
#define GEOTRSFRM_TOPLEFT_X 0
#define GEOTRSFRM_WE_RES 1
#define GEOTRSFRM_ROTATION_PARAM1 2
#define GEOTRSFRM_TOPLEFT_Y 3
#define GEOTRSFRM_ROTATION_PARAM2 4
#define GEOTRSFRM_NS_RES 5
namespace gdal::GDALBuildVRT
{
typedef enum
{
LOWEST_RESOLUTION,
HIGHEST_RESOLUTION,
AVERAGE_RESOLUTION,
USER_RESOLUTION
} ResolutionStrategy;
struct DatasetProperty
{
int isFileOK = FALSE;
int nRasterXSize = 0;
int nRasterYSize = 0;
double adfGeoTransform[6];
int nBlockXSize = 0;
int nBlockYSize = 0;
std::vector<GDALDataType> aeBandType{};
std::vector<bool> abHasNoData{};
std::vector<double> adfNoDataValues{};
std::vector<bool> abHasOffset{};
std::vector<double> adfOffset{};
std::vector<bool> abHasScale{};
std::vector<bool> abHasMaskBand{};
std::vector<double> adfScale{};
int bHasDatasetMask = 0;
bool bLastBandIsAlpha = false;
int nMaskBlockXSize = 0;
int nMaskBlockYSize = 0;
std::vector<int> anOverviewFactors{};
DatasetProperty()
{
adfGeoTransform[0] = 0;
adfGeoTransform[1] = 0;
adfGeoTransform[2] = 0;
adfGeoTransform[3] = 0;
adfGeoTransform[4] = 0;
adfGeoTransform[5] = 0;
}
};
struct BandProperty
{
GDALColorInterp colorInterpretation = GCI_Undefined;
GDALDataType dataType = GDT_Unknown;
std::unique_ptr<GDALColorTable> colorTable{};
bool bHasNoData = false;
double noDataValue = 0;
bool bHasOffset = false;
double dfOffset = 0;
bool bHasScale = false;
double dfScale = 0;
};
} // namespace gdal::GDALBuildVRT
using namespace gdal::GDALBuildVRT;
/************************************************************************/
/* GetSrcDstWin() */
/************************************************************************/
static int GetSrcDstWin(DatasetProperty *psDP, double we_res, double ns_res,
double minX, double minY, double maxX, double maxY,
int nTargetXSize, int nTargetYSize, double *pdfSrcXOff,
double *pdfSrcYOff, double *pdfSrcXSize,
double *pdfSrcYSize, double *pdfDstXOff,
double *pdfDstYOff, double *pdfDstXSize,
double *pdfDstYSize)
{
/* Check that the destination bounding box intersects the source bounding
* box */
if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] +
psDP->nRasterXSize * psDP->adfGeoTransform[GEOTRSFRM_WE_RES] <=
minX)
return FALSE;
if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] >= maxX)
return FALSE;
if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] +
psDP->nRasterYSize * psDP->adfGeoTransform[GEOTRSFRM_NS_RES] >=
maxY)
return FALSE;
if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] <= minY)
return FALSE;
if (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] < minX)
{
*pdfSrcXOff = (minX - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X]) /
psDP->adfGeoTransform[GEOTRSFRM_WE_RES];
*pdfDstXOff = 0.0;
}
else
{
*pdfSrcXOff = 0.0;
*pdfDstXOff =
((psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_X] - minX) / we_res);
}
if (maxY < psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y])
{
*pdfSrcYOff = (psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] - maxY) /
-psDP->adfGeoTransform[GEOTRSFRM_NS_RES];
*pdfDstYOff = 0.0;
}
else
{
*pdfSrcYOff = 0.0;
*pdfDstYOff =
((maxY - psDP->adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]) / -ns_res);
}
*pdfSrcXSize = psDP->nRasterXSize;
*pdfSrcYSize = psDP->nRasterYSize;
if (*pdfSrcXOff > 0)
*pdfSrcXSize -= *pdfSrcXOff;
if (*pdfSrcYOff > 0)
*pdfSrcYSize -= *pdfSrcYOff;
const double dfSrcToDstXSize =
psDP->adfGeoTransform[GEOTRSFRM_WE_RES] / we_res;
*pdfDstXSize = *pdfSrcXSize * dfSrcToDstXSize;
const double dfSrcToDstYSize =
psDP->adfGeoTransform[GEOTRSFRM_NS_RES] / ns_res;
*pdfDstYSize = *pdfSrcYSize * dfSrcToDstYSize;
if (*pdfDstXOff + *pdfDstXSize > nTargetXSize)
{
*pdfDstXSize = nTargetXSize - *pdfDstXOff;
*pdfSrcXSize = *pdfDstXSize / dfSrcToDstXSize;
}
if (*pdfDstYOff + *pdfDstYSize > nTargetYSize)
{
*pdfDstYSize = nTargetYSize - *pdfDstYOff;
*pdfSrcYSize = *pdfDstYSize / dfSrcToDstYSize;
}
return *pdfSrcXSize > 0 && *pdfDstXSize > 0 && *pdfSrcYSize > 0 &&
*pdfDstYSize > 0;
}
/************************************************************************/
/* VRTBuilder */
/************************************************************************/
class VRTBuilder
{
/* Input parameters */
bool bStrict = false;
char *pszOutputFilename = nullptr;
int nInputFiles = 0;
char **ppszInputFilenames = nullptr;
int nSrcDSCount = 0;
GDALDatasetH *pahSrcDS = nullptr;
int nTotalBands = 0;
bool bLastBandIsAlpha = false;
bool bExplicitBandList = false;
int nMaxSelectedBandNo = 0;
int nSelectedBands = 0;
int *panSelectedBandList = nullptr;
ResolutionStrategy resolutionStrategy = AVERAGE_RESOLUTION;
int nCountValid = 0;
double we_res = 0;
double ns_res = 0;
int bTargetAlignedPixels = 0;
double minX = 0;
double minY = 0;
double maxX = 0;
double maxY = 0;
int bSeparate = 0;
int bAllowProjectionDifference = 0;
int bAddAlpha = 0;
int bHideNoData = 0;
int nSubdataset = 0;
char *pszSrcNoData = nullptr;
char *pszVRTNoData = nullptr;
char *pszOutputSRS = nullptr;
char *pszResampling = nullptr;
char **papszOpenOptions = nullptr;
bool bUseSrcMaskBand = true;
bool bNoDataFromMask = false;
double dfMaskValueThreshold = 0;
CPLStringList aosCreateOptions{};
/* Internal variables */
char *pszProjectionRef = nullptr;
std::vector<BandProperty> asBandProperties{};
int bFirst = TRUE;
int bHasGeoTransform = 0;
int nRasterXSize = 0;
int nRasterYSize = 0;
std::vector<DatasetProperty> asDatasetProperties{};
int bUserExtent = 0;
int bAllowSrcNoData = TRUE;
double *padfSrcNoData = nullptr;
int nSrcNoDataCount = 0;
int bAllowVRTNoData = TRUE;
double *padfVRTNoData = nullptr;
int nVRTNoDataCount = 0;
int bHasRunBuild = 0;
int bHasDatasetMask = 0;
std::string AnalyseRaster(GDALDatasetH hDS,
DatasetProperty *psDatasetProperties);
void CreateVRTSeparate(VRTDatasetH hVRTDS);
void CreateVRTNonSeparate(VRTDatasetH hVRTDS);
CPL_DISALLOW_COPY_ASSIGN(VRTBuilder)
public:
VRTBuilder(bool bStrictIn, const char *pszOutputFilename, int nInputFiles,
const char *const *ppszInputFilenames, GDALDatasetH *pahSrcDSIn,
const int *panSelectedBandListIn, int nBandCount,
ResolutionStrategy resolutionStrategy, double we_res,
double ns_res, int bTargetAlignedPixels, double minX,
double minY, double maxX, double maxY, int bSeparate,
int bAllowProjectionDifference, int bAddAlpha, int bHideNoData,
int nSubdataset, const char *pszSrcNoData,
const char *pszVRTNoData, bool bUseSrcMaskBand,
bool bNoDataFromMask, double dfMaskValueThreshold,
const char *pszOutputSRS, const char *pszResampling,
const char *const *papszOpenOptionsIn,
const CPLStringList &aosCreateOptionsIn);
~VRTBuilder();
GDALDataset *Build(GDALProgressFunc pfnProgress, void *pProgressData);
};
/************************************************************************/
/* VRTBuilder() */
/************************************************************************/
VRTBuilder::VRTBuilder(
bool bStrictIn, const char *pszOutputFilenameIn, int nInputFilesIn,
const char *const *ppszInputFilenamesIn, GDALDatasetH *pahSrcDSIn,
const int *panSelectedBandListIn, int nBandCount,
ResolutionStrategy resolutionStrategyIn, double we_resIn, double ns_resIn,
int bTargetAlignedPixelsIn, double minXIn, double minYIn, double maxXIn,
double maxYIn, int bSeparateIn, int bAllowProjectionDifferenceIn,
int bAddAlphaIn, int bHideNoDataIn, int nSubdatasetIn,
const char *pszSrcNoDataIn, const char *pszVRTNoDataIn,
bool bUseSrcMaskBandIn, bool bNoDataFromMaskIn,
double dfMaskValueThresholdIn, const char *pszOutputSRSIn,
const char *pszResamplingIn, const char *const *papszOpenOptionsIn,
const CPLStringList &aosCreateOptionsIn)
: bStrict(bStrictIn), aosCreateOptions(aosCreateOptionsIn)
{
pszOutputFilename = CPLStrdup(pszOutputFilenameIn);
nInputFiles = nInputFilesIn;
papszOpenOptions = CSLDuplicate(const_cast<char **>(papszOpenOptionsIn));
if (ppszInputFilenamesIn)
{
ppszInputFilenames =
static_cast<char **>(CPLMalloc(nInputFiles * sizeof(char *)));
for (int i = 0; i < nInputFiles; i++)
{
ppszInputFilenames[i] = CPLStrdup(ppszInputFilenamesIn[i]);
}
}
else if (pahSrcDSIn)
{
nSrcDSCount = nInputFiles;
pahSrcDS = static_cast<GDALDatasetH *>(
CPLMalloc(nInputFiles * sizeof(GDALDatasetH)));
memcpy(pahSrcDS, pahSrcDSIn, nInputFiles * sizeof(GDALDatasetH));
ppszInputFilenames =
static_cast<char **>(CPLMalloc(nInputFiles * sizeof(char *)));
for (int i = 0; i < nInputFiles; i++)
{
ppszInputFilenames[i] =
CPLStrdup(GDALGetDescription(pahSrcDSIn[i]));
}
}
bExplicitBandList = nBandCount != 0;
nSelectedBands = nBandCount;
if (nBandCount)
{
panSelectedBandList =
static_cast<int *>(CPLMalloc(nSelectedBands * sizeof(int)));
memcpy(panSelectedBandList, panSelectedBandListIn,
nSelectedBands * sizeof(int));
}
resolutionStrategy = resolutionStrategyIn;
we_res = we_resIn;
ns_res = ns_resIn;
bTargetAlignedPixels = bTargetAlignedPixelsIn;
minX = minXIn;
minY = minYIn;
maxX = maxXIn;
maxY = maxYIn;
bSeparate = bSeparateIn;
bAllowProjectionDifference = bAllowProjectionDifferenceIn;
bAddAlpha = bAddAlphaIn;
bHideNoData = bHideNoDataIn;
nSubdataset = nSubdatasetIn;
pszSrcNoData = (pszSrcNoDataIn) ? CPLStrdup(pszSrcNoDataIn) : nullptr;
pszVRTNoData = (pszVRTNoDataIn) ? CPLStrdup(pszVRTNoDataIn) : nullptr;
pszOutputSRS = (pszOutputSRSIn) ? CPLStrdup(pszOutputSRSIn) : nullptr;
pszResampling = (pszResamplingIn) ? CPLStrdup(pszResamplingIn) : nullptr;
bUseSrcMaskBand = bUseSrcMaskBandIn;
bNoDataFromMask = bNoDataFromMaskIn;
dfMaskValueThreshold = dfMaskValueThresholdIn;
}
/************************************************************************/
/* ~VRTBuilder() */
/************************************************************************/
VRTBuilder::~VRTBuilder()
{
CPLFree(pszOutputFilename);
CPLFree(pszSrcNoData);
CPLFree(pszVRTNoData);
CPLFree(panSelectedBandList);
if (ppszInputFilenames)
{
for (int i = 0; i < nInputFiles; i++)
{
CPLFree(ppszInputFilenames[i]);
}
}
CPLFree(ppszInputFilenames);
CPLFree(pahSrcDS);
CPLFree(pszProjectionRef);
CPLFree(padfSrcNoData);
CPLFree(padfVRTNoData);
CPLFree(pszOutputSRS);
CPLFree(pszResampling);
CSLDestroy(papszOpenOptions);
}
/************************************************************************/
/* ProjAreEqual() */
/************************************************************************/
static int ProjAreEqual(const char *pszWKT1, const char *pszWKT2)
{
if (EQUAL(pszWKT1, pszWKT2))
return TRUE;
OGRSpatialReferenceH hSRS1 = OSRNewSpatialReference(pszWKT1);
OGRSpatialReferenceH hSRS2 = OSRNewSpatialReference(pszWKT2);
int bRet = hSRS1 != nullptr && hSRS2 != nullptr && OSRIsSame(hSRS1, hSRS2);
if (hSRS1)
OSRDestroySpatialReference(hSRS1);
if (hSRS2)
OSRDestroySpatialReference(hSRS2);
return bRet;
}
/************************************************************************/
/* GetProjectionName() */
/************************************************************************/
static CPLString GetProjectionName(const char *pszProjection)
{
if (!pszProjection)
return "(null)";
OGRSpatialReference oSRS;
oSRS.SetFromUserInput(pszProjection);
const char *pszRet = nullptr;
if (oSRS.IsProjected())
pszRet = oSRS.GetAttrValue("PROJCS");
else if (oSRS.IsGeographic())
pszRet = oSRS.GetAttrValue("GEOGCS");
return pszRet ? pszRet : "(null)";
}
/************************************************************************/
/* AnalyseRaster() */
/************************************************************************/
static void checkNoDataValues(const std::vector<BandProperty> &asProperties)
{
for (const auto &oProps : asProperties)
{
if (oProps.bHasNoData && GDALDataTypeIsInteger(oProps.dataType) &&
!GDALIsValueExactAs(oProps.noDataValue, oProps.dataType))
{
CPLError(CE_Warning, CPLE_NotSupported,
"Band data type of %s cannot represent the specified "
"NoData value of %g",
GDALGetDataTypeName(oProps.dataType), oProps.noDataValue);
}
}
}
std::string VRTBuilder::AnalyseRaster(GDALDatasetH hDS,
DatasetProperty *psDatasetProperties)
{
GDALDataset *poDS = GDALDataset::FromHandle(hDS);
const char *dsFileName = poDS->GetDescription();
char **papszMetadata = poDS->GetMetadata("SUBDATASETS");
if (CSLCount(papszMetadata) > 0 && poDS->GetRasterCount() == 0)
{
ppszInputFilenames = static_cast<char **>(CPLRealloc(
ppszInputFilenames,
sizeof(char *) * (nInputFiles + CSLCount(papszMetadata))));
if (nSubdataset < 0)
{
int count = 1;
char subdatasetNameKey[80];
snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
"SUBDATASET_%d_NAME", count);
while (*papszMetadata != nullptr)
{
if (EQUALN(*papszMetadata, subdatasetNameKey,
strlen(subdatasetNameKey)))
{
asDatasetProperties.resize(nInputFiles + 1);
ppszInputFilenames[nInputFiles] = CPLStrdup(
*papszMetadata + strlen(subdatasetNameKey) + 1);
nInputFiles++;
count++;
snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
"SUBDATASET_%d_NAME", count);
}
papszMetadata++;
}
}
else
{
char subdatasetNameKey[80];
const char *pszSubdatasetName;
snprintf(subdatasetNameKey, sizeof(subdatasetNameKey),
"SUBDATASET_%d_NAME", nSubdataset);
pszSubdatasetName =
CSLFetchNameValue(papszMetadata, subdatasetNameKey);
if (pszSubdatasetName)
{
asDatasetProperties.resize(nInputFiles + 1);
ppszInputFilenames[nInputFiles] = CPLStrdup(pszSubdatasetName);
nInputFiles++;
}
}
return "SILENTLY_IGNORE";
}
const char *proj = poDS->GetProjectionRef();
double *padfGeoTransform = psDatasetProperties->adfGeoTransform;
int bGotGeoTransform = poDS->GetGeoTransform(padfGeoTransform) == CE_None;
if (bSeparate)
{
if (bFirst)
{
bHasGeoTransform = bGotGeoTransform;
if (!bHasGeoTransform)
{
if (bUserExtent)
{
CPLError(CE_Warning, CPLE_NotSupported,
"User extent ignored by gdalbuildvrt -separate "
"with ungeoreferenced images.");
}
if (resolutionStrategy == USER_RESOLUTION)
{
CPLError(CE_Warning, CPLE_NotSupported,
"User resolution ignored by gdalbuildvrt "
"-separate with ungeoreferenced images.");
}
}
}
else if (bHasGeoTransform != bGotGeoTransform)
{
return "gdalbuildvrt -separate cannot stack ungeoreferenced and "
"georeferenced images.";
}
else if (!bHasGeoTransform && (nRasterXSize != poDS->GetRasterXSize() ||
nRasterYSize != poDS->GetRasterYSize()))
{
return "gdalbuildvrt -separate cannot stack ungeoreferenced images "
"that have not the same dimensions.";
}
}
else
{
if (!bGotGeoTransform)
{
return "gdalbuildvrt does not support ungeoreferenced image.";
}
bHasGeoTransform = TRUE;
}
if (bGotGeoTransform)
{
if (padfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] != 0 ||
padfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] != 0)
{
return "gdalbuildvrt does not support rotated geo transforms.";
}
if (padfGeoTransform[GEOTRSFRM_NS_RES] >= 0)
{
return "gdalbuildvrt does not support positive NS resolution.";
}
}
psDatasetProperties->nRasterXSize = poDS->GetRasterXSize();
psDatasetProperties->nRasterYSize = poDS->GetRasterYSize();
if (bFirst && bSeparate && !bGotGeoTransform)
{
nRasterXSize = poDS->GetRasterXSize();
nRasterYSize = poDS->GetRasterYSize();
}
double ds_minX = padfGeoTransform[GEOTRSFRM_TOPLEFT_X];
double ds_maxY = padfGeoTransform[GEOTRSFRM_TOPLEFT_Y];
double ds_maxX =
ds_minX + GDALGetRasterXSize(hDS) * padfGeoTransform[GEOTRSFRM_WE_RES];
double ds_minY =
ds_maxY + GDALGetRasterYSize(hDS) * padfGeoTransform[GEOTRSFRM_NS_RES];
int _nBands = GDALGetRasterCount(hDS);
if (_nBands == 0)
{
return "Dataset has no bands";
}
if (bNoDataFromMask &&
poDS->GetRasterBand(_nBands)->GetColorInterpretation() == GCI_AlphaBand)
_nBands--;
GDALRasterBand *poFirstBand = poDS->GetRasterBand(1);
poFirstBand->GetBlockSize(&psDatasetProperties->nBlockXSize,
&psDatasetProperties->nBlockYSize);
/* For the -separate case */
psDatasetProperties->aeBandType.resize(_nBands);
psDatasetProperties->adfNoDataValues.resize(_nBands);
psDatasetProperties->abHasNoData.resize(_nBands);
psDatasetProperties->adfOffset.resize(_nBands);
psDatasetProperties->abHasOffset.resize(_nBands);
psDatasetProperties->adfScale.resize(_nBands);
psDatasetProperties->abHasScale.resize(_nBands);
psDatasetProperties->abHasMaskBand.resize(_nBands);
psDatasetProperties->bHasDatasetMask =
poFirstBand->GetMaskFlags() == GMF_PER_DATASET;
if (psDatasetProperties->bHasDatasetMask)
bHasDatasetMask = TRUE;
poFirstBand->GetMaskBand()->GetBlockSize(
&psDatasetProperties->nMaskBlockXSize,
&psDatasetProperties->nMaskBlockYSize);
psDatasetProperties->bLastBandIsAlpha = false;
if (poDS->GetRasterBand(_nBands)->GetColorInterpretation() == GCI_AlphaBand)
psDatasetProperties->bLastBandIsAlpha = true;
// Collect overview factors. We only handle power-of-two situations for now
const int nOverviews = poFirstBand->GetOverviewCount();
int nExpectedOvFactor = 2;
for (int j = 0; j < nOverviews; j++)
{
GDALRasterBand *poOverview = poFirstBand->GetOverview(j);
if (!poOverview)
continue;
if (poOverview->GetXSize() < 128 && poOverview->GetYSize() < 128)
{
break;
}
const int nOvFactor = GDALComputeOvFactor(
poOverview->GetXSize(), poFirstBand->GetXSize(),
poOverview->GetYSize(), poFirstBand->GetYSize());
if (nOvFactor != nExpectedOvFactor)
break;
psDatasetProperties->anOverviewFactors.push_back(nOvFactor);
nExpectedOvFactor *= 2;
}
for (int j = 0; j < _nBands; j++)
{
GDALRasterBand *poBand = poDS->GetRasterBand(j + 1);
psDatasetProperties->aeBandType[j] = poBand->GetRasterDataType();
if (!bSeparate && nSrcNoDataCount > 0)
{
psDatasetProperties->abHasNoData[j] = true;
if (j < nSrcNoDataCount)
psDatasetProperties->adfNoDataValues[j] = padfSrcNoData[j];
else
psDatasetProperties->adfNoDataValues[j] =
padfSrcNoData[nSrcNoDataCount - 1];
}
else
{
int bHasNoData = false;
psDatasetProperties->adfNoDataValues[j] =
poBand->GetNoDataValue(&bHasNoData);
psDatasetProperties->abHasNoData[j] = bHasNoData != 0;
}
int bHasOffset = false;
psDatasetProperties->adfOffset[j] = poBand->GetOffset(&bHasOffset);
psDatasetProperties->abHasOffset[j] =
bHasOffset != 0 && psDatasetProperties->adfOffset[j] != 0.0;
int bHasScale = false;
psDatasetProperties->adfScale[j] = poBand->GetScale(&bHasScale);
psDatasetProperties->abHasScale[j] =
bHasScale != 0 && psDatasetProperties->adfScale[j] != 1.0;
const int nMaskFlags = poBand->GetMaskFlags();
psDatasetProperties->abHasMaskBand[j] =
(nMaskFlags != GMF_ALL_VALID && nMaskFlags != GMF_NODATA) ||
poBand->GetColorInterpretation() == GCI_AlphaBand;
}
if (bSeparate)
{
for (int j = 0; j < nSelectedBands; j++)
{
if (panSelectedBandList[j] > _nBands)
{
return CPLSPrintf("%s has %d bands, but %d is requested",
dsFileName, _nBands, panSelectedBandList[j]);
}
}
}
if (bFirst)
{
nTotalBands = _nBands;
if (bAddAlpha && psDatasetProperties->bLastBandIsAlpha)
{
bLastBandIsAlpha = true;
nTotalBands--;
}
if (proj)
pszProjectionRef = CPLStrdup(proj);
if (!bUserExtent)
{
minX = ds_minX;
minY = ds_minY;
maxX = ds_maxX;
maxY = ds_maxY;
}
if (!bSeparate)
{
// if not provided an explicit band list, take the one of the first
// dataset
if (nSelectedBands == 0)
{
nSelectedBands = nTotalBands;
CPLFree(panSelectedBandList);
panSelectedBandList =
static_cast<int *>(CPLMalloc(nSelectedBands * sizeof(int)));
for (int j = 0; j < nSelectedBands; j++)
{
panSelectedBandList[j] = j + 1;
}
}
for (int j = 0; j < nSelectedBands; j++)
{
nMaxSelectedBandNo =
std::max(nMaxSelectedBandNo, panSelectedBandList[j]);
}
asBandProperties.resize(nSelectedBands);
for (int j = 0; j < nSelectedBands; j++)
{
const int nSelBand = panSelectedBandList[j];
if (nSelBand <= 0 || nSelBand > nTotalBands)
{
return CPLSPrintf("Invalid band number: %d", nSelBand);
}
GDALRasterBand *poBand = poDS->GetRasterBand(nSelBand);
asBandProperties[j].colorInterpretation =
poBand->GetColorInterpretation();
asBandProperties[j].dataType = poBand->GetRasterDataType();
if (asBandProperties[j].colorInterpretation == GCI_PaletteIndex)
{
auto colorTable = poBand->GetColorTable();
if (colorTable)
{
asBandProperties[j].colorTable.reset(
colorTable->Clone());
}
}
else
asBandProperties[j].colorTable = nullptr;
if (nVRTNoDataCount > 0)
{
asBandProperties[j].bHasNoData = true;
if (j < nVRTNoDataCount)
asBandProperties[j].noDataValue = padfVRTNoData[j];
else
asBandProperties[j].noDataValue =
padfVRTNoData[nVRTNoDataCount - 1];
}
else
{
int bHasNoData = false;
asBandProperties[j].noDataValue =
poBand->GetNoDataValue(&bHasNoData);
asBandProperties[j].bHasNoData = bHasNoData != 0;
}
int bHasOffset = false;
asBandProperties[j].dfOffset = poBand->GetOffset(&bHasOffset);
asBandProperties[j].bHasOffset =
bHasOffset != 0 && asBandProperties[j].dfOffset != 0.0;
int bHasScale = false;
asBandProperties[j].dfScale = poBand->GetScale(&bHasScale);
asBandProperties[j].bHasScale =
bHasScale != 0 && asBandProperties[j].dfScale != 1.0;
}
}
}
else
{
if ((proj != nullptr && pszProjectionRef == nullptr) ||
(proj == nullptr && pszProjectionRef != nullptr) ||
(proj != nullptr && pszProjectionRef != nullptr &&
ProjAreEqual(proj, pszProjectionRef) == FALSE))
{
if (!bAllowProjectionDifference)
{
CPLString osExpected = GetProjectionName(pszProjectionRef);
CPLString osGot = GetProjectionName(proj);
return CPLSPrintf("gdalbuildvrt does not support heterogeneous "
"projection: expected %s, got %s.",
osExpected.c_str(), osGot.c_str());
}
}
if (!bSeparate)
{
if (!bExplicitBandList && _nBands != nTotalBands)
{
if (bAddAlpha && _nBands == nTotalBands + 1 &&
psDatasetProperties->bLastBandIsAlpha)
{
bLastBandIsAlpha = true;
}
else
{
return CPLSPrintf(
"gdalbuildvrt does not support heterogeneous band "
"numbers: expected %d, got %d.",
nTotalBands, _nBands);
}
}
else if (bExplicitBandList && _nBands < nMaxSelectedBandNo)
{
return CPLSPrintf(
"gdalbuildvrt does not support heterogeneous band "
"numbers: expected at least %d, got %d.",
nMaxSelectedBandNo, _nBands);
}
for (int j = 0; j < nSelectedBands; j++)
{
const int nSelBand = panSelectedBandList[j];
CPLAssert(nSelBand >= 1 && nSelBand <= _nBands);
GDALRasterBand *poBand = poDS->GetRasterBand(nSelBand);
if (asBandProperties[j].colorInterpretation !=
poBand->GetColorInterpretation())
{
return CPLSPrintf(
"gdalbuildvrt does not support heterogeneous "
"band color interpretation: expected %s, got %s.",
GDALGetColorInterpretationName(
asBandProperties[j].colorInterpretation),
GDALGetColorInterpretationName(
poBand->GetColorInterpretation()));
}
if (asBandProperties[j].dataType != poBand->GetRasterDataType())
{
return CPLSPrintf(
"gdalbuildvrt does not support heterogeneous "
"band data type: expected %s, got %s.",
GDALGetDataTypeName(asBandProperties[j].dataType),
GDALGetDataTypeName(poBand->GetRasterDataType()));
}
if (asBandProperties[j].colorTable)
{
const GDALColorTable *colorTable = poBand->GetColorTable();
int nRefColorEntryCount =
asBandProperties[j].colorTable->GetColorEntryCount();
if (colorTable == nullptr ||
colorTable->GetColorEntryCount() != nRefColorEntryCount)
{
return "gdalbuildvrt does not support rasters with "
"different color tables (different number of "
"color table entries)";
}
/* Check that the palette are the same too */
/* We just warn and still process the file. It is not a
* technical no-go, but the user */
/* should check that the end result is OK for him. */
for (int i = 0; i < nRefColorEntryCount; i++)
{
const GDALColorEntry *psEntry =
colorTable->GetColorEntry(i);
const GDALColorEntry *psEntryRef =
asBandProperties[j].colorTable->GetColorEntry(i);
if (psEntry->c1 != psEntryRef->c1 ||
psEntry->c2 != psEntryRef->c2 ||
psEntry->c3 != psEntryRef->c3 ||
psEntry->c4 != psEntryRef->c4)
{
static int bFirstWarningPCT = TRUE;
if (bFirstWarningPCT)
CPLError(
CE_Warning, CPLE_NotSupported,
"%s has different values than the first "
"raster for some entries in the color "
"table.\n"
"The end result might produce weird "
"colors.\n"
"You're advised to pre-process your "
"rasters with other tools, such as "
"pct2rgb.py or gdal_translate -expand RGB\n"
"to operate gdalbuildvrt on RGB rasters "
"instead",
dsFileName);
else
CPLError(CE_Warning, CPLE_NotSupported,
"%s has different values than the "
"first raster for some entries in the "
"color table.",
dsFileName);
bFirstWarningPCT = FALSE;
break;
}
}
}
if (psDatasetProperties->abHasOffset[j] !=
asBandProperties[j].bHasOffset ||
(asBandProperties[j].bHasOffset &&
psDatasetProperties->adfOffset[j] !=
asBandProperties[j].dfOffset))
{
return CPLSPrintf(
"gdalbuildvrt does not support heterogeneous "
"band offset: expected (%d,%f), got (%d,%f).",
static_cast<int>(asBandProperties[j].bHasOffset),
asBandProperties[j].dfOffset,
static_cast<int>(psDatasetProperties->abHasOffset[j]),
psDatasetProperties->adfOffset[j]);
}
if (psDatasetProperties->abHasScale[j] !=
asBandProperties[j].bHasScale ||
(asBandProperties[j].bHasScale &&
psDatasetProperties->adfScale[j] !=
asBandProperties[j].dfScale))
{
return CPLSPrintf(
"gdalbuildvrt does not support heterogeneous "
"band scale: expected (%d,%f), got (%d,%f).",
static_cast<int>(asBandProperties[j].bHasScale),
asBandProperties[j].dfScale,
static_cast<int>(psDatasetProperties->abHasScale[j]),
psDatasetProperties->adfScale[j]);
}
}
}
if (!bUserExtent)
{
if (ds_minX < minX)
minX = ds_minX;
if (ds_minY < minY)
minY = ds_minY;
if (ds_maxX > maxX)
maxX = ds_maxX;
if (ds_maxY > maxY)
maxY = ds_maxY;
}
}
if (resolutionStrategy == AVERAGE_RESOLUTION)
{
++nCountValid;
{
const double dfDelta = padfGeoTransform[GEOTRSFRM_WE_RES] - we_res;
we_res += dfDelta / nCountValid;
}
{
const double dfDelta = padfGeoTransform[GEOTRSFRM_NS_RES] - ns_res;
ns_res += dfDelta / nCountValid;
}
}
else if (resolutionStrategy != USER_RESOLUTION)
{
if (bFirst)
{
we_res = padfGeoTransform[GEOTRSFRM_WE_RES];
ns_res = padfGeoTransform[GEOTRSFRM_NS_RES];
}
else if (resolutionStrategy == HIGHEST_RESOLUTION)
{
we_res = std::min(we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
// ns_res is negative, the highest resolution is the max value.
ns_res = std::max(ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
}
else
{
we_res = std::max(we_res, padfGeoTransform[GEOTRSFRM_WE_RES]);
// ns_res is negative, the lowest resolution is the min value.
ns_res = std::min(ns_res, padfGeoTransform[GEOTRSFRM_NS_RES]);
}
}
checkNoDataValues(asBandProperties);
return "";
}
/************************************************************************/
/* CreateVRTSeparate() */
/************************************************************************/
void VRTBuilder::CreateVRTSeparate(VRTDatasetH hVRTDS)
{
int iBand = 1;
for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
{
DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
if (psDatasetProperties->isFileOK == FALSE)
continue;
const char *dsFileName = ppszInputFilenames[i];
double dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
dfDstYOff, dfDstXSize, dfDstYSize;
if (bHasGeoTransform)
{
if (!GetSrcDstWin(psDatasetProperties, we_res, ns_res, minX, minY,
maxX, maxY, nRasterXSize, nRasterYSize,
&dfSrcXOff, &dfSrcYOff, &dfSrcXSize, &dfSrcYSize,
&dfDstXOff, &dfDstYOff, &dfDstXSize, &dfDstYSize))
{
CPLDebug("BuildVRT",
"Skipping %s as not intersecting area of interest",
dsFileName);
continue;
}
}
else
{
dfSrcXOff = dfSrcYOff = dfDstXOff = dfDstYOff = 0;
dfSrcXSize = dfDstXSize = nRasterXSize;
dfSrcYSize = dfDstYSize = nRasterYSize;
}
GDALDatasetH hSourceDS;
bool bDropRef = false;
if (nSrcDSCount == nInputFiles &&
GDALGetDatasetDriver(pahSrcDS[i]) != nullptr &&
(dsFileName[0] == '\0' || // could be a unnamed VRT file
EQUAL(GDALGetDescription(GDALGetDatasetDriver(pahSrcDS[i])),
"MEM")))
{
hSourceDS = pahSrcDS[i];
}
else
{
bDropRef = true;
GDALProxyPoolDatasetH hProxyDS = GDALProxyPoolDatasetCreate(
dsFileName, psDatasetProperties->nRasterXSize,
psDatasetProperties->nRasterYSize, GA_ReadOnly, TRUE,
pszProjectionRef, psDatasetProperties->adfGeoTransform);
hSourceDS = static_cast<GDALDatasetH>(hProxyDS);
reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)->SetOpenOptions(
papszOpenOptions);
for (int jBand = 0;
jBand <
static_cast<int>(psDatasetProperties->aeBandType.size());
++jBand)
{
GDALProxyPoolDatasetAddSrcBandDescription(
hProxyDS, psDatasetProperties->aeBandType[jBand],
psDatasetProperties->nBlockXSize,
psDatasetProperties->nBlockYSize);
}
}
const int nBandsToIter =
nSelectedBands > 0
? nSelectedBands
: static_cast<int>(psDatasetProperties->aeBandType.size());
for (int iBandToIter = 0; iBandToIter < nBandsToIter; ++iBandToIter)
{
// 0-based
const int nSrcBandIdx = nSelectedBands > 0
? panSelectedBandList[iBandToIter] - 1
: iBandToIter;
assert(nSrcBandIdx >= 0);
GDALAddBand(hVRTDS, psDatasetProperties->aeBandType[nSrcBandIdx],
nullptr);
VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
GDALGetRasterBand(hVRTDS, iBand));
if (bHideNoData)
GDALSetMetadataItem(hVRTBand, "HideNoDataValue", "1", nullptr);
VRTSourcedRasterBand *poVRTBand =
static_cast<VRTSourcedRasterBand *>(hVRTBand);
if (bAllowVRTNoData)
{
if (nVRTNoDataCount > 0)
{
if (iBand - 1 < nVRTNoDataCount)
GDALSetRasterNoDataValue(hVRTBand,
padfVRTNoData[iBand - 1]);
else
GDALSetRasterNoDataValue(
hVRTBand, padfVRTNoData[nVRTNoDataCount - 1]);
}
else if (psDatasetProperties->abHasNoData[nSrcBandIdx])
{
GDALSetRasterNoDataValue(
hVRTBand,
psDatasetProperties->adfNoDataValues[nSrcBandIdx]);
}
}
VRTSimpleSource *poSimpleSource;
if (bAllowSrcNoData &&
(nSrcNoDataCount > 0 ||
psDatasetProperties->abHasNoData[nSrcBandIdx]))
{
auto poComplexSource = new VRTComplexSource();
poSimpleSource = poComplexSource;
if (nSrcNoDataCount > 0)
{
if (iBand - 1 < nSrcNoDataCount)
poComplexSource->SetNoDataValue(
padfSrcNoData[iBand - 1]);
else
poComplexSource->SetNoDataValue(
padfSrcNoData[nSrcNoDataCount - 1]);
}
else /* if (psDatasetProperties->abHasNoData[nSrcBandIdx]) */
{
poComplexSource->SetNoDataValue(
psDatasetProperties->adfNoDataValues[nSrcBandIdx]);
}
}
else if (bUseSrcMaskBand &&
psDatasetProperties->abHasMaskBand[nSrcBandIdx])
{
auto poSource = new VRTComplexSource();
poSource->SetUseMaskBand(true);
poSimpleSource = poSource;
}
else
poSimpleSource = new VRTSimpleSource();
if (pszResampling)
poSimpleSource->SetResampling(pszResampling);
poVRTBand->ConfigureSource(
poSimpleSource,
static_cast<GDALRasterBand *>(
GDALGetRasterBand(hSourceDS, nSrcBandIdx + 1)),
FALSE, dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
dfDstYOff, dfDstXSize, dfDstYSize);
if (psDatasetProperties->abHasOffset[nSrcBandIdx])
poVRTBand->SetOffset(
psDatasetProperties->adfOffset[nSrcBandIdx]);
if (psDatasetProperties->abHasScale[nSrcBandIdx])
poVRTBand->SetScale(psDatasetProperties->adfScale[nSrcBandIdx]);
poVRTBand->AddSource(poSimpleSource);
iBand++;
}
if (bDropRef)
{
GDALDereferenceDataset(hSourceDS);
}
}
}
/************************************************************************/
/* CreateVRTNonSeparate() */
/************************************************************************/
void VRTBuilder::CreateVRTNonSeparate(VRTDatasetH hVRTDS)
{
VRTDataset *poVRTDS = reinterpret_cast<VRTDataset *>(hVRTDS);
for (int j = 0; j < nSelectedBands; j++)
{
poVRTDS->AddBand(asBandProperties[j].dataType);
GDALRasterBand *poBand = poVRTDS->GetRasterBand(j + 1);
poBand->SetColorInterpretation(asBandProperties[j].colorInterpretation);
if (asBandProperties[j].colorInterpretation == GCI_PaletteIndex)
{
poBand->SetColorTable(asBandProperties[j].colorTable.get());
}
if (bAllowVRTNoData && asBandProperties[j].bHasNoData)
poBand->SetNoDataValue(asBandProperties[j].noDataValue);
if (bHideNoData)
poBand->SetMetadataItem("HideNoDataValue", "1");
if (asBandProperties[j].bHasOffset)
poBand->SetOffset(asBandProperties[j].dfOffset);
if (asBandProperties[j].bHasScale)
poBand->SetScale(asBandProperties[j].dfScale);
}
VRTSourcedRasterBand *poMaskVRTBand = nullptr;
if (bAddAlpha)
{
poVRTDS->AddBand(GDT_Byte);
GDALRasterBand *poBand = poVRTDS->GetRasterBand(nSelectedBands + 1);
poBand->SetColorInterpretation(GCI_AlphaBand);
}
else if (bHasDatasetMask)
{
poVRTDS->CreateMaskBand(GMF_PER_DATASET);
poMaskVRTBand = static_cast<VRTSourcedRasterBand *>(
poVRTDS->GetRasterBand(1)->GetMaskBand());
}
bool bCanCollectOverviewFactors = true;
std::set<int> anOverviewFactorsSet;
std::vector<int> anIdxValidDatasets;
for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
{
DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
if (psDatasetProperties->isFileOK == FALSE)
continue;
const char *dsFileName = ppszInputFilenames[i];
double dfSrcXOff;
double dfSrcYOff;
double dfSrcXSize;
double dfSrcYSize;
double dfDstXOff;
double dfDstYOff;
double dfDstXSize;
double dfDstYSize;
if (!GetSrcDstWin(psDatasetProperties, we_res, ns_res, minX, minY, maxX,
maxY, nRasterXSize, nRasterYSize, &dfSrcXOff,
&dfSrcYOff, &dfSrcXSize, &dfSrcYSize, &dfDstXOff,
&dfDstYOff, &dfDstXSize, &dfDstYSize))
{
CPLDebug("BuildVRT",
"Skipping %s as not intersecting area of interest",
dsFileName);
continue;
}
anIdxValidDatasets.push_back(i);
if (bCanCollectOverviewFactors)
{
if (std::abs(psDatasetProperties->adfGeoTransform[1] - we_res) >
1e-8 * std::abs(we_res) ||
std::abs(psDatasetProperties->adfGeoTransform[5] - ns_res) >
1e-8 * std::abs(ns_res))
{
bCanCollectOverviewFactors = false;
anOverviewFactorsSet.clear();
}
}
if (bCanCollectOverviewFactors)
{
for (int nOvFactor : psDatasetProperties->anOverviewFactors)
anOverviewFactorsSet.insert(nOvFactor);
}
GDALDatasetH hSourceDS;
bool bDropRef = false;
if (nSrcDSCount == nInputFiles &&
GDALGetDatasetDriver(pahSrcDS[i]) != nullptr &&
(dsFileName[0] == '\0' || // could be a unnamed VRT file
EQUAL(GDALGetDescription(GDALGetDatasetDriver(pahSrcDS[i])),
"MEM")))
{
hSourceDS = pahSrcDS[i];
}
else
{
bDropRef = true;
GDALProxyPoolDatasetH hProxyDS = GDALProxyPoolDatasetCreate(
dsFileName, psDatasetProperties->nRasterXSize,
psDatasetProperties->nRasterYSize, GA_ReadOnly, TRUE,
pszProjectionRef, psDatasetProperties->adfGeoTransform);
reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)->SetOpenOptions(
papszOpenOptions);
for (int j = 0;
j < nMaxSelectedBandNo +
(bAddAlpha && psDatasetProperties->bLastBandIsAlpha
? 1
: 0);
j++)
{
GDALProxyPoolDatasetAddSrcBandDescription(
hProxyDS,
j < static_cast<int>(asBandProperties.size())
? asBandProperties[j].dataType
: GDT_Byte,
psDatasetProperties->nBlockXSize,
psDatasetProperties->nBlockYSize);
}
if (bHasDatasetMask && !bAddAlpha)
{
static_cast<GDALProxyPoolRasterBand *>(
reinterpret_cast<GDALProxyPoolDataset *>(hProxyDS)
->GetRasterBand(1))
->AddSrcMaskBandDescription(
GDT_Byte, psDatasetProperties->nMaskBlockXSize,
psDatasetProperties->nMaskBlockYSize);
}
hSourceDS = static_cast<GDALDatasetH>(hProxyDS);
}
for (int j = 0;
j <
nSelectedBands +
(bAddAlpha && psDatasetProperties->bLastBandIsAlpha ? 1 : 0);
j++)
{
VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
poVRTDS->GetRasterBand(j + 1));
const int nSelBand = j == nSelectedBands ? nSelectedBands + 1
: panSelectedBandList[j];
/* Place the raster band at the right position in the VRT */
VRTSourcedRasterBand *poVRTBand =
static_cast<VRTSourcedRasterBand *>(hVRTBand);
VRTSimpleSource *poSimpleSource;
if (bNoDataFromMask)
{
auto poNoDataFromMaskSource = new VRTNoDataFromMaskSource();
poSimpleSource = poNoDataFromMaskSource;
poNoDataFromMaskSource->SetParameters(
(nVRTNoDataCount > 0)
? ((j < nVRTNoDataCount)
? padfVRTNoData[j]
: padfVRTNoData[nVRTNoDataCount - 1])
: 0,
dfMaskValueThreshold);
}
else if (bAllowSrcNoData &&
psDatasetProperties->abHasNoData[nSelBand - 1])
{
auto poComplexSource = new VRTComplexSource();
poSimpleSource = poComplexSource;
poComplexSource->SetNoDataValue(
psDatasetProperties->adfNoDataValues[nSelBand - 1]);
}
else if (bUseSrcMaskBand &&
psDatasetProperties->abHasMaskBand[nSelBand - 1])
{
auto poSource = new VRTComplexSource();
poSource->SetUseMaskBand(true);
poSimpleSource = poSource;
}
else
poSimpleSource = new VRTSimpleSource();
if (pszResampling)
poSimpleSource->SetResampling(pszResampling);
auto poSrcBand = GDALRasterBand::FromHandle(
GDALGetRasterBand(hSourceDS, nSelBand));
poVRTBand->ConfigureSource(poSimpleSource, poSrcBand, FALSE,
dfSrcXOff, dfSrcYOff, dfSrcXSize,
dfSrcYSize, dfDstXOff, dfDstYOff,
dfDstXSize, dfDstYSize);
poVRTBand->AddSource(poSimpleSource);
}
if (bAddAlpha && !psDatasetProperties->bLastBandIsAlpha)
{
VRTSourcedRasterBandH hVRTBand = static_cast<VRTSourcedRasterBandH>(
GDALGetRasterBand(hVRTDS, nSelectedBands + 1));
/* Little trick : we use an offset of 255 and a scaling of 0, so
* that in areas covered */
/* by the source, the value of the alpha band will be 255, otherwise
* it will be 0 */
static_cast<VRTSourcedRasterBand *>(hVRTBand)->AddComplexSource(
static_cast<GDALRasterBand *>(GDALGetRasterBand(hSourceDS, 1)),
dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
dfDstYOff, dfDstXSize, dfDstYSize, 255, 0, VRT_NODATA_UNSET);
}
else if (bHasDatasetMask)
{
VRTSimpleSource *poSource;
if (bUseSrcMaskBand)
{
auto poComplexSource = new VRTComplexSource();
poComplexSource->SetUseMaskBand(true);
poSource = poComplexSource;
}
else
{
poSource = new VRTSimpleSource();
}
if (pszResampling)
poSource->SetResampling(pszResampling);
assert(poMaskVRTBand);
poMaskVRTBand->ConfigureSource(
poSource,
static_cast<GDALRasterBand *>(GDALGetRasterBand(hSourceDS, 1)),
TRUE, dfSrcXOff, dfSrcYOff, dfSrcXSize, dfSrcYSize, dfDstXOff,
dfDstYOff, dfDstXSize, dfDstYSize);
poMaskVRTBand->AddSource(poSource);
}
if (bDropRef)
{
GDALDereferenceDataset(hSourceDS);
}
}
for (int i : anIdxValidDatasets)
{
const DatasetProperty *psDatasetProperties = &asDatasetProperties[i];
for (auto oIter = anOverviewFactorsSet.begin();
oIter != anOverviewFactorsSet.end();)
{
const int nGlobalOvrFactor = *oIter;
auto oIterNext = oIter;
++oIterNext;
if (psDatasetProperties->nRasterXSize / nGlobalOvrFactor < 128 &&
psDatasetProperties->nRasterYSize / nGlobalOvrFactor < 128)
{
break;
}
if (std::find(psDatasetProperties->anOverviewFactors.begin(),
psDatasetProperties->anOverviewFactors.end(),
nGlobalOvrFactor) ==
psDatasetProperties->anOverviewFactors.end())
{
anOverviewFactorsSet.erase(oIter);
}
oIter = oIterNext;
}
}
if (!anOverviewFactorsSet.empty() &&
CPLTestBool(CPLGetConfigOption("VRT_VIRTUAL_OVERVIEWS", "YES")))
{
std::vector<int> anOverviewFactors;
anOverviewFactors.insert(anOverviewFactors.end(),
anOverviewFactorsSet.begin(),
anOverviewFactorsSet.end());
const char *const apszOptions[] = {"VRT_VIRTUAL_OVERVIEWS=YES",
nullptr};
poVRTDS->BuildOverviews(pszResampling ? pszResampling : "nearest",
static_cast<int>(anOverviewFactors.size()),
&anOverviewFactors[0], 0, nullptr, nullptr,
nullptr, apszOptions);
}
}
/************************************************************************/
/* Build() */
/************************************************************************/
GDALDataset *VRTBuilder::Build(GDALProgressFunc pfnProgress,
void *pProgressData)
{
if (bHasRunBuild)
return nullptr;
bHasRunBuild = TRUE;
if (pfnProgress == nullptr)
pfnProgress = GDALDummyProgress;
bUserExtent = (minX != 0 || minY != 0 || maxX != 0 || maxY != 0);
if (bUserExtent)
{
if (minX >= maxX || minY >= maxY)
{
CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user extent");
return nullptr;
}
}
if (resolutionStrategy == USER_RESOLUTION)
{
if (we_res <= 0 || ns_res <= 0)
{
CPLError(CE_Failure, CPLE_IllegalArg, "Invalid user resolution");
return nullptr;
}
/* We work with negative north-south resolution in all the following
* code */
ns_res = -ns_res;
}
else
{
we_res = ns_res = 0;
}
asDatasetProperties.resize(nInputFiles);
if (pszSrcNoData != nullptr)
{
if (EQUAL(pszSrcNoData, "none"))
{
bAllowSrcNoData = FALSE;
}
else
{
char **papszTokens = CSLTokenizeString(pszSrcNoData);
nSrcNoDataCount = CSLCount(papszTokens);
padfSrcNoData = static_cast<double *>(
CPLMalloc(sizeof(double) * nSrcNoDataCount));
for (int i = 0; i < nSrcNoDataCount; i++)
{
if (!ArgIsNumeric(papszTokens[i]) &&
!EQUAL(papszTokens[i], "nan") &&
!EQUAL(papszTokens[i], "-inf") &&
!EQUAL(papszTokens[i], "inf"))
{
CPLError(CE_Failure, CPLE_IllegalArg,
"Invalid -srcnodata value");
CSLDestroy(papszTokens);
return nullptr;
}
padfSrcNoData[i] = CPLAtofM(papszTokens[i]);
}
CSLDestroy(papszTokens);
}
}
if (pszVRTNoData != nullptr)
{
if (EQUAL(pszVRTNoData, "none"))
{
bAllowVRTNoData = FALSE;
}
else
{
char **papszTokens = CSLTokenizeString(pszVRTNoData);
nVRTNoDataCount = CSLCount(papszTokens);
padfVRTNoData = static_cast<double *>(
CPLMalloc(sizeof(double) * nVRTNoDataCount));
for (int i = 0; i < nVRTNoDataCount; i++)
{
if (!ArgIsNumeric(papszTokens[i]) &&
!EQUAL(papszTokens[i], "nan") &&
!EQUAL(papszTokens[i], "-inf") &&
!EQUAL(papszTokens[i], "inf"))
{
CPLError(CE_Failure, CPLE_IllegalArg,
"Invalid -vrtnodata value");
CSLDestroy(papszTokens);
return nullptr;
}
padfVRTNoData[i] = CPLAtofM(papszTokens[i]);
}
CSLDestroy(papszTokens);
}
}
bool bFoundValid = false;
for (int i = 0; ppszInputFilenames != nullptr && i < nInputFiles; i++)
{
const char *dsFileName = ppszInputFilenames[i];
if (!pfnProgress(1.0 * (i + 1) / nInputFiles, nullptr, pProgressData))
{
return nullptr;
}
GDALDatasetH hDS = (pahSrcDS)
? pahSrcDS[i]
: GDALOpenEx(dsFileName, GDAL_OF_RASTER, nullptr,
papszOpenOptions, nullptr);
asDatasetProperties[i].isFileOK = FALSE;
if (hDS)
{
const auto osErrorMsg = AnalyseRaster(hDS, &asDatasetProperties[i]);
if (osErrorMsg.empty())
{
asDatasetProperties[i].isFileOK = TRUE;
bFoundValid = true;
bFirst = FALSE;
}
if (pahSrcDS == nullptr)
GDALClose(hDS);
if (!osErrorMsg.empty() && osErrorMsg != "SILENTLY_IGNORE")
{
if (bStrict)
{
CPLError(CE_Failure, CPLE_AppDefined, "%s",
osErrorMsg.c_str());
return nullptr;
}
else
{
CPLError(CE_Warning, CPLE_AppDefined, "%s Skipping %s",
osErrorMsg.c_str(), dsFileName);
}
}
}
else
{
if (bStrict)
{
CPLError(CE_Failure, CPLE_AppDefined, "Can't open %s.",
dsFileName);
return nullptr;
}
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Can't open %s. Skipping it", dsFileName);
}
}
}
if (!bFoundValid)
return nullptr;
if (bHasGeoTransform)
{
if (bTargetAlignedPixels)
{
minX = floor(minX / we_res) * we_res;
maxX = ceil(maxX / we_res) * we_res;
minY = floor(minY / -ns_res) * -ns_res;
maxY = ceil(maxY / -ns_res) * -ns_res;
}
nRasterXSize = static_cast<int>(0.5 + (maxX - minX) / we_res);
nRasterYSize = static_cast<int>(0.5 + (maxY - minY) / -ns_res);
}
if (nRasterXSize == 0 || nRasterYSize == 0)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Computed VRT dimension is invalid. You've probably "
"specified inappropriate resolution.");
return nullptr;
}
VRTDatasetH hVRTDS = cpl::down_cast<VRTDataset *>(
VRTDataset::Create(pszOutputFilename, nRasterXSize, nRasterYSize, 0,
GDT_Unknown, aosCreateOptions.List()));
if (!hVRTDS)
{
return nullptr;
}
if (pszOutputSRS)
{
GDALSetProjection(hVRTDS, pszOutputSRS);
}
else if (pszProjectionRef)
{
GDALSetProjection(hVRTDS, pszProjectionRef);
}
if (bHasGeoTransform)
{
double adfGeoTransform[6];
adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX;
adfGeoTransform[GEOTRSFRM_WE_RES] = we_res;
adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0;
adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = maxY;
adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0;
adfGeoTransform[GEOTRSFRM_NS_RES] = ns_res;
GDALSetGeoTransform(hVRTDS, adfGeoTransform);
}
if (bSeparate)
{
CreateVRTSeparate(hVRTDS);
}
else
{
CreateVRTNonSeparate(hVRTDS);
}
return static_cast<GDALDataset *>(hVRTDS);
}
/************************************************************************/
/* add_file_to_list() */
/************************************************************************/
static bool add_file_to_list(const char *filename, const char *tile_index,
CPLStringList &aosList)
{
if (EQUAL(CPLGetExtension(filename), "SHP"))
{
/* Handle gdaltindex Shapefile as a special case */
auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(filename));
if (poDS == nullptr)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Unable to open shapefile `%s'.", filename);
return false;
}
auto poLayer = poDS->GetLayer(0);
const auto poFDefn = poLayer->GetLayerDefn();
if (poFDefn->GetFieldIndex("LOCATION") >= 0 &&
strcmp("LOCATION", tile_index) != 0)
{
CPLError(CE_Failure, CPLE_AppDefined,
"This shapefile seems to be a tile index of "
"OGR features and not GDAL products.");
}
const int ti_field = poFDefn->GetFieldIndex(tile_index);
if (ti_field < 0)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Unable to find field `%s' in DBF file `%s'.", tile_index,
filename);
return false;
}
/* Load in memory existing file names in SHP */
const auto nTileIndexFiles = poLayer->GetFeatureCount(TRUE);
if (nTileIndexFiles == 0)
{
CPLError(CE_Warning, CPLE_AppDefined,
"Tile index %s is empty. Skipping it.", filename);
return true;
}
if (nTileIndexFiles > 100 * 1024 * 1024)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Too large feature count in tile index");
return false;
}
for (auto &&poFeature : poLayer)
{
aosList.AddString(poFeature->GetFieldAsString(ti_field));
}
}
else
{
aosList.AddString(filename);
}
return true;
}
/************************************************************************/
/* GDALBuildVRTOptions */
/************************************************************************/
/** Options for use with GDALBuildVRT(). GDALBuildVRTOptions* must be allocated
* and freed with GDALBuildVRTOptionsNew() and GDALBuildVRTOptionsFree()
* respectively.
*/
struct GDALBuildVRTOptions
{
std::string osTileIndex = "location";
bool bStrict = false;
std::string osResolution{};
bool bSeparate = false;
bool bAllowProjectionDifference = false;
double we_res = 0;
double ns_res = 0;
bool bTargetAlignedPixels = false;
double xmin = 0;
double ymin = 0;
double xmax = 0;
double ymax = 0;
bool bAddAlpha = false;
bool bHideNoData = false;
int nSubdataset = -1;
std::string osSrcNoData{};
std::string osVRTNoData{};
std::string osOutputSRS{};
std::vector<int> anSelectedBandList{};
std::string osResampling{};
CPLStringList aosOpenOptions{};
CPLStringList aosCreateOptions{};
bool bUseSrcMaskBand = true;
bool bNoDataFromMask = false;
double dfMaskValueThreshold = 0;
/*! allow or suppress progress monitor and other non-error output */
bool bQuiet = true;
/*! the progress function to use */
GDALProgressFunc pfnProgress = GDALDummyProgress;
/*! pointer to the progress data variable */
void *pProgressData = nullptr;
};
/************************************************************************/
/* GDALBuildVRT() */
/************************************************************************/
/* clang-format off */
/**
* Build a VRT from a list of datasets.
*
* This is the equivalent of the
* <a href="/programs/gdalbuildvrt.html">gdalbuildvrt</a> utility.
*
* GDALBuildVRTOptions* must be allocated and freed with
* GDALBuildVRTOptionsNew() and GDALBuildVRTOptionsFree() respectively. pahSrcDS
* and papszSrcDSNames cannot be used at the same time.
*
* @param pszDest the destination dataset path.
* @param nSrcCount the number of input datasets.
* @param pahSrcDS the list of input datasets (or NULL, exclusive with
* papszSrcDSNames). For practical purposes, the type
* of this argument should be considered as "const GDALDatasetH* const*", that
* is neither the array nor its values are mutated by this function.
* @param papszSrcDSNames the list of input dataset names (or NULL, exclusive
* with pahSrcDS)
* @param psOptionsIn the options struct returned by GDALBuildVRTOptionsNew() or
* NULL.
* @param pbUsageError pointer to a integer output variable to store if any
* usage error has occurred.
* @return the output dataset (new dataset that must be closed using
* GDALClose()) or NULL in case of error. If using pahSrcDS, the returned VRT
* dataset has a reference to each pahSrcDS[] element. Hence pahSrcDS[] elements
* should be closed after the returned dataset if using GDALClose().
* A safer alternative is to use GDALReleaseDataset() instead of using
* GDALClose(), in which case you can close datasets in any order.
*
* @since GDAL 2.1
*/
/* clang-format on */
GDALDatasetH GDALBuildVRT(const char *pszDest, int nSrcCount,
GDALDatasetH *pahSrcDS,
const char *const *papszSrcDSNames,
const GDALBuildVRTOptions *psOptionsIn,
int *pbUsageError)
{
if (pszDest == nullptr)
pszDest = "";
if (nSrcCount == 0)
{
CPLError(CE_Failure, CPLE_AppDefined, "No input dataset specified.");
if (pbUsageError)
*pbUsageError = TRUE;
return nullptr;
}
// cppcheck-suppress unreadVariable
GDALBuildVRTOptions sOptions(psOptionsIn ? *psOptionsIn
: GDALBuildVRTOptions());
if (sOptions.we_res != 0 && sOptions.ns_res != 0 &&
!sOptions.osResolution.empty() &&
!EQUAL(sOptions.osResolution.c_str(), "user"))
{
CPLError(CE_Failure, CPLE_NotSupported,
"-tr option is not compatible with -resolution %s",
sOptions.osResolution.c_str());
if (pbUsageError)
*pbUsageError = TRUE;
return nullptr;
}
if (sOptions.bTargetAlignedPixels && sOptions.we_res == 0 &&
sOptions.ns_res == 0)
{
CPLError(CE_Failure, CPLE_NotSupported,
"-tap option cannot be used without using -tr");
if (pbUsageError)
*pbUsageError = TRUE;
return nullptr;
}
if (sOptions.bAddAlpha && sOptions.bSeparate)
{
CPLError(CE_Failure, CPLE_NotSupported,
"-addalpha option is not compatible with -separate.");
if (pbUsageError)
*pbUsageError = TRUE;
return nullptr;
}
ResolutionStrategy eStrategy = AVERAGE_RESOLUTION;
if (sOptions.osResolution.empty() ||
EQUAL(sOptions.osResolution.c_str(), "user"))
{
if (sOptions.we_res != 0 || sOptions.ns_res != 0)
eStrategy = USER_RESOLUTION;
else if (EQUAL(sOptions.osResolution.c_str(), "user"))
{
CPLError(CE_Failure, CPLE_NotSupported,
"-tr option must be used with -resolution user.");
if (pbUsageError)
*pbUsageError = TRUE;
return nullptr;
}
}
else if (EQUAL(sOptions.osResolution.c_str(), "average"))
eStrategy = AVERAGE_RESOLUTION;
else if (EQUAL(sOptions.osResolution.c_str(), "highest"))
eStrategy = HIGHEST_RESOLUTION;
else if (EQUAL(sOptions.osResolution.c_str(), "lowest"))
eStrategy = LOWEST_RESOLUTION;
/* If -srcnodata is specified, use it as the -vrtnodata if the latter is not
*/
/* specified */
if (!sOptions.osSrcNoData.empty() && sOptions.osVRTNoData.empty())
sOptions.osVRTNoData = sOptions.osSrcNoData;
VRTBuilder oBuilder(
sOptions.bStrict, pszDest, nSrcCount, papszSrcDSNames, pahSrcDS,
sOptions.anSelectedBandList.empty()
? nullptr
: sOptions.anSelectedBandList.data(),
static_cast<int>(sOptions.anSelectedBandList.size()), eStrategy,
sOptions.we_res, sOptions.ns_res, sOptions.bTargetAlignedPixels,
sOptions.xmin, sOptions.ymin, sOptions.xmax, sOptions.ymax,
sOptions.bSeparate, sOptions.bAllowProjectionDifference,
sOptions.bAddAlpha, sOptions.bHideNoData, sOptions.nSubdataset,
sOptions.osSrcNoData.empty() ? nullptr : sOptions.osSrcNoData.c_str(),
sOptions.osVRTNoData.empty() ? nullptr : sOptions.osVRTNoData.c_str(),
sOptions.bUseSrcMaskBand, sOptions.bNoDataFromMask,
sOptions.dfMaskValueThreshold,
sOptions.osOutputSRS.empty() ? nullptr : sOptions.osOutputSRS.c_str(),
sOptions.osResampling.empty() ? nullptr : sOptions.osResampling.c_str(),
sOptions.aosOpenOptions.List(), sOptions.aosCreateOptions);
return GDALDataset::ToHandle(
oBuilder.Build(sOptions.pfnProgress, sOptions.pProgressData));
}
/************************************************************************/
/* SanitizeSRS */
/************************************************************************/
static char *SanitizeSRS(const char *pszUserInput)
{
OGRSpatialReferenceH hSRS;
char *pszResult = nullptr;
CPLErrorReset();
hSRS = OSRNewSpatialReference(nullptr);
if (OSRSetFromUserInput(hSRS, pszUserInput) == OGRERR_NONE)
OSRExportToWkt(hSRS, &pszResult);
else
{
CPLError(CE_Failure, CPLE_AppDefined, "Translating SRS failed:\n%s",
pszUserInput);
}
OSRDestroySpatialReference(hSRS);
return pszResult;
}
/************************************************************************/
/* GDALBuildVRTOptionsGetParser() */
/************************************************************************/
static std::unique_ptr<GDALArgumentParser>
GDALBuildVRTOptionsGetParser(GDALBuildVRTOptions *psOptions,
GDALBuildVRTOptionsForBinary *psOptionsForBinary)
{
auto argParser = std::make_unique<GDALArgumentParser>(
"gdalbuildvrt", /* bForBinary=*/psOptionsForBinary != nullptr);
argParser->add_description(_("Builds a VRT from a list of datasets."));
argParser->add_epilog(_(
"\n"
"e.g.\n"
" % gdalbuildvrt doq_index.vrt doq/*.tif\n"
" % gdalbuildvrt -input_file_list my_list.txt doq_index.vrt\n"
"\n"
"NOTES:\n"
" o With -separate, each files goes into a separate band in the VRT "
"band.\n"
" Otherwise, the files are considered as tiles of a larger mosaic.\n"
" o -b option selects a band to add into vrt. Multiple bands can be "
"listed.\n"
" By default all bands are queried.\n"
" o The default tile index field is 'location' unless otherwise "
"specified by\n"
" -tileindex.\n"
" o In case the resolution of all input files is not the same, the "
"-resolution\n"
" flag enable the user to control the way the output resolution is "
"computed.\n"
" Average is the default.\n"
" o Input files may be any valid GDAL dataset or a GDAL raster tile "
"index.\n"
" o For a GDAL raster tile index, all entries will be added to the "
"VRT.\n"
" o If one GDAL dataset is made of several subdatasets and has 0 "
"raster bands,\n"
" its datasets will be added to the VRT rather than the dataset "
"itself.\n"
" Single subdataset could be selected by its number using the -sd "
"option.\n"
" o By default, only datasets of same projection and band "
"characteristics\n"
" may be added to the VRT.\n"
"\n"
"For more details, consult "
"https://gdal.org/programs/gdalbuildvrt.html"));
argParser->add_quiet_argument(
psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr);
{
auto &group = argParser->add_mutually_exclusive_group();
group.add_argument("-strict")
.flag()
.store_into(psOptions->bStrict)
.help(_("Turn warnings as failures."));
group.add_argument("-non_strict")
.flag()
.action([psOptions](const std::string &)
{ psOptions->bStrict = false; })
.help(_("Skip source datasets that have issues with warnings, and "
"continue processing."));
}
argParser->add_argument("-tile_index")
.metavar("<field_name>")
.store_into(psOptions->osTileIndex)
.help(_("Use the specified value as the tile index field, instead of "
"the default value which is 'location'."));
argParser->add_argument("-resolution")
.metavar("user|average|highest|lowest")
.action(
[psOptions](const std::string &s)
{
psOptions->osResolution = s;
if (!EQUAL(psOptions->osResolution.c_str(), "user") &&
!EQUAL(psOptions->osResolution.c_str(), "average") &&
!EQUAL(psOptions->osResolution.c_str(), "highest") &&
!EQUAL(psOptions->osResolution.c_str(), "lowest"))
{
throw std::invalid_argument(
CPLSPrintf("Illegal resolution value (%s).",
psOptions->osResolution.c_str()));
}
})
.help(_("Control the way the output resolution is computed."));
argParser->add_argument("-tr")
.metavar("<xres> <yes>")
.nargs(2)
.scan<'g', double>()
.help(_("Set target resolution."));
if (psOptionsForBinary)
{
argParser->add_argument("-input_file_list")
.metavar("<filename>")
.action(
[psOptions, psOptionsForBinary](const std::string &s)
{
const char *input_file_list = s.c_str();
auto f = VSIVirtualHandleUniquePtr(
VSIFOpenL(input_file_list, "r"));
if (f)
{
while (1)
{
const char *filename = CPLReadLineL(f.get());
if (filename == nullptr)
break;
if (!add_file_to_list(
filename, psOptions->osTileIndex.c_str(),
psOptionsForBinary->aosSrcFiles))
{
throw std::invalid_argument(
std::string("Cannot add ")
.append(filename)
.append(" to input file list"));
}
}
}
})
.help(_("Text file with an input filename on each line"));
}
argParser->add_argument("-separate")
.flag()
.store_into(psOptions->bSeparate)
.help(_("Place each input file into a separate band."));
argParser->add_argument("-allow_projection_difference")
.flag()
.store_into(psOptions->bAllowProjectionDifference)
.help(_("Accept source files not in the same projection (but without "
"reprojecting them!)."));
argParser->add_argument("-sd")
.metavar("<n>")
.store_into(psOptions->nSubdataset)
.help(_("Use subdataset of specified index (starting at 1), instead of "
"the source dataset itself."));
argParser->add_argument("-tap")
.flag()
.store_into(psOptions->bTargetAlignedPixels)
.help(_("Align the coordinates of the extent of the output file to the "
"values of the resolution."));
argParser->add_argument("-te")
.metavar("<xmin> <ymin> <xmax> <ymax>")
.nargs(4)
.scan<'g', double>()
.help(_("Set georeferenced extents of output file to be created."));
argParser->add_argument("-addalpha")
.flag()
.store_into(psOptions->bAddAlpha)
.help(_("Adds an alpha mask band to the VRT when the source raster "
"have none."));
argParser->add_argument("-b")
.metavar("<band>")
.append()
.store_into(psOptions->anSelectedBandList)
.help(_("Specify input band(s) number."));
argParser->add_argument("-hidenodata")
.flag()
.store_into(psOptions->bHideNoData)
.help(_("Makes the VRT band not report the NoData."));
if (psOptionsForBinary)
{
argParser->add_argument("-overwrite")
.flag()
.store_into(psOptionsForBinary->bOverwrite)
.help(_("Overwrite the VRT if it already exists."));
}
argParser->add_argument("-srcnodata")
.metavar("\"<value>[ <value>]...\"")
.store_into(psOptions->osSrcNoData)
.help(_("Set nodata values for input bands."));
argParser->add_argument("-vrtnodata")
.metavar("\"<value>[ <value>]...\"")
.store_into(psOptions->osVRTNoData)
.help(_("Set nodata values at the VRT band level."));
argParser->add_argument("-a_srs")
.metavar("<srs_def>")
.action(
[psOptions](const std::string &s)
{
char *pszSRS = SanitizeSRS(s.c_str());
if (pszSRS == nullptr)
{
throw std::invalid_argument("Invalid value for -a_srs");
}
psOptions->osOutputSRS = pszSRS;
CPLFree(pszSRS);
})
.help(_("Override the projection for the output file.."));
argParser->add_argument("-r")
.metavar("nearest|bilinear|cubic|cubicspline|lanczos|average|mode")
.store_into(psOptions->osResampling)
.help(_("Resampling algorithm."));
argParser->add_open_options_argument(&psOptions->aosOpenOptions);
argParser->add_creation_options_argument(psOptions->aosCreateOptions);
argParser->add_argument("-ignore_srcmaskband")
.flag()
.action([psOptions](const std::string &)
{ psOptions->bUseSrcMaskBand = false; })
.help(_("Cause mask band of sources will not be taken into account."));
argParser->add_argument("-nodata_max_mask_threshold")
.metavar("<threshold>")
.scan<'g', double>()
.action(
[psOptions](const std::string &s)
{
psOptions->bNoDataFromMask = true;
psOptions->dfMaskValueThreshold = CPLAtofM(s.c_str());
})
.help(_("Replaces the value of the source with the value of -vrtnodata "
"when the value of the mask band of the source is less or "
"equal to the threshold."));
if (psOptionsForBinary)
{
if (psOptionsForBinary->osDstFilename.empty())
{
// We normally go here, unless undocumented -o switch is used
argParser->add_argument("vrt_dataset_name")
.metavar("<vrt_dataset_name>")
.store_into(psOptionsForBinary->osDstFilename)
.help(_("Output VRT."));
}
argParser->add_argument("src_dataset_name")
.metavar("<src_dataset_name>")
.nargs(argparse::nargs_pattern::any)
.action(
[psOptions, psOptionsForBinary](const std::string &s)
{
if (!add_file_to_list(s.c_str(),
psOptions->osTileIndex.c_str(),
psOptionsForBinary->aosSrcFiles))
{
throw std::invalid_argument(
std::string("Cannot add ")
.append(s)
.append(" to input file list"));
}
})
.help(_("Input dataset(s)."));
}
return argParser;
}
/************************************************************************/
/* GDALBuildVRTGetParserUsage() */
/************************************************************************/
std::string GDALBuildVRTGetParserUsage()
{
try
{
GDALBuildVRTOptions sOptions;
GDALBuildVRTOptionsForBinary sOptionsForBinary;
auto argParser =
GDALBuildVRTOptionsGetParser(&sOptions, &sOptionsForBinary);
return argParser->usage();
}
catch (const std::exception &err)
{
CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
err.what());
return std::string();
}
}
/************************************************************************/
/* GDALBuildVRTOptionsNew() */
/************************************************************************/
/**
* Allocates a GDALBuildVRTOptions 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/gdalbuildvrt.html">gdalbuildvrt</a> utility.
* @param psOptionsForBinary (output) may be NULL (and should generally be
* NULL), otherwise (gdalbuildvrt_bin.cpp use case) must be allocated with
* GDALBuildVRTOptionsForBinaryNew() prior to this function. Will be filled
* with potentially present filename, open options,...
* @return pointer to the allocated GDALBuildVRTOptions struct. Must be freed
* with GDALBuildVRTOptionsFree().
*
* @since GDAL 2.1
*/
GDALBuildVRTOptions *
GDALBuildVRTOptionsNew(char **papszArgv,
GDALBuildVRTOptionsForBinary *psOptionsForBinary)
{
auto psOptions = std::make_unique<GDALBuildVRTOptions>();
CPLStringList aosArgv;
const int nArgc = CSLCount(papszArgv);
for (int i = 0;
i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++)
{
if (psOptionsForBinary && EQUAL(papszArgv[i], "-o") && i + 1 < nArgc &&
papszArgv[i + 1] != nullptr)
{
// Undocumented alternate way of specifying the destination file
psOptionsForBinary->osDstFilename = papszArgv[i + 1];
++i;
}
// argparser will be confused if the value of a string argument
// starts with a negative sign.
else if (EQUAL(papszArgv[i], "-srcnodata") && i + 1 < nArgc)
{
++i;
psOptions->osSrcNoData = papszArgv[i];
}
// argparser will be confused if the value of a string argument
// starts with a negative sign.
else if (EQUAL(papszArgv[i], "-vrtnodata") && i + 1 < nArgc)
{
++i;
psOptions->osVRTNoData = papszArgv[i];
}
else
{
aosArgv.AddString(papszArgv[i]);
}
}
try
{
auto argParser =
GDALBuildVRTOptionsGetParser(psOptions.get(), psOptionsForBinary);
argParser->parse_args_without_binary_name(aosArgv.List());
if (auto adfTargetRes = argParser->present<std::vector<double>>("-tr"))
{
psOptions->we_res = (*adfTargetRes)[0];
psOptions->ns_res = (*adfTargetRes)[1];
}
if (auto oTE = argParser->present<std::vector<double>>("-te"))
{
psOptions->xmin = (*oTE)[0];
psOptions->ymin = (*oTE)[1];
psOptions->xmax = (*oTE)[2];
psOptions->ymax = (*oTE)[3];
}
return psOptions.release();
}
catch (const std::exception &err)
{
CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what());
return nullptr;
}
}
/************************************************************************/
/* GDALBuildVRTOptionsFree() */
/************************************************************************/
/**
* Frees the GDALBuildVRTOptions struct.
*
* @param psOptions the options struct for GDALBuildVRT().
*
* @since GDAL 2.1
*/
void GDALBuildVRTOptionsFree(GDALBuildVRTOptions *psOptions)
{
delete psOptions;
}
/************************************************************************/
/* GDALBuildVRTOptionsSetProgress() */
/************************************************************************/
/**
* Set a progress function.
*
* @param psOptions the options struct for GDALBuildVRT().
* @param pfnProgress the progress callback.
* @param pProgressData the user data for the progress callback.
*
* @since GDAL 2.1
*/
void GDALBuildVRTOptionsSetProgress(GDALBuildVRTOptions *psOptions,
GDALProgressFunc pfnProgress,
void *pProgressData)
{
psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
psOptions->pProgressData = pProgressData;
if (pfnProgress == GDALTermProgress)
psOptions->bQuiet = false;
}