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 93 : GDALVectorMakePointAlgorithm::GDALVectorMakePointAlgorithm(bool standaloneStep)
29 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 93 : standaloneStep)
31 : {
32 186 : AddArg("x", 0, _("Field from which X coordinate should be read"), &m_xField)
33 93 : .SetRequired();
34 186 : AddArg("y", 0, _("Field from which Y coordinate should be read"), &m_yField)
35 93 : .SetRequired();
36 : AddArg("z", 0, _("Optional field from which Z coordinate should be read"),
37 93 : &m_zField);
38 : AddArg("m", 0, _("Optional field from which M coordinate should be read"),
39 93 : &m_mField);
40 186 : AddArg(GDAL_ARG_NAME_OUTPUT_CRS, 0, _("Output CRS"), &m_dstCrs)
41 186 : .AddHiddenAlias("dst-crs")
42 93 : .SetIsCRSArg();
43 93 : }
44 :
45 : namespace
46 : {
47 :
48 : /************************************************************************/
49 : /* GDALVectorMakePointAlgorithmLayer */
50 : /************************************************************************/
51 :
52 : class GDALVectorMakePointAlgorithmLayer final
53 : : public GDALVectorPipelineOutputLayer
54 : {
55 : public:
56 12 : 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 12 : : GDALVectorPipelineOutputLayer(oSrcLayer), m_xField(xField),
63 : m_yField(yField), m_zField(zField), m_mField(mField),
64 : m_xFieldIndex(
65 12 : oSrcLayer.GetLayerDefn()->GetFieldIndex(xField.c_str())),
66 : m_yFieldIndex(
67 12 : oSrcLayer.GetLayerDefn()->GetFieldIndex(yField.c_str())),
68 : m_zFieldIndex(
69 12 : zField.empty()
70 12 : ? -1
71 6 : : oSrcLayer.GetLayerDefn()->GetFieldIndex(zField.c_str())),
72 : m_mFieldIndex(
73 12 : mField.empty()
74 12 : ? -1
75 6 : : oSrcLayer.GetLayerDefn()->GetFieldIndex(mField.c_str())),
76 36 : m_hasZ(!zField.empty()), m_hasM(!mField.empty()),
77 60 : m_srs(std::move(srs)), m_defn(oSrcLayer.GetLayerDefn()->Clone())
78 : {
79 12 : if (!CheckField("X", m_xField, m_xFieldIndex, m_xFieldIsString))
80 4 : return;
81 11 : if (!CheckField("Y", m_yField, m_yFieldIndex, m_yFieldIsString))
82 1 : return;
83 14 : if (m_hasZ &&
84 14 : !CheckField("Z", m_zField, m_zFieldIndex, m_zFieldIsString))
85 1 : return;
86 12 : if (m_hasM &&
87 12 : !CheckField("M", m_mField, m_mFieldIndex, m_mFieldIsString))
88 1 : return;
89 :
90 8 : OGRwkbGeometryType eGeomType = wkbPoint;
91 8 : if (m_hasZ)
92 2 : eGeomType = OGR_GT_SetZ(eGeomType);
93 8 : if (m_hasM)
94 2 : eGeomType = OGR_GT_SetM(eGeomType);
95 :
96 : auto poGeomFieldDefn =
97 16 : std::make_unique<OGRGeomFieldDefn>("geometry", eGeomType);
98 8 : if (m_srs)
99 : {
100 4 : poGeomFieldDefn->SetSpatialRef(m_srs.get());
101 : }
102 :
103 9 : while (m_defn->GetGeomFieldCount() > 0)
104 1 : m_defn->DeleteGeomFieldDefn(0);
105 8 : m_defn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
106 : }
107 :
108 58 : double GetField(const OGRFeature &feature, int fieldIndex, bool isString)
109 : {
110 58 : 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 52 : return feature.GetFieldAsDouble(fieldIndex);
137 : }
138 : }
139 :
140 30 : bool CheckField(const std::string &dim, const std::string &fieldName,
141 : int index, bool &isStringVar)
142 : {
143 30 : 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 26 : const auto eType = m_defn->GetFieldDefn(index)->GetType();
153 26 : if (eType == OFTString)
154 : {
155 6 : isStringVar = true;
156 : }
157 20 : 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 26 : return true;
166 : }
167 :
168 91 : const OGRFeatureDefn *GetLayerDefn() const override
169 : {
170 91 : return m_defn.get();
171 : }
172 :
173 12 : int 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 19 : bool GDALVectorMakePointAlgorithmLayer::TranslateFeature(
210 : std::unique_ptr<OGRFeature> poSrcFeature,
211 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
212 : {
213 19 : const double x = GetField(*poSrcFeature, m_xFieldIndex, m_xFieldIsString);
214 19 : const double y = GetField(*poSrcFeature, m_yFieldIndex, m_yFieldIsString);
215 : const double z =
216 19 : m_hasZ ? GetField(*poSrcFeature, m_zFieldIndex, m_zFieldIsString) : 0;
217 : const double m =
218 19 : m_hasM ? GetField(*poSrcFeature, m_mFieldIndex, m_mFieldIsString) : 0;
219 :
220 19 : if (m_fatalError)
221 : {
222 7 : return false;
223 : }
224 :
225 12 : std::unique_ptr<OGRPoint> poGeom;
226 :
227 12 : if (m_hasZ && m_hasM)
228 : {
229 3 : poGeom = std::make_unique<OGRPoint>(x, y, z, m);
230 : }
231 9 : else if (m_hasZ)
232 : {
233 3 : poGeom = std::make_unique<OGRPoint>(x, y, z);
234 : }
235 6 : else if (m_hasM)
236 : {
237 3 : poGeom.reset(OGRPoint::createXYM(x, y, m));
238 : }
239 : else
240 : {
241 3 : poGeom = std::make_unique<OGRPoint>(x, y);
242 : }
243 :
244 12 : if (m_srs)
245 : {
246 12 : poGeom->assignSpatialReference(m_srs.get());
247 : }
248 :
249 12 : auto poDstFeature = std::make_unique<OGRFeature>(m_defn.get());
250 12 : poDstFeature->SetFID(poSrcFeature->GetFID());
251 12 : poDstFeature->SetFrom(poSrcFeature.get());
252 12 : poDstFeature->SetGeometry(std::move(poGeom));
253 :
254 12 : apoOutFeatures.push_back(std::move(poDstFeature));
255 :
256 12 : return true;
257 : }
258 :
259 : } // namespace
260 :
261 : /************************************************************************/
262 : /* GDALVectorMakePointAlgorithm::RunStep() */
263 : /************************************************************************/
264 :
265 13 : bool GDALVectorMakePointAlgorithm::RunStep(GDALPipelineStepRunContext &)
266 : {
267 13 : GDALDataset *poSrcDS = m_inputDataset[0].GetDatasetRef();
268 13 : if (poSrcDS->GetLayerCount() == 0)
269 : {
270 1 : ReportError(CE_Failure, CPLE_AppDefined, "No input vector layer");
271 1 : return false;
272 : }
273 12 : OGRLayer *poSrcLayer = poSrcDS->GetLayer(0);
274 :
275 24 : OGRSpatialReferenceRefCountedPtr poCRS;
276 12 : if (!m_dstCrs.empty())
277 : {
278 4 : poCRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
279 4 : poCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
280 4 : auto eErr = poCRS->SetFromUserInput(m_dstCrs.c_str());
281 4 : if (eErr != OGRERR_NONE)
282 : {
283 0 : return false;
284 : }
285 : }
286 :
287 12 : auto outDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
288 :
289 24 : outDS->AddLayer(*poSrcLayer,
290 12 : std::make_unique<GDALVectorMakePointAlgorithmLayer>(
291 12 : *poSrcLayer, m_xField, m_yField, m_zField, m_mField,
292 12 : std::move(poCRS)));
293 :
294 12 : m_outputDataset.Set(std::move(outDS));
295 :
296 12 : return true;
297 : }
298 :
299 : GDALVectorMakePointAlgorithmStandalone::
300 : ~GDALVectorMakePointAlgorithmStandalone() = default;
301 :
302 : //! @endcond
|