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