Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_mdim_reproject.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "reproject" step of "mdim pipeline"
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_mdim_reproject.h"
14
#include "gdalalg_raster_reproject.h"
15
16
#include "gdal_priv.h"
17
18
#include <map>
19
#include <set>
20
#include <utility>
21
22
//! @cond Doxygen_Suppress
23
24
#ifndef _
25
0
#define _(x) (x)
26
#endif
27
28
/************************************************************************/
29
/*       GDALMdimReprojectAlgorithm::GDALMdimReprojectAlgorithm()       */
30
/************************************************************************/
31
32
GDALMdimReprojectAlgorithm::GDALMdimReprojectAlgorithm(bool standaloneStep)
33
0
    : GDALMdimPipelineStepAlgorithm(
34
0
          NAME, DESCRIPTION, HELP_URL,
35
0
          ConstructorOptions().SetStandaloneStep(standaloneStep))
36
0
{
37
0
    AddArg(GDAL_ARG_NAME_OUTPUT_CRS, 'd', _("Output CRS"), &m_dstCrs)
38
0
        .SetIsCRSArg()
39
0
        .AddHiddenAlias("t_srs")
40
0
        .AddHiddenAlias("dst-crs");
41
42
0
    GDALRasterReprojectUtils::AddResamplingArg(this, m_resampling);
43
0
}
44
45
namespace
46
{
47
48
/************************************************************************/
49
/*                       GDALMdimReprojectParams                        */
50
/************************************************************************/
51
52
struct GDALMdimReprojectParams
53
{
54
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
55
    OGRSpatialReferenceRefCountedPtr poTargetSRS{};
56
};
57
58
/************************************************************************/
59
/*                        GDALMdimReprojectGroup                        */
60
/************************************************************************/
61
62
/** Wrapper around a source group object, that is essentially passthrough,
63
 * except with 2D+ arrays.
64
 */
65
class GDALMdimReprojectGroup final : public GDALGroup
66
{
67
    std::shared_ptr<GDALGroup> m_poSrcGroup{};
68
    const GDALMdimReprojectParams m_sParams;
69
    std::vector<std::string> m_aosArrayNames{};
70
    std::map<std::string, std::shared_ptr<GDALMDArray>> m_oMapArrays{};
71
    std::vector<std::shared_ptr<GDALDimension>> m_apoDims{};
72
73
  public:
74
    GDALMdimReprojectGroup(const std::string &osParentName,
75
                           const std::shared_ptr<GDALGroup> &poSrcGroup,
76
                           const GDALMdimReprojectParams &sParams);
77
78
    std::shared_ptr<GDALAttribute>
79
    GetAttribute(const std::string &osName) const override
80
0
    {
81
0
        return m_poSrcGroup->GetAttribute(osName);
82
0
    }
83
84
    std::vector<std::shared_ptr<GDALAttribute>>
85
    GetAttributes(CSLConstList papszOptions = nullptr) const override
86
0
    {
87
0
        return m_poSrcGroup->GetAttributes(papszOptions);
88
0
    }
89
90
    std::vector<std::string>
91
    GetGroupNames(CSLConstList papszOptions = nullptr) const override
92
0
    {
93
0
        return m_poSrcGroup->GetGroupNames(papszOptions);
94
0
    }
95
96
    std::shared_ptr<GDALGroup>
97
    OpenGroup(const std::string &osName,
98
              CSLConstList papszOptions = nullptr) const override
99
0
    {
100
0
        auto poSrcChildGroup = m_poSrcGroup->OpenGroup(osName, papszOptions);
101
0
        if (!poSrcChildGroup)
102
0
            return nullptr;
103
0
        return std::make_shared<GDALMdimReprojectGroup>(
104
0
            GetFullName(), std::move(poSrcChildGroup), m_sParams);
105
0
    }
106
107
    std::vector<std::string> GetMDArrayNames(CSLConstList) const override
108
0
    {
109
0
        return m_aosArrayNames;
110
0
    }
111
112
    std::shared_ptr<GDALMDArray> OpenMDArray(const std::string &osName,
113
                                             CSLConstList) const override
114
0
    {
115
0
        const auto oIter = m_oMapArrays.find(osName);
116
0
        if (oIter != m_oMapArrays.end())
117
0
            return oIter->second;
118
0
        return nullptr;
119
0
    }
120
121
    std::vector<std::shared_ptr<GDALDimension>>
122
        GetDimensions(CSLConstList) const override;
123
};
124
125
/************************************************************************/
126
/*           GDALMdimReprojectGroup::GDALMdimReprojectGroup()           */
127
/************************************************************************/
128
129
GDALMdimReprojectGroup::GDALMdimReprojectGroup(
130
    const std::string &osParentName,
131
    const std::shared_ptr<GDALGroup> &poSrcGroup,
132
    const GDALMdimReprojectParams &sParams)
133
0
    : GDALGroup(osParentName, poSrcGroup->GetName()), m_poSrcGroup(poSrcGroup),
134
0
      m_sParams(sParams)
135
0
{
136
    // We collect source arrays and dimensions, and remove dimensions that
137
    // are only referenced by the spatial dimensions of reprojected arrays.
138
139
    // First bool in pair: referenced by 1d variables (other than its own indexing variable)
140
    // Second bool in pair: referenced by >= 2d variables
141
0
    std::map<std::string, std::pair<bool, bool>> oMapArrayDims;
142
0
    std::map<std::string, std::shared_ptr<GDALDimension>> oMapNewDims;
143
0
    std::vector<std::shared_ptr<GDALMDArray>> apoIndexingVariables;
144
145
0
    for (const std::string &osName : m_poSrcGroup->GetMDArrayNames())
146
0
    {
147
0
        auto poSrcArray = m_poSrcGroup->OpenMDArray(osName);
148
0
        if (!poSrcArray)
149
0
            continue;
150
0
        if (poSrcArray->GetDimensionCount() == 0)
151
0
        {
152
0
            m_aosArrayNames.push_back(osName);
153
0
            m_oMapArrays[osName] = std::move(poSrcArray);
154
0
        }
155
0
        else if (poSrcArray->GetDimensionCount() == 1)
156
0
        {
157
0
            m_aosArrayNames.push_back(osName);
158
0
            const auto &poDim = poSrcArray->GetDimensions()[0];
159
0
            if (poDim->GetName() == osName)
160
0
            {
161
0
                apoIndexingVariables.push_back(std::move(poSrcArray));
162
0
            }
163
0
            else
164
0
            {
165
0
                oMapArrayDims[poDim->GetName()].first = true;
166
0
                m_oMapArrays[osName] = std::move(poSrcArray);
167
0
            }
168
0
        }
169
0
        else
170
0
        {
171
0
            std::vector<std::shared_ptr<GDALDimension>> apoNewDims(
172
0
                poSrcArray->GetDimensionCount());
173
0
            CPLStringList aosOptions;
174
0
            aosOptions.SetNameValue("PARENT_PATH", GetFullName().c_str());
175
0
            aosOptions.SetNameValue("NAME", osName.c_str());
176
0
            auto poDstArray = poSrcArray->GetResampled(
177
0
                apoNewDims, m_sParams.eResampleAlg, m_sParams.poTargetSRS.get(),
178
0
                aosOptions.List());
179
0
            if (poDstArray)
180
0
            {
181
0
                m_aosArrayNames.push_back(osName);
182
0
                CPLAssert(poDstArray->GetDimensionCount() ==
183
0
                          poSrcArray->GetDimensionCount());
184
0
                for (size_t i = 0; i < poDstArray->GetDimensionCount(); ++i)
185
0
                {
186
0
                    const auto &poSrcDim = poSrcArray->GetDimensions()[i];
187
0
                    const auto &poDstDim = poDstArray->GetDimensions()[i];
188
0
                    if (poSrcDim.get() != poDstDim.get())
189
0
                    {
190
0
                        oMapArrayDims[poSrcDim->GetName()].second = true;
191
0
                        oMapNewDims[poDstDim->GetName()] = poDstDim;
192
193
0
                        auto poVar = poDstDim->GetIndexingVariable();
194
0
                        if (poVar)
195
0
                        {
196
0
                            m_aosArrayNames.push_back(poVar->GetName());
197
0
                            m_oMapArrays[poVar->GetName()] = poVar;
198
0
                        }
199
0
                    }
200
0
                }
201
0
                m_oMapArrays[osName] = std::move(poDstArray);
202
0
            }
203
0
        }
204
0
    }
205
206
0
    for (auto &poArray : apoIndexingVariables)
207
0
    {
208
0
        auto oIter = oMapArrayDims.find(poArray->GetName());
209
0
        if (oIter == oMapArrayDims.end() || !oIter->second.second)
210
0
        {
211
0
            m_aosArrayNames.push_back(poArray->GetName());
212
0
            m_oMapArrays[poArray->GetName()] = poArray;
213
0
        }
214
0
    }
215
216
0
    for (const auto &poDim : m_poSrcGroup->GetDimensions())
217
0
    {
218
0
        auto oIter = oMapArrayDims.find(poDim->GetName());
219
0
        if (oIter == oMapArrayDims.end() || !oIter->second.second)
220
0
        {
221
0
            m_apoDims.push_back(poDim);
222
0
        }
223
0
    }
224
0
    for (const auto &[_, poDim] : oMapNewDims)
225
0
    {
226
0
        m_apoDims.push_back(poDim);
227
0
    }
228
0
}
229
230
/************************************************************************/
231
/*               GDALMdimReprojectGroup::GetDimensions()                */
232
/************************************************************************/
233
234
std::vector<std::shared_ptr<GDALDimension>>
235
GDALMdimReprojectGroup::GetDimensions(CSLConstList) const
236
0
{
237
0
    return m_apoDims;
238
0
}
239
240
/************************************************************************/
241
/*                       GDALMdimReprojectDataset                       */
242
/************************************************************************/
243
244
class GDALMdimReprojectDataset final : public GDALDataset
245
{
246
    std::shared_ptr<GDALGroup> m_poRootGroup{};
247
248
  public:
249
    GDALMdimReprojectDataset(GDALDataset *poSrcDS,
250
                             const GDALMdimReprojectParams &params)
251
0
    {
252
0
        auto poSrcRootGroup = poSrcDS->GetRootGroup();
253
0
        if (poSrcRootGroup)
254
0
        {
255
0
            m_poRootGroup = std::make_shared<GDALMdimReprojectGroup>(
256
0
                std::string(), std::move(poSrcRootGroup), params);
257
0
        }
258
0
    }
259
260
    std::shared_ptr<GDALGroup> GetRootGroup() const override;
261
};
262
263
std::shared_ptr<GDALGroup> GDALMdimReprojectDataset::GetRootGroup() const
264
0
{
265
0
    return m_poRootGroup;
266
0
}
267
268
}  // namespace
269
270
/************************************************************************/
271
/*                GDALMdimReprojectAlgorithm::RunStep()                 */
272
/************************************************************************/
273
274
bool GDALMdimReprojectAlgorithm::RunStep(GDALPipelineStepRunContext &)
275
0
{
276
0
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
277
0
    CPLAssert(poSrcDS);
278
0
    CPLAssert(m_outputDataset.GetName().empty());
279
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
280
281
0
    GDALMdimReprojectParams sParams;
282
0
    sParams.eResampleAlg = GDALRasterIOGetResampleAlg(m_resampling.c_str());
283
0
    if (!m_dstCrs.empty())
284
0
    {
285
0
        sParams.poTargetSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
286
0
        sParams.poTargetSRS->SetFromUserInput(m_dstCrs.c_str());
287
0
    }
288
0
    m_outputDataset.Set(
289
0
        std::make_unique<GDALMdimReprojectDataset>(poSrcDS, sParams));
290
291
0
    return true;
292
0
}
293
294
0
GDALMdimReprojectAlgorithmStandalone::~GDALMdimReprojectAlgorithmStandalone() =
295
    default;
296
297
//! @endcond