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