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