Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_make_point.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "gdal vector make-point"
5
 * Author:   Dan Baston
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2025, ISciences LLC
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "gdalalg_vector_make_point.h"
14
15
#include "gdal_priv.h"
16
#include "ogrsf_frmts.h"
17
18
//! @cond Doxygen_Suppress
19
20
#ifndef _
21
0
#define _(x) (x)
22
#endif
23
24
/************************************************************************/
25
/*                    GDALVectorMakePointAlgorithm()                    */
26
/************************************************************************/
27
28
GDALVectorMakePointAlgorithm::GDALVectorMakePointAlgorithm(bool standaloneStep)
29
0
    : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30
0
                                      standaloneStep)
31
0
{
32
0
    AddArg("x", 0, _("Field from which X coordinate should be read"), &m_xField)
33
0
        .SetRequired();
34
0
    AddArg("y", 0, _("Field from which Y coordinate should be read"), &m_yField)
35
0
        .SetRequired();
36
0
    AddArg("z", 0, _("Optional field from which Z coordinate should be read"),
37
0
           &m_zField);
38
0
    AddArg("m", 0, _("Optional field from which M coordinate should be read"),
39
0
           &m_mField);
40
0
    AddArg(GDAL_ARG_NAME_OUTPUT_CRS, 0, _("Output CRS"), &m_dstCrs)
41
0
        .AddHiddenAlias("dst-crs")
42
0
        .SetIsCRSArg();
43
0
}
44
45
namespace
46
{
47
48
/************************************************************************/
49
/*                  GDALVectorMakePointAlgorithmLayer                   */
50
/************************************************************************/
51
52
class GDALVectorMakePointAlgorithmLayer final
53
    : public GDALVectorPipelineOutputLayer
54
{
55
  public:
56
    GDALVectorMakePointAlgorithmLayer(OGRLayer &oSrcLayer,
57
                                      const std::string &xField,
58
                                      const std::string &yField,
59
                                      const std::string &zField,
60
                                      const std::string &mField,
61
                                      OGRSpatialReferenceRefCountedPtr srs)
62
0
        : GDALVectorPipelineOutputLayer(oSrcLayer), m_xField(xField),
63
0
          m_yField(yField), m_zField(zField), m_mField(mField),
64
          m_xFieldIndex(
65
0
              oSrcLayer.GetLayerDefn()->GetFieldIndex(xField.c_str())),
66
          m_yFieldIndex(
67
0
              oSrcLayer.GetLayerDefn()->GetFieldIndex(yField.c_str())),
68
          m_zFieldIndex(
69
0
              zField.empty()
70
0
                  ? -1
71
0
                  : oSrcLayer.GetLayerDefn()->GetFieldIndex(zField.c_str())),
72
          m_mFieldIndex(
73
0
              mField.empty()
74
0
                  ? -1
75
0
                  : oSrcLayer.GetLayerDefn()->GetFieldIndex(mField.c_str())),
76
0
          m_hasZ(!zField.empty()), m_hasM(!mField.empty()),
77
0
          m_srs(std::move(srs)), m_defn(oSrcLayer.GetLayerDefn()->Clone())
78
0
    {
79
0
        if (!CheckField("X", m_xField, m_xFieldIndex, m_xFieldIsString))
80
0
            return;
81
0
        if (!CheckField("Y", m_yField, m_yFieldIndex, m_yFieldIsString))
82
0
            return;
83
0
        if (m_hasZ &&
84
0
            !CheckField("Z", m_zField, m_zFieldIndex, m_zFieldIsString))
85
0
            return;
86
0
        if (m_hasM &&
87
0
            !CheckField("M", m_mField, m_mFieldIndex, m_mFieldIsString))
88
0
            return;
89
90
0
        OGRwkbGeometryType eGeomType = wkbPoint;
91
0
        if (m_hasZ)
92
0
            eGeomType = OGR_GT_SetZ(eGeomType);
93
0
        if (m_hasM)
94
0
            eGeomType = OGR_GT_SetM(eGeomType);
95
96
0
        auto poGeomFieldDefn =
97
0
            std::make_unique<OGRGeomFieldDefn>("geometry", eGeomType);
98
0
        if (m_srs)
99
0
        {
100
0
            poGeomFieldDefn->SetSpatialRef(m_srs.get());
101
0
        }
102
103
0
        while (m_defn->GetGeomFieldCount() > 0)
104
0
            m_defn->DeleteGeomFieldDefn(0);
105
0
        m_defn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
106
0
    }
107
108
    double GetField(const OGRFeature &feature, int fieldIndex, bool isString)
109
0
    {
110
0
        if (isString)
111
0
        {
112
0
            const char *pszValue = feature.GetFieldAsString(fieldIndex);
113
0
            while (std::isspace(static_cast<unsigned char>(*pszValue)))
114
0
            {
115
0
                pszValue++;
116
0
            }
117
0
            char *end = nullptr;
118
0
            double dfValue = CPLStrtodM(pszValue, &end);
119
0
            while (std::isspace(static_cast<unsigned char>(*end)))
120
0
            {
121
0
                end++;
122
0
            }
123
0
            if (end == pszValue || *end != '\0')
124
0
            {
125
0
                const char *pszFieldName =
126
0
                    m_defn->GetFieldDefn(fieldIndex)->GetNameRef();
127
0
                CPLError(CE_Failure, CPLE_AppDefined,
128
0
                         "Invalid value in field %s: %s ", pszFieldName,
129
0
                         pszValue);
130
0
                m_fatalError = true;
131
0
            }
132
0
            return dfValue;
133
0
        }
134
0
        else
135
0
        {
136
0
            return feature.GetFieldAsDouble(fieldIndex);
137
0
        }
138
0
    }
139
140
    bool CheckField(const std::string &dim, const std::string &fieldName,
141
                    int index, bool &isStringVar)
142
0
    {
143
0
        if (index == -1)
144
0
        {
145
0
            CPLError(CE_Failure, CPLE_AppDefined,
146
0
                     "Specified %s field name '%s' does not exist", dim.c_str(),
147
0
                     fieldName.c_str());
148
0
            m_fatalError = true;
149
0
            return false;
150
0
        }
151
152
0
        const auto eType = m_defn->GetFieldDefn(index)->GetType();
153
0
        if (eType == OFTString)
154
0
        {
155
0
            isStringVar = true;
156
0
        }
157
0
        else if (eType != OFTInteger && eType != OFTReal)
158
0
        {
159
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s field type: %s",
160
0
                     dim.c_str(), OGR_GetFieldTypeName(eType));
161
0
            m_fatalError = true;
162
0
            return false;
163
0
        }
164
165
0
        return true;
166
0
    }
167
168
    const OGRFeatureDefn *GetLayerDefn() const override
169
0
    {
170
0
        return m_defn.get();
171
0
    }
172
173
    int TestCapability(const char *pszCap) const override
174
0
    {
175
0
        return m_srcLayer.TestCapability(pszCap);
176
0
    }
177
178
  protected:
179
    bool TranslateFeature(
180
        std::unique_ptr<OGRFeature> poSrcFeature,
181
        std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
182
183
  private:
184
    const std::string m_xField;
185
    const std::string m_yField;
186
    const std::string m_zField;
187
    const std::string m_mField;
188
    const int m_xFieldIndex;
189
    const int m_yFieldIndex;
190
    const int m_zFieldIndex;
191
    const int m_mFieldIndex;
192
    const bool m_hasZ;
193
    const bool m_hasM;
194
    bool m_fatalError = false;
195
    bool m_xFieldIsString = false;
196
    bool m_yFieldIsString = false;
197
    bool m_zFieldIsString = false;
198
    bool m_mFieldIsString = false;
199
    const OGRSpatialReferenceRefCountedPtr m_srs;
200
    const OGRFeatureDefnRefCountedPtr m_defn;
201
202
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorMakePointAlgorithmLayer)
203
};
204
205
/************************************************************************/
206
/*                          TranslateFeature()                          */
207
/************************************************************************/
208
209
bool GDALVectorMakePointAlgorithmLayer::TranslateFeature(
210
    std::unique_ptr<OGRFeature> poSrcFeature,
211
    std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
212
0
{
213
0
    const double x = GetField(*poSrcFeature, m_xFieldIndex, m_xFieldIsString);
214
0
    const double y = GetField(*poSrcFeature, m_yFieldIndex, m_yFieldIsString);
215
0
    const double z =
216
0
        m_hasZ ? GetField(*poSrcFeature, m_zFieldIndex, m_zFieldIsString) : 0;
217
0
    const double m =
218
0
        m_hasM ? GetField(*poSrcFeature, m_mFieldIndex, m_mFieldIsString) : 0;
219
220
0
    if (m_fatalError)
221
0
    {
222
0
        return false;
223
0
    }
224
225
0
    std::unique_ptr<OGRPoint> poGeom;
226
227
0
    if (m_hasZ && m_hasM)
228
0
    {
229
0
        poGeom = std::make_unique<OGRPoint>(x, y, z, m);
230
0
    }
231
0
    else if (m_hasZ)
232
0
    {
233
0
        poGeom = std::make_unique<OGRPoint>(x, y, z);
234
0
    }
235
0
    else if (m_hasM)
236
0
    {
237
0
        poGeom.reset(OGRPoint::createXYM(x, y, m));
238
0
    }
239
0
    else
240
0
    {
241
0
        poGeom = std::make_unique<OGRPoint>(x, y);
242
0
    }
243
244
0
    if (m_srs)
245
0
    {
246
0
        poGeom->assignSpatialReference(m_srs.get());
247
0
    }
248
249
0
    auto poDstFeature = std::make_unique<OGRFeature>(m_defn.get());
250
0
    poDstFeature->SetFID(poSrcFeature->GetFID());
251
0
    poDstFeature->SetFrom(poSrcFeature.get());
252
0
    poDstFeature->SetGeometry(std::move(poGeom));
253
254
0
    apoOutFeatures.push_back(std::move(poDstFeature));
255
256
0
    return true;
257
0
}
258
259
}  // namespace
260
261
/************************************************************************/
262
/*               GDALVectorMakePointAlgorithm::RunStep()                */
263
/************************************************************************/
264
265
bool GDALVectorMakePointAlgorithm::RunStep(GDALPipelineStepRunContext &)
266
0
{
267
0
    GDALDataset *poSrcDS = m_inputDataset[0].GetDatasetRef();
268
0
    if (poSrcDS->GetLayerCount() == 0)
269
0
    {
270
0
        ReportError(CE_Failure, CPLE_AppDefined, "No input vector layer");
271
0
        return false;
272
0
    }
273
0
    OGRLayer *poSrcLayer = poSrcDS->GetLayer(0);
274
275
0
    OGRSpatialReferenceRefCountedPtr poCRS;
276
0
    if (!m_dstCrs.empty())
277
0
    {
278
0
        poCRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
279
0
        poCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
280
0
        auto eErr = poCRS->SetFromUserInput(m_dstCrs.c_str());
281
0
        if (eErr != OGRERR_NONE)
282
0
        {
283
0
            return false;
284
0
        }
285
0
    }
286
287
0
    auto outDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
288
289
0
    outDS->AddLayer(*poSrcLayer,
290
0
                    std::make_unique<GDALVectorMakePointAlgorithmLayer>(
291
0
                        *poSrcLayer, m_xField, m_yField, m_zField, m_mField,
292
0
                        std::move(poCRS)));
293
294
0
    m_outputDataset.Set(std::move(outDS));
295
296
0
    return true;
297
0
}
298
299
GDALVectorMakePointAlgorithmStandalone::
300
0
    ~GDALVectorMakePointAlgorithmStandalone() = default;
301
302
//! @endcond