Coverage Report

Created: 2025-06-09 08:44

/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