/src/gdal/apps/gdalalg_vector_write.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL |
4 | | * Purpose: "write" step of "vector pipeline" |
5 | | * Author: Even Rouault <even dot rouault at spatialys.com> |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "gdalalg_vector_write.h" |
14 | | #include "cpl_string.h" |
15 | | #include "gdal_utils.h" |
16 | | #include "gdal_priv.h" |
17 | | |
18 | | #ifndef _ |
19 | 0 | #define _(x) (x) |
20 | | #endif |
21 | | |
22 | | //! @cond Doxygen_Suppress |
23 | | |
24 | | /************************************************************************/ |
25 | | /* GDALVectorWriteAlgorithm::GDALVectorWriteAlgorithm() */ |
26 | | /************************************************************************/ |
27 | | |
28 | | GDALVectorWriteAlgorithm::GDALVectorWriteAlgorithm() |
29 | 0 | : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL, |
30 | 0 | ConstructorOptions() |
31 | 0 | .SetStandaloneStep(false) |
32 | 0 | .SetNoCreateEmptyLayersArgument(true)) |
33 | 0 | { |
34 | 0 | AddVectorOutputArgs(/* hiddenForCLI = */ false, |
35 | 0 | /* shortNameOutputLayerAllowed=*/true); |
36 | |
|
37 | 0 | AddArg(GDAL_ARG_NAME_QUIET, 'q', |
38 | 0 | _("Quiet mode (suppress warning messages)"), &m_quiet) |
39 | 0 | .SetCategory(GAAC_COMMON); |
40 | 0 | } |
41 | | |
42 | | /************************************************************************/ |
43 | | /* GDALVectorWriteAlgorithm::RunStep() */ |
44 | | /************************************************************************/ |
45 | | |
46 | | namespace |
47 | | { |
48 | | class OGRReadBufferedLayer |
49 | | : public OGRLayer, |
50 | | public OGRGetNextFeatureThroughRaw<OGRReadBufferedLayer> |
51 | | { |
52 | | public: |
53 | | explicit OGRReadBufferedLayer(OGRLayer &srcLayer) |
54 | 0 | : m_srcLayer(srcLayer), m_poFeature(nullptr) |
55 | 0 | { |
56 | 0 | m_poFeature.reset(m_srcLayer.GetNextFeature()); |
57 | 0 | } |
58 | | |
59 | | ~OGRReadBufferedLayer() override; |
60 | | |
61 | | const char *GetDescription() const override |
62 | 0 | { |
63 | 0 | return m_srcLayer.GetDescription(); |
64 | 0 | } |
65 | | |
66 | | GIntBig GetFeatureCount(int bForce) override |
67 | 0 | { |
68 | 0 | if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr) |
69 | 0 | { |
70 | 0 | return m_srcLayer.GetFeatureCount(bForce); |
71 | 0 | } |
72 | | |
73 | 0 | return OGRLayer::GetFeatureCount(bForce); |
74 | 0 | } |
75 | | |
76 | | const OGRFeatureDefn *GetLayerDefn() const override |
77 | 0 | { |
78 | 0 | return m_srcLayer.GetLayerDefn(); |
79 | 0 | } |
80 | | |
81 | | OGRFeature *GetNextRawFeature() |
82 | 0 | { |
83 | 0 | auto ret = m_poFeature.release(); |
84 | 0 | m_poFeature.reset(m_srcLayer.GetNextFeature()); |
85 | 0 | return ret; |
86 | 0 | } |
87 | | |
88 | | DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRReadBufferedLayer) |
89 | | |
90 | | OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent, |
91 | | bool bForce) override |
92 | 0 | { |
93 | 0 | return m_srcLayer.GetExtent(iGeomField, psExtent, bForce); |
94 | 0 | } |
95 | | |
96 | | OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent, |
97 | | bool bForce) override |
98 | 0 | { |
99 | 0 | return m_srcLayer.GetExtent3D(iGeomField, psExtent, bForce); |
100 | 0 | } |
101 | | |
102 | | const OGRFeature *PeekNextFeature() const |
103 | 0 | { |
104 | 0 | return m_poFeature.get(); |
105 | 0 | } |
106 | | |
107 | | int TestCapability(const char *pszCap) const override |
108 | 0 | { |
109 | 0 | if (EQUAL(pszCap, OLCFastFeatureCount) || |
110 | 0 | EQUAL(pszCap, OLCFastGetExtent) || |
111 | 0 | EQUAL(pszCap, OLCFastGetExtent3D) || |
112 | 0 | EQUAL(pszCap, OLCZGeometries) || |
113 | 0 | EQUAL(pszCap, OLCMeasuredGeometries) || |
114 | 0 | EQUAL(pszCap, OLCCurveGeometries)) |
115 | 0 | { |
116 | 0 | return m_srcLayer.TestCapability(pszCap); |
117 | 0 | } |
118 | | |
119 | 0 | return false; |
120 | 0 | } |
121 | | |
122 | | void ResetReading() override |
123 | 0 | { |
124 | 0 | m_srcLayer.ResetReading(); |
125 | 0 | m_poFeature.reset(m_srcLayer.GetNextFeature()); |
126 | 0 | } |
127 | | |
128 | | private: |
129 | | OGRLayer &m_srcLayer; |
130 | | std::unique_ptr<OGRFeature> m_poFeature; |
131 | | }; |
132 | | |
133 | 0 | OGRReadBufferedLayer::~OGRReadBufferedLayer() = default; |
134 | | |
135 | | class GDALReadBufferedDataset final : public GDALDataset |
136 | | { |
137 | | public: |
138 | 0 | explicit GDALReadBufferedDataset(GDALDataset &srcDS) : m_srcDS(srcDS) |
139 | 0 | { |
140 | 0 | m_srcDS.Reference(); |
141 | |
|
142 | 0 | for (int i = 0; i < srcDS.GetLayerCount(); i++) |
143 | 0 | { |
144 | 0 | auto poLayer = |
145 | 0 | std::make_unique<OGRReadBufferedLayer>(*srcDS.GetLayer(i)); |
146 | 0 | if (poLayer->PeekNextFeature()) |
147 | 0 | { |
148 | 0 | m_layers.push_back(std::move(poLayer)); |
149 | 0 | } |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | ~GDALReadBufferedDataset() override; |
154 | | |
155 | | int GetLayerCount() const override |
156 | 0 | { |
157 | 0 | return static_cast<int>(m_layers.size()); |
158 | 0 | } |
159 | | |
160 | | const OGRLayer *GetLayer(int nLayer) const override |
161 | 0 | { |
162 | 0 | if (nLayer < 0 || nLayer >= static_cast<int>(m_layers.size())) |
163 | 0 | { |
164 | 0 | return nullptr; |
165 | 0 | } |
166 | 0 | return m_layers[nLayer].get(); |
167 | 0 | } |
168 | | |
169 | | private: |
170 | | GDALDataset &m_srcDS; |
171 | | std::vector<std::unique_ptr<OGRReadBufferedLayer>> m_layers{}; |
172 | | }; |
173 | | |
174 | | GDALReadBufferedDataset::~GDALReadBufferedDataset() |
175 | 0 | { |
176 | 0 | m_srcDS.Release(); |
177 | 0 | } |
178 | | |
179 | | } // namespace |
180 | | |
181 | | bool GDALVectorWriteAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt) |
182 | 0 | { |
183 | 0 | auto pfnProgress = ctxt.m_pfnProgress; |
184 | 0 | auto pProgressData = ctxt.m_pProgressData; |
185 | 0 | auto poSrcDS = m_inputDataset[0].GetDatasetRef(); |
186 | 0 | CPLAssert(poSrcDS); |
187 | | |
188 | 0 | std::unique_ptr<GDALDataset> poReadBufferedDataset; |
189 | |
|
190 | 0 | if (m_noCreateEmptyLayers) |
191 | 0 | { |
192 | 0 | if (poSrcDS->TestCapability(ODsCRandomLayerRead)) |
193 | 0 | { |
194 | 0 | CPLError( |
195 | 0 | CE_Warning, CPLE_AppDefined, |
196 | 0 | "Source dataset supports random-layer reading, but this " |
197 | 0 | "is not compatible with --no-create-empty-layers. Attempting " |
198 | 0 | "to read features by layer, but this may fail if the " |
199 | 0 | "source dataset is large."); |
200 | 0 | } |
201 | |
|
202 | 0 | poReadBufferedDataset = |
203 | 0 | std::make_unique<GDALReadBufferedDataset>(*poSrcDS); |
204 | |
|
205 | 0 | if (m_format == "stream") |
206 | 0 | { |
207 | 0 | m_outputDataset.Set(std::move(poReadBufferedDataset)); |
208 | 0 | return true; |
209 | 0 | } |
210 | | |
211 | 0 | poSrcDS = poReadBufferedDataset.get(); |
212 | 0 | } |
213 | | |
214 | 0 | if (m_format == "stream") |
215 | 0 | { |
216 | 0 | m_outputDataset.Set(poSrcDS); |
217 | 0 | return true; |
218 | 0 | } |
219 | | |
220 | 0 | CPLStringList aosOptions; |
221 | 0 | aosOptions.AddString("--invoked-from-gdal-algorithm"); |
222 | 0 | if (!m_overwrite) |
223 | 0 | { |
224 | 0 | aosOptions.AddString("--no-overwrite"); |
225 | 0 | } |
226 | 0 | if (m_overwriteLayer) |
227 | 0 | { |
228 | 0 | aosOptions.AddString("-overwrite"); |
229 | 0 | } |
230 | 0 | if (m_appendLayer) |
231 | 0 | { |
232 | 0 | aosOptions.AddString("-append"); |
233 | 0 | } |
234 | 0 | if (m_upsert) |
235 | 0 | { |
236 | 0 | aosOptions.AddString("-upsert"); |
237 | 0 | } |
238 | 0 | if (!m_format.empty()) |
239 | 0 | { |
240 | 0 | aosOptions.AddString("-of"); |
241 | 0 | aosOptions.AddString(m_format.c_str()); |
242 | 0 | } |
243 | 0 | for (const auto &co : m_creationOptions) |
244 | 0 | { |
245 | 0 | aosOptions.AddString("-dsco"); |
246 | 0 | aosOptions.AddString(co.c_str()); |
247 | 0 | } |
248 | 0 | for (const auto &co : m_layerCreationOptions) |
249 | 0 | { |
250 | 0 | aosOptions.AddString("-lco"); |
251 | 0 | aosOptions.AddString(co.c_str()); |
252 | 0 | } |
253 | 0 | if (!m_outputLayerName.empty()) |
254 | 0 | { |
255 | 0 | aosOptions.AddString("-nln"); |
256 | 0 | aosOptions.AddString(m_outputLayerName.c_str()); |
257 | 0 | } |
258 | 0 | if (pfnProgress && pfnProgress != GDALDummyProgress) |
259 | 0 | { |
260 | 0 | aosOptions.AddString("-progress"); |
261 | 0 | } |
262 | 0 | if (m_skipErrors) |
263 | 0 | { |
264 | 0 | aosOptions.AddString("-skipfailures"); |
265 | 0 | } |
266 | 0 | if (m_quiet) |
267 | 0 | { |
268 | 0 | aosOptions.AddString("-q"); |
269 | 0 | } |
270 | |
|
271 | 0 | GDALDataset *poRetDS = nullptr; |
272 | 0 | GDALDatasetH hOutDS = |
273 | 0 | GDALDataset::ToHandle(m_outputDataset.GetDatasetRef()); |
274 | 0 | GDALVectorTranslateOptions *psOptions = |
275 | 0 | GDALVectorTranslateOptionsNew(aosOptions.List(), nullptr); |
276 | 0 | if (psOptions) |
277 | 0 | { |
278 | 0 | GDALVectorTranslateOptionsSetProgress(psOptions, pfnProgress, |
279 | 0 | pProgressData); |
280 | |
|
281 | 0 | GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS); |
282 | 0 | poRetDS = GDALDataset::FromHandle( |
283 | 0 | GDALVectorTranslate(m_outputDataset.GetName().c_str(), hOutDS, 1, |
284 | 0 | &hSrcDS, psOptions, nullptr)); |
285 | 0 | GDALVectorTranslateOptionsFree(psOptions); |
286 | 0 | } |
287 | |
|
288 | 0 | if (!poRetDS) |
289 | 0 | { |
290 | 0 | return false; |
291 | 0 | } |
292 | | |
293 | 0 | if (!hOutDS) |
294 | 0 | { |
295 | 0 | m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS)); |
296 | 0 | } |
297 | |
|
298 | 0 | return true; |
299 | 0 | } |
300 | | |
301 | | //! @endcond |