Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Class to abstract outputting to a vector layer
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_vector_output_abstract.h"
14 :
15 : #include "cpl_vsi.h"
16 : #include "ogrsf_frmts.h"
17 :
18 : //! @cond Doxygen_Suppress
19 :
20 : #ifndef _
21 : #define _(x) (x)
22 : #endif
23 :
24 : /************************************************************************/
25 : /* GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs() */
26 : /************************************************************************/
27 :
28 35 : void GDALVectorOutputAbstractAlgorithm::AddAllOutputArgs()
29 : {
30 35 : AddOutputFormatArg(&m_outputFormat)
31 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
32 105 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE});
33 35 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR);
34 35 : m_outputDataset.SetInputFlags(GADV_NAME | GADV_OBJECT);
35 35 : AddCreationOptionsArg(&m_creationOptions);
36 35 : AddLayerCreationOptionsArg(&m_layerCreationOptions);
37 35 : AddOverwriteArg(&m_overwrite).SetMutualExclusionGroup("overwrite-update");
38 35 : AddUpdateArg(&m_update).SetMutualExclusionGroup("overwrite-update");
39 : AddArg("overwrite-layer", 0,
40 : _("Whether overwriting existing layer is allowed"),
41 70 : &m_overwriteLayer)
42 35 : .SetDefault(false)
43 : .AddValidationAction(
44 1 : [this]
45 : {
46 1 : GetArg(GDAL_ARG_NAME_UPDATE)->Set(true);
47 1 : return true;
48 35 : });
49 : AddArg("append", 0, _("Whether appending to existing layer is allowed"),
50 70 : &m_appendLayer)
51 35 : .SetDefault(false)
52 : .AddValidationAction(
53 2 : [this]
54 : {
55 2 : GetArg(GDAL_ARG_NAME_UPDATE)->Set(true);
56 2 : return true;
57 35 : });
58 : {
59 35 : auto &arg = AddLayerNameArg(&m_outputLayerName)
60 70 : .AddAlias("nln")
61 35 : .SetMinCharCount(0);
62 35 : if (!m_outputLayerName.empty())
63 0 : arg.SetDefault(m_outputLayerName);
64 : }
65 35 : }
66 :
67 : /************************************************************************/
68 : /* GDALVectorOutputAbstractAlgorithm::SetupOutputDataset() */
69 : /************************************************************************/
70 :
71 : GDALVectorOutputAbstractAlgorithm::SetupOutputDatasetRet
72 18 : GDALVectorOutputAbstractAlgorithm::SetupOutputDataset()
73 : {
74 18 : SetupOutputDatasetRet ret;
75 :
76 : VSIStatBufL sStat;
77 21 : if (!m_update && !m_outputDataset.GetName().empty() &&
78 4 : (VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
79 19 : std::unique_ptr<GDALDataset>(
80 2 : GDALDataset::Open(m_outputDataset.GetName().c_str()))))
81 : {
82 2 : if (!m_overwrite)
83 : {
84 1 : ReportError(CE_Failure, CPLE_AppDefined,
85 : "File '%s' already exists. Specify the --overwrite "
86 : "option to overwrite it, or --update to update it.",
87 1 : m_outputDataset.GetName().c_str());
88 1 : return ret;
89 : }
90 : else
91 : {
92 1 : VSIUnlink(m_outputDataset.GetName().c_str());
93 : }
94 : }
95 :
96 17 : GDALDataset *poDstDS = m_outputDataset.GetDatasetRef();
97 17 : std::unique_ptr<GDALDataset> poRetDS;
98 17 : if (!poDstDS)
99 : {
100 13 : if (m_outputFormat.empty())
101 : {
102 : const auto aosFormats =
103 : CPLStringList(GDALGetOutputDriversForDatasetName(
104 2 : m_outputDataset.GetName().c_str(), GDAL_OF_VECTOR,
105 : /* bSingleMatch = */ true,
106 2 : /* bWarn = */ true));
107 2 : if (aosFormats.size() != 1)
108 : {
109 0 : ReportError(CE_Failure, CPLE_AppDefined,
110 : "Cannot guess driver for %s",
111 0 : m_outputDataset.GetName().c_str());
112 0 : return ret;
113 : }
114 2 : m_outputFormat = aosFormats[0];
115 : }
116 :
117 : auto poDriver =
118 13 : GetGDALDriverManager()->GetDriverByName(m_outputFormat.c_str());
119 13 : if (!poDriver)
120 : {
121 : // shouldn't happen given checks done in GDALAlgorithm
122 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
123 : m_outputFormat.c_str());
124 0 : return ret;
125 : }
126 :
127 13 : poRetDS.reset(poDriver->Create(
128 13 : m_outputDataset.GetName().c_str(), 0, 0, 0, GDT_Unknown,
129 26 : CPLStringList(m_creationOptions).List()));
130 13 : if (!poRetDS)
131 0 : return ret;
132 :
133 13 : poDstDS = poRetDS.get();
134 : }
135 :
136 17 : auto poDstDriver = poDstDS->GetDriver();
137 17 : if (poDstDriver && EQUAL(poDstDriver->GetDescription(), "ESRI Shapefile") &&
138 40 : EQUAL(CPLGetExtensionSafe(poDstDS->GetDescription()).c_str(), "shp") &&
139 6 : poDstDS->GetLayerCount() <= 1)
140 : {
141 6 : m_outputLayerName = CPLGetBasenameSafe(poDstDS->GetDescription());
142 : }
143 :
144 17 : auto poDstLayer = m_outputLayerName.empty()
145 17 : ? nullptr
146 16 : : poDstDS->GetLayerByName(m_outputLayerName.c_str());
147 17 : if (poDstLayer)
148 : {
149 4 : if (m_overwriteLayer)
150 : {
151 1 : int iLayer = -1;
152 1 : const int nLayerCount = poDstDS->GetLayerCount();
153 1 : for (iLayer = 0; iLayer < nLayerCount; iLayer++)
154 : {
155 1 : if (poDstDS->GetLayer(iLayer) == poDstLayer)
156 1 : break;
157 : }
158 :
159 1 : if (iLayer < nLayerCount)
160 : {
161 1 : if (poDstDS->DeleteLayer(iLayer) != OGRERR_NONE)
162 : {
163 0 : ReportError(CE_Failure, CPLE_AppDefined,
164 : "Cannot delete layer '%s'",
165 : m_outputLayerName.c_str());
166 0 : return ret;
167 : }
168 : }
169 1 : poDstLayer = nullptr;
170 : }
171 3 : else if (!m_appendLayer)
172 : {
173 1 : ReportError(CE_Failure, CPLE_AppDefined,
174 : "Layer '%s' already exists. Specify the "
175 : "--overwrite-layer option to overwrite it, or --append "
176 : "to append it.",
177 : m_outputLayerName.c_str());
178 1 : return ret;
179 : }
180 : }
181 13 : else if (m_appendLayer || m_overwriteLayer)
182 : {
183 0 : ReportError(CE_Failure, CPLE_AppDefined, "Cannot find layer '%s'",
184 : m_outputLayerName.c_str());
185 0 : return ret;
186 : }
187 :
188 16 : ret.newDS = std::move(poRetDS);
189 16 : ret.outDS = poDstDS;
190 16 : ret.layer = poDstLayer;
191 16 : return ret;
192 : }
193 :
194 : /************************************************************************/
195 : /* GDALVectorOutputAbstractAlgorithm::SetDefaultOutputLayerNameIfNeeded */
196 : /************************************************************************/
197 :
198 16 : bool GDALVectorOutputAbstractAlgorithm::SetDefaultOutputLayerNameIfNeeded(
199 : GDALDataset *poOutDS)
200 : {
201 16 : if (m_outputLayerName.empty())
202 : {
203 : VSIStatBufL sStat;
204 1 : auto poDriver = poOutDS->GetDriver();
205 2 : if (VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
206 1 : (poDriver && EQUAL(poDriver->GetDescription(), "ESRI Shapefile")))
207 : {
208 : m_outputLayerName =
209 0 : CPLGetBasenameSafe(m_outputDataset.GetName().c_str());
210 : }
211 : }
212 16 : if (m_outputLayerName.empty())
213 : {
214 1 : ReportError(CE_Failure, CPLE_AppDefined,
215 : "Argument 'layer' must be specified");
216 1 : return false;
217 : }
218 15 : return true;
219 : }
220 :
221 : //! @endcond
|