Implement RFC 104: Adding a "gdal" front-end command line interface
This commit is contained in:
parent
7ab4e4fa46
commit
ef8d0f6baa
|
@ -6,6 +6,21 @@ add_library(
|
|||
commonutils.h
|
||||
gdal_utils.h
|
||||
gdalargumentparser.cpp
|
||||
gdalalg_convert.cpp
|
||||
gdalalg_info.cpp
|
||||
gdalalg_main.cpp
|
||||
gdalalg_pipeline.cpp
|
||||
gdalalg_raster.cpp
|
||||
gdalalg_raster_info.cpp
|
||||
gdalalg_raster_convert.cpp
|
||||
gdalalg_vector.cpp
|
||||
gdalalg_vector_info.cpp
|
||||
gdalalg_vector_convert.cpp
|
||||
gdalalg_vector_pipeline.cpp
|
||||
gdalalg_vector_read.cpp
|
||||
gdalalg_vector_filter.cpp
|
||||
gdalalg_vector_reproject.cpp
|
||||
gdalalg_vector_write.cpp
|
||||
gdalinfo_lib.cpp
|
||||
gdalbuildvrt_lib.cpp
|
||||
gdal_grid_lib.cpp
|
||||
|
@ -49,6 +64,7 @@ target_public_header(TARGET ${GDAL_LIB_TARGET_NAME} HEADERS gdal_utils.h)
|
|||
|
||||
if (BUILD_APPS)
|
||||
# Default Apps
|
||||
add_executable(gdal gdal_utils_priv.h gdal.cpp)
|
||||
add_executable(gdalinfo gdal_utils_priv.h gdalinfo_bin.cpp)
|
||||
add_executable(gdalbuildvrt gdal_utils_priv.h gdalbuildvrt_bin.cpp)
|
||||
add_executable(gdaladdo gdal_utils_priv.h gdaladdo.cpp)
|
||||
|
@ -89,6 +105,7 @@ if (BUILD_APPS)
|
|||
add_dependencies(utils_common generate_gdal_version_h)
|
||||
|
||||
set(APPS_TARGETS
|
||||
gdal
|
||||
gdalinfo
|
||||
gdalbuildvrt
|
||||
gdaladdo
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: CLI front-end
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
#include "commonutils.h"
|
||||
|
||||
#include "gdal.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
/************************************************************************/
|
||||
/* main() */
|
||||
/************************************************************************/
|
||||
|
||||
MAIN_START(argc, argv)
|
||||
{
|
||||
EarlySetConfigOptions(argc, argv);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Register standard GDAL drivers, and process generic GDAL */
|
||||
/* command options. */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
GDALAllRegister();
|
||||
argc = GDALGeneralCmdLineProcessor(
|
||||
argc, &argv, GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
|
||||
if (argc < 1)
|
||||
return (-argc);
|
||||
|
||||
auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate(
|
||||
GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME);
|
||||
assert(alg);
|
||||
alg->SetCallPath(std::vector<std::string>{argv[0]});
|
||||
std::vector<std::string> args;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
args.push_back(argv[i]);
|
||||
|
||||
if (!alg->ParseCommandLineArguments(args))
|
||||
{
|
||||
fprintf(stderr, "%s", alg->GetUsageForCLI(true).c_str());
|
||||
CSLDestroy(argv);
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
const auto stdoutArg = alg->GetActualAlgorithm().GetArg("stdout");
|
||||
if (stdoutArg && stdoutArg->GetType() == GAAT_BOOLEAN)
|
||||
stdoutArg->Set(true);
|
||||
}
|
||||
|
||||
GDALProgressFunc pfnProgress =
|
||||
alg->IsProgressBarRequested() ? GDALTermProgress : nullptr;
|
||||
void *pProgressData = nullptr;
|
||||
|
||||
int ret = 0;
|
||||
if (alg->Run(pfnProgress, pProgressData) && alg->Finalize())
|
||||
{
|
||||
const auto outputArg =
|
||||
alg->GetActualAlgorithm().GetArg("output-string");
|
||||
if (outputArg && outputArg->GetType() == GAAT_STRING &&
|
||||
outputArg->IsOutput())
|
||||
{
|
||||
printf("%s", outputArg->Get<std::string>().c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
CSLDestroy(argv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MAIN_END
|
|
@ -301,6 +301,9 @@ struct GDALTranslateOptions
|
|||
/*! overview level of source file to be used */
|
||||
int nOvLevel = OVR_LEVEL_AUTO;
|
||||
|
||||
/*! set to true to prevent overwriting existing dataset */
|
||||
bool bNoOverwrite = false;
|
||||
|
||||
GDALTranslateOptions() = default;
|
||||
~GDALTranslateOptions();
|
||||
GDALTranslateOptions *Clone() const;
|
||||
|
@ -1106,6 +1109,29 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
|
|||
"@QUIET_DELETE_ON_CREATE_COPY", "NO");
|
||||
}
|
||||
|
||||
if (psOptions->bNoOverwrite && !EQUAL(pszDest, ""))
|
||||
{
|
||||
VSIStatBufL sStat;
|
||||
if (VSIStatL(pszDest, &sStat) == 0)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"File '%s' already exists. Specify the --overwrite "
|
||||
"option to overwrite it.",
|
||||
pszDest);
|
||||
GDALTranslateOptionsFree(psOptions);
|
||||
return nullptr;
|
||||
}
|
||||
else if (std::unique_ptr<GDALDataset>(GDALDataset::Open(pszDest)))
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Dataset '%s' already exists. Specify the --overwrite "
|
||||
"option to overwrite it.",
|
||||
pszDest);
|
||||
GDALTranslateOptionsFree(psOptions);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GDALDriver::FromHandle(hDriver)->QuietDeleteForCreateCopy(pszDest,
|
||||
poSrcDS);
|
||||
|
||||
|
@ -3127,6 +3153,11 @@ GDALTranslateOptionsGetParser(GDALTranslateOptions *psOptions,
|
|||
.hidden()
|
||||
.store_into(psOptions->nLimitOutSize);
|
||||
|
||||
// Undocumented option used by gdal raster convert
|
||||
argParser->add_argument("--no-overwrite")
|
||||
.store_into(psOptions->bNoOverwrite)
|
||||
.hidden();
|
||||
|
||||
if (psOptionsForBinary)
|
||||
{
|
||||
argParser->add_argument("input_file")
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "convert" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "cpl_error.h"
|
||||
#include "gdalalgorithm.h"
|
||||
#include "gdalalg_raster_convert.h"
|
||||
#include "gdalalg_vector_convert.h"
|
||||
#include "gdalalg_dispatcher.h"
|
||||
#include "gdal_priv.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALConvertAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALConvertAlgorithm
|
||||
: public GDALDispatcherAlgorithm<GDALRasterConvertAlgorithm,
|
||||
GDALVectorConvertAlgorithm>
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "convert";
|
||||
static constexpr const char *DESCRIPTION =
|
||||
"Convert a dataset (shortcut for 'gdal raster convert' or "
|
||||
"'gdal vector convert').";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALConvertAlgorithm()
|
||||
: GDALDispatcherAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
// only for the help message
|
||||
AddProgressArg();
|
||||
AddOutputFormatArg(&m_format);
|
||||
AddInputDatasetArg(&m_inputDataset);
|
||||
AddOutputDatasetArg(&m_outputDataset);
|
||||
|
||||
m_longDescription = "For all options, run 'gdal raster convert --help' "
|
||||
"or 'gdal vector convert --help'";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_format{};
|
||||
GDALArgDatasetValue m_inputDataset{};
|
||||
GDALArgDatasetValue m_outputDataset{};
|
||||
};
|
||||
|
||||
GDAL_STATIC_REGISTER_ALG(GDALConvertAlgorithm);
|
|
@ -0,0 +1,238 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal subcommand dispatcher
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_DISPATCHER_INCLUDED
|
||||
#define GDALALG_DISPATCHER_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
#include "gdal_priv.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALDispatcherAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
template <class RasterDispatcher, class VectorDispatcher>
|
||||
class GDALDispatcherAlgorithm : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
GDALDispatcherAlgorithm(const std::string &name,
|
||||
const std::string &description,
|
||||
const std::string &helpURL)
|
||||
: GDALAlgorithm(name, description, helpURL),
|
||||
m_rasterDispatcher(std::make_unique<RasterDispatcher>(
|
||||
/* openForMixedRasterVector = */ true)),
|
||||
m_vectorDispatcher(std::make_unique<VectorDispatcher>())
|
||||
{
|
||||
// A "info" dispacher command is a shortcut for something like
|
||||
// "raster info", "vector info". Best to expose the latter.
|
||||
SetDisplayInJSONUsage(false);
|
||||
}
|
||||
|
||||
bool
|
||||
ParseCommandLineArguments(const std::vector<std::string> &args) override;
|
||||
|
||||
std::string GetUsageForCLI(bool shortUsage,
|
||||
const UsageOptions &usageOptions) const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<RasterDispatcher> m_rasterDispatcher{};
|
||||
std::unique_ptr<VectorDispatcher> m_vectorDispatcher{};
|
||||
bool m_showUsage = true;
|
||||
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"The Run() method should not be called directly on the \"gdal "
|
||||
"%s\" program.",
|
||||
GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALDispatcherAlgorithm::ParseCommandLineArguments() */
|
||||
/************************************************************************/
|
||||
|
||||
template <class RasterDispatcher, class VectorDispatcher>
|
||||
bool GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::
|
||||
ParseCommandLineArguments(const std::vector<std::string> &args)
|
||||
{
|
||||
bool ok;
|
||||
{
|
||||
std::unique_ptr<CPLErrorStateBackuper> oErrorHandler;
|
||||
if (args.size() > 1)
|
||||
{
|
||||
oErrorHandler =
|
||||
std::make_unique<CPLErrorStateBackuper>(CPLQuietErrorHandler);
|
||||
}
|
||||
ok = m_rasterDispatcher->ParseCommandLineArguments(args);
|
||||
CPL_IGNORE_RET_VAL(oErrorHandler);
|
||||
}
|
||||
|
||||
if (m_rasterDispatcher->PropagateSpecialActionTo(this))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
auto poDS = m_rasterDispatcher->GetDataset();
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (poDS &&
|
||||
(poDS->GetRasterCount() > 0 || poDS->GetMetadata("SUBDATASETS")))
|
||||
{
|
||||
if (poDS->GetLayerCount() != 0)
|
||||
{
|
||||
m_showUsage = false;
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"'%s' has both raster and vector content. "
|
||||
"Please use 'gdal raster %s' or 'gdal vector %s'.",
|
||||
poDS->GetDescription(), GetName().c_str(),
|
||||
GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_selectedSubAlg = m_rasterDispatcher.get();
|
||||
std::vector<std::string> callPath(m_callPath);
|
||||
callPath.push_back("raster");
|
||||
m_selectedSubAlg->SetCallPath(callPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (args.size() <= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto poDSFromRaster = m_rasterDispatcher->GetDataset();
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
if (poDSFromRaster)
|
||||
{
|
||||
m_vectorDispatcher->SetDataset(poDSFromRaster, false);
|
||||
}
|
||||
|
||||
std::vector<std::string> argsWithoutInput;
|
||||
bool skipNext = false;
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
if (arg == "-i" || arg == "--input")
|
||||
{
|
||||
skipNext = true;
|
||||
}
|
||||
else if (!skipNext)
|
||||
{
|
||||
if (!STARTS_WITH(arg.c_str(), "--input=") &&
|
||||
!(poDSFromRaster && arg == poDSFromRaster->GetDescription()))
|
||||
{
|
||||
argsWithoutInput.push_back(arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
skipNext = false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
|
||||
ok = m_vectorDispatcher->ParseCommandLineArguments(argsWithoutInput);
|
||||
}
|
||||
if (ok)
|
||||
{
|
||||
m_selectedSubAlg = m_vectorDispatcher.get();
|
||||
std::vector<std::string> callPath(m_callPath);
|
||||
callPath.push_back("vector");
|
||||
m_selectedSubAlg->SetCallPath(callPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
VSIStatBufL sStat;
|
||||
if (VSIStatL(arg.c_str(), &sStat) == 0)
|
||||
{
|
||||
auto poDS =
|
||||
std::unique_ptr<GDALDataset>(GDALDataset::Open(arg.c_str()));
|
||||
if (poDS)
|
||||
{
|
||||
if (poDS->GetRasterCount() > 0 ||
|
||||
poDS->GetMetadata("SUBDATASETS"))
|
||||
{
|
||||
if (poDS->GetLayerCount() != 0)
|
||||
{
|
||||
m_showUsage = false;
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"'%s' has both raster and vector content. "
|
||||
"Please use 'gdal raster %s' or 'gdal "
|
||||
"vector %s'.",
|
||||
poDS->GetDescription(), GetName().c_str(),
|
||||
GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
m_rasterDispatcher = std::make_unique<RasterDispatcher>();
|
||||
m_rasterDispatcher->SetDataset(poDS.release(), true);
|
||||
m_selectedSubAlg = m_rasterDispatcher.get();
|
||||
std::vector<std::string> callPath(m_callPath);
|
||||
callPath.push_back("raster");
|
||||
m_selectedSubAlg->SetCallPath(callPath);
|
||||
ret = m_selectedSubAlg->ParseCommandLineArguments(
|
||||
argsWithoutInput);
|
||||
break;
|
||||
}
|
||||
else if (poDS->GetLayerCount() != 0)
|
||||
{
|
||||
m_vectorDispatcher = std::make_unique<VectorDispatcher>();
|
||||
m_vectorDispatcher->SetDataset(poDS.release(), true);
|
||||
m_selectedSubAlg = m_vectorDispatcher.get();
|
||||
std::vector<std::string> callPath(m_callPath);
|
||||
callPath.push_back("vector");
|
||||
m_selectedSubAlg->SetCallPath(callPath);
|
||||
ret = m_selectedSubAlg->ParseCommandLineArguments(
|
||||
argsWithoutInput);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALDispatcherAlgorithm::GetUsageForCLI() */
|
||||
/************************************************************************/
|
||||
|
||||
template <class RasterDispatcher, class VectorDispatcher>
|
||||
std::string
|
||||
GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::GetUsageForCLI(
|
||||
bool shortUsage, const UsageOptions &usageOptions) const
|
||||
{
|
||||
if (m_selectedSubAlg)
|
||||
{
|
||||
return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
|
||||
}
|
||||
if (m_showUsage)
|
||||
{
|
||||
return GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif // GDALALG_DISPATCHER_INCLUDED
|
|
@ -0,0 +1,67 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "info" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "cpl_error.h"
|
||||
#include "gdalalgorithm.h"
|
||||
#include "gdalalg_raster_info.h"
|
||||
#include "gdalalg_vector_info.h"
|
||||
#include "gdalalg_dispatcher.h"
|
||||
#include "gdal_priv.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALInfoAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALInfoAlgorithm final
|
||||
: public GDALDispatcherAlgorithm<GDALRasterInfoAlgorithm,
|
||||
GDALVectorInfoAlgorithm>
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "info";
|
||||
static constexpr const char *DESCRIPTION =
|
||||
"Return information on a dataset (shortcut for 'gdal raster info' or "
|
||||
"'gdal vector info').";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALInfoAlgorithm() : GDALDispatcherAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
// only for the help message
|
||||
AddOutputFormatArg(&m_format).SetDefault("json").SetChoices("json",
|
||||
"text");
|
||||
AddInputDatasetArg(&m_dataset);
|
||||
|
||||
m_longDescription = "For all options, run 'gdal raster info --help' or "
|
||||
"'gdal vector info --help'";
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<GDALRasterInfoAlgorithm> m_rasterInfo{};
|
||||
std::unique_ptr<GDALVectorInfoAlgorithm> m_vectorInfo{};
|
||||
|
||||
std::string m_format{};
|
||||
GDALArgDatasetValue m_dataset{};
|
||||
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"The Run() method should not be called directly on the \"gdal "
|
||||
"info\" program.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
GDAL_STATIC_REGISTER_ALG(GDALInfoAlgorithm);
|
|
@ -0,0 +1,123 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "main" command
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_main.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALMainAlgorithm::GDALMainAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALMainAlgorithm::GDALMainAlgorithm()
|
||||
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
for (const std::string &subAlgName :
|
||||
GDALGlobalAlgorithmRegistry::GetSingleton().GetNames())
|
||||
{
|
||||
const auto pInfo =
|
||||
GDALGlobalAlgorithmRegistry::GetSingleton().GetInfo(subAlgName);
|
||||
if (pInfo)
|
||||
RegisterSubAlgorithm(*pInfo);
|
||||
}
|
||||
|
||||
m_longDescription = "'gdal <FILENAME>' can also be used as a shortcut for "
|
||||
"'gdal info <FILENAME>'.\n"
|
||||
"And 'gdal read <FILENAME> ! ...' as a shortcut for "
|
||||
"'gdal pipeline <FILENAME> ! ...'.";
|
||||
|
||||
SetDisplayInJSONUsage(false);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALMainAlgorithm::ParseCommandLineArguments() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALMainAlgorithm::ParseCommandLineArguments(
|
||||
const std::vector<std::string> &args)
|
||||
{
|
||||
if (args.size() >= 2 && args[0] == "read")
|
||||
{
|
||||
m_subAlg =
|
||||
GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate("pipeline");
|
||||
if (m_subAlg)
|
||||
{
|
||||
bool ret = m_subAlg->ParseCommandLineArguments(args);
|
||||
if (ret)
|
||||
{
|
||||
m_selectedSubAlg = &(m_subAlg->GetActualAlgorithm());
|
||||
std::vector<std::string> callPath(m_callPath);
|
||||
callPath.push_back("vector");
|
||||
m_selectedSubAlg->SetCallPath(callPath);
|
||||
return true;
|
||||
}
|
||||
else if (strstr(CPLGetLastErrorMsg(),
|
||||
"has both raster and vector content"))
|
||||
{
|
||||
m_showUsage = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!(args.size() >= 1 && InstantiateSubAlgorithm(args[0])))
|
||||
{
|
||||
VSIStatBufL sStat;
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
if (VSIStatL(arg.c_str(), &sStat) == 0)
|
||||
{
|
||||
m_subAlg =
|
||||
GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate(
|
||||
"info");
|
||||
if (m_subAlg)
|
||||
{
|
||||
bool ret = m_subAlg->ParseCommandLineArguments(args);
|
||||
if (ret)
|
||||
{
|
||||
m_selectedSubAlg = &(m_subAlg->GetActualAlgorithm());
|
||||
std::vector<std::string> callPath(m_callPath);
|
||||
callPath.push_back(m_selectedSubAlg->GetArg("layer")
|
||||
? "vector"
|
||||
: "raster");
|
||||
m_selectedSubAlg->SetCallPath(callPath);
|
||||
return true;
|
||||
}
|
||||
else if (strstr(CPLGetLastErrorMsg(),
|
||||
"has both raster and vector content"))
|
||||
{
|
||||
m_showUsage = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GDALAlgorithm::ParseCommandLineArguments(args);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALMainAlgorithm::GetUsageForCLI() */
|
||||
/************************************************************************/
|
||||
|
||||
std::string
|
||||
GDALMainAlgorithm::GetUsageForCLI(bool shortUsage,
|
||||
const UsageOptions &usageOptions) const
|
||||
{
|
||||
if (m_selectedSubAlg)
|
||||
return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
|
||||
if (m_showUsage)
|
||||
return GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,57 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "main" command
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_MAIN_INCLUDED
|
||||
#define GDALALG_MAIN_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALMainAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALMainAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME =
|
||||
GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME;
|
||||
static constexpr const char *DESCRIPTION = "Main gdal entry point.";
|
||||
static constexpr const char *HELP_URL = "/programs/index.html";
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALMainAlgorithm();
|
||||
|
||||
bool
|
||||
ParseCommandLineArguments(const std::vector<std::string> &args) override;
|
||||
|
||||
std::string GetUsageForCLI(bool shortUsage,
|
||||
const UsageOptions &usageOptions) const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<GDALAlgorithm> m_subAlg{};
|
||||
bool m_showUsage = true;
|
||||
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif // GDALALG_MAIN_INCLUDED
|
|
@ -0,0 +1,112 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "pipeline" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "cpl_error.h"
|
||||
#include "gdalalgorithm.h"
|
||||
//#include "gdalalg_raster_pipeline.h"
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
#include "gdalalg_dispatcher.h"
|
||||
#include "gdal_priv.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALDummyRasterPipelineAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALDummyRasterPipelineAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "pipeline";
|
||||
static constexpr const char *DESCRIPTION = "Dummy raster pipeline.";
|
||||
static constexpr const char *HELP_URL = "";
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
explicit GDALDummyRasterPipelineAlgorithm(bool = false)
|
||||
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
}
|
||||
|
||||
bool ParseCommandLineArguments(const std::vector<std::string> &) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress functionStatic */
|
||||
GDALDataset *GetDataset()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* cppcheck-suppress functionStatic */
|
||||
void SetDataset(GDALDataset *, bool)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALPipelineAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALPipelineAlgorithm final
|
||||
: public GDALDispatcherAlgorithm<GDALDummyRasterPipelineAlgorithm,
|
||||
GDALVectorPipelineAlgorithm>
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "pipeline";
|
||||
static constexpr const char *DESCRIPTION =
|
||||
"Execute a pipeline (shortcut for 'gdal vector pipeline').";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALPipelineAlgorithm()
|
||||
: GDALDispatcherAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
// only for the help message
|
||||
AddOutputFormatArg(&m_format).SetDefault("json").SetChoices("json",
|
||||
"text");
|
||||
AddInputDatasetArg(&m_dataset);
|
||||
|
||||
m_longDescription =
|
||||
"For all options, run 'gdal raster pipeline --help' or "
|
||||
"'gdal vector pipeline --help'";
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<GDALDummyRasterPipelineAlgorithm> m_rasterPipeline{};
|
||||
std::unique_ptr<GDALVectorPipelineAlgorithm> m_vectorPipeline{};
|
||||
|
||||
std::string m_format{};
|
||||
GDALArgDatasetValue m_dataset{};
|
||||
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"The Run() method should not be called directly on the \"gdal "
|
||||
"pipeline\" program.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
GDAL_STATIC_REGISTER_ALG(GDALPipelineAlgorithm);
|
|
@ -0,0 +1,50 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "raster" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
#include "gdalalg_raster_info.h"
|
||||
#include "gdalalg_raster_convert.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALRasterAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "raster";
|
||||
static constexpr const char *DESCRIPTION = "Raster commands.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALRasterAlgorithm() : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
RegisterSubAlgorithm<GDALRasterInfoAlgorithm>();
|
||||
RegisterSubAlgorithm<GDALRasterConvertAlgorithm>();
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"The Run() method should not be called directly on the \"gdal "
|
||||
"raster\" program.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
GDAL_STATIC_REGISTER_ALG(GDALRasterAlgorithm);
|
|
@ -0,0 +1,108 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "raster convert" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_raster_convert.h"
|
||||
|
||||
#include "cpl_conv.h"
|
||||
#include "gdal_priv.h"
|
||||
#include "gdal_utils.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterConvertAlgorithm::GDALRasterConvertAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALRasterConvertAlgorithm::GDALRasterConvertAlgorithm(
|
||||
bool openForMixedRasterVector)
|
||||
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddProgressArg();
|
||||
AddOutputFormatArg(&m_outputFormat)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
|
||||
{GDAL_DCAP_RASTER, GDAL_DCAP_CREATECOPY});
|
||||
AddOpenOptionsArg(&m_openOptions);
|
||||
AddInputFormatsArg(&m_inputFormats)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
|
||||
AddInputDatasetArg(&m_inputDataset, openForMixedRasterVector
|
||||
? GDAL_OF_RASTER | GDAL_OF_VECTOR |
|
||||
GDAL_OF_MULTIDIM_RASTER
|
||||
: GDAL_OF_RASTER);
|
||||
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
|
||||
AddCreationOptionsArg(&m_creationOptions);
|
||||
const char *exclusionGroup = "overwrite-append";
|
||||
AddOverwriteArg(&m_overwrite).SetMutualExclusionGroup(exclusionGroup);
|
||||
AddArg("append", 0, _("Append as a subdataset to existing output"),
|
||||
&m_append)
|
||||
.SetDefault(false)
|
||||
.SetMutualExclusionGroup(exclusionGroup);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterConvertAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALRasterConvertAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
|
||||
void *pProgressData)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDataset());
|
||||
if (m_outputDataset.GetDataset())
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_NotSupported,
|
||||
"gdal raster convert does not support outputting to an "
|
||||
"already opened output dataset");
|
||||
return false;
|
||||
}
|
||||
|
||||
CPLStringList aosOptions;
|
||||
if (!m_outputFormat.empty())
|
||||
{
|
||||
aosOptions.AddString("-of");
|
||||
aosOptions.AddString(m_outputFormat.c_str());
|
||||
}
|
||||
if (!m_overwrite)
|
||||
{
|
||||
aosOptions.AddString("--no-overwrite");
|
||||
}
|
||||
if (m_append)
|
||||
{
|
||||
aosOptions.AddString("-co");
|
||||
aosOptions.AddString("APPEND_SUBDATASET=YES");
|
||||
}
|
||||
for (const auto &co : m_creationOptions)
|
||||
{
|
||||
aosOptions.AddString("-co");
|
||||
aosOptions.AddString(co.c_str());
|
||||
}
|
||||
|
||||
GDALTranslateOptions *psOptions =
|
||||
GDALTranslateOptionsNew(aosOptions.List(), nullptr);
|
||||
GDALTranslateOptionsSetProgress(psOptions, pfnProgress, pProgressData);
|
||||
|
||||
auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
|
||||
GDALTranslate(m_outputDataset.GetName().c_str(),
|
||||
GDALDataset::ToHandle(m_inputDataset.GetDataset()),
|
||||
psOptions, nullptr)));
|
||||
GDALTranslateOptionsFree(psOptions);
|
||||
if (!poOutDS)
|
||||
return false;
|
||||
|
||||
m_outputDataset.Set(std::move(poOutDS));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,65 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "raster convert" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_RASTER_CONVERT_INCLUDED
|
||||
#define GDALALG_RASTER_CONVERT_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterConvertAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALRasterConvertAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "convert";
|
||||
static constexpr const char *DESCRIPTION = "Convert a raster dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
explicit GDALRasterConvertAlgorithm(bool openForMixedRasterVector = false);
|
||||
|
||||
GDALDataset *GetDataset()
|
||||
{
|
||||
return m_inputDataset.GetDataset();
|
||||
}
|
||||
|
||||
void SetDataset(GDALDataset *poDS, bool owned)
|
||||
{
|
||||
auto arg = GetArg(GDAL_ARG_NAME_INPUT);
|
||||
arg->Set(poDS, owned);
|
||||
arg->SetSkipIfAlreadySet();
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::string m_outputFormat{};
|
||||
GDALArgDatasetValue m_inputDataset{};
|
||||
std::vector<std::string> m_openOptions{};
|
||||
std::vector<std::string> m_inputFormats{};
|
||||
GDALArgDatasetValue m_outputDataset{};
|
||||
std::vector<std::string> m_creationOptions{};
|
||||
bool m_overwrite = false;
|
||||
bool m_append = false;
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif
|
|
@ -0,0 +1,178 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "raster info" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_raster_info.h"
|
||||
|
||||
#include "cpl_conv.h"
|
||||
#include "gdal_priv.h"
|
||||
#include "gdal_utils.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterInfoAlgorithm::GDALRasterInfoAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALRasterInfoAlgorithm::GDALRasterInfoAlgorithm(bool openForMixedRasterVector)
|
||||
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddOutputFormatArg(&m_format).SetDefault("json").SetChoices("json", "text");
|
||||
AddArg("min-max", 0, _("Compute minimum and maximum value"), &m_minMax)
|
||||
.AddAlias("mm");
|
||||
AddArg("stats", 0, _("Retrieve or compute statistics, using all pixels"),
|
||||
&m_stats)
|
||||
.SetMutualExclusionGroup("stats");
|
||||
AddArg("approx-stats", 0,
|
||||
_("Retrieve or compute statistics, using a subset of pixels"),
|
||||
&m_approxStats)
|
||||
.SetMutualExclusionGroup("stats");
|
||||
AddArg("hist", 0, _("Retrieve or compute histogram"), &m_hist);
|
||||
|
||||
AddOpenOptionsArg(&m_openOptions);
|
||||
AddInputFormatsArg(&m_inputFormats)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
|
||||
AddArg("no-gcp", 0, _("Suppress ground control points list printing"),
|
||||
&m_noGCP)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
AddArg("no-md", 0, _("Suppress metadata printing"), &m_noMD)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
AddArg("no-ct", 0, _("Suppress color table printing"), &m_noCT)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
AddArg("no-fl", 0, _("Suppress file list printing"), &m_noFL)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
AddArg("checksum", 0, _("Compute pixel checksum"), &m_checksum)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
AddArg("list-mdd", 0,
|
||||
_("List all metadata domains available for the dataset"), &m_listMDD)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
AddArg("mdd", 0,
|
||||
_("Report metadata for the specified domain. 'all' can be used to "
|
||||
"report metadata in all domains"),
|
||||
&m_mdd)
|
||||
.SetCategory(GAAC_ADVANCED);
|
||||
|
||||
AddArg("no-nodata", 0, _("Suppress retrieving nodata value"), &m_noNodata)
|
||||
.SetCategory(GAAC_ESOTERIC);
|
||||
AddArg("no-mask", 0, _("Suppress mask band information"), &m_noMask)
|
||||
.SetCategory(GAAC_ESOTERIC);
|
||||
AddArg("subdataset", 0,
|
||||
_("Use subdataset of specified index (starting at 1), instead of "
|
||||
"the source dataset itself"),
|
||||
&m_subDS)
|
||||
.SetCategory(GAAC_ESOTERIC);
|
||||
|
||||
AddInputDatasetArg(&m_dataset, openForMixedRasterVector
|
||||
? GDAL_OF_RASTER | GDAL_OF_VECTOR |
|
||||
GDAL_OF_MULTIDIM_RASTER
|
||||
: GDAL_OF_RASTER);
|
||||
|
||||
AddOutputStringArg(&m_output);
|
||||
AddArg("stdout", 0,
|
||||
_("Directly output on stdout (format=text mode only). If enabled, "
|
||||
"output-string will be empty"),
|
||||
&m_stdout)
|
||||
.SetHiddenForCLI();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterInfoAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALRasterInfoAlgorithm::RunImpl(GDALProgressFunc, void *)
|
||||
{
|
||||
CPLAssert(m_dataset.GetDataset());
|
||||
|
||||
CPLStringList aosOptions;
|
||||
if (m_format == "json")
|
||||
aosOptions.AddString("-json");
|
||||
if (m_minMax)
|
||||
aosOptions.AddString("-mm");
|
||||
if (m_stats)
|
||||
aosOptions.AddString("-stats");
|
||||
if (m_approxStats)
|
||||
aosOptions.AddString("-approx_stats");
|
||||
if (m_hist)
|
||||
aosOptions.AddString("-hist");
|
||||
if (m_noGCP)
|
||||
aosOptions.AddString("-nogcp");
|
||||
if (m_noMD)
|
||||
aosOptions.AddString("-nomd");
|
||||
if (m_noCT)
|
||||
aosOptions.AddString("-noct");
|
||||
if (m_noFL)
|
||||
aosOptions.AddString("-nofl");
|
||||
if (m_noMask)
|
||||
aosOptions.AddString("-nomask");
|
||||
if (m_noNodata)
|
||||
aosOptions.AddString("-nonodata");
|
||||
if (m_checksum)
|
||||
aosOptions.AddString("-checksum");
|
||||
if (m_listMDD)
|
||||
aosOptions.AddString("-listmdd");
|
||||
if (!m_mdd.empty())
|
||||
{
|
||||
aosOptions.AddString("-mdd");
|
||||
aosOptions.AddString(m_mdd.c_str());
|
||||
}
|
||||
|
||||
GDALDatasetH hDS = GDALDataset::ToHandle(m_dataset.GetDataset());
|
||||
std::unique_ptr<GDALDataset> poSubDataset;
|
||||
|
||||
if (m_subDS > 0)
|
||||
{
|
||||
char **papszSubdatasets = GDALGetMetadata(hDS, "SUBDATASETS");
|
||||
const int nSubdatasets = CSLCount(papszSubdatasets) / 2;
|
||||
if (m_subDS > nSubdatasets)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Invalid value for 'subdataset' argument. Should be "
|
||||
"between 1 and %d",
|
||||
nSubdatasets);
|
||||
return false;
|
||||
}
|
||||
|
||||
char szKeyName[64];
|
||||
snprintf(szKeyName, sizeof(szKeyName), "SUBDATASET_%d_NAME", m_subDS);
|
||||
const std::string osSubDSName =
|
||||
CSLFetchNameValueDef(papszSubdatasets, szKeyName, "");
|
||||
|
||||
poSubDataset.reset(GDALDataset::Open(
|
||||
osSubDSName.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
|
||||
nullptr, nullptr, nullptr));
|
||||
if (!poSubDataset)
|
||||
return false;
|
||||
hDS = GDALDataset::ToHandle(poSubDataset.get());
|
||||
}
|
||||
|
||||
if (m_stdout)
|
||||
{
|
||||
aosOptions.AddString("-stdout");
|
||||
}
|
||||
|
||||
GDALInfoOptions *psOptions = GDALInfoOptionsNew(aosOptions.List(), nullptr);
|
||||
char *ret = GDALInfo(hDS, psOptions);
|
||||
GDALInfoOptionsFree(psOptions);
|
||||
const bool bOK = ret != nullptr;
|
||||
if (ret)
|
||||
{
|
||||
m_output = ret;
|
||||
}
|
||||
CPLFree(ret);
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,78 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "raster info" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_RASTER_INFO_INCLUDED
|
||||
#define GDALALG_RASTER_INFO_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterInfoAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALRasterInfoAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "info";
|
||||
static constexpr const char *DESCRIPTION =
|
||||
"Return information on a raster dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
explicit GDALRasterInfoAlgorithm(bool openForMixedRasterVector = false);
|
||||
|
||||
GDALDataset *GetDataset()
|
||||
{
|
||||
return m_dataset.GetDataset();
|
||||
}
|
||||
|
||||
void SetDataset(GDALDataset *poDS, bool owned)
|
||||
{
|
||||
auto arg = GetArg(GDAL_ARG_NAME_INPUT);
|
||||
arg->Set(poDS, owned);
|
||||
arg->SetSkipIfAlreadySet();
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::string m_format{};
|
||||
bool m_minMax = false;
|
||||
bool m_stats = false;
|
||||
bool m_approxStats = false;
|
||||
bool m_hist = false;
|
||||
bool m_noGCP = false;
|
||||
bool m_noMD = false;
|
||||
bool m_noCT = false;
|
||||
bool m_noFL = false;
|
||||
bool m_noMask = false;
|
||||
bool m_noNodata = false;
|
||||
bool m_checksum = false;
|
||||
bool m_listMDD = false;
|
||||
bool m_stdout = false;
|
||||
std::string m_mdd{};
|
||||
int m_subDS = 0;
|
||||
GDALArgDatasetValue m_dataset{};
|
||||
std::vector<std::string> m_openOptions{};
|
||||
std::vector<std::string> m_inputFormats{};
|
||||
std::string m_output{};
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
#include "gdalalg_vector_info.h"
|
||||
#include "gdalalg_vector_convert.h"
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "vector";
|
||||
static constexpr const char *DESCRIPTION = "Vector commands.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorAlgorithm() : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
RegisterSubAlgorithm<GDALVectorInfoAlgorithm>();
|
||||
RegisterSubAlgorithm<GDALVectorConvertAlgorithm>();
|
||||
RegisterSubAlgorithm<GDALVectorPipelineAlgorithm>();
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc, void *) override
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"The Run() method should not be called directly on the \"gdal "
|
||||
"vector\" program.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
GDAL_STATIC_REGISTER_ALG(GDALVectorAlgorithm);
|
|
@ -0,0 +1,136 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector convert" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_convert.h"
|
||||
|
||||
#include "cpl_conv.h"
|
||||
#include "gdal_priv.h"
|
||||
#include "gdal_utils.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorConvertAlgorithm::GDALVectorConvertAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorConvertAlgorithm::GDALVectorConvertAlgorithm()
|
||||
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddProgressArg();
|
||||
AddOutputFormatArg(&m_outputFormat)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
|
||||
{GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
|
||||
AddOpenOptionsArg(&m_openOptions);
|
||||
AddInputFormatsArg(&m_inputFormats)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
|
||||
AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR);
|
||||
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR);
|
||||
m_outputDataset.SetInputFlags(GADV_NAME | GADV_OBJECT);
|
||||
AddCreationOptionsArg(&m_creationOptions);
|
||||
AddLayerCreationOptionsArg(&m_layerCreationOptions);
|
||||
AddOverwriteArg(&m_overwrite);
|
||||
AddUpdateArg(&m_update);
|
||||
AddArg("overwrite-layer", 0,
|
||||
_("Whether overwriting existing layer is allowed"),
|
||||
&m_overwriteLayer)
|
||||
.SetDefault(false);
|
||||
AddArg("append", 0, _("Whether appending to existing layer is allowed"),
|
||||
&m_appendLayer)
|
||||
.SetDefault(false);
|
||||
AddArg("input-layer", 'l', _("Input layer name(s)"), &m_inputLayerNames)
|
||||
.AddAlias("layer");
|
||||
AddArg("output-layer", 0, _("Output layer name"), &m_outputLayerName)
|
||||
.AddHiddenAlias("nln"); // For ogr2ogr nostalgic people
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorConvertAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorConvertAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
|
||||
void *pProgressData)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDataset());
|
||||
|
||||
CPLStringList aosOptions;
|
||||
aosOptions.AddString("--invoked-from-gdal-vector-convert");
|
||||
if (!m_overwrite)
|
||||
{
|
||||
aosOptions.AddString("--no-overwrite");
|
||||
}
|
||||
if (m_overwriteLayer)
|
||||
{
|
||||
aosOptions.AddString("-overwrite");
|
||||
}
|
||||
if (m_appendLayer)
|
||||
{
|
||||
aosOptions.AddString("-append");
|
||||
}
|
||||
if (!m_outputFormat.empty())
|
||||
{
|
||||
aosOptions.AddString("-of");
|
||||
aosOptions.AddString(m_outputFormat.c_str());
|
||||
}
|
||||
for (const auto &co : m_creationOptions)
|
||||
{
|
||||
aosOptions.AddString("-dsco");
|
||||
aosOptions.AddString(co.c_str());
|
||||
}
|
||||
for (const auto &co : m_layerCreationOptions)
|
||||
{
|
||||
aosOptions.AddString("-lco");
|
||||
aosOptions.AddString(co.c_str());
|
||||
}
|
||||
if (!m_outputLayerName.empty())
|
||||
{
|
||||
aosOptions.AddString("-nln");
|
||||
aosOptions.AddString(m_outputLayerName.c_str());
|
||||
}
|
||||
if (pfnProgress && pfnProgress != GDALDummyProgress)
|
||||
{
|
||||
aosOptions.AddString("-progress");
|
||||
}
|
||||
|
||||
// Must be last, as positional
|
||||
for (const auto &name : m_inputLayerNames)
|
||||
{
|
||||
aosOptions.AddString(name.c_str());
|
||||
}
|
||||
|
||||
GDALVectorTranslateOptions *psOptions =
|
||||
GDALVectorTranslateOptionsNew(aosOptions.List(), nullptr);
|
||||
|
||||
GDALVectorTranslateOptionsSetProgress(psOptions, pfnProgress,
|
||||
pProgressData);
|
||||
|
||||
GDALDatasetH hOutDS = GDALDataset::ToHandle(m_outputDataset.GetDataset());
|
||||
GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDataset());
|
||||
auto poRetDS = GDALDataset::FromHandle(
|
||||
GDALVectorTranslate(m_outputDataset.GetName().c_str(), hOutDS, 1,
|
||||
&hSrcDS, psOptions, nullptr));
|
||||
GDALVectorTranslateOptionsFree(psOptions);
|
||||
if (!poRetDS)
|
||||
return false;
|
||||
|
||||
if (!hOutDS)
|
||||
{
|
||||
m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,68 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector convert" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_CONVERT_INCLUDED
|
||||
#define GDALALG_VECTOR_CONVERT_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorConvertAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorConvertAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "convert";
|
||||
static constexpr const char *DESCRIPTION = "Convert a vector dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorConvertAlgorithm();
|
||||
|
||||
void SetDataset(GDALDataset *poDS, bool owned)
|
||||
{
|
||||
auto arg = GetArg(GDAL_ARG_NAME_INPUT);
|
||||
if (arg)
|
||||
{
|
||||
arg->Set(poDS, owned);
|
||||
arg->SetSkipIfAlreadySet();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::string m_outputFormat{};
|
||||
GDALArgDatasetValue m_inputDataset{};
|
||||
std::vector<std::string> m_openOptions{};
|
||||
std::vector<std::string> m_inputFormats{};
|
||||
GDALArgDatasetValue m_outputDataset{};
|
||||
std::vector<std::string> m_creationOptions{};
|
||||
std::vector<std::string> m_layerCreationOptions{};
|
||||
bool m_overwrite = false;
|
||||
bool m_update = false;
|
||||
bool m_overwriteLayer = false;
|
||||
bool m_appendLayer = false;
|
||||
std::vector<std::string> m_inputLayerNames{};
|
||||
std::string m_outputLayerName{};
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "filter" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_filter.h"
|
||||
|
||||
#include "gdal_priv.h"
|
||||
#include "ogrsf_frmts.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorFilterAlgorithm::GDALVectorFilterAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorFilterAlgorithm::GDALVectorFilterAlgorithm()
|
||||
: GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
auto &arg =
|
||||
AddArg("bbox", 0, _("Bounding box as xmin,ymin,xmax,ymax"), &m_bbox)
|
||||
.SetRepeatedArgAllowed(false)
|
||||
.SetMinCount(4)
|
||||
.SetMaxCount(4)
|
||||
.SetDisplayHintAboutRepetition(false);
|
||||
arg.AddValidationAction(
|
||||
[&arg]()
|
||||
{
|
||||
const auto &val = arg.Get<std::vector<double>>();
|
||||
CPLAssert(val.size() == 4);
|
||||
if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Value of 'bbox' should be xmin,ymin,xmax,ymax with "
|
||||
"xmin <= xmax and ymin <= ymax");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorFilterAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorFilterAlgorithm::RunImpl(GDALProgressFunc, void *)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDataset());
|
||||
CPLAssert(m_outputDataset.GetName().empty());
|
||||
CPLAssert(!m_outputDataset.GetDataset());
|
||||
|
||||
bool ret = true;
|
||||
if (m_bbox.size() == 4)
|
||||
{
|
||||
const double xmin = m_bbox[0];
|
||||
const double ymin = m_bbox[1];
|
||||
const double xmax = m_bbox[2];
|
||||
const double ymax = m_bbox[3];
|
||||
auto poSrcDS = m_inputDataset.GetDataset();
|
||||
const int nLayerCount = poSrcDS->GetLayerCount();
|
||||
for (int i = 0; i < nLayerCount; ++i)
|
||||
{
|
||||
auto poSrcLayer = poSrcDS->GetLayer(i);
|
||||
ret = ret && (poSrcLayer != nullptr);
|
||||
if (poSrcLayer)
|
||||
poSrcLayer->SetSpatialFilterRect(xmin, ymin, xmax, ymax);
|
||||
}
|
||||
}
|
||||
|
||||
m_outputDataset.Set(m_inputDataset.GetDataset(), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,46 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "filter" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_FILTER_INCLUDED
|
||||
#define GDALALG_VECTOR_FILTER_INCLUDED
|
||||
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorFilterAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorFilterAlgorithm final : public GDALVectorPipelineStepAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "filter";
|
||||
static constexpr const char *DESCRIPTION = "Filter.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorFilterAlgorithm();
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::vector<double> m_bbox{};
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif /* GDALALG_VECTOR_FILTER_INCLUDED */
|
|
@ -0,0 +1,128 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector info" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_info.h"
|
||||
|
||||
#include "cpl_conv.h"
|
||||
#include "gdal_priv.h"
|
||||
#include "gdal_utils.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorInfoAlgorithm::GDALVectorInfoAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorInfoAlgorithm::GDALVectorInfoAlgorithm()
|
||||
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddOutputFormatArg(&m_format).SetDefault("json").SetChoices("json", "text");
|
||||
AddOpenOptionsArg(&m_openOptions);
|
||||
AddInputFormatsArg(&m_inputFormats)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
|
||||
AddInputDatasetArg(&m_dataset, GDAL_OF_VECTOR);
|
||||
AddLayerNameArg(&m_layerNames).SetMutualExclusionGroup("layer-sql");
|
||||
AddArg("features", 0,
|
||||
_("List all features (beware of RAM consumption on large layers)"),
|
||||
&m_listFeatures);
|
||||
AddArg("sql", 0,
|
||||
_("Execute the indicated SQL statement and return the result"),
|
||||
&m_sql)
|
||||
.SetReadFromFileAtSyntaxAllowed()
|
||||
.SetMetaVar("<statement>|@<filename>")
|
||||
.SetRemoveSQLCommentsEnabled()
|
||||
.SetMutualExclusionGroup("layer-sql");
|
||||
AddArg("where", 0,
|
||||
_("Attribute query in a restricted form of the queries used in the "
|
||||
"SQL WHERE statement"),
|
||||
&m_where)
|
||||
.SetReadFromFileAtSyntaxAllowed()
|
||||
.SetMetaVar("<WHERE>|@<filename>")
|
||||
.SetRemoveSQLCommentsEnabled();
|
||||
AddArg("dialect", 0, _("SQL dialect"), &m_dialect);
|
||||
AddArg(GDAL_ARG_NAME_UPDATE, 0, _("Open the dataset in update mode"),
|
||||
&m_update);
|
||||
AddOutputStringArg(&m_output);
|
||||
AddArg("stdout", 0,
|
||||
_("Directly output on stdout (format=text mode only). If enabled, "
|
||||
"output-string will be empty"),
|
||||
&m_stdout)
|
||||
.SetHiddenForCLI();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorInfoAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorInfoAlgorithm::RunImpl(GDALProgressFunc, void *)
|
||||
{
|
||||
CPLAssert(m_dataset.GetDataset());
|
||||
|
||||
CPLStringList aosOptions;
|
||||
if (m_format == "json")
|
||||
{
|
||||
aosOptions.AddString("-json");
|
||||
}
|
||||
else
|
||||
{
|
||||
aosOptions.AddString("-al");
|
||||
if (!m_listFeatures)
|
||||
aosOptions.AddString("-so");
|
||||
}
|
||||
if (m_listFeatures)
|
||||
{
|
||||
aosOptions.AddString("-features");
|
||||
}
|
||||
if (!m_sql.empty())
|
||||
{
|
||||
aosOptions.AddString("-sql");
|
||||
aosOptions.AddString(m_sql.c_str());
|
||||
}
|
||||
if (!m_where.empty())
|
||||
{
|
||||
aosOptions.AddString("-where");
|
||||
aosOptions.AddString(m_where.c_str());
|
||||
}
|
||||
if (!m_dialect.empty())
|
||||
{
|
||||
aosOptions.AddString("-dialect");
|
||||
aosOptions.AddString(m_dialect.c_str());
|
||||
}
|
||||
if (m_stdout)
|
||||
{
|
||||
aosOptions.AddString("-stdout");
|
||||
}
|
||||
|
||||
// Must be last, as positional
|
||||
aosOptions.AddString("dummy");
|
||||
for (const std::string &name : m_layerNames)
|
||||
aosOptions.AddString(name.c_str());
|
||||
|
||||
GDALVectorInfoOptions *psInfo =
|
||||
GDALVectorInfoOptionsNew(aosOptions.List(), nullptr);
|
||||
char *ret =
|
||||
GDALVectorInfo(GDALDataset::ToHandle(m_dataset.GetDataset()), psInfo);
|
||||
GDALVectorInfoOptionsFree(psInfo);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
m_output = ret;
|
||||
CPLFree(ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,68 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector info" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_INFO_INCLUDED
|
||||
#define GDALALG_VECTOR_INFO_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorInfoAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorInfoAlgorithm final : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "info";
|
||||
static constexpr const char *DESCRIPTION =
|
||||
"Return information on a vector dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorInfoAlgorithm();
|
||||
|
||||
void SetDataset(GDALDataset *poDS, bool owned)
|
||||
{
|
||||
auto arg = GetArg(GDAL_ARG_NAME_INPUT);
|
||||
if (arg)
|
||||
{
|
||||
arg->Set(poDS, owned);
|
||||
arg->SetSkipIfAlreadySet();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::string m_format{};
|
||||
GDALArgDatasetValue m_dataset{};
|
||||
std::vector<std::string> m_openOptions{};
|
||||
std::vector<std::string> m_inputFormats{};
|
||||
bool m_update = false;
|
||||
std::vector<std::string> m_layerNames{};
|
||||
bool m_listFeatures = false;
|
||||
bool m_stdout = false;
|
||||
std::string m_sql{};
|
||||
std::string m_where{};
|
||||
std::string m_dialect{};
|
||||
std::string m_output{};
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif
|
|
@ -0,0 +1,537 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector pipeline" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
#include "gdalalg_vector_read.h"
|
||||
#include "gdalalg_vector_filter.h"
|
||||
#include "gdalalg_vector_reproject.h"
|
||||
#include "gdalalg_vector_write.h"
|
||||
|
||||
#include "cpl_conv.h"
|
||||
#include "cpl_json.h"
|
||||
#include "cpl_string.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineStepAlgorithm::AddInputArgs() */
|
||||
/************************************************************************/
|
||||
|
||||
void GDALVectorPipelineStepAlgorithm::AddInputArgs(bool hiddenForCLI)
|
||||
{
|
||||
AddInputFormatsArg(&m_inputFormats)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR})
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
AddOpenOptionsArg(&m_openOptions).SetHiddenForCLI(hiddenForCLI);
|
||||
AddInputDatasetArg(&m_inputDataset, GDAL_OF_VECTOR,
|
||||
/* positionalAndRequired = */ !hiddenForCLI)
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
AddArg("input-layer", 'l', _("Input layer name(s)"), &m_inputLayerNames)
|
||||
.AddAlias("layer")
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineStepAlgorithm::AddOutputArgs() */
|
||||
/************************************************************************/
|
||||
|
||||
void GDALVectorPipelineStepAlgorithm::AddOutputArgs(
|
||||
bool hiddenForCLI, bool shortNameOutputLayerAllowed)
|
||||
{
|
||||
AddOutputFormatArg(&m_format)
|
||||
.AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
|
||||
{GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
|
||||
/* positionalAndRequired = */ !hiddenForCLI)
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
m_outputDataset.SetInputFlags(GADV_NAME | GADV_OBJECT);
|
||||
AddCreationOptionsArg(&m_creationOptions).SetHiddenForCLI(hiddenForCLI);
|
||||
AddLayerCreationOptionsArg(&m_layerCreationOptions)
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
AddOverwriteArg(&m_overwrite).SetHiddenForCLI(hiddenForCLI);
|
||||
AddUpdateArg(&m_update).SetHiddenForCLI(hiddenForCLI);
|
||||
AddArg("overwrite-layer", 0,
|
||||
_("Whether overwriting existing layer is allowed"),
|
||||
&m_overwriteLayer)
|
||||
.SetDefault(false)
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
AddArg("append", 0, _("Whether appending to existing layer is allowed"),
|
||||
&m_appendLayer)
|
||||
.SetDefault(false)
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
AddArg("output-layer", shortNameOutputLayerAllowed ? 'l' : 0,
|
||||
_("Output layer name"), &m_outputLayerName)
|
||||
.AddHiddenAlias("nln") // For ogr2ogr nostalgic people
|
||||
.SetHiddenForCLI(hiddenForCLI);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm()
|
||||
: GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddInputArgs(/* hiddenForCLI = */ true);
|
||||
AddProgressArg();
|
||||
AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
|
||||
.SetHiddenForCLI()
|
||||
.SetPositional();
|
||||
AddOutputArgs(/* hiddenForCLI = */ true,
|
||||
/* shortNameOutputLayerAllowed=*/false);
|
||||
|
||||
m_stepRegistry.Register<GDALVectorReadAlgorithm>();
|
||||
m_stepRegistry.Register<GDALVectorWriteAlgorithm>();
|
||||
m_stepRegistry.Register<GDALVectorReprojectAlgorithm>();
|
||||
m_stepRegistry.Register<GDALVectorFilterAlgorithm>();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm::GetStepAlg() */
|
||||
/************************************************************************/
|
||||
|
||||
std::unique_ptr<GDALVectorPipelineStepAlgorithm>
|
||||
GDALVectorPipelineAlgorithm::GetStepAlg(const std::string &name) const
|
||||
{
|
||||
auto alg = m_stepRegistry.Instantiate(name);
|
||||
return std::unique_ptr<GDALVectorPipelineStepAlgorithm>(
|
||||
cpl::down_cast<GDALVectorPipelineStepAlgorithm *>(alg.release()));
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm::ParseCommandLineArguments() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorPipelineAlgorithm::ParseCommandLineArguments(
|
||||
const std::vector<std::string> &args)
|
||||
{
|
||||
if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help" ||
|
||||
args[0] == "help" || args[0] == "--json-usage"))
|
||||
return GDALAlgorithm::ParseCommandLineArguments(args);
|
||||
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
if (arg.find("--pipeline") == 0)
|
||||
return GDALAlgorithm::ParseCommandLineArguments(args);
|
||||
|
||||
// gdal vector pipeline [--progress] "read poly.gpkg ..."
|
||||
if (arg.find("read ") == 0)
|
||||
return GDALAlgorithm::ParseCommandLineArguments(args);
|
||||
}
|
||||
|
||||
if (!m_steps.empty())
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"ParseCommandLineArguments() can only be called once per "
|
||||
"instance.");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct Step
|
||||
{
|
||||
std::unique_ptr<GDALVectorPipelineStepAlgorithm> alg{};
|
||||
std::vector<std::string> args{};
|
||||
};
|
||||
|
||||
std::vector<Step> steps;
|
||||
steps.resize(1);
|
||||
|
||||
for (const auto &arg : args)
|
||||
{
|
||||
if (arg == "--progress")
|
||||
{
|
||||
m_progressBarRequested = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &curStep = steps.back();
|
||||
|
||||
if (arg == "!" || arg == "|")
|
||||
{
|
||||
if (curStep.alg)
|
||||
{
|
||||
steps.resize(steps.size() + 1);
|
||||
}
|
||||
}
|
||||
#ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
|
||||
else if (arg == "+step")
|
||||
{
|
||||
if (curStep.alg)
|
||||
{
|
||||
steps.resize(steps.size() + 1);
|
||||
}
|
||||
}
|
||||
else if (arg.find("+gdal=") == 0)
|
||||
{
|
||||
const std::string stepName = arg.substr(strlen("+gdal="));
|
||||
curStep.alg = GetStepAlg(stepName);
|
||||
if (!curStep.alg)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"unknown step name: %s", stepName.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (!curStep.alg)
|
||||
{
|
||||
std::string algName = arg;
|
||||
#ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
|
||||
if (!algName.empty() && algName[0] == '+')
|
||||
algName = algName.substr(1);
|
||||
#endif
|
||||
curStep.alg = GetStepAlg(algName);
|
||||
if (!curStep.alg)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"unknown step name: %s", algName.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
|
||||
if (!arg.empty() && arg[0] == '+')
|
||||
{
|
||||
curStep.args.push_back("--" + arg.substr(1));
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
std::string value = arg;
|
||||
|
||||
// #define GDAL_PIPELINE_NATURAL_LANGUAGE
|
||||
#ifdef GDAL_PIPELINE_NATURAL_LANGUAGE
|
||||
// gdal vector pipeline "read [from] poly.gpkg, reproject [from EPSG:4326] to EPSG:32632 and write to out.gpkg with overwriting"
|
||||
if (value == "and")
|
||||
{
|
||||
steps.resize(steps.size() + 1);
|
||||
}
|
||||
else if (value == "from" && curStep.alg->GetName() == "read")
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (value == "from" && curStep.alg->GetName() == "reproject")
|
||||
{
|
||||
curStep.args.push_back("--src-crs");
|
||||
}
|
||||
else if (value == "to" && curStep.alg->GetName() == "reproject")
|
||||
{
|
||||
curStep.args.push_back("--dst-crs");
|
||||
}
|
||||
else if (value == "to" && curStep.alg->GetName() == "write")
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (value == "with" && curStep.alg->GetName() == "write")
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (value == "overwriting" &&
|
||||
curStep.alg->GetName() == "write")
|
||||
{
|
||||
curStep.args.push_back("--overwrite");
|
||||
}
|
||||
else if (!value.empty() && value.back() == ',')
|
||||
{
|
||||
curStep.args.push_back(value.substr(0, value.size() - 1));
|
||||
steps.resize(steps.size() + 1);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
{
|
||||
curStep.args.push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// As we initially added a step without alg to bootstrap things, make
|
||||
// sure to remove it if it hasn't been filled, or the user has terminated
|
||||
// the pipeline with a '!' separator.
|
||||
if (!steps.back().alg)
|
||||
steps.pop_back();
|
||||
|
||||
if (steps.size() < 2)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"At least 2 steps must be provided");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (steps.front().alg->GetName() != GDALVectorReadAlgorithm::NAME)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined, "First step should be '%s'",
|
||||
GDALVectorReadAlgorithm::NAME);
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i < steps.size() - 1; ++i)
|
||||
{
|
||||
if (steps[i].alg->GetName() == GDALVectorReadAlgorithm::NAME)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Only first step can be '%s'",
|
||||
GDALVectorReadAlgorithm::NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (steps.back().alg->GetName() != GDALVectorWriteAlgorithm::NAME)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined, "Last step should be '%s'",
|
||||
GDALVectorWriteAlgorithm::NAME);
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < steps.size() - 1; ++i)
|
||||
{
|
||||
if (steps[i].alg->GetName() == GDALVectorWriteAlgorithm::NAME)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Only last step can be '%s'",
|
||||
GDALVectorWriteAlgorithm::NAME);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_pipeline.empty())
|
||||
{
|
||||
// Propagate input parameters set at the pipeline level to the
|
||||
// "read" step
|
||||
{
|
||||
auto &step = steps.front();
|
||||
for (auto &arg : step.alg->GetArgs())
|
||||
{
|
||||
auto pipelineArg = GetArg(arg->GetName());
|
||||
if (pipelineArg && pipelineArg->IsExplicitlySet())
|
||||
{
|
||||
arg->SetSkipIfAlreadySet(true);
|
||||
arg->SetFrom(*pipelineArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same with "write" step
|
||||
{
|
||||
auto &step = steps.back();
|
||||
for (auto &arg : step.alg->GetArgs())
|
||||
{
|
||||
auto pipelineArg = GetArg(arg->GetName());
|
||||
if (pipelineArg && pipelineArg->IsExplicitlySet())
|
||||
{
|
||||
arg->SetSkipIfAlreadySet(true);
|
||||
arg->SetFrom(*pipelineArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse each step, but without running the validation
|
||||
for (const auto &step : steps)
|
||||
{
|
||||
step.alg->m_skipValidationInParseCommandLine = true;
|
||||
if (!step.alg->ParseCommandLineArguments(step.args))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Evaluate "input" argument of "read" step, together with the "output"
|
||||
// argument of the "write" step, in case they point to the same dataset.
|
||||
auto inputArg = steps.front().alg->GetArg(GDAL_ARG_NAME_INPUT);
|
||||
if (inputArg && inputArg->IsExplicitlySet() &&
|
||||
inputArg->GetType() == GAAT_DATASET)
|
||||
{
|
||||
steps.front().alg->ProcessDatasetArg(inputArg, steps.back().alg.get());
|
||||
}
|
||||
|
||||
for (const auto &step : steps)
|
||||
{
|
||||
if (!step.alg->ValidateArguments())
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &step : steps)
|
||||
m_steps.push_back(std::move(step.alg));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorPipelineAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
|
||||
void *pProgressData)
|
||||
{
|
||||
if (m_steps.empty())
|
||||
{
|
||||
// If invoked programmatically, not from the command line.
|
||||
|
||||
if (m_pipeline.empty())
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"'pipeline' argument not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
const CPLStringList aosTokens(CSLTokenizeString(m_pipeline.c_str()));
|
||||
if (!ParseCommandLineArguments(aosTokens))
|
||||
return false;
|
||||
}
|
||||
|
||||
GDALDataset *poCurDS = nullptr;
|
||||
for (size_t i = 0; i < m_steps.size(); ++i)
|
||||
{
|
||||
auto &step = m_steps[i];
|
||||
if (i > 0)
|
||||
{
|
||||
if (step->m_inputDataset.GetDataset())
|
||||
{
|
||||
// Shouldn't happen
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Step nr %d (%s) has already an input dataset",
|
||||
static_cast<int>(i), step->GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
step->m_inputDataset.Set(poCurDS, false);
|
||||
}
|
||||
if (i + 1 < m_steps.size() && step->m_outputDataset.GetDataset())
|
||||
{
|
||||
// Shouldn't happen
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Step nr %d (%s) has already an output dataset",
|
||||
static_cast<int>(i), step->GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
if (!step->Run(i < m_steps.size() - 1 ? nullptr : pfnProgress,
|
||||
i < m_steps.size() - 1 ? nullptr : pProgressData))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
poCurDS = step->m_outputDataset.GetDataset();
|
||||
if (!poCurDS)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Step nr %d (%s) failed to produce an output dataset",
|
||||
static_cast<int>(i), step->GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_outputDataset.GetDataset())
|
||||
{
|
||||
m_outputDataset.Set(poCurDS, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithm::Finalize() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorPipelineAlgorithm::Finalize()
|
||||
{
|
||||
bool ret = GDALAlgorithm::Finalize();
|
||||
for (auto &step : m_steps)
|
||||
{
|
||||
ret = step->Finalize() && ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm::GetUsageForCLI() */
|
||||
/************************************************************************/
|
||||
|
||||
std::string GDALVectorPipelineAlgorithm::GetUsageForCLI(
|
||||
bool shortUsage, const UsageOptions &usageOptions) const
|
||||
{
|
||||
std::string ret = GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
|
||||
if (shortUsage)
|
||||
return ret;
|
||||
|
||||
ret += "\n<PIPELINE> is of the form: read [READ-OPTIONS] "
|
||||
"( ! <STEP-NAME> [STEP-OPTIONS] )* ! write [WRITE-OPTIONS]\n";
|
||||
ret += '\n';
|
||||
ret += "Example: 'gdal vector pipeline --progress ! read in.gpkg ! \\\n";
|
||||
ret += " reproject --dst-crs=EPSG:32632 ! ";
|
||||
ret += "write out.gpkg --overwrite'\n";
|
||||
ret += '\n';
|
||||
ret += "Potential steps are:\n";
|
||||
|
||||
UsageOptions stepUsageOptions;
|
||||
stepUsageOptions.isPipelineStep = true;
|
||||
|
||||
for (const std::string &name : m_stepRegistry.GetNames())
|
||||
{
|
||||
auto alg = GetStepAlg(name);
|
||||
auto [options, maxOptLen] = alg->GetArgNamesForCLI();
|
||||
stepUsageOptions.maxOptLen =
|
||||
std::max(stepUsageOptions.maxOptLen, maxOptLen);
|
||||
}
|
||||
|
||||
{
|
||||
const auto name = GDALVectorReadAlgorithm::NAME;
|
||||
ret += '\n';
|
||||
auto alg = GetStepAlg(name);
|
||||
alg->SetCallPath({name});
|
||||
ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
|
||||
}
|
||||
for (const std::string &name : m_stepRegistry.GetNames())
|
||||
{
|
||||
if (name != GDALVectorReadAlgorithm::NAME &&
|
||||
name != GDALVectorWriteAlgorithm::NAME)
|
||||
{
|
||||
ret += '\n';
|
||||
auto alg = GetStepAlg(name);
|
||||
alg->SetCallPath({name});
|
||||
ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
|
||||
}
|
||||
}
|
||||
{
|
||||
const auto name = GDALVectorWriteAlgorithm::NAME;
|
||||
ret += '\n';
|
||||
auto alg = GetStepAlg(name);
|
||||
alg->SetCallPath({name});
|
||||
ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm::GetUsageAsJSON() */
|
||||
/************************************************************************/
|
||||
|
||||
std::string GDALVectorPipelineAlgorithm::GetUsageAsJSON() const
|
||||
{
|
||||
CPLJSONDocument oDoc;
|
||||
oDoc.LoadMemory(GDALAlgorithm::GetUsageAsJSON());
|
||||
|
||||
CPLJSONArray jPipelineSteps;
|
||||
for (const std::string &name : m_stepRegistry.GetNames())
|
||||
{
|
||||
auto alg = GetStepAlg(name);
|
||||
CPLJSONDocument oStepDoc;
|
||||
oStepDoc.LoadMemory(alg->GetUsageAsJSON());
|
||||
jPipelineSteps.Add(oStepDoc.GetRoot());
|
||||
}
|
||||
oDoc.GetRoot().Add("pipeline_algorithms", jPipelineSteps);
|
||||
|
||||
return oDoc.SaveAsString();
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,116 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: gdal "vector pipeline" subcommand
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_PIPELINE_INCLUDED
|
||||
#define GDALALG_VECTOR_PIPELINE_INCLUDED
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineStepAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorPipelineStepAlgorithm /* non final */ : public GDALAlgorithm
|
||||
{
|
||||
protected:
|
||||
GDALVectorPipelineStepAlgorithm(const std::string &name,
|
||||
const std::string &description,
|
||||
const std::string &helpURL)
|
||||
: GDALAlgorithm(name, description, helpURL)
|
||||
{
|
||||
}
|
||||
|
||||
friend class GDALVectorPipelineAlgorithm;
|
||||
|
||||
void AddInputArgs(bool hiddenForCLI);
|
||||
void AddOutputArgs(bool hiddenForCLI, bool shortNameOutputLayerAllowed);
|
||||
|
||||
// Input arguments
|
||||
GDALArgDatasetValue m_inputDataset{};
|
||||
std::vector<std::string> m_openOptions{};
|
||||
std::vector<std::string> m_inputFormats{};
|
||||
std::vector<std::string> m_inputLayerNames{};
|
||||
|
||||
// Output arguments
|
||||
GDALArgDatasetValue m_outputDataset{};
|
||||
std::string m_format{};
|
||||
std::vector<std::string> m_creationOptions{};
|
||||
std::vector<std::string> m_layerCreationOptions{};
|
||||
bool m_overwrite = false;
|
||||
bool m_update = false;
|
||||
bool m_overwriteLayer = false;
|
||||
bool m_appendLayer = false;
|
||||
std::string m_outputLayerName{};
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorPipelineAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
// This is an easter egg to pay tribute to PROJ pipeline syntax
|
||||
// We accept "gdal vector +gdal=pipeline +step +gdal=read +input=poly.gpkg +step +gdal=reproject +dst-crs=EPSG:32632 +step +gdal=write +output=out.gpkg +overwrite"
|
||||
// as an alternative to (recommended):
|
||||
// "gdal vector pipeline ! read poly.gpkg ! reproject--dst-crs=EPSG:32632 ! write out.gpkg --overwrite"
|
||||
#define GDAL_PIPELINE_PROJ_NOSTALGIA
|
||||
|
||||
class GDALVectorPipelineAlgorithm final : public GDALVectorPipelineStepAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "pipeline";
|
||||
static constexpr const char *DESCRIPTION = "Process a vector dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {
|
||||
#ifdef GDAL_PIPELINE_PROJ_NOSTALGIA
|
||||
GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR,
|
||||
"+pipeline",
|
||||
"+gdal=pipeline",
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
GDALVectorPipelineAlgorithm();
|
||||
|
||||
bool
|
||||
ParseCommandLineArguments(const std::vector<std::string> &args) override;
|
||||
|
||||
bool Finalize() override;
|
||||
|
||||
std::string GetUsageForCLI(bool shortUsage,
|
||||
const UsageOptions &usageOptions) const override;
|
||||
|
||||
std::string GetUsageAsJSON() const override;
|
||||
|
||||
/* cppcheck-suppress functionStatic */
|
||||
void SetDataset(GDALDataset *, bool)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_pipeline{};
|
||||
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::unique_ptr<GDALVectorPipelineStepAlgorithm>
|
||||
GetStepAlg(const std::string &name) const;
|
||||
|
||||
GDALAlgorithmRegistry m_stepRegistry{};
|
||||
std::vector<std::unique_ptr<GDALVectorPipelineStepAlgorithm>> m_steps{};
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif
|
|
@ -0,0 +1,100 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "read" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_read.h"
|
||||
|
||||
#include "gdal_priv.h"
|
||||
#include "ogrsf_frmts.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReadAlgorithm::GDALVectorReadAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorReadAlgorithm::GDALVectorReadAlgorithm()
|
||||
: GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddInputArgs(/* hiddenForCLI = */ false);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReadAlgorithmDataset */
|
||||
/************************************************************************/
|
||||
|
||||
namespace
|
||||
{
|
||||
class GDALVectorReadAlgorithmDataset final : public GDALDataset
|
||||
{
|
||||
std::vector<OGRLayer *> m_srcLayers{};
|
||||
|
||||
public:
|
||||
GDALVectorReadAlgorithmDataset() = default;
|
||||
|
||||
void AddLayer(OGRLayer *poSrcLayer)
|
||||
{
|
||||
m_srcLayers.push_back(poSrcLayer);
|
||||
}
|
||||
|
||||
int GetLayerCount() override
|
||||
{
|
||||
return static_cast<int>(m_srcLayers.size());
|
||||
}
|
||||
|
||||
OGRLayer *GetLayer(int idx) override
|
||||
{
|
||||
return idx >= 0 && idx < GetLayerCount() ? m_srcLayers[idx] : nullptr;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReadAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorReadAlgorithm::RunImpl(GDALProgressFunc, void *)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDataset());
|
||||
CPLAssert(m_outputDataset.GetName().empty());
|
||||
CPLAssert(!m_outputDataset.GetDataset());
|
||||
|
||||
if (m_inputLayerNames.empty())
|
||||
{
|
||||
m_outputDataset.Set(m_inputDataset.GetDataset(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto poSrcDS = m_inputDataset.GetDataset();
|
||||
auto poOutDS = std::make_unique<GDALVectorReadAlgorithmDataset>();
|
||||
poOutDS->SetDescription(poSrcDS->GetDescription());
|
||||
for (const auto &srcLayerName : m_inputLayerNames)
|
||||
{
|
||||
auto poSrcLayer = poSrcDS->GetLayerByName(srcLayerName.c_str());
|
||||
if (!poSrcLayer)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Cannot find source layer '%s'",
|
||||
srcLayerName.c_str());
|
||||
return false;
|
||||
}
|
||||
poOutDS->AddLayer(poSrcLayer);
|
||||
}
|
||||
m_outputDataset.Set(std::move(poOutDS));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,44 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "read" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_READ_INCLUDED
|
||||
#define GDALALG_VECTOR_READ_INCLUDED
|
||||
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReadAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorReadAlgorithm final : public GDALVectorPipelineStepAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "read";
|
||||
static constexpr const char *DESCRIPTION = "Read a vector dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorReadAlgorithm();
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc, void *) override;
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif
|
|
@ -0,0 +1,148 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "reproject" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_reproject.h"
|
||||
|
||||
#include "gdal_priv.h"
|
||||
#include "ogr_spatialref.h"
|
||||
#include "ogrsf_frmts.h"
|
||||
#include "ogrwarpedlayer.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReprojectAlgorithm::GDALVectorReprojectAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorReprojectAlgorithm::GDALVectorReprojectAlgorithm()
|
||||
: GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs).AddHiddenAlias("s_srs");
|
||||
AddArg("dst-crs", 'd', _("Destination CRS"), &m_dstCrs)
|
||||
.SetRequired()
|
||||
.AddHiddenAlias("t_srs");
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReprojectAlgorithmDataset */
|
||||
/************************************************************************/
|
||||
|
||||
namespace
|
||||
{
|
||||
class GDALVectorReprojectAlgorithmDataset final : public GDALDataset
|
||||
{
|
||||
std::vector<std::unique_ptr<OGRLayer>> m_layers{};
|
||||
|
||||
public:
|
||||
GDALVectorReprojectAlgorithmDataset() = default;
|
||||
|
||||
void AddLayer(std::unique_ptr<OGRLayer> poLayer)
|
||||
{
|
||||
m_layers.push_back(std::move(poLayer));
|
||||
}
|
||||
|
||||
int GetLayerCount() override
|
||||
{
|
||||
return static_cast<int>(m_layers.size());
|
||||
}
|
||||
|
||||
OGRLayer *GetLayer(int idx) override
|
||||
{
|
||||
return idx >= 0 && idx < GetLayerCount() ? m_layers[idx].get()
|
||||
: nullptr;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReprojectAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorReprojectAlgorithm::RunImpl(GDALProgressFunc, void *)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDataset());
|
||||
CPLAssert(m_outputDataset.GetName().empty());
|
||||
CPLAssert(!m_outputDataset.GetDataset());
|
||||
|
||||
std::unique_ptr<OGRSpatialReference> poSrcCRS;
|
||||
if (!m_srsCrs.empty())
|
||||
{
|
||||
poSrcCRS = std::make_unique<OGRSpatialReference>();
|
||||
if (poSrcCRS->SetFromUserInput(m_srsCrs.c_str()) != OGRERR_NONE)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Invalid value for '--src-crs'");
|
||||
return false;
|
||||
}
|
||||
poSrcCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
}
|
||||
|
||||
OGRSpatialReference oDstCRS;
|
||||
if (oDstCRS.SetFromUserInput(m_dstCrs.c_str()) != OGRERR_NONE)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Invalid value for '--dst-crs'");
|
||||
return false;
|
||||
}
|
||||
oDstCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
|
||||
auto poSrcDS = m_inputDataset.GetDataset();
|
||||
|
||||
auto reprojectedDataset =
|
||||
std::make_unique<GDALVectorReprojectAlgorithmDataset>();
|
||||
reprojectedDataset->SetDescription(poSrcDS->GetDescription());
|
||||
|
||||
const int nLayerCount = poSrcDS->GetLayerCount();
|
||||
bool ret = true;
|
||||
for (int i = 0; i < nLayerCount; ++i)
|
||||
{
|
||||
auto poSrcLayer = poSrcDS->GetLayer(i);
|
||||
ret = ret && (poSrcLayer != nullptr);
|
||||
if (poSrcLayer)
|
||||
{
|
||||
OGRSpatialReference *poSrcLayerCRS;
|
||||
if (poSrcCRS)
|
||||
poSrcLayerCRS = poSrcCRS.get();
|
||||
else
|
||||
poSrcLayerCRS = poSrcLayer->GetSpatialRef();
|
||||
if (!poSrcLayerCRS)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Layer '%s' has no spatial reference system",
|
||||
poSrcLayer->GetName());
|
||||
return false;
|
||||
}
|
||||
auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
|
||||
OGRCreateCoordinateTransformation(poSrcLayerCRS, &oDstCRS));
|
||||
auto poReversedCT = std::unique_ptr<OGRCoordinateTransformation>(
|
||||
OGRCreateCoordinateTransformation(&oDstCRS, poSrcLayerCRS));
|
||||
ret = ret && (poCT != nullptr) && (poReversedCT != nullptr);
|
||||
if (poCT && poReversedCT)
|
||||
{
|
||||
reprojectedDataset->AddLayer(std::make_unique<OGRWarpedLayer>(
|
||||
poSrcLayer, /* iGeomField = */ 0,
|
||||
/*bTakeOwnership = */ false, poCT.release(),
|
||||
poReversedCT.release()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_outputDataset.Set(std::move(reprojectedDataset));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,48 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "reproject" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_REPROJECT_INCLUDED
|
||||
#define GDALALG_VECTOR_REPROJECT_INCLUDED
|
||||
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorReprojectAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorReprojectAlgorithm final
|
||||
: public GDALVectorPipelineStepAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "reproject";
|
||||
static constexpr const char *DESCRIPTION = "Reproject.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorReprojectAlgorithm();
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
std::string m_srsCrs{};
|
||||
std::string m_dstCrs{};
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif /* GDALALG_VECTOR_REPROJECT_INCLUDED */
|
|
@ -0,0 +1,106 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "write" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalg_vector_write.h"
|
||||
|
||||
#include "cpl_string.h"
|
||||
#include "gdal_utils.h"
|
||||
#include "gdal_priv.h"
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorWriteAlgorithm::GDALVectorWriteAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALVectorWriteAlgorithm::GDALVectorWriteAlgorithm()
|
||||
: GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL)
|
||||
{
|
||||
AddOutputArgs(/* hiddenForCLI = */ false,
|
||||
/* shortNameOutputLayerAllowed=*/true);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorWriteAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALVectorWriteAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
|
||||
void *pProgressData)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDataset());
|
||||
|
||||
CPLStringList aosOptions;
|
||||
aosOptions.AddString("--invoked-from-gdal-vector-convert");
|
||||
if (!m_overwrite)
|
||||
{
|
||||
aosOptions.AddString("--no-overwrite");
|
||||
}
|
||||
if (m_overwriteLayer)
|
||||
{
|
||||
aosOptions.AddString("-overwrite");
|
||||
}
|
||||
if (m_appendLayer)
|
||||
{
|
||||
aosOptions.AddString("-append");
|
||||
}
|
||||
if (!m_format.empty())
|
||||
{
|
||||
aosOptions.AddString("-of");
|
||||
aosOptions.AddString(m_format.c_str());
|
||||
}
|
||||
for (const auto &co : m_creationOptions)
|
||||
{
|
||||
aosOptions.AddString("-dsco");
|
||||
aosOptions.AddString(co.c_str());
|
||||
}
|
||||
for (const auto &co : m_layerCreationOptions)
|
||||
{
|
||||
aosOptions.AddString("-lco");
|
||||
aosOptions.AddString(co.c_str());
|
||||
}
|
||||
if (!m_outputLayerName.empty())
|
||||
{
|
||||
aosOptions.AddString("-nln");
|
||||
aosOptions.AddString(m_outputLayerName.c_str());
|
||||
}
|
||||
if (pfnProgress && pfnProgress != GDALDummyProgress)
|
||||
{
|
||||
aosOptions.AddString("-progress");
|
||||
}
|
||||
|
||||
GDALVectorTranslateOptions *psOptions =
|
||||
GDALVectorTranslateOptionsNew(aosOptions.List(), nullptr);
|
||||
GDALVectorTranslateOptionsSetProgress(psOptions, pfnProgress,
|
||||
pProgressData);
|
||||
|
||||
GDALDatasetH hOutDS = GDALDataset::ToHandle(m_outputDataset.GetDataset());
|
||||
GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDataset());
|
||||
auto poRetDS = GDALDataset::FromHandle(
|
||||
GDALVectorTranslate(m_outputDataset.GetName().c_str(), hOutDS, 1,
|
||||
&hSrcDS, psOptions, nullptr));
|
||||
GDALVectorTranslateOptionsFree(psOptions);
|
||||
if (!poRetDS)
|
||||
return false;
|
||||
|
||||
if (!hOutDS)
|
||||
{
|
||||
m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,44 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "write" step of "vector pipeline"
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef GDALALG_VECTOR_WRITE_INCLUDED
|
||||
#define GDALALG_VECTOR_WRITE_INCLUDED
|
||||
|
||||
#include "gdalalg_vector_pipeline.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALVectorWriteAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALVectorWriteAlgorithm final : public GDALVectorPipelineStepAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "write";
|
||||
static constexpr const char *DESCRIPTION = "Write a vector dataset.";
|
||||
static constexpr const char *HELP_URL = ""; // TODO
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
GDALVectorWriteAlgorithm();
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif /* GDALALG_VECTOR_WRITE_INCLUDED */
|
|
@ -453,6 +453,12 @@ struct GDALVectorTranslateOptions
|
|||
|
||||
/*! Whether to unset geometry coordinate precision */
|
||||
bool bUnsetCoordPrecision = false;
|
||||
|
||||
/*! set to true to prevent overwriting existing dataset */
|
||||
bool bNoOverwrite = false;
|
||||
|
||||
/*! set to true to prevent if called from "gdal vector convert" */
|
||||
bool bInvokedFromGdalVectorConvert = false;
|
||||
};
|
||||
|
||||
struct TargetLayerInfo
|
||||
|
@ -2514,7 +2520,17 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS,
|
|||
bError = strcmp(psOptions->osNewLayerName.c_str(),
|
||||
psOptions->aosLayers[0]) == 0;
|
||||
else if (psOptions->osSQLStatement.empty())
|
||||
bError = true;
|
||||
{
|
||||
if (psOptions->aosLayers.empty() && poDS->GetLayerCount() == 1)
|
||||
{
|
||||
bError = strcmp(psOptions->osNewLayerName.c_str(),
|
||||
poDS->GetLayer(0)->GetName()) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bError = true;
|
||||
}
|
||||
}
|
||||
if (bError)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_IllegalArg,
|
||||
|
@ -2607,6 +2623,27 @@ GDALDatasetH GDALVectorTranslate(const char *pszDest, GDALDatasetH hDstDS,
|
|||
{
|
||||
GDALDriverManager *poDM = GetGDALDriverManager();
|
||||
|
||||
if (psOptions->bNoOverwrite && !EQUAL(pszDest, ""))
|
||||
{
|
||||
VSIStatBufL sStat;
|
||||
if (VSIStatL(pszDest, &sStat) == 0)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"File '%s' already exists. Specify the --overwrite "
|
||||
"option to overwrite it.",
|
||||
pszDest);
|
||||
return nullptr;
|
||||
}
|
||||
else if (std::unique_ptr<GDALDataset>(GDALDataset::Open(pszDest)))
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Dataset '%s' already exists. Specify the --overwrite "
|
||||
"option to overwrite it.",
|
||||
pszDest);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (psOptions->osFormat.empty())
|
||||
{
|
||||
if (aoDrivers.empty())
|
||||
|
@ -4896,10 +4933,20 @@ SetupTargetLayer::Setup(OGRLayer *poSrcLayer, const char *pszNewLayerName,
|
|||
/* -------------------------------------------------------------------- */
|
||||
else if (!bAppend && !m_bNewDataSource)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Layer %s already exists, and -append not specified.\n"
|
||||
" Consider using -append, or -overwrite.",
|
||||
pszNewLayerName);
|
||||
if (psOptions->bInvokedFromGdalVectorConvert)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Layer %s already exists, and --append not specified. "
|
||||
"Consider using --append, or --overwrite-layer.",
|
||||
pszNewLayerName);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"Layer %s already exists, and -append not specified.\n"
|
||||
" Consider using -append, or -overwrite.",
|
||||
pszNewLayerName);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
|
@ -7935,6 +7982,16 @@ static std::unique_ptr<GDALArgumentParser> GDALVectorTranslateOptionsGetParser(
|
|||
.help(_("Disable copying of metadata from source dataset and layers "
|
||||
"into target dataset and layers."));
|
||||
|
||||
// Undocumented option used by gdal vector convert
|
||||
argParser->add_argument("--no-overwrite")
|
||||
.store_into(psOptions->bNoOverwrite)
|
||||
.hidden();
|
||||
|
||||
// Undocumented option used by gdal vector convert
|
||||
argParser->add_argument("--invoked-from-gdal-vector-convert")
|
||||
.store_into(psOptions->bInvokedFromGdalVectorConvert)
|
||||
.hidden();
|
||||
|
||||
if (psOptionsForBinary)
|
||||
{
|
||||
argParser->add_argument("dst_dataset_name")
|
||||
|
|
|
@ -2423,6 +2423,12 @@ static std::unique_ptr<GDALArgumentParser> GDALVectorInfoOptionsGetParser(
|
|||
})
|
||||
.help(_("Format/driver name(s) to try when opening the input file."));
|
||||
|
||||
argParser->add_argument("-stdout")
|
||||
.flag()
|
||||
.store_into(psOptions->bStdoutOutput)
|
||||
.hidden()
|
||||
.help(_("Directly output on stdout (format=text mode only)"));
|
||||
|
||||
auto &argFilename = argParser->add_argument("filename")
|
||||
.action(
|
||||
[psOptionsForBinary](const std::string &s)
|
||||
|
|
|
@ -75,6 +75,7 @@ add_executable(
|
|||
test_cpl.cpp
|
||||
test_gdal.cpp
|
||||
test_gdal_aaigrid.cpp
|
||||
test_gdal_algorithm.cpp
|
||||
test_gdal_dted.cpp
|
||||
test_gdal_gtiff.cpp
|
||||
test_gdal_minmax_element.cpp
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,184 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: Test gdalalgorithm.h functionality
|
||||
# Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def test_algorithm(tmp_path):
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
|
||||
names = reg.GetAlgNames()
|
||||
assert isinstance(names, list)
|
||||
assert "raster" in names
|
||||
|
||||
alg = reg.InstantiateAlg("non_existing")
|
||||
assert alg is None
|
||||
|
||||
with pytest.raises(Exception, match="NULL"):
|
||||
reg.InstantiateAlg(None)
|
||||
|
||||
alg = reg.InstantiateAlg("raster")
|
||||
assert alg
|
||||
|
||||
assert alg.GetName() == "raster"
|
||||
assert alg.GetDescription() == "Raster commands."
|
||||
assert alg.GetLongDescription() == ""
|
||||
assert alg.GetHelpFullURL() == ""
|
||||
|
||||
assert alg.HasSubAlgorithms()
|
||||
|
||||
with pytest.raises(Exception, match="NULL"):
|
||||
alg.InstantiateSubAlgorithm(None)
|
||||
assert alg.InstantiateSubAlgorithm("non_existing") is None
|
||||
|
||||
subalgs = alg.GetSubAlgorithmNames()
|
||||
assert isinstance(subalgs, list)
|
||||
assert "info" in subalgs
|
||||
|
||||
convert = alg.InstantiateSubAlgorithm("convert")
|
||||
assert convert
|
||||
|
||||
assert json.loads(convert.GetUsageAsJSON())["name"] == "convert"
|
||||
|
||||
outfilename = str(tmp_path / "out.tif")
|
||||
assert convert.ParseCommandLineArguments(["data/byte.tif", outfilename])
|
||||
|
||||
last_pct = [0]
|
||||
|
||||
def my_progress(pct, msg, user_data):
|
||||
last_pct[0] = pct
|
||||
return True
|
||||
|
||||
assert convert.Run(my_progress)
|
||||
assert last_pct[0] == 1.0
|
||||
|
||||
assert convert.Finalize()
|
||||
|
||||
with gdal.Open(outfilename) as ds:
|
||||
assert ds.GetRasterBand(1).Checksum() == 4672
|
||||
|
||||
gdal.Unlink(outfilename)
|
||||
|
||||
last_pct = [0]
|
||||
|
||||
convert = alg.InstantiateSubAlgorithm("convert")
|
||||
assert convert.ParseRunAndFinalize(["data/byte.tif", outfilename], my_progress)
|
||||
assert last_pct[0] == 1.0
|
||||
|
||||
assert convert.Finalize()
|
||||
|
||||
with gdal.Open(outfilename) as ds:
|
||||
assert ds.GetRasterBand(1).Checksum() == 4672
|
||||
|
||||
argNames = convert.GetArgNames()
|
||||
assert isinstance(argNames, list)
|
||||
assert "input" in argNames
|
||||
|
||||
with pytest.raises(Exception):
|
||||
convert.GetArg(None)
|
||||
|
||||
assert convert.GetArg("non_existing") is None
|
||||
|
||||
arg = convert.GetArg("append")
|
||||
assert arg.GetName() == "append"
|
||||
assert arg.GetType() == gdal.GAAT_BOOLEAN
|
||||
assert arg.GetShortName() == ""
|
||||
assert arg.GetDescription() == "Append as a subdataset to existing output"
|
||||
assert arg.GetAliases() is None
|
||||
assert arg.GetMetaVar() == ""
|
||||
assert arg.GetCategory() == "Base"
|
||||
assert not arg.IsPositional()
|
||||
assert not arg.IsRequired()
|
||||
assert arg.GetMinCount() == 0
|
||||
assert arg.GetMaxCount() == 1
|
||||
assert arg.GetPackedValuesAllowed()
|
||||
assert arg.GetRepeatedArgAllowed()
|
||||
assert arg.GetChoices() is None
|
||||
assert not arg.IsExplicitlySet()
|
||||
assert arg.HasDefaultValue()
|
||||
assert not arg.IsHiddenForCLI()
|
||||
assert not arg.IsOnlyForCLI()
|
||||
assert arg.IsInput()
|
||||
assert not arg.IsOutput()
|
||||
assert arg.GetMutualExclusionGroup() == "overwrite-append"
|
||||
assert arg.GetAsBoolean() is False
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsInteger()
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsDouble()
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsString()
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsDatasetValue()
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsIntegerList()
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsDoubleList()
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsStringList()
|
||||
assert arg.Get() is False
|
||||
|
||||
arg = convert.GetArg("input")
|
||||
with pytest.raises(Exception, match="must only be called on arguments of type"):
|
||||
arg.GetAsBoolean()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Test running an algorithm without using the ParseCommandLineArguments()
|
||||
# interface, but directly setting argument values.
|
||||
|
||||
|
||||
def test_algorithm_dataset_value(tmp_path):
|
||||
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
raster = reg.InstantiateAlg("raster")
|
||||
convert = raster.InstantiateSubAlgorithm("convert")
|
||||
|
||||
input_arg = convert.GetArg("input")
|
||||
input_arg_value = input_arg.Get()
|
||||
input_arg_value.SetName("data/byte.tif")
|
||||
assert input_arg_value.GetName() == "data/byte.tif"
|
||||
assert input_arg_value.GetDataset() is None
|
||||
|
||||
output_arg = convert.GetArg("output")
|
||||
outfilename = str(tmp_path / "out.tif")
|
||||
output_arg_value = output_arg.Get()
|
||||
output_arg_value.SetName(outfilename)
|
||||
|
||||
assert convert.Run()
|
||||
|
||||
in_ds = input_arg_value.GetDataset()
|
||||
assert in_ds is not None
|
||||
assert input_arg_value.IsDatasetOwned()
|
||||
|
||||
out_ds = output_arg_value.GetDataset()
|
||||
assert out_ds is not None
|
||||
assert output_arg_value.IsDatasetOwned()
|
||||
assert out_ds.GetRasterBand(1).Checksum() == 4672
|
||||
|
||||
output_arg_value.SetDatasetWithoutOwnership(None)
|
||||
output_arg_value.SetDatasetWithOwnership(None)
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="Dataset object 'output' is created by algorithm and cannot be set as an input",
|
||||
):
|
||||
output_arg.SetDatasetWithoutOwnership(None)
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="Dataset object 'output' is created by algorithm and cannot be set as an input",
|
||||
):
|
||||
output_arg.SetDatasetWithOwnership(None)
|
|
@ -105,5 +105,10 @@ for i in $prefix/include/*.h; do
|
|||
${CXX} -Wall -Wpedantic -std=c++11 -c $(${GDAL_CONFIG} --cflags) $i;
|
||||
done
|
||||
|
||||
echo "Test that we can compile all headers with C++17 using ${CXX}"
|
||||
for i in $prefix/include/*.h; do
|
||||
${CXX} -Wall -Wpedantic -std=c++17 -c $(${GDAL_CONFIG} --cflags) $i;
|
||||
done
|
||||
|
||||
echo "$ERRORS tests failed out of $NTESTS"
|
||||
exit $ERRORS
|
||||
|
|
|
@ -35,7 +35,7 @@ def get_cli_utility_path_internal(cli_utility_name):
|
|||
if sys.platform == "win32":
|
||||
cli_utility_path = cli_utility_path.replace("\\", "/")
|
||||
if os.path.isfile(cli_utility_path):
|
||||
ret = gdaltest.runexternal(cli_utility_path + " --utility_version")
|
||||
ret = gdaltest.runexternal(cli_utility_path + " --version")
|
||||
|
||||
if "GDAL" in ret:
|
||||
return cli_utility_path
|
||||
|
@ -46,7 +46,7 @@ def get_cli_utility_path_internal(cli_utility_name):
|
|||
print(f"Could not find {cli_utility_name} in {build_dir}/apps. Trying with PATH")
|
||||
try:
|
||||
cli_utility_path = cli_utility_name
|
||||
ret = gdaltest.runexternal(cli_utility_path + " --utility_version")
|
||||
ret = gdaltest.runexternal(cli_utility_path + " --version")
|
||||
|
||||
if "GDAL" in ret:
|
||||
return cli_utility_path
|
||||
|
@ -72,6 +72,14 @@ def get_cli_utility_path(cli_utility_name):
|
|||
#
|
||||
|
||||
|
||||
def get_gdal_path():
|
||||
return get_cli_utility_path("gdal")
|
||||
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
|
||||
|
||||
def get_gdalinfo_path():
|
||||
return get_cli_utility_path("gdalinfo")
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal' program testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import json
|
||||
|
||||
import gdaltest
|
||||
import pytest
|
||||
import test_cli_utilities
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
test_cli_utilities.get_gdal_path() is None, reason="gdal binary not available"
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def gdal_path():
|
||||
return test_cli_utilities.get_gdal_path()
|
||||
|
||||
|
||||
def test_gdal_no_argument(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(f"{gdal_path}")
|
||||
assert out == ""
|
||||
assert "gdal: Missing command name" in err
|
||||
assert "Usage: " in err
|
||||
assert "ret code = 1" in err
|
||||
|
||||
|
||||
def test_gdal_help(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(f"{gdal_path} --help")
|
||||
assert out.startswith("Usage: ")
|
||||
assert err == ""
|
||||
|
||||
|
||||
def test_gdal_json_usage(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(f"{gdal_path} --json-usage")
|
||||
assert out.startswith("{")
|
||||
assert err == ""
|
||||
assert "description" in json.loads(out)
|
||||
|
||||
|
||||
def test_gdal_invalid_command_line(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(f"{gdal_path} --invalid")
|
||||
assert out == ""
|
||||
assert "Long name option '--invalid' is unknown" in err
|
||||
assert "Usage: " in err
|
||||
assert "ret code = 1" in err
|
||||
|
||||
|
||||
def test_gdal_failure_during_run(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(
|
||||
f"{gdal_path} raster info i_do_not_exist"
|
||||
)
|
||||
assert out == ""
|
||||
assert "i_do_not_exist:" in err
|
||||
assert "Usage: " in err
|
||||
assert "ret code = 1" in err
|
||||
|
||||
|
||||
def test_gdal_success(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(f"{gdal_path} data/utmsmall.tif")
|
||||
assert err == ""
|
||||
assert "description" in json.loads(out)
|
||||
|
||||
|
||||
def test_gdal_failure_during_finalize(gdal_path):
|
||||
|
||||
out, err = gdaltest.runexternal_out_and_err(
|
||||
f"{gdal_path} raster convert ../gcore/data/byte.tif /vsimem/out.tif||maxlength=100"
|
||||
)
|
||||
assert "ret code = 1" in err
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal convert' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_convert_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
return reg.InstantiateAlg("convert")
|
||||
|
||||
|
||||
def test_gdalalg_convert_raster(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.tif")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(["data/utmsmall.tif", out_filename])
|
||||
|
||||
with gdal.Open(out_filename) as ds:
|
||||
assert ds.GetRasterBand(1).Checksum() == 50054
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_convert_vector(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.gpkg")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(["../ogr/data/poly.shp", out_filename])
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayer(0).GetFeatureCount() == 10
|
||||
|
||||
|
||||
def test_gdalalg_convert_on_raster_invalid_arg():
|
||||
convert = get_convert_alg()
|
||||
with pytest.raises(Exception, match="Long name option '--invalid' is unknown"):
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["--of=MEM", "--invalid", "data/utmsmall.tif", "out"]
|
||||
)
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal info' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_info_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
return reg.InstantiateAlg("info")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"args",
|
||||
[
|
||||
(["--format=text", "data/utmsmall.tif"]),
|
||||
(["--format=text", "data/utmsmall.tif", "--checksum"]),
|
||||
],
|
||||
)
|
||||
def test_gdalalg_info_on_raster(args):
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(args)
|
||||
output_string = info.GetActualAlgorithm().GetArg("output-string").Get()
|
||||
assert output_string.startswith("Driver: GTiff/GeoTIFF")
|
||||
|
||||
|
||||
def test_gdalalg_info_on_raster_invalid_arg():
|
||||
info = get_info_alg()
|
||||
with pytest.raises(Exception, match="Long name option '--invalid' is unknown"):
|
||||
assert info.ParseRunAndFinalize(
|
||||
["--format=text", "--invalid", "data/utmsmall.tif"]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"args",
|
||||
[
|
||||
(["--format=text", "data/path.shp"]),
|
||||
(["data/path.shp", "--format=text"]),
|
||||
(["--format=text", "-i", "data/path.shp"]),
|
||||
(["--format=text", "--input", "data/path.shp"]),
|
||||
(["--format=text", "--input=data/path.shp"]),
|
||||
(["--format=text", "--dialect=OGRSQL", "data/path.shp"]),
|
||||
],
|
||||
)
|
||||
def test_gdalalg_info_on_vector(args):
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(args)
|
||||
output_string = info.GetActualAlgorithm().GetArg("output-string").Get()
|
||||
assert output_string.startswith("INFO: Open of")
|
||||
|
||||
|
||||
def test_gdalalg_info_on_vector_invalid_arg():
|
||||
info = get_info_alg()
|
||||
with pytest.raises(Exception, match="Long name option '--invalid' is unknown"):
|
||||
assert info.ParseRunAndFinalize(["--format=text", "--invalid", "data/path.shp"])
|
||||
|
||||
|
||||
def test_gdalalg_info_invalid_arg():
|
||||
info = get_info_alg()
|
||||
with pytest.raises(Exception, match="Long name option '--invalid' is unknown"):
|
||||
assert info.ParseRunAndFinalize(["--invalid"])
|
||||
|
||||
|
||||
def test_gdalalg_info_run_cannot_be_run():
|
||||
info = get_info_alg()
|
||||
ds = gdal.GetDriverByName("MEM").Create("", 1, 1)
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
with pytest.raises(Exception, match="method should not be called directly"):
|
||||
info.Run()
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_info_mixed_raster_vector(tmp_vsimem):
|
||||
|
||||
filename = str(tmp_vsimem / "test.gpkg")
|
||||
gdal.Translate(filename, "data/utmsmall.tif")
|
||||
with gdal.OpenEx(filename, gdal.OF_UPDATE) as ds:
|
||||
ds.CreateLayer("vector_layer")
|
||||
info = get_info_alg()
|
||||
with pytest.raises(Exception, match="has both raster and vector content"):
|
||||
assert info.ParseRunAndFinalize([filename])
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_info_mixed_raster_vector_with_invalid_arg(tmp_vsimem):
|
||||
|
||||
filename = str(tmp_vsimem / "test.gpkg")
|
||||
gdal.Translate(filename, "data/utmsmall.tif")
|
||||
with gdal.OpenEx(filename, gdal.OF_UPDATE) as ds:
|
||||
ds.CreateLayer("vector_layer")
|
||||
info = get_info_alg()
|
||||
with pytest.raises(Exception, match="has both raster and vector content"):
|
||||
assert info.ParseRunAndFinalize([filename, "--invalid"])
|
||||
|
||||
|
||||
def test_gdalalg_info_mixed_run_without_arg(tmp_vsimem):
|
||||
|
||||
info = get_info_alg()
|
||||
info.GetArg("input").Get().SetName("data/utmsmall.tif")
|
||||
with pytest.raises(Exception, match="should not be called directly"):
|
||||
assert info.Run()
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal raster' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_raster_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
return reg.InstantiateAlg("raster")
|
||||
|
||||
|
||||
def test_gdalalg_raster_run_error():
|
||||
info = get_raster_alg()
|
||||
with pytest.raises(Exception, match="method should not be called directly"):
|
||||
info.Run()
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal raster convert' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_convert_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
raster = reg.InstantiateAlg("raster")
|
||||
return raster.InstantiateSubAlgorithm("convert")
|
||||
|
||||
|
||||
def test_gdalalg_raster_convert(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.tif")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(["data/utmsmall.tif", out_filename])
|
||||
|
||||
with gdal.Open(out_filename, gdal.GA_Update) as ds:
|
||||
assert ds.GetRasterBand(1).Checksum() == 50054
|
||||
ds.GetRasterBand(1).Fill(0)
|
||||
|
||||
convert = get_convert_alg()
|
||||
with pytest.raises(
|
||||
Exception, match="already exists. Specify the --overwrite option"
|
||||
):
|
||||
convert.ParseRunAndFinalize(["data/utmsmall.tif", out_filename])
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["--overwrite", "--co=TILED=YES", "data/utmsmall.tif", out_filename]
|
||||
)
|
||||
|
||||
with gdal.Open(out_filename) as ds:
|
||||
assert ds.GetRasterBand(1).GetBlockSize() == [256, 256]
|
||||
assert ds.GetRasterBand(1).Checksum() == 50054
|
||||
|
||||
|
||||
def test_gdalalg_raster_convert_to_mem():
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseCommandLineArguments(["--of=MEM", "data/utmsmall.tif", ""])
|
||||
assert convert.Run()
|
||||
out_ds = convert.GetArg("output").Get().GetDataset()
|
||||
assert out_ds.GetRasterBand(1).Checksum() == 50054
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_raster_convert_append(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.gpkg")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(["data/utmsmall.tif", out_filename])
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
[
|
||||
"data/utmsmall.tif",
|
||||
out_filename,
|
||||
"--append",
|
||||
"--co",
|
||||
"RASTER_TABLE=new_table",
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.Open(out_filename) as ds:
|
||||
assert len(ds.GetSubDatasets()) == 2
|
||||
|
||||
|
||||
def test_gdalalg_raster_convert_error_output_already_set():
|
||||
convert = get_convert_alg()
|
||||
ds = gdal.GetDriverByName("MEM").Create("", 1, 1)
|
||||
convert.GetArg("output").Get().SetDatasetWithoutOwnership(ds)
|
||||
assert convert.ParseCommandLineArguments(["data/utmsmall.tif"])
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="gdal raster convert does not support outputting to an already opened output dataset",
|
||||
):
|
||||
convert.Run()
|
|
@ -0,0 +1,157 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal raster info' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_info_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
raster = reg.InstantiateAlg("raster")
|
||||
return raster.InstantiateSubAlgorithm("info")
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_stdout():
|
||||
import gdaltest
|
||||
import test_cli_utilities
|
||||
|
||||
gdal_path = test_cli_utilities.get_gdal_path()
|
||||
if gdal_path is None:
|
||||
pytest.skip("gdal binary missing")
|
||||
out, err = gdaltest.runexternal_out_and_err(
|
||||
f"{gdal_path} raster info data/utmsmall.tif"
|
||||
)
|
||||
j = json.loads(out)
|
||||
assert len(j["bands"]) == 1
|
||||
|
||||
|
||||
def test_gdalalg_raster_info():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(["--format=text", "data/utmsmall.tif"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
assert output_string.startswith("Driver: GTiff/GeoTIFF")
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_mm_checksum():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(
|
||||
["--format=text", "--mm", "--checksum", "data/utmsmall.tif"]
|
||||
)
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
assert " Computed Min/Max=0.000,255.000" in output_string
|
||||
assert "Checksum=" in output_string
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_stats():
|
||||
info = get_info_alg()
|
||||
ds = gdal.Translate("", "../gcore/data/byte.tif", format="MEM")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
assert info.ParseRunAndFinalize(["--stats"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert "stdDev" in j["bands"][0]
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_approx_stats():
|
||||
info = get_info_alg()
|
||||
ds = gdal.Translate("", "../gcore/data/byte.tif", format="MEM")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
assert info.ParseRunAndFinalize(["--approx-stats"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert "stdDev" in j["bands"][0]
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_hist():
|
||||
info = get_info_alg()
|
||||
ds = gdal.Translate("", "../gcore/data/byte.tif", format="MEM")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
assert info.ParseRunAndFinalize(["--hist"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert "histogram" in j["bands"][0]
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_no_options():
|
||||
info = get_info_alg()
|
||||
ds = gdal.Translate("", "../gcore/data/byte.tif", format="MEM")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
assert info.ParseRunAndFinalize(
|
||||
["--no-gcp", "--no-md", "--no-ct", "--no-fl", "--no-nodata", "--no-mask"]
|
||||
)
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_list_mdd():
|
||||
info = get_info_alg()
|
||||
ds = gdal.Translate("", "../gcore/data/byte.tif", format="MEM")
|
||||
ds.SetMetadataItem("foo", "bar", "MY_DOMAIN")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
assert info.ParseRunAndFinalize(["--list-mdd"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert "MY_DOMAIN" in j["metadata"]["metadataDomains"]
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_mdd_all():
|
||||
info = get_info_alg()
|
||||
ds = gdal.Translate("", "../gcore/data/byte.tif", format="MEM")
|
||||
ds.SetMetadataItem("foo", "bar", "MY_DOMAIN")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
assert info.ParseRunAndFinalize(["--mdd=all"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert j["metadata"] == {
|
||||
"": {"AREA_OR_POINT": "Area"},
|
||||
"MY_DOMAIN": {"foo": "bar"},
|
||||
"DERIVED_SUBDATASETS": {
|
||||
"DERIVED_SUBDATASET_1_NAME": "DERIVED_SUBDATASET:LOGAMPLITUDE:",
|
||||
"DERIVED_SUBDATASET_1_DESC": "log10 of amplitude of input bands from ",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_list_subdataset():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(
|
||||
["--input=../gcore/data/tiff_with_subifds.tif", "--subdataset=2"]
|
||||
)
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert j["description"] == "GTIFF_DIR:2:../gcore/data/tiff_with_subifds.tif"
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_list_subdataset_error():
|
||||
info = get_info_alg()
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="Invalid value for 'subdataset' argument. Should be between 1 and 2",
|
||||
):
|
||||
info.ParseRunAndFinalize(
|
||||
["--input=../gcore/data/tiff_with_subifds.tif", "--subdataset=3"]
|
||||
)
|
||||
|
||||
|
||||
def test_gdalalg_raster_info_list_subdataset_error_cannot_open_subdataset():
|
||||
info = get_info_alg()
|
||||
ds = gdal.GetDriverByName("MEM").Create("", 1, 1)
|
||||
ds.SetMetadataItem("SUBDATASET_1_DESC", "desc", "SUBDATASETS")
|
||||
ds.SetMetadataItem("SUBDATASET_1_NAME", "i_do_not_exist", "SUBDATASETS")
|
||||
info.GetArg("input").SetDatasetWithoutOwnership(ds)
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="i_do_not_exist",
|
||||
):
|
||||
info.ParseRunAndFinalize(["--subdataset=1"])
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal vector' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_vector_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
return reg.InstantiateAlg("vector")
|
||||
|
||||
|
||||
def test_gdalalg_vector_run_error():
|
||||
info = get_vector_alg()
|
||||
with pytest.raises(Exception, match="method should not be called directly"):
|
||||
info.Run()
|
|
@ -0,0 +1,165 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal vector convert' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_convert_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
vector = reg.InstantiateAlg("vector")
|
||||
return vector.InstantiateSubAlgorithm("convert")
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_vector_convert_base(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.gpkg")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(["../ogr/data/poly.shp", out_filename])
|
||||
|
||||
with gdal.OpenEx(out_filename, gdal.OF_UPDATE) as ds:
|
||||
assert ds.GetLayer(0).GetFeatureCount() == 10
|
||||
for i in range(10):
|
||||
ds.GetLayer(0).DeleteFeature(i + 1)
|
||||
|
||||
convert = get_convert_alg()
|
||||
with pytest.raises(
|
||||
Exception, match="already exists. Specify the --overwrite option"
|
||||
):
|
||||
convert.ParseRunAndFinalize(["../ogr/data/poly.shp", out_filename])
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayer(0).GetFeatureCount() == 0
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["--overwrite", "../ogr/data/poly.shp", out_filename]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayer(0).GetFeatureCount() == 10
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["--append", "../ogr/data/poly.shp", out_filename]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayer(0).GetFeatureCount() == 20
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["--update", "--nln", "layer2", "../ogr/data/poly.shp", out_filename]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayerByName("poly").GetFeatureCount() == 20
|
||||
assert ds.GetLayerByName("layer2").GetFeatureCount() == 10
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
[
|
||||
"--of",
|
||||
"GPKG",
|
||||
"--overwrite-layer",
|
||||
"--nln",
|
||||
"poly",
|
||||
"../ogr/data/poly.shp",
|
||||
out_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayerByName("poly").GetFeatureCount() == 10
|
||||
assert ds.GetLayerByName("layer2").GetFeatureCount() == 10
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_vector_convert_dsco(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.gpkg")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["../ogr/data/poly.shp", out_filename, "--co", "ADD_GPKG_OGR_CONTENTS=NO"]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
with ds.ExecuteSQL(
|
||||
"SELECT * FROM sqlite_master WHERE name = 'gpkg_ogr_contents'"
|
||||
) as lyr:
|
||||
assert lyr.GetFeatureCount() == 0
|
||||
|
||||
|
||||
@pytest.mark.require_driver("GPKG")
|
||||
def test_gdalalg_vector_convert_lco(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.gpkg")
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["../ogr/data/poly.shp", out_filename, "--lco", "FID=my_fid"]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayer(0).GetFIDColumn() == "my_fid"
|
||||
|
||||
|
||||
def test_gdalalg_vector_convert_progress(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.shp")
|
||||
|
||||
last_pct = [0]
|
||||
|
||||
def my_progress(pct, msg, user_data):
|
||||
last_pct[0] = pct
|
||||
return True
|
||||
|
||||
convert = get_convert_alg()
|
||||
assert convert.ParseRunAndFinalize(
|
||||
["../ogr/data/poly.shp", out_filename], my_progress
|
||||
)
|
||||
|
||||
assert last_pct[0] == 1.0
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetLayer(0).GetFeatureCount() == 10
|
||||
|
||||
|
||||
def test_gdalalg_vector_wrong_layer_name(tmp_vsimem):
|
||||
|
||||
convert = get_convert_alg()
|
||||
with pytest.raises(Exception, match="Couldn't fetch requested layer"):
|
||||
convert.ParseRunAndFinalize(
|
||||
[
|
||||
"../ogr/data/poly.shp",
|
||||
"--of=Memory",
|
||||
"--output=empty",
|
||||
"--layer",
|
||||
"invalid",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_gdalalg_vector_convert_error_output_not_set():
|
||||
convert = get_convert_alg()
|
||||
convert.GetArg("input").Get().SetName("../ogr/data/poly.shp")
|
||||
convert.GetArg("output").Set(convert.GetArg("output").Get())
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="convert: Argument 'output' has no dataset object or dataset name",
|
||||
):
|
||||
convert.Run()
|
|
@ -0,0 +1,115 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal vector info' testing
|
||||
# Author: Even Rouault <even dot rouault @ spatialys.com>
|
||||
#
|
||||
###############################################################################
|
||||
# Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
###############################################################################
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from osgeo import gdal
|
||||
|
||||
|
||||
def get_info_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
vector = reg.InstantiateAlg("vector")
|
||||
return vector.InstantiateSubAlgorithm("info")
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_stdout():
|
||||
import gdaltest
|
||||
import test_cli_utilities
|
||||
|
||||
gdal_path = test_cli_utilities.get_gdal_path()
|
||||
if gdal_path is None:
|
||||
pytest.skip("gdal binary missing")
|
||||
out, err = gdaltest.runexternal_out_and_err(
|
||||
f"{gdal_path} vector info data/path.shp"
|
||||
)
|
||||
j = json.loads(out)
|
||||
assert j["layers"][0]["name"] == "path"
|
||||
assert "features" not in j["layers"][0]
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_text():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(["--format=text", "data/path.shp"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
assert output_string.startswith("INFO: Open of")
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_json():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(["data/path.shp"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert j["layers"][0]["name"] == "path"
|
||||
assert "features" not in j["layers"][0]
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_features():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(["--features", "data/path.shp"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert "features" in j["layers"][0]
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_sql():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(
|
||||
["--sql", "SELECT 1 AS foo FROM path", "data/path.shp"]
|
||||
)
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert len(j["layers"][0]["fields"]) == 1
|
||||
assert j["layers"][0]["fields"][0]["name"] == "foo"
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_layer():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(["-l", "path", "data/path.shp"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert j["layers"][0]["name"] == "path"
|
||||
|
||||
|
||||
def test_gdalalg_vector_info_wrong_layer():
|
||||
info = get_info_alg()
|
||||
with pytest.raises(Exception, match="Couldn't fetch requested layer"):
|
||||
info.ParseRunAndFinalize(["-l", "invalid", "data/path.shp"])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cond,featureCount", [("0", 0), ("1", 1)])
|
||||
def test_gdalalg_vector_info_where(cond, featureCount):
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(["--where", cond, "data/path.shp"])
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert j["layers"][0]["featureCount"] == featureCount
|
||||
|
||||
|
||||
@pytest.mark.require_driver("SQLite")
|
||||
def test_gdalalg_vector_info_dialect():
|
||||
info = get_info_alg()
|
||||
assert info.ParseRunAndFinalize(
|
||||
[
|
||||
"--features",
|
||||
"--dialect",
|
||||
"SQLite",
|
||||
"--sql",
|
||||
"SELECT sqlite_version() AS version",
|
||||
"data/path.shp",
|
||||
]
|
||||
)
|
||||
output_string = info.GetArg("output-string").Get()
|
||||
j = json.loads(output_string)
|
||||
assert j["layers"][0]["features"][0]["properties"]["version"].startswith("3.")
|
|
@ -2,6 +2,8 @@
|
|||
add_library(
|
||||
gcore OBJECT
|
||||
gdal_adbc.cpp
|
||||
gdalalgorithm.cpp
|
||||
gdalalgorithmregistry.cpp
|
||||
gdalopeninfo.cpp
|
||||
gdaldriver.cpp
|
||||
gdaldrivermanager.cpp
|
||||
|
@ -199,6 +201,7 @@ target_public_header(
|
|||
HEADERS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/gdal_version_full/gdal_version.h
|
||||
gdal.h
|
||||
gdalalgorithm.h
|
||||
gdaljp2metadata.h
|
||||
gdaljp2abstractdataset.h
|
||||
gdal_frmts.h
|
||||
|
|
|
@ -3786,7 +3786,8 @@ int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
|
|||
bool bJSON = false;
|
||||
for (int i = 1; i < nArgc; i++)
|
||||
{
|
||||
if (strcmp(papszArgv[i], "-json") == 0)
|
||||
if (strcmp(papszArgv[i], "-json") == 0 ||
|
||||
strcmp(papszArgv[i], "--json") == 0)
|
||||
{
|
||||
bJSON = true;
|
||||
break;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,210 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: GDALAlgorithm class
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
****************************************************************************/
|
||||
|
||||
#include "gdalalgorithm.h"
|
||||
#include "gdalalg_main.h"
|
||||
|
||||
#include "cpl_vsi.h"
|
||||
|
||||
#include "gdal_priv.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistry::~GDALAlgorithmRegistry() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALAlgorithmRegistry::~GDALAlgorithmRegistry() = default;
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistry::Register() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALAlgorithmRegistry::Register(const GDALAlgorithmRegistry::AlgInfo &info)
|
||||
{
|
||||
if (cpl::contains(m_mapNameToInfo, info.m_name))
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"GDAL algorithm '%s' already registered!",
|
||||
info.m_name.c_str());
|
||||
return false;
|
||||
}
|
||||
for (const std::string &alias : info.m_aliases)
|
||||
{
|
||||
if (cpl::contains(m_mapAliasToInfo, alias) ||
|
||||
cpl::contains(m_mapHiddenAliasToInfo, alias))
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"An algorithm with alias '%s' is already registered!",
|
||||
alias.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_mapNameToInfo[info.m_name] = info;
|
||||
bool hidden = false;
|
||||
for (const std::string &alias : info.m_aliases)
|
||||
{
|
||||
if (alias == HIDDEN_ALIAS_SEPARATOR)
|
||||
hidden = true;
|
||||
else if (hidden)
|
||||
m_mapAliasToInfo[alias] = info;
|
||||
else
|
||||
m_mapHiddenAliasToInfo[alias] = info;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistry::Instantiate() */
|
||||
/************************************************************************/
|
||||
|
||||
std::unique_ptr<GDALAlgorithm>
|
||||
GDALAlgorithmRegistry::Instantiate(const std::string &name) const
|
||||
{
|
||||
auto iter = m_mapNameToInfo.find(name);
|
||||
if (iter == m_mapNameToInfo.end())
|
||||
{
|
||||
iter = m_mapAliasToInfo.find(name);
|
||||
if (iter == m_mapAliasToInfo.end())
|
||||
{
|
||||
iter = m_mapHiddenAliasToInfo.find(name);
|
||||
if (iter == m_mapHiddenAliasToInfo.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto alg = iter->second.m_creationFunc();
|
||||
alg->m_aliases = iter->second.m_aliases;
|
||||
return alg;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistry::GetNames() */
|
||||
/************************************************************************/
|
||||
|
||||
std::vector<std::string> GDALAlgorithmRegistry::GetNames() const
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
for (const auto &iter : m_mapNameToInfo)
|
||||
{
|
||||
res.push_back(iter.first);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALGlobalAlgorithmRegistry::GetSingleton() */
|
||||
/************************************************************************/
|
||||
|
||||
/* static */ GDALGlobalAlgorithmRegistry &
|
||||
GDALGlobalAlgorithmRegistry::GetSingleton()
|
||||
{
|
||||
static GDALGlobalAlgorithmRegistry singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALGlobalAlgorithmRegistry::Instantiate() */
|
||||
/************************************************************************/
|
||||
|
||||
std::unique_ptr<GDALAlgorithm>
|
||||
GDALGlobalAlgorithmRegistry::Instantiate(const std::string &name) const
|
||||
{
|
||||
if (name == GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME)
|
||||
return std::make_unique<GDALMainAlgorithm>();
|
||||
return GDALAlgorithmRegistry::Instantiate(name);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* struct GDALAlgorithmRegistryHS */
|
||||
/************************************************************************/
|
||||
|
||||
struct GDALAlgorithmRegistryHS
|
||||
{
|
||||
GDALAlgorithmRegistry *ptr = nullptr;
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALGetGlobalAlgorithmRegistry() */
|
||||
/************************************************************************/
|
||||
|
||||
/** Gets a handle to the GDALGetGlobalAlgorithmRegistry which references
|
||||
* all available top-level GDAL algorithms ("raster", "vector", etc.)
|
||||
*
|
||||
* The handle must be released with GDALAlgorithmRegistryRelease() (but
|
||||
* this does not destroy the GDALAlgorithmRegistryRelease singleton).
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
GDALAlgorithmRegistryH GDALGetGlobalAlgorithmRegistry()
|
||||
{
|
||||
auto ret = std::make_unique<GDALAlgorithmRegistryHS>();
|
||||
ret->ptr = &(GDALGlobalAlgorithmRegistry::GetSingleton());
|
||||
return ret.release();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistryRelease() */
|
||||
/************************************************************************/
|
||||
|
||||
/** Release a handle to an algorithm registry, but this does not destroy the
|
||||
* registry itself.
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
void GDALAlgorithmRegistryRelease(GDALAlgorithmRegistryH hReg)
|
||||
{
|
||||
delete hReg;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistryGetAlgNames() */
|
||||
/************************************************************************/
|
||||
|
||||
/** Return the names of the algorithms registered in the registry passed as
|
||||
* parameter.
|
||||
*
|
||||
* @param hReg Handle to a registry. Must NOT be null.
|
||||
* @return a NULL terminated list of names, which must be destroyed with
|
||||
* CSLDestroy()
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
char **GDALAlgorithmRegistryGetAlgNames(GDALAlgorithmRegistryH hReg)
|
||||
{
|
||||
VALIDATE_POINTER1(hReg, __func__, nullptr);
|
||||
return CPLStringList(hReg->ptr->GetNames()).StealList();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithmRegistryInstantiateAlg() */
|
||||
/************************************************************************/
|
||||
|
||||
/** Instantiate an algorithm available in a registry from its name.
|
||||
*
|
||||
* @param hReg Handle to a registry. Must NOT be null.
|
||||
* @param pszAlgName Algorithm name. Must NOT be null.
|
||||
* @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
|
||||
* or NULL if the algorithm does not exist or another error occurred.
|
||||
*
|
||||
* @since 3.11
|
||||
*/
|
||||
GDALAlgorithmH GDALAlgorithmRegistryInstantiateAlg(GDALAlgorithmRegistryH hReg,
|
||||
const char *pszAlgName)
|
||||
{
|
||||
VALIDATE_POINTER1(hReg, __func__, nullptr);
|
||||
VALIDATE_POINTER1(pszAlgName, __func__, nullptr);
|
||||
auto alg = hReg->ptr->Instantiate(pszAlgName);
|
||||
return alg ? std::make_unique<GDALAlgorithmHS>(std::move(alg)).release()
|
||||
: nullptr;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
# CMake4GDAL project is distributed under MIT license. See accompanying file LICENSE.txt.
|
||||
|
||||
set(GDAL_SWIG_COMMON_INTERFACE_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/Algorithm.i
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/Band.i
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/ColorTable.i
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/cpl.i
|
||||
|
|
|
@ -0,0 +1,416 @@
|
|||
/******************************************************************************
|
||||
* Name: Algorithm.i
|
||||
* Project: GDAL Python Interface
|
||||
* Purpose: GDAL Algorithm SWIG Interface declarations.
|
||||
* Author: Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
******************************************************************************
|
||||
* Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*****************************************************************************/
|
||||
|
||||
%{
|
||||
#include "gdalalgorithm.h"
|
||||
|
||||
typedef GDALAlgorithmRegistryHS GDALAlgorithmRegistryHS;
|
||||
typedef GDALAlgorithmHS GDALAlgorithmHS;
|
||||
typedef GDALAlgorithmArgHS GDALAlgorithmArgHS;
|
||||
typedef GDALArgDatasetValueHS GDALArgDatasetValueHS;
|
||||
%}
|
||||
|
||||
typedef int GDALAlgorithmArgType;
|
||||
|
||||
%rename (AlgorithmRegistry) GDALAlgorithmRegistryHS;
|
||||
%rename (Algorithm) GDALAlgorithmHS;
|
||||
%rename (AlgorithmArg) GDALAlgorithmArgHS;
|
||||
%rename (ArgDatasetValue) GDALArgDatasetValueHS;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Global methods */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
%rename (GetGlobalAlgorithmRegistry) GDALGetGlobalAlgorithmRegistry;
|
||||
%newobject GDALGetGlobalAlgorithmRegistry;
|
||||
GDALAlgorithmRegistryHS* GDALGetGlobalAlgorithmRegistry(void);
|
||||
|
||||
%rename (AlgorithmArgTypeIsList) GDALAlgorithmArgTypeIsList;
|
||||
bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type);
|
||||
|
||||
%rename (AlgorithmArgTypeName) GDALAlgorithmArgTypeName;
|
||||
const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type);
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* GDALAlgorithmArgHS */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
class GDALAlgorithmArgHS {
|
||||
private:
|
||||
GDALAlgorithmArgHS();
|
||||
public:
|
||||
%extend {
|
||||
~GDALAlgorithmArgHS() {
|
||||
GDALAlgorithmArgRelease( self );
|
||||
}
|
||||
|
||||
const char* GetName() {
|
||||
return GDALAlgorithmArgGetName(self);
|
||||
}
|
||||
|
||||
#if defined(SWIGCSHARP)
|
||||
GDALAlgorithmArgType GetType_()
|
||||
#else
|
||||
GDALAlgorithmArgType GetType()
|
||||
#endif
|
||||
{
|
||||
return GDALAlgorithmArgGetType(self);
|
||||
}
|
||||
|
||||
const char* GetDescription() {
|
||||
return GDALAlgorithmArgGetDescription(self);
|
||||
}
|
||||
|
||||
const char* GetShortName() {
|
||||
return GDALAlgorithmArgGetShortName(self);
|
||||
}
|
||||
|
||||
%apply (char **CSL) {char **};
|
||||
char **GetAliases() {
|
||||
return GDALAlgorithmArgGetAliases( self );
|
||||
}
|
||||
%clear char **;
|
||||
|
||||
const char* GetMetaVar() {
|
||||
return GDALAlgorithmArgGetMetaVar(self);
|
||||
}
|
||||
|
||||
const char* GetCategory() {
|
||||
return GDALAlgorithmArgGetCategory(self);
|
||||
}
|
||||
|
||||
bool IsPositional() {
|
||||
return GDALAlgorithmArgIsPositional(self);
|
||||
}
|
||||
|
||||
bool IsRequired() {
|
||||
return GDALAlgorithmArgIsRequired(self);
|
||||
}
|
||||
|
||||
int GetMinCount() {
|
||||
return GDALAlgorithmArgGetMinCount(self);
|
||||
}
|
||||
|
||||
int GetMaxCount() {
|
||||
return GDALAlgorithmArgGetMaxCount(self);
|
||||
}
|
||||
|
||||
bool GetPackedValuesAllowed() {
|
||||
return GDALAlgorithmArgGetPackedValuesAllowed(self);
|
||||
}
|
||||
|
||||
bool GetRepeatedArgAllowed() {
|
||||
return GDALAlgorithmArgGetRepeatedArgAllowed(self);
|
||||
}
|
||||
|
||||
%apply (char **CSL) {char **};
|
||||
char **GetChoices() {
|
||||
return GDALAlgorithmArgGetChoices( self );
|
||||
}
|
||||
%clear char **;
|
||||
|
||||
bool IsExplicitlySet() {
|
||||
return GDALAlgorithmArgIsExplicitlySet(self);
|
||||
}
|
||||
|
||||
bool HasDefaultValue() {
|
||||
return GDALAlgorithmArgHasDefaultValue(self);
|
||||
}
|
||||
|
||||
bool IsHiddenForCLI() {
|
||||
return GDALAlgorithmArgIsHiddenForCLI(self);
|
||||
}
|
||||
|
||||
bool IsOnlyForCLI() {
|
||||
return GDALAlgorithmArgIsOnlyForCLI(self);
|
||||
}
|
||||
|
||||
bool IsInput() {
|
||||
return GDALAlgorithmArgIsInput(self);
|
||||
}
|
||||
|
||||
bool IsOutput() {
|
||||
return GDALAlgorithmArgIsOutput(self);
|
||||
}
|
||||
|
||||
const char* GetMutualExclusionGroup() {
|
||||
return GDALAlgorithmArgGetMutualExclusionGroup(self);
|
||||
}
|
||||
|
||||
bool GetAsBoolean() {
|
||||
return GDALAlgorithmArgGetAsBoolean(self);
|
||||
}
|
||||
|
||||
const char* GetAsString() {
|
||||
return GDALAlgorithmArgGetAsString(self);
|
||||
}
|
||||
|
||||
int GetAsInteger() {
|
||||
return GDALAlgorithmArgGetAsInteger(self);
|
||||
}
|
||||
|
||||
int GetAsDouble() {
|
||||
return GDALAlgorithmArgGetAsDouble(self);
|
||||
}
|
||||
|
||||
%newobject GetAsDataset;
|
||||
GDALArgDatasetValueHS* GetAsDatasetValue() {
|
||||
return GDALAlgorithmArgGetAsDatasetValue(self);
|
||||
}
|
||||
|
||||
%apply (char **CSL) {char **};
|
||||
char **GetAsStringList() {
|
||||
return GDALAlgorithmArgGetAsStringList( self );
|
||||
}
|
||||
%clear char **;
|
||||
|
||||
#if defined(SWIGPYTHON)
|
||||
void GetAsIntegerList(int *nLen, const int **pList) {
|
||||
size_t nLenSizet = 0;
|
||||
*pList = GDALAlgorithmArgGetAsIntegerList(self, &nLenSizet);
|
||||
*nLen = (int)nLenSizet;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SWIGPYTHON)
|
||||
void GetAsDoubleList(int *nLen, const double **pList) {
|
||||
size_t nLenSizet = 0;
|
||||
*pList = GDALAlgorithmArgGetAsDoubleList(self, &nLenSizet);
|
||||
*nLen = (int)nLenSizet;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SetAsBoolean(bool value) {
|
||||
return GDALAlgorithmArgSetAsBoolean(self, value);
|
||||
}
|
||||
|
||||
bool SetAsString(const char* value) {
|
||||
return GDALAlgorithmArgSetAsString(self, value);
|
||||
}
|
||||
|
||||
bool SetAsInteger(int value) {
|
||||
return GDALAlgorithmArgSetAsInteger(self, value);
|
||||
}
|
||||
|
||||
bool SetAsDouble(double value) {
|
||||
return GDALAlgorithmArgSetAsDouble(self, value);
|
||||
}
|
||||
|
||||
bool SetAsDatasetValue(GDALArgDatasetValueHS* value) {
|
||||
return GDALAlgorithmArgSetAsDatasetValue(self, value);
|
||||
}
|
||||
|
||||
%apply (char **options) {char**value};
|
||||
bool SetAsStringList(char** value) {
|
||||
return GDALAlgorithmArgSetAsStringList(self, value);
|
||||
}
|
||||
%clear char** value;
|
||||
|
||||
bool SetAsIntegerList(int nList, int *pList) {
|
||||
return GDALAlgorithmArgSetAsIntegerList(self, nList, pList);
|
||||
}
|
||||
|
||||
bool SetAsDoubleList(int nList, double *pList) {
|
||||
return GDALAlgorithmArgSetAsDoubleList(self, nList, pList);
|
||||
}
|
||||
|
||||
void SetDatasetWithoutOwnership(GDALDatasetShadow* ds) {
|
||||
GDALAlgorithmArgSetDatasetWithoutOwnership(self, ds);
|
||||
}
|
||||
|
||||
void SetDatasetWithOwnership(GDALDatasetShadow* ds) {
|
||||
GDALAlgorithmArgSetDatasetWithOwnership(self, ds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* GDALAlgorithmHS */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
class GDALAlgorithmHS {
|
||||
private:
|
||||
GDALAlgorithmHS();
|
||||
public:
|
||||
%extend {
|
||||
~GDALAlgorithmHS() {
|
||||
GDALAlgorithmRelease( self );
|
||||
}
|
||||
|
||||
const char* GetName() {
|
||||
return GDALAlgorithmGetName(self);
|
||||
}
|
||||
|
||||
const char* GetDescription() {
|
||||
return GDALAlgorithmGetDescription(self);
|
||||
}
|
||||
|
||||
const char* GetLongDescription() {
|
||||
return GDALAlgorithmGetLongDescription(self);
|
||||
}
|
||||
|
||||
const char* GetHelpFullURL() {
|
||||
return GDALAlgorithmGetHelpFullURL(self);
|
||||
}
|
||||
|
||||
bool HasSubAlgorithms() {
|
||||
return GDALAlgorithmHasSubAlgorithms(self);
|
||||
}
|
||||
|
||||
%apply (char **CSL) {char **};
|
||||
char **GetSubAlgorithmNames() {
|
||||
return GDALAlgorithmGetSubAlgorithmNames( self );
|
||||
}
|
||||
%clear char **;
|
||||
|
||||
%apply Pointer NONNULL {const char *algName};
|
||||
%newobject InstantiateSubAlgorithm;
|
||||
GDALAlgorithmHS* InstantiateSubAlgorithm(const char *algName) {
|
||||
return GDALAlgorithmInstantiateSubAlgorithm(self, algName);
|
||||
}
|
||||
%clear const char *algName;
|
||||
|
||||
%apply (char **options) { char ** args };
|
||||
bool ParseCommandLineArguments(char** args) {
|
||||
return GDALAlgorithmParseCommandLineArguments(self, args);
|
||||
}
|
||||
%clear char** args;
|
||||
|
||||
%newobject GetActualAlgorithm;
|
||||
GDALAlgorithmHS* GetActualAlgorithm() {
|
||||
return GDALAlgorithmGetActualAlgorithm(self);
|
||||
}
|
||||
|
||||
bool Run(GDALProgressFunc callback=NULL, void* callback_data=NULL) {
|
||||
return GDALAlgorithmRun(self, callback, callback_data);
|
||||
}
|
||||
|
||||
#if defined(SWIGCSHARP)
|
||||
bool Finalize_()
|
||||
#else
|
||||
bool Finalize()
|
||||
#endif
|
||||
{
|
||||
return GDALAlgorithmFinalize(self);
|
||||
}
|
||||
|
||||
%apply (char **options) { char ** args };
|
||||
bool ParseRunAndFinalize(char** args, GDALProgressFunc callback=NULL, void* callback_data=NULL) {
|
||||
return GDALAlgorithmParseCommandLineArguments(self, args) &&
|
||||
GDALAlgorithmRun(self, callback, callback_data) &&
|
||||
GDALAlgorithmFinalize(self);
|
||||
}
|
||||
%clear char** args;
|
||||
|
||||
retStringAndCPLFree *GetUsageAsJSON() {
|
||||
return GDALAlgorithmGetUsageAsJSON(self);
|
||||
}
|
||||
|
||||
%apply (char **CSL) {char **};
|
||||
char **GetArgNames() {
|
||||
return GDALAlgorithmGetArgNames( self );
|
||||
}
|
||||
%clear char **;
|
||||
|
||||
%apply Pointer NONNULL {const char *argName};
|
||||
%newobject GetArg;
|
||||
GDALAlgorithmArgHS* GetArg(const char *argName) {
|
||||
return GDALAlgorithmGetArg(self, argName);
|
||||
}
|
||||
%clear const char *argName;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* GDALAlgorithmRegistryHS */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
class GDALAlgorithmRegistryHS {
|
||||
private:
|
||||
GDALAlgorithmRegistryHS();
|
||||
public:
|
||||
%extend {
|
||||
~GDALAlgorithmRegistryHS() {
|
||||
GDALAlgorithmRegistryRelease( self );
|
||||
}
|
||||
|
||||
%apply (char **CSL) {char **};
|
||||
char **GetAlgNames() {
|
||||
return GDALAlgorithmRegistryGetAlgNames( self );
|
||||
}
|
||||
%clear char **;
|
||||
|
||||
%apply Pointer NONNULL {const char *algName};
|
||||
%newobject InstantiateAlg;
|
||||
GDALAlgorithmHS* InstantiateAlg(const char *algName) {
|
||||
return GDALAlgorithmRegistryInstantiateAlg(self, algName);
|
||||
}
|
||||
%clear const char *algName;
|
||||
}
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* GDALArgDatasetValueHS */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
class GDALArgDatasetValueHS {
|
||||
private:
|
||||
GDALArgDatasetValueHS();
|
||||
public:
|
||||
%extend {
|
||||
~GDALArgDatasetValueHS() {
|
||||
GDALArgDatasetValueRelease( self );
|
||||
}
|
||||
|
||||
#if defined(SWIGCSHARP)
|
||||
int GetType_()
|
||||
#else
|
||||
int GetType()
|
||||
#endif
|
||||
{
|
||||
return GDALArgDatasetValueGetType(self);
|
||||
}
|
||||
|
||||
const char* GetName() {
|
||||
return GDALArgDatasetValueGetName(self);
|
||||
}
|
||||
|
||||
GDALDatasetShadow* GetDataset() {
|
||||
return GDALArgDatasetValueGetDataset(self);
|
||||
}
|
||||
|
||||
bool IsDatasetOwned() {
|
||||
return GDALArgDatasetValueIsDatasetOwned(self);
|
||||
}
|
||||
|
||||
int GetInputFlags() {
|
||||
return GDALArgDatasetValueGetInputFlags(self);
|
||||
}
|
||||
|
||||
int GetOutputFlags() {
|
||||
return GDALArgDatasetValueGetOutputFlags(self);
|
||||
}
|
||||
|
||||
void SetName(const char* name) {
|
||||
GDALArgDatasetValueSetName(self, name);
|
||||
}
|
||||
|
||||
void SetDatasetWithoutOwnership(GDALDatasetShadow* ds) {
|
||||
GDALArgDatasetValueSetDatasetWithoutOwnership(self, ds);
|
||||
}
|
||||
|
||||
void SetDatasetWithOwnership(GDALDatasetShadow* ds) {
|
||||
GDALArgDatasetValueSetDatasetWithOwnership(self, ds);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -612,6 +612,8 @@ RETURN_NONE GDALGCPsToGeoTransform( int nGCPs, GDAL_GCP const * pGCPs,
|
|||
//************************************************************************
|
||||
%include "Operations.i"
|
||||
|
||||
%include "Algorithm.i"
|
||||
|
||||
%apply (double argin[ANY]) {(double padfGeoTransform[6])};
|
||||
%apply (double *OUTPUT) {(double *pdfGeoX)};
|
||||
%apply (double *OUTPUT) {(double *pdfGeoY)};
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "gdalwarper.h"
|
||||
#include "cpl_string.h"
|
||||
#include "cpl_minixml.h"
|
||||
#include "gdalalgorithm.h"
|
||||
%}
|
||||
|
||||
// GDALDataType
|
||||
|
@ -488,6 +489,17 @@
|
|||
%constant GRT_ASSOCIATION = GRT_ASSOCIATION;
|
||||
%constant GRT_AGGREGATION = GRT_AGGREGATION;
|
||||
|
||||
// GDALAlgorithmArgType
|
||||
%constant GAAT_BOOLEAN = GAAT_BOOLEAN;
|
||||
%constant GAAT_STRING = GAAT_STRING;
|
||||
%constant GAAT_INTEGER = GAAT_INTEGER;
|
||||
%constant GAAT_REAL = GAAT_REAL;
|
||||
%constant GAAT_DATASET = GAAT_DATASET;
|
||||
%constant GAAT_STRING_LIST = GAAT_STRING_LIST;
|
||||
%constant GAAT_INTEGER_LIST = GAAT_INTEGER_LIST;
|
||||
%constant GAAT_REAL_LIST = GAAT_REAL_LIST;
|
||||
%constant GAAT_DATASET_LIST = GAAT_DATASET_LIST;
|
||||
|
||||
#ifdef SWIGPYTHON
|
||||
%thread;
|
||||
#endif
|
||||
|
|
|
@ -5152,3 +5152,53 @@ class VSIFile(BytesIO):
|
|||
def tell(self):
|
||||
return VSIFTellL(self._fp)
|
||||
%}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* GDALAlgorithmArgHS */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
%extend GDALAlgorithmArgHS {
|
||||
%pythoncode %{
|
||||
|
||||
def Get(self):
|
||||
type = self.GetType()
|
||||
if type == GAAT_BOOLEAN:
|
||||
return self.GetAsBoolean()
|
||||
if type == GAAT_STRING:
|
||||
return self.GetAsString()
|
||||
if type == GAAT_INTEGER:
|
||||
return self.GetAsInteger()
|
||||
if type == GAAT_REAL:
|
||||
return self.GetAsDouble()
|
||||
if type == GAAT_DATASET:
|
||||
return self.GetAsDatasetValue()
|
||||
if type == GAAT_STRING_LIST:
|
||||
return self.GetAsStringList()
|
||||
if type == GAAT_INTEGER_LIST:
|
||||
return self.GetAsIntegerList()
|
||||
if type == GAAT_REAL_LIST:
|
||||
return self.GetAsDoubleList()
|
||||
raise Exception("Unhandled algorithm argument data type")
|
||||
|
||||
def Set(self, value):
|
||||
type = self.GetType()
|
||||
if type == GAAT_BOOLEAN:
|
||||
return self.SetAsBoolean(value)
|
||||
if type == GAAT_STRING:
|
||||
return self.SetAsString(value)
|
||||
if type == GAAT_INTEGER:
|
||||
return self.SetAsInteger(value)
|
||||
if type == GAAT_REAL:
|
||||
return self.SetAsDouble(value)
|
||||
if type == GAAT_DATASET:
|
||||
return self.SetAsDatasetValue(value)
|
||||
if type == GAAT_STRING_LIST:
|
||||
return self.SetAsStringList(value)
|
||||
if type == GAAT_INTEGER_LIST:
|
||||
return self.SetAsIntegerList(value)
|
||||
if type == GAAT_REAL_LIST:
|
||||
return self.SetAsDoubleList(value)
|
||||
raise Exception("Unhandled algorithm argument data type")
|
||||
|
||||
%}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue