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