/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 ¶ms) |
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 |