Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename 'gdal raster buildvrt' to 'gdal raster mosaic', and add 'gdal raster stack' #11715

Merged
merged 2 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ add_library(
gdalalg_main.cpp
gdalalg_pipeline.cpp
gdalalg_raster.cpp
gdalalg_raster_buildvrt.cpp
gdalalg_raster_info.cpp
gdalalg_raster_clip.cpp
gdalalg_raster_convert.cpp
gdalalg_raster_edit.cpp
gdalalg_raster_mosaic.cpp
gdalalg_raster_pipeline.cpp
gdalalg_raster_overview_add.cpp
gdalalg_raster_overview_delete.cpp
gdalalg_raster_read.cpp
gdalalg_raster_reproject.cpp
gdalalg_raster_stack.cpp
gdalalg_raster_write.cpp
gdalalg_vector.cpp
gdalalg_vector_info.cpp
Expand Down
6 changes: 4 additions & 2 deletions apps/gdalalg_raster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@

#include "gdalalgorithm.h"

#include "gdalalg_raster_buildvrt.h"
#include "gdalalg_raster_info.h"
#include "gdalalg_raster_clip.h"
#include "gdalalg_raster_convert.h"
#include "gdalalg_raster_edit.h"
#include "gdalalg_raster_mosaic.h"
#include "gdalalg_raster_overview.h"
#include "gdalalg_raster_pipeline.h"
#include "gdalalg_raster_reproject.h"
#include "gdalalg_raster_stack.h"

/************************************************************************/
/* GDALRasterAlgorithm */
Expand All @@ -46,7 +47,8 @@ class GDALRasterAlgorithm final : public GDALAlgorithm
RegisterSubAlgorithm<GDALRasterOverviewAlgorithm>();
RegisterSubAlgorithm<GDALRasterPipelineAlgorithm>();
RegisterSubAlgorithm<GDALRasterReprojectAlgorithmStandalone>();
RegisterSubAlgorithm<GDALRasterBuildVRTAlgorithm>();
RegisterSubAlgorithm<GDALRasterMosaicAlgorithm>();
RegisterSubAlgorithm<GDALRasterStackAlgorithm>();
}

private:
Expand Down
320 changes: 320 additions & 0 deletions apps/gdalalg_raster_mosaic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
/******************************************************************************
*
* Project: GDAL
* Purpose: gdal "raster mosaic" 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_mosaic.h"

#include "cpl_conv.h"
#include "cpl_vsi_virtual.h"

#include "gdal_priv.h"
#include "gdal_utils.h"

//! @cond Doxygen_Suppress

#ifndef _
#define _(x) (x)
#endif

/************************************************************************/
/* GDALRasterMosaicAlgorithm::GDALRasterMosaicAlgorithm() */
/************************************************************************/

GDALRasterMosaicAlgorithm::GDALRasterMosaicAlgorithm()
: GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
{
AddProgressArg();
AddOutputFormatArg(&m_format);
AddArg(GDAL_ARG_NAME_INPUT, 'i',
_("Input raster datasets (or specify a @<filename> to point to a "
"file containing filenames)"),
&m_inputDatasets, GDAL_OF_RASTER)
.SetPositional()
.SetMinCount(1)
.SetAutoOpenDataset(false)
.SetMetaVar("INPUTS");
AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
AddCreationOptionsArg(&m_creationOptions);
AddArg("band", 'b', _("Specify input band(s) number."), &m_bands);
AddOverwriteArg(&m_overwrite);
{
auto &arg = AddArg("resolution", 0,
_("Target resolution (in destination CRS units)"),
&m_resolution)
.SetMetaVar("<xres>,<yres>|average|highest|lowest");
arg.AddValidationAction(
[this, &arg]()
{
const std::string val = arg.Get<std::string>();
if (val != "average" && val != "highest" && val != "lowest")
{
const auto aosTokens =
CPLStringList(CSLTokenizeString2(val.c_str(), ",", 0));
if (aosTokens.size() != 2 ||
CPLGetValueType(aosTokens[0]) == CPL_VALUE_STRING ||
CPLGetValueType(aosTokens[1]) == CPL_VALUE_STRING ||
CPLAtof(aosTokens[0]) <= 0 ||
CPLAtof(aosTokens[1]) <= 0)
{
ReportError(CE_Failure, CPLE_AppDefined,
"resolution: two comma separated positive "
"values should be provided, or 'average', "
"'highest' or 'lowest'");
return false;
}
}
return true;
});
}
AddBBOXArg(&m_bbox, _("Target bounding box as xmin,ymin,xmax,ymax (in "
"destination CRS units)"));
AddArg("target-aligned-pixels", 0,
_("Round target extent to target resolution"),
&m_targetAlignedPixels)
.AddHiddenAlias("tap");
AddArg("srcnodata", 0, _("Set nodata values for input bands."),
&m_srcNoData)
.SetMinCount(1)
.SetRepeatedArgAllowed(false);
AddArg("dstnodata", 0,
_("Set nodata values at the destination band level."), &m_dstNoData)
.SetMinCount(1)
.SetRepeatedArgAllowed(false);
AddArg("hidenodata", 0,
_("Makes the destination band not report the NoData."),
&m_hideNoData);
AddArg("addalpha", 0,
_("Adds an alpha mask band to the destination when the source "
"raster have "
"none."),
&m_addAlpha);
}

/************************************************************************/
/* GDALRasterMosaicAlgorithm::RunImpl() */
/************************************************************************/

bool GDALRasterMosaicAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
void *pProgressData)
{
if (m_outputDataset.GetDatasetRef())
{
ReportError(CE_Failure, CPLE_NotSupported,
"gdal raster mosaic does not support outputting to an "
"already opened output dataset");
return false;
}

std::vector<GDALDatasetH> ahInputDatasets;
CPLStringList aosInputDatasetNames;
bool foundByRef = false;
bool foundByName = false;
for (auto &ds : m_inputDatasets)
{
if (ds.GetDatasetRef())
{
foundByRef = true;
ahInputDatasets.push_back(
GDALDataset::ToHandle(ds.GetDatasetRef()));
}
else if (!ds.GetName().empty())
{
foundByName = true;
if (ds.GetName()[0] == '@')
{
auto f = VSIVirtualHandleUniquePtr(
VSIFOpenL(ds.GetName().c_str() + 1, "r"));
if (!f)
{
ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
ds.GetName().c_str() + 1);
return false;
}
while (const char *filename = CPLReadLineL(f.get()))
{
aosInputDatasetNames.push_back(filename);
}
}
else if (ds.GetName().find_first_of("*?[") != std::string::npos)
{
CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
pfnProgress, pProgressData));
for (const char *pszStr : aosMatches)
{
aosInputDatasetNames.push_back(pszStr);
}
}
else
{
aosInputDatasetNames.push_back(ds.GetName().c_str());
}
}
}
if (foundByName && foundByRef)
{
ReportError(CE_Failure, CPLE_NotSupported,
"Input datasets should be provided either all by reference "
"or all by name");
return false;
}

VSIStatBufL sStat;
if (!m_overwrite && !m_outputDataset.GetName().empty() &&
(VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
std::unique_ptr<GDALDataset>(
GDALDataset::Open(m_outputDataset.GetName().c_str()))))
{
ReportError(CE_Failure, CPLE_AppDefined,
"File '%s' already exists. Specify the --overwrite "
"option to overwrite it.",
m_outputDataset.GetName().c_str());
return false;
}

const bool bVRTOutput =
m_outputDataset.GetName().empty() || EQUAL(m_format.c_str(), "VRT") ||
EQUAL(CPLGetExtensionSafe(m_outputDataset.GetName().c_str()).c_str(),
"VRT");

CPLStringList aosOptions;
if (!m_resolution.empty())
{
const auto aosTokens =
CPLStringList(CSLTokenizeString2(m_resolution.c_str(), ",", 0));
if (aosTokens.size() == 2)
{
aosOptions.push_back("-tr");
aosOptions.push_back(aosTokens[0]);
aosOptions.push_back(aosTokens[1]);
}
else
{
aosOptions.push_back("-resolution");
aosOptions.push_back(m_resolution);
}
}
if (!m_bbox.empty())
{
aosOptions.push_back("-te");
aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[0]));
aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[1]));
aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[2]));
aosOptions.push_back(CPLSPrintf("%.17g", m_bbox[3]));
}
if (m_targetAlignedPixels)
{
aosOptions.push_back("-tap");
}
if (!m_srcNoData.empty())
{
aosOptions.push_back("-srcnodata");
std::string s;
for (double v : m_srcNoData)
{
if (!s.empty())
s += " ";
s += CPLSPrintf("%.17g", v);
}
aosOptions.push_back(s);
}
if (!m_dstNoData.empty())
{
aosOptions.push_back("-vrtnodata");
std::string s;
for (double v : m_dstNoData)
{
if (!s.empty())
s += " ";
s += CPLSPrintf("%.17g", v);
}
aosOptions.push_back(s);
}
if (bVRTOutput)
{
for (const auto &co : m_creationOptions)
{
aosOptions.push_back("-co");
aosOptions.push_back(co);
}
}
for (const int b : m_bands)
{
aosOptions.push_back("-b");
aosOptions.push_back(CPLSPrintf("%d", b));
}
if (m_addAlpha)
{
aosOptions.push_back("-addalpha");
}
if (m_hideNoData)
{
aosOptions.push_back("-hidenodata");
}

GDALBuildVRTOptions *psOptions =
GDALBuildVRTOptionsNew(aosOptions.List(), nullptr);
if (bVRTOutput)
{
GDALBuildVRTOptionsSetProgress(psOptions, pfnProgress, pProgressData);
}

auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
GDALBuildVRT(bVRTOutput ? m_outputDataset.GetName().c_str() : "",
foundByName ? aosInputDatasetNames.size()
: static_cast<int>(m_inputDatasets.size()),
ahInputDatasets.empty() ? nullptr : ahInputDatasets.data(),
aosInputDatasetNames.List(), psOptions, nullptr)));
GDALBuildVRTOptionsFree(psOptions);
bool bOK = poOutDS != nullptr;
if (bOK)
{
if (bVRTOutput)
{
m_outputDataset.Set(std::move(poOutDS));
}
else
{
CPLStringList aosTranslateOptions;
if (!m_format.empty())
{
aosTranslateOptions.AddString("-of");
aosTranslateOptions.AddString(m_format.c_str());
}
for (const auto &co : m_creationOptions)
{
aosTranslateOptions.AddString("-co");
aosTranslateOptions.AddString(co.c_str());
}

GDALTranslateOptions *psTranslateOptions =
GDALTranslateOptionsNew(aosTranslateOptions.List(), nullptr);
GDALTranslateOptionsSetProgress(psTranslateOptions, pfnProgress,
pProgressData);

auto poFinalDS =
std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
GDALTranslate(m_outputDataset.GetName().c_str(),
GDALDataset::ToHandle(poOutDS.get()),
psTranslateOptions, nullptr)));
GDALTranslateOptionsFree(psTranslateOptions);

bOK = poFinalDS != nullptr;
if (bOK)
{
m_outputDataset.Set(std::move(poFinalDS));
}
}
}

return bOK;
}

//! @endcond
Loading
Loading