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 91 : GDALVectorMakePointAlgorithm::GDALVectorMakePointAlgorithm(bool standaloneStep)
29 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 91 : standaloneStep)
31 : {
32 182 : AddArg("x", 0, _("Field from which X coordinate should be read"), &m_xField)
33 91 : .SetRequired();
34 182 : AddArg("y", 0, _("Field from which Y coordinate should be read"), &m_yField)
35 91 : .SetRequired();
36 : AddArg("z", 0, _("Optional field from which Z coordinate should be read"),
37 91 : &m_zField);
38 : AddArg("m", 0, _("Optional field from which M coordinate should be read"),
39 91 : &m_mField);
40 182 : AddArg(GDAL_ARG_NAME_OUTPUT_CRS, 0, _("Output CRS"), &m_dstCrs)
41 182 : .AddHiddenAlias("dst-crs")
42 91 : .SetIsCRSArg();
43 91 : }
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 : FailTranslation();
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 : FailTranslation();
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 : FailTranslation();
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 : private:
179 : void TranslateFeature(
180 : std::unique_ptr<OGRFeature> poSrcFeature,
181 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
182 :
183 : const std::string m_xField;
184 : const std::string m_yField;
185 : const std::string m_zField;
186 : const std::string m_mField;
187 : const int m_xFieldIndex;
188 : const int m_yFieldIndex;
189 : const int m_zFieldIndex;
190 : const int m_mFieldIndex;
191 : const bool m_hasZ;
192 : const bool m_hasM;
193 : bool m_xFieldIsString = false;
194 : bool m_yFieldIsString = false;
195 : bool m_zFieldIsString = false;
196 : bool m_mFieldIsString = false;
197 : const OGRSpatialReferenceRefCountedPtr m_srs;
198 : const OGRFeatureDefnRefCountedPtr m_defn;
199 :
200 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorMakePointAlgorithmLayer)
201 : };
202 :
203 : /************************************************************************/
204 : /* TranslateFeature() */
205 : /************************************************************************/
206 :
207 19 : void GDALVectorMakePointAlgorithmLayer::TranslateFeature(
208 : std::unique_ptr<OGRFeature> poSrcFeature,
209 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
210 : {
211 19 : const double x = GetField(*poSrcFeature, m_xFieldIndex, m_xFieldIsString);
212 19 : const double y = GetField(*poSrcFeature, m_yFieldIndex, m_yFieldIsString);
213 : const double z =
214 19 : m_hasZ ? GetField(*poSrcFeature, m_zFieldIndex, m_zFieldIsString) : 0;
215 : const double m =
216 19 : m_hasM ? GetField(*poSrcFeature, m_mFieldIndex, m_mFieldIsString) : 0;
217 :
218 19 : std::unique_ptr<OGRPoint> poGeom;
219 :
220 19 : if (m_hasZ && m_hasM)
221 : {
222 7 : poGeom = std::make_unique<OGRPoint>(x, y, z, m);
223 : }
224 12 : else if (m_hasZ)
225 : {
226 3 : poGeom = std::make_unique<OGRPoint>(x, y, z);
227 : }
228 9 : else if (m_hasM)
229 : {
230 3 : poGeom.reset(OGRPoint::createXYM(x, y, m));
231 : }
232 : else
233 : {
234 6 : poGeom = std::make_unique<OGRPoint>(x, y);
235 : }
236 :
237 19 : if (m_srs)
238 : {
239 12 : poGeom->assignSpatialReference(m_srs.get());
240 : }
241 :
242 38 : auto poDstFeature = std::make_unique<OGRFeature>(m_defn.get());
243 19 : poDstFeature->SetFID(poSrcFeature->GetFID());
244 19 : poDstFeature->SetFrom(poSrcFeature.get());
245 19 : poDstFeature->SetGeometry(std::move(poGeom));
246 :
247 19 : apoOutFeatures.push_back(std::move(poDstFeature));
248 19 : }
249 :
250 : } // namespace
251 :
252 : /************************************************************************/
253 : /* GDALVectorMakePointAlgorithm::RunStep() */
254 : /************************************************************************/
255 :
256 13 : bool GDALVectorMakePointAlgorithm::RunStep(GDALPipelineStepRunContext &)
257 : {
258 13 : GDALDataset *poSrcDS = m_inputDataset[0].GetDatasetRef();
259 13 : if (poSrcDS->GetLayerCount() == 0)
260 : {
261 1 : ReportError(CE_Failure, CPLE_AppDefined, "No input vector layer");
262 1 : return false;
263 : }
264 12 : OGRLayer *poSrcLayer = poSrcDS->GetLayer(0);
265 :
266 24 : OGRSpatialReferenceRefCountedPtr poCRS;
267 12 : if (!m_dstCrs.empty())
268 : {
269 4 : poCRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
270 4 : poCRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
271 4 : auto eErr = poCRS->SetFromUserInput(m_dstCrs.c_str());
272 4 : if (eErr != OGRERR_NONE)
273 : {
274 0 : return false;
275 : }
276 : }
277 :
278 12 : auto outDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
279 :
280 24 : outDS->AddLayer(*poSrcLayer,
281 12 : std::make_unique<GDALVectorMakePointAlgorithmLayer>(
282 12 : *poSrcLayer, m_xField, m_yField, m_zField, m_mField,
283 12 : std::move(poCRS)));
284 :
285 12 : m_outputDataset.Set(std::move(outDS));
286 :
287 12 : return true;
288 : }
289 :
290 : GDALVectorMakePointAlgorithmStandalone::
291 : ~GDALVectorMakePointAlgorithmStandalone() = default;
292 :
293 : //! @endcond
|