Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "raster update" subcommand
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_update.h"
14 :
15 : #include "cpl_conv.h"
16 :
17 : #include "gdal_priv.h"
18 : #include "gdal_utils.h"
19 : #include "gdalalg_raster_reproject.h" // for GDALRasterReprojectUtils
20 : #include "gdalalg_raster_overview_refresh.h"
21 : #include "ogr_spatialref.h"
22 :
23 : #include <algorithm>
24 : #include <array>
25 : #include <cmath>
26 : #include <tuple>
27 :
28 : //! @cond Doxygen_Suppress
29 :
30 : #ifndef _
31 : #define _(x) (x)
32 : #endif
33 :
34 : /************************************************************************/
35 : /* GDALRasterUpdateAlgorithm::GDALRasterUpdateAlgorithm() */
36 : /************************************************************************/
37 :
38 12 : GDALRasterUpdateAlgorithm::GDALRasterUpdateAlgorithm()
39 12 : : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
40 : {
41 12 : AddProgressArg();
42 :
43 12 : AddOpenOptionsArg(&m_openOptions);
44 12 : AddInputFormatsArg(&m_inputFormats)
45 24 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
46 12 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER);
47 :
48 12 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER)
49 12 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
50 :
51 12 : AddUpdateArg(&m_update).SetDefault(true).SetHidden();
52 :
53 24 : AddArg("geometry", 0, _("Clipping geometry (WKT or GeoJSON)"), &m_geometry)
54 12 : .SetMutualExclusionGroup("bbox-geometry-like");
55 24 : AddArg("geometry-crs", 0, _("CRS of clipping geometry"), &m_geometryCrs)
56 24 : .SetIsCRSArg()
57 12 : .AddHiddenAlias("geometry_srs");
58 :
59 12 : GDALRasterReprojectUtils::AddResamplingArg(this, m_resampling);
60 :
61 12 : GDALRasterReprojectUtils::AddWarpOptTransformOptErrorThresholdArg(
62 12 : this, m_warpOptions, m_transformOptions, m_errorThreshold);
63 :
64 : AddArg("no-update-overviews", 0, _("Do not update existing overviews"),
65 12 : &m_noUpdateOverviews);
66 12 : }
67 :
68 : /************************************************************************/
69 : /* GDALRasterUpdateAlgorithm::RunImpl() */
70 : /************************************************************************/
71 :
72 8 : bool GDALRasterUpdateAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
73 : void *pProgressData)
74 : {
75 8 : auto poSrcDS = m_inputDataset.GetDatasetRef();
76 8 : CPLAssert(poSrcDS);
77 :
78 8 : auto poDstDS = m_outputDataset.GetDatasetRef();
79 8 : CPLAssert(poDstDS);
80 8 : CPLAssert(poDstDS->GetAccess() == GA_Update);
81 :
82 8 : std::unique_ptr<OGRGeometry> poClipGeom;
83 16 : std::string errMsg;
84 8 : if (!m_geometry.empty())
85 : {
86 2 : std::tie(poClipGeom, errMsg) = GetClipGeometry();
87 2 : if (!poClipGeom)
88 : {
89 1 : ReportError(CE_Failure, CPLE_AppDefined, "%s", errMsg.c_str());
90 1 : return false;
91 : }
92 : }
93 :
94 7 : auto poSrcDriver = poSrcDS->GetDriver();
95 7 : auto poDstDriver = poDstDS->GetDriver();
96 13 : if (poSrcDS == poDstDS ||
97 6 : (poSrcDriver && poDstDriver &&
98 6 : !EQUAL(poSrcDriver->GetDescription(), "MEM") &&
99 2 : !EQUAL(poDstDriver->GetDescription(), "MEM") &&
100 2 : strcmp(poSrcDS->GetDescription(), poDstDS->GetDescription()) == 0))
101 : {
102 2 : ReportError(CE_Failure, CPLE_NotSupported,
103 : "Source and destination datasets must be different");
104 2 : return false;
105 : }
106 :
107 10 : CPLStringList aosOptions;
108 5 : if (!m_resampling.empty())
109 : {
110 5 : aosOptions.AddString("-r");
111 5 : aosOptions.AddString(m_resampling.c_str());
112 : }
113 6 : for (const std::string &opt : m_warpOptions)
114 : {
115 1 : aosOptions.AddString("-wo");
116 1 : aosOptions.AddString(opt.c_str());
117 : }
118 6 : for (const std::string &opt : m_transformOptions)
119 : {
120 1 : aosOptions.AddString("-to");
121 1 : aosOptions.AddString(opt.c_str());
122 : }
123 5 : if (std::isfinite(m_errorThreshold))
124 : {
125 1 : aosOptions.AddString("-et");
126 1 : aosOptions.AddString(CPLSPrintf("%.17g", m_errorThreshold));
127 : }
128 :
129 5 : if (poClipGeom)
130 : {
131 1 : aosOptions.AddString("-cutline");
132 1 : aosOptions.AddString(poClipGeom->exportToWkt());
133 : }
134 :
135 5 : bool bOvrCanBeUpdated = false;
136 5 : std::vector<double> overviewRefreshBBox;
137 7 : if (poDstDS->GetRasterBand(1)->GetOverviewCount() > 0 &&
138 2 : !m_noUpdateOverviews)
139 : {
140 : std::array<double, 6> adfGT;
141 2 : const auto poSrcCRS = poSrcDS->GetSpatialRef();
142 2 : const auto poDstCRS = poDstDS->GetSpatialRef();
143 2 : const bool bBothCRS = poSrcCRS && poDstCRS;
144 2 : const bool bBothNoCRS = !poSrcCRS && !poDstCRS;
145 4 : if ((bBothCRS || bBothNoCRS) &&
146 2 : poSrcDS->GetGeoTransform(adfGT.data()) == CE_None)
147 : {
148 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
149 1 : bBothCRS ? OGRCreateCoordinateTransformation(poSrcCRS, poDstCRS)
150 5 : : nullptr);
151 2 : if (bBothNoCRS || poCT)
152 : {
153 2 : const double dfTLX = adfGT[0];
154 2 : const double dfTLY = adfGT[3];
155 :
156 2 : double dfTRX = 0;
157 2 : double dfTRY = 0;
158 2 : GDALApplyGeoTransform(adfGT.data(), poSrcDS->GetRasterXSize(),
159 : 0, &dfTRX, &dfTRY);
160 :
161 2 : double dfBLX = 0;
162 2 : double dfBLY = 0;
163 2 : GDALApplyGeoTransform(
164 2 : adfGT.data(), 0, poSrcDS->GetRasterYSize(), &dfBLX, &dfBLY);
165 :
166 2 : double dfBRX = 0;
167 2 : double dfBRY = 0;
168 2 : GDALApplyGeoTransform(adfGT.data(), poSrcDS->GetRasterXSize(),
169 2 : poSrcDS->GetRasterYSize(), &dfBRX,
170 : &dfBRY);
171 :
172 : const double dfXMin =
173 2 : std::min(std::min(dfTLX, dfTRX), std::min(dfBLX, dfBRX));
174 : const double dfYMin =
175 2 : std::min(std::min(dfTLY, dfTRY), std::min(dfBLY, dfBRY));
176 : const double dfXMax =
177 2 : std::max(std::max(dfTLX, dfTRX), std::max(dfBLX, dfBRX));
178 : const double dfYMax =
179 2 : std::max(std::max(dfTLY, dfTRY), std::max(dfBLY, dfBRY));
180 2 : double dfOutXMin = dfXMin;
181 2 : double dfOutYMin = dfYMin;
182 2 : double dfOutXMax = dfXMax;
183 2 : double dfOutYMax = dfYMax;
184 3 : if (!poCT || poCT->TransformBounds(
185 : dfXMin, dfYMin, dfXMax, dfYMax, &dfOutXMin,
186 1 : &dfOutYMin, &dfOutXMax, &dfOutYMax, 21))
187 : {
188 2 : bOvrCanBeUpdated = true;
189 2 : CPLDebug("update",
190 : "Refresh overviews from (%f,%f) to (%f,%f)",
191 : dfOutXMin, dfOutYMin, dfOutXMax, dfOutYMax);
192 4 : overviewRefreshBBox = std::vector<double>{
193 2 : dfOutXMin, dfOutYMin, dfOutXMax, dfOutYMax};
194 : }
195 : }
196 : }
197 2 : if (!bOvrCanBeUpdated)
198 : {
199 0 : ReportError(CE_Warning, CPLE_AppDefined,
200 : "Overviews can not be updated");
201 : }
202 : }
203 :
204 5 : bool bOK = false;
205 : GDALWarpAppOptions *psOptions =
206 5 : GDALWarpAppOptionsNew(aosOptions.List(), nullptr);
207 5 : if (psOptions)
208 : {
209 : std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData(
210 10 : nullptr, GDALDestroyScaledProgress);
211 5 : if (pfnProgress)
212 : {
213 3 : pScaledData.reset(
214 : GDALCreateScaledProgress(0.0, bOvrCanBeUpdated ? 0.75 : 1.0,
215 : pfnProgress, pProgressData));
216 3 : GDALWarpAppOptionsSetProgress(psOptions, GDALScaledProgress,
217 : pScaledData.get());
218 : }
219 :
220 5 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
221 5 : GDALDatasetH hDstDS = GDALDataset::ToHandle(poDstDS);
222 5 : auto poRetDS = GDALDataset::FromHandle(
223 : GDALWarp(nullptr, hDstDS, 1, &hSrcDS, psOptions, nullptr));
224 5 : GDALWarpAppOptionsFree(psOptions);
225 :
226 5 : bOK = poRetDS != nullptr;
227 5 : if (bOK && bOvrCanBeUpdated)
228 : {
229 2 : GDALRasterOverviewAlgorithmRefresh refresh;
230 2 : refresh.GetArg("dataset")->Set(poRetDS);
231 2 : if (!m_resampling.empty())
232 2 : refresh.GetArg("resampling")->Set(m_resampling);
233 2 : refresh.GetArg("bbox")->Set(overviewRefreshBBox);
234 2 : pScaledData.reset(GDALCreateScaledProgress(0.75, 1.0, pfnProgress,
235 : pProgressData));
236 2 : bOK = refresh.Run(pScaledData ? GDALScaledProgress : nullptr,
237 : pScaledData.get());
238 : }
239 5 : if (bOK && pfnProgress)
240 3 : pfnProgress(1.0, "", pProgressData);
241 : }
242 :
243 5 : return bOK;
244 : }
245 :
246 : //! @endcond
|