Coverage Report

Created: 2026-02-14 06:52

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("dst-crs", 0, _("Destination CRS"), &m_dstCrs).SetIsCRSArg();
41
0
}
42
43
namespace
44
{
45
46
/************************************************************************/
47
/*                  GDALVectorMakePointAlgorithmLayer                   */
48
/************************************************************************/
49
50
class GDALVectorMakePointAlgorithmLayer final
51
    : public GDALVectorPipelineOutputLayer
52
{
53
  public:
54
    GDALVectorMakePointAlgorithmLayer(OGRLayer &oSrcLayer,
55
                                      const std::string &xField,
56
                                      const std::string &yField,
57
                                      const std::string &zField,
58
                                      const std::string &mField,
59
                                      OGRSpatialReference *srs)
60
0
        : GDALVectorPipelineOutputLayer(oSrcLayer), m_xField(xField),
61
0
          m_yField(yField), m_zField(zField), m_mField(mField),
62
          m_xFieldIndex(
63
0
              oSrcLayer.GetLayerDefn()->GetFieldIndex(xField.c_str())),
64
          m_yFieldIndex(
65
0
              oSrcLayer.GetLayerDefn()->GetFieldIndex(yField.c_str())),
66
          m_zFieldIndex(
67
0
              zField.empty()
68
0
                  ? -1
69
0
                  : oSrcLayer.GetLayerDefn()->GetFieldIndex(zField.c_str())),
70
          m_mFieldIndex(
71
0
              mField.empty()
72
0
                  ? -1
73
0
                  : oSrcLayer.GetLayerDefn()->GetFieldIndex(mField.c_str())),
74
0
          m_hasZ(!zField.empty()), m_hasM(!mField.empty()), m_srs(srs),
75
0
          m_defn(oSrcLayer.GetLayerDefn()->Clone())
76
0
    {
77
0
        m_defn->Reference();
78
0
        if (m_srs)
79
0
        {
80
0
            m_srs->Reference();
81
0
        }
82
83
0
        if (!CheckField("X", m_xField, m_xFieldIndex, m_xFieldIsString))
84
0
            return;
85
0
        if (!CheckField("Y", m_yField, m_yFieldIndex, m_yFieldIsString))
86
0
            return;
87
0
        if (m_hasZ &&
88
0
            !CheckField("Z", m_zField, m_zFieldIndex, m_zFieldIsString))
89
0
            return;
90
0
        if (m_hasM &&
91
0
            !CheckField("M", m_mField, m_mFieldIndex, m_mFieldIsString))
92
0
            return;
93
94
0
        OGRwkbGeometryType eGeomType = wkbPoint;
95
0
        if (m_hasZ)
96
0
            eGeomType = OGR_GT_SetZ(eGeomType);
97
0
        if (m_hasM)
98
0
            eGeomType = OGR_GT_SetM(eGeomType);
99
100
0
        auto poGeomFieldDefn =
101
0
            std::make_unique<OGRGeomFieldDefn>("geometry", eGeomType);
102
0
        if (m_srs)
103
0
        {
104
0
            poGeomFieldDefn->SetSpatialRef(m_srs);
105
0
        }
106
107
0
        while (m_defn->GetGeomFieldCount() > 0)
108
0
            m_defn->DeleteGeomFieldDefn(0);
109
0
        m_defn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
110
0
    }
111
112
    ~GDALVectorMakePointAlgorithmLayer() override
113
0
    {
114
0
        m_defn->Release();
115
0
        if (m_srs)
116
0
        {
117
0
            m_srs->Release();
118
0
        }
119
0
    }
120
121
    double GetField(const OGRFeature &feature, int fieldIndex, bool isString)
122
0
    {
123
0
        if (isString)
124
0
        {
125
0
            const char *pszValue = feature.GetFieldAsString(fieldIndex);
126
0
            while (std::isspace(static_cast<unsigned char>(*pszValue)))
127
0
            {
128
0
                pszValue++;
129
0
            }
130
0
            char *end = nullptr;
131
0
            double dfValue = CPLStrtodM(pszValue, &end);
132
0
            while (std::isspace(static_cast<unsigned char>(*end)))
133
0
            {
134
0
                end++;
135
0
            }
136
0
            if (end == pszValue || *end != '\0')
137
0
            {
138
0
                const char *pszFieldName =
139
0
                    m_defn->GetFieldDefn(fieldIndex)->GetNameRef();
140
0
                CPLError(CE_Failure, CPLE_AppDefined,
141
0
                         "Invalid value in field %s: %s ", pszFieldName,
142
0
                         pszValue);
143
0
                FailTranslation();
144
0
            }
145
0
            return dfValue;
146
0
        }
147
0
        else
148
0
        {
149
0
            return feature.GetFieldAsDouble(fieldIndex);
150
0
        }
151
0
    }
152
153
    bool CheckField(const std::string &dim, const std::string &fieldName,
154
                    int index, bool &isStringVar)
155
0
    {
156
0
        if (index == -1)
157
0
        {
158
0
            CPLError(CE_Failure, CPLE_AppDefined,
159
0
                     "Specified %s field name '%s' does not exist", dim.c_str(),
160
0
                     fieldName.c_str());
161
0
            FailTranslation();
162
0
            return false;
163
0
        }
164
165
0
        const auto eType = m_defn->GetFieldDefn(index)->GetType();
166
0
        if (eType == OFTString)
167
0
        {
168
0
            isStringVar = true;
169
0
        }
170
0
        else if (eType != OFTInteger && eType != OFTReal)
171
0
        {
172
0
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid %s field type: %s",
173
0
                     dim.c_str(), OGR_GetFieldTypeName(eType));
174
0
            FailTranslation();
175
0
            return false;
176
0
        }
177
178
0
        return true;
179
0
    }
180
181
    const OGRFeatureDefn *GetLayerDefn() const override
182
0
    {
183
0
        return m_defn;
184
0
    }
185
186
    int TestCapability(const char *pszCap) const override
187
0
    {
188
0
        return m_srcLayer.TestCapability(pszCap);
189
0
    }
190
191
  private:
192
    void TranslateFeature(
193
        std::unique_ptr<OGRFeature> poSrcFeature,
194
        std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
195
196
    const std::string m_xField;
197
    const std::string m_yField;
198
    const std::string m_zField;
199
    const std::string m_mField;
200
    const int m_xFieldIndex;
201
    const int m_yFieldIndex;
202
    const int m_zFieldIndex;
203
    const int m_mFieldIndex;
204
    const bool m_hasZ;
205
    const bool m_hasM;
206
    bool m_xFieldIsString = false;
207
    bool m_yFieldIsString = false;
208
    bool m_zFieldIsString = false;
209
    bool m_mFieldIsString = false;
210
    OGRSpatialReference *m_srs;
211
    OGRFeatureDefn *m_defn;
212
213
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorMakePointAlgorithmLayer)
214
};
215
216
/************************************************************************/
217
/*                          TranslateFeature()                          */
218
/************************************************************************/
219
220
void GDALVectorMakePointAlgorithmLayer::TranslateFeature(
221
    std::unique_ptr<OGRFeature> poSrcFeature,
222
    std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
223
0
{
224
0
    const double x = GetField(*poSrcFeature, m_xFieldIndex, m_xFieldIsString);
225
0
    const double y = GetField(*poSrcFeature, m_yFieldIndex, m_yFieldIsString);
226
0
    const double z =
227
0
        m_hasZ ? GetField(*poSrcFeature, m_zFieldIndex, m_zFieldIsString) : 0;
228
0
    const double m =
229
0
        m_hasM ? GetField(*poSrcFeature, m_mFieldIndex, m_mFieldIsString) : 0;
230
231
0
    std::unique_ptr<OGRPoint> poGeom;
232
233
0
    if (m_hasZ && m_hasM)
234
0
    {
235
0
        poGeom = std::make_unique<OGRPoint>(x, y, z, m);
236
0
    }
237
0
    else if (m_hasZ)
238
0
    {
239
0
        poGeom = std::make_unique<OGRPoint>(x, y, z);
240
0
    }
241
0
    else if (m_hasM)
242
0
    {
243
0
        poGeom.reset(OGRPoint::createXYM(x, y, m));
244
0
    }
245
0
    else
246
0
    {
247
0
        poGeom = std::make_unique<OGRPoint>(x, y);
248
0
    }
249
250
0
    if (m_srs)
251
0
    {
252
0
        poGeom->assignSpatialReference(m_srs);
253
0
    }
254
255
0
    auto poDstFeature = std::make_unique<OGRFeature>(m_defn);
256
0
    poDstFeature->SetFID(poSrcFeature->GetFID());
257
0
    poDstFeature->SetFrom(poSrcFeature.get());
258
0
    poDstFeature->SetGeometry(std::move(poGeom));
259
260
0
    apoOutFeatures.push_back(std::move(poDstFeature));
261
0
}
262
263
}  // namespace
264
265
/************************************************************************/
266
/*               GDALVectorMakePointAlgorithm::RunStep()                */
267
/************************************************************************/
268
269
bool GDALVectorMakePointAlgorithm::RunStep(GDALPipelineStepRunContext &)
270
0
{
271
0
    GDALDataset *poSrcDS = m_inputDataset[0].GetDatasetRef();
272
0
    if (poSrcDS->GetLayerCount() == 0)
273
0
    {
274
0
        ReportError(CE_Failure, CPLE_AppDefined, "No input vector layer");
275
0
        return false;
276
0
    }
277
0
    OGRLayer *poSrcLayer = poSrcDS->GetLayer(0);
278
279
0
    std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poCRS;
280
0
    if (!m_dstCrs.empty())
281
0
    {
282
0
        poCRS.reset(new OGRSpatialReference());
283
0
        poCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
284
0
        auto eErr = poCRS->SetFromUserInput(m_dstCrs.c_str());
285
0
        if (eErr != OGRERR_NONE)
286
0
        {
287
0
            return false;
288
0
        }
289
0
    }
290
291
0
    auto outDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
292
293
0
    outDS->AddLayer(
294
0
        *poSrcLayer,
295
0
        std::make_unique<GDALVectorMakePointAlgorithmLayer>(
296
0
            *poSrcLayer, m_xField, m_yField, m_zField, m_mField, poCRS.get()));
297
298
0
    m_outputDataset.Set(std::move(outDS));
299
300
0
    return true;
301
0
}
302
303
GDALVectorMakePointAlgorithmStandalone::
304
0
    ~GDALVectorMakePointAlgorithmStandalone() = default;
305
306
//! @endcond