Add a 'gdal raster edit' command to override SRS, extent and metadata
This commit is contained in:
parent
49367db76d
commit
e4bb0c3de7
|
@ -13,6 +13,7 @@ add_library(
|
|||
gdalalg_raster.cpp
|
||||
gdalalg_raster_info.cpp
|
||||
gdalalg_raster_convert.cpp
|
||||
gdalalg_raster_edit.cpp
|
||||
gdalalg_raster_pipeline.cpp
|
||||
gdalalg_raster_read.cpp
|
||||
gdalalg_raster_reproject.cpp
|
||||
|
|
|
@ -760,7 +760,8 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
|
|||
CPLFree(pszSRS);
|
||||
}
|
||||
|
||||
if (!psOptions->osOutputSRS.empty())
|
||||
if (!psOptions->osOutputSRS.empty() && psOptions->osOutputSRS != "null" &&
|
||||
psOptions->osOutputSRS != "none")
|
||||
{
|
||||
OGRSpatialReference oOutputSRS;
|
||||
if (oOutputSRS.SetFromUserInput(psOptions->osOutputSRS.c_str()) !=
|
||||
|
@ -1520,23 +1521,31 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
|
|||
|
||||
if (psOptions->nGCPCount == 0)
|
||||
{
|
||||
OGRSpatialReference oSRS;
|
||||
if (!psOptions->osOutputSRS.empty())
|
||||
if (psOptions->osOutputSRS == "null" ||
|
||||
psOptions->osOutputSRS == "none")
|
||||
{
|
||||
oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
|
||||
oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
poVDS->SetSpatialRef(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
|
||||
if (poSrcSRS)
|
||||
oSRS = *poSrcSRS;
|
||||
}
|
||||
if (!oSRS.IsEmpty())
|
||||
{
|
||||
if (psOptions->dfOutputCoordinateEpoch > 0)
|
||||
oSRS.SetCoordinateEpoch(psOptions->dfOutputCoordinateEpoch);
|
||||
poVDS->SetSpatialRef(&oSRS);
|
||||
OGRSpatialReference oSRS;
|
||||
if (!psOptions->osOutputSRS.empty())
|
||||
{
|
||||
oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
|
||||
oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
}
|
||||
else
|
||||
{
|
||||
const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
|
||||
if (poSrcSRS)
|
||||
oSRS = *poSrcSRS;
|
||||
}
|
||||
if (!oSRS.IsEmpty())
|
||||
{
|
||||
if (psOptions->dfOutputCoordinateEpoch > 0)
|
||||
oSRS.SetCoordinateEpoch(psOptions->dfOutputCoordinateEpoch);
|
||||
poVDS->SetSpatialRef(&oSRS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1598,7 +1607,12 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
|
|||
if (psOptions->nGCPCount != 0)
|
||||
{
|
||||
OGRSpatialReference oSRS;
|
||||
if (!psOptions->osOutputSRS.empty())
|
||||
if (psOptions->osOutputSRS == "null" ||
|
||||
psOptions->osOutputSRS == "none")
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
else if (!psOptions->osOutputSRS.empty())
|
||||
{
|
||||
oSRS.SetFromUserInput(psOptions->osOutputSRS.c_str());
|
||||
oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "gdalalg_raster_info.h"
|
||||
#include "gdalalg_raster_convert.h"
|
||||
#include "gdalalg_raster_edit.h"
|
||||
#include "gdalalg_raster_pipeline.h"
|
||||
#include "gdalalg_raster_reproject.h"
|
||||
|
||||
|
@ -37,6 +38,7 @@ class GDALRasterAlgorithm final : public GDALAlgorithm
|
|||
{
|
||||
RegisterSubAlgorithm<GDALRasterInfoAlgorithm>();
|
||||
RegisterSubAlgorithm<GDALRasterConvertAlgorithm>();
|
||||
RegisterSubAlgorithm<GDALRasterEditAlgorithmStandalone>();
|
||||
RegisterSubAlgorithm<GDALRasterPipelineAlgorithm>();
|
||||
RegisterSubAlgorithm<GDALRasterReprojectAlgorithmStandalone>();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "edit" step of "raster 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_raster_edit.h"
|
||||
|
||||
#include "gdal_priv.h"
|
||||
#include "gdal_utils.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
#ifndef _
|
||||
#define _(x) (x)
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterEditAlgorithm::GDALRasterEditAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALRasterEditAlgorithm::GDALRasterEditAlgorithm(bool standaloneStep)
|
||||
: GDALRasterPipelineStepAlgorithm(
|
||||
NAME, DESCRIPTION, HELP_URL,
|
||||
// Avoid automatic addition of input/output arguments
|
||||
/*standaloneStep = */ false)
|
||||
{
|
||||
if (standaloneStep)
|
||||
{
|
||||
AddArg("dataset", 0, _("Dataset (in-place updated)"), &m_dataset,
|
||||
GDAL_OF_RASTER | GDAL_OF_UPDATE)
|
||||
.SetPositional()
|
||||
.SetRequired();
|
||||
m_standaloneStep = true;
|
||||
}
|
||||
|
||||
AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
|
||||
.AddHiddenAlias("a_srs")
|
||||
.SetIsCRSArg(/*noneAllowed=*/true);
|
||||
|
||||
{
|
||||
auto &arg =
|
||||
AddArg("extent", 0, _("Extent as xmin,ymin,xmax,ymax"), &m_extent)
|
||||
.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 'extent' should be xmin,ymin,xmax,ymax with "
|
||||
"xmin <= xmax and ymin <= ymax");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
|
||||
&m_metadata)
|
||||
.SetMetaVar("<KEY>=<VALUE>");
|
||||
arg.AddValidationAction([this, &arg]()
|
||||
{ return ValidateKeyValue(arg); });
|
||||
arg.AddHiddenAlias("mo");
|
||||
}
|
||||
|
||||
AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
|
||||
&m_unsetMetadata)
|
||||
.SetMetaVar("<KEY>");
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterEditAlgorithm::RunImpl() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALRasterEditAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
|
||||
void *pProgressData)
|
||||
{
|
||||
if (m_standaloneStep)
|
||||
{
|
||||
auto poDS = m_dataset.GetDatasetRef();
|
||||
CPLAssert(poDS);
|
||||
if (poDS->GetAccess() != GA_Update)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Dataset should be opened in update mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_overrideCrs == "null" || m_overrideCrs == "none")
|
||||
{
|
||||
if (poDS->SetSpatialRef(nullptr) != CE_None)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"SetSpatialRef(%s) failed", m_overrideCrs.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!m_overrideCrs.empty())
|
||||
{
|
||||
OGRSpatialReference oSRS;
|
||||
oSRS.SetFromUserInput(m_overrideCrs.c_str());
|
||||
oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
if (poDS->SetSpatialRef(&oSRS) != CE_None)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"SetSpatialRef(%s) failed", m_overrideCrs.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_extent.empty())
|
||||
{
|
||||
if (poDS->GetRasterXSize() == 0 || poDS->GetRasterYSize() == 0)
|
||||
{
|
||||
ReportError(
|
||||
CE_Failure, CPLE_AppDefined,
|
||||
"Cannot set extent because dataset has 0x0 dimension");
|
||||
return false;
|
||||
}
|
||||
double adfGT[6];
|
||||
adfGT[0] = m_extent[0];
|
||||
adfGT[1] = (m_extent[2] - m_extent[0]) / poDS->GetRasterXSize();
|
||||
adfGT[2] = 0;
|
||||
adfGT[3] = m_extent[3];
|
||||
adfGT[4] = 0;
|
||||
adfGT[5] = -(m_extent[3] - m_extent[1]) / poDS->GetRasterYSize();
|
||||
if (poDS->SetGeoTransform(adfGT) != CE_None)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Setting extent failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const CPLStringList aosMD(m_metadata);
|
||||
for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
|
||||
{
|
||||
if (poDS->SetMetadataItem(key, value) != CE_None)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"SetMetadataItem('%s', '%s') failed", key, value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string &key : m_unsetMetadata)
|
||||
{
|
||||
if (poDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"SetMetadataItem('%s', NULL) failed", key.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return RunStep(pfnProgress, pProgressData);
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterEditAlgorithm::RunStep() */
|
||||
/************************************************************************/
|
||||
|
||||
bool GDALRasterEditAlgorithm::RunStep(GDALProgressFunc, void *)
|
||||
{
|
||||
CPLAssert(m_inputDataset.GetDatasetRef());
|
||||
CPLAssert(m_outputDataset.GetName().empty());
|
||||
CPLAssert(!m_outputDataset.GetDatasetRef());
|
||||
|
||||
CPLStringList aosOptions;
|
||||
aosOptions.AddString("-of");
|
||||
aosOptions.AddString("VRT");
|
||||
if (!m_overrideCrs.empty())
|
||||
{
|
||||
aosOptions.AddString("-a_srs");
|
||||
aosOptions.AddString(m_overrideCrs.c_str());
|
||||
}
|
||||
if (!m_extent.empty())
|
||||
{
|
||||
aosOptions.AddString("-a_ullr");
|
||||
aosOptions.AddString(CPLSPrintf("%.17g", m_extent[0])); // upper-left X
|
||||
aosOptions.AddString(CPLSPrintf("%.17g", m_extent[3])); // upper-left Y
|
||||
aosOptions.AddString(
|
||||
CPLSPrintf("%.17g", m_extent[2])); // lower-right X
|
||||
aosOptions.AddString(
|
||||
CPLSPrintf("%.17g", m_extent[1])); // lower-right Y
|
||||
}
|
||||
|
||||
for (const auto &val : m_metadata)
|
||||
{
|
||||
aosOptions.AddString("-mo");
|
||||
aosOptions.AddString(val.c_str());
|
||||
}
|
||||
|
||||
for (const std::string &key : m_unsetMetadata)
|
||||
{
|
||||
aosOptions.AddString("-mo");
|
||||
aosOptions.AddString((key + "=").c_str());
|
||||
}
|
||||
|
||||
GDALTranslateOptions *psOptions =
|
||||
GDALTranslateOptionsNew(aosOptions.List(), nullptr);
|
||||
|
||||
GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
|
||||
auto poRetDS =
|
||||
GDALDataset::FromHandle(GDALTranslate("", hSrcDS, psOptions, nullptr));
|
||||
GDALTranslateOptionsFree(psOptions);
|
||||
const bool ok = poRetDS != nullptr;
|
||||
if (ok)
|
||||
m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//! @endcond
|
|
@ -0,0 +1,65 @@
|
|||
/******************************************************************************
|
||||
*
|
||||
* Project: GDAL
|
||||
* Purpose: "edit" step of "raster 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_RASTER_EDIT_INCLUDED
|
||||
#define GDALALG_RASTER_EDIT_INCLUDED
|
||||
|
||||
#include "gdalalg_raster_pipeline.h"
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterEditAlgorithm */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALRasterEditAlgorithm /* non final */
|
||||
: public GDALRasterPipelineStepAlgorithm
|
||||
{
|
||||
public:
|
||||
static constexpr const char *NAME = "edit";
|
||||
static constexpr const char *DESCRIPTION = "Edit a raster dataset.";
|
||||
static constexpr const char *HELP_URL = "/programs/gdal_raster_edit.html";
|
||||
|
||||
static std::vector<std::string> GetAliases()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
explicit GDALRasterEditAlgorithm(bool standaloneStep = false);
|
||||
|
||||
private:
|
||||
bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
bool RunStep(GDALProgressFunc pfnProgress, void *pProgressData) override;
|
||||
|
||||
GDALArgDatasetValue m_dataset{}; // standalone mode only
|
||||
std::string m_overrideCrs{};
|
||||
std::vector<double> m_extent{};
|
||||
std::vector<std::string> m_metadata{};
|
||||
std::vector<std::string> m_unsetMetadata{};
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALRasterEditAlgorithmStandalone */
|
||||
/************************************************************************/
|
||||
|
||||
class GDALRasterEditAlgorithmStandalone final : public GDALRasterEditAlgorithm
|
||||
{
|
||||
public:
|
||||
GDALRasterEditAlgorithmStandalone()
|
||||
: GDALRasterEditAlgorithm(/* standaloneStep = */ true)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//! @endcond
|
||||
|
||||
#endif /* GDALALG_RASTER_EDIT_INCLUDED */
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "gdalalg_raster_pipeline.h"
|
||||
#include "gdalalg_raster_read.h"
|
||||
#include "gdalalg_raster_edit.h"
|
||||
#include "gdalalg_raster_reproject.h"
|
||||
#include "gdalalg_raster_write.h"
|
||||
|
||||
|
@ -159,6 +160,7 @@ GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
|
|||
|
||||
m_stepRegistry.Register<GDALRasterReadAlgorithm>();
|
||||
m_stepRegistry.Register<GDALRasterWriteAlgorithm>();
|
||||
m_stepRegistry.Register<GDALRasterEditAlgorithm>();
|
||||
m_stepRegistry.Register<GDALRasterReprojectAlgorithm>();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,11 @@ GDALRasterReprojectAlgorithm::GDALRasterReprojectAlgorithm(bool standaloneStep)
|
|||
: GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
|
||||
standaloneStep)
|
||||
{
|
||||
AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs).AddHiddenAlias("s_srs");
|
||||
AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs)
|
||||
.SetIsCRSArg()
|
||||
.AddHiddenAlias("s_srs");
|
||||
AddArg("dst-crs", 'd', _("Destination CRS"), &m_dstCrs)
|
||||
.SetIsCRSArg()
|
||||
.AddHiddenAlias("t_srs");
|
||||
AddArg("resampling", 'r', _("Resampling method"), &m_resampling)
|
||||
.SetChoices("near", "bilinear", "cubic", "cubicspline", "lanczos",
|
||||
|
@ -98,28 +101,6 @@ bool GDALRasterReprojectAlgorithm::RunStep(GDALProgressFunc, void *)
|
|||
CPLAssert(m_outputDataset.GetName().empty());
|
||||
CPLAssert(!m_outputDataset.GetDatasetRef());
|
||||
|
||||
if (!m_srsCrs.empty())
|
||||
{
|
||||
OGRSpatialReference oSRS;
|
||||
if (oSRS.SetFromUserInput(m_srsCrs.c_str()) != OGRERR_NONE)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Invalid value for '--src-crs'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_dstCrs.empty())
|
||||
{
|
||||
OGRSpatialReference oSRS;
|
||||
if (oSRS.SetFromUserInput(m_dstCrs.c_str()) != OGRERR_NONE)
|
||||
{
|
||||
ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Invalid value for '--dst-crs'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CPLStringList aosOptions;
|
||||
aosOptions.AddString("-of");
|
||||
aosOptions.AddString("VRT");
|
||||
|
|
|
@ -31,8 +31,11 @@ GDALVectorReprojectAlgorithm::GDALVectorReprojectAlgorithm(bool standaloneStep)
|
|||
: GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
|
||||
standaloneStep)
|
||||
{
|
||||
AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs).AddHiddenAlias("s_srs");
|
||||
AddArg("src-crs", 's', _("Source CRS"), &m_srsCrs)
|
||||
.SetIsCRSArg()
|
||||
.AddHiddenAlias("s_srs");
|
||||
AddArg("dst-crs", 'd', _("Destination CRS"), &m_dstCrs)
|
||||
.SetIsCRSArg()
|
||||
.SetRequired()
|
||||
.AddHiddenAlias("t_srs");
|
||||
}
|
||||
|
@ -82,22 +85,12 @@ bool GDALVectorReprojectAlgorithm::RunStep(GDALProgressFunc, void *)
|
|||
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->SetFromUserInput(m_srsCrs.c_str());
|
||||
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.SetFromUserInput(m_dstCrs.c_str());
|
||||
oDstCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
||||
|
||||
auto poSrcDS = m_inputDataset.GetDatasetRef();
|
||||
|
|
|
@ -455,6 +455,19 @@ TEST_F(test_gdal_algorithm, RunValidationActions)
|
|||
EXPECT_FALSE(arg.Set(2));
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, SetIsCRSArg_wrong_type)
|
||||
{
|
||||
int val = 0;
|
||||
auto arg = GDALInConstructionAlgorithmArg(
|
||||
nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
|
||||
{
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
arg.SetIsCRSArg();
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
}
|
||||
}
|
||||
|
||||
class MyAlgorithmWithDummyRun : public GDALAlgorithm
|
||||
{
|
||||
public:
|
||||
|
@ -3070,4 +3083,229 @@ TEST_F(test_gdal_algorithm, DispatcherGetUsageForCLI)
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, raster_edit_failures_dataset_0_0)
|
||||
{
|
||||
auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
|
||||
auto raster = singleton.Instantiate("raster");
|
||||
ASSERT_NE(raster, nullptr);
|
||||
auto edit = raster->InstantiateSubAlgorithm("edit");
|
||||
ASSERT_NE(edit, nullptr);
|
||||
|
||||
class MyDataset : public GDALDataset
|
||||
{
|
||||
public:
|
||||
MyDataset()
|
||||
{
|
||||
nRasterXSize = 0;
|
||||
nRasterYSize = 0;
|
||||
eAccess = GA_Update;
|
||||
}
|
||||
};
|
||||
|
||||
auto datasetArg = edit->GetArg("dataset");
|
||||
ASSERT_NE(datasetArg, nullptr);
|
||||
datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
|
||||
|
||||
auto extentArg = edit->GetArg("extent");
|
||||
ASSERT_NE(extentArg, nullptr);
|
||||
extentArg->Set(std::vector<double>{2, 49, 3, 50});
|
||||
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
EXPECT_FALSE(edit->Run());
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
EXPECT_STREQ(CPLGetLastErrorMsg(),
|
||||
"edit: Cannot set extent because dataset has 0x0 dimension");
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_none)
|
||||
{
|
||||
auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
|
||||
auto raster = singleton.Instantiate("raster");
|
||||
ASSERT_NE(raster, nullptr);
|
||||
auto edit = raster->InstantiateSubAlgorithm("edit");
|
||||
ASSERT_NE(edit, nullptr);
|
||||
|
||||
class MyDataset : public GDALDataset
|
||||
{
|
||||
public:
|
||||
MyDataset()
|
||||
{
|
||||
eAccess = GA_Update;
|
||||
}
|
||||
|
||||
CPLErr SetSpatialRef(const OGRSpatialReference *) override
|
||||
{
|
||||
return CE_Failure;
|
||||
}
|
||||
};
|
||||
|
||||
auto datasetArg = edit->GetArg("dataset");
|
||||
ASSERT_NE(datasetArg, nullptr);
|
||||
datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
|
||||
|
||||
auto crsArg = edit->GetArg("crs");
|
||||
ASSERT_NE(crsArg, nullptr);
|
||||
crsArg->Set("none");
|
||||
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
EXPECT_FALSE(edit->Run());
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: SetSpatialRef(none) failed");
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_regular)
|
||||
{
|
||||
auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
|
||||
auto raster = singleton.Instantiate("raster");
|
||||
ASSERT_NE(raster, nullptr);
|
||||
auto edit = raster->InstantiateSubAlgorithm("edit");
|
||||
ASSERT_NE(edit, nullptr);
|
||||
|
||||
class MyDataset : public GDALDataset
|
||||
{
|
||||
public:
|
||||
MyDataset()
|
||||
{
|
||||
eAccess = GA_Update;
|
||||
}
|
||||
|
||||
CPLErr SetSpatialRef(const OGRSpatialReference *) override
|
||||
{
|
||||
return CE_Failure;
|
||||
}
|
||||
};
|
||||
|
||||
auto datasetArg = edit->GetArg("dataset");
|
||||
ASSERT_NE(datasetArg, nullptr);
|
||||
datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
|
||||
|
||||
auto crsArg = edit->GetArg("crs");
|
||||
ASSERT_NE(crsArg, nullptr);
|
||||
crsArg->Set("EPSG:32632");
|
||||
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
EXPECT_FALSE(edit->Run());
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
EXPECT_STREQ(CPLGetLastErrorMsg(),
|
||||
"edit: SetSpatialRef(EPSG:32632) failed");
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, raster_edit_failures_set_geo_transform)
|
||||
{
|
||||
auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
|
||||
auto raster = singleton.Instantiate("raster");
|
||||
ASSERT_NE(raster, nullptr);
|
||||
auto edit = raster->InstantiateSubAlgorithm("edit");
|
||||
ASSERT_NE(edit, nullptr);
|
||||
|
||||
class MyDataset : public GDALDataset
|
||||
{
|
||||
public:
|
||||
MyDataset()
|
||||
{
|
||||
eAccess = GA_Update;
|
||||
}
|
||||
|
||||
CPLErr SetGeoTransform(double *) override
|
||||
{
|
||||
return CE_Failure;
|
||||
}
|
||||
};
|
||||
|
||||
auto datasetArg = edit->GetArg("dataset");
|
||||
ASSERT_NE(datasetArg, nullptr);
|
||||
datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
|
||||
|
||||
auto extentArg = edit->GetArg("extent");
|
||||
ASSERT_NE(extentArg, nullptr);
|
||||
extentArg->Set(std::vector<double>{2, 49, 3, 50});
|
||||
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
EXPECT_FALSE(edit->Run());
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: Setting extent failed");
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, raster_edit_failures_set_metadata)
|
||||
{
|
||||
auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
|
||||
auto raster = singleton.Instantiate("raster");
|
||||
ASSERT_NE(raster, nullptr);
|
||||
auto edit = raster->InstantiateSubAlgorithm("edit");
|
||||
ASSERT_NE(edit, nullptr);
|
||||
|
||||
class MyDataset : public GDALDataset
|
||||
{
|
||||
public:
|
||||
MyDataset()
|
||||
{
|
||||
eAccess = GA_Update;
|
||||
}
|
||||
|
||||
CPLErr SetMetadataItem(const char *, const char *,
|
||||
const char *) override
|
||||
{
|
||||
return CE_Failure;
|
||||
}
|
||||
};
|
||||
|
||||
auto datasetArg = edit->GetArg("dataset");
|
||||
ASSERT_NE(datasetArg, nullptr);
|
||||
datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
|
||||
|
||||
auto extentArg = edit->GetArg("metadata");
|
||||
ASSERT_NE(extentArg, nullptr);
|
||||
extentArg->Set(std::vector<std::string>{"foo=bar"});
|
||||
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
EXPECT_FALSE(edit->Run());
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
EXPECT_STREQ(CPLGetLastErrorMsg(),
|
||||
"edit: SetMetadataItem('foo', 'bar') failed");
|
||||
}
|
||||
|
||||
TEST_F(test_gdal_algorithm, raster_edit_failures_unset_metadata)
|
||||
{
|
||||
auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
|
||||
auto raster = singleton.Instantiate("raster");
|
||||
ASSERT_NE(raster, nullptr);
|
||||
auto edit = raster->InstantiateSubAlgorithm("edit");
|
||||
ASSERT_NE(edit, nullptr);
|
||||
|
||||
class MyDataset : public GDALDataset
|
||||
{
|
||||
public:
|
||||
MyDataset()
|
||||
{
|
||||
eAccess = GA_Update;
|
||||
}
|
||||
|
||||
CPLErr SetMetadataItem(const char *, const char *,
|
||||
const char *) override
|
||||
{
|
||||
return CE_Failure;
|
||||
}
|
||||
};
|
||||
|
||||
auto datasetArg = edit->GetArg("dataset");
|
||||
ASSERT_NE(datasetArg, nullptr);
|
||||
datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
|
||||
|
||||
auto extentArg = edit->GetArg("unset-metadata");
|
||||
ASSERT_NE(extentArg, nullptr);
|
||||
extentArg->Set(std::vector<std::string>{"foo"});
|
||||
|
||||
CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
|
||||
CPLErrorReset();
|
||||
EXPECT_FALSE(edit->Run());
|
||||
EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
|
||||
EXPECT_STREQ(CPLGetLastErrorMsg(),
|
||||
"edit: SetMetadataItem('foo', NULL) failed");
|
||||
}
|
||||
|
||||
} // namespace test_gdal_algorithm
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
#!/usr/bin/env pytest
|
||||
# -*- coding: utf-8 -*-
|
||||
###############################################################################
|
||||
# Project: GDAL/OGR Test Suite
|
||||
# Purpose: 'gdal raster edit' 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_edit_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
raster = reg.InstantiateAlg("raster")
|
||||
return raster.InstantiateSubAlgorithm("edit")
|
||||
|
||||
|
||||
def test_gdalalg_raster_edit_read_only(tmp_vsimem):
|
||||
|
||||
tmp_filename = str(tmp_vsimem / "tmp.tif")
|
||||
gdal.FileFromMemBuffer(tmp_filename, open("../gcore/data/byte.tif", "rb").read())
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
pipeline.GetArg("dataset").Get().SetDataset(gdal.OpenEx(tmp_filename))
|
||||
with pytest.raises(
|
||||
Exception, match="edit: Dataset should be opened in update mode"
|
||||
):
|
||||
pipeline.Run()
|
||||
|
||||
|
||||
def test_gdalalg_raster_edit_crs(tmp_vsimem):
|
||||
|
||||
tmp_filename = str(tmp_vsimem / "tmp.tif")
|
||||
gdal.FileFromMemBuffer(tmp_filename, open("../gcore/data/byte.tif", "rb").read())
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"--crs=EPSG:32611",
|
||||
tmp_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(tmp_filename) as ds:
|
||||
assert ds.GetSpatialRef().GetAuthorityCode(None) == "32611"
|
||||
|
||||
|
||||
def test_gdalalg_raster_edit_crs_none(tmp_vsimem):
|
||||
|
||||
tmp_filename = str(tmp_vsimem / "tmp.tif")
|
||||
gdal.FileFromMemBuffer(tmp_filename, open("../gcore/data/byte.tif", "rb").read())
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"--crs=none",
|
||||
tmp_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(tmp_filename) as ds:
|
||||
assert ds.GetSpatialRef() is None
|
||||
|
||||
|
||||
def test_gdalalg_raster_edit_extent(tmp_vsimem):
|
||||
|
||||
tmp_filename = str(tmp_vsimem / "tmp.tif")
|
||||
gdal.FileFromMemBuffer(tmp_filename, open("../gcore/data/byte.tif", "rb").read())
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"--extent=1,2,10,200",
|
||||
tmp_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(tmp_filename) as ds:
|
||||
assert ds.GetGeoTransform() == pytest.approx((1.0, 0.45, 0.0, 200.0, 0.0, -9.9))
|
||||
|
||||
|
||||
def test_gdalalg_raster_edit_extent_invalid(tmp_vsimem):
|
||||
|
||||
tmp_filename = str(tmp_vsimem / "tmp.tif")
|
||||
gdal.FileFromMemBuffer(tmp_filename, open("../gcore/data/byte.tif", "rb").read())
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="Value of 'extent' should be xmin,ymin,xmax,ymax with xmin <= xmax and ymin <= ymax",
|
||||
):
|
||||
pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"--extent=1,200,10,2",
|
||||
tmp_filename,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_gdalalg_raster_edit_metadata(tmp_vsimem):
|
||||
|
||||
tmp_filename = str(tmp_vsimem / "tmp.tif")
|
||||
gdal.FileFromMemBuffer(tmp_filename, open("../gcore/data/byte.tif", "rb").read())
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"--metadata",
|
||||
"foo=bar",
|
||||
"--metadata",
|
||||
"bar=baz",
|
||||
tmp_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(tmp_filename) as ds:
|
||||
assert ds.GetMetadata() == {"AREA_OR_POINT": "Area", "foo": "bar", "bar": "baz"}
|
||||
|
||||
pipeline = get_edit_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"--unset-metadata",
|
||||
"foo",
|
||||
tmp_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(tmp_filename) as ds:
|
||||
assert ds.GetMetadata() == {"AREA_OR_POINT": "Area", "bar": "baz"}
|
||||
|
||||
|
||||
def get_pipeline_alg():
|
||||
reg = gdal.GetGlobalAlgorithmRegistry()
|
||||
raster = reg.InstantiateAlg("raster")
|
||||
return raster.InstantiateSubAlgorithm("pipeline")
|
||||
|
||||
|
||||
def test_gdalalg_raster_pipeline_edit_crs(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.tif")
|
||||
|
||||
pipeline = get_pipeline_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"read",
|
||||
"../gcore/data/byte.tif",
|
||||
"!",
|
||||
"edit",
|
||||
"--crs=EPSG:32611",
|
||||
"!",
|
||||
"write",
|
||||
"--overwrite",
|
||||
out_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetSpatialRef().GetAuthorityCode(None) == "32611"
|
||||
assert ds.GetRasterBand(1).Checksum() == 4672
|
||||
|
||||
|
||||
def test_gdalalg_raster_pipeline_edit_crs_none(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.tif")
|
||||
|
||||
pipeline = get_pipeline_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"read",
|
||||
"../gcore/data/byte.tif",
|
||||
"!",
|
||||
"edit",
|
||||
"--crs=none",
|
||||
"!",
|
||||
"write",
|
||||
"--overwrite",
|
||||
out_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetSpatialRef() is None
|
||||
|
||||
|
||||
def test_gdalalg_raster_pipeline_edit_extent(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.tif")
|
||||
|
||||
pipeline = get_pipeline_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"read",
|
||||
"../gcore/data/byte.tif",
|
||||
"!",
|
||||
"edit",
|
||||
"--extent=1,2,10,200",
|
||||
"!",
|
||||
"write",
|
||||
"--overwrite",
|
||||
out_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetGeoTransform() == pytest.approx((1.0, 0.45, 0.0, 200.0, 0.0, -9.9))
|
||||
|
||||
|
||||
def test_gdalalg_raster_pipeline_edit_metadata(tmp_vsimem):
|
||||
|
||||
out_filename = str(tmp_vsimem / "out.tif")
|
||||
|
||||
pipeline = get_pipeline_alg()
|
||||
assert pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
"read",
|
||||
"../gcore/data/byte.tif",
|
||||
"!",
|
||||
"edit",
|
||||
"--metadata=foo=bar,bar=baz",
|
||||
"!",
|
||||
"edit",
|
||||
"--unset-metadata=foo",
|
||||
"!",
|
||||
"write",
|
||||
"--overwrite",
|
||||
out_filename,
|
||||
]
|
||||
)
|
||||
|
||||
with gdal.OpenEx(out_filename) as ds:
|
||||
assert ds.GetMetadata() == {"AREA_OR_POINT": "Area", "bar": "baz"}
|
|
@ -372,7 +372,7 @@ def test_gdalalg_raster_pipeline_reproject_invalid_src_crs(tmp_vsimem):
|
|||
pipeline = get_pipeline_alg()
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="reproject: Invalid value for '--src-crs'",
|
||||
match="reproject: Invalid value for 'src-crs' argument",
|
||||
):
|
||||
pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
|
@ -396,7 +396,7 @@ def test_gdalalg_raster_pipeline_reproject_invalid_dst_crs(tmp_vsimem):
|
|||
pipeline = get_pipeline_alg()
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="reproject: Invalid value for '--dst-crs'",
|
||||
match="reproject: Invalid value for 'dst-crs' argument",
|
||||
):
|
||||
pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
|
|
|
@ -577,7 +577,7 @@ def test_gdalalg_vector_pipeline_reproject_invalid_src_crs(tmp_vsimem):
|
|||
pipeline = get_pipeline_alg()
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="reproject: Invalid value for '--src-crs'",
|
||||
match="reproject: Invalid value for 'src-crs' argument",
|
||||
):
|
||||
pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
|
@ -601,7 +601,7 @@ def test_gdalalg_vector_pipeline_reproject_invalid_dst_crs(tmp_vsimem):
|
|||
pipeline = get_pipeline_alg()
|
||||
with pytest.raises(
|
||||
Exception,
|
||||
match="reproject: Invalid value for '--dst-crs'",
|
||||
match="reproject: Invalid value for 'dst-crs' argument",
|
||||
):
|
||||
pipeline.ParseRunAndFinalize(
|
||||
[
|
||||
|
|
|
@ -229,6 +229,13 @@ man_pages = [
|
|||
[author_evenr],
|
||||
1,
|
||||
),
|
||||
(
|
||||
"programs/gdal_raster_edit",
|
||||
"gdal-raster-edit",
|
||||
"Edit in place a raster dataset",
|
||||
[author_evenr],
|
||||
1,
|
||||
),
|
||||
(
|
||||
"programs/gdal_raster_pipeline",
|
||||
"gdal-raster-pipeline",
|
||||
|
@ -239,7 +246,7 @@ man_pages = [
|
|||
(
|
||||
"programs/gdal_raster_reproject",
|
||||
"gdal-raster-reproject",
|
||||
"Reproect a raster dataset",
|
||||
"Reproject a raster dataset",
|
||||
[author_evenr],
|
||||
1,
|
||||
),
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
.. _gdal_raster_edit_subcommand:
|
||||
|
||||
================================================================================
|
||||
"gdal raster edit" sub-command
|
||||
================================================================================
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
.. only:: html
|
||||
|
||||
Edit in place a raster dataset.
|
||||
|
||||
.. Index:: gdal raster edit
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
.. code-block::
|
||||
|
||||
Usage: gdal raster edit [OPTIONS] <DATASET>
|
||||
|
||||
Edit a raster dataset.
|
||||
|
||||
Positional arguments:
|
||||
--dataset <DATASET> Dataset (in-place updated) [required]
|
||||
|
||||
Common Options:
|
||||
-h, --help Display help message and exit
|
||||
--version Display GDAL version and exit
|
||||
--json-usage Display usage as JSON document and exit
|
||||
--drivers Display driver list as JSON document and exit
|
||||
|
||||
Options:
|
||||
--crs <CRS> Override CRS (without reprojection)
|
||||
--extent <EXTENT> Extent as xmin,ymin,xmax,ymax
|
||||
--metadata <KEY>=<VALUE> Add/update dataset metadata item [may be repeated]
|
||||
--unset-metadata <KEY> Remove dataset metadata item [may be repeated]
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
:program:`gdal raster edit` can be used to edit a raster dataset.
|
||||
|
||||
This subcommand is also available as a potential step of :ref:`gdal_raster_pipeline_subcommand`
|
||||
|
||||
.. option:: --dataset <DATASET>
|
||||
|
||||
Dataset name, to be in-place updated. Required.
|
||||
|
||||
.. option:: --crs <CRS>
|
||||
|
||||
Override CRS, without reprojecting.
|
||||
|
||||
The coordinate reference systems that can be passed are anything supported by the
|
||||
:cpp:func:`OGRSpatialReference::SetFromUserInput` call, which includes EPSG Projected,
|
||||
Geographic or Compound CRS (i.e. EPSG:4296), a well known text (WKT) CRS definition,
|
||||
PROJ.4 declarations, or the name of a .prj file containing a WKT CRS definition.
|
||||
|
||||
``null`` or ``none`` can be specified to unset an existing CRS.
|
||||
|
||||
Note that the spatial extent is also left unchanged.
|
||||
|
||||
.. option:: --extent <xmin>,<ymin>,<xmax>,ymax>
|
||||
|
||||
Override the spatial extent, without reprojecting or subsetting.
|
||||
|
||||
.. option:: --metadata <KEY>=<VALUE>
|
||||
|
||||
Add/update dataset metadata item, at the dataset level.
|
||||
|
||||
.. option:: --unset-metadata <KEY>
|
||||
|
||||
Remove dataset metadata item, at the dataset level.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. example::
|
||||
:title: Override (without reprojecting) the CRS of a dataset
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ gdal raster edit --crs=EPSG:32632 my.tif
|
||||
|
||||
.. example::
|
||||
:title: Override (without reprojecting or subsetting) the extent of a dataset
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ gdal raster edit --extent=2,49,3,50 my.tif
|
||||
|
||||
.. example::
|
||||
:title: Add a metadata item
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ gdal raster edit --metadata AUTHOR=EvenR my.tif
|
||||
|
||||
.. example::
|
||||
:title: Remove a metadata item
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ gdal raster edit --unset-metadata AUTHOR my.tif
|
|
@ -49,6 +49,19 @@ Potential steps are:
|
|||
--if, --input-format <INPUT-FORMAT> Input formats [may be repeated]
|
||||
--oo, --open-option <KEY=VALUE> Open options [may be repeated]
|
||||
|
||||
* edit [OPTIONS]
|
||||
|
||||
.. code-block::
|
||||
|
||||
Edit a raster dataset.
|
||||
|
||||
Options:
|
||||
--crs <CRS> Override CRS (without reprojection)
|
||||
--extent <EXTENT> Extent as xmin,ymin,xmax,ymax
|
||||
--metadata <KEY>=<VALUE> Add/update dataset metadata item [may be repeated]
|
||||
--unset-metadata <KEY> Remove dataset metadata item [may be repeated]
|
||||
|
||||
Details for options can be found in :ref:`gdal_raster_edit_subcommand`.
|
||||
|
||||
* reproject [OPTIONS]
|
||||
|
||||
|
@ -64,6 +77,7 @@ Potential steps are:
|
|||
--extent <xmin>,<ymin>,<xmax>,<ymax> Target extent (in destination CRS units)
|
||||
--target-aligned-pixels Round target extent to target resolution
|
||||
|
||||
Details for options can be found in :ref:`gdal_raster_reproject_subcommand`.
|
||||
|
||||
* write [OPTIONS] <OUTPUT>
|
||||
|
||||
|
@ -91,8 +105,8 @@ Examples
|
|||
--------
|
||||
|
||||
.. example::
|
||||
:title: Reproject a GeoTIFF file to CRS EPSG:32632 ("WGS 84 / UTM zone 32N")
|
||||
:title: Reproject a GeoTIFF file to CRS EPSG:32632 ("WGS 84 / UTM zone 32N") and adding a metadata item
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ gdal raster pipeline --progress ! read in.tif ! reproject --dst-crs=EPSG:32632 ! write out.tif --overwrite
|
||||
$ gdal raster pipeline --progress ! read in.tif ! reproject --dst-crs=EPSG:32632 ! edit --metadata AUTHOR=EvenR ! write out.tif --overwrite
|
||||
|
|
|
@ -31,6 +31,7 @@ single :program:`gdal` program that accepts commands and subcommands.
|
|||
gdal_raster
|
||||
gdal_raster_info
|
||||
gdal_raster_convert
|
||||
gdal_raster_edit
|
||||
gdal_raster_pipeline
|
||||
gdal_raster_reproject
|
||||
gdal_vector
|
||||
|
@ -46,6 +47,7 @@ single :program:`gdal` program that accepts commands and subcommands.
|
|||
- :ref:`gdal_raster_command`: Entry point for raster commands
|
||||
- :ref:`gdal_raster_info_subcommand`: Get information on a raster dataset
|
||||
- :ref:`gdal_raster_convert_subcommand`: Convert a raster dataset
|
||||
- :ref:`gdal_raster_edit_subcommand`: Edit in place a raster dataset
|
||||
- :ref:`gdal_raster_pipeline_subcommand`: Process a raster dataset
|
||||
- :ref:`gdal_raster_reproject_subcommand`: Reproject a raster dataset
|
||||
- :ref:`gdal_vector_command`: Entry point for vector commands
|
||||
|
|
|
@ -699,6 +699,40 @@ GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
|
|||
other.m_name.clear();
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
|
||||
/************************************************************************/
|
||||
|
||||
GDALInConstructionAlgorithmArg &
|
||||
GDALInConstructionAlgorithmArg::SetIsCRSArg(bool noneAllowed)
|
||||
{
|
||||
if (GetType() != GAAT_STRING)
|
||||
{
|
||||
CPLError(CE_Failure, CPLE_AppDefined,
|
||||
"SetIsCRSArg() can only be called on a String argument");
|
||||
return *this;
|
||||
}
|
||||
return AddValidationAction(
|
||||
[this, noneAllowed]()
|
||||
{
|
||||
const std::string &osVal =
|
||||
static_cast<const GDALInConstructionAlgorithmArg *>(this)
|
||||
->Get<std::string>();
|
||||
if (!noneAllowed || (osVal != "none" && osVal != "null"))
|
||||
{
|
||||
OGRSpatialReference oSRS;
|
||||
if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
|
||||
{
|
||||
m_owner->ReportError(CE_Failure, CPLE_AppDefined,
|
||||
"Invalid value for '%s' argument",
|
||||
GetName().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GDALAlgorithm::GDALAlgorithm() */
|
||||
/************************************************************************/
|
||||
|
|
|
@ -449,7 +449,7 @@ class CPL_DLL GDALArgDatasetValue final
|
|||
|
||||
/** Get which type of dataset is allowed / generated.
|
||||
* Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
|
||||
* GDAL_OF_MULTIDIM_RASTER.
|
||||
* GDAL_OF_MULTIDIM_RASTER, possibly combined with GDAL_OF_UPDATE.
|
||||
*/
|
||||
GDALArgDatasetValueType GetType() const
|
||||
{
|
||||
|
@ -1583,6 +1583,13 @@ class CPL_DLL GDALInConstructionAlgorithmArg final : public GDALAlgorithmArg
|
|||
return *this;
|
||||
}
|
||||
|
||||
/** Register an action to validate that the argument value is a valid
|
||||
* CRS definition.
|
||||
* @param noneAllowed Set to true to mean that "null" or "none" are allowed
|
||||
* to mean to unset CRS.
|
||||
*/
|
||||
GDALInConstructionAlgorithmArg &SetIsCRSArg(bool noneAllowed = false);
|
||||
|
||||
private:
|
||||
GDALAlgorithm *const m_owner;
|
||||
|
||||
|
@ -2060,6 +2067,9 @@ class CPL_DLL GDALAlgorithmRegistry
|
|||
/** Add --progress argument. */
|
||||
GDALInConstructionAlgorithmArg &AddProgressArg();
|
||||
|
||||
/** Validation function to use for key=value type of arguments. */
|
||||
bool ValidateKeyValue(const GDALAlgorithmArg &arg) const;
|
||||
|
||||
//! @cond Doxygen_Suppress
|
||||
void AddAliasFor(GDALInConstructionAlgorithmArg *arg,
|
||||
const std::string &alias);
|
||||
|
@ -2112,7 +2122,6 @@ class CPL_DLL GDALAlgorithmRegistry
|
|||
&inConstructionValues);
|
||||
|
||||
bool ValidateFormat(const GDALAlgorithmArg &arg) const;
|
||||
bool ValidateKeyValue(const GDALAlgorithmArg &arg) const;
|
||||
|
||||
virtual bool RunImpl(GDALProgressFunc pfnProgress, void *pProgressData) = 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue