Skip to content

Commit e402b71

Browse files
committed
Add 'gdal raster color-map'
1 parent df940bf commit e402b71

16 files changed

+567
-16
lines changed

apps/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library(
1919
gdalalg_raster_astype.cpp
2020
gdalalg_raster_calc.cpp
2121
gdalalg_raster_clip.cpp
22+
gdalalg_raster_color_map.cpp
2223
gdalalg_raster_convert.cpp
2324
gdalalg_raster_edit.cpp
2425
gdalalg_raster_contour.cpp

apps/gdalalg_raster.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "gdalalg_raster_astype.h"
1818
#include "gdalalg_raster_calc.h"
1919
#include "gdalalg_raster_clip.h"
20+
#include "gdalalg_raster_color_map.h"
2021
#include "gdalalg_raster_convert.h"
2122
#include "gdalalg_raster_edit.h"
2223
#include "gdalalg_raster_contour.h"
@@ -52,6 +53,7 @@ class GDALRasterAlgorithm final : public GDALAlgorithm
5253
RegisterSubAlgorithm<GDALRasterAspectAlgorithmStandalone>();
5354
RegisterSubAlgorithm<GDALRasterAsTypeAlgorithmStandalone>();
5455
RegisterSubAlgorithm<GDALRasterCalcAlgorithm>();
56+
RegisterSubAlgorithm<GDALRasterColorMapAlgorithmStandalone>();
5557
RegisterSubAlgorithm<GDALRasterConvertAlgorithm>();
5658
RegisterSubAlgorithm<GDALRasterClipAlgorithmStandalone>();
5759
RegisterSubAlgorithm<GDALRasterEditAlgorithmStandalone>();

apps/gdalalg_raster_color_map.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/******************************************************************************
2+
*
3+
* Project: GDAL
4+
* Purpose: "color-map" step of "raster pipeline"
5+
* Author: Even Rouault <even dot rouault at spatialys.com>
6+
*
7+
******************************************************************************
8+
* Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9+
*
10+
* SPDX-License-Identifier: MIT
11+
****************************************************************************/
12+
13+
#include "gdalalg_raster_color_map.h"
14+
15+
#include "gdal_priv.h"
16+
#include "gdal_utils.h"
17+
18+
#include <cmath>
19+
20+
//! @cond Doxygen_Suppress
21+
22+
#ifndef _
23+
#define _(x) (x)
24+
#endif
25+
26+
/************************************************************************/
27+
/* GDALRasterColorMapAlgorithm::GDALRasterColorMapAlgorithm() */
28+
/************************************************************************/
29+
30+
GDALRasterColorMapAlgorithm::GDALRasterColorMapAlgorithm(bool standaloneStep)
31+
: GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
32+
standaloneStep)
33+
{
34+
AddBandArg(&m_band).SetDefault(m_band);
35+
AddArg("color-map", 0, _("Color map filename"), &m_colorMap);
36+
AddArg("addalpha", 0, _("Adds an alpha mask band to the destination."),
37+
&m_addAlpha);
38+
AddArg("color-selection", 0,
39+
_("How to compute output colors from input values"),
40+
&m_colorSelection)
41+
.SetChoices("interpolate", "exact", "nearest")
42+
.SetDefault(m_colorSelection);
43+
}
44+
45+
/************************************************************************/
46+
/* GDALRasterColorMapAlgorithm::RunStep() */
47+
/************************************************************************/
48+
49+
bool GDALRasterColorMapAlgorithm::RunStep(GDALProgressFunc, void *)
50+
{
51+
auto poSrcDS = m_inputDataset.GetDatasetRef();
52+
CPLAssert(poSrcDS);
53+
CPLAssert(m_outputDataset.GetName().empty());
54+
CPLAssert(!m_outputDataset.GetDatasetRef());
55+
56+
CPLStringList aosOptions;
57+
aosOptions.AddString("-of");
58+
aosOptions.AddString("VRT");
59+
aosOptions.AddString("-b");
60+
aosOptions.AddString(CPLSPrintf("%d", m_band));
61+
62+
if (m_colorMap.empty())
63+
{
64+
if (poSrcDS->GetRasterBand(m_band)->GetColorTable() == nullptr)
65+
{
66+
ReportError(CE_Failure, CPLE_AppDefined,
67+
"Input dataset has no color table and 'color-map' "
68+
"option was not specified.");
69+
return false;
70+
}
71+
72+
if (GetArg("color-selection")->IsExplicitlySet() &&
73+
m_colorSelection != "exact")
74+
{
75+
ReportError(
76+
CE_Warning, CPLE_NotSupported,
77+
"When using band color table, 'color-selection' is ignored");
78+
}
79+
80+
aosOptions.AddString("-expand");
81+
aosOptions.AddString(m_addAlpha ? "rgba" : "rgb");
82+
83+
GDALTranslateOptions *psOptions =
84+
GDALTranslateOptionsNew(aosOptions.List(), nullptr);
85+
86+
// Backup error state since GDALTranslate() resets it multiple times
87+
const auto nLastErrorNum = CPLGetLastErrorNo();
88+
const auto nLastErrorType = CPLGetLastErrorType();
89+
const std::string osLastErrorMsg = CPLGetLastErrorMsg();
90+
const auto nLastErrorCounter = CPLGetErrorCounter();
91+
92+
auto poOutDS =
93+
std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALTranslate(
94+
"", GDALDataset::ToHandle(poSrcDS), psOptions, nullptr)));
95+
96+
if (nLastErrorCounter > 0 && CPLGetErrorCounter() == 0)
97+
{
98+
CPLErrorSetState(nLastErrorType, nLastErrorNum,
99+
osLastErrorMsg.c_str(), &nLastErrorCounter);
100+
}
101+
102+
GDALTranslateOptionsFree(psOptions);
103+
const bool bRet = poOutDS != nullptr;
104+
if (poOutDS)
105+
{
106+
m_outputDataset.Set(std::move(poOutDS));
107+
}
108+
109+
return bRet;
110+
}
111+
else
112+
{
113+
if (m_addAlpha)
114+
aosOptions.AddString("-alpha");
115+
if (m_colorSelection == "exact")
116+
aosOptions.AddString("-exact_color_entry");
117+
else if (m_colorSelection == "nearest")
118+
aosOptions.AddString("-nearest_color_entry");
119+
120+
GDALDEMProcessingOptions *psOptions =
121+
GDALDEMProcessingOptionsNew(aosOptions.List(), nullptr);
122+
123+
auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
124+
GDALDEMProcessing("", GDALDataset::ToHandle(poSrcDS),
125+
"color-relief", m_colorMap.c_str(), psOptions,
126+
nullptr)));
127+
GDALDEMProcessingOptionsFree(psOptions);
128+
const bool bRet = poOutDS != nullptr;
129+
if (poOutDS)
130+
{
131+
m_outputDataset.Set(std::move(poOutDS));
132+
}
133+
134+
return bRet;
135+
}
136+
}
137+
138+
//! @endcond

apps/gdalalg_raster_color_map.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/******************************************************************************
2+
*
3+
* Project: GDAL
4+
* Purpose: "color-map" step of "raster pipeline"
5+
* Author: Even Rouault <even dot rouault at spatialys.com>
6+
*
7+
******************************************************************************
8+
* Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9+
*
10+
* SPDX-License-Identifier: MIT
11+
****************************************************************************/
12+
13+
#ifndef GDALALG_RASTER_COLOR_MAP_INCLUDED
14+
#define GDALALG_RASTER_COLOR_MAP_INCLUDED
15+
16+
#include "gdalalg_raster_pipeline.h"
17+
18+
#include <limits>
19+
20+
//! @cond Doxygen_Suppress
21+
22+
/************************************************************************/
23+
/* GDALRasterColorMapAlgorithm */
24+
/************************************************************************/
25+
26+
class GDALRasterColorMapAlgorithm /* non final */
27+
: public GDALRasterPipelineStepAlgorithm
28+
{
29+
public:
30+
static constexpr const char *NAME = "color-map";
31+
static constexpr const char *DESCRIPTION =
32+
"Generate a RGB or RGBA dataset from a single band, using a color "
33+
"map";
34+
static constexpr const char *HELP_URL =
35+
"/programs/gdal_raster_color_map.html";
36+
37+
explicit GDALRasterColorMapAlgorithm(bool standaloneStep = false);
38+
39+
private:
40+
bool RunStep(GDALProgressFunc pfnProgress, void *pProgressData) override;
41+
42+
int m_band = 1;
43+
std::string m_colorMap{};
44+
bool m_addAlpha = false;
45+
std::string m_colorSelection = "interpolate";
46+
};
47+
48+
/************************************************************************/
49+
/* GDALRasterColorMapAlgorithmStandalone */
50+
/************************************************************************/
51+
52+
class GDALRasterColorMapAlgorithmStandalone final
53+
: public GDALRasterColorMapAlgorithm
54+
{
55+
public:
56+
GDALRasterColorMapAlgorithmStandalone()
57+
: GDALRasterColorMapAlgorithm(/* standaloneStep = */ true)
58+
{
59+
}
60+
};
61+
62+
//! @endcond
63+
64+
#endif /* GDALALG_RASTER_COLOR_MAP_INCLUDED */

apps/gdalalg_raster_pipeline.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "gdalalg_raster_aspect.h"
1616
#include "gdalalg_raster_astype.h"
1717
#include "gdalalg_raster_clip.h"
18+
#include "gdalalg_raster_color_map.h"
1819
#include "gdalalg_raster_edit.h"
1920
#include "gdalalg_raster_hillshade.h"
2021
#include "gdalalg_raster_reproject.h"
@@ -262,6 +263,7 @@ GDALRasterPipelineAlgorithm::GDALRasterPipelineAlgorithm(
262263
m_stepRegistry.Register<GDALRasterAspectAlgorithm>();
263264
m_stepRegistry.Register<GDALRasterAsTypeAlgorithm>();
264265
m_stepRegistry.Register<GDALRasterClipAlgorithm>();
266+
m_stepRegistry.Register<GDALRasterColorMapAlgorithm>();
265267
m_stepRegistry.Register<GDALRasterEditAlgorithm>();
266268
m_stepRegistry.Register<GDALRasterHillshadeAlgorithm>();
267269
m_stepRegistry.Register<GDALRasterReprojectAlgorithm>();

apps/gdalalg_raster_write.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,23 @@ bool GDALRasterWriteAlgorithm::RunStep(GDALProgressFunc pfnProgress,
6565
GDALTranslateOptionsNew(aosOptions.List(), nullptr);
6666
GDALTranslateOptionsSetProgress(psOptions, pfnProgress, pProgressData);
6767

68+
// Backup error state since GDALTranslate() resets it multiple times
69+
const auto nLastErrorNum = CPLGetLastErrorNo();
70+
const auto nLastErrorType = CPLGetLastErrorType();
71+
const std::string osLastErrorMsg = CPLGetLastErrorMsg();
72+
const auto nLastErrorCounter = CPLGetErrorCounter();
73+
6874
GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
6975
auto poRetDS = GDALDataset::FromHandle(GDALTranslate(
7076
m_outputDataset.GetName().c_str(), hSrcDS, psOptions, nullptr));
7177
GDALTranslateOptionsFree(psOptions);
78+
79+
if (nLastErrorCounter > 0 && CPLGetErrorCounter() == 0)
80+
{
81+
CPLErrorSetState(nLastErrorType, nLastErrorNum, osLastErrorMsg.c_str(),
82+
&nLastErrorCounter);
83+
}
84+
7285
if (!poRetDS)
7386
return false;
7487

apps/gdaldem_lib.cpp

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,22 +2280,22 @@ GDALColorRelief(GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand1,
22802280
/* GDALGenerateVRTColorRelief() */
22812281
/************************************************************************/
22822282

2283-
static CPLErr GDALGenerateVRTColorRelief(const char *pszDstFilename,
2284-
GDALDatasetH hSrcDataset,
2285-
GDALRasterBandH hSrcBand,
2286-
const char *pszColorFilename,
2287-
ColorSelectionMode eColorSelectionMode,
2288-
bool bAddAlpha)
2283+
static bool GDALGenerateVRTColorRelief(const char *pszDstFilename,
2284+
GDALDatasetH hSrcDataset,
2285+
GDALRasterBandH hSrcBand,
2286+
const char *pszColorFilename,
2287+
ColorSelectionMode eColorSelectionMode,
2288+
bool bAddAlpha)
22892289
{
22902290
const auto asColorAssociation = GDALColorReliefParseColorFile(
22912291
hSrcBand, pszColorFilename, eColorSelectionMode);
22922292
if (asColorAssociation.empty())
2293-
return CE_Failure;
2293+
return false;
22942294

22952295
VSILFILE *fp = VSIFOpenL(pszDstFilename, "wt");
22962296
if (fp == nullptr)
22972297
{
2298-
return CE_Failure;
2298+
return false;
22992299
}
23002300

23012301
const int nXSize = GDALGetRasterBandXSize(hSrcBand);
@@ -2445,7 +2445,7 @@ static CPLErr GDALGenerateVRTColorRelief(const char *pszDstFilename,
24452445

24462446
VSIFCloseL(fp);
24472447

2448-
return (bOK) ? CE_None : CE_Failure;
2448+
return bOK;
24492449
}
24502450

24512451
/************************************************************************/
@@ -3965,11 +3965,31 @@ GDALDatasetH GDALDEMProcessing(const char *pszDest, GDALDatasetH hSrcDataset,
39653965
{
39663966
if (eUtilityMode == COLOR_RELIEF)
39673967
{
3968-
GDALGenerateVRTColorRelief(
3969-
pszDest, hSrcDataset, hSrcBand, pszColorFilename,
3970-
psOptions->eColorSelectionMode, psOptions->bAddAlpha);
3971-
3972-
return GDALOpen(pszDest, GA_Update);
3968+
const bool bTmpFile = pszDest[0] == 0;
3969+
const std::string osTmpFile =
3970+
VSIMemGenerateHiddenFilename("tmp.vrt");
3971+
if (bTmpFile)
3972+
pszDest = osTmpFile.c_str();
3973+
GDALDatasetH hDS = nullptr;
3974+
if (GDALGenerateVRTColorRelief(
3975+
pszDest, hSrcDataset, hSrcBand, pszColorFilename,
3976+
psOptions->eColorSelectionMode, psOptions->bAddAlpha))
3977+
{
3978+
if (bTmpFile)
3979+
{
3980+
const GByte *pabyData =
3981+
VSIGetMemFileBuffer(pszDest, nullptr, false);
3982+
hDS = GDALOpen(reinterpret_cast<const char *>(pabyData),
3983+
GA_Update);
3984+
}
3985+
else
3986+
{
3987+
hDS = GDALOpen(pszDest, GA_Update);
3988+
}
3989+
}
3990+
if (bTmpFile)
3991+
VSIUnlink(pszDest);
3992+
return hDS;
39733993
}
39743994
else
39753995
{

0 commit comments

Comments
 (0)